Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/bauhaus/bauhaus.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "control/conf.h"
#include "develop/develop.h"
#include "develop/imageop.h"
#include "develop/imageop_gui.h"
#include "gui/accelerators.h"
#include "gui/color_picker_proxy.h"
#include "gui/gtk.h"
Expand Down Expand Up @@ -1213,7 +1214,9 @@ static void _highlight_changed_notebook_tab(GtkWidget *w, gpointer user_data)
c;
c = g_list_delete_link(c, c))
{
if(!is_changed && DT_IS_BAUHAUS_WIDGET(c->data) && gtk_widget_get_visible(c->data))
if(is_changed) continue;

if(DT_IS_BAUHAUS_WIDGET(c->data) && gtk_widget_get_visible(c->data))
{
dt_bauhaus_widget_t *b = DT_BAUHAUS_WIDGET(c->data);
if(!b->field) continue;
Expand All @@ -1227,6 +1230,15 @@ static void _highlight_changed_notebook_tab(GtkWidget *w, gpointer user_data)
is_changed = b->combobox.entries->len
&& b->combobox.active != b->combobox.defpos;
}
// dt_bauhaus_toggle_from_params() checkbuttons are plain GtkCheckButtons,
// not bauhaus widgets, so they'd otherwise never be considered here.
// dt_bauhaus_toggle_widget_is_changed() (develop/imageop_gui.c) reads
// the module's current default_params live rather than a value cached
// at some earlier point, so this stays correct even for modules that
// recompute their real defaults per-image (e.g. in reload_defaults()),
// after the checkbox already exists.
else if(GTK_IS_TOGGLE_BUTTON(c->data) && gtk_widget_get_visible(c->data))
is_changed = dt_bauhaus_toggle_widget_is_changed(GTK_WIDGET(c->data));
}

GtkWidget *label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), w);
Expand All @@ -1237,6 +1249,11 @@ static void _highlight_changed_notebook_tab(GtkWidget *w, gpointer user_data)
dt_gui_remove_class(label, "changed");
}

void dt_bauhaus_highlight_changed_notebook_tab(GtkWidget *w, gpointer user_data)
{
_highlight_changed_notebook_tab(w, user_data);
}

void dt_bauhaus_update_from_field(dt_iop_module_t *module,
GtkWidget *widget,
gpointer params,
Expand Down
7 changes: 7 additions & 0 deletions src/bauhaus/bauhaus.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ void dt_bauhaus_widget_set_field(GtkWidget *widget,
gpointer field,
dt_introspection_type_t field_type);
gpointer dt_bauhaus_widget_get_field(GtkWidget *widget);
// mark (or re-evaluate, if user_data is NULL/FALSE) the enclosing notebook
// tab as "changed" -- the same mechanism dt_bauhaus_slider/combobox value
// changes already drive internally (_highlight_changed_notebook_tab); this
// is a public wrapper so non-bauhaus widgets bound via the same params
// (e.g. dt_bauhaus_toggle_from_params()'s plain GtkCheckButton) can drive
// the same tab-header indicator.
void dt_bauhaus_highlight_changed_notebook_tab(GtkWidget *w, gpointer user_data);
// 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 *widget,
Expand Down
55 changes: 55 additions & 0 deletions src/develop/imageop_gui.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ static void _iop_toggle_callback(GtkWidget *togglebutton, dt_module_param_t *dat

if(*field != previous)
{
dt_bauhaus_highlight_changed_notebook_tab(
togglebutton, GINT_TO_POINTER(dt_bauhaus_toggle_widget_is_changed(togglebutton)));
dt_iop_gui_changed(DT_ACTION(self), togglebutton, &previous);
}
}
Expand Down Expand Up @@ -246,6 +248,13 @@ GtkWidget *dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *para
DT_IOP_SECTION_FOR_PARAMS_UNWIND(module_param->module);
module_param->param = (uint8_t *)p + f->header.offset;
g_signal_connect_data(G_OBJECT(button), "toggled", G_CALLBACK(_iop_toggle_callback), module_param, (GClosureNotify)g_free, 0);
// Stash a second, non-owning reference to the same module_param so
// dt_bauhaus_toggle_widget_reset()/dt_bauhaus_toggle_widget_is_changed()
// can find it from just the widget (e.g. from generic group/page reset
// or tab-changed-tracking code with no other context available). Freed
// together with module_param by the "toggled" signal's GClosureNotify
// above when the button is destroyed; nothing here takes ownership.
g_object_set_data(G_OBJECT(button), "dt-toggle-module-param", module_param);

dt_action_define_iop(module_param->module, section, str, button, &dt_action_def_toggle);
}
Expand All @@ -263,6 +272,52 @@ GtkWidget *dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *para
return button;
}

// Both functions below read the module's current default_params live, at
// call time, rather than any value captured when the checkbox was first
// created -- some modules recompute their real defaults per-image (in
// reload_defaults(), which can run again long after the checkbox already
// exists), and a stale snapshot would silently go wrong for exactly those
// modules: resetting, or reporting "changed", against whatever the default
// used to be instead of what it actually is for the image currently loaded.
static gboolean _toggle_widget_default(GtkWidget *togglebutton, gboolean *out_default)
{
dt_module_param_t *data = g_object_get_data(G_OBJECT(togglebutton), "dt-toggle-module-param");
if(!data || !data->module || !data->module->default_params) return FALSE;

const size_t offset = (uint8_t *)data->param - (uint8_t *)data->module->params;
*out_default = *(gboolean *)((uint8_t *)data->module->default_params + offset);
return TRUE;
}

void dt_bauhaus_toggle_widget_reset(GtkWidget *togglebutton)
{
// An insensitive checkbox isn't in effect right now -- some modules also
// blank a checkbox's displayed tick while insensitive without touching
// the actual stored value, so there's nothing meaningful to reset, and
// forcing the display back to "checked" would only undo that module's
// own display logic for no reason.
if(!gtk_widget_get_sensitive(togglebutton)) return;

gboolean default_value;
if(!_toggle_widget_default(togglebutton, &default_value)) return; // not one of ours

gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglebutton), default_value);
// if the value was already at default, the "toggled" signal (and with it
// _iop_toggle_callback's own highlight call) never fired -- make sure the
// tab indicator still gets re-evaluated regardless, matching how
// dt_bauhaus_widget_reset()'s callers already expect group/page resets to
// always leave the "changed" state correct.
dt_bauhaus_highlight_changed_notebook_tab(togglebutton, GINT_TO_POINTER(FALSE));
}

gboolean dt_bauhaus_toggle_widget_is_changed(GtkWidget *togglebutton)
{
gboolean default_value;
if(!_toggle_widget_default(togglebutton, &default_value)) return FALSE;

return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton)) != default_value;
}

GtkWidget *dt_iop_togglebutton_new(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label,
GCallback callback, gboolean local, guint accel_key, GdkModifierType mods,
DTGTKCairoPaintIconFunc paint, GtkWidget *box)
Expand Down
15 changes: 15 additions & 0 deletions src/develop/imageop_gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ GtkWidget *dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *pa

GtkWidget *dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param);

// reset a dt_bauhaus_toggle_from_params() checkbutton to its param's current
// default value (read live from the module's default_params at the moment
// of the call, so this stays correct even if the module recomputes its
// defaults per-image after the checkbox was created). No-op if
// `togglebutton` wasn't created by dt_bauhaus_toggle_from_params(), or if
// it's currently insensitive (an insensitive control isn't in effect right
// now, so there's nothing meaningful to reset).
void dt_bauhaus_toggle_widget_reset(GtkWidget *togglebutton);

// true if a dt_bauhaus_toggle_from_params() checkbutton's current value
// differs from its param's current default (read live, same as above).
// False (not changed) if `togglebutton` wasn't created by
// dt_bauhaus_toggle_from_params().
gboolean dt_bauhaus_toggle_widget_is_changed(GtkWidget *togglebutton);

// package dt_iop_module_t pointer and section name to pass to a _from_params function
// it will then create a widget action in a section, rather than top level in the module
// optionally pass a box to add the widgets to
Expand Down
20 changes: 20 additions & 0 deletions src/gui/gtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "bauhaus/bauhaus.h"
#include "develop/develop.h"
#include "develop/imageop.h"
#include "develop/imageop_gui.h"
#include "dtgtk/drawingarea.h"
#include "dtgtk/expander.h"
#include "dtgtk/sidepanel.h"
Expand Down Expand Up @@ -3837,13 +3838,32 @@ GdkModifierType dt_key_modifier_state()
static void _reset_all_bauhaus(GtkNotebook *notebook,
GtkWidget *box)
{
// Two passes rather than one: some modules react to a slider/combobox
// changing by programmatically changing a checkbox too (e.g. turning a
// feature back on when one of its own settings is touched). If a
// checkbox were reset in the same pass as sliders/comboboxes, a slider
// reset that runs afterwards could still trigger that kind of side
// effect and silently undo the checkbox reset that already ran earlier
// in the same pass. Resetting every checkbox only after every slider/
// combobox on the page has already settled avoids that regardless of
// what order the widgets happen to appear in.
GList *toggles = NULL;
for(GList *c = gtk_container_get_children(GTK_CONTAINER(box));
c;
c = g_list_delete_link(c, c))
{
if(DT_IS_BAUHAUS_WIDGET(c->data))
dt_bauhaus_widget_reset(GTK_WIDGET(c->data));
// dt_bauhaus_toggle_from_params() checkbuttons are plain GtkCheckButtons,
// not bauhaus widgets, so they're otherwise silently skipped by the
// check above; dt_bauhaus_toggle_widget_reset() no-ops for any other
// GtkToggleButton (e.g. one not created by that function).
else if(GTK_IS_TOGGLE_BUTTON(c->data))
toggles = g_list_prepend(toggles, c->data);
}
for(GList *c = toggles; c; c = c->next)
dt_bauhaus_toggle_widget_reset(GTK_WIDGET(c->data));
g_list_free(toggles);

dt_gui_remove_class(gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), box), "changed");
}
Expand Down
Loading