ovui-widgets-stage 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """ovui_widgets.stage: USD stage browser widget package."""
12
+
13
+ from ovui_widgets.stage.widget import (
14
+ DRAG_MIME,
15
+ DropVisualController,
16
+ FilterPipeline,
17
+ HierarchyItem,
18
+ HierarchyModel,
19
+ RenameController,
20
+ StageDelegate,
21
+ StageWidget,
22
+ make_name_filter,
23
+ )
24
+ from ovui_widgets.stage.window import StageWindow
25
+
26
+ __all__ = [
27
+ "DRAG_MIME",
28
+ "DropVisualController",
29
+ "FilterPipeline",
30
+ "make_name_filter",
31
+ "HierarchyItem",
32
+ "HierarchyModel",
33
+ "RenameController",
34
+ "StageDelegate",
35
+ "StageWidget",
36
+ "StageWindow",
37
+ ]
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Back-compat shim. Canonical location: :mod:`ovui_widgets.stage.widget.drop_visual_controller`."""
12
+
13
+ from ovui_widgets.stage.widget.drop_visual_controller import DropVisualController
14
+
15
+ __all__ = ["DropVisualController"]
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Back-compat shim. Canonical location: :mod:`ovui_widgets.stage.widget.filter_pipeline`."""
12
+
13
+ from ovui_widgets.stage.widget.filter_pipeline import FilterPipeline, make_name_filter
14
+
15
+ __all__ = ["FilterPipeline", "make_name_filter"]
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Back-compat shim. Canonical location: :mod:`ovui_widgets.stage.widget.hierarchy_model`."""
12
+
13
+ from ovui_widgets.stage.widget.hierarchy_model import DRAG_MIME, HierarchyItem, HierarchyModel
14
+
15
+ __all__ = ["DRAG_MIME", "HierarchyItem", "HierarchyModel"]
@@ -0,0 +1,21 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Value models for the Stage Browser.
12
+
13
+ Thin wrappers over :class:`omni.ui.AbstractValueModel` that translate
14
+ adapter state into the shape each column delegate expects. Kept in a
15
+ dedicated subpackage so the delegate-per-column split in the stage implementation step 11
16
+ can import from here without pulling the whole widget layer.
17
+ """
18
+
19
+ from ovui_widgets.stage.models.visibility_value_model import VisibilityValueModel
20
+
21
+ __all__ = ["VisibilityValueModel"]
@@ -0,0 +1,136 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """VisibilityValueModel — eye-checkbox model with inverted, selection-aware semantics.
12
+
13
+ VisibilityValueModel supplies the StageWidget eye-checkbox behavior.
14
+
15
+ ``get_value_as_bool()`` returns ``True`` when the item is effectively
16
+ **invisible** — the convention Kit's visibility checkbox uses ("checked" ⇒
17
+ the eye-closed icon draws, so the prim is hidden). ``set_value(hidden)``
18
+ toggles visibility; if the owning item is part of a multi-selection,
19
+ *every* selected item is toggled in the same undo group (DAW-style group
20
+ operation). Instance proxies are gated out via ``is_enabled``.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from typing import TYPE_CHECKING
26
+
27
+ import omni.ui as ui
28
+ from ovui_data_adapters.common import VisibilityState
29
+
30
+ if TYPE_CHECKING:
31
+ from ovui_data_adapters.common import StageAdapter
32
+
33
+ from ovui_widgets.stage.hierarchy_model import HierarchyItem, HierarchyModel
34
+
35
+
36
+ class VisibilityValueModel(ui.AbstractValueModel):
37
+ """Inverted, selection-aware value model for the Visibility column.
38
+
39
+ Parameters
40
+ ----------
41
+ item
42
+ The :class:`HierarchyItem` this model drives.
43
+ adapter
44
+ The :class:`StageAdapter` the model reads and writes through.
45
+ model
46
+ The owning :class:`HierarchyModel` — used to read the current
47
+ selection so a group toggle can target every selected row.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ item: "HierarchyItem",
53
+ adapter: "StageAdapter",
54
+ model: "HierarchyModel",
55
+ ) -> None:
56
+ super().__init__()
57
+ self._item = item
58
+ self._adapter = adapter
59
+ self._model = model
60
+
61
+ # ── Read ──────────────────────────────────────────────────────────────────
62
+
63
+ def get_value_as_bool(self) -> bool:
64
+ """Return True when the item is effectively invisible (eye closed)."""
65
+ state = self._adapter.compute_visibility(self._item.adapter_item)
66
+ return state != VisibilityState.VISIBLE
67
+
68
+ def get_value_as_int(self) -> int:
69
+ return 1 if self.get_value_as_bool() else 0
70
+
71
+ def get_value_as_float(self) -> float:
72
+ return float(self.get_value_as_int())
73
+
74
+ def get_value_as_string(self) -> str:
75
+ return "true" if self.get_value_as_bool() else "false"
76
+
77
+ # ── Write ─────────────────────────────────────────────────────────────────
78
+
79
+ def set_value(self, value) -> None:
80
+ """Set the hidden state; group-toggles every selected item if applicable.
81
+
82
+ ``value`` is the inverted convention: True hides, False shows. A
83
+ single undo group wraps the whole batch so one click produces one
84
+ undo entry regardless of selection size.
85
+ """
86
+ hidden = bool(value)
87
+ visible = not hidden
88
+ selected = self._model._selected_items
89
+ if self._item in selected and len(selected) > 1:
90
+ candidates = [s.adapter_item for s in selected]
91
+ else:
92
+ candidates = [self._item.adapter_item]
93
+ targets = [
94
+ target for target in candidates
95
+ if self._adapter.can_edit_visibility(target)
96
+ ]
97
+ if not targets:
98
+ return
99
+
100
+ # Transactional acquisition: if begin raises, no scope exists and
101
+ # there is nothing to abort — the error propagates untouched.
102
+ self._adapter.begin_undo_group("Toggle Visibility")
103
+ try:
104
+ for target in targets:
105
+ self._adapter.set_visibility(target, visible)
106
+ except BaseException as member_error:
107
+ # Structured scope ownership: a failed member aborts the whole
108
+ # group so completed members are compensated instead of leaving
109
+ # a partial, undoable scope behind. The member error stays
110
+ # primary: abort/cleanup failures attach to it as notes and
111
+ # never displace it.
112
+ abort = getattr(self._adapter, "abort_undo_group", None)
113
+ try:
114
+ if callable(abort):
115
+ abort()
116
+ else:
117
+ self._adapter.end_undo_group()
118
+ except BaseException as cleanup_error:
119
+ add_note = getattr(member_error, "add_note", None)
120
+ if callable(add_note):
121
+ add_note(
122
+ "scope cleanup also failed: "
123
+ f"{type(cleanup_error).__name__}: {cleanup_error}"
124
+ )
125
+ raise
126
+ else:
127
+ self._adapter.end_undo_group()
128
+
129
+ # ── Enablement ────────────────────────────────────────────────────────────
130
+
131
+ def is_enabled(self) -> bool:
132
+ """False for items the adapter cannot edit (e.g. instance proxies).
133
+
134
+ Matches the StageWidget visibility callback shape.
135
+ """
136
+ return self._adapter.can_edit_visibility(self._item.adapter_item)
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Back-compat shim. Canonical location: :mod:`ovui_widgets.stage.widget.rename_controller`."""
12
+
13
+ from ovui_widgets.stage.widget.rename_controller import RenameController
14
+
15
+ __all__ = ["RenameController"]
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Back-compat shim. Canonical location: :mod:`ovui_widgets.stage.widget.stage_delegate`."""
12
+
13
+ from ovui_widgets.stage.widget.stage_delegate import _KEY_ESCAPE, StageDelegate
14
+
15
+ __all__ = ["StageDelegate", "_KEY_ESCAPE"]
@@ -0,0 +1,15 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Back-compat shim. Canonical location: :mod:`ovui_widgets.stage.widget.stage_widget`."""
12
+
13
+ from ovui_widgets.stage.widget.stage_widget import StageWidget
14
+
15
+ __all__ = ["StageWidget"]
@@ -0,0 +1,385 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: LicenseRef-NvidiaProprietary
3
+ #
4
+ # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
5
+ # property and proprietary rights in and to this material, related
6
+ # documentation and any modifications thereto. Any use, reproduction,
7
+ # disclosure or distribution of this material and related documentation
8
+ # without an express license agreement from NVIDIA CORPORATION or
9
+ # its affiliates is strictly prohibited.
10
+
11
+ """Stage Browser domain-scoped styles (stage browser behavior, §21).
12
+
13
+ All selectors start with ``Stage.*`` or ``TreeView*`` so they only affect
14
+ the Stage widget. Colors resolve through the shared ``cl.*`` ColorStore so
15
+ they follow theme switches (``set_shade("light")`` / ``"dark"``).
16
+ """
17
+
18
+ from omni.ui import color as cl
19
+ from omni.ui import constant as fl
20
+
21
+ STAGE_STYLES = {
22
+ # ── Column headers (UI-005 — design-reference rev-9 anchor) ─────────
23
+ # Reference header sits on the panel chrome above the tree well; hierarchy
24
+ # comes from the #222222 strip, readable muted header text, and the
25
+ # separator rule.
26
+ #
27
+ # Token anchor (locked to ovuiDark rev-9 — OVUI_REFERENCE_IMPLEMENTATION_PLAN
28
+ # Step 3.2 / UI-005):
29
+ # - header text colour → ``cl.text_secondary`` (#A7A7A7 muted grey,
30
+ # readable against the corrected reference #222222 header strip)
31
+ # - eye-icon colour → ``cl.text_secondary`` for the same visible
32
+ # header-column treatment as NAME / TYPE
33
+ # - separator-rule tone → ``cl.border_default`` (#4B4B4B — the rev-9
34
+ # quiet 1-px rule under the header band, paired with the matching
35
+ # ``Stage.Separator`` and ``Stage.Footer.Rule`` tokens)
36
+ "Stage.ColumnHeader.Bg": {
37
+ "background_color": cl.background_primary,
38
+ "border_radius": 0,
39
+ },
40
+ "Stage.ColumnHeader.Bg:hovered": {
41
+ "background_color": cl.background_primary,
42
+ "border_radius": 0,
43
+ },
44
+ "Stage.ColumnHeader": {
45
+ "color": cl.text_secondary,
46
+ "font_size": fl.font_size_small,
47
+ },
48
+ "Stage.ColumnHeader:hovered": {
49
+ "color": cl.text_secondary,
50
+ "font_size": fl.font_size_small,
51
+ },
52
+ "Stage.ColumnHeader.Icon": {
53
+ "color": cl.text_secondary,
54
+ },
55
+ "Stage.ColumnHeader.Icon:hovered": {
56
+ "color": cl.text_secondary,
57
+ },
58
+ "Stage.ColumnHeader.Rule": {
59
+ "background_color": cl.border_default,
60
+ "border_radius": 0,
61
+ },
62
+
63
+ # ── Type column label ────────────────────────────────────────────────
64
+ "Stage.TypeLabel": {
65
+ "color": cl.text_secondary,
66
+ "font_size": fl.font_size_tiny,
67
+ },
68
+ "Stage.TypeLabel:selected": {
69
+ "color": cl.text_secondary,
70
+ "font_size": fl.font_size_tiny,
71
+ },
72
+
73
+ # ── Default-prim pill (UI-033 — design-reference rev-9 anchor) ───────
74
+ # Reference (`OvuiSampleApp.png`) shows the Stage tree as a calm flat
75
+ # list of neutral labels with no high-chroma DEF pill or saturated
76
+ # prim-type icons. Step 3.4 / UI-033 retones the underlying tokens so
77
+ # the pill and the prim-icon family read at the reference's quiet
78
+ # baseline; the selectors below stay untouched so the wiring contract
79
+ # (background + border both resolve to the same pill token; label
80
+ # tones via ``cl.text_secondary``) remains stable.
81
+ #
82
+ # Token anchor (locked to ovuiDark rev-9 — OVUI_REFERENCE_IMPLEMENTATION_PLAN
83
+ # Step 3.4 / UI-033, defined in ``ovui_widgets/common/style/palette.py``):
84
+ # - pill background → ``cl.stage_default_prim_pill_background``
85
+ # (#1F1F1F — the ``cl.background_secondary`` panel-body family;
86
+ # down from the prior #1D2634 blue tint that read as a saturated
87
+ # accent against the calm reference)
88
+ # - prim-icon mesh tint → ``cl.prim_type_mesh`` (#7A8FA8 — muted
89
+ # blue-grey; down from the prior #008AF9 selection-blue which
90
+ # overlapped ``cl.accent_primary``)
91
+ # - prim-icon light tint → ``cl.prim_type_light`` (#B09269 — muted
92
+ # warm gold; down from the prior #DDAA50 amber)
93
+ # - prim-icon camera tint → ``cl.prim_type_camera`` (#8A93A8 — quiet
94
+ # neutral cool grey; down from the prior #99A3C9 light-blue)
95
+ # The active ``Stage.PrimIcon`` selector still paints via
96
+ # ``cl.text_secondary`` (its existing quiet-grey wiring); the
97
+ # ``cl.prim_type_*`` shades are toned so any future per-category
98
+ # tinting consumer inherits a reference-aligned neutral.
99
+ "Stage.DefaultPrimPill": {
100
+ "background_color": cl.stage_default_prim_pill_background,
101
+ "border_radius": fl.radius_small,
102
+ "border_color": cl.stage_default_prim_pill_background,
103
+ "border_width": 1,
104
+ },
105
+ "Stage.DefaultPrimPill:hovered": {
106
+ "background_color": cl.stage_default_prim_pill_background,
107
+ "border_radius": fl.radius_small,
108
+ "border_color": cl.stage_default_prim_pill_background,
109
+ "border_width": 1,
110
+ },
111
+ "Stage.DefaultPrimPill.Label": {
112
+ "color": cl.text_secondary,
113
+ "font_size": fl.font_size_tiny,
114
+ "padding": 0,
115
+ },
116
+ "Stage.DefaultPrimPill.Label:hovered": {
117
+ "color": cl.text_secondary,
118
+ "font_size": fl.font_size_tiny,
119
+ "padding": 0,
120
+ },
121
+
122
+ # ── Name column label (state-aware) ──────────────────────────────────
123
+ "Stage.Name": {
124
+ "color": cl.text_primary,
125
+ "font_size": fl.font_size_small,
126
+ },
127
+ "Stage.Name::inactive": {
128
+ "color": cl.text_disabled,
129
+ "font_size": fl.font_size_small,
130
+ },
131
+ "Stage.Name::abstract": {
132
+ "color": cl.text_secondary,
133
+ "font_size": fl.font_size_small,
134
+ },
135
+
136
+ # ── Row hover / selection (UI-006 — design-reference rev-9 anchor) ──
137
+ # Aggressive zero-out of every spacing attribute the omni.ui
138
+ # ``TreeView`` widget inherits from global ``Rectangle`` / ``Frame`` /
139
+ # ``ScrollingFrame`` styles. Without these, the standalone ovui build
140
+ # left an un-tunable ~22-px strip above the first data row even with
141
+ # ``header_visible=False`` (the TreeView quietly still reserves header
142
+ # chrome). Every attribute below is load-bearing — removing any one
143
+ # of them restores the gap.
144
+ #
145
+ # Token anchor (locked to ovuiDark rev-9 — OVUI_REFERENCE_IMPLEMENTATION_PLAN
146
+ # Step 3.3 / UI-006):
147
+ # - selected row fill → ``cl.treeview_selection`` (#232429 —
148
+ # the rev-9 quiet selected band; matches the reference's
149
+ # low-contrast active-row highlight)
150
+ # - hover row fill → ``cl.background_tertiary`` (#222222 —
151
+ # a raised hover band above the near-black tree well)
152
+ # - left selection-accent fill → ``cl.treeview_selection`` so the
153
+ # selected-row geometry remains but the reference's neutral tree
154
+ # treatment is preserved instead of adding a blue rail)
155
+ # - resting eye-icon colour → ``cl.text_secondary`` (#A7A7A7 — one
156
+ # step up from ``cl.text_disabled``; matches the reference's clearly
157
+ # readable right-edge eye glyph)
158
+ # - hidden / disabled eye tone → ``cl.text_disabled`` (#4B4B4B — the
159
+ # paired quiet token for the toggled-off / non-toggleable states)
160
+ # Visibility icon glyph size is 14 px (``_VISIBILITY_ICON_SIZE`` in
161
+ # ``stage_delegate.py``), bumped from 12 px under UI-006 so the eye
162
+ # reads at the reference's scale; the column-header eye preview in
163
+ # ``StageDelegate.build_column_header`` remains at 12 px because
164
+ # UI-005 fixed it as the column-header glyph size.
165
+ "TreeView": {
166
+ "background_color": cl.treeview_well_background,
167
+ "color": cl.text_primary,
168
+ "padding": 0,
169
+ "margin": 0,
170
+ "margin_width": 0,
171
+ "margin_height": 0,
172
+ "border_width": 0,
173
+ "border_radius": 0,
174
+ # UI-006 / Step 3.3 hover paint mechanism: ovui's TreeView resolves
175
+ # the *hover* tint from ``background_selected_color`` (see
176
+ # ``ovui/core/src/TreeView.cpp`` row-paint path documented in
177
+ # ``ovui-widgets/ovui_widgets/layers/style.py:33-37``), NOT from
178
+ # ``TreeView:hovered.background_color``. Setting this property is
179
+ # what actually fires when the cursor passes over a Stage row;
180
+ # without it the row hover treatment never paints. Token mirrors
181
+ # the existing ``TreeView:hovered`` value (``cl.background_tertiary``),
182
+ # one notch above the dedicated tree well, so resting / hover /
183
+ # selected paint a consistent three-tier band sequence.
184
+ "background_selected_color": cl.background_tertiary,
185
+ },
186
+ "TreeView.Header": {
187
+ "background_color": cl.treeview_well_background,
188
+ "color": cl.text_primary,
189
+ "height": 0,
190
+ "padding": 0,
191
+ "margin": 0,
192
+ "margin_height": 0,
193
+ "border_width": 0,
194
+ "font_size": 0,
195
+ },
196
+ "TreeView.Item": {
197
+ "color": cl.text_primary,
198
+ "font_size": fl.font_size_small,
199
+ "margin_height": 0,
200
+ "margin_width": 0,
201
+ "margin": 0,
202
+ "padding": 0,
203
+ },
204
+ "TreeView.ScrollingFrame": {
205
+ "padding": 0,
206
+ "margin": 0,
207
+ "border_width": 0,
208
+ "border_radius": 0,
209
+ },
210
+ "TreeView:hovered": {
211
+ "background_color": cl.background_tertiary,
212
+ },
213
+ "TreeView:selected": {
214
+ "background_color": cl.treeview_selection,
215
+ },
216
+ "TreeView.Item:selected": {
217
+ "color": cl.text_primary,
218
+ "font_size": fl.font_size_small,
219
+ },
220
+ "Stage.Name:selected": {
221
+ "color": cl.text_primary,
222
+ "font_size": fl.font_size_small,
223
+ },
224
+ "Stage.SelectionAccent": {
225
+ "background_color": cl.treeview_selection,
226
+ "border_radius": 0,
227
+ },
228
+ "Stage.TreeChevron": {
229
+ "color": cl.text_secondary,
230
+ },
231
+
232
+ # ── Type icon ────────────────────────────────────────────────────────
233
+ "Stage.PrimIcon": {
234
+ "color": cl.text_secondary,
235
+ },
236
+ "Stage.Badge": {},
237
+
238
+ # ── Visibility eye ───────────────────────────────────────────────────
239
+ "Stage.VisibilityIcon": {
240
+ "color": cl.text_secondary,
241
+ },
242
+ "Stage.VisibilityIcon::visible": {
243
+ "color": cl.text_secondary,
244
+ },
245
+ "Stage.VisibilityIcon::hidden": {
246
+ "color": cl.text_disabled,
247
+ },
248
+ "Stage.VisibilityIcon::disabled": {
249
+ "color": cl.text_disabled,
250
+ },
251
+
252
+ # ── Filter bar (UI-001 — design-reference rev-9 anchor) ──────────────
253
+ # The bar is the panel-width strip sitting flush at the top of the
254
+ # Stage column; ``Stage.FilterFieldBorder`` paints the one-pixel ring,
255
+ # while ``Stage.FilterField`` is the inner fill wrapping the magnifier
256
+ # icon + text input + clear button so the icon appears *inside* the field.
257
+ # The StringField itself (``Stage.FilterFieldInput``) is rendered
258
+ # transparent so only the outer Rectangle's border + radius read.
259
+ #
260
+ # Token anchor (locked to ovuiDark rev-9 — OVUI_REFERENCE_IMPLEMENTATION_PLAN
261
+ # Step 3.1 / UI-001):
262
+ # - resting border tone → ``cl.border_default`` (#4B4B4B — visible
263
+ # against the #222222 panel chrome, matches the reference's readable
264
+ # 1 px ring; reaffirmed against ``OvuiSampleApp.png``)
265
+ # - corner radius → ``fl.radius_small`` (~3 px, matches the
266
+ # reference filter pill arc)
267
+ # - placeholder colour → ``cl.text_disabled`` (#4B4B4B muted grey,
268
+ # reads as inactive helper text against the pill fill)
269
+ # - focused border tone → ``cl.border_focused`` (#008AF9 selection
270
+ # blue, mirrored from interactive-focus token; toggled imperatively
271
+ # via ``rect.name = "focused"`` in ``_on_filter_begin_edit``)
272
+ # The sibling ``Property.SearchField`` selectors carry the same anchor
273
+ # (UI-002 / Property.SearchField sibling contract); the cross-panel
274
+ # pill-token contract is pinned by
275
+ # ``test_property_and_stage_share_pill_tokens``.
276
+ "Stage.FilterBar": {
277
+ "background_color": cl.background_primary,
278
+ "border_radius": 0,
279
+ },
280
+ "Stage.FilterField": {
281
+ "background_color": cl.background_field,
282
+ "border_radius": fl.radius_small,
283
+ "border_width": 0,
284
+ "border_color": cl.border_default,
285
+ },
286
+ # ``Rectangle`` border rendering is inconsistent across the standalone
287
+ # ovui builds, so the live widget also draws a 1 px border rectangle
288
+ # behind the field fill. The token is still the required
289
+ # ``cl.border_default``; this selector exists only to make the border
290
+ # visible in screenshot QA.
291
+ "Stage.FilterFieldBorder": {
292
+ "background_color": cl.border_default,
293
+ "border_radius": fl.radius_small,
294
+ },
295
+ "Stage.FilterField::focused": {
296
+ "border_color": cl.border_focused,
297
+ },
298
+ "Stage.FilterFieldBorder::focused": {
299
+ "background_color": cl.border_focused,
300
+ "border_radius": fl.radius_small,
301
+ },
302
+ "Stage.FilterFieldInput": {
303
+ "background_color": cl.transparent,
304
+ "color": cl.text_primary,
305
+ "border_width": 0,
306
+ "border_radius": 0,
307
+ "padding": 0,
308
+ "font_size": fl.font_size_small,
309
+ },
310
+ "Stage.FilterPlaceholder": {
311
+ "color": cl.text_disabled,
312
+ "font_size": fl.font_size_small,
313
+ },
314
+ "Stage.FilterIcon": {
315
+ "color": cl.text_secondary,
316
+ },
317
+ "Stage.FilterIcon::active": {
318
+ "color": cl.accent_primary,
319
+ },
320
+ "Stage.FilterClearButton.Image": {
321
+ "color": cl.text_secondary,
322
+ },
323
+
324
+ # ── Separator between filter and tree ────────────────────────────────
325
+ "Stage.Separator": {
326
+ "background_color": cl.border_default,
327
+ },
328
+
329
+ # ── Tree-body scrolling frame ────────────────────────────────────────
330
+ # Step 19: thin, subtle scrollbar matching the Property Inspector.
331
+ # The track is transparent, the thumb is muted at rest, and the
332
+ # hovered/pressed frame state brightens the same thumb token.
333
+ "Stage.ScrollingFrame": {
334
+ "background_color": cl.treeview_well_background,
335
+ "secondary_color": cl.scrollbar_thumb,
336
+ "scrollbar_size": fl.scrollbar_width,
337
+ "border_radius": fl.radius_small,
338
+ "padding": 0,
339
+ "margin": 0,
340
+ },
341
+ "Stage.ScrollingFrame:hovered": {
342
+ "secondary_color": cl.scrollbar_thumb_hovered,
343
+ },
344
+ "Stage.ScrollingFrame:pressed": {
345
+ "secondary_color": cl.scrollbar_thumb_hovered,
346
+ },
347
+
348
+ # ── Fixed status footer ──────────────────────────────────────────────
349
+ "Stage.Footer": {
350
+ "background_color": cl.background_primary,
351
+ "color": cl.text_secondary,
352
+ "font_size": fl.font_size_small,
353
+ "border_radius": 0,
354
+ "padding": 0,
355
+ },
356
+ "Stage.Footer.Rule": {
357
+ "background_color": cl.border_default,
358
+ "border_radius": 0,
359
+ },
360
+
361
+ # ── Empty-state overlay shown when the tree has no visible children ──
362
+ "Stage.EmptyState": {
363
+ "color": cl.text_disabled,
364
+ "font_size": fl.font_size_small,
365
+ },
366
+
367
+ # ── Drop indicator ───────────────────────────────────────────────────
368
+ "Stage.DropIndicator": {
369
+ "background_color": cl.treeview_drop_indicator,
370
+ "border_width": 1,
371
+ "border_color": cl.treeview_drop_indicator,
372
+ "border_radius": 1,
373
+ },
374
+
375
+ # ── Inline rename field ──────────────────────────────────────────────
376
+ "Stage.RenameField": {
377
+ "background_color": cl.background_field,
378
+ "color": cl.text_primary,
379
+ "border_color": cl.border_focused,
380
+ "border_width": 1,
381
+ "border_radius": fl.radius_small,
382
+ "padding": 2,
383
+ "font_size": fl.font_size_small,
384
+ },
385
+ }