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 "system/macros.h" // IS_NULL_PTR -- a pure macro header, no state
28#include <string.h>
29
32static gboolean _thumbnail_btn_draw(GtkWidget *widget, cairo_t *cr);
33static gboolean _thumbnail_btn_enter_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event);
34
36{
37 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
38
39 widget_class->draw = _thumbnail_btn_draw;
40 widget_class->enter_notify_event = _thumbnail_btn_enter_leave_notify_callback;
41 widget_class->leave_notify_event = _thumbnail_btn_enter_leave_notify_callback;
42}
43
45{
46}
47
48static gboolean _thumbnail_btn_draw(GtkWidget *widget, cairo_t *cr)
49{
50 g_return_val_if_fail(DTGTK_IS_THUMBNAIL_BTN(widget), FALSE);
51
52 if(gtk_widget_get_allocated_height(widget) < 2 || gtk_widget_get_allocated_width(widget) < 2) return TRUE;
53
54 GtkStateFlags state = gtk_widget_get_state_flags(widget);
55
56 GdkRGBA *fg_color, *bg_color;
57 GtkStyleContext *context = gtk_widget_get_style_context(widget);
58 gtk_style_context_get(context, state, GTK_STYLE_PROPERTY_COLOR, &fg_color, GTK_STYLE_PROPERTY_BACKGROUND_COLOR,
59 &bg_color, NULL);
60
61 GtkBorder margin;
62 gtk_style_context_get_margin(context, state, &margin);
63 gtk_widget_set_margin_bottom(widget, margin.bottom);
64 gtk_widget_set_margin_top(widget, margin.top);
65 gtk_widget_set_margin_start(widget, margin.left);
66 gtk_widget_set_margin_end(widget, margin.right);
67
68 if(fg_color->alpha == 0 && bg_color->alpha == 0)
69 {
70 DTGTK_THUMBNAIL_BTN(widget)->hidden = TRUE;
71 gdk_rgba_free(fg_color);
72 gdk_rgba_free(bg_color);
73 return TRUE;
74 }
75 DTGTK_THUMBNAIL_BTN(widget)->hidden = FALSE;
76
77 cairo_save(cr);
78 gdk_cairo_set_source_rgba(cr, fg_color);
79
80 /* draw icon */
81 if(DTGTK_THUMBNAIL_BTN(widget)->icon)
82 {
83 GtkAllocation allocation;
84 gtk_widget_get_allocation(widget, &allocation);
85
86 int flags = DTGTK_THUMBNAIL_BTN(widget)->icon_flags;
87 if(state & GTK_STATE_FLAG_PRELIGHT)
89 else
90 flags &= ~CPF_PRELIGHT;
91
92 if(state & GTK_STATE_FLAG_ACTIVE)
94 else
95 flags &= ~CPF_ACTIVE;
96
97 GtkBorder padding;
98 gtk_style_context_get_padding(context, state, &padding);
99 // padding is a percent of the full size
100 const float icon_x = padding.left;
101 const float icon_y = padding.top;
102 const float icon_w = allocation.width - (padding.left + padding.right);
103 const float icon_h = allocation.height - (padding.top + padding.bottom);
104 // Only paint when there is positive room for the icon. The paint helpers do
105 // cairo_scale(cr, w, h); a zero dimension makes that matrix non-invertible, which poisons the
106 // shared cairo context and cascades a "drawing failure ... invalid matrix" up the whole widget
107 // tree. Padding can eat the whole allocation on tiny buttons (e.g. filmstrip thumbnails during
108 // a view transition), so the >= 2px allocation guard above is not enough on its own.
109 if(icon_w > 0.f && icon_h > 0.f)
110 DTGTK_THUMBNAIL_BTN(widget)->icon(
111 cr, icon_x, icon_y, icon_w, icon_h, flags,
112 DTGTK_THUMBNAIL_BTN(widget)->icon_data ? DTGTK_THUMBNAIL_BTN(widget)->icon_data : bg_color);
113 }
114 // and eventually the image border
115 cairo_restore(cr);
116 gtk_render_frame(context, cr, 0, 0, gtk_widget_get_allocated_width(widget),
117 gtk_widget_get_allocated_height(widget));
118
119 gdk_rgba_free(fg_color);
120 gdk_rgba_free(bg_color);
121 return TRUE;
122}
123
124static gboolean _thumbnail_btn_enter_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event)
125{
126 g_return_val_if_fail(!IS_NULL_PTR(widget), FALSE);
127
128 if(event->type == GDK_ENTER_NOTIFY)
129 gtk_widget_set_state_flags(widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
130 else
131 gtk_widget_unset_state_flags(widget, GTK_STATE_FLAG_PRELIGHT);
132
133 gtk_widget_queue_draw(widget);
134 return FALSE;
135}
136
137// Public functions
138GtkWidget *dtgtk_thumbnail_btn_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
139{
141 button = g_object_new(dtgtk_thumbnail_btn_get_type(), NULL);
142 dt_gui_add_class(GTK_WIDGET(button), "dt_thumb_btn");
143 button->icon = paint;
144 button->icon_flags = paintflags;
145 button->icon_data = paintdata;
146 gtk_widget_set_events(GTK_WIDGET(button), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK
147 | GDK_LEAVE_NOTIFY_MASK);
148 gtk_widget_set_app_paintable(GTK_WIDGET(button), TRUE);
149 gtk_widget_set_name(GTK_WIDGET(button), "thumbnail_btn");
150 return (GtkWidget *)button;
151}
152
154{
155 static GType dtgtk_thumbnail_btn_type = 0;
156 if(!dtgtk_thumbnail_btn_type)
157 {
158 static const GTypeInfo dtgtk_thumbnail_btn_info = {
160 (GBaseInitFunc)NULL,
161 (GBaseFinalizeFunc)NULL,
162 (GClassInitFunc)_thumbnail_btn_class_init,
163 NULL, /* class_finalize */
164 NULL, /* class_data */
166 0, /* n_preallocs */
167 (GInstanceInitFunc)_thumbnail_btn_init,
168 };
169 dtgtk_thumbnail_btn_type
170 = g_type_register_static(GTK_TYPE_DRAWING_AREA, "GtkDarktableThumbnailBtn", &dtgtk_thumbnail_btn_info, 0);
171 }
172 return dtgtk_thumbnail_btn_type;
173}
174
176{
177 g_return_val_if_fail(DTGTK_IS_THUMBNAIL_BTN(widget), TRUE);
178
179 return DTGTK_THUMBNAIL_BTN(widget)->hidden;
180}
181// clang-format off
182// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
183// vim: shiftwidth=2 expandtab tabstop=2 cindent
184// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
185// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
#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 macros.h:96
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
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)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
@ CPF_PRELIGHT
@ CPF_ACTIVE
void(* DTGTKCairoPaintIconFunc)(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)