All the changes from #21733, #21741 and #21742 are included in this PR. Their co…mmits are part of this branch. I closed those PRs because they shared most of their code paths with the review fixes below; a single PR is easier to review than three overlapping ones.
### From the previous PRs, incorporated here:
**Color picker shortcuts + double-press on hold keys (#21733).** The event-controller conversion broke shortcuts on standalone picker buttons (e.g. AgX auto-tune): shortcuts synthesize fake button-press events that capture-phase gestures never receive. Pickers now use their own action definition and the shared callback takes explicit ctrl/right-click flags instead of fabricating events. Also, a key with a hold shortcut (default `a`) could never trigger a double/triple-press shortcut on the same key; the hold press is now recorded and a fast second press is detected as a double press.
**Lighttable scrolling (#21741).** Scrolling stopped as soon as the pointer was over an always-visible thumbnail overlay icon (e.g. the star ratings): the converted scroll handlers were target-only instead of bubbling. Bubble phase restored, and the raw smooth scroll deltas in thumbtable/culling were restored (touchpad scrolling had been chopped into fixed 50 px steps).
**Gesture-driven toggle button shortcuts (#21742).** Widgets whose action lives in a gesture (mask shape buttons, retouch/spots, filmicrgb, temperature, liquify, ashift fit/structure, the Masks module buttons, ...) now register the gesture (`DT_ACTION_GESTURE_KEY`), and the shortcut layer fires the gesture's press signal — the same path a real click takes.
### New fixes
Help provided by the technical writeups of @kofa73 [here](https://github.com/darktable-org/darktable/pull/21742#issuecomment-5209144014) and [here](https://github.com/darktable-org/darktable/pull/21659#issuecomment-5208853641).
- Curve editors: right-clicking an endpoint reset it *and* silently deleted a node (dropped early return); right-clicking a node also opened the module presets menu. Both fixed, matching the pre-migration behavior.
- Removed the GTK3 double-click bridge: `GtkGestureMultiPress` counts `n_press` natively, and the bridge fired every double-click twice (double view switch in lighttable/culling, duplicate filmstrip select+ACTIVATE).
- `GdkEvent` leaks from `gtk_get_current_event()` in controller callbacks (culling, thumbtable, darkroom second window, colorlabels, shortcuts dialog, metadata, collection treeview), plus a `dt_gui_get_current_root_coords()` helper.
- Removed the smooth-scroll `*_fallback` helpers that double-accumulated after the DISCRETE proxy (bauhaus popup zoom-range reset, misleading `preset '(last)'` toast); preset scrolling now uses the proxy steps directly, with a reversal dampener at the preset list boundaries.
- Module reset created two undo records (duplicate `dt_dev_add_history_item`).
- Thumbtable/culling release handlers lost the primary-button guard (middle/right release changed the selection).
- Shortcut action effects are now carried into synthetic gesture presses: ctrl-/right-effect variants work again on ashift fit, blend shapes, retouch, spots and masks (previously a `ctrl+v` shortcut ran the ctrl variant of a plain-activate binding, and the plain fit was unreachable from that shortcut).
- Retouch "editing" shortcut regression fixed (it read the key event during dispatch and got button 0, so edit mode never toggled).
### Linked issues
Fixes #21730
Fixes #21736
Fixes #21735
And, besides those previously quoted fixes, plenty of other things were tested working properly along the way.
### Tests
<details>
<summary><b>Manual tests already conducted</b></summary>
All of the following were manually tested on this branch and verified working (where a behavior was broken before this PR, the old broken behavior is noted as "(before: ...)").
### Darkroom — module header
- **Right-click on a module body** (e.g. exposure, tone curve): the presets popup must open (this was dead before the button=0 fix upstream; verify it still works).
- **Module reset button:** single click → module resets and the undo history gains exactly **one** entry (undo once, then redo — before, one click created two undo steps).
- **Presets button scroll:** hover the module presets arrow and scroll the wheel → presets cycle (previous/next) with the toast; a smooth trackpad scroll must not show a bogus `preset 'x' (last)` toast when nothing was applied.
- **Ctrl+click the reset button** → re-applies auto-presets (once).
1. Store a preset on a module (presets arrow → *store new preset*) and tick **"auto apply this preset"** in the store dialog (set the image filter if you like).
2. Then **ctrl+click** the module's reset button → the auto preset is re-applied (module label shows the preset name). **Plain click** → resets to defaults.
---
### Darkroom — curve editors (tone curve, RGB curves, base curve, color zones)
- **Right-click a curve endpoint** (first or last node): the endpoint resets to 0/1 and **no node is deleted** (before this fix the node count silently shrank). Right-click a middle node → deletes the node as before. Check the persisted params: draw a curve, right-click an endpoint, then expand/collapse the module — node count must be unchanged. Right-click on *empty* curve area → presets menu still opens
- Double-click the curve area → resets the curve (unchanged). **Result:** Works correctly.
---
### Lighttable — thumbtable / culling
- **Double-click a thumbnail in filemanager/zoom mode** → switches to darkroom **once** (before, the bridge+gesture double-fired; on slow machines you could see the modules rebuild twice / a flash).
- **Right-click a thumbnail and release** → selection must **not** change. Middle-click release → selection must not change (before, any-button release selected the hovered image).
- **Culling mode:** middle-click → zoom toggle works and **does not** flip the culling selection (act-on set). Right-click release → no selection change.
- **Memory:** wiggle the mouse over the lighttable/culling grid and scroll a lot (e.g. 30 s), then check RSS with `top`/Activity Monitor — should stay flat (before, culling/thumbtable motion leaked one GdkEvent per event).
---
### Bauhaus slider popup
- **Open a slider popup** (e.g. Exposure → Black Level or any Bauhaus slider), then **smooth-scroll with a trackpad** over it: the slider's zoom range must **not** flip between soft/hard bounds (before, delta 0 reset the range). A clicky wheel should still zoom the range as before; middle-click should still reset the range.
### Second darkroom window (preview2)
- **Scroll / trackpad-pan in the second window** (preview pinned) for a while — memory must stay flat (the scroll callback leaked a GdkEvent per event).
### Shortcut-activated toggle buttons (PR #21742 fixes)
- **Retouch "editing" toggle via keyboard:** open Retouch, bind a shortcut to *Retouch → Editing* (or use the existing default), press it → mask edit mode must toggle on/off (before: only the shape buttons cleared and edit mode never changed).
- **effect variants**
1. Open the **shortcuts dialog** (keyboard icon).
2. In the left **actions** search, type `ashift` → find **"fit" → "vertical"**. Select it.
3. Press a key, e.g. `v` → a new row appears in the right **shortcuts** list.
4. Click the **"effect"** cell of that row → dropdown → pick **"ctrl-activate"**.
5. Close the dialog, go to darkroom, expand ashift, press `v` → it must run the **ctrl variant** (fit rotation/lens-shift only, not the full vertical fit). *Before this fix it ran the plain fit.*
6. Bonus scenario: assign `ctrl+v` with the plain **"activate"** effect → pressing `ctrl+v` must do the **plain** fit (*before: the live ctrl state hijacked it into the ctrl variant*).
7. Same for shapes: actions → **"blend" → "shapes" → "ellipse"**, effect **"ctrl-toggle"** → activating should start **continuous ("add multiple")** shape creation.
- **Retouch shape buttons** (path/circle/ellipse/brush) via shortcut: ctrl-effect shortcut → continuous (multi) shape creation starts.
- **Color labels:** The **"colorlabels"** module is in the **lighttable**, **bottom-left panel** (the section with the color squares under the thumbnails area). Right-click one of the color squares → the description-edit popup should open at the pointer.
### Collections / masks / metadata (leak + popup)
- **Masks tree** (Blend → Drawn Mask → right-click row) — context menu opens, no leak.
- **Shortcuts dialog**: type in the search box — memory stays flat.
- *Folders & filmrolls are single-select by design.* Click a folder row (e.g. `photos/2024/`) → only that row is selected and the collection filters to it. Shift-click or ctrl/cmd-click a second row must **not** extend the selection. Right-click a folder or filmroll row → the context menu opens at the pointer ("update path to files...", "remove...").
- *Shift-range selection only exists for the time/exif views* (day, month, time, aperture, focal length, ISO, exposure, aspect ratio, rating): click one row, shift-click another → the rows in between are selected and the rule becomes a range `[a;b]` (order follows the sort direction). Ctrl+click adds nothing here — it is single-select on those views too.
- *Double-click* toggles expand/collapse on tree parents; a *single shift- or ctrl-click* on a folder/filmroll row activates it directly (same as double-click); *shift+ctrl+click* on a folder row switches to the matching filmroll view and vice versa.
</details>
---
Related: #15920 #20433