Repository navigation
graphics: open a combo box's drop-down on a click - #2263
Merged
Merged
Conversation
… drag Clicking a combo box did nothing: the only way to change one on the canvas was to drag it like a slider, which is not what a combo box looks like it does. A press still begins a drag, but the value now only follows the pointer once it has travelled past the platform's drag threshold -- below that it is a click, and a click opens the drop-down that until now only the right button reached. So scrubbing is unchanged, a click gains the obvious behaviour, and a wobble of a pixel while clicking no longer nudges the value. Deliberately not a QMenu::exec(): that runs a nested event loop inside mousePressEvent while the item holds the implicit mouse grab, which is the situation the QEvent::UngrabMouse handling in the sliders, knobs and choosers exists to survive, and it leaves `self` dangling if the process is deleted while the menu is up. openEditor() already builds its editor from the event loop behind a QPointer, so the click reuses it. Making that path reachable from both buttons needed two things of it: - one drop-down at a time. The build is deferred, so a second click arriving before the first editor appeared would stack another on top, and closing the pair frees the proxy twice; - the pick is resolved by name, not by the index the editor was built with. A runtime-populated combo box can be repopulated while the drop-down is open, and the same index then means a different entry -- or none, in which case nothing is selected rather than the wrong thing. An empty combo box opens nothing: there is nothing to pick. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
The editor is built from the event loop and lives in the scene, so it can outlast the click, the item list, and the combo box itself. Each of those is now driven through real scene events: - a click opens it and a drag does not; - three clicks and a right click leave exactly one editor behind; - the items being reordered while it is open still selects what was picked, by name, and an entry that has since disappeared selects nothing -- leaving an index past the end of the shorter list, which is then painted to show that readers tolerate it; - the combo box is deleted with the editor open, and the editor is then activated anyway; - a degenerate (empty, single-entry) combo box is clicked; - the scene takes the grab away mid-drag, which sends no release, and that must not be mistaken for a click and open an editor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
Clicking a combo box and then clicking away left a collapsed editor sitting on the scene: a small box showing one entry, in the widget style rather than the canvas one. Clicking again put another next to it. ComboBoxWithEnter reports a focus change only while its list is down, and QComboBox has no signal for the list closing, so nothing ever took a non-editable editor off the scene unless an entry was chosen or a key pressed. Watch the view for QEvent::Hide instead. Deferred by one turn: the list hides before `activated` arrives, and that has to be allowed to commit first. An editable box keeps the old behaviour -- its list closing is how one gets to the text field. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
Dragging a control that causes a combo box to be refilled moved the value by one step and then stopped, left a widget behind, and could leave a right-click editor on the scene that nothing would close again. setAlternatives restated the domain, and DefaultEffectItem answers domainChanged by deleting every control of the process and rebuilding them from the event loop. So the sequence was: drag the control -> the running object refills the combo box -> setDomain -> domainChanged -> the control being dragged is destroyed. The pointer then had nothing to drag, and anything the destroyed control had put on the scene outlived the item that knew how to take it down. The item list is what changed, and alternativesChanged already says so -- the widgets rebuild from it in place. The domain still follows the items for whoever reads it later; it just no longer claims the control's shape changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Clicking a combo box on the canvas did nothing — the only way to change one was
to drag it like a slider. This makes a click open the drop-down, keeping the
drag.
Revisits the
QGraphicsCombo: open a popup menu on clickcommit that was leftout of #2262.
Why not the original approach
That commit called
QMenu::exec()frommousePressEvent. That runs a nestedevent loop while the item holds the implicit mouse grab — precisely the
situation the
QEvent::UngrabMousehandling in the sliders, knobs and choosersexists to survive — and
selfdangles if the process is deleted while the menuis up. There is no other
QMenu::exec()in a graphics item anywhere in score.It also replaced dragging outright, which silently neutered
TEST_CASE("degenerate combo boxes survive being dragged")— the regressiontest for a divide-by-zero on single-entry combos.
What this does instead
A press still begins a drag; the value only follows the pointer once it passes
QApplication::startDragDistance(). Below that it is a click, and a click opensthe same drop-down the right button already opened. Dragging is unchanged, its
regression test keeps its meaning, and a pixel of wobble while clicking no
longer nudges the value.
openEditor()already builds its editor from the event loop behind aQPointer, with the index re-checked at commit time — no nested loop. Making itreachable from both buttons needed two things of it:
before the first editor appeared would stack another on top; closing the pair
frees the proxy twice.
(the folder/dynamic comboboxes from Runtime-populated comboboxes and avnd UI file paths (reviewed #2110 + #2114) #2262) can be repopulated while the
drop-down is open. The same index then means a different entry — or none, in
which case nothing is selected rather than the wrong thing.
An empty combo box opens nothing.
Tests
Six new cases in
GraphicsComboTest.cpp, driven through real scene events:click opens / drag does not; three clicks plus a right click leave exactly one
editor; items reordered under an open drop-down still select what was picked;
an entry that has since disappeared selects nothing, leaving an index past the
end of the shorter list which is then painted to show readers tolerate it;
the combo box is deleted with the editor open and the editor activated anyway;
a degenerate combo box is clicked; and the scene taking the grab away mid-drag
is not mistaken for a click.
Verification
test_unit_graphics_combo: 67 assertions in 17 cases, all passing. Fullsuite 145/150 — the five failures (
analysis_gist,beat_tracker,media_trim,unused_files,explorer_editor_look) reproduce on an unmodifiedorigin/masterbuild.Also run under valgrind: 17/17 pass, and no error context touches the combobox
code. The Qt-startup noise it does report (
Invalid read of size 16insideQApplicationPrivate::init()) appears identically when running an untouchedpre-existing test, so it is environmental.
🤖 Generated with Claude Code
https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3