#pragma once #include "workspace.h" // AppActions, UIContext #include "rendering/view.h" #include struct ImFont; namespace Donut { // the workspaces and the panels they're built from. enum class Tab { Simulation, SceneEditor, Export }; enum class Panel { Outliner, Properties, BlackHole, Settings, Export, Stats }; inline constexpr int kTabCount = 3; inline constexpr int kPanelCount = 6; // the application's docking shell: a main menu bar, a workspace tab strip // ([ Simulation ] [ Scene Editor ] [ Export ]), a bottom status bar, and one // full-viewport dock space per workspace with a pass-through centre so the // live 3D render shows through. every workspace keeps its own dock tree and // its own panel instances, so rearranging one never touches the others, and // ImGui persists all of them in imgui.ini. draw() returns the View the // RenderPath should draw this frame. class UILayer { public: UILayer(); // loads the Inter faces and registers the workspace ini section on the // live ImGui context. call once after the device's init_imgui() and before // the first draw(): ImGui reads imgui.ini on its first NewFrame. auto init(float dpi_scale = 1.0f) -> void; auto draw(const UIContext& ctx) -> View; private: // one workspace: a fixed centre view, its own dock tree, its own window // instances of the panels it owns. struct TabState { Tab id = Tab::Simulation; const char* label = nullptr; // "Scene Editor" const char* key = nullptr; // "scene": the ### suffix and dock-id salt View view = View::BlackHole; unsigned int dock_id = 0; // ImHashStr("donut.dock.") std::string name[kPanelCount]; // "Black Hole###scene.black_hole" bool allowed[kPanelCount] = {}; // belongs to this workspace bool defaults[kPanelCount] = {}; // open after a seed / reset bool show[kPanelCount] = {}; // open right now bool reset = false; // rebuild the tree before it's next submitted bool appearing = false; // first frame back: panels mustn't grab focus }; auto draw_menu_bar(const UIContext& ctx) -> void; auto draw_tab_strip() -> void; auto draw_status_bar(const UIContext& ctx) -> void; auto build_layout(TabState& tab) -> void; // DockBuilder seed for one workspace auto request_tab(int index) -> void { m_tab_request = index; } auto sync_persisted() -> void; auto begin_panel(TabState& tab, Panel p) -> bool; auto draw_panel(const UIContext& ctx, TabState& tab, Panel p) -> void; auto panel_outliner(const UIContext& ctx, TabState& tab) -> void; auto panel_properties(const UIContext& ctx, TabState& tab) -> void; auto panel_black_hole(const UIContext& ctx, TabState& tab) -> void; auto panel_settings(const UIContext& ctx, TabState& tab) -> void; auto panel_export(const UIContext& ctx, TabState& tab) -> void; auto panel_stats(const UIContext& ctx, TabState& tab) -> void; // styled building blocks the panels share (they need the fonts, hence members) auto section(const char* title, bool default_open = true) -> bool; // collapsible heading auto caption(const char* text) -> void; // muted helper text auto segmented(const char* id, const char* const* labels, int count, int& value) -> bool; TabState m_tabs[kTabCount]; int m_active_tab = 0; // index into m_tabs; the source of truth int m_tab_request = -1; // one-frame programmatic strip selection bool m_first_frame = true; float m_dpi_scale = 1.0f; // monitor content scale (windows/linux); 1 on macos // properties-panel gizmo op (0 = translate, 1 = scale); cast in the .cpp. int m_gizmo_op = 0; // settings panel. float m_ui_scale = 1.0f; // export panel. bool m_exp_color = true, m_exp_redshift = false, m_exp_temperature = false, m_exp_impact = false; int m_exp_resolution = 1, m_exp_format = 0; int m_exp_written = 0; bool m_exp_reported = false; // typography: two faces, sized at PushFont time (ImGui 1.92 rasterises on demand). ImFont* m_font_body = nullptr; // Inter Regular ImFont* m_font_heading = nullptr; // Inter SemiBold // rolling frame-time history (ms) for the diagnostics graph. static constexpr int kFrameHist = 120; float m_frame_hist[kFrameHist] = {}; int m_frame_hist_i = 0; }; }