Files
whetstone_DSL/editor/src/PanelManager.h
2026-02-16 08:24:20 -07:00

231 lines
7.9 KiB
C++

#pragma once
// Step 342-343: Panel registration system and docking layout management
// Headless data model for panel state — no ImGui dependency
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <algorithm>
#include <nlohmann/json.hpp>
enum class DockPosition {
Left,
CenterTop,
CenterBottom,
Right,
Bottom
};
inline std::string dockPositionToString(DockPosition p) {
switch (p) {
case DockPosition::Left: return "left";
case DockPosition::CenterTop: return "center-top";
case DockPosition::CenterBottom: return "center-bottom";
case DockPosition::Right: return "right";
case DockPosition::Bottom: return "bottom";
}
return "unknown";
}
inline DockPosition dockPositionFromString(const std::string& s) {
if (s == "left") return DockPosition::Left;
if (s == "center-top") return DockPosition::CenterTop;
if (s == "center-bottom") return DockPosition::CenterBottom;
if (s == "right") return DockPosition::Right;
if (s == "bottom") return DockPosition::Bottom;
return DockPosition::CenterTop;
}
struct PanelInfo {
std::string id;
std::string title;
DockPosition defaultDock;
DockPosition currentDock;
bool isVisible = true;
int order = 0;
std::string icon; // Unicode icon character
std::string category; // "navigation", "editor", "analysis", "output"
float widthPercent = 0; // 0 = auto
float heightPercent = 0; // 0 = auto
nlohmann::json toJson() const {
return {
{"id", id}, {"title", title},
{"defaultDock", dockPositionToString(defaultDock)},
{"currentDock", dockPositionToString(currentDock)},
{"isVisible", isVisible}, {"order", order},
{"icon", icon}, {"category", category},
{"widthPercent", widthPercent}, {"heightPercent", heightPercent}
};
}
static PanelInfo fromJson(const nlohmann::json& j) {
PanelInfo p;
p.id = j.value("id", "");
p.title = j.value("title", "");
p.defaultDock = dockPositionFromString(j.value("defaultDock", "center-top"));
p.currentDock = dockPositionFromString(j.value("currentDock", j.value("defaultDock", "center-top")));
p.isVisible = j.value("isVisible", true);
p.order = j.value("order", 0);
p.icon = j.value("icon", "");
p.category = j.value("category", "");
p.widthPercent = j.value("widthPercent", 0.0f);
p.heightPercent = j.value("heightPercent", 0.0f);
return p;
}
};
struct DockingLayout {
float leftWidth = 20.0f; // percent
float rightWidth = 20.0f; // percent
float bottomHeight = 25.0f; // percent
float centerWidth = 60.0f; // auto-computed
nlohmann::json toJson() const {
return {
{"leftWidth", leftWidth}, {"rightWidth", rightWidth},
{"bottomHeight", bottomHeight}
};
}
static DockingLayout fromJson(const nlohmann::json& j) {
DockingLayout l;
l.leftWidth = j.value("leftWidth", 20.0f);
l.rightWidth = j.value("rightWidth", 20.0f);
l.bottomHeight = j.value("bottomHeight", 25.0f);
l.centerWidth = 100.0f - l.leftWidth - l.rightWidth;
return l;
}
};
class PanelManager {
public:
PanelManager() = default;
void registerPanel(const std::string& id, const std::string& title,
DockPosition dock, const std::string& icon = "",
const std::string& category = "", int order = 0) {
PanelInfo info;
info.id = id;
info.title = title;
info.defaultDock = dock;
info.currentDock = dock;
info.icon = icon;
info.category = category;
info.order = order;
panels_[id] = info;
}
void setVisible(const std::string& id, bool visible) {
auto it = panels_.find(id);
if (it != panels_.end()) it->second.isVisible = visible;
}
bool isVisible(const std::string& id) const {
auto it = panels_.find(id);
return it != panels_.end() && it->second.isVisible;
}
void togglePanel(const std::string& id) {
auto it = panels_.find(id);
if (it != panels_.end()) it->second.isVisible = !it->second.isVisible;
}
void focusPanel(const std::string& id) {
focusedPanel_ = id;
}
std::string getFocusedPanel() const { return focusedPanel_; }
bool hasPanel(const std::string& id) const {
return panels_.count(id) > 0;
}
PanelInfo getPanel(const std::string& id) const {
auto it = panels_.find(id);
if (it != panels_.end()) return it->second;
return {};
}
std::vector<PanelInfo> getPanelList() const {
std::vector<PanelInfo> result;
for (const auto& [id, info] : panels_) result.push_back(info);
std::sort(result.begin(), result.end(),
[](const PanelInfo& a, const PanelInfo& b) { return a.order < b.order; });
return result;
}
std::vector<PanelInfo> getVisiblePanels() const {
std::vector<PanelInfo> result;
for (const auto& [id, info] : panels_) {
if (info.isVisible) result.push_back(info);
}
std::sort(result.begin(), result.end(),
[](const PanelInfo& a, const PanelInfo& b) { return a.order < b.order; });
return result;
}
std::vector<PanelInfo> getPanelsByDock(DockPosition dock) const {
std::vector<PanelInfo> result;
for (const auto& [id, info] : panels_) {
if (info.currentDock == dock && info.isVisible) result.push_back(info);
}
std::sort(result.begin(), result.end(),
[](const PanelInfo& a, const PanelInfo& b) { return a.order < b.order; });
return result;
}
void movePanelToDock(const std::string& id, DockPosition dock) {
auto it = panels_.find(id);
if (it != panels_.end()) it->second.currentDock = dock;
}
int panelCount() const { return static_cast<int>(panels_.size()); }
void registerDefaults() {
registerPanel("file-tree", "File Tree", DockPosition::Left, "\xF0\x9F\x93\x81", "navigation", 0);
registerPanel("code-editor", "Code Editor", DockPosition::CenterTop, "\xE2\x9C\x8F", "editor", 1);
registerPanel("ast-view", "AST View", DockPosition::Right, "\xF0\x9F\x8C\xB3", "analysis", 2);
registerPanel("annotations", "Annotations", DockPosition::Right, "\xF0\x9F\x8F\xB7", "analysis", 3);
registerPanel("properties", "Properties", DockPosition::Right, "\xE2\x9A\x99", "analysis", 4);
registerPanel("diagnostics", "Diagnostics", DockPosition::Bottom, "\xE2\x9A\xA0", "output", 5);
registerPanel("output", "Output", DockPosition::Bottom, "\xF0\x9F\x93\x84", "output", 6);
registerPanel("workflow", "Workflow", DockPosition::Bottom, "\xF0\x9F\x94\x84", "output", 7);
}
// Layout management
DockingLayout& layout() { return layout_; }
const DockingLayout& layout() const { return layout_; }
nlohmann::json toJson() const {
nlohmann::json j;
j["layout"] = layout_.toJson();
nlohmann::json panelsJson = nlohmann::json::array();
for (const auto& [id, info] : panels_) {
panelsJson.push_back(info.toJson());
}
j["panels"] = panelsJson;
j["focusedPanel"] = focusedPanel_;
return j;
}
static PanelManager fromJson(const nlohmann::json& j) {
PanelManager pm;
if (j.contains("layout")) pm.layout_ = DockingLayout::fromJson(j["layout"]);
if (j.contains("panels")) {
for (const auto& pj : j["panels"]) {
PanelInfo info = PanelInfo::fromJson(pj);
pm.panels_[info.id] = info;
}
}
pm.focusedPanel_ = j.value("focusedPanel", "");
return pm;
}
private:
std::map<std::string, PanelInfo> panels_;
DockingLayout layout_;
std::string focusedPanel_;
};