439 ImGui::SetNextItemWidth(-1);
440 if (ImGui::InputTextWithHint(
"##search",
ICON_MD_SEARCH " Search panels...",
446 ImGui::SetNextItemWidth(-1);
447 if (ImGui::BeginCombo(
"##category_filter",
455 std::set<std::string> categories;
456 for (
const auto& panel : panels) {
457 categories.insert(panel.category);
460 for (
const auto& cat : categories) {
475 std::map<std::string, std::vector<PalettePanel>> grouped_panels;
476 int visible_count = 0;
478 for (
const auto& panel : panels) {
490 grouped_panels[panel.category].push_back(panel);
495 for (
const auto& [category, category_panels] : grouped_panels) {
497 bool category_open = ImGui::CollapsingHeader(
498 absl::StrFormat(
"%s (%d)", category, category_panels.size()).c_str(),
499 ImGuiTreeNodeFlags_DefaultOpen);
502 for (
const auto& panel : category_panels) {
503 ImGui::PushID(panel.id.c_str());
507 {{ImGuiCol_Header, ImVec4(0.2f, 0.2f, 0.25f, 1.0f)},
508 {ImGuiCol_HeaderHovered, ImVec4(0.25f, 0.25f, 0.35f, 1.0f)},
509 {ImGuiCol_HeaderActive, ImVec4(0.3f, 0.3f, 0.4f, 1.0f)}});
511 bool clicked = ImGui::Selectable(
512 absl::StrFormat(
"%s %s", panel.icon, panel.name).c_str(),
false, 0,
516 LOG_INFO(
"LayoutDesigner",
"Selected panel: %s", panel.name.c_str());
520 if (ImGui::IsItemHovered() && !panel.description.empty()) {
521 ImGui::SetTooltip(
"%s\n\nID: %s\nPriority: %d",
522 panel.description.c_str(), panel.id.c_str(),
527 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
531 ImGui::SetDragDropPayload(
532 kPanelPayloadType, panel.id.c_str(),
533 panel.id.size() + 1);
534 ImGui::Text(
"%s %s", panel.icon.c_str(), panel.name.c_str());
535 ImGui::TextDisabled(
"Drag to canvas");
536 ImGui::EndDragDropSource();
538 LOG_INFO(
"DragDrop",
"Drag started: %s", panel.name.c_str());
552 ImGui::TextDisabled(
"%d panels available", visible_count);
569 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
571 strcmp(drag_payload->DataType, kPanelPayloadType) == 0;
572 if (drag_payload && drag_payload->DataType) {
574 ImGui::TextColored(ImVec4(0, 1, 0, 1),
576 drag_payload->DataType);
583 "No layout loaded. Create a new layout or open an existing one.");
585 if (ImGui::Button(
"Create New Layout")) {
592 ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
596 ImDrawList* draw_list = ImGui::GetWindowDrawList();
597 const ImU32 grid_color = ImGui::GetColorU32(ImGuiCol_TableBorderStrong);
601 for (
float x_pos = 0; x_pos < scaled_size.x; x_pos += grid_step) {
603 ImVec2(canvas_pos.x + x_pos, canvas_pos.y),
604 ImVec2(canvas_pos.x + x_pos, canvas_pos.y + scaled_size.y), grid_color);
606 for (
float y_pos = 0; y_pos < scaled_size.y; y_pos += grid_step) {
608 ImVec2(canvas_pos.x, canvas_pos.y + y_pos),
609 ImVec2(canvas_pos.x + scaled_size.x, canvas_pos.y + y_pos), grid_color);
619 ImGui::SetCursorScreenPos(canvas_pos);
620 ImGui::InvisibleButton(
"canvas_drop_zone", scaled_size);
624 if (ImGui::BeginDragDropTarget()) {
626 const ImGuiPayload* preview = ImGui::GetDragDropPayload();
627 if (preview && strcmp(preview->DataType, kPanelPayloadType) == 0) {
631 if (
const ImGuiPayload* payload =
632 ImGui::AcceptDragDropPayload(kPanelPayloadType)) {
633 const char* panel_id_cstr =
static_cast<const char*
>(payload->Data);
634 std::string panel_id = panel_id_cstr ? panel_id_cstr :
"";
636 if (!resolved.has_value()) {
637 LOG_WARN(
"DragDrop",
"Unknown panel payload: %s", panel_id.c_str());
642 ImGui::EndDragDropTarget();
647 const ImVec2& size) {
651 ImDrawList* draw_list = ImGui::GetWindowDrawList();
652 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
653 auto alpha_color = [](ImU32 base,
float alpha_scale) {
654 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
656 return ImGui::ColorConvertFloat4ToU32(c);
660 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
661 bool is_drag_active = drag_payload !=
nullptr &&
662 drag_payload->DataType !=
nullptr &&
663 strcmp(drag_payload->DataType,
"PANEL_ID") == 0;
667 ImU32 border_color = ImGui::GetColorU32(ImGuiCol_Border);
672 border_color = ImGui::GetColorU32(ImGuiCol_CheckMark);
676 if (is_drag_active) {
678 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
679 border_color = ImGui::GetColorU32(ImGuiCol_TextDisabled);
681 border_color = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
688 draw_list->AddRect(pos, rect_max, border_color, 4.0f, 0, 2.0f);
692 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
695 LOG_INFO(
"LayoutDesigner",
"Selected dock node with %zu panels",
700 const float panel_padding = 8.0f;
701 const float capsule_height = 26.0f;
702 ImVec2 capsule_pos = ImVec2(pos.x + panel_padding, pos.y + panel_padding);
703 for (
const auto& panel : node->
panels) {
704 ImVec2 capsule_min = capsule_pos;
706 ImVec2(rect_max.x - panel_padding, capsule_pos.y + capsule_height);
708 alpha_color(ImGui::GetColorU32(ImGuiCol_Header), 0.7f);
709 ImU32 capsule_border = ImGui::GetColorU32(ImGuiCol_HeaderActive);
710 draw_list->AddRectFilled(capsule_min, capsule_max, capsule_fill, 6.0f);
711 draw_list->AddRect(capsule_min, capsule_max, capsule_border, 6.0f, 0,
715 absl::StrFormat(
"%s %s", panel.icon, panel.display_name);
716 draw_list->AddText(ImVec2(capsule_min.x + 8, capsule_min.y + 5),
717 ImGui::GetColorU32(ImGuiCol_Text), label.c_str());
720 std::string sub = absl::StrFormat(
"ID: %s", panel.panel_id.c_str());
721 draw_list->AddText(ImVec2(capsule_min.x + 8, capsule_min.y + 5 + 12),
722 alpha_color(ImGui::GetColorU32(ImGuiCol_Text), 0.7f),
726 ImRect capsule_rect(capsule_min, capsule_max);
727 if (capsule_rect.Contains(ImGui::GetMousePos())) {
728 ImGui::BeginTooltip();
729 ImGui::TextUnformatted(label.c_str());
730 ImGui::TextDisabled(
"%s", panel.panel_id.c_str());
734 capsule_pos.y += capsule_height + 6.0f;
738 std::string node_flags_str;
739 if (node->
flags & ImGuiDockNodeFlags_NoTabBar)
740 node_flags_str +=
"[NoTab] ";
741 if (node->
flags & ImGuiDockNodeFlags_HiddenTabBar)
742 node_flags_str +=
"[HiddenTab] ";
743 if (node->
flags & ImGuiDockNodeFlags_NoCloseButton)
744 node_flags_str +=
"[NoClose] ";
745 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe)
746 node_flags_str +=
"[NoDock] ";
748 if (!node_flags_str.empty()) {
749 ImVec2 flags_size = ImGui::CalcTextSize(node_flags_str.c_str());
750 draw_list->AddText(ImVec2(rect_max.x - flags_size.x - 5, pos.y + 5),
751 alpha_color(ImGui::GetColorU32(ImGuiCol_Text), 0.8f),
752 node_flags_str.c_str());
755 if (node->
panels.empty()) {
756 const char* empty_text = is_drag_active ?
"Drop panel here" :
"Empty";
757 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
758 empty_text =
"Docking Disabled";
760 ImVec2 text_size = ImGui::CalcTextSize(empty_text);
761 draw_list->AddText(ImVec2(pos.x + (size.x - text_size.x) / 2,
762 pos.y + (size.y - text_size.y) / 2),
763 ImGui::GetColorU32(ImGuiCol_TextDisabled), empty_text);
769 ImVec2 left_pos = pos;
775 left_size = ImVec2(split_x - 2, size.y);
776 right_size = ImVec2(size.x - split_x - 2, size.y);
777 right_pos = ImVec2(pos.x + split_x + 2, pos.y);
780 draw_list->AddLine(ImVec2(pos.x + split_x, pos.y),
781 ImVec2(pos.x + split_x, pos.y + size.y),
782 IM_COL32(200, 200, 100, 255), 3.0f);
786 left_size = ImVec2(size.x, split_y - 2);
787 right_size = ImVec2(size.x, size.y - split_y - 2);
788 right_pos = ImVec2(pos.x, pos.y + split_y + 2);
791 draw_list->AddLine(ImVec2(pos.x, pos.y + split_y),
792 ImVec2(pos.x + size.x, pos.y + split_y),
793 IM_COL32(200, 200, 100, 255), 3.0f);
951 return "// No layout loaded";
956 &code, absl::StrFormat(
957 "// Generated by YAZE Layout Designer\n"
958 "// Layout: \"%s\"\n"
959 "// Generated: <timestamp>\n\n"
960 "void LayoutManager::Build%sLayout(ImGuiID dockspace_id) {\n"
961 " ImGui::DockBuilderRemoveNode(dockspace_id);\n"
962 " ImGui::DockBuilderAddNode(dockspace_id, "
963 "ImGuiDockNodeFlags_DockSpace);\n"
964 " ImGui::DockBuilderSetNodeSize(dockspace_id, "
965 "ImGui::GetMainViewport()->Size);\n\n"
966 " ImGuiID dock_main_id = dockspace_id;\n",
970 std::function<void(
DockNode*, std::string)> generate_splits =
971 [&](
DockNode* node, std::string parent_id_var) {
972 if (!node || node->
IsLeaf()) {
974 if (node && node->
flags != ImGuiDockNodeFlags_None) {
975 absl::StrAppend(&code,
976 absl::StrFormat(
" if (ImGuiDockNode* node = "
977 "ImGui::DockBuilderGetNode(%s)) {\n"
978 " node->LocalFlags = %d;\n"
980 parent_id_var, node->
flags));
986 std::string child_left_var =
987 absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
988 std::string child_right_var =
989 absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
992 static int id_counter = 1000;
1002 dir_str =
"ImGuiDir_Left";
1004 case ImGuiDir_Right:
1005 dir_str =
"ImGuiDir_Right";
1008 dir_str =
"ImGuiDir_Up";
1011 dir_str =
"ImGuiDir_Down";
1014 dir_str =
"ImGuiDir_Left";
1021 " ImGuiID %s, %s;\n"
1022 " ImGui::DockBuilderSplitNode(%s, %s, %.2ff, &%s, &%s);\n",
1023 child_left_var, child_right_var, parent_id_var, dir_str,
1024 node->
split_ratio, child_left_var, child_right_var));
1027 if (node->
flags != ImGuiDockNodeFlags_None) {
1028 absl::StrAppend(&code,
1029 absl::StrFormat(
" if (ImGuiDockNode* node = "
1030 "ImGui::DockBuilderGetNode(%s)) {\n"
1031 " node->LocalFlags = %d;\n"
1033 parent_id_var, node->
flags));
1036 generate_splits(node->
child_left.get(), child_left_var);
1037 generate_splits(node->
child_right.get(), child_right_var);
1041 std::function<void(
DockNode*, std::string)> generate_docking =
1042 [&](
DockNode* node, std::string node_id_var) {
1047 for (
const auto& panel : node->
panels) {
1048 if (panel.flags != ImGuiWindowFlags_None) {
1051 absl::StrFormat(
" // Note: Panel '%s' requires flags: %d\n",
1052 panel.panel_id, panel.flags));
1056 absl::StrFormat(
" ImGui::DockBuilderDockWindow(\"%s\", %s);\n",
1057 panel.panel_id, node_id_var));
1060 std::string child_left_var =
1061 absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
1062 std::string child_right_var =
1063 absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
1065 generate_docking(node->
child_left.get(), child_left_var);
1066 generate_docking(node->
child_right.get(), child_right_var);
1072 absl::StrAppend(&code,
"\n");
1076 absl::StrAppend(&code,
"\n ImGui::DockBuilderFinish(dockspace_id);\n}\n");
1130 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow |
1131 ImGuiTreeNodeFlags_OpenOnDoubleClick |
1132 ImGuiTreeNodeFlags_DefaultOpen;
1135 flags |= ImGuiTreeNodeFlags_Selected;
1142 label = absl::StrFormat(
1149 label = absl::StrFormat(
"Leaf (%zu panels)", node->
panels.size());
1152 bool open = ImGui::TreeNodeEx((
void*)(intptr_t)node_index, flags,
"%s",
1155 if (ImGui::IsItemClicked()) {
1161 if (ImGui::BeginPopupContextItem()) {
1162 if (ImGui::MenuItem(
"Delete Node")) {
1180 for (
size_t i = 0; i < node->
panels.size(); i++) {
1181 auto& panel = node->
panels[i];
1182 ImGuiTreeNodeFlags panel_flags =
1183 ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
1185 panel_flags |= ImGuiTreeNodeFlags_Selected;
1188 ImGui::TreeNodeEx((
void*)(intptr_t)node_index, panel_flags,
"%s %s",
1189 panel.icon.c_str(), panel.display_name.c_str());
1191 if (ImGui::IsItemClicked()) {
1197 if (ImGui::BeginPopupContextItem()) {
1198 if (ImGui::MenuItem(
"Delete Panel")) {
1421bool LayoutDesignerWindow::MatchesSearchFilter(
1423 if (search_filter_[0] ==
'\0') {
1427 std::string filter_lower = search_filter_;
1428 std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(),
1432 std::string name_lower = panel.
name;
1433 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(),
1435 if (name_lower.find(filter_lower) != std::string::npos) {
1440 std::string id_lower = panel.
id;
1441 std::transform(id_lower.begin(), id_lower.end(), id_lower.begin(), ::tolower);
1442 if (id_lower.find(filter_lower) != std::string::npos) {
1448 std::transform(desc_lower.begin(), desc_lower.end(), desc_lower.begin(),
1450 if (desc_lower.find(filter_lower) != std::string::npos) {
1597void LayoutDesignerWindow::DrawDropZones(
const ImVec2& pos,
const ImVec2& size,
1600 LOG_WARN(
"DragDrop",
"DrawDropZones called with null target_node");
1604 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1605 ImVec2 mouse_pos = ImGui::GetMousePos();
1606 ImVec2 rect_min = pos;
1607 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
1608 auto alpha_color = [](ImU32 base,
float alpha_scale) {
1609 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
1611 return ImGui::ColorConvertFloat4ToU32(c);
1615 ImGuiDir zone = GetDropZone(mouse_pos, rect_min, rect_max);
1617 LOG_INFO(
"DragDrop",
"Drawing drop zones, mouse zone: %d",
1618 static_cast<int>(zone));
1621 float zone_size = 0.25f;
1630 std::vector<DropZone> zones = {
1632 {rect_min, ImVec2(rect_min.x + size.x * zone_size, rect_max.y),
1635 {ImVec2(rect_max.x - size.x * zone_size, rect_min.y), rect_max,
1638 {rect_min, ImVec2(rect_max.x, rect_min.y + size.y * zone_size),
1641 {ImVec2(rect_min.x, rect_max.y - size.y * zone_size), rect_max,
1644 {ImVec2(rect_min.x + size.x * zone_size, rect_min.y + size.y * zone_size),
1645 ImVec2(rect_max.x - size.x * zone_size, rect_max.y - size.y * zone_size),
1649 for (
const auto& drop_zone : zones) {
1650 bool is_hovered = (zone == drop_zone.dir);
1651 ImU32 base_zone = ImGui::GetColorU32(ImGuiCol_Header);
1652 ImU32 color = is_hovered ? alpha_color(base_zone, 0.8f)
1653 : alpha_color(base_zone, 0.35f);
1655 draw_list->AddRectFilled(drop_zone.min, drop_zone.max, color, 4.0f);
1656 draw_list->AddRect(drop_zone.min, drop_zone.max,
1657 ImGui::GetColorU32(ImGuiCol_HeaderActive), 4.0f, 0,
1662 drop_target_node_ = target_node;
1663 drop_direction_ = zone;
1665 LOG_INFO(
"DragDrop",
"✓ Drop target set: zone=%d",
1666 static_cast<int>(zone));
1669 const char* dir_text =
"";
1672 dir_text =
"← Left 30%";
1674 case ImGuiDir_Right:
1675 dir_text =
"Right 30% →";
1678 dir_text =
"↑ Top 30%";
1681 dir_text =
"↓ Bottom 30%";
1684 dir_text =
"⊕ Add to Center";
1686 case ImGuiDir_COUNT:
1690 ImVec2 text_size = ImGui::CalcTextSize(dir_text);
1692 ImVec2((drop_zone.min.x + drop_zone.max.x - text_size.x) / 2,
1693 (drop_zone.min.y + drop_zone.max.y - text_size.y) / 2);
1696 draw_list->AddRectFilled(
1697 ImVec2(text_pos.x - 5, text_pos.y - 2),
1698 ImVec2(text_pos.x + text_size.x + 5, text_pos.y + text_size.y + 2),
1699 IM_COL32(0, 0, 0, 200), 4.0f);
1701 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), dir_text);
1846void LayoutDesignerWindow::DrawWidgetPalette() {
1851 ImGui::SetNextItemWidth(-1);
1852 ImGui::InputTextWithHint(
1854 widget_search_filter_,
sizeof(widget_search_filter_));
1857 ImGui::SetNextItemWidth(-1);
1858 const char* categories[] = {
"All",
"Basic",
"Layout",
1859 "Containers",
"Tables",
"Custom"};
1860 if (ImGui::BeginCombo(
"##widget_category",
1861 selected_widget_category_.c_str())) {
1862 for (
const char* cat : categories) {
1863 if (ImGui::Selectable(cat, selected_widget_category_ == cat)) {
1864 selected_widget_category_ = cat;
1874 struct WidgetPaletteItem {
1876 std::string category;
1879 std::vector<WidgetPaletteItem> widgets = {
1881 {WidgetType::Text,
"Basic"},
1882 {WidgetType::TextWrapped,
"Basic"},
1883 {WidgetType::Button,
"Basic"},
1884 {WidgetType::SmallButton,
"Basic"},
1885 {WidgetType::Checkbox,
"Basic"},
1886 {WidgetType::InputText,
"Basic"},
1887 {WidgetType::InputInt,
"Basic"},
1888 {WidgetType::SliderInt,
"Basic"},
1889 {WidgetType::SliderFloat,
"Basic"},
1890 {WidgetType::ColorEdit,
"Basic"},
1893 {WidgetType::Separator,
"Layout"},
1894 {WidgetType::SameLine,
"Layout"},
1895 {WidgetType::Spacing,
"Layout"},
1896 {WidgetType::Dummy,
"Layout"},
1899 {WidgetType::BeginGroup,
"Containers"},
1900 {WidgetType::BeginChild,
"Containers"},
1901 {WidgetType::CollapsingHeader,
"Containers"},
1902 {WidgetType::TreeNode,
"Containers"},
1903 {WidgetType::TabBar,
"Containers"},
1906 {WidgetType::BeginTable,
"Tables"},
1907 {WidgetType::TableNextRow,
"Tables"},
1908 {WidgetType::TableNextColumn,
"Tables"},
1911 {WidgetType::Canvas,
"Custom"},
1912 {WidgetType::ProgressBar,
"Custom"},
1913 {WidgetType::Image,
"Custom"},
1917 std::string current_category;
1918 int visible_count = 0;
1920 for (
const auto& item : widgets) {
1922 if (selected_widget_category_ !=
"All" &&
1923 item.category != selected_widget_category_) {
1928 if (widget_search_filter_[0] !=
'\0') {
1930 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(),
1932 std::string filter_lower = widget_search_filter_;
1933 std::transform(filter_lower.begin(), filter_lower.end(),
1934 filter_lower.begin(), ::tolower);
1935 if (name_lower.find(filter_lower) == std::string::npos) {
1941 if (item.category != current_category) {
1942 if (!current_category.empty()) {
1945 current_category = item.category;
1946 ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f),
"%s",
1947 current_category.c_str());
1952 ImGui::PushID(
static_cast<int>(item.type));
1956 if (ImGui::Selectable(label.c_str(),
false, 0, ImVec2(0, 28))) {
1957 LOG_INFO(
"LayoutDesigner",
"Selected widget: %s",
1962 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
1963 ImGui::SetDragDropPayload(
"WIDGET_TYPE", &item.type,
sizeof(
WidgetType));
1964 ImGui::Text(
"%s", label.c_str());
1965 ImGui::TextDisabled(
"Drag to canvas");
1966 ImGui::EndDragDropSource();
1974 ImGui::TextDisabled(
"%d widgets available", visible_count);
1977void LayoutDesignerWindow::DrawWidgetCanvas() {
1980 ImGui::TextDisabled(
"Design panel internal layout");
1983 if (!current_panel_design_) {
1984 ImGui::TextWrapped(
"No panel design loaded.");
1987 "Create a new panel design or select a panel from the Panel Layout "
1990 if (ImGui::Button(
"Create New Panel Design")) {
1991 current_panel_design_ = std::make_unique<PanelDesign>();
1992 current_panel_design_->panel_id =
"new_panel";
1993 current_panel_design_->panel_name =
"New Panel";
1999 ImGui::Text(
"Panel: %s", current_panel_design_->panel_name.c_str());
2003 ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
2004 ImVec2 canvas_size = ImGui::GetContentRegionAvail();
2005 ImDrawList* draw_list = ImGui::GetWindowDrawList();
2008 draw_list->AddRectFilled(
2010 ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
2011 IM_COL32(30, 30, 35, 255));
2014 const float grid_step = 20.0f;
2015 for (
float x_pos = 0; x_pos < canvas_size.x; x_pos += grid_step) {
2017 ImVec2(canvas_pos.x + x_pos, canvas_pos.y),
2018 ImVec2(canvas_pos.x + x_pos, canvas_pos.y + canvas_size.y),
2019 IM_COL32(50, 50, 55, 255));
2021 for (
float y_pos = 0; y_pos < canvas_size.y; y_pos += grid_step) {
2023 ImVec2(canvas_pos.x, canvas_pos.y + y_pos),
2024 ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + y_pos),
2025 IM_COL32(50, 50, 55, 255));
2029 ImVec2 widget_pos = ImVec2(canvas_pos.x + 20, canvas_pos.y + 20);
2030 for (
const auto& widget : current_panel_design_->widgets) {
2034 std::string label = absl::StrFormat(
"%s %s", icon, name);
2036 ImU32 color = (selected_widget_ == widget.get())
2037 ? IM_COL32(255, 200, 100, 255)
2038 : IM_COL32(100, 150, 200, 255);
2040 ImVec2 widget_size = ImVec2(200, 30);
2041 draw_list->AddRectFilled(
2043 ImVec2(widget_pos.x + widget_size.x, widget_pos.y + widget_size.y),
2045 draw_list->AddText(ImVec2(widget_pos.x + 10, widget_pos.y + 8),
2046 IM_COL32(255, 255, 255, 255), label.c_str());
2049 ImVec2 mouse_pos = ImGui::GetMousePos();
2050 if (mouse_pos.x >= widget_pos.x &&
2051 mouse_pos.x <= widget_pos.x + widget_size.x &&
2052 mouse_pos.y >= widget_pos.y &&
2053 mouse_pos.y <= widget_pos.y + widget_size.y) {
2054 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
2055 selected_widget_ = widget.get();
2056 LOG_INFO(
"LayoutDesigner",
"Selected widget: %s", widget->id.c_str());
2060 widget_pos.y += widget_size.y + 10;
2064 ImGui::Dummy(canvas_size);
2065 if (ImGui::BeginDragDropTarget()) {
2067 if (
const ImGuiPayload* payload =
2068 ImGui::AcceptDragDropPayload(
"WIDGET_TYPE")) {
2072 auto new_widget = std::make_unique<WidgetDefinition>();
2074 absl::StrFormat(
"widget_%zu", current_panel_design_->widgets.size());
2075 new_widget->type = *widget_type;
2080 for (
const auto& prop : props) {
2081 new_widget->properties.push_back(prop);
2084 current_panel_design_->AddWidget(std::move(new_widget));
2085 LOG_INFO(
"LayoutDesigner",
"Added widget: %s",
2090 ImGui::EndDragDropTarget();
2094void LayoutDesignerWindow::DrawWidgetProperties() {
2098 if (!selected_widget_) {
2099 ImGui::TextWrapped(
"Select a widget to edit its properties");
2101 if (current_panel_design_) {
2104 ImGui::Text(
"Panel: %s", current_panel_design_->panel_name.c_str());
2105 ImGui::Text(
"Widgets: %zu", current_panel_design_->widgets.size());
2107 if (ImGui::Button(
"Clear All Widgets")) {
2108 current_panel_design_->widgets.clear();
2109 selected_widget_ =
nullptr;
2116 ImGui::Text(
"Widget: %s", selected_widget_->id.c_str());
2121 for (
auto& prop : selected_widget_->properties) {
2122 ImGui::PushID(prop.name.c_str());
2124 switch (prop.type) {
2125 case WidgetProperty::Type::String: {
2127 strncpy(buffer, prop.string_value.c_str(),
sizeof(buffer) - 1);
2128 buffer[
sizeof(buffer) - 1] =
'\0';
2129 if (ImGui::InputText(prop.name.c_str(), buffer,
sizeof(buffer))) {
2130 prop.string_value = buffer;
2134 case WidgetProperty::Type::Int:
2135 ImGui::InputInt(prop.name.c_str(), &prop.int_value);
2137 case WidgetProperty::Type::Float:
2138 ImGui::InputFloat(prop.name.c_str(), &prop.float_value);
2140 case WidgetProperty::Type::Bool:
2141 ImGui::Checkbox(prop.name.c_str(), &prop.bool_value);
2143 case WidgetProperty::Type::Color:
2144 ImGui::ColorEdit4(prop.name.c_str(), &prop.color_value.x);
2146 case WidgetProperty::Type::Vec2:
2147 ImGui::InputFloat2(prop.name.c_str(), &prop.vec2_value.x);
2150 ImGui::Text(
"%s: (unsupported type)", prop.name.c_str());
2160 ImGui::Checkbox(
"Same Line", &selected_widget_->same_line);
2162 char tooltip_buffer[256];
2163 strncpy(tooltip_buffer, selected_widget_->tooltip.c_str(),
2164 sizeof(tooltip_buffer) - 1);
2165 tooltip_buffer[
sizeof(tooltip_buffer) - 1] =
'\0';
2166 if (ImGui::InputText(
"Tooltip", tooltip_buffer,
sizeof(tooltip_buffer))) {
2167 selected_widget_->tooltip = tooltip_buffer;
2170 char callback_buffer[256];
2171 strncpy(callback_buffer, selected_widget_->callback_name.c_str(),
2172 sizeof(callback_buffer) - 1);
2173 callback_buffer[
sizeof(callback_buffer) - 1] =
'\0';
2174 if (ImGui::InputText(
"Callback", callback_buffer,
sizeof(callback_buffer))) {
2175 selected_widget_->callback_name = callback_buffer;
2180 if (ImGui::Button(
"Delete Widget")) {
2182 LOG_INFO(
"LayoutDesigner",
"Delete widget: %s",
2183 selected_widget_->id.c_str());