feat: Introduce card management system for dungeon editor
- Added EditorCardManager to handle registration, visibility, and management of editor cards. - Integrated card shortcuts and dynamic menu sections for improved user experience in the dungeon editor. - Registered multiple dungeon-related cards with associated shortcuts for quick access. - Enhanced DungeonEditorV2 to utilize the new card management system, allowing independent control of editor cards. - Updated UI components to support card-based editors, improving layout and usability.
This commit is contained in:
577
src/app/gui/editor_card_manager.cc
Normal file
577
src/app/gui/editor_card_manager.cc
Normal file
@@ -0,0 +1,577 @@
|
||||
#include "editor_card_manager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "util/file_util.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
EditorCardManager& EditorCardManager::Get() {
|
||||
static EditorCardManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void EditorCardManager::RegisterCard(const CardInfo& info) {
|
||||
if (info.card_id.empty()) {
|
||||
printf("[EditorCardManager] Warning: Attempted to register card with empty ID\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cards_[info.card_id] = info;
|
||||
printf("[EditorCardManager] Registered card: %s (%s)\n",
|
||||
info.card_id.c_str(), info.display_name.c_str());
|
||||
}
|
||||
|
||||
void EditorCardManager::UnregisterCard(const std::string& card_id) {
|
||||
auto it = cards_.find(card_id);
|
||||
if (it != cards_.end()) {
|
||||
printf("[EditorCardManager] Unregistered card: %s\n", card_id.c_str());
|
||||
cards_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::ClearAllCards() {
|
||||
printf("[EditorCardManager] Clearing all %zu registered cards\n", cards_.size());
|
||||
cards_.clear();
|
||||
}
|
||||
|
||||
bool EditorCardManager::ShowCard(const std::string& card_id) {
|
||||
auto it = cards_.find(card_id);
|
||||
if (it == cards_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it->second.visibility_flag) {
|
||||
*it->second.visibility_flag = true;
|
||||
|
||||
if (it->second.on_show) {
|
||||
it->second.on_show();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorCardManager::HideCard(const std::string& card_id) {
|
||||
auto it = cards_.find(card_id);
|
||||
if (it == cards_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it->second.visibility_flag) {
|
||||
*it->second.visibility_flag = false;
|
||||
|
||||
if (it->second.on_hide) {
|
||||
it->second.on_hide();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorCardManager::ToggleCard(const std::string& card_id) {
|
||||
auto it = cards_.find(card_id);
|
||||
if (it == cards_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (it->second.visibility_flag) {
|
||||
bool new_state = !(*it->second.visibility_flag);
|
||||
*it->second.visibility_flag = new_state;
|
||||
|
||||
if (new_state && it->second.on_show) {
|
||||
it->second.on_show();
|
||||
} else if (!new_state && it->second.on_hide) {
|
||||
it->second.on_hide();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorCardManager::IsCardVisible(const std::string& card_id) const {
|
||||
auto it = cards_.find(card_id);
|
||||
if (it != cards_.end() && it->second.visibility_flag) {
|
||||
return *it->second.visibility_flag;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditorCardManager::ShowAllCardsInCategory(const std::string& category) {
|
||||
for (auto& [id, info] : cards_) {
|
||||
if (info.category == category && info.visibility_flag) {
|
||||
*info.visibility_flag = true;
|
||||
if (info.on_show) info.on_show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::HideAllCardsInCategory(const std::string& category) {
|
||||
for (auto& [id, info] : cards_) {
|
||||
if (info.category == category && info.visibility_flag) {
|
||||
*info.visibility_flag = false;
|
||||
if (info.on_hide) info.on_hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::ShowOnlyCard(const std::string& card_id) {
|
||||
auto target = cards_.find(card_id);
|
||||
if (target == cards_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string category = target->second.category;
|
||||
|
||||
// Hide all cards in the same category
|
||||
for (auto& [id, info] : cards_) {
|
||||
if (info.category == category && info.visibility_flag) {
|
||||
*info.visibility_flag = (id == card_id);
|
||||
|
||||
if (id == card_id && info.on_show) {
|
||||
info.on_show();
|
||||
} else if (id != card_id && info.on_hide) {
|
||||
info.on_hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CardInfo> EditorCardManager::GetCardsInCategory(const std::string& category) const {
|
||||
std::vector<CardInfo> result;
|
||||
|
||||
for (const auto& [id, info] : cards_) {
|
||||
if (info.category == category) {
|
||||
result.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by priority
|
||||
std::sort(result.begin(), result.end(),
|
||||
[](const CardInfo& a, const CardInfo& b) {
|
||||
return a.priority < b.priority;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> EditorCardManager::GetAllCategories() const {
|
||||
std::vector<std::string> categories;
|
||||
|
||||
for (const auto& [id, info] : cards_) {
|
||||
if (std::find(categories.begin(), categories.end(), info.category) == categories.end()) {
|
||||
categories.push_back(info.category);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(categories.begin(), categories.end());
|
||||
return categories;
|
||||
}
|
||||
|
||||
const CardInfo* EditorCardManager::GetCardInfo(const std::string& card_id) const {
|
||||
auto it = cards_.find(card_id);
|
||||
return (it != cards_.end()) ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawViewMenuSection(const std::string& category) {
|
||||
auto cards_in_category = GetCardsInCategory(category);
|
||||
|
||||
if (cards_in_category.empty()) {
|
||||
ImGui::MenuItem("(No cards registered)", nullptr, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& info : cards_in_category) {
|
||||
if (!info.visibility_flag) continue;
|
||||
|
||||
std::string label = info.icon.empty()
|
||||
? info.display_name
|
||||
: info.icon + " " + info.display_name;
|
||||
|
||||
bool visible = *info.visibility_flag;
|
||||
|
||||
if (ImGui::MenuItem(label.c_str(),
|
||||
info.shortcut_hint.empty() ? nullptr : info.shortcut_hint.c_str(),
|
||||
visible)) {
|
||||
ToggleCard(info.card_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawViewMenuAll() {
|
||||
auto categories = GetAllCategories();
|
||||
|
||||
if (categories.empty()) {
|
||||
ImGui::TextDisabled("No cards registered");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& category : categories) {
|
||||
if (ImGui::BeginMenu(category.c_str())) {
|
||||
DrawViewMenuSection(category);
|
||||
ImGui::Separator();
|
||||
|
||||
// Category-level actions
|
||||
if (ImGui::MenuItem(absl::StrFormat("%s Show All", ICON_MD_VISIBILITY).c_str())) {
|
||||
ShowAllCardsInCategory(category);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem(absl::StrFormat("%s Hide All", ICON_MD_VISIBILITY_OFF).c_str())) {
|
||||
HideAllCardsInCategory(category);
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Global actions
|
||||
if (ImGui::MenuItem(absl::StrFormat("%s Show All Cards", ICON_MD_VISIBILITY).c_str())) {
|
||||
ShowAll();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem(absl::StrFormat("%s Hide All Cards", ICON_MD_VISIBILITY_OFF).c_str())) {
|
||||
HideAll();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(absl::StrFormat("%s Card Browser", ICON_MD_DASHBOARD).c_str(),
|
||||
"Ctrl+Shift+B")) {
|
||||
// This will be shown by EditorManager
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawCardBrowser(bool* p_open) {
|
||||
if (!p_open || !*p_open) return;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
|
||||
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if (!ImGui::Begin(absl::StrFormat("%s Card Browser", ICON_MD_DASHBOARD).c_str(),
|
||||
p_open)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
// Search filter
|
||||
static char search_filter[256] = "";
|
||||
ImGui::SetNextItemWidth(-100);
|
||||
ImGui::InputTextWithHint("##CardSearch",
|
||||
absl::StrFormat("%s Search cards...", ICON_MD_SEARCH).c_str(),
|
||||
search_filter, sizeof(search_filter));
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(absl::StrFormat("%s Clear", ICON_MD_CLEAR).c_str())) {
|
||||
search_filter[0] = '\0';
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Statistics
|
||||
ImGui::Text("%s Total Cards: %zu | Visible: %zu",
|
||||
ICON_MD_INFO, GetCardCount(), GetVisibleCardCount());
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Category tabs
|
||||
if (ImGui::BeginTabBar("CardBrowserTabs")) {
|
||||
|
||||
// All Cards tab
|
||||
if (ImGui::BeginTabItem(absl::StrFormat("%s All", ICON_MD_APPS).c_str())) {
|
||||
DrawCardBrowserTable(search_filter, "");
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
// Category tabs
|
||||
for (const auto& category : GetAllCategories()) {
|
||||
std::string tab_label = category;
|
||||
if (ImGui::BeginTabItem(tab_label.c_str())) {
|
||||
DrawCardBrowserTable(search_filter, category);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
// Presets tab
|
||||
if (ImGui::BeginTabItem(absl::StrFormat("%s Presets", ICON_MD_BOOKMARK).c_str())) {
|
||||
DrawPresetsTab();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawCardBrowserTable(const char* search_filter,
|
||||
const std::string& category_filter) {
|
||||
if (ImGui::BeginTable("CardBrowserTable", 4,
|
||||
ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_ScrollY)) {
|
||||
|
||||
ImGui::TableSetupColumn("Card", ImGuiTableColumnFlags_WidthStretch, 0.4f);
|
||||
ImGui::TableSetupColumn("Category", ImGuiTableColumnFlags_WidthStretch, 0.2f);
|
||||
ImGui::TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthStretch, 0.2f);
|
||||
ImGui::TableSetupColumn("Visible", ImGuiTableColumnFlags_WidthFixed, 80.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Collect and sort cards
|
||||
std::vector<CardInfo> display_cards;
|
||||
for (const auto& [id, info] : cards_) {
|
||||
// Apply filters
|
||||
if (!category_filter.empty() && info.category != category_filter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (search_filter && search_filter[0] != '\0') {
|
||||
std::string search_lower = search_filter;
|
||||
std::transform(search_lower.begin(), search_lower.end(),
|
||||
search_lower.begin(), ::tolower);
|
||||
|
||||
std::string name_lower = info.display_name;
|
||||
std::transform(name_lower.begin(), name_lower.end(),
|
||||
name_lower.begin(), ::tolower);
|
||||
|
||||
if (name_lower.find(search_lower) == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
display_cards.push_back(info);
|
||||
}
|
||||
|
||||
// Sort by category then priority
|
||||
std::sort(display_cards.begin(), display_cards.end(),
|
||||
[](const CardInfo& a, const CardInfo& b) {
|
||||
if (a.category != b.category) return a.category < b.category;
|
||||
return a.priority < b.priority;
|
||||
});
|
||||
|
||||
// Draw rows
|
||||
for (const auto& info : display_cards) {
|
||||
ImGui::PushID(info.card_id.c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Card name with icon
|
||||
std::string label = info.icon.empty()
|
||||
? info.display_name
|
||||
: info.icon + " " + info.display_name;
|
||||
ImGui::Text("%s", label.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextDisabled("%s", info.category.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (!info.shortcut_hint.empty()) {
|
||||
ImGui::TextDisabled("%s", info.shortcut_hint.c_str());
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (info.visibility_flag) {
|
||||
bool visible = *info.visibility_flag;
|
||||
if (ImGui::Checkbox("##Visible", &visible)) {
|
||||
*info.visibility_flag = visible;
|
||||
if (visible && info.on_show) {
|
||||
info.on_show();
|
||||
} else if (!visible && info.on_hide) {
|
||||
info.on_hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::DrawPresetsTab() {
|
||||
ImGui::Text("%s Workspace Presets", ICON_MD_BOOKMARK);
|
||||
ImGui::Separator();
|
||||
|
||||
// Save current as preset
|
||||
static char preset_name[256] = "";
|
||||
static char preset_desc[512] = "";
|
||||
|
||||
ImGui::Text("Save Current Layout:");
|
||||
ImGui::InputText("Name", preset_name, sizeof(preset_name));
|
||||
ImGui::InputText("Description", preset_desc, sizeof(preset_desc));
|
||||
|
||||
if (ImGui::Button(absl::StrFormat("%s Save Preset", ICON_MD_SAVE).c_str())) {
|
||||
if (preset_name[0] != '\0') {
|
||||
SavePreset(preset_name, preset_desc);
|
||||
preset_name[0] = '\0';
|
||||
preset_desc[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Saved Presets:");
|
||||
|
||||
// List presets
|
||||
auto presets = GetPresets();
|
||||
if (presets.empty()) {
|
||||
ImGui::TextDisabled("No presets saved");
|
||||
} else {
|
||||
for (const auto& preset : presets) {
|
||||
ImGui::PushID(preset.name.c_str());
|
||||
|
||||
if (ImGui::Button(absl::StrFormat("%s Load", ICON_MD_FOLDER_OPEN).c_str())) {
|
||||
LoadPreset(preset.name);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(absl::StrFormat("%s Delete", ICON_MD_DELETE).c_str())) {
|
||||
DeletePreset(preset.name);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s %s", ICON_MD_BOOKMARK, preset.name.c_str());
|
||||
|
||||
if (!preset.description.empty()) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("- %s", preset.description.c_str());
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("(%zu cards)", preset.visible_cards.size());
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::SavePreset(const std::string& name, const std::string& description) {
|
||||
WorkspacePreset preset;
|
||||
preset.name = name;
|
||||
preset.description = description;
|
||||
|
||||
// Save currently visible cards
|
||||
for (const auto& [id, info] : cards_) {
|
||||
if (info.visibility_flag && *info.visibility_flag) {
|
||||
preset.visible_cards.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
presets_[name] = preset;
|
||||
SavePresetsToFile();
|
||||
|
||||
printf("[EditorCardManager] Saved preset '%s' with %zu cards\n",
|
||||
name.c_str(), preset.visible_cards.size());
|
||||
}
|
||||
|
||||
bool EditorCardManager::LoadPreset(const std::string& name) {
|
||||
auto it = presets_.find(name);
|
||||
if (it == presets_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide all cards first
|
||||
HideAll();
|
||||
|
||||
// Show cards in preset
|
||||
for (const auto& card_id : it->second.visible_cards) {
|
||||
ShowCard(card_id);
|
||||
}
|
||||
|
||||
printf("[EditorCardManager] Loaded preset '%s' with %zu cards\n",
|
||||
name.c_str(), it->second.visible_cards.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EditorCardManager::DeletePreset(const std::string& name) {
|
||||
auto it = presets_.find(name);
|
||||
if (it != presets_.end()) {
|
||||
presets_.erase(it);
|
||||
SavePresetsToFile();
|
||||
printf("[EditorCardManager] Deleted preset '%s'\n", name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EditorCardManager::WorkspacePreset> EditorCardManager::GetPresets() const {
|
||||
std::vector<WorkspacePreset> result;
|
||||
for (const auto& [name, preset] : presets_) {
|
||||
result.push_back(preset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EditorCardManager::ShowAll() {
|
||||
for (auto& [id, info] : cards_) {
|
||||
if (info.visibility_flag) {
|
||||
*info.visibility_flag = true;
|
||||
if (info.on_show) info.on_show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::HideAll() {
|
||||
for (auto& [id, info] : cards_) {
|
||||
if (info.visibility_flag) {
|
||||
*info.visibility_flag = false;
|
||||
if (info.on_hide) info.on_hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCardManager::ResetToDefaults() {
|
||||
// Default visibility based on priority
|
||||
for (auto& [id, info] : cards_) {
|
||||
if (info.visibility_flag) {
|
||||
// Show high-priority cards (priority < 50)
|
||||
*info.visibility_flag = (info.priority < 50);
|
||||
|
||||
if (*info.visibility_flag && info.on_show) {
|
||||
info.on_show();
|
||||
} else if (!*info.visibility_flag && info.on_hide) {
|
||||
info.on_hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t EditorCardManager::GetVisibleCardCount() const {
|
||||
size_t count = 0;
|
||||
for (const auto& [id, info] : cards_) {
|
||||
if (info.visibility_flag && *info.visibility_flag) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void EditorCardManager::SavePresetsToFile() {
|
||||
// Save presets to JSON or simple format
|
||||
// TODO: Implement file I/O
|
||||
printf("[EditorCardManager] Saving %zu presets to file\n", presets_.size());
|
||||
}
|
||||
|
||||
void EditorCardManager::LoadPresetsFromFile() {
|
||||
// Load presets from file
|
||||
// TODO: Implement file I/O
|
||||
printf("[EditorCardManager] Loading presets from file\n");
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
198
src/app/gui/editor_card_manager.h
Normal file
198
src/app/gui/editor_card_manager.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#ifndef YAZE_APP_GUI_EDITOR_CARD_MANAGER_H
|
||||
#define YAZE_APP_GUI_EDITOR_CARD_MANAGER_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
class EditorCard; // Forward declaration
|
||||
|
||||
/**
|
||||
* @brief Metadata for an editor card
|
||||
*/
|
||||
struct CardInfo {
|
||||
std::string card_id; // Unique identifier (e.g., "dungeon.room_selector")
|
||||
std::string display_name; // Human-readable name (e.g., "Room Selector")
|
||||
std::string icon; // Material icon
|
||||
std::string category; // Category (e.g., "Dungeon", "Graphics", "Palette")
|
||||
std::string shortcut_hint; // Display hint (e.g., "Ctrl+Shift+R")
|
||||
bool* visibility_flag; // Pointer to bool controlling visibility
|
||||
EditorCard* card_instance; // Pointer to actual card (optional)
|
||||
std::function<void()> on_show; // Callback when card is shown
|
||||
std::function<void()> on_hide; // Callback when card is hidden
|
||||
int priority; // Display priority for menus (lower = higher)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Central registry and manager for all editor cards
|
||||
*
|
||||
* This singleton provides:
|
||||
* - Global card registration across all editors
|
||||
* - View menu integration
|
||||
* - Keyboard shortcut management
|
||||
* - Workspace preset system
|
||||
* - Quick search/filter
|
||||
* - Programmatic card control without GUI coupling
|
||||
*
|
||||
* Design Philosophy:
|
||||
* - Cards register themselves on creation
|
||||
* - Manager provides unified interface for visibility control
|
||||
* - No direct GUI dependency in card logic
|
||||
* - Supports dynamic card creation/destruction
|
||||
*
|
||||
* Usage:
|
||||
* ```cpp
|
||||
* // In editor initialization:
|
||||
* auto& manager = EditorCardManager::Get();
|
||||
* manager.RegisterCard({
|
||||
* .card_id = "dungeon.room_selector",
|
||||
* .display_name = "Room Selector",
|
||||
* .icon = ICON_MD_LIST,
|
||||
* .category = "Dungeon",
|
||||
* .visibility_flag = &show_room_selector_,
|
||||
* .on_show = []() { printf("Room selector opened\n"); }
|
||||
* });
|
||||
*
|
||||
* // Programmatic control:
|
||||
* manager.ShowCard("dungeon.room_selector");
|
||||
* manager.HideCard("dungeon.room_selector");
|
||||
* manager.ToggleCard("dungeon.room_selector");
|
||||
*
|
||||
* // In View menu:
|
||||
* manager.DrawViewMenuSection("Dungeon");
|
||||
* ```
|
||||
*/
|
||||
class EditorCardManager {
|
||||
public:
|
||||
static EditorCardManager& Get();
|
||||
|
||||
// Registration
|
||||
void RegisterCard(const CardInfo& info);
|
||||
void UnregisterCard(const std::string& card_id);
|
||||
void ClearAllCards();
|
||||
|
||||
// Card control (programmatic, no GUI)
|
||||
bool ShowCard(const std::string& card_id);
|
||||
bool HideCard(const std::string& card_id);
|
||||
bool ToggleCard(const std::string& card_id);
|
||||
bool IsCardVisible(const std::string& card_id) const;
|
||||
|
||||
// Batch operations
|
||||
void ShowAllCardsInCategory(const std::string& category);
|
||||
void HideAllCardsInCategory(const std::string& category);
|
||||
void ShowOnlyCard(const std::string& card_id); // Hide all others in category
|
||||
|
||||
// Query
|
||||
std::vector<CardInfo> GetCardsInCategory(const std::string& category) const;
|
||||
std::vector<std::string> GetAllCategories() const;
|
||||
const CardInfo* GetCardInfo(const std::string& card_id) const;
|
||||
|
||||
// View menu integration
|
||||
void DrawViewMenuSection(const std::string& category);
|
||||
void DrawViewMenuAll(); // Draw all categories as submenus
|
||||
|
||||
// Card browser UI
|
||||
void DrawCardBrowser(bool* p_open); // Visual card browser/toggler
|
||||
void DrawCardBrowserTable(const char* search_filter, const std::string& category_filter);
|
||||
void DrawPresetsTab();
|
||||
|
||||
// Workspace presets
|
||||
struct WorkspacePreset {
|
||||
std::string name;
|
||||
std::vector<std::string> visible_cards; // Card IDs
|
||||
std::string description;
|
||||
};
|
||||
|
||||
void SavePreset(const std::string& name, const std::string& description = "");
|
||||
bool LoadPreset(const std::string& name);
|
||||
void DeletePreset(const std::string& name);
|
||||
std::vector<WorkspacePreset> GetPresets() const;
|
||||
|
||||
// Quick actions
|
||||
void ShowAll(); // Show all registered cards
|
||||
void HideAll(); // Hide all registered cards
|
||||
void ResetToDefaults(); // Reset to default visibility state
|
||||
|
||||
// Statistics
|
||||
size_t GetCardCount() const { return cards_.size(); }
|
||||
size_t GetVisibleCardCount() const;
|
||||
|
||||
private:
|
||||
EditorCardManager() = default;
|
||||
~EditorCardManager() = default;
|
||||
EditorCardManager(const EditorCardManager&) = delete;
|
||||
EditorCardManager& operator=(const EditorCardManager&) = delete;
|
||||
|
||||
std::unordered_map<std::string, CardInfo> cards_;
|
||||
std::unordered_map<std::string, WorkspacePreset> presets_;
|
||||
|
||||
// Helper methods
|
||||
void SavePresetsToFile();
|
||||
void LoadPresetsFromFile();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RAII helper for auto-registering cards
|
||||
*
|
||||
* Usage:
|
||||
* ```cpp
|
||||
* class MyEditor {
|
||||
* CardRegistration room_selector_reg_;
|
||||
*
|
||||
* MyEditor() {
|
||||
* room_selector_reg_ = RegisterCard({
|
||||
* .card_id = "myeditor.room_selector",
|
||||
* .display_name = "Room Selector",
|
||||
* .visibility_flag = &show_room_selector_
|
||||
* });
|
||||
* }
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
class CardRegistration {
|
||||
public:
|
||||
CardRegistration() = default;
|
||||
explicit CardRegistration(const std::string& card_id) : card_id_(card_id) {}
|
||||
|
||||
~CardRegistration() {
|
||||
if (!card_id_.empty()) {
|
||||
EditorCardManager::Get().UnregisterCard(card_id_);
|
||||
}
|
||||
}
|
||||
|
||||
// No copy, allow move
|
||||
CardRegistration(const CardRegistration&) = delete;
|
||||
CardRegistration& operator=(const CardRegistration&) = delete;
|
||||
CardRegistration(CardRegistration&& other) noexcept : card_id_(std::move(other.card_id_)) {
|
||||
other.card_id_.clear();
|
||||
}
|
||||
CardRegistration& operator=(CardRegistration&& other) noexcept {
|
||||
if (this != &other) {
|
||||
card_id_ = std::move(other.card_id_);
|
||||
other.card_id_.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string card_id_;
|
||||
};
|
||||
|
||||
// Convenience function for registration
|
||||
inline CardRegistration RegisterCard(const CardInfo& info) {
|
||||
EditorCardManager::Get().RegisterCard(info);
|
||||
return CardRegistration(info.card_id);
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_EDITOR_CARD_MANAGER_H
|
||||
|
||||
@@ -248,8 +248,25 @@ void EditorCard::SetPosition(Position pos) {
|
||||
}
|
||||
|
||||
bool EditorCard::Begin(bool* p_open) {
|
||||
// Handle icon-collapsed state
|
||||
if (icon_collapsible_ && collapsed_to_icon_) {
|
||||
DrawFloatingIconButton();
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
|
||||
|
||||
// Apply headless mode
|
||||
if (headless_) {
|
||||
flags |= ImGuiWindowFlags_NoTitleBar;
|
||||
flags |= ImGuiWindowFlags_NoCollapse;
|
||||
}
|
||||
|
||||
// Control docking
|
||||
if (!docking_allowed_) {
|
||||
flags |= ImGuiWindowFlags_NoDocking;
|
||||
}
|
||||
|
||||
// Set initial position based on position enum
|
||||
if (first_draw_) {
|
||||
float display_width = ImGui::GetIO().DisplaySize.x;
|
||||
@@ -290,7 +307,13 @@ bool EditorCard::Begin(bool* p_open) {
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, GetThemeColor(ImGuiCol_TitleBg));
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, GetAccentColor());
|
||||
|
||||
bool visible = ImGui::Begin(window_title.c_str(), p_open, flags);
|
||||
// Use p_open parameter if provided, otherwise use stored p_open_
|
||||
bool* actual_p_open = p_open ? p_open : p_open_;
|
||||
|
||||
// If closable is false, don't pass p_open (removes X button)
|
||||
bool visible = ImGui::Begin(window_title.c_str(),
|
||||
closable_ ? actual_p_open : nullptr,
|
||||
flags);
|
||||
|
||||
// Register card window for test automation
|
||||
if (ImGui::GetCurrentWindow() && ImGui::GetCurrentWindow()->ID != 0) {
|
||||
@@ -318,6 +341,38 @@ void EditorCard::Focus() {
|
||||
focused_ = true;
|
||||
}
|
||||
|
||||
void EditorCard::DrawFloatingIconButton() {
|
||||
// Draw a small floating button with the icon
|
||||
ImGui::SetNextWindowPos(saved_icon_pos_, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(50, 50));
|
||||
|
||||
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoCollapse;
|
||||
|
||||
std::string icon_window_name = window_name_ + "##IconCollapsed";
|
||||
|
||||
if (ImGui::Begin(icon_window_name.c_str(), nullptr, flags)) {
|
||||
// Draw icon button
|
||||
if (ImGui::Button(icon_.c_str(), ImVec2(40, 40))) {
|
||||
collapsed_to_icon_ = false; // Expand back to full window
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Expand %s", title_.c_str());
|
||||
}
|
||||
|
||||
// Allow dragging the icon
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
|
||||
ImVec2 mouse_delta = ImGui::GetIO().MouseDelta;
|
||||
saved_icon_pos_.x += mouse_delta.x;
|
||||
saved_icon_pos_.y += mouse_delta.y;
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// EditorLayout Implementation
|
||||
// ============================================================================
|
||||
|
||||
@@ -121,6 +121,9 @@ class EditorCard {
|
||||
void SetPosition(Position pos);
|
||||
void SetMinimizable(bool minimizable) { minimizable_ = minimizable; }
|
||||
void SetClosable(bool closable) { closable_ = closable; }
|
||||
void SetHeadless(bool headless) { headless_ = headless; }
|
||||
void SetDockingAllowed(bool allowed) { docking_allowed_ = allowed; }
|
||||
void SetIconCollapsible(bool collapsible) { icon_collapsible_ = collapsible; }
|
||||
|
||||
// Begin drawing the card
|
||||
bool Begin(bool* p_open = nullptr);
|
||||
@@ -151,6 +154,15 @@ class EditorCard {
|
||||
bool first_draw_ = true;
|
||||
bool focused_ = false;
|
||||
bool* p_open_ = nullptr;
|
||||
|
||||
// UX enhancements
|
||||
bool headless_ = false; // Minimal chrome, no title bar
|
||||
bool docking_allowed_ = true; // Allow docking
|
||||
bool icon_collapsible_ = false; // Can collapse to floating icon
|
||||
bool collapsed_to_icon_ = false; // Currently collapsed
|
||||
ImVec2 saved_icon_pos_ = ImVec2(10, 100); // Position when collapsed to icon
|
||||
|
||||
void DrawFloatingIconButton();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ set(
|
||||
app/gui/widgets/widget_state_capture.cc
|
||||
app/gui/ui_helpers.cc
|
||||
app/gui/editor_layout.cc
|
||||
app/gui/editor_card_manager.cc
|
||||
# Canvas system components
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
|
||||
Reference in New Issue
Block a user