diff --git a/src/gui/gtk.c b/src/gui/gtk.c index 28d18223bd79..f953933a47de 100644 --- a/src/gui/gtk.c +++ b/src/gui/gtk.c @@ -4405,6 +4405,40 @@ void dt_gui_process_events() continue; } +void dt_gui_simulate_button_event(GtkWidget *widget, + const GdkEventType eventtype, + const int button) +{ + gboolean res = FALSE; + + // Create the event GdkEventButton + GdkEventButton event; + memset(&event, 0, sizeof(event)); + + event.type = eventtype; + event.window = gtk_widget_get_window(widget); + event.send_event = TRUE; + event.time = GDK_CURRENT_TIME; + event.x = 0; // not important in this case + event.y = 0; // not important in this case + event.button = button; + event.device = + gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display_get_default())); + + if (event.window != NULL) + { + g_object_ref(event.window); + } + + // send signal + g_signal_emit_by_name(G_OBJECT(widget), "button-press-event", &event, &res, NULL); + + if (event.window != NULL) + { + g_object_unref(event.window); + } +} + // clang-format off // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py // vim: shiftwidth=2 expandtab tabstop=2 cindent diff --git a/src/gui/gtk.h b/src/gui/gtk.h index 8e2a31e48a48..f94f44e795f7 100644 --- a/src/gui/gtk.h +++ b/src/gui/gtk.h @@ -547,6 +547,11 @@ void dt_gui_cursor_clear_busy(); // (i.e. the current function will do a lot of work before returning) void dt_gui_process_events(); +// Simulate a mouse button event (button is 1, 2, 3 - mouse button) sent to a Widget +void dt_gui_simulate_button_event(GtkWidget *widget, + const GdkEventType eventtype, + const int button); + #ifdef __cplusplus } // extern "C" #endif /* __cplusplus */ diff --git a/src/libs/colorpicker.c b/src/libs/colorpicker.c index 463b7d121445..e39cf8917bbc 100644 --- a/src/libs/colorpicker.c +++ b/src/libs/colorpicker.c @@ -64,6 +64,7 @@ typedef struct dt_lib_colorpicker_t GtkWidget *add_sample_button; GtkWidget *display_samples_check_box; dt_colorpicker_sample_t primary_sample; + dt_colorpicker_sample_t *target_sample; } dt_lib_colorpicker_t; const char *name(dt_lib_module_t *self) @@ -91,6 +92,48 @@ int position(const dt_lib_module_t *self) return 800; } +#if 0 +// kept for debug session +static void _dump_sample(char* ctx, dt_colorpicker_sample_t *sample) +{ + const gboolean isbox = sample->size == DT_LIB_COLORPICKER_SIZE_BOX; + printf(">>> %s KIND : %s (%lx)\n", ctx, isbox ? "box" : "point", (uint64_t)sample); + printf(" "); + if(isbox) + { + for(int k = 0; k < 8; k++) + printf("%1.2f ", sample->box[k]); + } + else + { + printf("%1.2f %1.2f", sample->point[0], sample->point[1]); + } + printf("\n"); +} + +static void _dump(char* ctx, dt_lib_module_t *self) +{ + dt_lib_colorpicker_t *data = self->data; + + { + char *txt = g_strdup_printf("primary sample (%s)", ctx); + _dump_sample(txt, &data->primary_sample); + g_free(txt); + } + + int k = 0; + for(GSList *samples = darktable.lib->proxy.colorpicker.live_samples; + samples; + samples = g_slist_next(samples)) + { + char *txt = g_strdup_printf("live sample (%d)", ++k); + dt_colorpicker_sample_t *sample = samples->data; + _dump_sample(txt, sample); + g_free(txt); + } +} +#endif + // GUI callbacks static gboolean _sample_draw_callback(GtkWidget *widget, @@ -121,6 +164,23 @@ static gboolean _sample_draw_callback(GtkWidget *widget, } } + // if the sample is locked we want to add a lock + if(sample->copied) + { + const int border = DT_PIXEL_APPLY_DPI(2); + const int icon_width = width - 2 * border; + const int icon_height = height - 2 * border; + if(icon_width > 0 && icon_height > 0) + { + GdkRGBA fg_color; + gtk_style_context_get_color(gtk_widget_get_style_context(widget), + gtk_widget_get_state_flags(widget), &fg_color); + + gdk_cairo_set_source_rgba(cr, &fg_color); + dtgtk_cairo_paint_store(cr, border, border, icon_width, icon_height, 0, NULL); + } + } + return FALSE; } @@ -219,6 +279,16 @@ static gboolean _large_patch_toggle(GtkWidget *widget, static void _picker_button_toggled(GtkToggleButton *button, dt_lib_colorpicker_t *data) { + const gboolean is_active = gtk_toggle_button_get_active(button); + + // reset copy target + if(!is_active && data->target_sample) + { + gtk_widget_queue_draw(data->target_sample->container); + data->target_sample->copied = FALSE; + data->target_sample = NULL; + } + gtk_widget_set_sensitive(GTK_WIDGET(data->add_sample_button), gtk_toggle_button_get_active(button)); } @@ -458,18 +528,54 @@ static gboolean _live_sample_button(GtkWidget *widget, // copy to active picker dt_lib_module_t *self = darktable.lib->proxy.colorpicker.module; dt_iop_color_picker_t *picker = darktable.lib->proxy.colorpicker.picker_proxy; + dt_lib_colorpicker_t *data = self->data; - // no active picker, too much iffy GTK work to activate a default - if(!picker) return FALSE; + const gboolean is_active = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->picker_button)); + const gboolean simulate_event = !is_active || data->target_sample; - if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT) - _set_sample_point(self, sample->point); - else if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX) - _set_sample_box_area(self, sample->box); + if(data->target_sample) + { + // we have a target sample, so we are editing a live sample. + // copy back the edited sample (primary_sample) into the target + // sample on which we clicked. + memcpy(&sample->point, + &data->primary_sample.point, + sizeof(data->primary_sample.point)); + memcpy(&sample->box, + &data->primary_sample.box, + sizeof(data->primary_sample.box)); + + sample->size = data->primary_sample.size; + data->target_sample->copied = FALSE; + data->target_sample = NULL; + } else - return FALSE; + { + // we don't have a target sample, copy the sample into + // the primary sample to be edited. + data->target_sample = sample; + sample->copied = TRUE; + darktable.lib->proxy.colorpicker.module = self; + + if(sample->size == DT_LIB_COLORPICKER_SIZE_POINT) + _set_sample_point(self, sample->point); + else if(sample->size == DT_LIB_COLORPICKER_SIZE_BOX) + _set_sample_box_area(self, sample->box); + } - if(picker->module) + if(simulate_event) + { + dt_gui_simulate_button_event + (data->picker_button, + GDK_BUTTON_PRESS, + /* button 1 to create use a point and 3 for a box */ + data->primary_sample.size == DT_LIB_COLORPICKER_SIZE_POINT + ? 1 + : 3); + } + + if(picker && picker->module) { picker->module->dev->preview_pipe->status = DT_DEV_PIXELPIPE_DIRTY; } @@ -491,8 +597,16 @@ static void _add_sample(GtkButton *widget, dt_lib_colorpicker_t *data = self->data; dt_colorpicker_sample_t *sample = malloc(sizeof(dt_colorpicker_sample_t)); + // reset copy target + if(data->target_sample) + { + data->target_sample->copied = FALSE; + data->target_sample = NULL; + } + memcpy(sample, &data->primary_sample, sizeof(dt_colorpicker_sample_t)); sample->locked = FALSE; + sample->copied = FALSE; sample->container = gtk_event_box_new(); gtk_widget_add_events(sample->container, diff --git a/src/libs/colorpicker.h b/src/libs/colorpicker.h index 72eadf900e7e..b90fc83ce485 100644 --- a/src/libs/colorpicker.h +++ b/src/libs/colorpicker.h @@ -51,6 +51,7 @@ typedef struct dt_colorpicker_sample_t gboolean pick_output; // NOTE: only applies to live samples gboolean locked; + gboolean copied; /** The actual picked colors */ // picked color in display profile, as picked from preview pixelpipe @@ -76,4 +77,3 @@ typedef struct dt_colorpicker_sample_t // vim: shiftwidth=2 expandtab tabstop=2 cindent // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified; // clang-format on -