439 ImGui::SetNextItemWidth(-1);
440 if (ImGui::InputTextWithHint(
"##search",
ICON_MD_SEARCH " Search panels...",
446 ImGui::SetNextItemWidth(-1);
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(),
514 false, 0, ImVec2(0, 32));
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(),
531 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
535 ImGui::SetDragDropPayload(kPanelPayloadType,
537 panel.id.size() + 1);
538 ImGui::Text(
"%s %s", panel.icon.c_str(), panel.name.c_str());
539 ImGui::TextDisabled(
"Drag to canvas");
540 ImGui::EndDragDropSource();
542 LOG_INFO(
"DragDrop",
"Drag started: %s", panel.name.c_str());
556 ImGui::TextDisabled(
"%d panels available", visible_count);
573 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
575 strcmp(drag_payload->DataType, kPanelPayloadType) == 0;
576 if (drag_payload && drag_payload->DataType) {
578 ImGui::TextColored(ImVec4(0, 1, 0, 1),
580 drag_payload->DataType);
586 ImGui::TextWrapped(
"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();
596 ImVec2 scaled_size = ImVec2(
600 ImDrawList* draw_list = ImGui::GetWindowDrawList();
601 const ImU32 grid_color = ImGui::GetColorU32(ImGuiCol_TableBorderStrong);
605 for (
float x_pos = 0; x_pos < scaled_size.x; x_pos += grid_step) {
607 ImVec2(canvas_pos.x + x_pos, canvas_pos.y),
608 ImVec2(canvas_pos.x + x_pos, canvas_pos.y + scaled_size.y),
611 for (
float y_pos = 0; y_pos < scaled_size.y; y_pos += grid_step) {
613 ImVec2(canvas_pos.x, canvas_pos.y + y_pos),
614 ImVec2(canvas_pos.x + scaled_size.x, canvas_pos.y + y_pos),
625 ImGui::SetCursorScreenPos(canvas_pos);
626 ImGui::InvisibleButton(
"canvas_drop_zone", scaled_size);
630 if (ImGui::BeginDragDropTarget()) {
632 const ImGuiPayload* preview = ImGui::GetDragDropPayload();
633 if (preview && strcmp(preview->DataType, kPanelPayloadType) == 0) {
637 if (
const ImGuiPayload* payload =
638 ImGui::AcceptDragDropPayload(kPanelPayloadType)) {
639 const char* panel_id_cstr =
static_cast<const char*
>(payload->Data);
640 std::string panel_id = panel_id_cstr ? panel_id_cstr :
"";
642 if (!resolved.has_value()) {
643 LOG_WARN(
"DragDrop",
"Unknown panel payload: %s", panel_id.c_str());
648 ImGui::EndDragDropTarget();
654 const ImVec2& size) {
657 ImDrawList* draw_list = ImGui::GetWindowDrawList();
658 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
659 auto alpha_color = [](ImU32 base,
float alpha_scale) {
660 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
662 return ImGui::ColorConvertFloat4ToU32(c);
666 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
667 bool is_drag_active = drag_payload !=
nullptr &&
668 drag_payload->DataType !=
nullptr &&
669 strcmp(drag_payload->DataType,
"PANEL_ID") == 0;
673 ImU32 border_color = ImGui::GetColorU32(ImGuiCol_Border);
678 border_color = ImGui::GetColorU32(ImGuiCol_CheckMark);
682 if (is_drag_active) {
684 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
685 border_color = ImGui::GetColorU32(ImGuiCol_TextDisabled);
687 border_color = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
694 draw_list->AddRect(pos, rect_max, border_color, 4.0f, 0, 2.0f);
698 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
701 LOG_INFO(
"LayoutDesigner",
"Selected dock node with %zu panels",
706 const float panel_padding = 8.0f;
707 const float capsule_height = 26.0f;
708 ImVec2 capsule_pos = ImVec2(pos.x + panel_padding, pos.y + panel_padding);
709 for (
const auto& panel : node->
panels) {
710 ImVec2 capsule_min = capsule_pos;
711 ImVec2 capsule_max = ImVec2(rect_max.x - panel_padding,
712 capsule_pos.y + capsule_height);
713 ImU32 capsule_fill = alpha_color(ImGui::GetColorU32(ImGuiCol_Header), 0.7f);
714 ImU32 capsule_border = ImGui::GetColorU32(ImGuiCol_HeaderActive);
715 draw_list->AddRectFilled(capsule_min, capsule_max, capsule_fill, 6.0f);
716 draw_list->AddRect(capsule_min, capsule_max, capsule_border, 6.0f, 0, 1.5f);
718 std::string label = 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) node_flags_str +=
"[NoTab] ";
743 if (node->
flags & ImGuiDockNodeFlags_HiddenTabBar) node_flags_str +=
"[HiddenTab] ";
744 if (node->
flags & ImGuiDockNodeFlags_NoCloseButton) node_flags_str +=
"[NoClose] ";
745 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) node_flags_str +=
"[NoDock] ";
747 if (!node_flags_str.empty()) {
748 ImVec2 flags_size = ImGui::CalcTextSize(node_flags_str.c_str());
749 draw_list->AddText(ImVec2(rect_max.x - flags_size.x - 5, pos.y + 5),
750 alpha_color(ImGui::GetColorU32(ImGuiCol_Text), 0.8f),
751 node_flags_str.c_str());
754 if (node->
panels.empty()) {
755 const char* empty_text = is_drag_active ?
"Drop panel here" :
"Empty";
756 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
757 empty_text =
"Docking Disabled";
759 ImVec2 text_size = ImGui::CalcTextSize(empty_text);
761 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);
781 ImVec2(pos.x + split_x, pos.y),
782 ImVec2(pos.x + split_x, pos.y + size.y),
783 IM_COL32(200, 200, 100, 255), 3.0f);
787 left_size = ImVec2(size.x, split_y - 2);
788 right_size = ImVec2(size.x, size.y - split_y - 2);
789 right_pos = ImVec2(pos.x, pos.y + split_y + 2);
793 ImVec2(pos.x, pos.y + split_y),
794 ImVec2(pos.x + size.x, pos.y + split_y),
795 IM_COL32(200, 200, 100, 255), 3.0f);
942 return "// No layout loaded";
946 absl::StrAppend(&code, absl::StrFormat(
947 "// Generated by YAZE Layout Designer\n"
948 "// Layout: \"%s\"\n"
949 "// Generated: <timestamp>\n\n"
950 "void LayoutManager::Build%sLayout(ImGuiID dockspace_id) {\n"
951 " ImGui::DockBuilderRemoveNode(dockspace_id);\n"
952 " ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);\n"
953 " ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetMainViewport()->Size);\n\n"
954 " ImGuiID dock_main_id = dockspace_id;\n",
959 std::function<void(
DockNode*, std::string)> generate_splits =
960 [&](
DockNode* node, std::string parent_id_var) {
961 if (!node || node->
IsLeaf()) {
963 if (node && node->
flags != ImGuiDockNodeFlags_None) {
964 absl::StrAppend(&code, absl::StrFormat(
965 " if (ImGuiDockNode* node = ImGui::DockBuilderGetNode(%s)) {\n"
966 " node->LocalFlags = %d;\n"
968 parent_id_var, node->
flags));
974 std::string child_left_var = absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
975 std::string child_right_var = absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
978 static int id_counter = 1000;
985 case ImGuiDir_Left: dir_str =
"ImGuiDir_Left";
break;
986 case ImGuiDir_Right: dir_str =
"ImGuiDir_Right";
break;
987 case ImGuiDir_Up: dir_str =
"ImGuiDir_Up";
break;
988 case ImGuiDir_Down: dir_str =
"ImGuiDir_Down";
break;
989 default: dir_str =
"ImGuiDir_Left";
break;
992 absl::StrAppend(&code, absl::StrFormat(
994 " ImGui::DockBuilderSplitNode(%s, %s, %.2ff, &%s, &%s);\n",
995 child_left_var, child_right_var,
997 child_left_var, child_right_var));
1000 if (node->
flags != ImGuiDockNodeFlags_None) {
1001 absl::StrAppend(&code, absl::StrFormat(
1002 " if (ImGuiDockNode* node = ImGui::DockBuilderGetNode(%s)) {\n"
1003 " node->LocalFlags = %d;\n"
1005 parent_id_var, node->
flags));
1008 generate_splits(node->
child_left.get(), child_left_var);
1009 generate_splits(node->
child_right.get(), child_right_var);
1013 std::function<void(
DockNode*, std::string)> generate_docking =
1014 [&](
DockNode* node, std::string node_id_var) {
1018 for (
const auto& panel : node->
panels) {
1019 if (panel.flags != ImGuiWindowFlags_None) {
1020 absl::StrAppend(&code, absl::StrFormat(
1021 " // Note: Panel '%s' requires flags: %d\n",
1022 panel.panel_id, panel.flags));
1024 absl::StrAppend(&code, absl::StrFormat(
1025 " ImGui::DockBuilderDockWindow(\"%s\", %s);\n",
1026 panel.panel_id, node_id_var));
1029 std::string child_left_var = absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
1030 std::string child_right_var = absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
1032 generate_docking(node->
child_left.get(), child_left_var);
1033 generate_docking(node->
child_right.get(), child_right_var);
1039 absl::StrAppend(&code,
"\n");
1043 absl::StrAppend(&code,
"\n ImGui::DockBuilderFinish(dockspace_id);\n}\n");
1367bool LayoutDesignerWindow::MatchesSearchFilter(
const PalettePanel& panel)
const {
1368 if (search_filter_[0] ==
'\0') {
1372 std::string filter_lower = search_filter_;
1373 std::transform(filter_lower.begin(), filter_lower.end(),
1374 filter_lower.begin(), ::tolower);
1377 std::string name_lower = panel.
name;
1378 std::transform(name_lower.begin(), name_lower.end(),
1379 name_lower.begin(), ::tolower);
1380 if (name_lower.find(filter_lower) != std::string::npos) {
1385 std::string id_lower = panel.
id;
1386 std::transform(id_lower.begin(), id_lower.end(),
1387 id_lower.begin(), ::tolower);
1388 if (id_lower.find(filter_lower) != std::string::npos) {
1394 std::transform(desc_lower.begin(), desc_lower.end(),
1395 desc_lower.begin(), ::tolower);
1396 if (desc_lower.find(filter_lower) != std::string::npos) {
1541void LayoutDesignerWindow::DrawDropZones(
const ImVec2& pos,
const ImVec2& size,
1544 LOG_WARN(
"DragDrop",
"DrawDropZones called with null target_node");
1548 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1549 ImVec2 mouse_pos = ImGui::GetMousePos();
1550 ImVec2 rect_min = pos;
1551 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
1552 auto alpha_color = [](ImU32 base,
float alpha_scale) {
1553 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
1555 return ImGui::ColorConvertFloat4ToU32(c);
1559 ImGuiDir zone = GetDropZone(mouse_pos, rect_min, rect_max);
1561 LOG_INFO(
"DragDrop",
"Drawing drop zones, mouse zone: %d",
static_cast<int>(zone));
1564 float zone_size = 0.25f;
1573 std::vector<DropZone> zones = {
1576 ImVec2(rect_min.x + size.x * zone_size, rect_max.y),
1579 {ImVec2(rect_max.x - size.x * zone_size, rect_min.y),
1584 ImVec2(rect_max.x, rect_min.y + size.y * zone_size),
1587 {ImVec2(rect_min.x, rect_max.y - size.y * zone_size),
1591 {ImVec2(rect_min.x + size.x * zone_size, rect_min.y + size.y * zone_size),
1592 ImVec2(rect_max.x - size.x * zone_size, rect_max.y - size.y * zone_size),
1597 for (
const auto& drop_zone : zones) {
1598 bool is_hovered = (zone == drop_zone.dir);
1599 ImU32 base_zone = ImGui::GetColorU32(ImGuiCol_Header);
1600 ImU32 color = is_hovered
1601 ? alpha_color(base_zone, 0.8f)
1602 : alpha_color(base_zone, 0.35f);
1604 draw_list->AddRectFilled(drop_zone.min, drop_zone.max, color, 4.0f);
1605 draw_list->AddRect(drop_zone.min, drop_zone.max,
1606 ImGui::GetColorU32(ImGuiCol_HeaderActive), 4.0f, 0, 1.0f);
1610 drop_target_node_ = target_node;
1611 drop_direction_ = zone;
1613 LOG_INFO(
"DragDrop",
"✓ Drop target set: zone=%d",
static_cast<int>(zone));
1616 const char* dir_text =
"";
1618 case ImGuiDir_Left: dir_text =
"← Left 30%";
break;
1619 case ImGuiDir_Right: dir_text =
"Right 30% →";
break;
1620 case ImGuiDir_Up: dir_text =
"↑ Top 30%";
break;
1621 case ImGuiDir_Down: dir_text =
"↓ Bottom 30%";
break;
1622 case ImGuiDir_None: dir_text =
"⊕ Add to Center";
break;
1623 case ImGuiDir_COUNT:
break;
1626 ImVec2 text_size = ImGui::CalcTextSize(dir_text);
1627 ImVec2 text_pos = ImVec2(
1628 (drop_zone.min.x + drop_zone.max.x - text_size.x) / 2,
1629 (drop_zone.min.y + drop_zone.max.y - text_size.y) / 2);
1632 draw_list->AddRectFilled(
1633 ImVec2(text_pos.x - 5, text_pos.y - 2),
1634 ImVec2(text_pos.x + text_size.x + 5, text_pos.y + text_size.y + 2),
1635 IM_COL32(0, 0, 0, 200), 4.0f);
1637 draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), dir_text);
1778void LayoutDesignerWindow::DrawWidgetPalette() {
1783 ImGui::SetNextItemWidth(-1);
1784 ImGui::InputTextWithHint(
"##widget_search",
ICON_MD_SEARCH " Search widgets...",
1785 widget_search_filter_,
sizeof(widget_search_filter_));
1788 ImGui::SetNextItemWidth(-1);
1789 const char* categories[] = {
"All",
"Basic",
"Layout",
"Containers",
"Tables",
"Custom"};
1790 if (ImGui::BeginCombo(
"##widget_category", selected_widget_category_.c_str())) {
1791 for (
const char* cat : categories) {
1792 if (ImGui::Selectable(cat, selected_widget_category_ == cat)) {
1793 selected_widget_category_ = cat;
1803 struct WidgetPaletteItem {
1805 std::string category;
1808 std::vector<WidgetPaletteItem> widgets = {
1810 {WidgetType::Text,
"Basic"},
1811 {WidgetType::TextWrapped,
"Basic"},
1812 {WidgetType::Button,
"Basic"},
1813 {WidgetType::SmallButton,
"Basic"},
1814 {WidgetType::Checkbox,
"Basic"},
1815 {WidgetType::InputText,
"Basic"},
1816 {WidgetType::InputInt,
"Basic"},
1817 {WidgetType::SliderInt,
"Basic"},
1818 {WidgetType::SliderFloat,
"Basic"},
1819 {WidgetType::ColorEdit,
"Basic"},
1822 {WidgetType::Separator,
"Layout"},
1823 {WidgetType::SameLine,
"Layout"},
1824 {WidgetType::Spacing,
"Layout"},
1825 {WidgetType::Dummy,
"Layout"},
1828 {WidgetType::BeginGroup,
"Containers"},
1829 {WidgetType::BeginChild,
"Containers"},
1830 {WidgetType::CollapsingHeader,
"Containers"},
1831 {WidgetType::TreeNode,
"Containers"},
1832 {WidgetType::TabBar,
"Containers"},
1835 {WidgetType::BeginTable,
"Tables"},
1836 {WidgetType::TableNextRow,
"Tables"},
1837 {WidgetType::TableNextColumn,
"Tables"},
1840 {WidgetType::Canvas,
"Custom"},
1841 {WidgetType::ProgressBar,
"Custom"},
1842 {WidgetType::Image,
"Custom"},
1846 std::string current_category;
1847 int visible_count = 0;
1849 for (
const auto& item : widgets) {
1851 if (selected_widget_category_ !=
"All" && item.category != selected_widget_category_) {
1856 if (widget_search_filter_[0] !=
'\0') {
1858 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
1859 std::string filter_lower = widget_search_filter_;
1860 std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(), ::tolower);
1861 if (name_lower.find(filter_lower) == std::string::npos) {
1867 if (item.category != current_category) {
1868 if (!current_category.empty()) {
1871 current_category = item.category;
1872 ImGui::TextColored(ImVec4(0.5f, 0.8f, 1.0f, 1.0f),
"%s", current_category.c_str());
1877 ImGui::PushID(
static_cast<int>(item.type));
1878 std::string label = absl::StrFormat(
"%s %s",
1882 if (ImGui::Selectable(label.c_str(),
false, 0, ImVec2(0, 28))) {
1887 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
1888 ImGui::SetDragDropPayload(
"WIDGET_TYPE", &item.type,
sizeof(
WidgetType));
1889 ImGui::Text(
"%s", label.c_str());
1890 ImGui::TextDisabled(
"Drag to canvas");
1891 ImGui::EndDragDropSource();
1899 ImGui::TextDisabled(
"%d widgets available", visible_count);
1902void LayoutDesignerWindow::DrawWidgetCanvas() {
1905 ImGui::TextDisabled(
"Design panel internal layout");
1908 if (!current_panel_design_) {
1909 ImGui::TextWrapped(
"No panel design loaded.");
1911 ImGui::TextWrapped(
"Create a new panel design or select a panel from the Panel Layout mode.");
1913 if (ImGui::Button(
"Create New Panel Design")) {
1914 current_panel_design_ = std::make_unique<PanelDesign>();
1915 current_panel_design_->panel_id =
"new_panel";
1916 current_panel_design_->panel_name =
"New Panel";
1922 ImGui::Text(
"Panel: %s", current_panel_design_->panel_name.c_str());
1926 ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
1927 ImVec2 canvas_size = ImGui::GetContentRegionAvail();
1928 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1931 draw_list->AddRectFilled(canvas_pos,
1932 ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y),
1933 IM_COL32(30, 30, 35, 255));
1936 const float grid_step = 20.0f;
1937 for (
float x_pos = 0; x_pos < canvas_size.x; x_pos += grid_step) {
1939 ImVec2(canvas_pos.x + x_pos, canvas_pos.y),
1940 ImVec2(canvas_pos.x + x_pos, canvas_pos.y + canvas_size.y),
1941 IM_COL32(50, 50, 55, 255));
1943 for (
float y_pos = 0; y_pos < canvas_size.y; y_pos += grid_step) {
1945 ImVec2(canvas_pos.x, canvas_pos.y + y_pos),
1946 ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + y_pos),
1947 IM_COL32(50, 50, 55, 255));
1951 ImVec2 widget_pos = ImVec2(canvas_pos.x + 20, canvas_pos.y + 20);
1952 for (
const auto& widget : current_panel_design_->widgets) {
1956 std::string label = absl::StrFormat(
"%s %s", icon, name);
1958 ImU32 color = (selected_widget_ == widget.get())
1959 ? IM_COL32(255, 200, 100, 255)
1960 : IM_COL32(100, 150, 200, 255);
1962 ImVec2 widget_size = ImVec2(200, 30);
1963 draw_list->AddRectFilled(widget_pos,
1964 ImVec2(widget_pos.x + widget_size.x, widget_pos.y + widget_size.y),
1966 draw_list->AddText(ImVec2(widget_pos.x + 10, widget_pos.y + 8),
1967 IM_COL32(255, 255, 255, 255), label.c_str());
1970 ImVec2 mouse_pos = ImGui::GetMousePos();
1971 if (mouse_pos.x >= widget_pos.x && mouse_pos.x <= widget_pos.x + widget_size.x &&
1972 mouse_pos.y >= widget_pos.y && mouse_pos.y <= widget_pos.y + widget_size.y) {
1973 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
1974 selected_widget_ = widget.get();
1975 LOG_INFO(
"LayoutDesigner",
"Selected widget: %s", widget->id.c_str());
1979 widget_pos.y += widget_size.y + 10;
1983 ImGui::Dummy(canvas_size);
1984 if (ImGui::BeginDragDropTarget()) {
1986 if (
const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(
"WIDGET_TYPE")) {
1990 auto new_widget = std::make_unique<WidgetDefinition>();
1991 new_widget->id = absl::StrFormat(
"widget_%zu", current_panel_design_->widgets.size());
1992 new_widget->type = *widget_type;
1997 for (
const auto& prop : props) {
1998 new_widget->properties.push_back(prop);
2001 current_panel_design_->AddWidget(std::move(new_widget));
2006 ImGui::EndDragDropTarget();
2010void LayoutDesignerWindow::DrawWidgetProperties() {
2014 if (!selected_widget_) {
2015 ImGui::TextWrapped(
"Select a widget to edit its properties");
2017 if (current_panel_design_) {
2020 ImGui::Text(
"Panel: %s", current_panel_design_->panel_name.c_str());
2021 ImGui::Text(
"Widgets: %zu", current_panel_design_->widgets.size());
2023 if (ImGui::Button(
"Clear All Widgets")) {
2024 current_panel_design_->widgets.clear();
2025 selected_widget_ =
nullptr;
2032 ImGui::Text(
"Widget: %s", selected_widget_->id.c_str());
2037 for (
auto& prop : selected_widget_->properties) {
2038 ImGui::PushID(prop.name.c_str());
2040 switch (prop.type) {
2041 case WidgetProperty::Type::String: {
2043 strncpy(buffer, prop.string_value.c_str(),
sizeof(buffer) - 1);
2044 buffer[
sizeof(buffer) - 1] =
'\0';
2045 if (ImGui::InputText(prop.name.c_str(), buffer,
sizeof(buffer))) {
2046 prop.string_value = buffer;
2050 case WidgetProperty::Type::Int:
2051 ImGui::InputInt(prop.name.c_str(), &prop.int_value);
2053 case WidgetProperty::Type::Float:
2054 ImGui::InputFloat(prop.name.c_str(), &prop.float_value);
2056 case WidgetProperty::Type::Bool:
2057 ImGui::Checkbox(prop.name.c_str(), &prop.bool_value);
2059 case WidgetProperty::Type::Color:
2060 ImGui::ColorEdit4(prop.name.c_str(), &prop.color_value.x);
2062 case WidgetProperty::Type::Vec2:
2063 ImGui::InputFloat2(prop.name.c_str(), &prop.vec2_value.x);
2066 ImGui::Text(
"%s: (unsupported type)", prop.name.c_str());
2076 ImGui::Checkbox(
"Same Line", &selected_widget_->same_line);
2078 char tooltip_buffer[256];
2079 strncpy(tooltip_buffer, selected_widget_->tooltip.c_str(),
sizeof(tooltip_buffer) - 1);
2080 tooltip_buffer[
sizeof(tooltip_buffer) - 1] =
'\0';
2081 if (ImGui::InputText(
"Tooltip", tooltip_buffer,
sizeof(tooltip_buffer))) {
2082 selected_widget_->tooltip = tooltip_buffer;
2085 char callback_buffer[256];
2086 strncpy(callback_buffer, selected_widget_->callback_name.c_str(),
sizeof(callback_buffer) - 1);
2087 callback_buffer[
sizeof(callback_buffer) - 1] =
'\0';
2088 if (ImGui::InputText(
"Callback", callback_buffer,
sizeof(callback_buffer))) {
2089 selected_widget_->callback_name = callback_buffer;
2094 if (ImGui::Button(
"Delete Widget")) {
2096 LOG_INFO(
"LayoutDesigner",
"Delete widget: %s", selected_widget_->id.c_str());