Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
thumbnail_btn.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2020, 2022 Aldric Renaudin.
4 Copyright (C) 2021 Dan Torop.
5 Copyright (C) 2021 Heiko Bauke.
6 Copyright (C) 2021 Hubert Kowalski.
7 Copyright (C) 2022, 2025 Aurélien PIERRE.
8 Copyright (C) 2022 Diederik Ter Rahe.
9 Copyright (C) 2022 Martin Bařinka.
10 Copyright (C) 2022 Nicolas Auffray.
11
12 darktable is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 darktable is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with darktable. If not, see <http://www.gnu.org/licenses/>.
24*/
25#include "thumbnail_btn.h"
26#include "gui/gtk.h"
27#include <string.h>
28
31static gboolean _thumbnail_btn_draw(GtkWidget *widget, cairo_t *cr);
32static gboolean _thumbnail_btn_enter_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event);
33
35{
36 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
37
38 widget_class->draw = _thumbnail_btn_draw;
39 widget_class->enter_notify_event = _thumbnail_btn_enter_leave_notify_callback;
40 widget_class->leave_notify_event = _thumbnail_btn_enter_leave_notify_callback;
41}
42
44{
45}
46
47static gboolean _thumbnail_btn_draw(GtkWidget *widget, cairo_t *cr)
48{
49 g_return_val_if_fail(DTGTK_IS_THUMBNAIL_BTN(widget), FALSE);
50
51 if(gtk_widget_get_allocated_height(widget) < 2 || gtk_widget_get_allocated_width(widget) < 2) return TRUE;
52
53 GtkStateFlags state = gtk_widget_get_state_flags(widget);
54
55 GdkRGBA *fg_color, *bg_color;
56 GtkStyleContext *context = gtk_widget_get_style_context(widget);
57 gtk_style_context_get(context, state, GTK_STYLE_PROPERTY_COLOR, &fg_color, GTK_STYLE_PROPERTY_BACKGROUND_COLOR,
58 &bg_color, NULL);
59
60 GtkBorder margin;
61 gtk_style_context_get_margin(context, state, &margin);
62 gtk_widget_set_margin_bottom(widget, margin.bottom);
63 gtk_widget_set_margin_top(widget, margin.top);
64 gtk_widget_set_margin_start(widget, margin.left);
65 gtk_widget_set_margin_end(widget, margin.right);
66
67 if(fg_color->alpha == 0 && bg_color->alpha == 0)
68 {
69 DTGTK_THUMBNAIL_BTN(widget)->hidden = TRUE;
70 gdk_rgba_free(fg_color);
71 gdk_rgba_free(bg_color);
72 return TRUE;
73 }
74 DTGTK_THUMBNAIL_BTN(widget)->hidden = FALSE;
75
76 cairo_save(cr);
77 gdk_cairo_set_source_rgba(cr, fg_color);
78
79 /* draw icon */
80 if(DTGTK_THUMBNAIL_BTN(widget)->icon)
81 {
82 GtkAllocation allocation;
83 gtk_widget_get_allocation(widget, &allocation);
84
85 int flags = DTGTK_THUMBNAIL_BTN(widget)->icon_flags;
86 if(state & GTK_STATE_FLAG_PRELIGHT)
88 else
89 flags &= ~CPF_PRELIGHT;
90
91 if(state & GTK_STATE_FLAG_ACTIVE)
93 else
94 flags &= ~CPF_ACTIVE;
95
96 GtkBorder padding;
97 gtk_style_context_get_padding(context, state, &padding);
98 // padding is a percent of the full size
99 const float icon_x = padding.left;
100 const float icon_y = padding.top;
101 const float icon_w = allocation.width - (padding.left + padding.right);
102 const float icon_h = allocation.height - (padding.top + padding.bottom);
103 // Only paint when there is positive room for the icon. The paint helpers do
104 // cairo_scale(cr, w, h); a zero dimension makes that matrix non-invertible, which poisons the
105 // shared cairo context and cascades a "drawing failure ... invalid matrix" up the whole widget
106 // tree. Padding can eat the whole allocation on tiny buttons (e.g. filmstrip thumbnails during
107 // a view transition), so the >= 2px allocation guard above is not enough on its own.
108 if(icon_w > 0.f && icon_h > 0.f)
109 DTGTK_THUMBNAIL_BTN(widget)->icon(
110 cr, icon_x, icon_y, icon_w, icon_h, flags,
111 DTGTK_THUMBNAIL_BTN(widget)->icon_data ? DTGTK_THUMBNAIL_BTN(widget)->icon_data : bg_color);
112 }
113 // and eventually the image border
114 cairo_restore(cr);
115 gtk_render_frame(context, cr, 0, 0, gtk_widget_get_allocated_width(widget),
116 gtk_widget_get_allocated_height(widget));
117
118 gdk_rgba_free(fg_color);
119 gdk_rgba_free(bg_color);
120 return TRUE;
121}
122
123static gboolean _thumbnail_btn_enter_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event)
124{
125 g_return_val_if_fail(!IS_NULL_PTR(widget), FALSE);
126
127 if(event->type == GDK_ENTER_NOTIFY)
128 gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
129 else
130 gtk_widget_unset_state_flags(widget, GTK_STATE_FLAG_PRELIGHT);
131
132 gtk_widget_queue_draw(widget);
133 return FALSE;
134}
135
136// Public functions
137GtkWidget *dtgtk_thumbnail_btn_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
138{
140 button = g_object_new(dtgtk_thumbnail_btn_get_type(), NULL);
141 dt_gui_add_class(GTK_WIDGET(button), "dt_thumb_btn");
142 button->icon = paint;
143 button->icon_flags = paintflags;
144 button->icon_data = paintdata;
145 gtk_widget_set_events(GTK_WIDGET(button), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK
146 | GDK_LEAVE_NOTIFY_MASK);
147 gtk_widget_set_app_paintable(GTK_WIDGET(button), TRUE);
148 gtk_widget_set_name(GTK_WIDGET(button), "thumbnail_btn");
149 return (GtkWidget *)button;
150}
151
153{
154 static GType dtgtk_thumbnail_btn_type = 0;
155 if(!dtgtk_thumbnail_btn_type)
156 {
157 static const GTypeInfo dtgtk_thumbnail_btn_info = {
159 (GBaseInitFunc)NULL,
160 (GBaseFinalizeFunc)NULL,
161 (GClassInitFunc)_thumbnail_btn_class_init,
162 NULL, /* class_finalize */
163 NULL, /* class_data */
165 0, /* n_preallocs */
166 (GInstanceInitFunc)_thumbnail_btn_init,
167 };
168 dtgtk_thumbnail_btn_type
169 = g_type_register_static(GTK_TYPE_DRAWING_AREA, "GtkDarktableThumbnailBtn", &dtgtk_thumbnail_btn_info, 0);
170 }
171 return dtgtk_thumbnail_btn_type;
172}
173
175{
176 g_return_val_if_fail(DTGTK_IS_THUMBNAIL_BTN(widget), TRUE);
177
178 return DTGTK_THUMBNAIL_BTN(widget)->hidden;
179}
180// clang-format off
181// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
182// vim: shiftwidth=2 expandtab tabstop=2 cindent
183// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
184// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
@ CPF_PRELIGHT
Definition dtgtk/paint.h:68
@ CPF_ACTIVE
Definition dtgtk/paint.h:67
void(* DTGTKCairoPaintIconFunc)(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
Definition dtgtk/paint.h:75
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
DTGTKCairoPaintIconFunc icon
static gboolean _thumbnail_btn_enter_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event)
static void _thumbnail_btn_init(GtkDarktableThumbnailBtn *button)
static void _thumbnail_btn_class_init(GtkDarktableThumbnailBtnClass *klass)
GtkWidget * dtgtk_thumbnail_btn_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
GType dtgtk_thumbnail_btn_get_type()
static gboolean _thumbnail_btn_draw(GtkWidget *widget, cairo_t *cr)
gboolean dtgtk_thumbnail_btn_is_hidden(GtkWidget *widget)
struct _GtkDarktableThumbnailBtnClass GtkDarktableThumbnailBtnClass
struct _GtkDarktableThumbnailBtn GtkDarktableThumbnailBtn
#define DTGTK_IS_THUMBNAIL_BTN(obj)
#define DTGTK_THUMBNAIL_BTN(obj)