Skip to content

avnd: support comboboxes with runtime-populated item lists#2110

Open
bltzr wants to merge 8 commits into
ossia:masterfrom
bltzr:feature/dynamic-combobox
Open

avnd: support comboboxes with runtime-populated item lists#2110
bltzr wants to merge 8 commits into
ossia:masterfrom
bltzr:feature/dynamic-combobox

Conversation

@bltzr

@bltzr bltzr commented Jul 12, 2026

Copy link
Copy Markdown
Member

PR (ossia/score): runtime-populated comboboxes for avnd objects

Branch: feature/dynamic-combobox (off master)
Depends on: avendish feature/dynamic-combobox (companion PR).
Diff: 4 files, +119/-10.

What

Lets an avendish object repopulate a combobox port's item list at run time —
e.g. a "File" combobox that shows the audio files currently in a folder,
updating as the folder changes. Consumes the avendish halp::dynamic_combobox
port.

Changes

  • Process::ComboBox (WidgetInlets.hpp/.cpp): setAlternatives() +
    alternativesChanged signal. setAlternatives refreshes the value domain
    and re-clamps the current value when the list shrinks. Alternatives were
    already serialized, so documents reopen with their last-known items.
  • Widgets (ControlWidgets.hpp): the inspector and canvas comboboxes read
    their items live and rebuild on alternativesChanged (previously the list
    was fixed at widget construction).
  • Executor (Crousti/Executor.hpp): connect_dynamic_items injects the
    port's update_items callback, marshalling runtime updates to the model
    inlet on the main thread (QPointer-guarded). Nodes with type-only inputs
    are skipped.

Compatibility

Purely additive. Objects and ports that don't use the feature are unaffected;
the widget/executor paths are guarded by requires/if constexpr on the new
members.

Testing

A throwaway DynComboTest object (in the score-avnd-flucoma addon) lists a
folder's audio files in a dynamic_combobox and follows folder changes live;
verified in the running app. Not part of this PR.

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>
@bltzr

bltzr commented Jul 12, 2026

Copy link
Copy Markdown
Member Author

Depends on the avendish port: celtera/avendish#171 (halp::dynamic_combobox). That should land first — this PR consumes its update_items callback.

@bltzr

bltzr commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Closing for now — the avendish port this consumes (celtera/avendish#171) has no real consumer yet, so parking both until one exists rather than adding to the review queue. Kept on bltzr:feature/dynamic-combobox; will reopen together with the avendish PR when adopted.

@bltzr bltzr closed this Jul 13, 2026
@bltzr

bltzr commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Reference, not an active review request — parked alongside the avendish port it consumes (celtera/avendish#171). Working example: DynComboTest.

We solved our own case (Granola's sound picker) with a custom widget rather than this binding, so nothing depends on it. Happy to revive both together if you think a runtime-populated combobox is worth having generally.

@jcelerier

Copy link
Copy Markdown
Member

eventually it's a feature we want though ! it's very useful in many cases

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>
@bltzr

bltzr commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

OK then I'll reopen it and move this forward, as it's going to be useful for the flucoma port

@bltzr bltzr reopened this Jul 13, 2026
bltzr and others added 4 commits July 13, 2026 19:29
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
The canvas combobox item's only interaction was an invisible
drag-up/down-to-spin (InfiniteScroller); a plain click did nothing, so
comboboxes looked read-only on the process panel while working fine in the
inspector. The old popup attempt had been left commented out.

Open a QMenu on mouse press instead, like a native combobox: items are the
current alternatives, the selected one is checked, and QMenu natively
supports both click-then-click and press-drag-release selection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDQaHG5FbEyH9D8VsjEyXe
@bltzr

bltzr commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

Runtime-tested end-to-end now (populate at document load, save/reload round-trip, selection preserved). Two additions since last push:

  • ComboBox now serializes its folder-port metadata, so a loaded document re-scans the folder and keeps live refresh (previously factory-set only, lost on deserialization).
  • QGraphicsCombo: clicking a combobox on the canvas now opens a QMenu popup (items = current alternatives). Before, the only interaction was the drag-to-spin, which made comboboxes effectively read-only on the process panel while working in the inspector — this fixes that for every combobox/enum port, independently of this feature.

celtera/avendish#171 should still land first.

bltzr and others added 2 commits July 14, 2026 00:43
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
@bltzr

bltzr commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

Follow-ups, all runtime-tested:

  • Folder-backed comboboxes now install a QFileSystemWatcher, so files written while the document is open (an upstream analysis, a recorder…) appear in the list immediately.
  • Value refresh keeps a selection that names a not-yet-existing file (it becomes selected once written) instead of clamping to an arbitrary entry.
  • The sibling port may also hold a file path — the file's containing folder is listed then, so a picker can sit next to an existing sound-file port without a dedicated folder port.

Exercised across ~45 processes in a FluCoMa addon: full analysis chain (onset slicing → corpus building → KD-tree fit/query) runs zero-config with the pickers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants