438 ImGui::SetNextItemWidth(-1);
439 if (ImGui::InputTextWithHint(
"##search",
ICON_MD_SEARCH " Search panels...",
445 ImGui::SetNextItemWidth(-1);
446 if (ImGui::BeginCombo(
"##category_filter",
454 std::set<std::string> categories;
455 for (
const auto& panel : panels) {
456 categories.insert(panel.category);
459 for (
const auto& cat : categories) {
474 std::map<std::string, std::vector<PalettePanel>> grouped_panels;
475 int visible_count = 0;
477 for (
const auto& panel : panels) {
489 grouped_panels[panel.category].push_back(panel);
494 for (
const auto& [category, category_panels] : grouped_panels) {
496 bool category_open = ImGui::CollapsingHeader(
497 absl::StrFormat(
"%s (%d)", category, category_panels.size()).c_str(),
498 ImGuiTreeNodeFlags_DefaultOpen);
501 for (
const auto& panel : category_panels) {
502 ImGui::PushID(panel.id.c_str());
505 ImVec4 bg_color = ImVec4(0.2f, 0.2f, 0.25f, 1.0f);
506 ImGui::PushStyleColor(ImGuiCol_Header, bg_color);
507 ImGui::PushStyleColor(ImGuiCol_HeaderHovered,
508 ImVec4(0.25f, 0.25f, 0.35f, 1.0f));
509 ImGui::PushStyleColor(ImGuiCol_HeaderActive,
510 ImVec4(0.3f, 0.3f, 0.4f, 1.0f));
512 bool clicked = ImGui::Selectable(
513 absl::StrFormat(
"%s %s", panel.icon, panel.name).c_str(),
false, 0,
516 ImGui::PopStyleColor(3);
519 LOG_INFO(
"LayoutDesigner",
"Selected panel: %s", panel.name.c_str());
523 if (ImGui::IsItemHovered() && !panel.description.empty()) {
524 ImGui::SetTooltip(
"%s\n\nID: %s\nPriority: %d",
525 panel.description.c_str(), panel.id.c_str(),
530 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
534 ImGui::SetDragDropPayload(
535 kPanelPayloadType, panel.id.c_str(),
536 panel.id.size() + 1);
537 ImGui::Text(
"%s %s", panel.icon.c_str(), panel.name.c_str());
538 ImGui::TextDisabled(
"Drag to canvas");
539 ImGui::EndDragDropSource();
541 LOG_INFO(
"DragDrop",
"Drag started: %s", panel.name.c_str());
555 ImGui::TextDisabled(
"%d panels available", visible_count);
572 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
574 strcmp(drag_payload->DataType, kPanelPayloadType) == 0;
575 if (drag_payload && drag_payload->DataType) {
577 ImGui::TextColored(ImVec4(0, 1, 0, 1),
579 drag_payload->DataType);
586 "No layout loaded. Create a new layout or open an existing one.");
588 if (ImGui::Button(
"Create New Layout")) {
595 ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
599 ImDrawList* draw_list = ImGui::GetWindowDrawList();
600 const ImU32 grid_color = ImGui::GetColorU32(ImGuiCol_TableBorderStrong);
604 for (
float x_pos = 0; x_pos < scaled_size.x; x_pos += grid_step) {
606 ImVec2(canvas_pos.x + x_pos, canvas_pos.y),
607 ImVec2(canvas_pos.x + x_pos, canvas_pos.y + scaled_size.y), grid_color);
609 for (
float y_pos = 0; y_pos < scaled_size.y; y_pos += grid_step) {
611 ImVec2(canvas_pos.x, canvas_pos.y + y_pos),
612 ImVec2(canvas_pos.x + scaled_size.x, canvas_pos.y + y_pos), grid_color);
622 ImGui::SetCursorScreenPos(canvas_pos);
623 ImGui::InvisibleButton(
"canvas_drop_zone", scaled_size);
627 if (ImGui::BeginDragDropTarget()) {
629 const ImGuiPayload* preview = ImGui::GetDragDropPayload();
630 if (preview && strcmp(preview->DataType, kPanelPayloadType) == 0) {
634 if (
const ImGuiPayload* payload =
635 ImGui::AcceptDragDropPayload(kPanelPayloadType)) {
636 const char* panel_id_cstr =
static_cast<const char*
>(payload->Data);
637 std::string panel_id = panel_id_cstr ? panel_id_cstr :
"";
639 if (!resolved.has_value()) {
640 LOG_WARN(
"DragDrop",
"Unknown panel payload: %s", panel_id.c_str());
645 ImGui::EndDragDropTarget();
650 const ImVec2& size) {
654 ImDrawList* draw_list = ImGui::GetWindowDrawList();
655 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
656 auto alpha_color = [](ImU32 base,
float alpha_scale) {
657 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
659 return ImGui::ColorConvertFloat4ToU32(c);
663 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
664 bool is_drag_active = drag_payload !=
nullptr &&
665 drag_payload->DataType !=
nullptr &&
666 strcmp(drag_payload->DataType,
"PANEL_ID") == 0;
670 ImU32 border_color = ImGui::GetColorU32(ImGuiCol_Border);
675 border_color = ImGui::GetColorU32(ImGuiCol_CheckMark);
679 if (is_drag_active) {
681 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
682 border_color = ImGui::GetColorU32(ImGuiCol_TextDisabled);
684 border_color = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
691 draw_list->AddRect(pos, rect_max, border_color, 4.0f, 0, 2.0f);
695 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
698 LOG_INFO(
"LayoutDesigner",
"Selected dock node with %zu panels",
703 const float panel_padding = 8.0f;
704 const float capsule_height = 26.0f;
705 ImVec2 capsule_pos = ImVec2(pos.x + panel_padding, pos.y + panel_padding);
706 for (
const auto& panel : node->
panels) {
707 ImVec2 capsule_min = capsule_pos;
709 ImVec2(rect_max.x - panel_padding, capsule_pos.y + capsule_height);
711 alpha_color(ImGui::GetColorU32(ImGuiCol_Header), 0.7f);
712 ImU32 capsule_border = ImGui::GetColorU32(ImGuiCol_HeaderActive);
713 draw_list->AddRectFilled(capsule_min, capsule_max, capsule_fill, 6.0f);
714 draw_list->AddRect(capsule_min, capsule_max, capsule_border, 6.0f, 0,
718 absl::StrFormat(
"%s %s", panel.icon, panel.display_name);
719 draw_list->AddText(ImVec2(capsule_min.x + 8, capsule_min.y + 5),
720 ImGui::GetColorU32(ImGuiCol_Text), label.c_str());
723 std::string sub = absl::StrFormat(
"ID: %s", panel.panel_id.c_str());
724 draw_list->AddText(ImVec2(capsule_min.x + 8, capsule_min.y + 5 + 12),
725 alpha_color(ImGui::GetColorU32(ImGuiCol_Text), 0.7f),
729 ImRect capsule_rect(capsule_min, capsule_max);
730 if (capsule_rect.Contains(ImGui::GetMousePos())) {
731 ImGui::BeginTooltip();
732 ImGui::TextUnformatted(label.c_str());
733 ImGui::TextDisabled(
"%s", panel.panel_id.c_str());
737 capsule_pos.y += capsule_height + 6.0f;
741 std::string node_flags_str;
742 if (node->
flags & ImGuiDockNodeFlags_NoTabBar)
743 node_flags_str +=
"[NoTab] ";
744 if (node->
flags & ImGuiDockNodeFlags_HiddenTabBar)
745 node_flags_str +=
"[HiddenTab] ";
746 if (node->
flags & ImGuiDockNodeFlags_NoCloseButton)
747 node_flags_str +=
"[NoClose] ";
748 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe)
749 node_flags_str +=
"[NoDock] ";
751 if (!node_flags_str.empty()) {
752 ImVec2 flags_size = ImGui::CalcTextSize(node_flags_str.c_str());
753 draw_list->AddText(ImVec2(rect_max.x - flags_size.x - 5, pos.y + 5),
754 alpha_color(ImGui::GetColorU32(ImGuiCol_Text), 0.8f),
755 node_flags_str.c_str());
758 if (node->
panels.empty()) {
759 const char* empty_text = is_drag_active ?
"Drop panel here" :
"Empty";
760 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
761 empty_text =
"Docking Disabled";
763 ImVec2 text_size = ImGui::CalcTextSize(empty_text);
764 draw_list->AddText(ImVec2(pos.x + (size.x - text_size.x) / 2,
765 pos.y + (size.y - text_size.y) / 2),
766 ImGui::GetColorU32(ImGuiCol_TextDisabled), empty_text);
772 ImVec2 left_pos = pos;
778 left_size = ImVec2(split_x - 2, size.y);
779 right_size = ImVec2(size.x - split_x - 2, size.y);
780 right_pos = ImVec2(pos.x + split_x + 2, pos.y);
783 draw_list->AddLine(ImVec2(pos.x + split_x, pos.y),
784 ImVec2(pos.x + split_x, pos.y + size.y),
785 IM_COL32(200, 200, 100, 255), 3.0f);
789 left_size = ImVec2(size.x, split_y - 2);
790 right_size = ImVec2(size.x, size.y - split_y - 2);
791 right_pos = ImVec2(pos.x, pos.y + split_y + 2);
794 draw_list->AddLine(ImVec2(pos.x, pos.y + split_y),
795 ImVec2(pos.x + size.x, pos.y + split_y),
796 IM_COL32(200, 200, 100, 255), 3.0f);
954 return "// No layout loaded";
959 &code, absl::StrFormat(
960 "// Generated by YAZE Layout Designer\n"
961 "// Layout: \"%s\"\n"
962 "// Generated: <timestamp>\n\n"
963 "void LayoutManager::Build%sLayout(ImGuiID dockspace_id) {\n"
964 " ImGui::DockBuilderRemoveNode(dockspace_id);\n"
965 " ImGui::DockBuilderAddNode(dockspace_id, "
966 "ImGuiDockNodeFlags_DockSpace);\n"
967 " ImGui::DockBuilderSetNodeSize(dockspace_id, "
968 "ImGui::GetMainViewport()->Size);\n\n"
969 " ImGuiID dock_main_id = dockspace_id;\n",
973 std::function<void(
DockNode*, std::string)> generate_splits =
974 [&](
DockNode* node, std::string parent_id_var) {
975 if (!node || node->
IsLeaf()) {
977 if (node && node->
flags != ImGuiDockNodeFlags_None) {
978 absl::StrAppend(&code,
979 absl::StrFormat(
" if (ImGuiDockNode* node = "
980 "ImGui::DockBuilderGetNode(%s)) {\n"
981 " node->LocalFlags = %d;\n"
983 parent_id_var, node->
flags));
989 std::string child_left_var =
990 absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
991 std::string child_right_var =
992 absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
995 static int id_counter = 1000;
1002 std::string dir_str;
1005 dir_str =
"ImGuiDir_Left";
1007 case ImGuiDir_Right:
1008 dir_str =
"ImGuiDir_Right";
1011 dir_str =
"ImGuiDir_Up";
1014 dir_str =
"ImGuiDir_Down";
1017 dir_str =
"ImGuiDir_Left";
1024 " ImGuiID %s, %s;\n"
1025 " ImGui::DockBuilderSplitNode(%s, %s, %.2ff, &%s, &%s);\n",
1026 child_left_var, child_right_var, parent_id_var, dir_str,
1027 node->
split_ratio, child_left_var, child_right_var));
1030 if (node->
flags != ImGuiDockNodeFlags_None) {
1031 absl::StrAppend(&code,
1032 absl::StrFormat(
" if (ImGuiDockNode* node = "
1033 "ImGui::DockBuilderGetNode(%s)) {\n"
1034 " node->LocalFlags = %d;\n"
1036 parent_id_var, node->
flags));
1039 generate_splits(node->
child_left.get(), child_left_var);
1040 generate_splits(node->
child_right.get(), child_right_var);
1044 std::function<void(
DockNode*, std::string)> generate_docking =
1045 [&](
DockNode* node, std::string node_id_var) {
1050 for (
const auto& panel : node->
panels) {
1051 if (panel.flags != ImGuiWindowFlags_None) {
1054 absl::StrFormat(
" // Note: Panel '%s' requires flags: %d\n",
1055 panel.panel_id, panel.flags));
1059 absl::StrFormat(
" ImGui::DockBuilderDockWindow(\"%s\", %s);\n",
1060 panel.panel_id, node_id_var));
1063 std::string child_left_var =
1064 absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
1065 std::string child_right_var =
1066 absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
1068 generate_docking(node->
child_left.get(), child_left_var);
1069 generate_docking(node->
child_right.get(), child_right_var);
1075 absl::StrAppend(&code,
"\n");
1079 absl::StrAppend(&code,
"\n ImGui::DockBuilderFinish(dockspace_id);\n}\n");
1133 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow |
1134 ImGuiTreeNodeFlags_OpenOnDoubleClick |
1135 ImGuiTreeNodeFlags_DefaultOpen;
1138 flags |= ImGuiTreeNodeFlags_Selected;
1145 label = absl::StrFormat(
1152 label = absl::StrFormat(
"Leaf (%zu panels)", node->
panels.size());
1155 bool open = ImGui::TreeNodeEx((
void*)(intptr_t)node_index, flags,
"%s",
1158 if (ImGui::IsItemClicked()) {
1164 if (ImGui::BeginPopupContextItem()) {
1165 if (ImGui::MenuItem(
"Delete Node")) {
1183 for (
size_t i = 0; i < node->
panels.size(); i++) {
1184 auto& panel = node->
panels[i];
1185 ImGuiTreeNodeFlags panel_flags =
1186 ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
1188 panel_flags |= ImGuiTreeNodeFlags_Selected;
1191 ImGui::TreeNodeEx((
void*)(intptr_t)node_index, panel_flags,
"%s %s",
1192 panel.icon.c_str(), panel.display_name.c_str());
1194 if (ImGui::IsItemClicked()) {
1200 if (ImGui::BeginPopupContextItem()) {
1201 if (ImGui::MenuItem(
"Delete Panel")) {
1424bool LayoutDesignerWindow::MatchesSearchFilter(
1426 if (search_filter_[0] ==
'\0') {
1430 std::string filter_lower = search_filter_;
1431 std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(),
1435 std::string name_lower = panel.
name;
1436 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(),
1438 if (name_lower.find(filter_lower) != std::string::npos) {
1443 std::string id_lower = panel.
id;
1444 std::transform(id_lower.begin(), id_lower.end(), id_lower.begin(), ::tolower);
1445 if (id_lower.find(filter_lower) != std::string::npos) {
1451 std::transform(desc_lower.begin(), desc_lower.end(), desc_lower.begin(),
1453 if (desc_lower.find(filter_lower) != std::string::npos) {
1600void LayoutDesignerWindow::DrawDropZones(
const ImVec2& pos,
const ImVec2& size,
1603 LOG_WARN(
"DragDrop",
"DrawDropZones called with null target_node");
1607 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1608 ImVec2 mouse_pos = ImGui::GetMousePos();
1609 ImVec2 rect_min = pos;
1610 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
1611 auto alpha_color = [](ImU32 base,
float alpha_scale) {
1612 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
1614 return ImGui::ColorConvertFloat4ToU32(c);
1618 ImGuiDir zone = GetDropZone(mouse_pos, rect_min, rect_max);
1620 LOG_INFO(
"DragDrop",
"Drawing drop zones, mouse zone: %d",
1621 static_cast<int>(zone));
1624 float zone_size = 0.25f;
1633 std::vector<DropZone> zones = {
1635 {rect_min, ImVec2(rect_min.x + size.x * zone_size, rect_max.y),
1638 {ImVec2(rect_max.x - size.x * zone_size, rect_min.y), rect_max,
1641 {rect_min, ImVec2(rect_max.x, rect_min.y + size.y * zone_size),
1644 {ImVec2(rect_min.x, rect_max.y - size.y * zone_size), rect_max,
1647 {ImVec2(rect_min.x + size.x * zone_size, rect_min.y + size.y * zone_size),
1648 ImVec2(rect_max.x - size.x * zone_size, rect_max.y - size.y * zone_size),
1652 for (
const auto& drop_zone : zones) {
1653 bool is_hovered = (zone == drop_zone.dir);
1654 ImU32 base_zone = ImGui::GetColorU32(ImGuiCol_Header);
1655 ImU32 color = is_hovered ? alpha_color(base_zone, 0.8f)
1656 : alpha_color(base_zone, 0.35f);
1658 draw_list->AddRectFilled(drop_zone.min, drop_zone.max, color, 4.0f);
1659 draw_list->AddRect(drop_zone.min, drop_zone.max,
1660 ImGui::GetColorU32(ImGuiCol_HeaderActive), 4.0f, 0,
1665 drop_target_node_ = target_node;
1666 drop_direction_ = zone;
1668 LOG_INFO(
"DragDrop",
"✓ Drop target set: zone=%d",
1669 static_cast<int>(zone));
1672 const char* dir_text =
"";
1675 dir_text =
"← Left 30%";
1677 case ImGuiDir_Right:
1678 dir_text =
"Right 30% →";
1681 dir_text =
"↑ Top 30%";
1684 dir_text =
"↓ Bottom 30%";
1687 dir_text =
"⊕ Add to Center";
1689 case ImGuiDir_COUNT:
1693 ImVec2 text_size = ImGui::CalcTextSize(dir_text);
1695 ImVec2((drop_zone.min.x + drop_zone.max.x - text_size.x) / 2,
1696 (drop_zone.min.y + drop_zone.max.y - text_size.y) / 2);
1699 draw_list->AddRectFilled(
1700 ImVec2(text_pos.x - 5, text_pos.y - 2),
1701 ImVec2(text_pos.x + text_size.x + 5, text_pos.y + text_size.y + 2),
1702 IM_COL32(0, 0, 0, 200), 4.0f);
1704 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), dir_text);
1849void LayoutDesignerWindow::DrawWidgetPalette() {
1854 ImGui::SetNextItemWidth(-1);
1855 ImGui::InputTextWithHint(
1857 widget_search_filter_,
sizeof(widget_search_filter_));
1860 ImGui::SetNextItemWidth(-1);
1861 const char* categories[] = {
"All",
"Basic",
"Layout",
1862 "Containers",
"Tables",
"Custom"};
1863 if (ImGui::BeginCombo(
"##widget_category",
1864 selected_widget_category_.c_str())) {
1865 for (
const char* cat : categories) {
1866 if (ImGui::Selectable(cat, selected_widget_category_ == cat)) {
1867 selected_widget_category_ = cat;
1877 struct WidgetPaletteItem {
1879 std::string category;
1882 std::vector<WidgetPaletteItem> widgets = {
1884 {WidgetType::Text,
"Basic"},
1885 {WidgetType::TextWrapped,
"Basic"},
1886 {WidgetType::Button,
"Basic"},
1887 {WidgetType::SmallButton,
"Basic"},
1888 {WidgetType::Checkbox,
"Basic"},
1889 {WidgetType::InputText,
"Basic"},
1890 {WidgetType::InputInt,
"Basic"},
1891 {WidgetType::SliderInt,
"Basic"},
1892 {WidgetType::SliderFloat,
"Basic"},
1893 {WidgetType::ColorEdit,
"Basic"},
1896 {WidgetType::Separator,
"Layout"},
1897 {WidgetType::SameLine,
"Layout"},
1898 {WidgetType::Spacing,
"Layout"},
1899 {WidgetType::Dummy,
"Layout"},
1902 {WidgetType::BeginGroup,
"Containers"},
1903 {WidgetType::BeginChild,
"Containers"},
1904 {WidgetType::CollapsingHeader,
"Containers"},
1905 {WidgetType::TreeNode,
"Containers"},
1906 {WidgetType::TabBar,
"Containers"},
1909 {WidgetType::BeginTable,
"Tables"},
1910 {WidgetType::TableNextRow,
"Tables"},
1911 {WidgetType::TableNextColumn,
"Tables"},
1914 {WidgetType::Canvas,
"Custom"},
1915 {WidgetType::ProgressBar,
"Custom"},
1916 {WidgetType::Image,
"Custom"},
1920 std::string current_category;
1921 int visible_count = 0;
1923 for (
const auto& item : widgets) {
1925 if (selected_widget_category_ !=
"All" &&
1926 item.category != selected_widget_category_) {
1931 if (widget_search_filter_[0] !=
'\0') {
1933 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(),
1935 std::string filter_lower = widget_search_filter_;
1936 std::transform(filter_lower.begin(), filter_lower.end(),
1937 filter_lower.begin(), ::tolower);
1938 if (name_lower.find(filter_lower) == std::string::npos) {
1944 if (item.category != current_category) {
1945 if (!current_category.empty()) {
1948 current_category = item.category;
1949 ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f),
"%s",
1950 current_category.c_str());
1955 ImGui::PushID(
static_cast<int>(item.type));
1959 if (ImGui::Selectable(label.c_str(),
false, 0, ImVec2(0, 28))) {
1960 LOG_INFO(
"LayoutDesigner",
"Selected widget: %s",
1965 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
1966 ImGui::SetDragDropPayload(
"WIDGET_TYPE", &item.type,
sizeof(
WidgetType));
1967 ImGui::Text(
"%s", label.c_str());
1968 ImGui::TextDisabled(
"Drag to canvas");
1969 ImGui::EndDragDropSource();
1977 ImGui::TextDisabled(
"%d widgets available", visible_count);
1980void LayoutDesignerWindow::DrawWidgetCanvas() {
1983 ImGui::TextDisabled(
"Design panel internal layout");
1986 if (!current_panel_design_) {
1987 ImGui::TextWrapped(
"No panel design loaded.");
1990 "Create a new panel design or select a panel from the Panel Layout "
1993 if (ImGui::Button(
"Create New Panel Design")) {
1994 current_panel_design_ = std::make_unique<PanelDesign>();
1995 current_panel_design_->panel_id =
"new_panel";
1996 current_panel_design_->panel_name =
"New Panel";
2002 ImGui::Text(
"Panel: %s", current_panel_design_->panel_name.c_str());
2006 ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
2007 ImVec2 canvas_size = ImGui::GetContentRegionAvail();
2008 ImDrawList* draw_list = ImGui::GetWindowDrawList();
2011 draw_list->AddRectFilled(
2013 ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
2014 IM_COL32(30, 30, 35, 255));
2017 const float grid_step = 20.0f;
2018 for (
float x_pos = 0; x_pos < canvas_size.x; x_pos += grid_step) {
2020 ImVec2(canvas_pos.x + x_pos, canvas_pos.y),
2021 ImVec2(canvas_pos.x + x_pos, canvas_pos.y + canvas_size.y),
2022 IM_COL32(50, 50, 55, 255));
2024 for (
float y_pos = 0; y_pos < canvas_size.y; y_pos += grid_step) {
2026 ImVec2(canvas_pos.x, canvas_pos.y + y_pos),
2027 ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + y_pos),
2028 IM_COL32(50, 50, 55, 255));
2032 ImVec2 widget_pos = ImVec2(canvas_pos.x + 20, canvas_pos.y + 20);
2033 for (
const auto& widget : current_panel_design_->widgets) {
2037 std::string label = absl::StrFormat(
"%s %s", icon, name);
2039 ImU32 color = (selected_widget_ == widget.get())
2040 ? IM_COL32(255, 200, 100, 255)
2041 : IM_COL32(100, 150, 200, 255);
2043 ImVec2 widget_size = ImVec2(200, 30);
2044 draw_list->AddRectFilled(
2046 ImVec2(widget_pos.x + widget_size.x, widget_pos.y + widget_size.y),
2048 draw_list->AddText(ImVec2(widget_pos.x + 10, widget_pos.y + 8),
2049 IM_COL32(255, 255, 255, 255), label.c_str());
2052 ImVec2 mouse_pos = ImGui::GetMousePos();
2053 if (mouse_pos.x >= widget_pos.x &&
2054 mouse_pos.x <= widget_pos.x + widget_size.x &&
2055 mouse_pos.y >= widget_pos.y &&
2056 mouse_pos.y <= widget_pos.y + widget_size.y) {
2057 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
2058 selected_widget_ = widget.get();
2059 LOG_INFO(
"LayoutDesigner",
"Selected widget: %s", widget->id.c_str());
2063 widget_pos.y += widget_size.y + 10;
2067 ImGui::Dummy(canvas_size);
2068 if (ImGui::BeginDragDropTarget()) {
2070 if (
const ImGuiPayload* payload =
2071 ImGui::AcceptDragDropPayload(
"WIDGET_TYPE")) {
2075 auto new_widget = std::make_unique<WidgetDefinition>();
2077 absl::StrFormat(
"widget_%zu", current_panel_design_->widgets.size());
2078 new_widget->type = *widget_type;
2083 for (
const auto& prop : props) {
2084 new_widget->properties.push_back(prop);
2087 current_panel_design_->AddWidget(std::move(new_widget));
2088 LOG_INFO(
"LayoutDesigner",
"Added widget: %s",
2093 ImGui::EndDragDropTarget();
2097void LayoutDesignerWindow::DrawWidgetProperties() {
2101 if (!selected_widget_) {
2102 ImGui::TextWrapped(
"Select a widget to edit its properties");
2104 if (current_panel_design_) {
2107 ImGui::Text(
"Panel: %s", current_panel_design_->panel_name.c_str());
2108 ImGui::Text(
"Widgets: %zu", current_panel_design_->widgets.size());
2110 if (ImGui::Button(
"Clear All Widgets")) {
2111 current_panel_design_->widgets.clear();
2112 selected_widget_ =
nullptr;
2119 ImGui::Text(
"Widget: %s", selected_widget_->id.c_str());
2124 for (
auto& prop : selected_widget_->properties) {
2125 ImGui::PushID(prop.name.c_str());
2127 switch (prop.type) {
2128 case WidgetProperty::Type::String: {
2130 strncpy(buffer, prop.string_value.c_str(),
sizeof(buffer) - 1);
2131 buffer[
sizeof(buffer) - 1] =
'\0';
2132 if (ImGui::InputText(prop.name.c_str(), buffer,
sizeof(buffer))) {
2133 prop.string_value = buffer;
2137 case WidgetProperty::Type::Int:
2138 ImGui::InputInt(prop.name.c_str(), &prop.int_value);
2140 case WidgetProperty::Type::Float:
2141 ImGui::InputFloat(prop.name.c_str(), &prop.float_value);
2143 case WidgetProperty::Type::Bool:
2144 ImGui::Checkbox(prop.name.c_str(), &prop.bool_value);
2146 case WidgetProperty::Type::Color:
2147 ImGui::ColorEdit4(prop.name.c_str(), &prop.color_value.x);
2149 case WidgetProperty::Type::Vec2:
2150 ImGui::InputFloat2(prop.name.c_str(), &prop.vec2_value.x);
2153 ImGui::Text(
"%s: (unsupported type)", prop.name.c_str());
2163 ImGui::Checkbox(
"Same Line", &selected_widget_->same_line);
2165 char tooltip_buffer[256];
2166 strncpy(tooltip_buffer, selected_widget_->tooltip.c_str(),
2167 sizeof(tooltip_buffer) - 1);
2168 tooltip_buffer[
sizeof(tooltip_buffer) - 1] =
'\0';
2169 if (ImGui::InputText(
"Tooltip", tooltip_buffer,
sizeof(tooltip_buffer))) {
2170 selected_widget_->tooltip = tooltip_buffer;
2173 char callback_buffer[256];
2174 strncpy(callback_buffer, selected_widget_->callback_name.c_str(),
2175 sizeof(callback_buffer) - 1);
2176 callback_buffer[
sizeof(callback_buffer) - 1] =
'\0';
2177 if (ImGui::InputText(
"Callback", callback_buffer,
sizeof(callback_buffer))) {
2178 selected_widget_->callback_name = callback_buffer;
2183 if (ImGui::Button(
"Delete Widget")) {
2185 LOG_INFO(
"LayoutDesigner",
"Delete widget: %s",
2186 selected_widget_->id.c_str());