diff --git a/src/bauhaus/bauhaus.c b/src/bauhaus/bauhaus.c index c53a200e73d6..153d07f99924 100644 --- a/src/bauhaus/bauhaus.c +++ b/src/bauhaus/bauhaus.c @@ -37,6 +37,101 @@ #include +// data portion for a slider +typedef struct dt_bauhaus_slider_data_t +{ + float pos; // normalized slider value + float step; // step width (not normalized) + float defpos; // default value (not normalized) + float min, max; // min and max range + float soft_min, soft_max; + float hard_min, hard_max; + int digits; // how many decimals to round to + + float (*grad_col)[3]; // colors for gradient slider + int grad_cnt; // how many stops + float *grad_pos; // and position of these. + + int fill_feedback : 1; // fill the slider with brighter part up to the handle? + + const char *format; // numeric value is printed with this format + float factor; // multiplication factor before printing + float offset; // addition before printing + + int is_dragging : 1; // indicates is mouse is dragging slider + int is_changed : 1; // indicates new data + guint timeout_handle; // used to store id of timeout routine + float (*curve)(float, dt_bauhaus_curve_t); // callback function +} dt_bauhaus_slider_data_t; + +typedef struct dt_bauhaus_combobox_entry_t +{ + char *label; + dt_bauhaus_combobox_alignment_t alignment; + gboolean sensitive; + gpointer data; + void (*free_func)(gpointer); // callback to free data elements +} dt_bauhaus_combobox_entry_t; + +typedef struct dt_bauhaus_combobox_data_t +{ + int active; // currently active element + int defpos; // default position + int editable; // 1 if arbitrary text may be typed + dt_bauhaus_combobox_alignment_t text_align; // if selected text in combo should be aligned to the left/right + char *text; // to hold arbitrary text if editable + PangoEllipsizeMode entries_ellipsis; + GPtrArray *entries; + gboolean mute_scrolling; // if set, prevents to issue "data-changed" + dt_bauhaus_combobox_populate_fct populate; // function to populate the combo list on the fly + dt_bauhaus_combobox_entry_select_fct entry_select; // function to select an entry based on context +} dt_bauhaus_combobox_data_t; + +struct _DtBauhausWidget +{ + // gtk base widget + GtkDrawingArea parent; + // which type of control + dt_bauhaus_type_t type; + // associated image operation module (to handle focus and such) + dt_action_t *module; + // pointer to iop field linked to widget + gpointer field; + // type of field + dt_introspection_type_t field_type; + + // label text, short + char *label; + gboolean show_label; + // section, short + gchar *section; + gboolean show_extended_label; + // callback function to draw the quad icon + dt_bauhaus_quad_paint_f quad_paint; + // tooltip to show when mouse is over the quad section + gchar *tooltip; + // minimal modifiers for paint function. + int quad_paint_flags; + // data for the paint callback + void *quad_paint_data; + // quad is a toggle button? + gboolean quad_toggle; + // show quad icon or space + gboolean show_quad; + + // margin and padding structure, defined in css, retrieve on each draw + GtkBorder margin, padding; + // gap to add to the top padding due to the vertical centering + int top_gap; + + // goes last, might extend past the end: + union + { + dt_bauhaus_slider_data_t slider; + dt_bauhaus_combobox_data_t combobox; + }; +}; + G_DEFINE_TYPE(DtBauhausWidget, dt_bh, GTK_TYPE_DRAWING_AREA) enum @@ -61,23 +156,10 @@ static const dt_action_def_t _action_def_slider, _action_def_combo, static const double INNER_PADDING = 4.0; // fwd declare -static void _popup_show(GtkWidget *w); static void _popup_reject(void); static void _popup_hide(void); static gboolean _popup_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data); static gboolean _popup_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data); -static gboolean _widget_draw(GtkWidget *widget, cairo_t *crf); -static gboolean _widget_scroll(GtkWidget *widget, GdkEventScroll *event); -static gboolean _widget_key_press(GtkWidget *widget, GdkEventKey *event); -static gboolean _widget_button_press(GtkWidget *widget, GdkEventButton *event); -static gboolean _widget_button_release(GtkWidget *widget, GdkEventButton *event); -static gboolean _widget_motion_notify(GtkWidget *widget, GdkEventMotion *event); -static void _widget_get_preferred_width(GtkWidget *widget, - gint *minimum_size, - gint *natural_size); -static void _widget_get_preferred_height(GtkWidget *widget, - gint *minimum_height, - gint *natural_height); static void _combobox_set(dt_bauhaus_widget_t *w, const int pos, const gboolean mute); @@ -94,7 +176,7 @@ static void _request_focus(dt_bauhaus_widget_t *w) gtk_widget_set_state_flags(GTK_WIDGET(w), GTK_STATE_FLAG_FOCUSED, FALSE); } -static float _widget_get_quad_width(dt_bauhaus_widget_t *w) +static float _widget_get_quad_width(const dt_bauhaus_widget_t *w) { if(w->show_quad) return darktable.bauhaus->quad_width + INNER_PADDING; @@ -112,7 +194,7 @@ static void _combobox_next_sensitive(dt_bauhaus_widget_t *w, guint state, const gboolean mute) { - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; delta *= dt_accel_get_speed_multiplier(GTK_WIDGET(w), state); int new_pos = d->active; @@ -201,12 +283,6 @@ static void _margins_retrieve(dt_bauhaus_widget_t *w) gtk_style_context_get_padding(context, state, &w->padding); } -void dt_bauhaus_widget_set_section(GtkWidget *widget, const gboolean is_section) -{ - dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - w->is_section = is_section; -} - static int _show_pango_text(dt_bauhaus_widget_t *w, GtkStyleContext *context, cairo_t *cr, @@ -329,59 +405,24 @@ static gboolean _is_full_circle(const dt_bauhaus_slider_data_t *d) && dt_conf_get_bool("bauhaus/color_wheel"); } -static float _slider_right_pos(const float width, - dt_bauhaus_widget_t *w) -{ - // relative position (in widget) of the right bound of the slider - // corrected with the inner padding - return 1.0f - _widget_get_quad_width(w) / width; -} - -static float _slider_coordinate(const float abs_position, - const float width, - dt_bauhaus_widget_t *w) -{ - // Translates an horizontal position relative to the slider in an - // horizontal position relative to the widget - const float left_bound = 0.0f; - const float right_bound = _slider_right_pos(width, w); // exclude the quad area on - // the right - return (left_bound + abs_position * (right_bound - left_bound)) * width; -} - - static float _slider_get_line_offset(const float pos, const float scale, float x, float y, - const float ht, - const int width, - dt_bauhaus_widget_t *w) + const float ht) { // ht is in [0,1] scale here - const float l = 0.0f; - const float r = _slider_right_pos(width, w); - - float offset = 0.0f; + float offset = x - pos; // handle linear startup and rescale y to fit the whole range again - if(y < ht) + if(y > ht) { - offset = (x - l) / (r - l) - pos; - } - else - { - y -= ht; - y /= (1.0f - ht); + y = (y - ht) / (1.0f - ht); - offset = (x - y * y * .5f - (1.0f - y * y) * (l + pos * (r - l))) - / (.5f * y * y / scale + (1.0f - y * y) * (r - l)); + offset = (x - y * y * .5f - (1.0f - y * y) * pos) + / (.5f * y * y / scale + (1.0f - y * y)); } // clamp to result in a [0,1] range: - if(pos + offset > 1.0f) - offset = 1.0f - pos; - if(pos + offset < 0.0f) - offset = -pos; - return offset; + return CLAMP(pos + offset, 0.0f, 1.0f) - pos; } // draw a loupe guideline for the quadratic zoom in in the slider interface: @@ -396,15 +437,13 @@ static void _slider_draw_line(cairo_t *cr, { // pos is normalized position [0,1], offset is on that scale. // ht is in pixels here - const float r = _slider_right_pos(width, w); - const int steps = 64; - cairo_move_to(cr, width * (pos + off) * r, ht * .7f); - cairo_line_to(cr, width * (pos + off) * r, ht); + cairo_move_to(cr, width * (pos + off), ht * .7f); + cairo_line_to(cr, width * (pos + off), ht); for(int j = 1; j < steps; j++) { const float y = j / (steps - 1.0f); - const float x = y * y * .5f * (1.f + off / scale) + (1.0f - y * y) * (pos + off) * r; + const float x = y * y * .5f * (1.f + off / scale) + (1.0f - y * y) * (pos + off); cairo_line_to(cr, x * width, ht + y * (height - ht)); } cairo_stroke(cr); @@ -414,7 +453,7 @@ static void _slider_draw_line(cairo_t *cr, static void _slider_zoom_range(dt_bauhaus_widget_t *w, const float zoom) { - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; dt_bauhaus_t *bh = darktable.bauhaus; bh->change_active = FALSE; bh->mouse_line_distance = 0.0f; @@ -429,6 +468,7 @@ static void _slider_zoom_range(dt_bauhaus_widget_t *w, dt_bauhaus_slider_set(GTK_WIDGET(w), value); // restore value (and // move min/max again // if needed) + bh->popup.oldpos = d->pos; return; } @@ -452,7 +492,7 @@ static void _slider_zoom_range(dt_bauhaus_widget_t *w, static void _slider_zoom_toast(dt_bauhaus_widget_t *w) { - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; gchar *min_text = dt_bauhaus_slider_get_text(GTK_WIDGET(w), d->factor > 0 ? d->min : d->max); @@ -472,7 +512,7 @@ static gboolean _popup_scroll(GtkWidget *widget, if(dt_gui_get_scroll_unit_delta(event, &delta_y)) { if(w->type == DT_BAUHAUS_COMBOBOX) - _combobox_next_sensitive(w, delta_y, 0, w->data.combobox.mute_scrolling); + _combobox_next_sensitive(w, delta_y, 0, w->combobox.mute_scrolling); else _slider_zoom_range(w, delta_y); } @@ -578,17 +618,17 @@ static gboolean _window_motion_notify(GtkWidget *widget, if(w->type == DT_BAUHAUS_SLIDER) { - const dt_bauhaus_slider_data_t *d = &w->data.slider; + const dt_bauhaus_slider_data_t *d = &w->slider; const float width = allocation.width - padding->left - padding->right; const float ht = bh->line_height + INNER_PADDING * 2.0f; const float cx = 0.5f - bh->mouse_x / width; const float cy = 0.5f - bh->mouse_y / width; const float mouse_off = _is_full_circle(d) ? hypotf(cx, cy) < .25f ? 0 - : atan2f(cx, cy) * -.5f * M_1_PI + 0.5f - d->oldpos + : atan2f(cx, cy) * -.5f * M_1_PI + 0.5f - pop->oldpos : _slider_get_line_offset - (d->oldpos, 5.0 * powf(10.0f, -d->digits) / (d->max - d->min) / fabsf(d->factor), - bh->mouse_x / width, bh->mouse_y / width, ht / width, allocation.width, w); + (pop->oldpos, 5.0 * powf(10.0f, -d->digits) / (d->max - d->min) / fabsf(d->factor), + bh->mouse_x / (width - _widget_get_quad_width(w)), bh->mouse_y / width, ht / width); if(event->state & GDK_BUTTON1_MASK || (bh->mouse_line_distance && ((bh->mouse_line_distance * mouse_off <= 0) ^ @@ -596,18 +636,18 @@ static gboolean _window_motion_notify(GtkWidget *widget, bh->change_active = TRUE; bh->mouse_line_distance = mouse_off; if(bh->change_active) - _slider_set_normalized(w, d->oldpos + mouse_off); + _slider_set_normalized(w, pop->oldpos + mouse_off); } else if(w->type == DT_BAUHAUS_COMBOBOX) { - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; const int active = (bh->mouse_y - w->top_gap) / bh->line_height; if(active >= 0 && active < d->entries->len) { if(_combobox_entry(d, active)->sensitive && event->state & GDK_BUTTON1_MASK) { if(active != d->active) - _combobox_set(w, active, w->data.combobox.mute_scrolling); + _combobox_set(w, active, w->combobox.mute_scrolling); } } } @@ -681,41 +721,22 @@ static void _window_show(GtkWidget *w, gpointer user_data) gtk_grab_add(GTK_WIDGET(user_data)); } -static void dt_bh_init(DtBauhausWidget *w) +static void _widget_leave(GtkEventControllerMotion *controller, + GtkWidget *widget) { - w->field = NULL; + // gtk_widget_set_state_flags triggers resize&draw avalanche + // instead add GTK_STATE_FLAG_PRELIGHT in _widget_draw + darktable.bauhaus->hovered = !controller ? widget : NULL; - w->section = NULL; - - // no quad icon and no toggle button: - w->quad_paint = 0; - w->quad_paint_data = NULL; - w->quad_toggle = 0; - w->show_quad = TRUE; - w->show_label = TRUE; - - gtk_widget_add_events(GTK_WIDGET(w), GDK_POINTER_MOTION_MASK - | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK - | GDK_FOCUS_CHANGE_MASK - | darktable.gui->scroll_mask); - - gtk_widget_set_can_focus(GTK_WIDGET(w), TRUE); - dt_gui_add_class(GTK_WIDGET(w), "dt_bauhaus"); + gtk_widget_queue_draw(widget); } -static gboolean _widget_enter_leave(GtkWidget *widget, GdkEventCrossing *event) +static void _widget_enter(GtkEventControllerMotion *controller, + double x, + double y, + GtkWidget *widget) { - if(event->type == GDK_ENTER_NOTIFY) - // gtk_widget_set_state_flags triggers resize&draw avalanche - // instead add GTK_STATE_FLAG_PRELIGHT in _widget_draw - darktable.bauhaus->hovered = widget; - else - darktable.bauhaus->hovered = NULL; - - gtk_widget_queue_draw(widget); - - return FALSE; + _widget_leave(NULL, widget); } static void _widget_finalize(GObject *widget) @@ -723,48 +744,25 @@ static void _widget_finalize(GObject *widget) dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type == DT_BAUHAUS_SLIDER) { - dt_bauhaus_slider_data_t *d = &w->data.slider; - if(d->timeout_handle) g_source_remove(d->timeout_handle); + dt_bauhaus_slider_data_t *d = &w->slider; + if(d->timeout_handle && d->timeout_handle != G_MAXUINT) + g_source_remove(d->timeout_handle); free(d->grad_col); free(d->grad_pos); } else { - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; g_ptr_array_free(d->entries, TRUE); free(d->text); } + g_free(w->label); g_free(w->section); g_free(w->tooltip); G_OBJECT_CLASS(dt_bh_parent_class)->finalize(widget); } -static void dt_bh_class_init(DtBauhausWidgetClass *class) -{ - darktable.bauhaus->signals[DT_BAUHAUS_VALUE_CHANGED_SIGNAL] - = g_signal_new("value-changed", G_TYPE_FROM_CLASS(class), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - darktable.bauhaus->signals[DT_BAUHAUS_QUAD_PRESSED_SIGNAL] - = g_signal_new("quad-pressed", G_TYPE_FROM_CLASS(class), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class); - widget_class->draw = _widget_draw; - widget_class->scroll_event = _widget_scroll; - widget_class->key_press_event = _widget_key_press; - widget_class->button_press_event = _widget_button_press; - widget_class->button_release_event = _widget_button_release; - widget_class->motion_notify_event = _widget_motion_notify; - widget_class->get_preferred_width = _widget_get_preferred_width; - widget_class->get_preferred_height = _widget_get_preferred_height; - widget_class->enter_notify_event = _widget_enter_leave; - widget_class->leave_notify_event = _widget_enter_leave; - G_OBJECT_CLASS(class)->finalize = _widget_finalize; -} - void dt_bauhaus_load_theme() { GtkWidget *root_window = dt_ui_main_window(darktable.gui->ui); @@ -831,17 +829,6 @@ void dt_bauhaus_load_theme() gtk_style_context_get(ctx, GTK_STATE_FLAG_NORMAL, "font", &bh->pango_font_desc, NULL); - if(bh->pango_sec_font_desc) - pango_font_description_free(bh->pango_sec_font_desc); - bh->pango_sec_font_desc = NULL; - - // now get the font for the section labels - gtk_widget_path_iter_add_class(path, pos, "dt_section_label"); - gtk_style_context_set_path(ctx, path); - gtk_style_context_get(ctx, GTK_STATE_FLAG_NORMAL, "font", - &bh->pango_sec_font_desc, NULL); - gtk_widget_path_free(path); - cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 128, 128); cairo_t *cr = cairo_create(cst); PangoLayout *layout = pango_cairo_create_layout(cr); @@ -939,75 +926,57 @@ void dt_bauhaus_cleanup() void dt_bauhaus_combobox_set_default(GtkWidget *widget, const int def) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_combobox_data_t *d = &w->data.combobox; - d->defpos = def; + w->combobox.defpos = def; } int dt_bauhaus_combobox_get_default(GtkWidget *widget) { const dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - const dt_bauhaus_combobox_data_t *d = &w->data.combobox; - return d->defpos; + return w->combobox.defpos; } void dt_bauhaus_slider_set_hard_min(GtkWidget* widget, const float val) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; float pos = dt_bauhaus_slider_get(widget); d->hard_min = val; d->min = MAX(d->min, d->hard_min); d->soft_min = MAX(d->soft_min, d->hard_min); if(val > d->hard_max) dt_bauhaus_slider_set_hard_max(widget,val); - if(pos < val) - { - dt_bauhaus_slider_set(widget,val); - } - else - { - dt_bauhaus_slider_set(widget,pos); - } + dt_bauhaus_slider_set(widget, MAX(val, pos)); } float dt_bauhaus_slider_get_hard_min(GtkWidget* widget) { const dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - const dt_bauhaus_slider_data_t *d = &w->data.slider; - return d->hard_min; + return w->slider.hard_min; } void dt_bauhaus_slider_set_hard_max(GtkWidget* widget, const float val) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; float pos = dt_bauhaus_slider_get(widget); d->hard_max = val; d->max = MIN(d->max, d->hard_max); d->soft_max = MIN(d->soft_max, d->hard_max); if(val < d->hard_min) dt_bauhaus_slider_set_hard_min(widget,val); - if(pos > val) - { - dt_bauhaus_slider_set(widget,val); - } - else - { - dt_bauhaus_slider_set(widget,pos); - } + dt_bauhaus_slider_set(widget, MIN(val, pos)); } float dt_bauhaus_slider_get_hard_max(GtkWidget* widget) { const dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - const dt_bauhaus_slider_data_t *d = &w->data.slider; - return d->hard_max; + return w->slider.hard_max; } void dt_bauhaus_slider_set_soft_min(GtkWidget* widget, const float val) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; float oldval = dt_bauhaus_slider_get(widget); d->min = d->soft_min = CLAMP(val,d->hard_min,d->hard_max); dt_bauhaus_slider_set(widget,oldval); @@ -1016,14 +985,13 @@ void dt_bauhaus_slider_set_soft_min(GtkWidget* widget, const float val) float dt_bauhaus_slider_get_soft_min(GtkWidget* widget) { const dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - const dt_bauhaus_slider_data_t *d = &w->data.slider; - return d->soft_min; + return w->slider.soft_min; } void dt_bauhaus_slider_set_soft_max(GtkWidget* widget, const float val) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; float oldval = dt_bauhaus_slider_get(widget); d->max = d->soft_max = CLAMP(val,d->hard_min,d->hard_max); dt_bauhaus_slider_set(widget,oldval); @@ -1032,15 +1000,13 @@ void dt_bauhaus_slider_set_soft_max(GtkWidget* widget, const float val) float dt_bauhaus_slider_get_soft_max(GtkWidget* widget) { const dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - const dt_bauhaus_slider_data_t *d = &w->data.slider; - return d->soft_max; + return w->slider.soft_max; } void dt_bauhaus_slider_set_default(GtkWidget *widget, const float def) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_slider_data_t *d = &w->data.slider; - d->defpos = def; + w->slider.defpos = def; } void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, @@ -1054,8 +1020,15 @@ void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float dt_bauhaus_slider_get_default(GtkWidget *widget) { const dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - const dt_bauhaus_slider_data_t *d = &w->data.slider; - return d->defpos; + return w->slider.defpos; +} + +dt_bauhaus_type_t dt_bauhaus_widget_get_type(GtkWidget *widget) +{ + if(!DT_IS_BAUHAUS_WIDGET(widget)) return DT_BAUHAUS_INVALID; + + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + return w->type; } dt_action_t *dt_bauhaus_widget_set_label(GtkWidget *widget, @@ -1064,8 +1037,8 @@ dt_action_t *dt_bauhaus_widget_set_label(GtkWidget *widget, { dt_action_t *ac = NULL; dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - memset(w->label, 0, sizeof(w->label)); // keep valgrind happy - if(label) g_strlcpy(w->label, Q_(label), sizeof(w->label)); + g_free(w->label); + w->label = label ? g_strdup(Q_(label)) : NULL; if(section) w->section = g_strdup(Q_(section)); if(w->module) @@ -1124,6 +1097,26 @@ void dt_bauhaus_widget_hide_label(GtkWidget *widget) w->show_label = FALSE; } +void dt_bauhaus_widget_set_show_extended_label(GtkWidget *widget, + gboolean show) +{ + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + w->show_extended_label = show; +} + +void dt_bauhaus_widget_set_module(GtkWidget *widget, + dt_action_t *module) +{ + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + w->module = module; +} + +gpointer dt_bauhaus_widget_get_module(GtkWidget *widget) +{ + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + return w->module; +} + void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, const int paint_flags, @@ -1145,16 +1138,49 @@ void dt_bauhaus_widget_set_quad_tooltip(GtkWidget *widget, w->tooltip = g_strdup(text); } -gchar *dt_bauhaus_widget_get_tooltip_markup(GtkWidget *widget, - dt_action_element_t element) +static float _widget_width(const dt_bauhaus_widget_t *w) { - gchar *tooltip = DT_IS_BAUHAUS_WIDGET(widget) - && element == DT_ACTION_ELEMENT_BUTTON - ? DT_BAUHAUS_WIDGET(widget)->tooltip : NULL; - if(!(tooltip)) - return gtk_widget_get_tooltip_markup(widget); + return gtk_widget_get_allocated_width(GTK_WIDGET(w)) + - w->margin.left - w->padding.left + - w->margin.right - w->padding.right + - _widget_get_quad_width(w); +} - return g_markup_escape_text(tooltip, -1); +gchar *dt_bauhaus_widget_get_tooltip_markup(GtkWidget *widget, + const int x, + const int y) +{ + gchar *markup = darktable.control->element == DT_ACTION_ELEMENT_BUTTON + && DT_IS_BAUHAUS_WIDGET(widget) + && DT_BAUHAUS_WIDGET(widget)->tooltip + ? g_markup_escape_text(DT_BAUHAUS_WIDGET(widget)->tooltip, -1) + : gtk_widget_get_tooltip_markup(widget); + if(!darktable.control->mapping_widget + && dt_bauhaus_widget_get_type(widget) == DT_BAUHAUS_SLIDER) + { + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + dt_bauhaus_slider_data_t *d = &w->slider; + + const float pos = (x - w->margin.left - w->padding.left) / _widget_width(w); + + if(y > darktable.bauhaus->line_height / 2.0f + w->margin.top + w->padding.top + && pos <= 1.0f) + dt_util_str_cat(&markup, "%s%s", markup ? "\n" : "", + dt_bauhaus_slider_get_text(widget, pos * (d->max - d->min) + d->min)); + + if(darktable.control->element == DT_ACTION_ELEMENT_FORCE + && (d->min != d->hard_min || d->max != d->hard_max)) + { + dt_util_str_cat(&markup, + _("%sright-click to type a specific value between %s and %s" + "\nor hold ctrl+shift while dragging to ignore soft limits."), + markup ? "\n\n" : "", + dt_bauhaus_slider_get_text(widget, d->hard_min), + dt_bauhaus_slider_get_text(widget, d->hard_max)); + } + } + + return markup; } void dt_bauhaus_widget_set_field(GtkWidget *widget, @@ -1162,7 +1188,7 @@ void dt_bauhaus_widget_set_field(GtkWidget *widget, const dt_introspection_type_t field_type) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - if(*w->label) + if(w->label) dt_print(DT_DEBUG_ALWAYS, "[dt_bauhaus_widget_set_field] bauhaus label '%s'" " set before field (needs to be after)", @@ -1171,6 +1197,12 @@ void dt_bauhaus_widget_set_field(GtkWidget *widget, w->field_type = field_type; } +gpointer dt_bauhaus_widget_get_field(GtkWidget *widget) +{ + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + return w->field; +} + static void _highlight_changed_notebook_tab(GtkWidget *w, gpointer user_data) { GtkWidget *notebook = gtk_widget_get_parent(w); @@ -1193,13 +1225,13 @@ static void _highlight_changed_notebook_tab(GtkWidget *w, gpointer user_data) if(!b->field) continue; if(b->type == DT_BAUHAUS_SLIDER) { - dt_bauhaus_slider_data_t *d = &b->data.slider; + dt_bauhaus_slider_data_t *d = &b->slider; is_changed = fabsf(d->pos - d->curve((d->defpos - d->min) / (d->max - d->min), DT_BAUHAUS_SET)) > 0.001f; } else - is_changed = b->data.combobox.entries->len - && b->data.combobox.active != b->data.combobox.defpos; + is_changed = b->combobox.entries->len + && b->combobox.active != b->combobox.defpos; } } @@ -1299,14 +1331,14 @@ void dt_bauhaus_update_from_field(dt_iop_module_t *module, // make this quad a toggle button: void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, - const int toggle) + const gboolean toggle) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); w->quad_toggle = toggle; } void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, - const int active) + const gboolean active) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(active) @@ -1427,14 +1459,13 @@ GtkWidget *dt_bauhaus_slider_from_widget(dt_bauhaus_widget_t* w, { w->type = DT_BAUHAUS_SLIDER; w->module = DT_ACTION(self); - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; d->min = d->soft_min = d->hard_min = min; d->max = d->soft_max = d->hard_max = max; d->step = step; // normalize default: d->defpos = defval; d->pos = (defval - min) / (max - min); - d->oldpos = d->pos; d->digits = digits; d->format = ""; d->factor = 1.0f; @@ -1494,7 +1525,7 @@ GtkWidget *dt_bauhaus_combobox_from_widget(dt_bauhaus_widget_t* w, { w->type = DT_BAUHAUS_COMBOBOX; w->module = DT_ACTION(self); - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; d->entries = g_ptr_array_new_full(4, _free_combobox_entry); d->defpos = -1; d->active = -1; @@ -1515,7 +1546,7 @@ static dt_bauhaus_combobox_data_t *_combobox_data(GtkWidget *widget) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return NULL; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; if(d->active >= d->entries->len) d->active = -1; @@ -1527,7 +1558,7 @@ void dt_bauhaus_combobox_add_populate_fct(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type == DT_BAUHAUS_COMBOBOX) - w->data.combobox.populate = fct; + w->combobox.populate = fct; } void dt_bauhaus_combobox_add_entry_select_fct(GtkWidget *widget, @@ -1535,7 +1566,7 @@ void dt_bauhaus_combobox_add_entry_select_fct(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type == DT_BAUHAUS_COMBOBOX) - w->data.combobox.entry_select = fct; + w->combobox.entry_select = fct; } void dt_bauhaus_combobox_add_list(GtkWidget *widget, @@ -1616,7 +1647,7 @@ void dt_bauhaus_combobox_add_full(GtkWidget *widget, dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; if(!data && d->entries->len > 0 && !_combobox_entry(d, 0)->data) data =_combobox_entry(d, d->entries->len - 1)->data + 1; dt_bauhaus_combobox_entry_t *entry = _new_combobox_entry(text, align, @@ -1638,7 +1669,7 @@ gboolean dt_bauhaus_combobox_set_entry_label(GtkWidget *widget, // the initial one dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return FALSE; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; if(!d || pos < 0 || pos >= d->entries->len) return FALSE; dt_bauhaus_combobox_entry_t *entry = _combobox_entry(d, pos); g_free(entry->label); @@ -1651,8 +1682,7 @@ void dt_bauhaus_combobox_set_entries_ellipsis(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; - d->entries_ellipsis = ellipis; + w->combobox.entries_ellipsis = ellipis; } PangoEllipsizeMode dt_bauhaus_combobox_get_entries_ellipsis(GtkWidget *widget) @@ -1667,7 +1697,7 @@ void dt_bauhaus_combobox_set_editable(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; d->editable = editable ? 1 : 0; if(d->editable && !d->text) d->text = calloc(1, DT_BAUHAUS_MAX_TEXT); @@ -1686,8 +1716,7 @@ void dt_bauhaus_combobox_set_selected_text_align { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; - d->text_align = text_align; + w->combobox.text_align = text_align; } void dt_bauhaus_combobox_remove_at(GtkWidget *widget, @@ -1723,7 +1752,7 @@ void dt_bauhaus_combobox_insert_full(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; dt_bauhaus_combobox_entry_t *entry = _new_combobox_entry(text, align, TRUE, data, free_func); if(entry) g_ptr_array_insert(d->entries, pos, entry); @@ -1760,7 +1789,7 @@ void dt_bauhaus_combobox_clear(GtkWidget *widget) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_COMBOBOX) return; - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; d->active = -1; g_ptr_array_set_size(d->entries, 0); } @@ -1788,7 +1817,7 @@ static void _combobox_set(dt_bauhaus_widget_t *w, const int pos, const gboolean mute) { - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; d->active = CLAMP(pos, -1, (int)d->entries->len - 1); gtk_widget_queue_draw(GTK_WIDGET(w)); @@ -1926,8 +1955,7 @@ void dt_bauhaus_slider_clear_stops(GtkWidget *widget) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; - d->grad_cnt = 0; + w->slider.grad_cnt = 0; // keep allocated memory for stops } void dt_bauhaus_slider_set_stop(GtkWidget *widget, @@ -1938,7 +1966,7 @@ void dt_bauhaus_slider_set_stop(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; if(!d->grad_col) { @@ -1975,18 +2003,19 @@ void dt_bauhaus_slider_set_stop(GtkWidget *widget, static void _draw_indicator_shape(cairo_t *cr, float radius) { - if(darktable.bauhaus->marker_shape == DT_BAUHAUS_MARKER_CIRCLE) + dt_bauhaus_t *bh = darktable.bauhaus; + if(bh->marker_shape == DT_BAUHAUS_MARKER_CIRCLE) { cairo_arc(cr, 0.0, 0.0, radius, 0.0, 2.0 * M_PI); } - else if(darktable.bauhaus->marker_shape == DT_BAUHAUS_MARKER_DIAMOND) + else if(bh->marker_shape == DT_BAUHAUS_MARKER_DIAMOND) { cairo_move_to(cr, 0.0, radius); cairo_line_to(cr, -radius, 0.0); cairo_line_to(cr, 0.0, -radius); cairo_line_to(cr, radius, 0.0); } - else if(darktable.bauhaus->marker_shape == DT_BAUHAUS_MARKER_BAR) + else if(bh->marker_shape == DT_BAUHAUS_MARKER_BAR) { cairo_move_to(cr, -radius*.2, radius); cairo_line_to(cr, -radius*.2, -radius); @@ -2015,14 +2044,16 @@ static void _draw_indicator(dt_bauhaus_widget_t *w, // draw scale indicator (the tiny triangle) if(w->type != DT_BAUHAUS_SLIDER) return; - const float border_width = darktable.bauhaus->border_width; - const float size = darktable.bauhaus->marker_size; + dt_bauhaus_t *bh = darktable.bauhaus; + + const float border_width = bh->border_width; + const float size = bh->marker_size; cairo_save(cr); if(wd) - cairo_translate(cr, _slider_coordinate(pos, wd, w), - darktable.bauhaus->line_height + INNER_PADDING - + (darktable.bauhaus->baseline_size - border_width) / 2.0f); + cairo_translate(cr, pos * wd, + bh->line_height + INNER_PADDING + + (bh->baseline_size - border_width) / 2.0f); cairo_scale(cr, 1.0f, -1.0f); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); @@ -2040,9 +2071,7 @@ static void _draw_indicator(dt_bauhaus_widget_t *w, set_color(cr, fg_color); cairo_set_line_width(cr, border_width); - const dt_bauhaus_slider_data_t *d = &w->data.slider; - - if(d->fill_feedback || !wd) + if(w->slider.fill_feedback || !wd) cairo_fill(cr); // Plain indicator (regular sliders) else cairo_stroke(cr); // Hollow indicator to see a color through it (gradient sliders) @@ -2069,7 +2098,7 @@ static void _draw_quad(dt_bauhaus_widget_t *w, set_color(cr, sensitive && (w->quad_paint_flags & CPF_ACTIVE) ? hovering ? bh->color_fg_hover : bh->color_fg : hovering ? bh->color_fg : bh->color_fg_insensitive); - w->quad_paint(cr, width - bh->quad_width, // x + w->quad_paint(cr, width + INNER_PADDING, // x 0.0, // y bh->quad_width, // width bh->quad_width, // height @@ -2085,15 +2114,15 @@ static void _draw_quad(dt_bauhaus_widget_t *w, switch(w->type) { case DT_BAUHAUS_COMBOBOX: - cairo_translate(cr, width - bh->quad_width * .5f, height * .5f); + cairo_move_to(cr, width + INNER_PADDING + bh->quad_width * .3, + height * .5 - bh->quad_width * .1); GdkRGBA *text_color = _default_color_assign(); GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(w)); const GtkStateFlags state = gtk_widget_get_state_flags(GTK_WIDGET(w)); gtk_style_context_get_color(context, state, text_color); - const float r = bh->quad_width * .2f; - cairo_move_to(cr, -r, -r * .5f); - cairo_line_to(cr, 0, r * .5f); - cairo_line_to(cr, r, -r * .5f); + const double r = bh->quad_width * .2; + cairo_rel_line_to(cr, r, r); + cairo_rel_line_to(cr, r, -r); set_color(cr, *text_color); cairo_stroke(cr); gdk_rgba_free(text_color); @@ -2102,7 +2131,7 @@ static void _draw_quad(dt_bauhaus_widget_t *w, break; default: cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); - cairo_rectangle(cr, width - bh->quad_width, 0.0, bh->quad_width, bh->quad_width); + cairo_rectangle(cr, width + INNER_PADDING, 0.0, bh->quad_width, bh->quad_width); cairo_fill(cr); break; } @@ -2134,7 +2163,7 @@ static void _draw_color_wheel(dt_bauhaus_widget_t *w, { if(w->type != DT_BAUHAUS_SLIDER) return; - const dt_bauhaus_slider_data_t *d = &w->data.slider; + const dt_bauhaus_slider_data_t *d = &w->slider; cairo_save(cr); @@ -2198,20 +2227,20 @@ static void _draw_color_wheel(dt_bauhaus_widget_t *w, static void _draw_baseline(dt_bauhaus_widget_t *w, cairo_t *cr, - const float width) + const float slider_width) { // draw line for orientation in slider if(w->type != DT_BAUHAUS_SLIDER) return; - const float slider_width = width - _widget_get_quad_width(w); + dt_bauhaus_t *bh = darktable.bauhaus; cairo_save(cr); - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; // pos of baseline - const float htm = darktable.bauhaus->line_height + INNER_PADDING; + const float htm = bh->line_height + INNER_PADDING; // thickness of baseline - const float htM = darktable.bauhaus->baseline_size - darktable.bauhaus->border_width; + const float htM = bh->baseline_size - bh->border_width; // the background of the line cairo_pattern_t *gradient = NULL; @@ -2234,7 +2263,7 @@ static void _draw_baseline(dt_bauhaus_widget_t *w, else { // regular baseline - set_color(cr, darktable.bauhaus->color_bg); + set_color(cr, bh->color_bg); } cairo_fill(cr); @@ -2253,7 +2282,7 @@ static void _draw_baseline(dt_bauhaus_widget_t *w, { // only brighten, useful for colored sliders to not get too faint: cairo_set_operator(cr, CAIRO_OPERATOR_SCREEN); - set_color(cr, darktable.bauhaus->color_fill); + set_color(cr, bh->color_fill); cairo_rectangle(cr, origin, htm, delta, htM); cairo_fill(cr); @@ -2262,9 +2291,9 @@ static void _draw_baseline(dt_bauhaus_widget_t *w, } // draw the 0 reference graduation if it's different than the bounds of the slider - const float graduation_top = htm + htM + 2.0f * darktable.bauhaus->border_width; - const float graduation_height = darktable.bauhaus->border_width / 2.0f; - set_color(cr, darktable.bauhaus->color_fg); + const float graduation_top = htm + htM + 2.0f * bh->border_width; + const float graduation_height = bh->border_width / 2.0f; + set_color(cr, bh->color_fg); // If the max of the slider is 180 or 360, it is likely a hue slider in degrees // a zero in periodic stuff has not much meaning so we skip it @@ -2290,7 +2319,7 @@ static void _popup_reject(void) { dt_bauhaus_widget_t *w = darktable.bauhaus->current; if(w->type == DT_BAUHAUS_SLIDER) - _slider_set_normalized(w, w->data.slider.oldpos); + _slider_set_normalized(w, darktable.bauhaus->popup.oldpos); _popup_hide(); } @@ -2314,6 +2343,7 @@ static gboolean _popup_draw(GtkWidget *widget, const int width = gtk_widget_get_allocated_width(widget); const GtkBorder *padding = &pop->padding; const int w2 = width - padding->left - padding->right; + const int w3 = w2 - _widget_get_quad_width(w); const int h2 = pop->position.height - padding->top - padding->bottom; GtkStyleContext *context = gtk_widget_get_style_context(widget); @@ -2346,7 +2376,7 @@ static gboolean _popup_draw(GtkWidget *widget, { case DT_BAUHAUS_SLIDER: { - const dt_bauhaus_slider_data_t *d = &w->data.slider; + const dt_bauhaus_slider_data_t *d = &w->slider; cairo_save(cr); set_color(cr, *fg_color); @@ -2354,7 +2384,7 @@ static gboolean _popup_draw(GtkWidget *widget, _draw_color_wheel(w, context, cr, w2, fg_color, bg_color); else { - _draw_baseline(w, cr, w2); + _draw_baseline(w, cr, w3); cairo_save(cr); cairo_set_line_width(cr, 0.5); @@ -2367,21 +2397,21 @@ static gboolean _popup_draw(GtkWidget *widget, for(int k = 0; k < num_scales; k++) { - const float off = k * scale - d->oldpos; + const float off = k * scale - pop->oldpos; GdkRGBA fg_copy = *fg_color; fg_copy.alpha = scale / fabsf(off); set_color(cr, fg_copy); - _slider_draw_line(cr, d->oldpos, off, scale, w2, h2, ht, w); + _slider_draw_line(cr, pop->oldpos, off, scale, w3, h2, ht, w); } cairo_restore(cr); // draw mouse over indicator line cairo_save(cr); cairo_set_line_width(cr, 2.); - _slider_draw_line(cr, d->oldpos, d->pos - d->oldpos, scale, w2, h2, ht, w); + _slider_draw_line(cr, pop->oldpos, d->pos - pop->oldpos, scale, w3, h2, ht, w); cairo_restore(cr); - _draw_indicator(w, d->pos, cr, w2, *fg_color, *bg_color); + _draw_indicator(w, d->pos, cr, w3, *fg_color, *bg_color); // show min/max of range set_color(cr, text_color_insensitive); @@ -2392,8 +2422,7 @@ static gboolean _popup_draw(GtkWidget *widget, g_free(min); char *max = dt_bauhaus_slider_get_text(GTK_WIDGET(w), d->factor > 0 ? d->max : d->min); - _show_pango_text(w, context, cr, max, w2 - - _widget_get_quad_width(w), ht + INNER_PADDING, 0, + _show_pango_text(w, context, cr, max, w3, ht + INNER_PADDING, 0, TRUE, FALSE, PANGO_ELLIPSIZE_END, FALSE, FALSE, NULL, NULL); g_free(max); } @@ -2402,15 +2431,13 @@ static gboolean _popup_draw(GtkWidget *widget, char *text = dt_bauhaus_slider_get_text(GTK_WIDGET(w), dt_bauhaus_slider_get(GTK_WIDGET(w))); set_color(cr, *fg_color); - const float value_width = _show_pango_text(w, context, cr, text, w2 - - _widget_get_quad_width(w), 0, 0, + const float value_width = _show_pango_text(w, context, cr, text, w3, 0, 0, TRUE, FALSE, PANGO_ELLIPSIZE_END, FALSE, FALSE, NULL, NULL); g_free(text); - const float label_width = - w2 - _widget_get_quad_width(w) - INNER_PADDING - value_width; + const float label_width = w3 - INNER_PADDING - value_width; if(label_width > 0) { gchar *lb = _build_label(w); @@ -2423,7 +2450,7 @@ static gboolean _popup_draw(GtkWidget *widget, break; case DT_BAUHAUS_COMBOBOX: { - const dt_bauhaus_combobox_data_t *d = &w->data.combobox; + const dt_bauhaus_combobox_data_t *d = &w->combobox; cairo_save(cr); float first_label_width = 0.0; gboolean first_label = *w->label; @@ -2442,7 +2469,7 @@ static gboolean _popup_draw(GtkWidget *widget, gchar *entry_found = entry->label + (search_found - text_cmp); gchar *label = NULL; - float max_width = w2 - _widget_get_quad_width(w); + float max_width = w3; float label_width = 0.0f; if(!entry->sensitive) { @@ -2502,8 +2529,7 @@ static gboolean _popup_draw(GtkWidget *widget, if(first_label) max_width *= 0.8; // give the label at least some room label_width = _show_pango_text(w, context, cr, - label, w2 - _widget_get_quad_width(w), - ht * j + w->top_gap, max_width, + label, w3, ht * j + w->top_gap, max_width, TRUE, FALSE, ellipsis, TRUE, FALSE, NULL, NULL); } g_free(label); @@ -2525,9 +2551,7 @@ static gboolean _popup_draw(GtkWidget *widget, { set_color(cr, text_color); gchar *lb = _build_label(w); - _show_pango_text(w, context, cr, - lb, 0, w->top_gap, - w2 - _widget_get_quad_width(w) - first_label_width, + _show_pango_text(w, context, cr, lb, 0, w->top_gap, w3 - first_label_width, FALSE, FALSE, PANGO_ELLIPSIZE_END, FALSE, TRUE, NULL, NULL); g_free(lb); } @@ -2571,7 +2595,7 @@ static gboolean _popup_draw(GtkWidget *widget, pango_layout_set_text(layout, bh->keys, bh->keys_cnt); pango_layout_get_pixel_extents(layout, &ink, NULL); - cairo_move_to(cr, w2 - _widget_get_quad_width(w) - ink.width, h2 * 0.5 - size); + cairo_move_to(cr, w3 - ink.width, h2 * 0.5 - size); pango_cairo_show_layout(cr, layout); cairo_restore(cr); pango_font_description_free(desc); @@ -2583,7 +2607,7 @@ static gboolean _popup_draw(GtkWidget *widget, cairo_save(cr); set_color(cr, text_color); cairo_move_to(cr, w2 - bh->quad_width + 3, h2 * 0.5 + size / 3); - cairo_line_to(cr, w2 - bh->quad_width + 3, h2 * 0.5 - size); + cairo_rel_line_to(cr, 0, - size * 4.0 / 3.0); cairo_set_line_width(cr, 2.); cairo_stroke(cr); cairo_restore(cr); @@ -2596,14 +2620,12 @@ static gboolean _popup_draw(GtkWidget *widget, } static gboolean _widget_draw(GtkWidget *widget, - cairo_t *crf) + cairo_t *cr) { GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); const int width = allocation.width, height = allocation.height; - cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *cr = cairo_create(cst); GtkStyleContext *context = gtk_widget_get_style_context(widget); GdkRGBA *fg_color = _default_color_assign(); @@ -2621,7 +2643,7 @@ static gboolean _widget_draw(GtkWidget *widget, const int h2 = height - w->margin.top - w->margin.bottom; const int w2 = width - w->margin.left - w->margin.right; const int h3 = h2 - w->padding.top - w->padding.bottom; - const int w3 = w2 - w->padding.left - w->padding.right; + const int w3 = w2 - w->padding.left - w->padding.right - _widget_get_quad_width(w); gtk_render_background(context, cr, w->margin.left, w->margin.top, w2, h2); cairo_translate(cr, w->margin.left + w->padding.left, w->margin.top + w->padding.top); @@ -2636,7 +2658,7 @@ static gboolean _widget_draw(GtkWidget *widget, // draw label and quad area at right end set_color(cr, *text_color); - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; const PangoEllipsizeMode combo_ellipsis = d->entries_ellipsis; const gchar *text = d->text; if(d->active >= 0 && d->active < d->entries->len) @@ -2645,8 +2667,6 @@ static gboolean _widget_draw(GtkWidget *widget, } set_color(cr, *text_color); - const float available_width = w3 - _widget_get_quad_width(w); - //calculate total widths of label and combobox gchar *label_text = _build_label(w); float label_width = 0; @@ -2658,35 +2678,32 @@ static gboolean _widget_draw(GtkWidget *widget, FALSE, TRUE, &label_width, &label_height); float combo_width = 0; float combo_height = 0; - _show_pango_text(w, context, cr, text, available_width, 0, 0, + _show_pango_text(w, context, cr, text, w3, 0, 0, TRUE, TRUE, combo_ellipsis, FALSE, FALSE, &combo_width, &combo_height); // we want to center the text vertically w->top_gap = floor((h3 - fmaxf(label_height, combo_height)) / 2.0f); //check if they fit - if((label_width + combo_width) > available_width) + if((label_width + combo_width) > w3) { if(d->text_align == DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT) { // they don't fit: evenly divide the available width between the two in proportion const float ratio = label_width / (label_width + combo_width); if(w->show_label) - _show_pango_text(w, context, cr, - label_text, 0, - w->top_gap, available_width * ratio - INNER_PADDING * 2, + _show_pango_text(w, context, cr, label_text, + 0, w->top_gap, w3 * ratio - INNER_PADDING * 2, FALSE, FALSE, PANGO_ELLIPSIZE_END, FALSE, TRUE, NULL, NULL); - _show_pango_text(w, context, cr, - text, available_width, w->top_gap, - available_width * (1.0f - ratio), + _show_pango_text(w, context, cr, text, w3, w->top_gap, w3 * (1.0f - ratio), TRUE, FALSE, combo_ellipsis, FALSE, FALSE, NULL, NULL); } else if(d->text_align == DT_BAUHAUS_COMBOBOX_ALIGN_MIDDLE) { - const int posx = MAX(0, (available_width - combo_width) / 2); - _show_pango_text(w, context, cr, text, posx, w->top_gap, available_width, + const int posx = MAX(0, (w3 - combo_width) / 2); + _show_pango_text(w, context, cr, text, posx, w->top_gap, w3, FALSE, FALSE, combo_ellipsis, FALSE, FALSE, NULL, NULL); } else - _show_pango_text(w, context, cr, text, 0, w->top_gap, available_width, + _show_pango_text(w, context, cr, text, 0, w->top_gap, w3, FALSE, FALSE, combo_ellipsis, FALSE, FALSE, NULL, NULL); } else @@ -2696,12 +2713,12 @@ static gboolean _widget_draw(GtkWidget *widget, if(w->show_label) _show_pango_text(w, context, cr, label_text, 0, w->top_gap, 0, FALSE, FALSE, PANGO_ELLIPSIZE_END, FALSE, TRUE, NULL, NULL); - _show_pango_text(w, context, cr, text, available_width, w->top_gap, 0, + _show_pango_text(w, context, cr, text, w3, w->top_gap, 0, TRUE, FALSE, combo_ellipsis, FALSE, FALSE, NULL, NULL); } else if(d->text_align == DT_BAUHAUS_COMBOBOX_ALIGN_MIDDLE) { - const int posx = MAX(0, (available_width - combo_width) / 2); + const int posx = MAX(0, (w3 - combo_width) / 2); _show_pango_text(w, context, cr, text, posx, w->top_gap, 0, FALSE, FALSE, combo_ellipsis, FALSE, FALSE, NULL, NULL); } @@ -2721,9 +2738,9 @@ static gboolean _widget_draw(GtkWidget *widget, if(gtk_widget_is_sensitive(widget)) { cairo_save(cr); - cairo_rectangle(cr, 0, 0, w3 - _widget_get_quad_width(w), h3 + INNER_PADDING); + cairo_rectangle(cr, 0, 0, w3, h3 + INNER_PADDING); cairo_clip(cr); - _draw_indicator(w, w->data.slider.pos, cr, w3, *fg_color, *bg_color); + _draw_indicator(w, w->slider.pos, cr, w3, *fg_color, *bg_color); cairo_restore(cr); // TODO: merge that text with combo @@ -2731,7 +2748,7 @@ static gboolean _widget_draw(GtkWidget *widget, char *text = dt_bauhaus_slider_get_text(widget, dt_bauhaus_slider_get(widget)); set_color(cr, *text_color); value_width = _show_pango_text(w, context, cr, - text, w3 - _widget_get_quad_width(w), 0, 0, + text, w3, 0, 0, TRUE, FALSE, PANGO_ELLIPSIZE_END, FALSE, FALSE, NULL, NULL); g_free(text); @@ -2739,7 +2756,7 @@ static gboolean _widget_draw(GtkWidget *widget, // label on top of marker: gchar *label_text = _build_label(w); set_color(cr, *text_color); - const float label_width = w3 - _widget_get_quad_width(w) - value_width; + const float label_width = w3 - value_width; if(label_width > 0) _show_pango_text(w, context, cr, label_text, 0, 0, label_width, FALSE, FALSE, PANGO_ELLIPSIZE_END, FALSE, TRUE, NULL, NULL); @@ -2750,13 +2767,7 @@ static gboolean _widget_draw(GtkWidget *widget, break; } cairo_restore(cr); - cairo_destroy(cr); - cairo_set_source_surface(crf, cst, 0, 0); - cairo_paint(crf); - cairo_surface_destroy(cst); - - // render eventual css borders - gtk_render_frame(context, crf, w->margin.left, w->margin.top, w2, h2); + gtk_render_frame(context, cr, w->margin.left, w->margin.top, w2, h2); gdk_rgba_free(text_color); gdk_rgba_free(fg_color); @@ -2784,7 +2795,7 @@ static gint _natural_width(GtkWidget *widget, pango_layout_set_attributes(layout, attrlist); pango_attr_list_unref(attrlist); - if(w->show_label || popup) + if(w->label && (w->show_label || popup)) { pango_layout_set_text(layout, w->label, -1); pango_layout_get_size(layout, &natural_size, NULL); @@ -2793,7 +2804,7 @@ static gint _natural_width(GtkWidget *widget, if(w->type == DT_BAUHAUS_COMBOBOX) { - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; gint label_width = 0, entry_width = 0; @@ -2818,8 +2829,8 @@ static gint _natural_width(GtkWidget *widget, else { gint number_width = 0; - char *max = dt_bauhaus_slider_get_text(widget, w->data.slider.max); - char *min = dt_bauhaus_slider_get_text(widget, w->data.slider.min); + char *max = dt_bauhaus_slider_get_text(widget, w->slider.max); + char *min = dt_bauhaus_slider_get_text(widget, w->slider.min); char *text = strlen(max) >= strlen(min) ? max : min; pango_layout_set_text(layout, text, -1); pango_layout_get_size(layout, &number_width, NULL); @@ -2873,9 +2884,9 @@ static void _popup_hide() if(w) { if(w->type == DT_BAUHAUS_COMBOBOX - && w->data.combobox.mute_scrolling + && w->combobox.mute_scrolling && bh->change_active) - g_signal_emit(G_OBJECT(w), darktable.bauhaus->signals[DT_BAUHAUS_VALUE_CHANGED_SIGNAL], 0); + g_signal_emit(G_OBJECT(w), bh->signals[DT_BAUHAUS_VALUE_CHANGED_SIGNAL], 0); gtk_grab_remove(pop->area); gtk_widget_hide(pop->window); @@ -2977,8 +2988,8 @@ static void _popup_show(GtkWidget *widget) { case DT_BAUHAUS_SLIDER: { - dt_bauhaus_slider_data_t *d = &w->data.slider; - d->oldpos = d->pos; + dt_bauhaus_slider_data_t *d = &w->slider; + pop->oldpos = d->pos; p->height = p->width; _start_cursor(6); pop->offset = 0; @@ -2989,7 +3000,7 @@ static void _popup_show(GtkWidget *widget) { // we launch the dynamic populate fct if any dt_iop_module_t *module = (dt_iop_module_t *)(w->module); - const dt_bauhaus_combobox_data_t *d = &w->data.combobox; + const dt_bauhaus_combobox_data_t *d = &w->combobox; if(d->populate) d->populate(widget, &module); if(!d->entries->len) return; @@ -3035,7 +3046,7 @@ static void _slider_add_step(GtkWidget *widget, if(delta == 0) return; dt_bauhaus_widget_t *w = (dt_bauhaus_widget_t *)widget; - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; const float value = dt_bauhaus_slider_get(widget); @@ -3143,7 +3154,7 @@ float dt_bauhaus_slider_get(GtkWidget *widget) // first cast to bh widget, to check that type: const dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_SLIDER) return -1.0f; - const dt_bauhaus_slider_data_t *d = &w->data.slider; + const dt_bauhaus_slider_data_t *d = &w->slider; if(d->max == d->min) { return d->max; @@ -3154,14 +3165,14 @@ float dt_bauhaus_slider_get(GtkWidget *widget) float dt_bauhaus_slider_get_val(GtkWidget *widget) { - const dt_bauhaus_slider_data_t *d = &DT_BAUHAUS_WIDGET(widget)->data.slider; + const dt_bauhaus_slider_data_t *d = &DT_BAUHAUS_WIDGET(widget)->slider; return dt_bauhaus_slider_get(widget) * d->factor + d->offset; } char *dt_bauhaus_slider_get_text(GtkWidget *w, const float val) { - const dt_bauhaus_slider_data_t *d = &DT_BAUHAUS_WIDGET(w)->data.slider; + const dt_bauhaus_slider_data_t *d = &DT_BAUHAUS_WIDGET(w)->slider; if((d->hard_max * d->factor + d->offset)*(d->hard_min * d->factor + d->offset) < 0) return g_strdup_printf("%+.*f%s", d->digits, val * d->factor + d->offset, d->format); else @@ -3178,7 +3189,7 @@ void dt_bauhaus_slider_set(GtkWidget *widget, if(!w || w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; const float rpos = CLAMP(pos, d->hard_min, d->hard_max); // if this is an angle or gradient, wrap around // don't wrap yet if exactly at min or max @@ -3196,7 +3207,7 @@ void dt_bauhaus_slider_set(GtkWidget *widget, void dt_bauhaus_slider_set_val(GtkWidget *widget, const float val) { - const dt_bauhaus_slider_data_t *d = &DT_BAUHAUS_WIDGET(widget)->data.slider; + const dt_bauhaus_slider_data_t *d = &DT_BAUHAUS_WIDGET(widget)->slider; dt_bauhaus_slider_set(widget, (val - d->offset) / d->factor); } @@ -3207,9 +3218,7 @@ void dt_bauhaus_slider_set_digits(GtkWidget *widget, if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; - - d->digits = val; + w->slider.digits = val; } int dt_bauhaus_slider_get_digits(GtkWidget *widget) @@ -3218,9 +3227,7 @@ int dt_bauhaus_slider_get_digits(GtkWidget *widget) if(w->type != DT_BAUHAUS_SLIDER) return 0; - const dt_bauhaus_slider_data_t *d = &w->data.slider; - - return d->digits; + return w->slider.digits; } void dt_bauhaus_slider_set_step(GtkWidget *widget, @@ -3230,9 +3237,7 @@ void dt_bauhaus_slider_set_step(GtkWidget *widget, if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; - - d->step = val; + w->slider.step = val; } float dt_bauhaus_slider_get_step(GtkWidget *widget) @@ -3241,7 +3246,7 @@ float dt_bauhaus_slider_get_step(GtkWidget *widget) if(w->type != DT_BAUHAUS_SLIDER) return 0; - const dt_bauhaus_slider_data_t *d = &w->data.slider; + const dt_bauhaus_slider_data_t *d = &w->slider; float step = d->step; @@ -3278,9 +3283,7 @@ void dt_bauhaus_slider_set_feedback(GtkWidget *widget, if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; - - d->fill_feedback = feedback; + w->slider.fill_feedback = feedback; gtk_widget_queue_draw(widget); } @@ -3291,9 +3294,7 @@ int dt_bauhaus_slider_get_feedback(GtkWidget *widget) if(w->type != DT_BAUHAUS_SLIDER) return 0; - dt_bauhaus_slider_data_t *d = &w->data.slider; - - return d->fill_feedback; + return w->slider.fill_feedback; } void dt_bauhaus_widget_reset(GtkWidget *widget) @@ -3302,7 +3303,7 @@ void dt_bauhaus_widget_reset(GtkWidget *widget) if(w->type == DT_BAUHAUS_SLIDER) { - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; d->is_dragging = 0; d->min = d->soft_min; @@ -3311,7 +3312,7 @@ void dt_bauhaus_widget_reset(GtkWidget *widget) dt_bauhaus_slider_set(widget, d->defpos); } else - dt_bauhaus_combobox_set_from_value(widget, w->data.combobox.defpos); + dt_bauhaus_combobox_set_from_value(widget, w->combobox.defpos); return; } @@ -3321,7 +3322,7 @@ void dt_bauhaus_slider_set_format(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; d->format = g_intern_string(format); @@ -3337,9 +3338,8 @@ void dt_bauhaus_slider_set_factor(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; - d->factor = factor; - if(factor < 0) d->curve = _reverse_linear_curve; + w->slider.factor = factor; + if(factor < 0) w->slider.curve = _reverse_linear_curve; } void dt_bauhaus_slider_set_offset(GtkWidget *widget, @@ -3347,8 +3347,7 @@ void dt_bauhaus_slider_set_offset(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; - d->offset = offset; + w->slider.offset = offset; } void dt_bauhaus_slider_set_curve(GtkWidget *widget, @@ -3357,7 +3356,7 @@ void dt_bauhaus_slider_set_curve(GtkWidget *widget, { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; if(curve == NULL) curve = _default_linear_curve; d->pos = curve(d->curve(d->pos, DT_BAUHAUS_GET), DT_BAUHAUS_SET); @@ -3376,7 +3375,7 @@ static void _slider_value_change(dt_bauhaus_widget_t *w) { if(!GTK_IS_WIDGET(w)) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; if(d->is_changed && !d->timeout_handle) { if(w->field) @@ -3414,14 +3413,14 @@ static void _slider_value_change(dt_bauhaus_widget_t *w) static gboolean _slider_value_change_dragging(gpointer data) { dt_bauhaus_widget_t *w = data; - w->data.slider.timeout_handle = 0; + w->slider.timeout_handle = 0; _slider_value_change(w); return G_SOURCE_REMOVE; } static void _slider_set_normalized(dt_bauhaus_widget_t *w, float pos) { - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; float rpos = CLAMP(pos, 0.0f, 1.0f); rpos = d->curve(rpos, DT_BAUHAUS_GET); rpos = d->min + (d->max - d->min) * rpos; @@ -3466,10 +3465,10 @@ static gboolean _popup_key_press(GtkWidget *widget, { if(!bh->change_active) { - dt_bauhaus_combobox_data_t *d = &w->data.combobox; + dt_bauhaus_combobox_data_t *d = &w->combobox; if(bh->unique_match == -1) { - if(w->data.combobox.editable) + if(w->combobox.editable) g_strlcpy(d->text, bh->keys, DT_BAUHAUS_MAX_TEXT); else break; // don't hide popup @@ -3513,7 +3512,7 @@ static gboolean _popup_key_press(GtkWidget *widget, case GDK_KEY_Left: case GDK_KEY_KP_Left: if(is_combo) - _combobox_next_sensitive(w, delta, 0, w->data.combobox.mute_scrolling); + _combobox_next_sensitive(w, delta, 0, w->combobox.mute_scrolling); else _slider_add_step(GTK_WIDGET(w), delta, event->state, FALSE); break; @@ -3536,147 +3535,203 @@ static gboolean _popup_key_press(GtkWidget *widget, return TRUE; } -static gboolean _widget_button_press(GtkWidget *widget, - GdkEventButton *event) +static void _widget_button_press(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + GtkWidget *widget) { - dt_bauhaus_widget_t *w = (dt_bauhaus_widget_t *)widget; + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + dt_bauhaus_t *bh = darktable.bauhaus; _request_focus(w); gtk_widget_grab_focus(widget); - const int width = gtk_widget_get_allocated_width(widget); - const int w3 = width - w->margin.left - w->padding.left - - w->margin.right - w->padding.right; - const double ex = event->x - w->margin.left - w->padding.left; - const double ey = event->y - w->margin.top - w->padding.top; + const float width = _widget_width(w); + const float pos = (x - w->margin.left - w->padding.left) / width; - if(w->quad_paint && event->window == gtk_widget_get_window(widget) - && event->x > width - _widget_get_quad_width(w) - - w->margin.right - w->padding.right) + guint button = gtk_gesture_single_get_current_button(gesture); + + gboolean passthrough_from_histogram = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)) != widget; + if(w->quad_paint + && !passthrough_from_histogram + && pos > 1.0f + INNER_PADDING / width) { dt_bauhaus_widget_press_quad(widget); } - else if(event->button == GDK_BUTTON_PRIMARY - && event->type == GDK_2BUTTON_PRESS) + else if(button == GDK_BUTTON_PRIMARY && n_press == 2) { - if(!(dt_modifier_is(event->state, GDK_CONTROL_MASK) && w->field + if(!(dt_modifier_eq(gesture, GDK_CONTROL_MASK) && w->field && dt_gui_presets_autoapply_for_module((dt_iop_module_t *)w->module, widget))) dt_bauhaus_widget_reset(widget); // reset to default. // never called for combo, as we popup the other window under your cursor before. // (except in weird corner cases where the popup is under the -1st entry _popup_hide(); } - else if(event->button == GDK_BUTTON_SECONDARY || w->type == DT_BAUHAUS_COMBOBOX) + else if(button == GDK_BUTTON_SECONDARY || w->type == DT_BAUHAUS_COMBOBOX) { - darktable.bauhaus->opentime = event->time; - darktable.bauhaus->mouse_x = event->x; - darktable.bauhaus->mouse_y = event->y; + bh->opentime = gdk_event_get_time(gtk_get_current_event()); + bh->mouse_x = x; + bh->mouse_y = y; _popup_show(widget); } - else if(event->button == GDK_BUTTON_MIDDLE) + else if(button == GDK_BUTTON_MIDDLE) { _slider_zoom_range(w, 0); // reset zoom range to soft min/max _slider_zoom_toast(w); } else { - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_slider_data_t *d = &w->slider; d->is_dragging = -1; - if(!dt_modifier_is(event->state, 0) || event->window != gtk_widget_get_window(widget)) - darktable.bauhaus->mouse_x = ex; - else if(ey > darktable.bauhaus->line_height / 2.0f) + if(!dt_modifier_eq(gesture, 0) || passthrough_from_histogram) + bh->mouse_x = x; + else if(pos <= 1.0f && y > .5 * bh->line_height + w->margin.top + w->padding.top) { - const float r = _slider_right_pos((float)w3, w); - _slider_set_normalized(w, (ex / w3) / r); - - darktable.bauhaus->mouse_x = NAN; + bh->mouse_x = NAN; + w->slider.timeout_handle = G_MAXUINT; + _slider_set_normalized(w, pos); } - else - return FALSE; } - return TRUE; + dt_gui_claim(gesture); } -static gboolean _widget_button_release(GtkWidget *widget, - GdkEventButton *event) +static void _widget_button_release(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + GtkWidget *widget) { - dt_bauhaus_widget_t *w = (dt_bauhaus_widget_t *)widget; + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); dt_bauhaus_widget_release_quad(widget); - if(w->type != DT_BAUHAUS_SLIDER) return FALSE; + dt_gui_claim(gesture); + if(w->type != DT_BAUHAUS_SLIDER) return; - dt_bauhaus_slider_data_t *d = &w->data.slider; - if(event->button == GDK_BUTTON_PRIMARY && d->is_dragging) - { + dt_bauhaus_slider_data_t *d = &w->slider; + if(gtk_gesture_single_get_current_button(gesture) == GDK_BUTTON_PRIMARY) d->is_dragging = 0; - if(d->timeout_handle) g_source_remove(d->timeout_handle); - d->timeout_handle = 0; - _slider_set_normalized(w, d->pos); +} - return TRUE; - } - return FALSE; +static void _widget_button_stopped(GtkGestureSingle *gesture, + dt_bauhaus_widget_t *w) +{ + if(w->slider.timeout_handle == G_MAXUINT) + _slider_value_change_dragging(w); } -static gboolean _widget_motion_notify(GtkWidget *widget, - GdkEventMotion *event) +static void _widget_motion(GtkEventControllerMotion *controller, + double x, + double y, + GtkWidget *widget) { - dt_bauhaus_widget_t *w = (dt_bauhaus_widget_t *)widget; - dt_bauhaus_slider_data_t *d = &w->data.slider; + dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); + dt_bauhaus_slider_data_t *d = &w->slider; + dt_bauhaus_t *bh = darktable.bauhaus; - const int width = gdk_window_get_width(event->window); - const int w3 = width - w->margin.left - w->padding.left - - w->margin.right - w->padding.right; - const double ex = event->x - w->margin.left - w->padding.left; + const float width = _widget_width(w); + const float pos = (x - w->margin.left - w->padding.left) / width; if(w->type == DT_BAUHAUS_COMBOBOX) { darktable.control->element = - event->x <= width - _widget_get_quad_width(w) || !w->quad_paint - ? DT_ACTION_ELEMENT_SELECTION : DT_ACTION_ELEMENT_BUTTON; + pos > 1.0f && w->quad_paint + ? DT_ACTION_ELEMENT_BUTTON : DT_ACTION_ELEMENT_SELECTION; } - else if(d->is_dragging && event->state & GDK_BUTTON1_MASK) + else if(d->is_dragging) { - const float r = _slider_right_pos((float)w3, w); - - if(dt_isnan(darktable.bauhaus->mouse_x)) + if(dt_isnan(bh->mouse_x)) { - if(dt_modifier_is(event->state, 0)) - _slider_set_normalized(w, (ex / w3) / r); + if(dt_modifier_eq(controller, 0)) + _slider_set_normalized(w, pos); else - darktable.bauhaus->mouse_x = ex; + bh->mouse_x = x; } else { const float scaled_step = - w3 * r * dt_bauhaus_slider_get_step(widget) / (d->max - d->min); - const float steps = floorf((ex - darktable.bauhaus->mouse_x) / scaled_step); + width * dt_bauhaus_slider_get_step(widget) / (d->max - d->min); + const float steps = floorf((x - bh->mouse_x) / scaled_step); + GdkEventMotion *event = (GdkEventMotion *)gtk_get_current_event(); // TODO cleanup _slider_add_step(widget, copysignf(1, d->factor) * steps, event->state, FALSE); - darktable.bauhaus->mouse_x += steps * scaled_step; + bh->mouse_x += steps * scaled_step; } darktable.control->element = DT_ACTION_ELEMENT_VALUE; } - else if(ex <= w3 - _widget_get_quad_width(w)) + else { - darktable.control->element - = ex > (0.1 * (w3 - _widget_get_quad_width(w))) - && ex < (0.9 * (w3 - _widget_get_quad_width(w))) - ? DT_ACTION_ELEMENT_VALUE : DT_ACTION_ELEMENT_FORCE; + darktable.control->element = pos > 1.0f && w->quad_paint ? DT_ACTION_ELEMENT_BUTTON + : pos > 0.1f && pos < 0.9f ? DT_ACTION_ELEMENT_VALUE + : DT_ACTION_ELEMENT_FORCE; } - else - darktable.control->element = - w->quad_paint ? DT_ACTION_ELEMENT_BUTTON : DT_ACTION_ELEMENT_VALUE; gtk_widget_queue_draw(widget); - return TRUE; +} + +static void dt_bh_class_init(DtBauhausWidgetClass *class) +{ + dt_bauhaus_t *bh = darktable.bauhaus; + bh->signals[DT_BAUHAUS_VALUE_CHANGED_SIGNAL] + = g_signal_new("value-changed", G_TYPE_FROM_CLASS(class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + bh->signals[DT_BAUHAUS_QUAD_PRESSED_SIGNAL] + = g_signal_new("quad-pressed", G_TYPE_FROM_CLASS(class), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class); + widget_class->draw = _widget_draw; + // widget_class->snapshot = _widget_snapshot; + widget_class->scroll_event = _widget_scroll; + widget_class->key_press_event = _widget_key_press; + widget_class->get_preferred_width = _widget_get_preferred_width; + widget_class->get_preferred_height = _widget_get_preferred_height; + // widget_class->measure = _widget_measure; + G_OBJECT_CLASS(class)->finalize = _widget_finalize; + + // for histogram -> exposure proxy + bh->press = _widget_button_press; + bh->release = _widget_button_release; + bh->motion = _widget_motion; + bh->scroll = _widget_scroll; +} + +static void dt_bh_init(DtBauhausWidget *w) +{ + w->field = NULL; + w->label = NULL; + w->section = NULL; + + // no quad icon and no toggle button: + w->quad_paint = 0; + w->quad_paint_data = NULL; + w->quad_toggle = 0; + w->show_quad = TRUE; + w->show_label = TRUE; + + GtkWidget *widget = GTK_WIDGET(w); + gtk_widget_add_events(widget, GDK_FOCUS_CHANGE_MASK | darktable.gui->scroll_mask); + + GtkGestureSingle *gesture = dt_gui_connect_click(w, _widget_button_press, _widget_button_release, widget); + gtk_gesture_single_set_button(gesture, 0); + g_signal_connect(gesture, "stopped", G_CALLBACK(_widget_button_stopped), w); + + dt_gui_connect_motion(w, _widget_motion, _widget_enter, _widget_leave, widget); + + // GtkEventController *controller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE); + // gtk_widget_add_controller(GTK_WIDGET(w), GTK_EVENT_CONTROLLER(controller)); + // g_signal_connect(controller, "scroll", G_CALLBACK(_widget_scroll), w); + + gtk_widget_set_can_focus(widget, TRUE); + dt_gui_add_class(widget, "dt_bauhaus"); } void dt_bauhaus_combobox_mute_scrolling(GtkWidget *widget) { dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_combobox_data_t *d = &w->data.combobox; - d->mute_scrolling = TRUE; + w->combobox.mute_scrolling = TRUE; } static void _action_process_button(GtkWidget *widget, @@ -3705,7 +3760,7 @@ static float _action_process_slider(gpointer target, { GtkWidget *widget = GTK_WIDGET(target); dt_bauhaus_widget_t *bhw = DT_BAUHAUS_WIDGET(widget); - dt_bauhaus_slider_data_t *d = &bhw->data.slider; + dt_bauhaus_slider_data_t *d = &bhw->slider; if(DT_PERFORM_ACTION(move_size)) { @@ -3811,7 +3866,7 @@ static float _action_process_slider(gpointer target, static gboolean _combobox_idle_value_changed(gpointer widget) { dt_bauhaus_widget_t *w = (dt_bauhaus_widget_t *)widget; - _combobox_set(w, w->data.combobox.active, FALSE); + _combobox_set(w, w->combobox.active, FALSE); while(g_idle_remove_by_data(widget)); @@ -3828,7 +3883,7 @@ static float _action_process_combo(gpointer target, if(DT_PERFORM_ACTION(move_size)) { - if(element == DT_ACTION_ELEMENT_BUTTON || !w->data.combobox.entries->len) + if(element == DT_ACTION_ELEMENT_BUTTON || !w->combobox.entries->len) { _action_process_button(widget, effect); return dt_bauhaus_widget_get_quad_active(widget); @@ -3869,7 +3924,7 @@ static float _action_process_combo(gpointer target, dt_action_widget_toast(w->module, widget, "\n%s", dt_bauhaus_combobox_get_text(widget)); } - if(element == DT_ACTION_ELEMENT_BUTTON || !w->data.combobox.entries->len) + if(element == DT_ACTION_ELEMENT_BUTTON || !w->combobox.entries->len) return dt_bauhaus_widget_get_quad_active(widget); if(effect == DT_ACTION_EFFECT_RESET) @@ -3879,7 +3934,7 @@ static float _action_process_combo(gpointer target, int value = dt_bauhaus_combobox_get(widget); for(int i = value; i >= 0; i--) { - if(!_combobox_entry(&w->data.combobox, i)->sensitive) value--; // don't count unselectable combo items in value + if(!_combobox_entry(&w->combobox, i)->sensitive) value--; // don't count unselectable combo items in value } return - 1 - value + (value == effect - DT_ACTION_EFFECT_COMBO_SEPARATOR - 1 diff --git a/src/bauhaus/bauhaus.h b/src/bauhaus/bauhaus.h index 42327d8b6934..63af5bfc340f 100644 --- a/src/bauhaus/bauhaus.h +++ b/src/bauhaus/bauhaus.h @@ -44,6 +44,7 @@ G_DECLARE_FINAL_TYPE(DtBauhausWidget, dt_bh, DT, BAUHAUS_WIDGET, GtkDrawingArea) typedef enum dt_bauhaus_type_t { + DT_BAUHAUS_INVALID = -1, DT_BAUHAUS_BUTTON = 0, DT_BAUHAUS_SLIDER = 1, DT_BAUHAUS_COMBOBOX = 2, @@ -56,34 +57,6 @@ typedef enum dt_bauhaus_curve_t DT_BAUHAUS_GET = 2 } dt_bauhaus_curve_t; -// data portion for a slider -typedef struct dt_bauhaus_slider_data_t -{ - float pos; // normalized slider value - float oldpos; // slider value before entering finetune mode (normalized) - float step; // step width (not normalized) - float defpos; // default value (not normalized) - float min, max; // min and max range - float soft_min, soft_max; - float hard_min, hard_max; - int digits; // how many decimals to round to - - float (*grad_col)[3]; // colors for gradient slider - int grad_cnt; // how many stops - float *grad_pos; // and position of these. - - int fill_feedback : 1; // fill the slider with brighter part up to the handle? - - const char *format; // numeric value is printed with this format - float factor; // multiplication factor before printing - float offset; // addition before printing - - int is_dragging : 1; // indicates is mouse is dragging slider - int is_changed : 1; // indicates new data - guint timeout_handle; // used to store id of timeout routine - float (*curve)(float, dt_bauhaus_curve_t); // callback function -} dt_bauhaus_slider_data_t; - typedef enum dt_bauhaus_combobox_alignment_t { DT_BAUHAUS_COMBOBOX_ALIGN_LEFT = 0, @@ -92,94 +65,18 @@ typedef enum dt_bauhaus_combobox_alignment_t } dt_bauhaus_combobox_alignment_t; // data portion for a combobox -typedef struct dt_bauhaus_combobox_entry_t -{ - char *label; - dt_bauhaus_combobox_alignment_t alignment; - gboolean sensitive; - gpointer data; - void (*free_func)(gpointer); // callback to free data elements -} dt_bauhaus_combobox_entry_t; - typedef void (*dt_bauhaus_combobox_populate_fct) - (GtkWidget *w, + (GtkWidget *widget, struct dt_iop_module_t **module); typedef int (*dt_bauhaus_combobox_entry_select_fct) - (GtkWidget *w, + (GtkWidget *widget, const char *entry, const int delta, struct dt_iop_module_t **module); -typedef struct dt_bauhaus_combobox_data_t -{ - int active; // currently active element - int defpos; // default position - int editable; // 1 if arbitrary text may be typed - dt_bauhaus_combobox_alignment_t text_align; // if selected text in combo should be aligned to the left/right - char *text; // to hold arbitrary text if editable - PangoEllipsizeMode entries_ellipsis; - GPtrArray *entries; - gboolean mute_scrolling; // if set, prevents to issue "data-changed" - dt_bauhaus_combobox_populate_fct populate; // function to populate the combo list on the fly - dt_bauhaus_combobox_entry_select_fct entry_select; // function to select an entry based on context -} dt_bauhaus_combobox_data_t; - -typedef union dt_bauhaus_data_t -{ - // this is the placeholder for the data portions - // associated with the implementations such as - // sliders, combo boxes, .. - dt_bauhaus_slider_data_t slider; - dt_bauhaus_combobox_data_t combobox; -} dt_bauhaus_data_t; - typedef DTGTKCairoPaintIconFunc dt_bauhaus_quad_paint_f; -// our new widget and its private members, inheriting from drawing area: -typedef struct _DtBauhausWidget -{ - // gtk base widget - GtkDrawingArea parent; - // which type of control - dt_bauhaus_type_t type; - // associated image operation module (to handle focus and such) - dt_action_t *module; - // pointer to iop field linked to widget - gpointer field; - // type of field - dt_introspection_type_t field_type; - - // label text, short - char label[256]; - gboolean show_label; - // section, short - gchar *section; - gboolean show_extended_label; - // callback function to draw the quad icon - dt_bauhaus_quad_paint_f quad_paint; - // tooltip to show when mouse is over the quad section - gchar *tooltip; - // minimal modifiers for paint function. - int quad_paint_flags; - // data for the paint callback - void *quad_paint_data; - // quad is a toggle button? - int quad_toggle; - // show quad icon or space - gboolean show_quad; - // if a section label - gboolean is_section; - - // margin and padding structure, defined in css, retrieve on each draw - GtkBorder margin, padding; - // gap to add to the top padding due to the vertical centering - int top_gap; - - // goes last, might extend past the end: - dt_bauhaus_data_t data; -} dt_bauhaus_widget_t; - // global static data: enum { @@ -188,6 +85,8 @@ enum DT_BAUHAUS_LAST_SIGNAL }; +typedef struct _DtBauhausWidget dt_bauhaus_widget_t; + typedef enum dt_bauhaus_marker_shape_t { DT_BAUHAUS_MARKER_TRIANGLE, @@ -204,6 +103,7 @@ typedef struct dt_bauhaus_popup_t GdkRectangle position; int offset; int offcut; + float oldpos; // slider value before entering finetune mode (normalized) } dt_bauhaus_popup_t; typedef struct dt_bauhaus_t @@ -241,7 +141,6 @@ typedef struct dt_bauhaus_t float border_width; // width of the border of the slider marker float quad_width; // width of the quad area to paint icons PangoFontDescription *pango_font_desc; // no need to recreate this for every string we want to print - PangoFontDescription *pango_sec_font_desc; // as above but for section labels // the slider popup has a blinking cursor guint cursor_timeout; @@ -255,6 +154,12 @@ typedef struct dt_bauhaus_t GdkRGBA graph_bg, graph_exterior, graph_border, graph_fg, graph_grid, graph_fg_active, graph_overlay, inset_histogram; GdkRGBA graph_colors[3]; // primaries GdkRGBA colorlabels[DT_COLORLABELS_LAST]; + + // for use by histogram -> exposure proxy + void (*press)(GtkGestureSingle*, int, double, double, GtkWidget*); + void (*release)(GtkGestureSingle*, int, double, double, GtkWidget*); + void (*motion)(GtkEventControllerMotion*, double, double, GtkWidget*); + gboolean (*scroll)(GtkWidget*, GdkEventScroll*); } dt_bauhaus_t; #define DT_BAUHAUS_SPACE 0 @@ -265,58 +170,63 @@ void dt_bauhaus_cleanup(); // load theme colors, fonts, etc void dt_bauhaus_load_theme(); -// set the bauhaus widget as a module section and in this case the font used will be the one -// from the CSS section_label. -void dt_bauhaus_widget_set_section(GtkWidget *w, - const gboolean is_section); - // common functions: +dt_bauhaus_type_t dt_bauhaus_widget_get_type(GtkWidget *widget); // set the label text: -dt_action_t *dt_bauhaus_widget_set_label(GtkWidget *w, +dt_action_t *dt_bauhaus_widget_set_label(GtkWidget *widget, const char *section, const char *label); -const char* dt_bauhaus_widget_get_label(GtkWidget *w); -void dt_bauhaus_widget_hide_label(GtkWidget *w); +const char* dt_bauhaus_widget_get_label(GtkWidget *widget); +void dt_bauhaus_widget_hide_label(GtkWidget *widget); +void dt_bauhaus_widget_set_show_extended_label(GtkWidget *widget, + gboolean show); +void dt_bauhaus_widget_set_module(GtkWidget *widget, + dt_action_t *module); +gpointer dt_bauhaus_widget_get_module(GtkWidget *widget); // attach a custom painted quad to the space at the right side (overwriting the default icon if any): -void dt_bauhaus_widget_set_quad_paint(GtkWidget *w, +void dt_bauhaus_widget_set_quad_paint(GtkWidget *widget, dt_bauhaus_quad_paint_f f, const int paint_flags, void *paint_data); // make this quad a toggle button: -void dt_bauhaus_widget_set_quad_toggle(GtkWidget *w, int toggle); +void dt_bauhaus_widget_set_quad_toggle(GtkWidget *widget, + gboolean toggle); // set active status for the quad toggle button: -void dt_bauhaus_widget_set_quad_active(GtkWidget *w, int active); +void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, + gboolean active); // get active status for the quad toggle button: -int dt_bauhaus_widget_get_quad_active(GtkWidget *w); +int dt_bauhaus_widget_get_quad_active(GtkWidget *widget); // set quad visibility: -void dt_bauhaus_widget_set_quad_visibility(GtkWidget *w, +void dt_bauhaus_widget_set_quad_visibility(GtkWidget *widget, const gboolean visible); // set a tooltip for the quad button: -void dt_bauhaus_widget_set_quad_tooltip(GtkWidget *w, +void dt_bauhaus_widget_set_quad_tooltip(GtkWidget *widget, const gchar *text); // helper macro to set all quad properties at once -static inline void dt_bauhaus_widget_set_quad(GtkWidget *w, +static inline void dt_bauhaus_widget_set_quad(GtkWidget *widget, dt_iop_module_t *self, dt_bauhaus_quad_paint_f paint, - int toggle, + gboolean toggle, void (*callback)(GtkWidget *a, dt_iop_module_t *b), const gchar *tooltip) { - dt_bauhaus_widget_set_quad_paint(w, paint, 0, NULL); - dt_bauhaus_widget_set_quad_toggle(w, toggle); - g_signal_connect(G_OBJECT(w), "quad-pressed", G_CALLBACK(callback), self); - if(tooltip) dt_bauhaus_widget_set_quad_tooltip(w, tooltip); + dt_bauhaus_widget_set_quad_paint(widget, paint, 0, NULL); + dt_bauhaus_widget_set_quad_toggle(widget, toggle); + g_signal_connect(G_OBJECT(widget), "quad-pressed", G_CALLBACK(callback), self); + if(tooltip) dt_bauhaus_widget_set_quad_tooltip(widget, tooltip); } // get the tooltip for widget or quad button: gchar *dt_bauhaus_widget_get_tooltip_markup(GtkWidget *widget, - dt_action_element_t element); + const int x, + const int y); // set pointer to iop params field: -void dt_bauhaus_widget_set_field(GtkWidget *w, +void dt_bauhaus_widget_set_field(GtkWidget *widget, gpointer field, dt_introspection_type_t field_type); +gpointer dt_bauhaus_widget_get_field(GtkWidget *widget); // update one bauhaus widget or all widgets in a module from the provided (blend)params void dt_bauhaus_update_from_field(dt_iop_module_t *module, - GtkWidget *w, + GtkWidget *widget, gpointer params, gpointer blend_params); // reset widget to default value @@ -353,46 +263,57 @@ GtkWidget *dt_bauhaus_slider_new_action(dt_action_t *self, int digits); // outside doesn't see the real type, we cast it internally. -void dt_bauhaus_slider_set(GtkWidget *w, float pos); -void dt_bauhaus_slider_set_val(GtkWidget *w, float val); -float dt_bauhaus_slider_get(GtkWidget *w); -float dt_bauhaus_slider_get_val(GtkWidget *w); -char *dt_bauhaus_slider_get_text(GtkWidget *w, float val); - -void dt_bauhaus_slider_set_soft_min(GtkWidget* w, float val); -float dt_bauhaus_slider_get_soft_min(GtkWidget* w); -void dt_bauhaus_slider_set_soft_max(GtkWidget* w, float val); -float dt_bauhaus_slider_get_soft_max(GtkWidget* w); +void dt_bauhaus_slider_set(GtkWidget *widget, + float pos); +void dt_bauhaus_slider_set_val(GtkWidget *widget, + float val); +float dt_bauhaus_slider_get(GtkWidget *widget); +float dt_bauhaus_slider_get_val(GtkWidget *widget); +char *dt_bauhaus_slider_get_text(GtkWidget *widget, + float val); + +void dt_bauhaus_slider_set_soft_min(GtkWidget* widget, + float val); +float dt_bauhaus_slider_get_soft_min(GtkWidget* widget); +void dt_bauhaus_slider_set_soft_max(GtkWidget* widget, + float val); +float dt_bauhaus_slider_get_soft_max(GtkWidget* widget); void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, const float soft_min, const float soft_max); -void dt_bauhaus_slider_set_hard_min(GtkWidget* w, +void dt_bauhaus_slider_set_hard_min(GtkWidget* widget, const float val); -float dt_bauhaus_slider_get_hard_min(GtkWidget* w); -void dt_bauhaus_slider_set_hard_max(GtkWidget* w, +float dt_bauhaus_slider_get_hard_min(GtkWidget* widget); +void dt_bauhaus_slider_set_hard_max(GtkWidget* widget, const float val); -float dt_bauhaus_slider_get_hard_max(GtkWidget* w); +float dt_bauhaus_slider_get_hard_max(GtkWidget* widget); -void dt_bauhaus_slider_set_digits(GtkWidget *w, int val); -int dt_bauhaus_slider_get_digits(GtkWidget *w); -void dt_bauhaus_slider_set_step(GtkWidget *w, float val); -float dt_bauhaus_slider_get_step(GtkWidget *w); +void dt_bauhaus_slider_set_digits(GtkWidget *widget, + int val); +int dt_bauhaus_slider_get_digits(GtkWidget *widget); +void dt_bauhaus_slider_set_step(GtkWidget *widget, + float val); +float dt_bauhaus_slider_get_step(GtkWidget *widget); -void dt_bauhaus_slider_set_feedback(GtkWidget *w, +void dt_bauhaus_slider_set_feedback(GtkWidget *widget, const int feedback); -int dt_bauhaus_slider_get_feedback(GtkWidget *w); - -void dt_bauhaus_slider_set_format(GtkWidget *w, const char *format); -void dt_bauhaus_slider_set_factor(GtkWidget *w, float factor); -void dt_bauhaus_slider_set_offset(GtkWidget *w, float offset); +int dt_bauhaus_slider_get_feedback(GtkWidget *widget); + +void dt_bauhaus_slider_set_format(GtkWidget *widget, + const char *format); +void dt_bauhaus_slider_set_factor(GtkWidget *widget, + float factor); +void dt_bauhaus_slider_set_offset(GtkWidget *widget, + float offset); void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b); void dt_bauhaus_slider_clear_stops(GtkWidget *widget); -void dt_bauhaus_slider_set_default(GtkWidget *widget, float def); +void dt_bauhaus_slider_set_default(GtkWidget *widget, + float def); float dt_bauhaus_slider_get_default(GtkWidget *widget); void dt_bauhaus_slider_set_curve(GtkWidget *widget, float (*curve)(float value, dt_bauhaus_curve_t dir)); @@ -434,10 +355,11 @@ void dt_bauhaus_combobox_add_full(GtkWidget *widget, gboolean dt_bauhaus_combobox_set_entry_label(GtkWidget *widget, const int pos, const gchar *label); -void dt_bauhaus_combobox_set(GtkWidget *w, int pos); -gboolean dt_bauhaus_combobox_set_from_text(GtkWidget *w, +void dt_bauhaus_combobox_set(GtkWidget *widget, + int pos); +gboolean dt_bauhaus_combobox_set_from_text(GtkWidget *widget, const char *text); -gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *w, +gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, const int value); int dt_bauhaus_combobox_get_from_value(GtkWidget *widget, const int value); @@ -453,20 +375,20 @@ void dt_bauhaus_combobox_insert_full(GtkWidget *widget, void (*free_func)(void *data), const int pos); int dt_bauhaus_combobox_length(GtkWidget *widget); -void dt_bauhaus_combobox_set_editable(GtkWidget *w, +void dt_bauhaus_combobox_set_editable(GtkWidget *widget, const int editable); void dt_bauhaus_combobox_set_selected_text_align (GtkWidget *widget, const dt_bauhaus_combobox_alignment_t text_align); -int dt_bauhaus_combobox_get_editable(GtkWidget *w); -const char *dt_bauhaus_combobox_get_text(GtkWidget *w); -void dt_bauhaus_combobox_set_text(GtkWidget *w, +int dt_bauhaus_combobox_get_editable(GtkWidget *widget); +const char *dt_bauhaus_combobox_get_text(GtkWidget *widget); +void dt_bauhaus_combobox_set_text(GtkWidget *widget, const char *text); -int dt_bauhaus_combobox_get(GtkWidget *w); -const char *dt_bauhaus_combobox_get_entry(GtkWidget *w, +int dt_bauhaus_combobox_get(GtkWidget *widget); +const char *dt_bauhaus_combobox_get_entry(GtkWidget *widget, int pos); gpointer dt_bauhaus_combobox_get_data(GtkWidget *widget); -void dt_bauhaus_combobox_clear(GtkWidget *w); +void dt_bauhaus_combobox_clear(GtkWidget *widget); void dt_bauhaus_combobox_set_default(GtkWidget *widget, int def); int dt_bauhaus_combobox_get_default(GtkWidget *widget); diff --git a/src/develop/blend_gui.c b/src/develop/blend_gui.c index 8f8afb8717c1..e21ad170a7cf 100644 --- a/src/develop/blend_gui.c +++ b/src/develop/blend_gui.c @@ -2766,7 +2766,6 @@ void dt_iop_gui_init_masks(GtkWidget *blendw, dt_iop_module_t *module) bd->masks_combo = dt_bauhaus_combobox_new(module); dt_bauhaus_widget_set_label(bd->masks_combo, N_("blend"), N_("drawn mask")); - dt_bauhaus_widget_set_section(bd->masks_combo, TRUE); dt_bauhaus_combobox_add(bd->masks_combo, _("no mask used")); g_signal_connect(G_OBJECT(bd->masks_combo), "value-changed", diff --git a/src/develop/develop.c b/src/develop/develop.c index 51fba58feac2..85e44e54a486 100644 --- a/src/develop/develop.c +++ b/src/develop/develop.c @@ -2957,10 +2957,10 @@ float dt_dev_exposure_get_black(dt_develop_t *dev) return instance && instance->get_black && instance->module->enabled ? instance->get_black(instance->module) : 0.0f; } -void dt_dev_exposure_handle_event(GdkEvent *event, const gboolean blackwhite) +void dt_dev_exposure_handle_event(gpointer controller, int n_press, gdouble x, const gboolean blackwhite) { if(darktable.develop->proxy.exposure.handle_event) - darktable.develop->proxy.exposure.handle_event(event, blackwhite); + darktable.develop->proxy.exposure.handle_event(controller, n_press, x, blackwhite); } void dt_dev_modulegroups_set(dt_develop_t *dev, diff --git a/src/develop/develop.h b/src/develop/develop.h index 24560a24bfb6..c2f75f823e52 100644 --- a/src/develop/develop.h +++ b/src/develop/develop.h @@ -102,7 +102,7 @@ typedef struct dt_dev_proxy_exposure_t struct dt_iop_module_t *module; float (*get_exposure)(struct dt_iop_module_t *exp); float (*get_black)(struct dt_iop_module_t *exp); - void (*handle_event)(GdkEvent *event, gboolean blackwhite); + void (*handle_event)(gpointer, int, gdouble, const gboolean); } dt_dev_proxy_exposure_t; struct dt_dev_pixelpipe_t; @@ -471,7 +471,7 @@ float dt_dev_exposure_get_exposure(dt_develop_t *dev); /** get exposure black level */ float dt_dev_exposure_get_black(dt_develop_t *dev); -void dt_dev_exposure_handle_event(GdkEvent *event, gboolean blackwhite); +void dt_dev_exposure_handle_event(gpointer controller, int n_press, gdouble x, const gboolean blackwhite); /* * modulegroups plugin hooks diff --git a/src/gui/accelerators.c b/src/gui/accelerators.c index c6bb7eae22b3..a1903b3e4053 100644 --- a/src/gui/accelerators.c +++ b/src/gui/accelerators.c @@ -919,7 +919,7 @@ static gchar *_shortcut_lua_command(GtkWidget *widget, if(DT_IS_BAUHAUS_WIDGET(widget) && s->element == DT_ACTION_ELEMENT_DEFAULT) { - if(DT_BAUHAUS_WIDGET(widget)->type == DT_BAUHAUS_COMBOBOX) + if(dt_bauhaus_widget_get_type(widget) == DT_BAUHAUS_COMBOBOX) { int value = GPOINTER_TO_INT(dt_bauhaus_combobox_get_data(widget)); dt_introspection_type_enum_tuple_t *values @@ -1034,8 +1034,7 @@ gboolean dt_shortcut_tooltip_callback(GtkWidget *widget, int show_element = 0; dt_shortcut_t lua_shortcut = { .speed = 1.0 }; - gchar *original_markup = - dt_bauhaus_widget_get_tooltip_markup(widget, darktable.control->element); + gchar *original_markup = dt_bauhaus_widget_get_tooltip_markup(widget, x, y); const gchar *widget_name = gtk_widget_get_name(widget); if(!strcmp(widget_name, "actions_view") || !strcmp(widget_name, "shortcuts_view")) @@ -1129,23 +1128,6 @@ gboolean dt_shortcut_tooltip_callback(GtkWidget *widget, _("scroll to change default speed"), _("right-click to exit mapping mode")); } - else if(DT_IS_BAUHAUS_WIDGET(widget) - && DT_BAUHAUS_WIDGET(widget)->type == DT_BAUHAUS_SLIDER - && darktable.control->element == 2) // DT_ACTION_ELEMENT_FORCE - { - float hard_min = dt_bauhaus_slider_get_hard_min(widget); - float hard_max = dt_bauhaus_slider_get_hard_max(widget); - if(dt_bauhaus_slider_get_soft_min(widget) != hard_min || - dt_bauhaus_slider_get_soft_max(widget) != hard_max) - { - dt_util_str_cat(&original_markup, - _("%sright-click to type a specific value between %s and %s" - "\nor hold ctrl+shift while dragging to ignore soft limits."), - original_markup ? "\n\n" : "", - dt_bauhaus_slider_get_text(widget, hard_min), - dt_bauhaus_slider_get_text(widget, hard_max)); - } - } } if(!def) def = _action_find_definition(action); diff --git a/src/gui/gtk.c b/src/gui/gtk.c index f2eb08a9090f..586e2d3c4b5d 100644 --- a/src/gui/gtk.c +++ b/src/gui/gtk.c @@ -3595,28 +3595,26 @@ static void _notebook_size_callback(GtkNotebook *notebook, // GTK_STATE_FLAG_PRELIGHT does not seem to get set on the label on // hover so state-flags-changed cannot update // darktable.control->element for shortcut mapping -static gboolean _notebook_motion_notify_callback(GtkWidget *widget, - const GdkEventMotion *event, - gpointer user_data) +static void _notebook_motion_notify_callback(GtkEventControllerMotion *controller, + double x, + double y, + GtkNotebook *notebook) { GtkAllocation notebook_alloc, label_alloc; - gtk_widget_get_allocation(widget, ¬ebook_alloc); + gtk_widget_get_allocation(GTK_WIDGET(notebook), ¬ebook_alloc); - GtkNotebook *notebook = GTK_NOTEBOOK(widget); const int n = gtk_notebook_get_n_pages(notebook); for(int i = 0; i < n; i++) { gtk_widget_get_allocation(gtk_notebook_get_tab_label (notebook, gtk_notebook_get_nth_page(notebook, i)), &label_alloc); - if(event->x + notebook_alloc.x < label_alloc.x + label_alloc.width) + if(x + notebook_alloc.x < label_alloc.x + label_alloc.width) { darktable.control->element = i; break; } } - - return FALSE; } static float _action_process_tabs(const gpointer target, @@ -3739,7 +3737,7 @@ static gboolean _notebook_button_press_callback(GtkNotebook *notebook, const GdkEventButton *event, gpointer user_data) { - if(event->type == GDK_2BUTTON_PRESS) + if(event->type == GDK_2BUTTON_PRESS && gtk_get_event_widget((GdkEvent*)event) == GTK_WIDGET(notebook)) _reset_all_bauhaus(notebook, gtk_notebook_get_nth_page(notebook, gtk_notebook_get_current_page(notebook))); return FALSE; @@ -3770,8 +3768,7 @@ GtkWidget *dt_ui_notebook_page(GtkNotebook *notebook, { g_signal_connect(G_OBJECT(notebook), "size-allocate", G_CALLBACK(_notebook_size_callback), NULL); - g_signal_connect(G_OBJECT(notebook), "motion-notify-event", - G_CALLBACK(_notebook_motion_notify_callback), NULL); + dt_gui_connect_motion(notebook, _notebook_motion_notify_callback, NULL, NULL, notebook); g_signal_connect(G_OBJECT(notebook), "scroll-event", G_CALLBACK(_notebook_scroll_callback), NULL); g_signal_connect(G_OBJECT(notebook), "button-press-event", @@ -4396,6 +4393,43 @@ gboolean dt_gui_long_click(const guint second, return second - delay > first; } +GtkGestureSingle *(dt_gui_connect_click)(GtkWidget *widget, + GCallback pressed, + GCallback released, + gpointer data) +{ + GtkGesture *gesture = gtk_gesture_multi_press_new(widget); + g_object_weak_ref(G_OBJECT (widget), (GWeakNotify) g_object_unref, gesture); + // GTK4 GtkGesture *gesture = gtk_gesture_click_new(); + // gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(gesture)); + + if(pressed) g_signal_connect(gesture, "pressed", pressed, data); + if(released) g_signal_connect(gesture, "released", released, data); + + return (GtkGestureSingle *)gesture; +} + +GtkEventController *(dt_gui_connect_motion)(GtkWidget *widget, + GCallback motion, + GCallback enter, + GCallback leave, + gpointer data) +{ + GtkEventController *controller = gtk_event_controller_motion_new(widget); + gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_TARGET); + g_object_weak_ref(G_OBJECT (widget), (GWeakNotify) g_object_unref, controller); + // GTK4 gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(controller)); + + gtk_widget_add_events(widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); // still needed for now by _main_do_event_keymap + + if(motion) g_signal_connect(controller, "motion", motion, data); + if(enter) g_signal_connect(controller, "enter", enter, data); + if(leave) g_signal_connect(controller, "leave", leave, data); + + return controller; +} + + static int busy_nest_count = 0; static GdkCursor* busy_prev_cursor = NULL; @@ -4498,7 +4532,7 @@ GtkWidget *(dt_gui_box_add)(const char *file, const int line, const char *functi else if(gtk_widget_get_parent(*list)) dt_print(DT_DEBUG_ALWAYS, "%s:%d %s: trying to add widget that already has a parent to box (#%d)", file, line, function, i); else - gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(*list)); + gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(*list)); // GTK4 gtk_box_append } return GTK_WIDGET(box); diff --git a/src/gui/gtk.h b/src/gui/gtk.h index 0b50b2b7d9ea..9fa8614ea380 100644 --- a/src/gui/gtk.h +++ b/src/gui/gtk.h @@ -533,6 +533,37 @@ void dt_gui_hide_collapsible_section(const dt_gui_collapsible_section_t *cs); gboolean dt_gui_long_click(const guint second, const guint first); +#define ASSERT_FUNC_TYPE(func, expected_type) (void)(1 ? (func) : (expected_type)0) + +GtkGestureSingle *(dt_gui_connect_click)(GtkWidget *widget, + GCallback pressed, + GCallback released, + gpointer data); +#define dt_gui_connect_click(widget, pressed, released, data) ( \ + ASSERT_FUNC_TYPE(pressed, void(*)(GtkGestureSingle *, int, double, double, __typeof__(data))), \ + ASSERT_FUNC_TYPE(released, void(*)(GtkGestureSingle *, int, double, double, __typeof__(data))), \ + dt_gui_connect_click(GTK_WIDGET(widget), G_CALLBACK(pressed), G_CALLBACK(released), (data))) +#define dt_gui_connect_click_all(widget, pressed, released, data) \ + gtk_gesture_single_set_button(dt_gui_connect_click(widget, pressed, released, data), 0) + +#define dt_gui_claim(gesture) \ + gtk_gesture_set_state(GTK_GESTURE(gesture), GTK_EVENT_SEQUENCE_CLAIMED) + +GtkEventController *(dt_gui_connect_motion)(GtkWidget *widget, + GCallback motion, + GCallback enter, + GCallback leave, + gpointer data); +#define dt_gui_connect_motion(widget, motion, enter, leave, data) ( \ + ASSERT_FUNC_TYPE(motion, void(*)(GtkEventControllerMotion *, double, double, __typeof__(data))), \ + ASSERT_FUNC_TYPE(enter, void(*)(GtkEventControllerMotion *, double, double, __typeof__(data))), \ + ASSERT_FUNC_TYPE(leave, void(*)(GtkEventControllerMotion *, __typeof__(data))), \ + dt_gui_connect_motion(GTK_WIDGET(widget), G_CALLBACK(motion), G_CALLBACK(enter), G_CALLBACK(leave), (data))) + +// GTK4 gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(controller)); +#define dt_modifier_eq(controller, mask)\ + dt_modifier_is(dt_key_modifier_state(), mask) + // control whether the mouse pointer displays as a "busy" cursor, e.g. watch or timer // the calls may be nested, but must be matched void dt_gui_cursor_set_busy(); diff --git a/src/iop/exposure.c b/src/iop/exposure.c index 3ab52c4fda75..59cc6d4d313d 100644 --- a/src/iop/exposure.c +++ b/src/iop/exposure.c @@ -814,25 +814,37 @@ static float _exposure_proxy_get_black(dt_iop_module_t *self) } -static void _exposure_proxy_handle_event(GdkEvent *event, const gboolean blackwhite) +static void _exposure_proxy_handle_event(gpointer controller, + int n_press, + double x, + const gboolean blackwhite) { dt_iop_module_t *self = darktable.develop->proxy.exposure.module; if(self && self->gui_data) { static gboolean black = FALSE; - if(event->type == GDK_BUTTON_PRESS || event->type == GDK_SCROLL) + if((n_press > 0 && GTK_IS_GESTURE_SINGLE(controller)) // button press + || !n_press) // scroll event black = blackwhite; if(black) - event->button.x *= -1; + x *= -1; const dt_iop_exposure_params_t *p = self->params; dt_iop_exposure_gui_data_t *g = self->gui_data; GtkWidget *widget = black ? g->black : p->mode == EXPOSURE_MODE_DEFLICKER - ? g->deflicker_target_level : g->exposure; - gtk_widget_realize(widget); - gtk_widget_event(widget, event); + ? g->deflicker_target_level : g->exposure; + if(!n_press) + darktable.bauhaus->scroll(widget, controller); + else + if(GTK_IS_GESTURE_SINGLE(controller)) + if(n_press > 0) + darktable.bauhaus->press(controller, n_press, x, 0, widget); + else + darktable.bauhaus->release(controller, -n_press, x, 0, widget); + else + darktable.bauhaus->motion(controller, x, 0, widget); gchar *text = dt_bauhaus_slider_get_text(widget, dt_bauhaus_slider_get(widget)); dt_action_widget_toast(DT_ACTION(self), widget, "%s", text); diff --git a/src/libs/filtering.c b/src/libs/filtering.c index 2ba2fa2fc500..e6a9bfb5dafc 100644 --- a/src/libs/filtering.c +++ b/src/libs/filtering.c @@ -1246,7 +1246,7 @@ static gboolean _widget_init(dt_lib_filtering_rule_t *rule, const dt_collection_ // operator type rule->w_operator = dt_bauhaus_combobox_new(NULL); - DT_BAUHAUS_WIDGET(rule->w_operator)->show_quad = FALSE; + dt_bauhaus_widget_set_quad_visibility(rule->w_operator, FALSE); dt_bauhaus_combobox_add_aligned(rule->w_operator, _("and"), DT_BAUHAUS_COMBOBOX_ALIGN_LEFT); dt_bauhaus_combobox_add_aligned(rule->w_operator, _("or"), DT_BAUHAUS_COMBOBOX_ALIGN_LEFT); dt_bauhaus_combobox_add_aligned(rule->w_operator, _("and not"), DT_BAUHAUS_COMBOBOX_ALIGN_LEFT); @@ -1264,7 +1264,7 @@ static gboolean _widget_init(dt_lib_filtering_rule_t *rule, const dt_collection_ { rule->w_prop = dt_bauhaus_combobox_new(NULL); dt_bauhaus_combobox_set_selected_text_align(rule->w_prop, DT_BAUHAUS_COMBOBOX_ALIGN_MIDDLE); - DT_BAUHAUS_WIDGET(rule->w_prop)->show_quad = FALSE; + dt_bauhaus_widget_set_quad_visibility(rule->w_prop, FALSE); _rule_populate_prop_combo(rule); g_object_set_data(G_OBJECT(rule->w_prop), "rule", rule); dt_bauhaus_combobox_set_from_value(rule->w_prop, prop); diff --git a/src/libs/histogram.c b/src/libs/histogram.c index b33e3a22e67b..06ed97d8080a 100644 --- a/src/libs/histogram.c +++ b/src/libs/histogram.c @@ -1635,24 +1635,25 @@ static gboolean _drawable_draw_callback(GtkWidget *widget, return FALSE; } -static gboolean _drawable_motion_notify_callback(GtkWidget *widget, - GdkEventMotion *event, - dt_lib_histogram_t *d) +static void _drawable_motion(GtkEventControllerMotion *controller, + double x, + double y, + dt_lib_histogram_t *d) { - if(event->state & GDK_BUTTON1_MASK) + if(gtk_get_current_event()->motion.state & GDK_BUTTON1_MASK) { if(d->scope_type != DT_LIB_HISTOGRAM_SCOPE_HISTOGRAM && d->scope_orient != DT_LIB_HISTOGRAM_ORIENT_VERT) - event->x = -event->y; + x = -y; - dt_dev_exposure_handle_event((GdkEvent *)event, FALSE); + dt_dev_exposure_handle_event(controller, 1, x, FALSE); } else { GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - const float posx = event->x / (float)(allocation.width); - const float posy = event->y / (float)(allocation.height); + gtk_widget_get_allocation(d->scope_draw, &allocation); + const float posx = x / (float)(allocation.width); + const float posy = y / (float)(allocation.height); const dt_lib_histogram_highlight_t prior_highlight = d->highlight; // FIXME: make just one tooltip for the widget depending on @@ -1695,12 +1696,12 @@ static gboolean _drawable_motion_notify_callback(GtkWidget *widget, _("double-click resets")); } } - gtk_widget_set_tooltip_text(widget, tip); + gtk_widget_set_tooltip_text(d->scope_draw, tip); g_free(tip); if(prior_highlight != d->highlight) { - gtk_widget_queue_draw(widget); + gtk_widget_queue_draw(d->scope_draw); if(d->highlight != DT_LIB_HISTOGRAM_HIGHLIGHT_NONE) { // FIXME: should really use named cursors, and differentiate @@ -1709,25 +1710,22 @@ static gboolean _drawable_motion_notify_callback(GtkWidget *widget, } } } - - //bubble event to eventbox to update the button tooltip - return FALSE; } -static gboolean _drawable_button_press_callback(GtkWidget *widget, - GdkEventButton *event, - const dt_lib_histogram_t *d) +static void _drawable_button_press(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_lib_histogram_t *d) { if(d->highlight != DT_LIB_HISTOGRAM_HIGHLIGHT_NONE) { if(d->scope_type != DT_LIB_HISTOGRAM_SCOPE_HISTOGRAM && d->scope_orient != DT_LIB_HISTOGRAM_ORIENT_VERT) - event->x = -event->y; + x = -y; - dt_dev_exposure_handle_event((GdkEvent *)event, d->highlight == DT_LIB_HISTOGRAM_HIGHLIGHT_BLACK_POINT); + dt_dev_exposure_handle_event(gesture, n_press, x, d->highlight == DT_LIB_HISTOGRAM_HIGHLIGHT_BLACK_POINT); } - - return TRUE; } static void _color_harmony_button_on(const dt_lib_histogram_t *d) @@ -1760,7 +1758,7 @@ static gboolean _eventbox_scroll_callback(GtkWidget *widget, { const gboolean black = d->highlight == DT_LIB_HISTOGRAM_HIGHLIGHT_BLACK_POINT; if(black) { event->delta_x *= -1; event->delta_y *= -1; } - dt_dev_exposure_handle_event((GdkEvent *)event, black); + dt_dev_exposure_handle_event(event, 0, 0, black); } // note we are using unit rather than smooth scroll events, as // exposure changes can get laggy if handling a multitude of smooth @@ -1805,29 +1803,28 @@ static gboolean _eventbox_scroll_callback(GtkWidget *widget, return TRUE; } -static gboolean _drawable_button_release_callback(GtkWidget *widget, - GdkEventButton *event, - dt_lib_histogram_t *d) +static void _drawable_button_release(GtkGestureSingle *gesture, + int n_press, + double x, + double y, + dt_lib_histogram_t *d) { - dt_dev_exposure_handle_event((GdkEvent *)event, FALSE); - return TRUE; + dt_dev_exposure_handle_event(gesture, -n_press, x, FALSE); } -static gboolean _drawable_leave_notify_callback(GtkWidget *widget, - const GdkEventCrossing *event, - dt_lib_histogram_t *d) +static void _drawable_leave(GtkEventControllerMotion *controller, + dt_lib_histogram_t *d) { // if dragging, gtk keeps up motion notifications until mouse button // is released, at which point we'll get another leave event for // drawable if pointer is still outside of the widget - if(!(event->state & GDK_BUTTON1_MASK) && d->highlight != DT_LIB_HISTOGRAM_HIGHLIGHT_NONE) + if(!(gtk_get_current_event()->motion.state & GDK_BUTTON1_MASK) + && d->highlight != DT_LIB_HISTOGRAM_HIGHLIGHT_NONE) { d->highlight = DT_LIB_HISTOGRAM_HIGHLIGHT_NONE; dt_control_change_cursor(GDK_LEFT_PTR); - gtk_widget_queue_draw(widget); + gtk_widget_queue_draw(d->scope_draw); } - // event should bubble up to the eventbox - return FALSE; } static void _histogram_scale_update(const dt_lib_histogram_t *d) @@ -2720,14 +2717,10 @@ void gui_init(dt_lib_module_t *self) | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); // FIXME: why does cursor motion over buttons trigger multiple draw callbacks? g_signal_connect(G_OBJECT(d->scope_draw), "draw", G_CALLBACK(_drawable_draw_callback), d); - g_signal_connect(G_OBJECT(d->scope_draw), "leave-notify-event", - G_CALLBACK(_drawable_leave_notify_callback), d); - g_signal_connect(G_OBJECT(d->scope_draw), "button-press-event", - G_CALLBACK(_drawable_button_press_callback), d); - g_signal_connect(G_OBJECT(d->scope_draw), "button-release-event", - G_CALLBACK(_drawable_button_release_callback), d); - g_signal_connect(G_OBJECT(d->scope_draw), "motion-notify-event", - G_CALLBACK(_drawable_motion_notify_callback), d); + dt_gui_connect_click_all(d->scope_draw, _drawable_button_press, + _drawable_button_release, d); + dt_gui_connect_motion(d->scope_draw, _drawable_motion, NULL, + _drawable_leave, d); gtk_widget_add_events (eventbox, diff --git a/src/libs/history.c b/src/libs/history.c index 8a690380fff9..a80e1c130137 100644 --- a/src/libs/history.c +++ b/src/libs/history.c @@ -696,7 +696,7 @@ static gchar *_lib_history_bauhaus_text(gpointer bhfield, for(GSList *w = module->widget_list_bh; w; w = g_slist_next(w)) { GtkWidget *widget = ((dt_action_target_t *)w->data)->target; - if(DT_BAUHAUS_WIDGET(widget)->field == bhfield) + if(dt_bauhaus_widget_get_field(widget) == bhfield) { gchar *old_text = dt_bauhaus_slider_get_text(widget, oval); gchar *new_text = dt_bauhaus_slider_get_text(widget, pval); diff --git a/src/libs/modulegroups.c b/src/libs/modulegroups.c index 10acf53fc699..264e69341768 100644 --- a/src/libs/modulegroups.c +++ b/src/libs/modulegroups.c @@ -297,11 +297,10 @@ static void _basics_init_item(dt_lib_modulegroups_basic_item_t *item) item->module_op = g_strdup(elems[0]); if(item->widget && DT_IS_BAUHAUS_WIDGET(item->widget)) { - DtBauhausWidget *bw = DT_BAUHAUS_WIDGET(item->widget); if(g_strv_length(elems) > 2) - item->widget_name = g_strdup_printf("%s - %s", _(elems[1]), bw->label); + item->widget_name = g_strdup_printf("%s - %s", _(elems[1]), dt_bauhaus_widget_get_label(item->widget)); else if(g_strv_length(elems) > 1) - item->widget_name = g_strdup(bw->label); + item->widget_name = g_strdup(dt_bauhaus_widget_get_label(item->widget)); else { item->widget_name = g_strdup(_("on-off")); @@ -369,10 +368,7 @@ static void _basics_remove_widget(dt_lib_modulegroups_basic_item_t *item) } // put back label if(DT_IS_BAUHAUS_WIDGET(item->widget)) - { - DtBauhausWidget *bw = DT_BAUHAUS_WIDGET(item->widget); - bw->show_extended_label = FALSE; - } + dt_bauhaus_widget_set_show_extended_label(item->widget, FALSE); } // cleanup item item->widget = NULL; @@ -546,9 +542,8 @@ static void _basics_add_widget(dt_lib_module_t *self, dt_lib_modulegroups_basic_ // change the widget label to integrate section name if(DT_IS_BAUHAUS_WIDGET(w)) { - DtBauhausWidget *bw = DT_BAUHAUS_WIDGET(w); - bw->show_extended_label = TRUE; - item->module = (dt_iop_module_t *)bw->module; + dt_bauhaus_widget_set_show_extended_label(item->widget, TRUE); + item->module = dt_bauhaus_widget_get_module(item->widget); } // we put the temporary widget at the place of the real widget in the module diff --git a/src/lua/widget/combobox.c b/src/lua/widget/combobox.c index 8d71bb79a87d..6da16b0be0d5 100644 --- a/src/lua/widget/combobox.c +++ b/src/lua/widget/combobox.c @@ -84,7 +84,7 @@ static int label_member(lua_State *L) char tmp[256]; luaA_to(L,char_256,&tmp,3); lua_getglobal(L, "script_manager_running_script"); - DT_BAUHAUS_WIDGET(combobox->widget)->module = &darktable.control->actions_lua; + dt_bauhaus_widget_set_module(combobox->widget, &darktable.control->actions_lua); dt_bauhaus_widget_set_label(combobox->widget,lua_tolstring(L,-1,NULL),tmp); return 0; } diff --git a/src/lua/widget/slider.c b/src/lua/widget/slider.c index 10ad131b4044..a18d048e79d8 100644 --- a/src/lua/widget/slider.c +++ b/src/lua/widget/slider.c @@ -49,7 +49,7 @@ static int label_member(lua_State *L) char tmp[256]; luaA_to(L,char_256,&tmp,3); lua_getglobal(L, "script_manager_running_script"); - DT_BAUHAUS_WIDGET(slider->widget)->module = &darktable.control->actions_lua; + dt_bauhaus_widget_set_module(slider->widget, &darktable.control->actions_lua); dt_bauhaus_widget_set_label(slider->widget,lua_tolstring(L,-1,NULL),tmp); return 0; }