Repository navigation
Runtime-populated comboboxes and avnd UI file paths (reviewed #2110 + #2114) - #2262
Merged
Merged
Conversation
Brings in halp::dynamic_combobox and halp::folder_combobox, and the avnd::dynamic_items_parameter / folder_items_parameter concepts the score binding matches on (celtera/avendish#204, superseding #171). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
File-chooser port values are stored in the document as path templates (<PROJECT>:..., <LIBRARY>:..., or document-relative). Score's own file widgets resolve them via score::locateFilePath, but the avnd layout builder pushed the raw template string into custom ui items' `value`, so an add-on widget bound to a soundfile/file/folder port received a path it could not use (e.g. a waveform widget that decodes the file in edit mode could never find it after the document moved or used project-relative paths). Resolve through score::locateFilePath in setupControl, both for the initial sync and in the valueChanged connections, whenever the inlet is a FileChooserBase or FolderChooser and the value is a non-empty string. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AvD2PFHZtW6pfsh3gGhayg
Address review: replace the per-widget qobject_cast checks with a SetGUIValue struct specialized through avendish port concepts (soundfile_port, midifile_port, file_port, folder widget), so the file/folder-only path resolution is selected at compile time. Co-Authored-By: Claude <noreply@anthropic.com>
Lets an avendish object repopulate a combobox port's choices at run time (e.g. from the files it finds in a folder). Complements the avendish halp::dynamic_combobox port. - Process::ComboBox gains setAlternatives() + an alternativesChanged signal. setAlternatives refreshes the value domain and re-clamps the current value when the list shrinks. The alternatives were already serialized, so saved documents reopen with their last-known items. - The inspector and canvas combobox widgets read their item list live and rebuild on alternativesChanged. - The avnd executor injects the port's update_items callback, marshalling the object's runtime updates to the model inlet on the main thread (QPointer-guarded). Nodes with type-only inputs are skipped. Objects that don't use the feature are unaffected. Co-authored-by: Pia Baltazar <pia@baltazar.studio>
Adds the host side of avendish's halp::folder_combobox: a combobox whose items are the files of a sibling folder/path port, listed at edit/load time. - Process::ComboBox gains folderPortName + fileExtensions and repopulateFromFolder(): lists a folder's matching files as the items. - The avnd binding (Concepts.hpp) stamps that metadata onto the ComboBox when the field is an avnd::folder_items_parameter. - ProcessModel wires each folder-combobox to its sibling folder port after port creation (i.e. after deserialization): populates immediately and refreshes on the folder's valueChanged. Populated at document load, no execution needed — unlike the executor-pushed dynamic_combobox, this is the right shape for file pickers in an editor (FluCoMa's folder file ports, Granola's sound picker). NOT yet compile-tested (needs a full score build). Co-authored-by: Pia Baltazar <pia@baltazar.studio>
Make the folder-backed combobox string-valued (the selected file name) rather than an index, so an object consumes it directly like a file-name string — adopting it is then a pure port-type swap (lineedit -> folder_combobox) with no object-code change. The folder binding builds (name, name) alternatives. Co-authored-by: Pia Baltazar <pia@baltazar.studio>
folderPortName / fileExtensions were only set by the avnd port factory at creation; the deserializing ProcessModel constructor restores ports from the document instead of re-running the factory, so on load they were empty and init_folder_comboboxes skipped the combobox — no folder re-scan and no live refresh, only the stale saved alternatives. Serialize both fields with the port (extensions as one space-joined string, like FileChooser's filters; JSON reads guarded for older documents). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDQaHG5FbEyH9D8VsjEyXe
Folder-backed comboboxes now install a QFileSystemWatcher on their folder, so files created while the document is open (an upstream process writing its analysis, a recorder...) appear in the list without reloading. Also change the refresh policy for the current value: keep it when still listed, fall back to the port's init when that is listed, and otherwise leave it untouched instead of clamping to an arbitrary entry - it may name a file that simply does not exist yet, and becomes selected once the file appears. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDQaHG5FbEyH9D8VsjEyXe
When the sibling port named by folder_port() holds a file path rather than a folder (e.g. a sound-file port), list the file's containing folder. Lets an object expose a picker next to an existing file port without adding a dedicated folder port. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDQaHG5FbEyH9D8VsjEyXe
…value Resolving templates on the way in only works if the way out reverses it: item.set writes straight to the inlet, so a custom widget that hands back the absolute path it was given would store that path in the document and the project would stop being relocatable. score's own file widgets have always paired locateFilePath with relativizeFilePath; this restores the other half. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
W_SIGNAL emits nothing for a connection made from another shared library, and every consumer of this signal -- the inspector and canvas comboboxes in score_lib_process's headers, the avnd executor in score_plugin_avnd -- is in a different one. The item list would be replaced and nothing would redraw. Every other signal on these ports already uses E_SIGNAL for that reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
…xtensions Two things kept a folder-backed combobox empty in ordinary documents: - the sibling folder port's value is a stored template (<PROJECT>:..., <LIBRARY>:..., or document-relative), and it was handed to QDir raw, so a project-relative folder listed nothing. The document is found by walking up rather than through IDocument::documentFromObject, which throws when a process is not attached to one yet; - extensions() is written by hand and ".wav" is as natural as "wav", but only the second worked: the first became the glob "*..wav". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3
Pins the behaviour the feature rests on: the selected value survives the list being replaced under it, disappears to the init value when it is gone, and is left alone when it names a file an upstream process has not written yet. Folder listings are filtered, sorted and degrade to a placeholder rather than to nothing. Documents written before any of this existed still load. The missing alternativesChanged emission was found by the first of these. 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.
Supersedes #2110 and #2114, which come from a fork and so could not take the
review fixes directly. Their commits are here unchanged, with authorship
intact; the four commits on top are the result of reviewing them, and the
avendish bump they depend on is first.
Depends on celtera/avendish#204 (merged), which supersedes celtera/avendish#171.
What the review found
The dynamic-combobox feature did not work at all.
alternativesChangedwasdeclared with
W_SIGNAL, which emits nothing for connections made from anothershared library — and every consumer is in one: the inspector and canvas
comboboxes live in
score_lib_process's headers, the executor inscore_plugin_avnd. The item list would be replaced and nothing would redraw.Every other signal on these ports already uses
E_SIGNALfor exactly thisreason. The first new test catches it.
Folder-backed comboboxes listed nothing in ordinary documents. The sibling
folder port's value is a stored template (
<PROJECT>:…,<LIBRARY>:…, ordocument-relative) and was handed to
QDirraw. Resolved now — via a walk upthe parent chain rather than
IDocument::documentFromObject, which throws whena process is not attached to a document yet.
#2114resolved paths one way only.item.setwrites straight back to theinlet, so a custom widget echoing the absolute path it was just given would
store that path in the document and the project would stop being relocatable.
score's own file widgets have always paired
locateFilePathwithrelativizeFilePath; the other half is restored.Extensions written as
".wav"matched nothing, becoming the glob"*..wav".Both spellings are accepted now.
One commit from #2110 is not included.
QGraphicsCombo: open a popup menu on clickreplaces drag-selection withQMenu::exec()— a nested event loop insidemousePressEventon a graphics item, which has no precedent in this codebase,and which the
QEvent::UngrabMousehandling in the sliders/knobs/choosersexists precisely to avoid. It also silently neuters
TEST_CASE("degenerate combo boxes survive being dragged"), the regression testfor a divide-by-zero on single-entry combos. It is unrelated to the feature and
the remaining seven commits rebase cleanly without it, so it is left out; worth
its own PR if the popup behaviour is wanted.
On the DataStream change
ComboBox's DataStream layout gains two fields (folderPortName,fileExtensions). I originally flagged this as a backwards-compatibilityhazard for
.scorebin; that was wrong. The binary format is only used toserialize in and out of a live score instance, so reader and writer are always
the same build and there is no older stream to desynchronise. No version guard
is needed. The JSON path is guarded with
tryGetregardless.Tests
tests/unit/ComboBoxAlternativesTest.cpp(7 cases, 41 assertions): the selectedvalue survives the list being replaced under it, falls back to init when it
disappears, and is left alone when it names a file an upstream process has not
written yet; folder listings are filtered, sorted, and degrade to a placeholder;
JSON round-trips preserve the new metadata and documents without it still load.
celtera/avendish#204 added the matching compile-time tests on the avendish side.
Verification
Built clean and run against master: 145/150. The five failures
(
analysis_gist,beat_tracker,media_trim,unused_files,explorer_editor_look) reproduce identically on an unmodifiedorigin/masterbuild and are pre-existing.
test_unit_graphics_combostill passes, which itwould not with the popup commit included.
🤖 Generated with Claude Code
https://claude.ai/code/session_018oUFSPD1bswcWtvrsq25t3