Skip to content

Commit b03e8db

Browse files
author
Shane Wall
committed
Improve dock UX architecture and fix signal-driven sync regressions
This commit delivers a substantial dock UX refactor and closes follow-up regressions introduced during the polling-to-signal migration. UI and layout changes: - Reorganize selection-dependent tools (hollow, move floor/ceiling, tie/untie, clip, duplicate array) into a contextual 'Selection Tools' section in the Brush tab. - Keep Selection Tools hidden when no selection exists and visible when selection is present. - Compact toolbar presentation to single-character labels (D, S, +, -, P, ▲, ▼) with richer tooltip descriptions and a separator before extrude controls. - Move autosave warning label definition into dock.tscn and bind it via @onready instead of runtime construction. - Improve section ergonomics via collapsible-section separators, indented content, and persisted expanded/collapsed state through user prefs. - Apply small readability/usability tweaks (UV justify grid layout, standard label widths, wider +/- buttons, list sizing adjustments). Signal and synchronization changes: - Remove frame-throttled paint/material/surface polling from _process and wire updates through LevelRoot signals. - Add and emit LevelRoot material_list_changed on material add/remove; connect dock handlers for immediate palette refresh. - Ensure initial root attach performs explicit _sync_materials_from_root and _sync_surface_paint_from_root so existing scene state is reflected immediately. - Add face_selection_changed signal to LevelRoot and connect dock handler to keep UV/surface paint panels synchronized with face selection changes. - Emit face_selection_changed from select_face_at_screen only when face_selection actually changes (covers hit toggle and miss-clear paths without spurious emits). Startup and selection-state correctness: - Initialize dock selection state during plugin _enter_tree after reading current EditorSelection so contextual controls are correct on startup/reload. - Retain existing selection-changed wiring for ongoing updates. Documentation updates: - Update README, changelog, spec, roadmap, development, contributor guide, install/upgrade, MVP guide, and user guide to match new dock structure, toolbar behavior, and signal-driven sync model. Notes: - This commit aligns implementation and docs around the 4-tab dock architecture and contextual tooling model. - User-reported full suite status: 344/344 tests passing after the face-selection emission fix.
1 parent 6e43326 commit b03e8db

14 files changed

Lines changed: 423 additions & 229 deletions

CHANGELOG.md

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,27 @@ The format is based on Keep a Changelog, and this project follows semantic versi
55

66
## [Unreleased]
77
### Added
8+
- **Dock UX improvements (Mar 2026):**
9+
- **Selection Tools section** in Brush tab: hollow, clip, move floor/ceiling, tie entity, and
10+
duplicator controls now appear contextually when brushes are selected (moved from Manage tab).
11+
- **Collapsible section polish**: each section now has an HSeparator divider and 4px left-indented
12+
content for visual hierarchy. Collapsed state persists across sessions via user preferences.
13+
- **Signal-driven paint/material/face sync**: paint layer, material palette, surface paint, and
14+
face selection updates are now instant via `paint_layer_changed`, `material_list_changed`,
15+
`face_selection_changed`, and `selection_changed` signals (replaced 10-frame polling throttle).
16+
- **`material_list_changed` signal** on LevelRoot: emitted on material add/remove for instant
17+
dock sync.
18+
- **`face_selection_changed` signal** on LevelRoot: emitted from `select_face_at_screen()`,
19+
`toggle_face_selection()`, and `clear_face_selection()` (only when selection actually changes).
20+
Drives UV/surface paint panel sync and disabled-hint updates.
21+
- **Initial sync on root connect**: `_connect_root_signals()` now calls `_sync_materials_from_root()`
22+
and `_sync_surface_paint_from_root()` so existing materials/surface data appear immediately.
23+
- **Initial selection state on startup**: plugin pushes cached editor selection to dock in
24+
`_enter_tree()`, so Selection Tools visibility is correct from first frame.
25+
- **Compact toolbar**: single-char button labels (D, S, +, -, P, ▲, ▼) with full descriptions in
26+
tooltips. VSeparator before extrude buttons. Labels update from keymap.
27+
- **UV Justify grid**: 3x2 GridContainer layout replaces cramped 2-row HBoxContainer.
28+
- **Autosave warning** defined in dock.tscn (was runtime-created Label).
829
- **Customizable keymaps** (`hf_keymap.gd`): all keyboard shortcuts are now data-driven via
930
`HFKeymap` instead of hardcoded `KEY_*` constants. Bindings stored as action → {keycode, ctrl,
1031
shift, alt} maps. `load_or_default()` reads `user://hammerforge_keymap.json` or falls back to
@@ -49,9 +70,9 @@ The format is based on Keep a Changelog, and this project follows semantic versi
4970
`autosave_failed` signal on LevelRoot. Dock shows a red warning label when autosave fails.
5071
Warning auto-hides after 30 seconds and reappears on subsequent failures.
5172
- **Central signal registry** on LevelRoot: `brush_added`, `brush_removed`, `brush_changed`,
52-
`entity_added`, `entity_removed`, `selection_changed`, `paint_layer_changed`, `state_saved`,
53-
`state_loaded`, `autosave_failed`. Subsystems emit these signals; UI can subscribe instead of
54-
polling.
73+
`entity_added`, `entity_removed`, `selection_changed`, `paint_layer_changed`,
74+
`material_list_changed`, `face_selection_changed`, `state_saved`, `state_loaded`,
75+
`autosave_failed`. Subsystems emit these signals; UI subscribes instead of polling.
5576
- **Material manager persistence**: `save_library()` / `load_library()` for JSON-based material
5677
palette save/load. Usage tracking via `record_usage()` / `release_usage()` /
5778
`find_unused_materials()`.
@@ -70,8 +91,8 @@ The format is based on Keep a Changelog, and this project follows semantic versi
7091
- **Duplicator / instanced geometry** (`hf_duplicator.gd`): create N copies of selected
7192
brushes with progressive offset. `HFDuplicator` RefCounted class with `generate()`,
7293
`clear_instances()`, `to_dict()`/`from_dict()` serialization. Dock UI: count SpinBox,
73-
X/Y/Z offset, Create/Remove Array buttons in Actions section. Undo/redo via state snapshot.
74-
Inspired by QuArK's duplicator system.
94+
X/Y/Z offset, Create/Remove Array buttons in Selection Tools section (Brush tab). Undo/redo
95+
via state snapshot. Inspired by QuArK's duplicator system.
7596
- **Multi-format .map export adapters**: strategy-pattern writers for map export.
7697
- `HFMapAdapter` base class with `format_face_line()` and `format_entity_properties()`.
7798
- `HFMapQuake`: Classic Quake format (existing behavior, extracted).
@@ -86,7 +107,7 @@ The format is based on Keep a Changelog, and this project follows semantic versi
86107
- `clip_brush_by_id(brush_id, axis, split_pos)` on `hf_brush_system.gd`.
87108
- Auto-detect split axis from face normal via `clip_brush_at_point()`.
88109
- Snaps split position to grid. Copies material, brush entity class, visgroups, and group ID.
89-
- Keyboard shortcut: Shift+X. Clip button in Actions section of Manage tab.
110+
- Keyboard shortcut: Shift+X. Clip button in Selection Tools section of Brush tab.
90111
- Full undo/redo support via state snapshot.
91112
- **Entity I/O system:** Source-style entity input/output connections.
92113
- Data model: output connections stored as `entity_io_outputs` meta on entity nodes.
@@ -104,35 +125,35 @@ The format is based on Keep a Changelog, and this project follows semantic versi
104125
- **Hollow tool:** Convert a solid brush into a hollow room with configurable wall thickness.
105126
- Creates 6 wall brushes (top/bottom/left/right/front/back) and removes the original.
106127
- Preserves material from the original brush. Keyboard shortcut: Ctrl+H.
107-
- Wall thickness SpinBox in Actions section of Manage tab.
128+
- Wall thickness SpinBox in Selection Tools section of Brush tab.
108129
- Full undo/redo support via state snapshot.
109130
- **Numeric input during drag:** Type exact dimensions while drawing or extruding brushes.
110131
- During base drag or height adjustment, type digits to set precise size.
111132
- Enter applies the value and advances/commits. Backspace edits. Escape cancels.
112133
- Numeric buffer displayed in the shortcut HUD during drag.
113134
- **Brush entity conversion (Tie to Entity):** Tag brushes as brush entity classes.
114-
- Tie/Untie buttons in Actions section with class dropdown (func_detail, func_wall, trigger_once, trigger_multiple).
135+
- Tie/Untie buttons in Selection Tools section (Brush tab) with class dropdown (func_detail, func_wall, trigger_once, trigger_multiple).
115136
- `func_detail` brushes are excluded from structural CSG bake (detail geometry).
116137
- `trigger_*` brushes are excluded from structural bake (collision-only volumes).
117138
- `brush_entity_class` meta persists in `.hflevel` saves and undo/redo state.
118139
- **Move to Floor / Move to Ceiling:** Snap selected brushes to the nearest surface.
119140
- Raycasts against other brushes and physics bodies to find nearest surface.
120141
- Grid-snapped result. Keyboard shortcuts: Ctrl+Shift+F (floor), Ctrl+Shift+C (ceiling).
121-
- Buttons in Actions section of Manage tab. Full undo/redo support.
142+
- Buttons in Selection Tools section of Brush tab. Full undo/redo support.
122143
- **Texture alignment Justify panel:** Quick UV alignment controls in the UV Editor section.
123144
- Fit, Center, Left, Right, Top, Bottom alignment modes.
124145
- "Treat as One" checkbox for aligning multiple selected faces as a unified surface.
125146
- Works with the existing face selection system.
126147
- **Hammer gap analysis** documented in ROADMAP.md with prioritized wave plan.
127148
- **Dock UX overhaul:** Consolidated from 8 tabs to 4 (Brush, Paint, Entities, Manage).
128-
- New `HFCollapsibleSection` (`ui/collapsible_section.gd`) reusable component for collapsible UI sections.
129-
- **Brush tab** (was Build): focused on shape, size, grid snap, material, operation mode, texture lock.
130-
- **Paint tab** (merged FloorPaint + SurfacePaint + Materials + UV): 7 collapsible sections.
131-
- **Manage tab** reorganized with 8 collapsible sections: Bake, Actions, File, Presets, History, Settings, Performance, plus Visgroups & Cordon.
132-
- "No LevelRoot" banner displayed at dock top when no root is found.
133-
- Toolbar buttons now show keyboard shortcut labels: `Draw (D)`, `Sel (S)`, `Ext▲ (U)`, `Ext▼ (J)`.
134-
- Paint and Manage tab contents built programmatically via `_build_paint_tab()` and `_build_manage_tab()`.
135-
- Bake options and editor toggles moved from old Build tab into Manage → Bake Settings and Settings sections.
149+
- `HFCollapsibleSection` (`ui/collapsible_section.gd`) with HSeparator, indented content, persisted collapsed state.
150+
- **Brush tab** (was Build): shape, size, grid snap, material, operation mode, texture lock, plus contextual **Selection Tools** section (hollow, clip, move, tie, duplicator — visible when brushes selected).
151+
- **Paint tab** (merged FloorPaint + SurfacePaint + Materials + UV): 7 collapsible sections. UV Justify uses 3×2 grid layout.
152+
- **Manage tab**: Bake, Actions (floor/cuts/clear), File, Presets, History, Settings, Performance, plus Visgroups & Cordon.
153+
- "No LevelRoot" banner and autosave warning defined in dock.tscn.
154+
- Compact toolbar: single-char labels (D, S, +, -, P, ▲, ▼) with tooltips. VSeparator before extrude buttons.
155+
- Paint/material sync is signal-driven (instant). Form label widths standardized to 70px. +/- buttons 32px wide.
156+
- Tab contents built programmatically via `_build_paint_tab()`, `_build_manage_tab()`, `_build_selection_tools_section()`.
136157
- **Sticky LevelRoot discovery:** Users no longer need to re-select LevelRoot after clicking other nodes.
137158
- `plugin.gd`: `_handles()` returns true for any node when a LevelRoot exists; `_edit()` keeps `active_root` sticky; deep recursive tree search via `_find_level_root_deep()`.
138159
- `dock.gd`: sticky `level_root` reference in `_process()`; deep recursive search via `_find_level_root_in()` / `_find_level_root_recursive()`.
@@ -224,10 +245,11 @@ The format is based on Keep a Changelog, and this project follows semantic versi
224245
- Dock reorganized: Floor Paint and Surface Paint tabs.
225246

226247
### Changed
227-
- Dock consolidated from 8 tabs (Build, FloorPaint, Materials, UV, SurfacePaint, Entities, Manage) to 4 tabs (Brush, Paint, Entities, Manage).
248+
- Dock consolidated from 8 tabs to 4 (Brush, Paint, Entities, Manage). Selection-dependent tools (hollow, clip, move, tie, duplicator) moved from Manage → Brush tab's contextual Selection Tools section.
228249
- Build tab renamed to **Brush** tab; bake options and editor toggles moved to Manage tab.
229250
- FloorPaint, SurfacePaint, Materials, and UV tabs merged into single **Paint** tab with collapsible sections.
230-
- Manage tab reorganized with collapsible sections for better discoverability.
251+
- Manage tab trimmed: Actions section now contains only floor/cuts/clear. Toolbar uses single-char labels with tooltips.
252+
- Paint layer/material/surface paint sync changed from 10-frame polling to signal-driven instant updates.
231253
- LevelRoot discovery is now "sticky": selecting non-LevelRoot nodes no longer breaks viewport input.
232254
- Plugin `_handles()` uses deep recursive tree search and accepts any node when a LevelRoot exists.
233255
- Dock `_process()` uses sticky reference; only nulls `level_root` when node is removed from tree.
@@ -287,9 +309,10 @@ The format is based on Keep a Changelog, and this project follows semantic versi
287309
- Added CI workflow (`.github/workflows/ci.yml`) for automated `gdformat` and `gdlint` checks on push/PR.
288310

289311
### Refactored
290-
- Dock UX: rewrote `dock.gd` to build Paint and Manage tab contents programmatically using `HFCollapsibleSection`.
312+
- Dock UX: rewrote `dock.gd` to build Paint, Manage, and Selection Tools contents programmatically using `HFCollapsibleSection`.
291313
- Dock UX: ~100 `@onready var` declarations changed to plain `var` (controls created in code, not in .tscn).
292-
- Dock UX: `dock.tscn` reduced to ~280 lines (tab shells only; content populated by `_ready()`).
314+
- Dock UX: `dock.tscn` reduced to ~320 lines (tab shells + toolbar + autosave warning; content populated by `_ready()`).
315+
- Dock UX: collapsible sections now have HSeparator + indented content + persisted state. All 18 sections registered in `_all_sections` dict.
293316
- Replaced duck-typing in `baker.gd` (`has_method("get_faces")/.call()`) with typed `DraftBrush` access.
294317
- Added `_find_level_root_deep()` to `plugin.gd` for recursive LevelRoot discovery.
295318
- Added `_find_level_root_in()` and `_find_level_root_recursive()` to `dock.gd` for deep tree search.
@@ -315,14 +338,17 @@ The format is based on Keep a Changelog, and this project follows semantic versi
315338
- Cordon visual: persistent `ImmediateMesh` reused via `clear_surfaces()`.
316339
- Extracted inline GLSL to `highlight.gdshader` file.
317340
- Added `build_heightmap_model()` on `hf_paint_tool.gd` (shared by 3 heightmap reconcile callers).
318-
- Signal-driven sync in `dock.gd`: replaced 17 per-frame property writes with signal handlers; throttled perf updates (every 30 frames), sync calls (every 10 frames), flag-driven disabled hints; cached `_control_has_property()`.
341+
- Signal-driven sync in `dock.gd`: replaced 17 per-frame property writes with signal handlers; paint/material/surface paint sync now fully signal-driven via LevelRoot signals; throttled perf updates (every 30 frames), flag-driven disabled hints; cached `_control_has_property()`.
319342
- Input decomposition in `plugin.gd`: split 260-line `_forward_3d_gui_input()` into ~50-line dispatcher + 7 focused handlers + shared `_get_nudge_direction()`.
320343

321344
### UX
322345
- Dock now has 4 tabs (Brush, Paint, Entities, Manage) instead of 8 for faster navigation.
323-
- Collapsible sections throughout Paint and Manage tabs for visual hierarchy and reduced scrolling.
346+
- Selection tools (hollow, clip, move, tie, duplicator) appear contextually in Brush tab when brushes are selected.
347+
- Collapsible sections with separators, indented content, and persisted collapsed state across sessions.
324348
- "No LevelRoot" banner at dock top guides users when no LevelRoot is found.
325-
- Toolbar buttons display keyboard shortcut labels (Draw (D), Sel (S), Ext▲ (U), Ext▼ (J)).
349+
- Compact toolbar with single-char labels (D, S, +, -, P, ▲, ▼) and descriptive tooltips.
350+
- Paint layer and material changes sync instantly (signal-driven, no 167ms polling delay).
351+
- Wider +/- buttons (32px), standardized label widths (70px), UV Justify in clean 3×2 grid.
326352
- LevelRoot stays active when clicking other scene nodes (sticky root discovery).
327353
- Shortcut HUD now shows context-sensitive shortcuts (6 different views: draw idle, dragging base, adjusting height, select, floor paint, surface paint).
328354
- HUD displays current axis lock state (e.g. "[X Locked]").

CONTRIBUTING.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ Thanks for helping improve HammerForge.
2020
- New entity types go in `entities.json`, not hardcoded in GDScript.
2121
- New input tools should subclass `HFGesture` for self-contained state management.
2222
- Subscribe to LevelRoot signals instead of polling in `_process()`.
23-
- **Keyboard shortcuts** go through `_keymap.matches("action_name", event)`, never hardcoded `KEY_*` checks. Add new default bindings in `HFKeymap._default_bindings()`.
23+
- **Keyboard shortcuts** go through `_keymap.matches("action_name", event)`, never hardcoded `KEY_*` checks. Add new default bindings in `HFKeymap._default_bindings()`. Toolbar uses single-char labels with tooltips.
2424
- **External tools** should implement `can_activate()` for tool availability and `get_settings_schema()` for auto-generated dock UI. See `hf_editor_tool.gd` for the full API.
25+
- **New dock sections** should use `HFCollapsibleSection.create()` and register with `_register_section()` for persisted collapse state. Use 70px label widths for form rows.
2526
- **Brush mutations** should call `root.tag_brush_dirty(id)` (guarded with `has_method`) so the reconciler can skip unchanged geometry.
2627
- **Multi-brush operations** should wrap in `begin_signal_batch()` / `end_signal_batch()` (or use transactions, which batch automatically) to prevent UI thrash.
2728
- **User preferences** (application-scoped) go in `HFUserPrefs`. **Level settings** go on LevelRoot.

0 commit comments

Comments
 (0)