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());
506 {{ImGuiCol_Header, ImVec4(0.2f, 0.2f, 0.25f, 1.0f)},
507 {ImGuiCol_HeaderHovered, ImVec4(0.25f, 0.25f, 0.35f, 1.0f)},
508 {ImGuiCol_HeaderActive, ImVec4(0.3f, 0.3f, 0.4f, 1.0f)}});
510 bool clicked = ImGui::Selectable(
511 absl::StrFormat(
"%s %s", panel.icon, panel.name).c_str(),
false, 0,
515 LOG_INFO(
"LayoutDesigner",
"Selected panel: %s", panel.name.c_str());
519 if (ImGui::IsItemHovered() && !panel.description.empty()) {
520 ImGui::SetTooltip(
"%s\n\nID: %s\nPriority: %d",
521 panel.description.c_str(), panel.id.c_str(),
526 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
530 ImGui::SetDragDropPayload(
531 kPanelPayloadType, panel.id.c_str(),
532 panel.id.size() + 1);
533 ImGui::Text(
"%s %s", panel.icon.c_str(), panel.name.c_str());
534 ImGui::TextDisabled(
"Drag to canvas");
535 ImGui::EndDragDropSource();
537 LOG_INFO(
"DragDrop",
"Drag started: %s", panel.name.c_str());
551 ImGui::TextDisabled(
"%d panels available", visible_count);
568 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
570 strcmp(drag_payload->DataType, kPanelPayloadType) == 0;
571 if (drag_payload && drag_payload->DataType) {
573 ImGui::TextColored(ImVec4(0, 1, 0, 1),
575 drag_payload->DataType);
582 "No layout loaded. Create a new layout or open an existing one.");
584 if (ImGui::Button(
"Create New Layout")) {
591 ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
595 ImDrawList* draw_list = ImGui::GetWindowDrawList();
596 const ImU32 grid_color = ImGui::GetColorU32(ImGuiCol_TableBorderStrong);
600 for (
float x_pos = 0; x_pos < scaled_size.x; x_pos += grid_step) {
601 draw_list->AddLineV(canvas_pos.x + x_pos, canvas_pos.y,
602 canvas_pos.y + scaled_size.y, grid_color);
604 for (
float y_pos = 0; y_pos < scaled_size.y; y_pos += grid_step) {
605 draw_list->AddLineH(canvas_pos.x, canvas_pos.x + scaled_size.x,
606 canvas_pos.y + y_pos, grid_color);
616 ImGui::SetCursorScreenPos(canvas_pos);
617 ImGui::InvisibleButton(
"canvas_drop_zone", scaled_size);
621 if (ImGui::BeginDragDropTarget()) {
623 const ImGuiPayload* preview = ImGui::GetDragDropPayload();
624 if (preview && strcmp(preview->DataType, kPanelPayloadType) == 0) {
628 if (
const ImGuiPayload* payload =
629 ImGui::AcceptDragDropPayload(kPanelPayloadType)) {
630 const char* panel_id_cstr =
static_cast<const char*
>(payload->Data);
631 std::string panel_id = panel_id_cstr ? panel_id_cstr :
"";
633 if (!resolved.has_value()) {
634 LOG_WARN(
"DragDrop",
"Unknown panel payload: %s", panel_id.c_str());
639 ImGui::EndDragDropTarget();
644 const ImVec2& size) {
648 ImDrawList* draw_list = ImGui::GetWindowDrawList();
649 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
650 auto alpha_color = [](ImU32 base,
float alpha_scale) {
651 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
653 return ImGui::ColorConvertFloat4ToU32(c);
657 const ImGuiPayload* drag_payload = ImGui::GetDragDropPayload();
658 bool is_drag_active = drag_payload !=
nullptr &&
659 drag_payload->DataType !=
nullptr &&
660 strcmp(drag_payload->DataType,
"PANEL_ID") == 0;
664 ImU32 border_color = ImGui::GetColorU32(ImGuiCol_Border);
669 border_color = ImGui::GetColorU32(ImGuiCol_CheckMark);
673 if (is_drag_active) {
675 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
676 border_color = ImGui::GetColorU32(ImGuiCol_TextDisabled);
678 border_color = ImGui::GetColorU32(ImGuiCol_HeaderHovered);
685 draw_list->AddRect(pos, rect_max, border_color, 4.0f, 2.0f);
689 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
692 LOG_INFO(
"LayoutDesigner",
"Selected dock node with %zu panels",
697 const float panel_padding = 8.0f;
698 const float capsule_height = 26.0f;
699 ImVec2 capsule_pos = ImVec2(pos.x + panel_padding, pos.y + panel_padding);
700 for (
const auto& panel : node->
panels) {
701 ImVec2 capsule_min = capsule_pos;
704 ImVec2(std::max(capsule_min.x, rect_max.x - panel_padding),
705 capsule_pos.y + capsule_height);
707 alpha_color(ImGui::GetColorU32(ImGuiCol_Header), 0.7f);
708 ImU32 capsule_border = ImGui::GetColorU32(ImGuiCol_HeaderActive);
709 draw_list->AddRectFilled(capsule_min, capsule_max, capsule_fill, 6.0f);
710 draw_list->AddRect(capsule_min, capsule_max, capsule_border, 6.0f, 1.5f);
713 absl::StrFormat(
"%s %s", panel.icon, panel.display_name);
714 draw_list->AddText(ImVec2(capsule_min.x + 8, capsule_min.y + 5),
715 ImGui::GetColorU32(ImGuiCol_Text), label.c_str());
718 std::string sub = absl::StrFormat(
"ID: %s", panel.panel_id.c_str());
719 draw_list->AddText(ImVec2(capsule_min.x + 8, capsule_min.y + 5 + 12),
720 alpha_color(ImGui::GetColorU32(ImGuiCol_Text), 0.7f),
724 ImRect capsule_rect(capsule_min, capsule_max);
725 if (capsule_rect.Contains(ImGui::GetMousePos())) {
726 ImGui::BeginTooltip();
727 ImGui::TextUnformatted(label.c_str());
728 ImGui::TextDisabled(
"%s", panel.panel_id.c_str());
732 capsule_pos.y += capsule_height + 6.0f;
736 std::string node_flags_str;
737 if (node->
flags & ImGuiDockNodeFlags_NoTabBar)
738 node_flags_str +=
"[NoTab] ";
739 if (node->
flags & ImGuiDockNodeFlags_HiddenTabBar)
740 node_flags_str +=
"[HiddenTab] ";
741 if (node->
flags & ImGuiDockNodeFlags_NoCloseButton)
742 node_flags_str +=
"[NoClose] ";
743 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe)
744 node_flags_str +=
"[NoDock] ";
746 if (!node_flags_str.empty()) {
747 ImVec2 flags_size = ImGui::CalcTextSize(node_flags_str.c_str());
748 draw_list->AddText(ImVec2(rect_max.x - flags_size.x - 5, pos.y + 5),
749 alpha_color(ImGui::GetColorU32(ImGuiCol_Text), 0.8f),
750 node_flags_str.c_str());
753 if (node->
panels.empty()) {
754 const char* empty_text = is_drag_active ?
"Drop panel here" :
"Empty";
755 if (node->
flags & ImGuiDockNodeFlags_NoDockingOverMe) {
756 empty_text =
"Docking Disabled";
758 ImVec2 text_size = ImGui::CalcTextSize(empty_text);
759 draw_list->AddText(ImVec2(pos.x + (size.x - text_size.x) / 2,
760 pos.y + (size.y - text_size.y) / 2),
761 ImGui::GetColorU32(ImGuiCol_TextDisabled), empty_text);
767 ImVec2 left_pos = pos;
775 left_size = ImVec2(std::max(0.0f, split_x - 2), size.y);
776 right_size = ImVec2(std::max(0.0f, 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, std::max(0.0f, split_y - 2));
787 right_size = ImVec2(size.x, std::max(0.0f, 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);
952 return "// No layout loaded";
957 &code, absl::StrFormat(
958 "// Generated by YAZE Layout Designer\n"
959 "// Layout: \"%s\"\n"
960 "// Generated: <timestamp>\n\n"
961 "void LayoutManager::Build%sLayout(ImGuiID dockspace_id) {\n"
962 " ImGui::DockBuilderRemoveNode(dockspace_id);\n"
963 " ImGui::DockBuilderAddNode(dockspace_id, "
964 "ImGuiDockNodeFlags_DockSpace);\n"
965 " ImGui::DockBuilderSetNodeSize(dockspace_id, "
966 "ImGui::GetMainViewport()->Size);\n\n"
967 " ImGuiID dock_main_id = dockspace_id;\n",
971 std::function<void(
DockNode*, std::string)> generate_splits =
972 [&](
DockNode* node, std::string parent_id_var) {
973 if (!node || node->
IsLeaf()) {
975 if (node && node->
flags != ImGuiDockNodeFlags_None) {
976 absl::StrAppend(&code,
977 absl::StrFormat(
" if (ImGuiDockNode* node = "
978 "ImGui::DockBuilderGetNode(%s)) {\n"
979 " node->LocalFlags = %d;\n"
981 parent_id_var, node->
flags));
987 std::string child_left_var =
988 absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
989 std::string child_right_var =
990 absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
993 static int id_counter = 1000;
1000 std::string dir_str;
1003 dir_str =
"ImGuiDir_Left";
1005 case ImGuiDir_Right:
1006 dir_str =
"ImGuiDir_Right";
1009 dir_str =
"ImGuiDir_Up";
1012 dir_str =
"ImGuiDir_Down";
1015 dir_str =
"ImGuiDir_Left";
1022 " ImGuiID %s, %s;\n"
1023 " ImGui::DockBuilderSplitNode(%s, %s, %.2ff, &%s, &%s);\n",
1024 child_left_var, child_right_var, parent_id_var, dir_str,
1025 node->
split_ratio, child_left_var, child_right_var));
1028 if (node->
flags != ImGuiDockNodeFlags_None) {
1029 absl::StrAppend(&code,
1030 absl::StrFormat(
" if (ImGuiDockNode* node = "
1031 "ImGui::DockBuilderGetNode(%s)) {\n"
1032 " node->LocalFlags = %d;\n"
1034 parent_id_var, node->
flags));
1037 generate_splits(node->
child_left.get(), child_left_var);
1038 generate_splits(node->
child_right.get(), child_right_var);
1042 std::function<void(
DockNode*, std::string)> generate_docking =
1043 [&](
DockNode* node, std::string node_id_var) {
1048 for (
const auto& panel : node->
panels) {
1049 if (panel.flags != ImGuiWindowFlags_None) {
1052 absl::StrFormat(
" // Note: Panel '%s' requires flags: %d\n",
1053 panel.panel_id, panel.flags));
1057 absl::StrFormat(
" ImGui::DockBuilderDockWindow(\"%s\", %s);\n",
1058 panel.panel_id, node_id_var));
1061 std::string child_left_var =
1062 absl::StrFormat(
"dock_id_%d", node->
child_left->node_id);
1063 std::string child_right_var =
1064 absl::StrFormat(
"dock_id_%d", node->
child_right->node_id);
1066 generate_docking(node->
child_left.get(), child_left_var);
1067 generate_docking(node->
child_right.get(), child_right_var);
1073 absl::StrAppend(&code,
"\n");
1077 absl::StrAppend(&code,
"\n ImGui::DockBuilderFinish(dockspace_id);\n}\n");
1422bool LayoutDesignerWindow::MatchesSearchFilter(
1424 if (search_filter_[0] ==
'\0') {
1428 std::string filter_lower = search_filter_;
1429 std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(),
1433 std::string name_lower = panel.
name;
1434 std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(),
1436 if (name_lower.find(filter_lower) != std::string::npos) {
1441 std::string id_lower = panel.
id;
1442 std::transform(id_lower.begin(), id_lower.end(), id_lower.begin(), ::tolower);
1443 if (id_lower.find(filter_lower) != std::string::npos) {
1449 std::transform(desc_lower.begin(), desc_lower.end(), desc_lower.begin(),
1451 if (desc_lower.find(filter_lower) != std::string::npos) {
1598void LayoutDesignerWindow::DrawDropZones(
const ImVec2& pos,
const ImVec2& size,
1601 LOG_WARN(
"DragDrop",
"DrawDropZones called with null target_node");
1605 ImDrawList* draw_list = ImGui::GetWindowDrawList();
1606 ImVec2 mouse_pos = ImGui::GetMousePos();
1607 ImVec2 rect_min = pos;
1608 ImVec2 rect_max = ImVec2(pos.x + size.x, pos.y + size.y);
1609 auto alpha_color = [](ImU32 base,
float alpha_scale) {
1610 ImVec4 c = ImGui::ColorConvertU32ToFloat4(base);
1612 return ImGui::ColorConvertFloat4ToU32(c);
1616 ImGuiDir zone = GetDropZone(mouse_pos, rect_min, rect_max);
1618 LOG_INFO(
"DragDrop",
"Drawing drop zones, mouse zone: %d",
1619 static_cast<int>(zone));
1622 float zone_size = 0.25f;
1631 std::vector<DropZone> zones = {
1633 {rect_min, ImVec2(rect_min.x + size.x * zone_size, rect_max.y),
1636 {ImVec2(rect_max.x - size.x * zone_size, rect_min.y), rect_max,
1639 {rect_min, ImVec2(rect_max.x, rect_min.y + size.y * zone_size),
1642 {ImVec2(rect_min.x, rect_max.y - size.y * zone_size), rect_max,
1645 {ImVec2(rect_min.x + size.x * zone_size, rect_min.y + size.y * zone_size),
1646 ImVec2(rect_max.x - size.x * zone_size, rect_max.y - size.y * zone_size),
1650 for (
const auto& drop_zone : zones) {
1651 bool is_hovered = (zone == drop_zone.dir);
1652 ImU32 base_zone = ImGui::GetColorU32(ImGuiCol_Header);
1653 ImU32 color = is_hovered ? alpha_color(base_zone, 0.8f)
1654 : alpha_color(base_zone, 0.35f);
1656 draw_list->AddRectFilled(drop_zone.min, drop_zone.max, color, 4.0f);
1657 draw_list->AddRect(drop_zone.min, drop_zone.max,
1658 ImGui::GetColorU32(ImGuiCol_HeaderActive), 4.0f, 1.0f);
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) {
2016 draw_list->AddLineV(canvas_pos.x + x_pos, canvas_pos.y,
2017 canvas_pos.y + canvas_size.y,
2018 IM_COL32(50, 50, 55, 255));
2020 for (
float y_pos = 0; y_pos < canvas_size.y; y_pos += grid_step) {
2021 draw_list->AddLineH(canvas_pos.x, canvas_pos.x + canvas_size.x,
2022 canvas_pos.y + y_pos, IM_COL32(50, 50, 55, 255));
2026 ImVec2 widget_pos = ImVec2(canvas_pos.x + 20, canvas_pos.y + 20);
2027 for (
const auto& widget : current_panel_design_->widgets) {
2031 std::string label = absl::StrFormat(
"%s %s", icon, name);
2033 ImU32 color = (selected_widget_ == widget.get())
2034 ? IM_COL32(255, 200, 100, 255)
2035 : IM_COL32(100, 150, 200, 255);
2037 ImVec2 widget_size = ImVec2(200, 30);
2038 draw_list->AddRectFilled(
2040 ImVec2(widget_pos.x + widget_size.x, widget_pos.y + widget_size.y),
2042 draw_list->AddText(ImVec2(widget_pos.x + 10, widget_pos.y + 8),
2043 IM_COL32(255, 255, 255, 255), label.c_str());
2046 ImVec2 mouse_pos = ImGui::GetMousePos();
2047 if (mouse_pos.x >= widget_pos.x &&
2048 mouse_pos.x <= widget_pos.x + widget_size.x &&
2049 mouse_pos.y >= widget_pos.y &&
2050 mouse_pos.y <= widget_pos.y + widget_size.y) {
2051 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
2052 selected_widget_ = widget.get();
2053 LOG_INFO(
"LayoutDesigner",
"Selected widget: %s", widget->id.c_str());
2057 widget_pos.y += widget_size.y + 10;
2061 ImGui::Dummy(canvas_size);
2062 if (ImGui::BeginDragDropTarget()) {
2064 if (
const ImGuiPayload* payload =
2065 ImGui::AcceptDragDropPayload(
"WIDGET_TYPE")) {
2069 auto new_widget = std::make_unique<WidgetDefinition>();
2071 absl::StrFormat(
"widget_%zu", current_panel_design_->widgets.size());
2072 new_widget->type = *widget_type;
2077 for (
const auto& prop : props) {
2078 new_widget->properties.push_back(prop);
2081 current_panel_design_->AddWidget(std::move(new_widget));
2082 LOG_INFO(
"LayoutDesigner",
"Added widget: %s",
2087 ImGui::EndDragDropTarget();
2091void LayoutDesignerWindow::DrawWidgetProperties() {
2095 if (!selected_widget_) {
2096 ImGui::TextWrapped(
"Select a widget to edit its properties");
2098 if (current_panel_design_) {
2101 ImGui::Text(
"Panel: %s", current_panel_design_->panel_name.c_str());
2102 ImGui::Text(
"Widgets: %zu", current_panel_design_->widgets.size());
2104 if (ImGui::Button(
"Clear All Widgets")) {
2105 current_panel_design_->widgets.clear();
2106 selected_widget_ =
nullptr;
2113 ImGui::Text(
"Widget: %s", selected_widget_->id.c_str());
2118 for (
auto& prop : selected_widget_->properties) {
2119 ImGui::PushID(prop.name.c_str());
2121 switch (prop.type) {
2122 case WidgetProperty::Type::String: {
2124 strncpy(buffer, prop.string_value.c_str(),
sizeof(buffer) - 1);
2125 buffer[
sizeof(buffer) - 1] =
'\0';
2126 if (ImGui::InputText(prop.name.c_str(), buffer,
sizeof(buffer))) {
2127 prop.string_value = buffer;
2131 case WidgetProperty::Type::Int:
2132 ImGui::InputInt(prop.name.c_str(), &prop.int_value);
2134 case WidgetProperty::Type::Float:
2135 ImGui::InputFloat(prop.name.c_str(), &prop.float_value);
2137 case WidgetProperty::Type::Bool:
2138 ImGui::Checkbox(prop.name.c_str(), &prop.bool_value);
2140 case WidgetProperty::Type::Color:
2141 ImGui::ColorEdit4(prop.name.c_str(), &prop.color_value.x);
2143 case WidgetProperty::Type::Vec2:
2144 ImGui::InputFloat2(prop.name.c_str(), &prop.vec2_value.x);
2147 ImGui::Text(
"%s: (unsupported type)", prop.name.c_str());
2157 ImGui::Checkbox(
"Same Line", &selected_widget_->same_line);
2159 char tooltip_buffer[256];
2160 strncpy(tooltip_buffer, selected_widget_->tooltip.c_str(),
2161 sizeof(tooltip_buffer) - 1);
2162 tooltip_buffer[
sizeof(tooltip_buffer) - 1] =
'\0';
2163 if (ImGui::InputText(
"Tooltip", tooltip_buffer,
sizeof(tooltip_buffer))) {
2164 selected_widget_->tooltip = tooltip_buffer;
2167 char callback_buffer[256];
2168 strncpy(callback_buffer, selected_widget_->callback_name.c_str(),
2169 sizeof(callback_buffer) - 1);
2170 callback_buffer[
sizeof(callback_buffer) - 1] =
'\0';
2171 if (ImGui::InputText(
"Callback", callback_buffer,
sizeof(callback_buffer))) {
2172 selected_widget_->callback_name = callback_buffer;
2177 if (ImGui::Button(
"Delete Widget")) {
2179 LOG_INFO(
"LayoutDesigner",
"Delete widget: %s",
2180 selected_widget_->id.c_str());