feat: Refactor editor structure and enhance multi-session support
- Moved `menu_builder.cc` to a new `ui` directory for better organization. - Updated `EditorManager` to generate unique ImGui IDs for multi-session support, ensuring separate window handling. - Added session ID management in `EditorContext` for improved child panel identification. - Introduced a new `MenuBuilder` class for streamlined ImGui menu creation, enhancing UI maintainability.
This commit is contained in:
187
src/app/editor/ui/menu_builder.cc
Normal file
187
src/app/editor/ui/menu_builder.cc
Normal file
@@ -0,0 +1,187 @@
|
||||
#include "app/editor/ui/menu_builder.h"
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
MenuBuilder& MenuBuilder::BeginMenu(const char* label, const char* icon) {
|
||||
Menu menu;
|
||||
menu.label = label;
|
||||
if (icon) {
|
||||
menu.icon = icon;
|
||||
}
|
||||
menus_.push_back(menu);
|
||||
current_menu_ = &menus_.back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::BeginSubMenu(const char* label, const char* icon,
|
||||
EnabledCheck enabled) {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kSubMenuBegin;
|
||||
item.label = label;
|
||||
if (icon) {
|
||||
item.icon = icon;
|
||||
}
|
||||
item.enabled = enabled;
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::EndMenu() {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
// Check if we're ending a submenu or top-level menu
|
||||
bool is_submenu = false;
|
||||
for (auto it = current_menu_->items.rbegin();
|
||||
it != current_menu_->items.rend(); ++it) {
|
||||
if (it->type == MenuItem::Type::kSubMenuBegin) {
|
||||
is_submenu = true;
|
||||
break;
|
||||
} else if (it->type == MenuItem::Type::kSubMenuEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_submenu) {
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kSubMenuEnd;
|
||||
current_menu_->items.push_back(item);
|
||||
} else {
|
||||
current_menu_ = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::Item(const char* label, const char* icon,
|
||||
Callback callback, const char* shortcut,
|
||||
EnabledCheck enabled, EnabledCheck checked) {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kItem;
|
||||
item.label = label;
|
||||
if (icon) {
|
||||
item.icon = icon;
|
||||
}
|
||||
if (shortcut) {
|
||||
item.shortcut = shortcut;
|
||||
}
|
||||
item.callback = callback;
|
||||
item.enabled = enabled;
|
||||
item.checked = checked;
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::Item(const char* label, Callback callback,
|
||||
const char* shortcut, EnabledCheck enabled) {
|
||||
return Item(label, nullptr, callback, shortcut, enabled, nullptr);
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::Separator() {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kSeparator;
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::DisabledItem(const char* label, const char* icon) {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kDisabled;
|
||||
item.label = label;
|
||||
if (icon) {
|
||||
item.icon = icon;
|
||||
}
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MenuBuilder::Draw() {
|
||||
for (const auto& menu : menus_) {
|
||||
std::string menu_label = menu.icon.empty()
|
||||
? menu.label
|
||||
: absl::StrCat(menu.icon, " ", menu.label);
|
||||
|
||||
if (ImGui::BeginMenu(menu_label.c_str())) {
|
||||
for (const auto& item : menu.items) {
|
||||
DrawMenuItem(item);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuBuilder::DrawMenuItem(const MenuItem& item) {
|
||||
switch (item.type) {
|
||||
case MenuItem::Type::kSeparator:
|
||||
ImGui::Separator();
|
||||
break;
|
||||
|
||||
case MenuItem::Type::kDisabled: {
|
||||
std::string label = item.icon.empty()
|
||||
? item.label
|
||||
: absl::StrCat(item.icon, " ", item.label);
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::MenuItem(label.c_str(), nullptr, false, false);
|
||||
ImGui::EndDisabled();
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItem::Type::kSubMenuBegin: {
|
||||
std::string label = item.icon.empty()
|
||||
? item.label
|
||||
: absl::StrCat(item.icon, " ", item.label);
|
||||
|
||||
bool enabled = !item.enabled || item.enabled();
|
||||
if (enabled && ImGui::BeginMenu(label.c_str())) {
|
||||
// Submenu items will be drawn in subsequent calls
|
||||
} else if (!enabled) {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::MenuItem(label.c_str(), nullptr, false, false);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItem::Type::kSubMenuEnd:
|
||||
ImGui::EndMenu();
|
||||
break;
|
||||
|
||||
case MenuItem::Type::kItem: {
|
||||
std::string label = item.icon.empty()
|
||||
? item.label
|
||||
: absl::StrCat(item.icon, " ", item.label);
|
||||
|
||||
bool enabled = !item.enabled || item.enabled();
|
||||
bool checked = item.checked && item.checked();
|
||||
|
||||
const char* shortcut_str = item.shortcut.empty()
|
||||
? nullptr
|
||||
: item.shortcut.c_str();
|
||||
|
||||
if (ImGui::MenuItem(label.c_str(), shortcut_str, checked, enabled)) {
|
||||
if (item.callback) {
|
||||
item.callback();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuBuilder::Clear() {
|
||||
menus_.clear();
|
||||
current_menu_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
119
src/app/editor/ui/menu_builder.h
Normal file
119
src/app/editor/ui/menu_builder.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef YAZE_APP_EDITOR_UI_MENU_BUILDER_H_
|
||||
#define YAZE_APP_EDITOR_UI_MENU_BUILDER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
/**
|
||||
* @class MenuBuilder
|
||||
* @brief Fluent interface for building ImGui menus with icons
|
||||
*
|
||||
* Provides a cleaner, more maintainable way to construct menus:
|
||||
*
|
||||
* MenuBuilder menu;
|
||||
* menu.BeginMenu("File", ICON_MD_FOLDER)
|
||||
* .Item("Open", ICON_MD_FILE_OPEN, []() { OpenFile(); })
|
||||
* .Separator()
|
||||
* .Item("Quit", ICON_MD_EXIT_TO_APP, []() { Quit(); })
|
||||
* .EndMenu();
|
||||
*/
|
||||
class MenuBuilder {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
using EnabledCheck = std::function<bool()>;
|
||||
|
||||
MenuBuilder() = default;
|
||||
|
||||
/**
|
||||
* @brief Begin a top-level menu
|
||||
*/
|
||||
MenuBuilder& BeginMenu(const char* label, const char* icon = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Begin a submenu
|
||||
*/
|
||||
MenuBuilder& BeginSubMenu(const char* label, const char* icon = nullptr,
|
||||
EnabledCheck enabled = nullptr);
|
||||
|
||||
/**
|
||||
* @brief End the current menu/submenu
|
||||
*/
|
||||
MenuBuilder& EndMenu();
|
||||
|
||||
/**
|
||||
* @brief Add a menu item
|
||||
*/
|
||||
MenuBuilder& Item(const char* label, const char* icon, Callback callback,
|
||||
const char* shortcut = nullptr,
|
||||
EnabledCheck enabled = nullptr,
|
||||
EnabledCheck checked = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Add a menu item without icon (convenience)
|
||||
*/
|
||||
MenuBuilder& Item(const char* label, Callback callback,
|
||||
const char* shortcut = nullptr,
|
||||
EnabledCheck enabled = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Add a separator
|
||||
*/
|
||||
MenuBuilder& Separator();
|
||||
|
||||
/**
|
||||
* @brief Add a disabled item (grayed out)
|
||||
*/
|
||||
MenuBuilder& DisabledItem(const char* label, const char* icon = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Draw the menu bar (call in main menu bar)
|
||||
*/
|
||||
void Draw();
|
||||
|
||||
/**
|
||||
* @brief Clear all menus
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
struct MenuItem {
|
||||
enum class Type {
|
||||
kItem,
|
||||
kSubMenuBegin,
|
||||
kSubMenuEnd,
|
||||
kSeparator,
|
||||
kDisabled
|
||||
};
|
||||
|
||||
Type type;
|
||||
std::string label;
|
||||
std::string icon;
|
||||
std::string shortcut;
|
||||
Callback callback;
|
||||
EnabledCheck enabled;
|
||||
EnabledCheck checked;
|
||||
};
|
||||
|
||||
struct Menu {
|
||||
std::string label;
|
||||
std::string icon;
|
||||
std::vector<MenuItem> items;
|
||||
};
|
||||
|
||||
std::vector<Menu> menus_;
|
||||
Menu* current_menu_ = nullptr;
|
||||
|
||||
void DrawMenuItem(const MenuItem& item);
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UI_MENU_BUILDER_H_
|
||||
Reference in New Issue
Block a user