deargui-vpl/applications/nodehub/utilities/style_manager.cpp
2026-02-03 18:25:25 +01:00

418 lines
18 KiB
C++

#include "style_manager.h"
#include <crude_json.h>
#include <imgui_node_editor_internal.h>
#include <fstream>
#include <cstdio>
namespace ax {
namespace NodeEditor {
// Macros for cleaner JSON serialization
#define SAVE_COLOR(obj, field, name) \
obj[name]["r"] = (double)field.Value.x; \
obj[name]["g"] = (double)field.Value.y; \
obj[name]["b"] = (double)field.Value.z; \
obj[name]["a"] = (double)field.Value.w
#define LOAD_COLOR(obj, field, name) \
if (obj.contains(name) && obj[name].is_object()) { \
auto& c = obj[name]; \
field = ImColor( \
c.contains("r") ? (float)c["r"].get<double>() : 1.0f, \
c.contains("g") ? (float)c["g"].get<double>() : 1.0f, \
c.contains("b") ? (float)c["b"].get<double>() : 1.0f, \
c.contains("a") ? (float)c["a"].get<double>() : 1.0f); \
}
#define SAVE_VEC4(obj, field, name) \
obj[name]["x"] = (double)field.x; \
obj[name]["y"] = (double)field.y; \
obj[name]["z"] = (double)field.z; \
obj[name]["w"] = (double)field.w
#define LOAD_VEC4(obj, field, name) \
if (obj.contains(name) && obj[name].is_object()) { \
auto& v = obj[name]; \
field = ImVec4( \
v.contains("x") ? (float)v["x"].get<double>() : 0.0f, \
v.contains("y") ? (float)v["y"].get<double>() : 0.0f, \
v.contains("z") ? (float)v["z"].get<double>() : 0.0f, \
v.contains("w") ? (float)v["w"].get<double>() : 0.0f); \
}
#define SAVE_VEC2(obj, field, name) \
obj[name]["x"] = (double)field.x; \
obj[name]["y"] = (double)field.y
#define LOAD_VEC2(obj, field, name) \
if (obj.contains(name) && obj[name].is_object()) { \
auto& v = obj[name]; \
field = ImVec2( \
v.contains("x") ? (float)v["x"].get<double>() : 0.0f, \
v.contains("y") ? (float)v["y"].get<double>() : 0.0f); \
}
#define SAVE_FLOAT(obj, field, name) obj[name] = (double)field
#define LOAD_FLOAT(obj, field, name) if (obj.contains(name)) field = (float)obj[name].get<double>()
StyleManager::StyleManager()
{
// Initialize with clean, minimal defaults
// Block style
BlockStyle.BgColor = ImColor(50, 50, 60, 240);
BlockStyle.BorderColor = ImColor(100, 100, 110, 255);
BlockStyle.BorderColorRunning = ImColor(255, 0, 0, 255);
BlockStyle.Rounding = 1.0f;
BlockStyle.BorderWidth = 1.0f;
BlockStyle.BorderWidthRunning = 3.0f;
BlockStyle.Padding = ImVec4(8, 8, 8, 8);
// Group style (slightly lighter/different for visual distinction)
GroupStyle.BgColor = ImColor(55, 55, 70, 240); // Slightly different from blocks
GroupStyle.BorderColor = ImColor(110, 110, 120, 255);
GroupStyle.BorderColorRunning = ImColor(255, 0, 0, 255);
GroupStyle.Rounding = 2.0f; // Slightly more rounded
GroupStyle.BorderWidth = 1.0f;
GroupStyle.BorderWidthRunning = 3.0f;
GroupStyle.Padding = ImVec4(12, 12, 12, 12); // More padding for groups
// Parameter style (more rounded and grayer for visual distinction from blocks)
ParameterStyle.BgColor = ImColor(70, 70, 80, 255); // More gray for distinction
ParameterStyle.BorderColor = ImColor(255, 255, 255, 200);
ParameterStyle.BorderColorRunning = ImColor(255, 0, 0, 255);
ParameterStyle.Rounding = 4.0f; // More rounded than blocks
ParameterStyle.BorderWidth = 1.0f;
ParameterStyle.BorderWidthRunning = 3.0f;
ParameterStyle.Padding = ImVec4(4, 2, 4, 2);
// Pin colors (minimal, professional)
PinColor_Input = ImColor(120, 120, 150, 255); // Muted blue
PinColor_Output = ImColor(150, 120, 120, 255); // Muted red
PinColor_Running = ImColor(80, 160, 80, 255); // Muted green
PinColor_Deactivated = ImColor(80, 80, 80, 200); // Dark gray
PinColor_Error = ImColor(200, 80, 80, 255); // Muted red
PinColor_Warning = ImColor(200, 160, 80, 255); // Muted orange
// Pin sizes (small and minimal)
FlowPinSize = 8.0f;
ParameterPinWidth = 8.0f;
ParameterPinHeight = 4.0f;
// Pin edge offsets
ParameterPinEdgeOffset = 0.0f; // Exactly on edge
FlowPinEdgeOffset = 0.0f; // Exactly on edge
// Layout
MinNodeWidth = 80.0f;
MinNodeHeight = 40.0f;
MinGroupSize = 100.0f;
// Group-specific
GroupResizeGripSize = 16.0f;
GroupResizeGripLineSpacing = 3.5f;
GroupResizeGripColor = ImColor(200, 200, 200, 220);
// Parameter node-specific
ParamBorderColorSource = ImColor(255, 215, 0, 255); // Gold
ParamBorderColorShortcut = ImColor(200, 200, 200, 180); // Dimmed
ParamBorderWidthSource = 2.0f;
ParamBorderWidthNameAndValue = 1.5f;
ParamBorderWidthSourceNameAndValue = 2.5f;
ParamPaddingNameOnly = ImVec4(4, 2, 4, 2);
ParamPaddingNameAndValue = ImVec4(8, 4, 8, 4);
ParamPaddingSmallBox = ImVec4(2, 2, 2, 2);
ParamPaddingMinimal = ImVec4(4, 4, 4, 4);
ParamInputWidthNameOnly = 80.0f;
ParamInputWidthNameAndValue = 100.0f;
ParamInputWidthSmallBox = 50.0f;
ParamMinimalSize = ImVec2(27, 20);
ParamMinimalLinksSize = ImVec2(40, 20);
// Link styling
LinkThicknessNormal = 2.0f;
LinkThicknessSelected = 4.0f;
LinkThicknessParameterNormal = 1.5f;
LinkThicknessParameterSelected = 3.0f;
LinkColorHighlighted = ImColor(255, 0, 0, 255); // Red for execution
// Waypoint/Control Point styling
WaypointRadius = 6.0f;
WaypointBorderWidth = 2.0f;
WaypointColor = ImColor(255, 200, 100, 255); // Yellow/orange
WaypointBorderColor = ImColor(0, 0, 0, 200); // Black border
WaypointColorHovered = ImColor(255, 220, 120, 255); // Lighter yellow when hovered
WaypointColorSelected = ImColor(255, 255, 150, 255); // Bright yellow when selected
WaypointPreviewRadius = 4.0f;
WaypointPreviewColor = ImColor(255, 200, 100, 180); // Semi-transparent yellow
WaypointPreviewBorderColor = ImColor(0, 0, 0, 150); // Semi-transparent black
}
bool StyleManager::LoadFromFile(const std::string& filename)
{
std::ifstream file(filename);
if (!file)
{
return false;
}
std::string jsonData((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
return Deserialize(jsonData);
}
bool StyleManager::SaveToFile(const std::string& filename) const
{
std::string jsonData = Serialize();
std::ofstream file(filename);
if (!file)
{
return false;
}
file << jsonData;
return true;
}
std::string StyleManager::Serialize() const
{
crude_json::value root;
// Block Style
auto& bs = root["block_style"];
SAVE_COLOR(bs, BlockStyle.BgColor, "bg_color");
SAVE_COLOR(bs, BlockStyle.BorderColor, "border_color");
SAVE_COLOR(bs, BlockStyle.BorderColorRunning, "border_color_running");
SAVE_FLOAT(bs, BlockStyle.Rounding, "rounding");
SAVE_FLOAT(bs, BlockStyle.BorderWidth, "border_width");
SAVE_FLOAT(bs, BlockStyle.BorderWidthRunning, "border_width_running");
SAVE_VEC4(bs, BlockStyle.Padding, "padding");
// Group Style
auto& gs = root["group_style"];
SAVE_COLOR(gs, GroupStyle.BgColor, "bg_color");
SAVE_COLOR(gs, GroupStyle.BorderColor, "border_color");
SAVE_COLOR(gs, GroupStyle.BorderColorRunning, "border_color_running");
SAVE_FLOAT(gs, GroupStyle.Rounding, "rounding");
SAVE_FLOAT(gs, GroupStyle.BorderWidth, "border_width");
SAVE_FLOAT(gs, GroupStyle.BorderWidthRunning, "border_width_running");
SAVE_VEC4(gs, GroupStyle.Padding, "padding");
SAVE_FLOAT(gs, GroupResizeGripSize, "resize_grip_size");
SAVE_FLOAT(gs, GroupResizeGripLineSpacing, "resize_grip_line_spacing");
SAVE_COLOR(gs, GroupResizeGripColor, "resize_grip_color");
// Parameter Style
auto& ps = root["parameter_style"];
SAVE_COLOR(ps, ParameterStyle.BgColor, "bg_color");
SAVE_COLOR(ps, ParameterStyle.BorderColor, "border_color");
SAVE_FLOAT(ps, ParameterStyle.Rounding, "rounding");
SAVE_FLOAT(ps, ParameterStyle.BorderWidth, "border_width");
SAVE_VEC4(ps, ParameterStyle.Padding, "padding");
SAVE_COLOR(ps, ParamBorderColorSource, "border_color_source");
SAVE_COLOR(ps, ParamBorderColorShortcut, "border_color_shortcut");
SAVE_FLOAT(ps, ParamBorderWidthSource, "border_width_source");
SAVE_FLOAT(ps, ParamBorderWidthNameAndValue, "border_width_name_and_value");
SAVE_FLOAT(ps, ParamBorderWidthSourceNameAndValue, "border_width_source_name_and_value");
SAVE_VEC4(ps, ParamPaddingNameOnly, "padding_name_only");
SAVE_VEC4(ps, ParamPaddingNameAndValue, "padding_name_and_value");
SAVE_VEC4(ps, ParamPaddingSmallBox, "padding_small_box");
SAVE_VEC4(ps, ParamPaddingMinimal, "padding_minimal");
SAVE_FLOAT(ps, ParamInputWidthNameOnly, "input_width_name_only");
SAVE_FLOAT(ps, ParamInputWidthNameAndValue, "input_width_name_and_value");
SAVE_FLOAT(ps, ParamInputWidthSmallBox, "input_width_small_box");
SAVE_VEC2(ps, ParamMinimalSize, "minimal_size");
SAVE_VEC2(ps, ParamMinimalLinksSize, "minimal_links_size");
// Pin Colors
auto& pc = root["pin_colors"];
SAVE_COLOR(pc, PinColor_Input, "input");
SAVE_COLOR(pc, PinColor_Output, "output");
SAVE_COLOR(pc, PinColor_Running, "running");
SAVE_COLOR(pc, PinColor_Deactivated, "deactivated");
SAVE_COLOR(pc, PinColor_Error, "error");
SAVE_COLOR(pc, PinColor_Warning, "warning");
// Pin Sizes
SAVE_FLOAT(root, FlowPinSize, "flow_pin_size");
SAVE_FLOAT(root, ParameterPinWidth, "parameter_pin_width");
SAVE_FLOAT(root, ParameterPinHeight, "parameter_pin_height");
SAVE_FLOAT(root, ParameterPinEdgeOffset, "parameter_pin_edge_offset");
SAVE_FLOAT(root, FlowPinEdgeOffset, "flow_pin_edge_offset");
// Layout
SAVE_FLOAT(root, MinNodeWidth, "min_node_width");
SAVE_FLOAT(root, MinNodeHeight, "min_node_height");
SAVE_FLOAT(root, MinGroupSize, "min_group_size");
// Link styling
auto& ls = root["link_style"];
SAVE_FLOAT(ls, LinkThicknessNormal, "thickness_normal");
SAVE_FLOAT(ls, LinkThicknessSelected, "thickness_selected");
SAVE_FLOAT(ls, LinkThicknessParameterNormal, "thickness_parameter_normal");
SAVE_FLOAT(ls, LinkThicknessParameterSelected, "thickness_parameter_selected");
SAVE_COLOR(ls, LinkColorHighlighted, "color_highlighted");
// Waypoint styling
auto& ws = root["waypoint_style"];
SAVE_FLOAT(ws, WaypointRadius, "radius");
SAVE_FLOAT(ws, WaypointBorderWidth, "border_width");
SAVE_COLOR(ws, WaypointColor, "color");
SAVE_COLOR(ws, WaypointBorderColor, "border_color");
SAVE_COLOR(ws, WaypointColorHovered, "color_hovered");
SAVE_COLOR(ws, WaypointColorSelected, "color_selected");
SAVE_FLOAT(ws, WaypointPreviewRadius, "preview_radius");
SAVE_COLOR(ws, WaypointPreviewColor, "preview_color");
SAVE_COLOR(ws, WaypointPreviewBorderColor, "preview_border_color");
return root.dump(4);
}
bool StyleManager::Deserialize(const std::string& jsonData)
{
auto root = crude_json::value::parse(jsonData);
if (root.is_discarded() || !root.is_object())
{
printf("[StyleManager] Failed to parse JSON\n");
return false;
}
// Block Style
if (root.contains("block_style") && root["block_style"].is_object())
{
auto& bs = root["block_style"];
LOAD_COLOR(bs, BlockStyle.BgColor, "bg_color");
LOAD_COLOR(bs, BlockStyle.BorderColor, "border_color");
LOAD_COLOR(bs, BlockStyle.BorderColorRunning, "border_color_running");
LOAD_FLOAT(bs, BlockStyle.Rounding, "rounding");
LOAD_FLOAT(bs, BlockStyle.BorderWidth, "border_width");
LOAD_FLOAT(bs, BlockStyle.BorderWidthRunning, "border_width_running");
LOAD_VEC4(bs, BlockStyle.Padding, "padding");
}
// Group Style
if (root.contains("group_style") && root["group_style"].is_object())
{
auto& gs = root["group_style"];
LOAD_COLOR(gs, GroupStyle.BgColor, "bg_color");
LOAD_COLOR(gs, GroupStyle.BorderColor, "border_color");
LOAD_COLOR(gs, GroupStyle.BorderColorRunning, "border_color_running");
LOAD_FLOAT(gs, GroupStyle.Rounding, "rounding");
LOAD_FLOAT(gs, GroupStyle.BorderWidth, "border_width");
LOAD_FLOAT(gs, GroupStyle.BorderWidthRunning, "border_width_running");
LOAD_VEC4(gs, GroupStyle.Padding, "padding");
LOAD_FLOAT(gs, GroupResizeGripSize, "resize_grip_size");
LOAD_FLOAT(gs, GroupResizeGripLineSpacing, "resize_grip_line_spacing");
LOAD_COLOR(gs, GroupResizeGripColor, "resize_grip_color");
}
// Parameter Style
if (root.contains("parameter_style") && root["parameter_style"].is_object())
{
auto& ps = root["parameter_style"];
LOAD_COLOR(ps, ParameterStyle.BgColor, "bg_color");
LOAD_COLOR(ps, ParameterStyle.BorderColor, "border_color");
LOAD_FLOAT(ps, ParameterStyle.Rounding, "rounding");
LOAD_FLOAT(ps, ParameterStyle.BorderWidth, "border_width");
LOAD_VEC4(ps, ParameterStyle.Padding, "padding");
LOAD_COLOR(ps, ParamBorderColorSource, "border_color_source");
LOAD_COLOR(ps, ParamBorderColorShortcut, "border_color_shortcut");
LOAD_FLOAT(ps, ParamBorderWidthSource, "border_width_source");
LOAD_FLOAT(ps, ParamBorderWidthNameAndValue, "border_width_name_and_value");
LOAD_FLOAT(ps, ParamBorderWidthSourceNameAndValue, "border_width_source_name_and_value");
LOAD_VEC4(ps, ParamPaddingNameOnly, "padding_name_only");
LOAD_VEC4(ps, ParamPaddingNameAndValue, "padding_name_and_value");
LOAD_VEC4(ps, ParamPaddingSmallBox, "padding_small_box");
LOAD_VEC4(ps, ParamPaddingMinimal, "padding_minimal");
LOAD_FLOAT(ps, ParamInputWidthNameOnly, "input_width_name_only");
LOAD_FLOAT(ps, ParamInputWidthNameAndValue, "input_width_name_and_value");
LOAD_FLOAT(ps, ParamInputWidthSmallBox, "input_width_small_box");
LOAD_VEC2(ps, ParamMinimalSize, "minimal_size");
LOAD_VEC2(ps, ParamMinimalLinksSize, "minimal_links_size");
}
// Pin Colors
if (root.contains("pin_colors") && root["pin_colors"].is_object())
{
auto& pc = root["pin_colors"];
LOAD_COLOR(pc, PinColor_Input, "input");
LOAD_COLOR(pc, PinColor_Output, "output");
LOAD_COLOR(pc, PinColor_Running, "running");
LOAD_COLOR(pc, PinColor_Deactivated, "deactivated");
LOAD_COLOR(pc, PinColor_Error, "error");
LOAD_COLOR(pc, PinColor_Warning, "warning");
}
// Pin Sizes
LOAD_FLOAT(root, FlowPinSize, "flow_pin_size");
LOAD_FLOAT(root, ParameterPinWidth, "parameter_pin_width");
LOAD_FLOAT(root, ParameterPinHeight, "parameter_pin_height");
LOAD_FLOAT(root, ParameterPinEdgeOffset, "parameter_pin_edge_offset");
LOAD_FLOAT(root, FlowPinEdgeOffset, "flow_pin_edge_offset");
// Layout
LOAD_FLOAT(root, MinNodeWidth, "min_node_width");
LOAD_FLOAT(root, MinNodeHeight, "min_node_height");
LOAD_FLOAT(root, MinGroupSize, "min_group_size");
// Link styling
if (root.contains("link_style") && root["link_style"].is_object())
{
auto& ls = root["link_style"];
LOAD_FLOAT(ls, LinkThicknessNormal, "thickness_normal");
LOAD_FLOAT(ls, LinkThicknessSelected, "thickness_selected");
LOAD_FLOAT(ls, LinkThicknessParameterNormal, "thickness_parameter_normal");
LOAD_FLOAT(ls, LinkThicknessParameterSelected, "thickness_parameter_selected");
LOAD_COLOR(ls, LinkColorHighlighted, "color_highlighted");
}
// Waypoint styling
if (root.contains("waypoint_style") && root["waypoint_style"].is_object())
{
auto& ws = root["waypoint_style"];
LOAD_FLOAT(ws, WaypointRadius, "radius");
LOAD_FLOAT(ws, WaypointBorderWidth, "border_width");
LOAD_COLOR(ws, WaypointColor, "color");
LOAD_COLOR(ws, WaypointBorderColor, "border_color");
LOAD_COLOR(ws, WaypointColorHovered, "color_hovered");
LOAD_COLOR(ws, WaypointColorSelected, "color_selected");
LOAD_FLOAT(ws, WaypointPreviewRadius, "preview_radius");
LOAD_COLOR(ws, WaypointPreviewColor, "preview_color");
LOAD_COLOR(ws, WaypointPreviewBorderColor, "preview_border_color");
}
return true;
}
void StyleManager::ApplyToEditorStyle(EditorContext* editorCtx)
{
if (!editorCtx)
return;
// Set the editor context before getting style (GetStyle uses global context)
// Use full namespace since we're inside ax::NodeEditor namespace
ax::NodeEditor::SetCurrentEditor(editorCtx);
// Get editor style via public API
auto& style = ax::NodeEditor::GetStyle();
// Set editor-level defaults
// These can still be overridden per-node via PushStyleVar/PushStyleColor
style.NodePadding = BlockStyle.Padding;
style.NodeRounding = BlockStyle.Rounding;
style.NodeBorderWidth = BlockStyle.BorderWidth;
style.PinArrowSize = 0.0f; // No arrows
style.PinArrowWidth = 0.0f;
// Apply waypoint styling (convert ImColor to ImU32)
style.WaypointRadius = WaypointRadius;
style.WaypointBorderWidth = WaypointBorderWidth;
style.WaypointColor = WaypointColor;
style.WaypointBorderColor = WaypointBorderColor;
style.WaypointColorHovered = WaypointColorHovered;
style.WaypointColorSelected = WaypointColorSelected;
}
} // namespace NodeEditor
} // namespace ax