Skip to content

Commit fff0e9b

Browse files
committed
refactor: extract status bar
1 parent ab34d1e commit fff0e9b

5 files changed

Lines changed: 156 additions & 65 deletions

File tree

NEXT.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ replacing the former flat `src/MeshCraft/MeshCraftApplication_*.cpp` layout.
2020
`MeshCraft::Application::MeshCraftApplication`; the old public include remains
2121
a compatibility forwarder. Actual UI components currently cover Validation,
2222
Registry results, Toolbar tools/display/snap-surface/proportional/grid controls,
23-
Properties delegation, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, and the View-menu panel/overlay/direction/focus/
23+
Properties delegation, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, Status Bar, and the View-menu panel/overlay/direction/focus/
2424
Camera-Bookmarks/Walk-Mode/Bloom-SSAO presentation, plus the Add/CSG and Help menu
2525
presentation and the Edit-history, clipboard, object-action, and
2626
selection-action groups, the Select-by-Type/Tag/Material, Align-Selection,
@@ -105,7 +105,7 @@ before when explicitly requested (`SYS-W14-##` rows).
105105
## 2. Current status
106106

107107
- **Last full build: clean after the current Phase 13 File-open-recent,
108-
View-Bloom/SSAO, SSAO-regression, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, and Measurement Overlay slices.** Testing is enabled in the current Ninja Release tree, and
108+
View-Bloom/SSAO, SSAO-regression, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, and Status Bar slices.** Testing is enabled in the current Ninja Release tree, and
109109
`CCACHE_DISABLE=1 cmake --build b-release -j4` linked all targets successfully
110110
on EASYGL. Alternate-backend runtime qualification remains blocked.
111111
- **Tests:** the fresh Release tree registers 182 tests. All passed again after
@@ -174,7 +174,7 @@ before when explicitly requested (`SYS-W14-##` rows).
174174
application into `MeshCraft::Application`, organized implementation files
175175
by application/UI ownership, and is extracting UI presentation through
176176
narrow contexts. Validation, Registry results, Toolbar controls, Properties
177-
delegation, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, and the View-menu directions/focus/overlays/panels/
177+
delegation, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, Status Bar, and the View-menu directions/focus/overlays/panels/
178178
Camera-Bookmarks/Walk-Mode/Bloom-SSAO presentation, plus the Add/CSG and Help menu
179179
presentation and the Edit-history, clipboard, object-action, and
180180
selection-action groups, the Select-by-Type/Tag/Material, Align-Selection,
@@ -249,6 +249,14 @@ before when explicitly requested (`SYS-W14-##` rows).
249249
point hint, and lower-left result panel without access to the camera,
250250
document, or mutation paths. Its header compiles independently and focused
251251
`mc3_commands` plus Xvfb `smoke_test` regressions passed.
252+
- **Recently implemented (2026-07-25):** the bottom bar is now
253+
`Application::UI::StatusBar`. The application prepares either a timed
254+
notification or a scene-summary snapshot and supplies validation metadata
255+
plus the sole callback to open the existing panel. Notification lifetime,
256+
document traversal, selection, and validation-panel state remain in the
257+
application. The component preserves the colors, selected-object text,
258+
validation indicator, tooltip, and click action; its header compiles
259+
independently and focused `mc3_commands` plus Xvfb `smoke_test` passed.
252260
- **Recently implemented (2026-07-20 through 2026-07-25):** all 7
253261
raw-OpenGL(ES)-vs-CNA migrations, `AUD-082` through `AUD-088` — full
254262
detail with file:line evidence and
@@ -850,7 +858,7 @@ git stash pop && cmake --build b-release -j4 --target <affected-target>
850858
No actionable follow-up audit task remains: `AUD-089` through `AUD-092` are
851859
complete, while Android (`AUD-042`) is environment/owner deferred.
852860
`SYS-W3-01` has 12 completed subsystem phases and an active Phase 13 for
853-
application/UI ownership. The authorized Camera Bookmarks, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, Walk Mode, View
861+
application/UI ownership. The authorized Camera Bookmarks, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, Status Bar, Walk Mode, View
854862
Bloom/SSAO, Help, Add/CSG, Edit-history, Edit-clipboard, and Edit-object-actions
855863
menu slices are implemented and verified, together with Edit-selection-actions,
856864
Edit-select-by-type/tag/material, Edit-copy-properties, Edit-grouping,
@@ -1113,10 +1121,19 @@ one item does not authorize the next.
11131121
retains measurement state and world-to-screen projection, passing only
11141122
projected points, values, and viewport bounds to the component.
11151123

1116-
5. **Fresh post-overlay boundary audit.** Identify the next coherent
1117-
presentation-only cluster before changing `MeshCraftApplication`; avoid a
1118-
broad `Overlays.cpp` extraction or a context that exposes document/renderer
1119-
internals.
1124+
5. **Fourth post-overlay follow-up — complete.** The bottom bar is now
1125+
`Application::UI::StatusBar`; application code constructs its notification
1126+
or scene-summary snapshot and retains the validation-panel transition.
1127+
1128+
### Final post-overlay audit (2026-07-25)
1129+
1130+
No additional safe narrow extraction remains. `drawDialogs()` is composed of
1131+
stateful workflows with their own buffers, transitions, undo/document effects,
1132+
and keyboard routes; `drawPanelSplitters()` owns global panel geometry; and
1133+
`drawShadowDebugOverlay()` carries a live renderer texture token plus shadow
1134+
light lookup. Splitting any one without a new, broader ownership design would
1135+
create a god-context or relocate application state into UI. The next work should
1136+
be a separately scoped subsystem, not another mechanical `Overlays.cpp` slice.
11201137

11211138
### Tracked work that is not implementation-ready
11221139

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <string_view>
5+
6+
namespace MeshCraft::Application::UI {
7+
8+
struct StatusBarContext {
9+
int screenWidth;
10+
int screenHeight;
11+
int statusHeight;
12+
bool hasNotification;
13+
bool notificationIsError;
14+
std::string_view notificationMessage{""};
15+
int totalObjectCount;
16+
int selectedObjectCount;
17+
std::string_view selectedObjectName{""};
18+
bool hasValidation;
19+
int validationEntryCount;
20+
std::string_view validationSource{""};
21+
std::function<void()> openValidation;
22+
};
23+
24+
class StatusBar final {
25+
public:
26+
static void draw(const StatusBarContext& context);
27+
};
28+
29+
} // namespace MeshCraft::Application::UI

plan.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ still internally consistent.
164164
research pass.
165165
6. **SYS-W3-01 (P2/W3), Phase 13 is active:** application/UI ownership is
166166
being reduced one narrow presentation slice at a time. The authorized
167-
Camera Bookmarks, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, Walk Mode, View Bloom/SSAO, Help, Add/CSG, Edit-history,
167+
Camera Bookmarks, Camera Preset Overlay, Gizmo Drag Overlay, Stats Overlay, Measurement Overlay, Status Bar, Walk Mode, View Bloom/SSAO, Help, Add/CSG, Edit-history,
168168
Edit-clipboard,
169169
Edit-object-actions, Edit-selection-actions, Edit-select-by-type,
170170
Edit-select-by-tag/material, Edit-copy-properties, Edit-grouping,
@@ -194,7 +194,12 @@ still internally consistent.
194194
traversal and renderer statistics stay application-owned. The next audit
195195
moved 2D measurement rendering into `Application::UI::MeasurementOverlay`;
196196
world-to-screen projection and measurement state stay application-owned.
197-
The next Phase 13 action is a fresh narrow boundary audit.
197+
The final narrow overlay slice moved the bottom status bar into
198+
`Application::UI::StatusBar`; scene summary calculation, notification
199+
lifecycle, and validation-panel state remain application-owned. A final
200+
audit found no additional safe narrow boundary: the remaining dialogs are
201+
stateful workflows, splitters own global layout, and Shadow Debug carries a
202+
live render resource token.
198203

199204
---
200205

@@ -638,6 +643,10 @@ _All items in this workstream are DONE — archived to [`docs/history/plan_20260
638643
point markers, hint, and result panel. `Overlays.cpp` retains the cached
639644
view-projection calculation and all measurement state, passing projected
640645
points, immutable coordinates, distance, and viewport bounds by value.
646+
`Application::UI::StatusBar` owns only bottom-bar presentation. The
647+
application constructs the notification or scene-summary snapshot and retains
648+
notification lifetime, scene traversal, selection, validation data, and the
649+
sole callback that opens the existing validation panel.
641650
The bookmark state remains the already-extracted
642651
`Editor::CameraBookmarks`; a `CameraBookmarksContext` exposes only the
643652
read-only slots plus Save/Restore callbacks, leaving camera mutation and
@@ -814,7 +823,7 @@ _All items in this workstream are DONE — archived to [`docs/history/plan_20260
814823
File-export-selection, File-export-GLB, File-export-OBJ, File-save,
815824
File-save-as, File-import-OBJ, File-new, File-open, File-open-recent, and
816825
File-exit slices, the Bloom/SSAO controls, CameraPresetOverlay, and
817-
GizmoDragOverlay, StatsOverlay, and MeasurementOverlay,
826+
GizmoDragOverlay, StatsOverlay, MeasurementOverlay, and StatusBar,
818827
each incremental Release link and the same 147/147 + 35/35 partitions pass
819828
again.
820829
For the current MenuBar slices, the public UI header also compiles as a
@@ -827,7 +836,9 @@ _All items in this workstream are DONE — archived to [`docs/history/plan_20260
827836
and callbacks. GizmoDragOverlay follows the same boundary for a pure
828837
read-only drag snapshot, and StatsOverlay does so for the renderer-derived
829838
top-right summary. MeasurementOverlay similarly consumes only projected
830-
ruler values. The next Phase 13 action is a fresh narrow-boundary audit.
839+
ruler values, while StatusBar presents the application-computed bottom
840+
summary. No further safe narrow Phase 13 extraction remains after the final
841+
overlay audit.
831842
**SYS-W3-01 roadmap status after this session's investigation round:**
832843
Phases 1–12 done (Keybindings, Preferences, MacroRecorder, UndoManager,
833844
animation-override computation, WalkController, AudioPreview,

src/MeshCraft/Application/UI/Overlays.cpp

Lines changed: 32 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "MeshCraft/Application/UI/GizmoDragOverlay.hpp"
44
#include "MeshCraft/Application/UI/MeasurementOverlay.hpp"
55
#include "MeshCraft/Application/UI/StatsOverlay.hpp"
6+
#include "MeshCraft/Application/UI/StatusBar.hpp"
67
#include "MeshCraft/MeshCraftPrivate.hpp"
78
#include "MeshCraft/EditorAlgorithms.hpp"
89
#include "MeshCraft/Scene/SceneHierarchyPanel.hpp"
@@ -165,59 +166,37 @@ void MeshCraftApplication::drawStatsOverlay(int screenW, [[maybe_unused]] int sc
165166

166167
void MeshCraftApplication::drawStatusBar(int screenW, int screenH)
167168
{
168-
(void)screenW;
169-
// Status bar (bottom)
170-
// -----------------------------------------------------------------------
171-
ImGui::SetNextWindowPos(ImVec2(0, static_cast<float>(screenH - kStatusH)));
172-
ImGui::SetNextWindowSize(ImVec2(static_cast<float>(screenW), static_cast<float>(kStatusH)));
173-
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6, 3));
174-
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.086f, 0.094f, 0.176f, 1.0f));
175-
ImGui::Begin("##statusbar", nullptr,
176-
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
177-
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoScrollbar |
178-
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings);
179-
{
180-
// Timed notification takes priority; falls back to scene info
181-
if (statusNotification_.active()) {
182-
ImVec4 col = statusNotification_.isError() ? ImVec4(1.0f, 0.45f, 0.45f, 1.0f)
183-
: ImVec4(0.55f, 1.0f, 0.55f, 1.0f);
184-
ImGui::TextColored(col, "%s", statusNotification_.message().c_str());
185-
} else {
186-
int totalObjs = 0;
187-
std::function<void(const std::vector<std::shared_ptr<Mc3::Mc3Object>>&)> countAll =
188-
[&](const std::vector<std::shared_ptr<Mc3::Mc3Object>>& list) {
189-
totalObjs += static_cast<int>(list.size());
190-
for (const auto& o : list) countAll(o->children);
191-
};
192-
countAll(document_.objects);
193-
int selCount = static_cast<int>(selection_.selection().size());
194-
if (selCount > 0) {
195-
const std::string& selName = selection_.selection().front()->name;
196-
ImGui::Text("%d objects | %d selected | %s", totalObjs, selCount, selName.c_str());
197-
} else {
198-
ImGui::Text("%d objects", totalObjs);
199-
}
200-
}
201-
202-
// SYS-W14-02: small clickable indicator for the last load/save/export's
203-
// validation findings -- otherwise SYS-W1-01's diagnostics are console-only.
204-
if (!lastValidation_.empty()) {
205-
const std::string label =
206-
"[!] " + std::to_string(lastValidation_.entries.size()) + " validation note(s)";
207-
ImVec2 textSize = ImGui::CalcTextSize(label.c_str());
208-
ImGui::SameLine(ImGui::GetWindowWidth() - textSize.x - 12.0f);
209-
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.35f, 1.0f), "%s", label.c_str());
210-
if (ImGui::IsItemClicked()) showValidationPanel_ = true;
211-
if (ImGui::IsItemHovered())
212-
ImGui::SetTooltip("From: %s\nClick to open the Validation panel",
213-
lastValidationSource_.c_str());
214-
}
215-
}
216-
ImGui::End();
217-
ImGui::PopStyleColor();
218-
ImGui::PopStyleVar();
219-
220-
169+
const bool hasNotification = statusNotification_.active();
170+
int totalObjects = 0;
171+
int selectedObjects = 0;
172+
std::string_view selectedObjectName;
173+
if (!hasNotification) {
174+
std::function<void(const std::vector<std::shared_ptr<Mc3::Mc3Object>>&)> countAll =
175+
[&](const std::vector<std::shared_ptr<Mc3::Mc3Object>>& list) {
176+
totalObjects += static_cast<int>(list.size());
177+
for (const auto& object : list)
178+
countAll(object->children);
179+
};
180+
countAll(document_.objects);
181+
selectedObjects = static_cast<int>(selection_.selection().size());
182+
if (selectedObjects > 0)
183+
selectedObjectName = selection_.selection().front()->name;
184+
}
185+
UI::StatusBar::draw({
186+
.screenWidth = screenW,
187+
.screenHeight = screenH,
188+
.statusHeight = kStatusH,
189+
.hasNotification = hasNotification,
190+
.notificationIsError = statusNotification_.isError(),
191+
.notificationMessage = statusNotification_.message(),
192+
.totalObjectCount = totalObjects,
193+
.selectedObjectCount = selectedObjects,
194+
.selectedObjectName = selectedObjectName,
195+
.hasValidation = !lastValidation_.empty(),
196+
.validationEntryCount = static_cast<int>(lastValidation_.entries.size()),
197+
.validationSource = lastValidationSource_,
198+
.openValidation = [this] { showValidationPanel_ = true; },
199+
});
221200
}
222201

223202
void MeshCraftApplication::drawDialogs()
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "MeshCraft/Application/UI/StatusBar.hpp"
2+
3+
#include <imgui.h>
4+
5+
#include <string>
6+
7+
namespace MeshCraft::Application::UI {
8+
9+
void StatusBar::draw(const StatusBarContext& context)
10+
{
11+
ImGui::SetNextWindowPos(ImVec2(0, static_cast<float>(context.screenHeight - context.statusHeight)));
12+
ImGui::SetNextWindowSize(
13+
ImVec2(static_cast<float>(context.screenWidth), static_cast<float>(context.statusHeight)));
14+
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6, 3));
15+
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.086f, 0.094f, 0.176f, 1.0f));
16+
ImGui::Begin("##statusbar", nullptr,
17+
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
18+
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoScrollbar |
19+
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings);
20+
21+
if (context.hasNotification) {
22+
const ImVec4 color = context.notificationIsError
23+
? ImVec4(1.0f, 0.45f, 0.45f, 1.0f)
24+
: ImVec4(0.55f, 1.0f, 0.55f, 1.0f);
25+
ImGui::TextColored(color, "%.*s", static_cast<int>(context.notificationMessage.size()),
26+
context.notificationMessage.data());
27+
} else if (context.selectedObjectCount > 0) {
28+
ImGui::Text("%d objects | %d selected | %.*s", context.totalObjectCount,
29+
context.selectedObjectCount, static_cast<int>(context.selectedObjectName.size()),
30+
context.selectedObjectName.data());
31+
} else {
32+
ImGui::Text("%d objects", context.totalObjectCount);
33+
}
34+
35+
if (context.hasValidation) {
36+
const std::string label =
37+
"[!] " + std::to_string(context.validationEntryCount) + " validation note(s)";
38+
const ImVec2 textSize = ImGui::CalcTextSize(label.c_str());
39+
ImGui::SameLine(ImGui::GetWindowWidth() - textSize.x - 12.0f);
40+
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.35f, 1.0f), "%s", label.c_str());
41+
if (ImGui::IsItemClicked())
42+
context.openValidation();
43+
if (ImGui::IsItemHovered()) {
44+
ImGui::SetTooltip("From: %.*s\nClick to open the Validation panel",
45+
static_cast<int>(context.validationSource.size()),
46+
context.validationSource.data());
47+
}
48+
}
49+
50+
ImGui::End();
51+
ImGui::PopStyleColor();
52+
ImGui::PopStyleVar();
53+
}
54+
55+
} // namespace MeshCraft::Application::UI

0 commit comments

Comments
 (0)