From 5f32a883f6b299b26b382b32922556049ddb4f22 Mon Sep 17 00:00:00 2001 From: "Federico A. Corazza" Date: Mon, 27 Jul 2026 13:45:27 +0200 Subject: [PATCH 1/2] add auto rotate button to rotation and perspective module --- src/iop/ashift.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/iop/ashift.c b/src/iop/ashift.c index 55b45f5c4b20..507a8cf2b3ad 100644 --- a/src/iop/ashift.c +++ b/src/iop/ashift.c @@ -5318,6 +5318,31 @@ static void cropmode_callback(GtkWidget *widget, dt_iop_module_t *self) _swap_shadow_crop_box(p,g); } +static void _event_auto_rotate_quad_callback(GtkWidget *widget, + dt_iop_module_t *self) +{ + DT_GUARD_GUI_UPDATE(); + + dt_iop_ashift_params_t *p = self->params; + dt_iop_ashift_gui_data_t *g = self->gui_data; + + dt_iop_request_focus(self); + + if(self->enabled) + { + do_fit(self, p, ASHIFT_FIT_ROTATION_BOTH_LINES); + } + else + { + g->jobcode = ASHIFT_JOBCODE_FIT; + g->jobparams = ASHIFT_FIT_ROTATION_BOTH_LINES; + } + + _swap_shadow_crop_box(p, g); + dt_dev_add_history_item(darktable.develop, self, TRUE); + _swap_shadow_crop_box(p, g); +} + static int _event_fit_v_button_clicked(GtkWidget *widget, const GdkEventButton *event, dt_iop_module_t *self) @@ -5966,6 +5991,10 @@ void gui_init(dt_iop_module_t *self) dt_shortcut_register(ac, 0, DT_ACTION_EFFECT_DOWN, GDK_KEY_bracketright, GDK_MOD1_MASK); dt_shortcut_register(ac, 0, 0, GDK_KEY_r, GDK_MOD1_MASK); + dt_bauhaus_widget_set_quad(g->rotation, self, dtgtk_cairo_paint_wand, FALSE, + _event_auto_rotate_quad_callback, + _("auto-rotate the image using detected structure")); + g->cropmode = dt_bauhaus_combobox_from_params(self, "cropmode"); g_signal_connect(G_OBJECT(g->cropmode), "value-changed", G_CALLBACK(cropmode_callback), self); From 0157de85a1fd1586865dd345448797aec408c1c5 Mon Sep 17 00:00:00 2001 From: "Federico A. Corazza" Date: Mon, 27 Jul 2026 14:34:41 +0200 Subject: [PATCH 2/2] fix: apply largest-area crop after auto-rotate - commit crop values to params after the swap/history/swap cycle - remove focus gate in commit_params so valid crop is always rendered --- src/iop/ashift.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/iop/ashift.c b/src/iop/ashift.c index 507a8cf2b3ad..91a00bea08b4 100644 --- a/src/iop/ashift.c +++ b/src/iop/ashift.c @@ -5341,6 +5341,7 @@ static void _event_auto_rotate_quad_callback(GtkWidget *widget, _swap_shadow_crop_box(p, g); dt_dev_add_history_item(darktable.develop, self, TRUE); _swap_shadow_crop_box(p, g); + _commit_crop_box(p, g); } static int _event_fit_v_button_clicked(GtkWidget *widget, @@ -5629,13 +5630,12 @@ void commit_params(dt_iop_module_t *self, d->orthocorr = (p->mode == ASHIFT_MODE_GENERIC) ? 0.0f : p->orthocorr; d->aspect = (p->mode == ASHIFT_MODE_GENERIC) ? 1.0f : p->aspect; - if(dt_iop_has_focus(self) - || dt_isnan(p->cl) + if(dt_isnan(p->cl) || dt_isnan(p->cr) || dt_isnan(p->ct) || dt_isnan(p->cb)) { - // if gui has focus we want to see the full uncropped image + // if crop parameters are not yet known, show the full image d->cl = 0.0f; d->cr = 1.0f; d->ct = 0.0f;