feat(editor): enhance EditorManager with card-based editor functionality

- Introduced methods to determine if an editor is card-based and to retrieve its category.
- Implemented a sidebar toggle shortcut (Ctrl+B) for managing card visibility.
- Added functionality to hide current editor cards when switching editors.
- Updated editor initialization to register global shortcuts for activating specific editors.
- Enhanced the Update method to draw a sidebar for the current category editor.

Benefits:
- Improved user experience by organizing editor cards and providing quick access to editor categories.
- Streamlined editor management, making it easier to switch between different editing contexts.
This commit is contained in:
scawful
2025-10-12 11:57:39 -04:00
parent b7f78008b7
commit 312522d709
20 changed files with 2213 additions and 343 deletions

View File

@@ -260,5 +260,54 @@ absl::Status DisplayEditablePalette(gfx::SnesPalette& palette,
return absl::OkStatus();
}
IMGUI_API bool PaletteColorButton(const char* id, const gfx::SnesColor& color,
bool is_selected, bool is_modified,
const ImVec2& size,
ImGuiColorEditFlags flags) {
ImVec4 display_color = ConvertSnesColorToImVec4(color);
// Add visual indicators for selection and modification
ImGui::PushID(id);
// Selection border
if (is_selected) {
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.8f, 0.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
}
bool clicked = ImGui::ColorButton(id, display_color, flags, size);
if (is_selected) {
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
// Modification indicator (small dot in corner)
if (is_modified) {
ImVec2 pos = ImGui::GetItemRectMin();
ImVec2 dot_pos = ImVec2(pos.x + size.x - 6, pos.y + 2);
ImGui::GetWindowDrawList()->AddCircleFilled(dot_pos, 3.0f,
IM_COL32(255, 128, 0, 255));
}
// Tooltip with color info
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("SNES: $%04X", color.snes());
auto rgb = color.rgb();
ImGui::Text("RGB: (%d, %d, %d)",
static_cast<int>(rgb.x),
static_cast<int>(rgb.y),
static_cast<int>(rgb.z));
if (is_modified) {
ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Modified");
}
ImGui::EndTooltip();
}
ImGui::PopID();
return clicked;
}
} // namespace gui
} // namespace yaze

View File

@@ -56,6 +56,12 @@ IMGUI_API absl::Status DisplayEditablePalette(gfx::SnesPalette &palette,
void SelectablePalettePipeline(uint64_t &palette_id, bool &refresh_graphics,
gfx::SnesPalette &palette);
// Palette color button with selection and modification indicators
IMGUI_API bool PaletteColorButton(const char* id, const gfx::SnesColor& color,
bool is_selected, bool is_modified,
const ImVec2& size = ImVec2(28, 28),
ImGuiColorEditFlags flags = 0);
} // namespace gui
} // namespace yaze

View File

@@ -5,8 +5,8 @@
#include "absl/strings/str_format.h"
#include "app/gui/icons.h"
#include "app/gui/theme_manager.h"
#include "imgui/imgui.h"
#include "util/file_util.h"
namespace yaze {
namespace gui {
@@ -34,11 +34,54 @@ void EditorCardManager::RegisterCard(const CardInfo& info) {
info.card_id.c_str(), info.display_name.c_str());
}
void EditorCardManager::RegisterCard(const std::string& card_id,
const std::string& display_name,
const std::string& icon,
const std::string& category,
const std::string& shortcut_hint,
int priority,
std::function<void()> on_show,
std::function<void()> on_hide,
bool visible_by_default) {
if (card_id.empty()) {
printf("[EditorCardManager] Warning: Attempted to register card with empty ID\n");
return;
}
// Check if already registered
if (cards_.find(card_id) != cards_.end()) {
printf("[EditorCardManager] WARNING: Card '%s' already registered, skipping duplicate\n",
card_id.c_str());
return;
}
// Create centralized visibility flag
centralized_visibility_[card_id] = visible_by_default;
// Register card with pointer to centralized flag
CardInfo info;
info.card_id = card_id;
info.display_name = display_name;
info.icon = icon;
info.category = category;
info.shortcut_hint = shortcut_hint;
info.priority = priority;
info.visibility_flag = &centralized_visibility_[card_id];
info.on_show = on_show;
info.on_hide = on_hide;
cards_[card_id] = info;
printf("[EditorCardManager] Registered card with centralized visibility: %s (%s) [default: %s]\n",
card_id.c_str(), display_name.c_str(), visible_by_default ? "visible" : "hidden");
}
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);
// Also remove centralized visibility if it exists
centralized_visibility_.erase(card_id);
}
}
@@ -56,6 +99,9 @@ bool EditorCardManager::ShowCard(const std::string& card_id) {
if (it->second.visibility_flag) {
*it->second.visibility_flag = true;
// Set active category when showing a card
SetActiveCategory(it->second.category);
if (it->second.on_show) {
it->second.on_show();
}
@@ -669,6 +715,107 @@ void EditorCardManager::LoadPresetsFromFile() {
printf("[EditorCardManager] Loading presets from file\n");
}
void EditorCardManager::SetActiveCategory(const std::string& category) {
if (active_category_ != category) {
active_category_ = category;
printf("[EditorCardManager] Active category changed to: %s\n", category.c_str());
}
}
void EditorCardManager::DrawSidebar(const std::string& category) {
// Set this category as active when sidebar is drawn
SetActiveCategory(category);
// Use ThemeManager for consistent theming
const auto& theme = ThemeManager::Get().GetCurrentTheme();
const float sidebar_width = GetSidebarWidth();
// Fixed sidebar window on the left edge of screen
ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetFrameHeight())); // Below menu bar
ImGui::SetNextWindowSize(ImVec2(sidebar_width, -1)); // Full height below menu
ImGuiWindowFlags sidebar_flags =
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse |
ImGuiWindowFlags_NoBringToFrontOnFocus;
ImGui::PushStyleColor(ImGuiCol_WindowBg, ConvertColorToImVec4(theme.child_bg));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.0f, 8.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 6.0f));
if (ImGui::Begin(absl::StrFormat("##%s_Sidebar", category).c_str(), nullptr, sidebar_flags)) {
// Get cards for this category
auto cards = GetCardsInCategory(category);
// Close All button at top
ImVec4 error_color = ConvertColorToImVec4(theme.error);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(
error_color.x * 0.6f, error_color.y * 0.6f, error_color.z * 0.6f, 0.7f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_color);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(
error_color.x * 1.2f, error_color.y * 1.2f, error_color.z * 1.2f, 1.0f));
if (ImGui::Button(ICON_MD_CLOSE, ImVec2(40.0f, 40.0f))) {
HideAllCardsInCategory(category);
}
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Close All %s Cards", category.c_str());
}
ImGui::Dummy(ImVec2(0, 4.0f));
// Draw card buttons
ImVec4 accent_color = ConvertColorToImVec4(theme.accent);
ImVec4 button_bg = ConvertColorToImVec4(theme.button);
for (const auto& card : cards) {
ImGui::PushID(card.card_id.c_str());
bool is_active = card.visibility_flag && *card.visibility_flag;
if (is_active) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(
accent_color.x, accent_color.y, accent_color.z, 0.5f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(
accent_color.x, accent_color.y, accent_color.z, 0.7f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, accent_color);
} else {
ImGui::PushStyleColor(ImGuiCol_Button, button_bg);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ConvertColorToImVec4(theme.button_hovered));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ConvertColorToImVec4(theme.button_active));
}
if (ImGui::Button(card.icon.c_str(), ImVec2(40.0f, 40.0f))) {
ToggleCard(card.card_id);
SetActiveCategory(category);
}
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered() || ImGui::IsItemActive()) {
SetActiveCategory(category);
ImGui::SetTooltip("%s\n%s", card.display_name.c_str(),
card.shortcut_hint.empty() ? "" : card.shortcut_hint.c_str());
}
ImGui::PopID();
}
}
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
}
} // namespace gui
} // namespace yaze

View File

@@ -75,6 +75,18 @@ class EditorCardManager {
// Registration
void RegisterCard(const CardInfo& info);
// Register card with centralized visibility management (preferred method)
void RegisterCard(const std::string& card_id,
const std::string& display_name,
const std::string& icon,
const std::string& category,
const std::string& shortcut_hint = "",
int priority = 50,
std::function<void()> on_show = nullptr,
std::function<void()> on_hide = nullptr,
bool visible_by_default = false);
void UnregisterCard(const std::string& card_id);
void ClearAllCards();
@@ -98,6 +110,15 @@ class EditorCardManager {
void DrawViewMenuSection(const std::string& category);
void DrawViewMenuAll(); // Draw all categories as submenus
// VSCode-style sidebar (replaces Toolset)
void DrawSidebar(const std::string& category); // Icon-only sidebar for category
static constexpr float GetSidebarWidth() { return 48.0f; }
// Active editor tracking (based on most recently interacted card)
void SetActiveCategory(const std::string& category);
std::string GetActiveCategory() const { return active_category_; }
bool IsCategoryActive(const std::string& category) const { return active_category_ == category; }
// Compact inline card control for menu bar
void DrawCompactCardControl(const std::string& category); // Shows only active editor's cards
void DrawInlineCardToggles(const std::string& category); // Minimal inline checkboxes
@@ -135,7 +156,9 @@ class EditorCardManager {
EditorCardManager& operator=(const EditorCardManager&) = delete;
std::unordered_map<std::string, CardInfo> cards_;
std::unordered_map<std::string, bool> centralized_visibility_; // Centralized card visibility flags
std::unordered_map<std::string, WorkspacePreset> presets_;
std::string active_category_; // Currently active editor category (based on last card interaction)
// Helper methods
void SavePresetsToFile();

View File

@@ -254,50 +254,7 @@ void ThemedProgressBar(float fraction, const ImVec2& size, const char* overlay)
// ============================================================================
// Palette Editor Widgets
// ============================================================================
bool PaletteColorButton(const char* label, const yaze::gfx::SnesColor& color,
bool is_selected, bool is_modified,
const ImVec2& size) {
const auto& theme = GetTheme();
int style_count = 0;
// Draw modified indicator with warning border
if (is_modified) {
ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.warning));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.0f);
style_count++;
}
// Draw selection border (overrides modified if both)
if (is_selected) {
ImGui::PushStyleColor(ImGuiCol_Border, ConvertColorToImVec4(theme.accent));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 3.0f);
if (is_modified) {
ImGui::PopStyleVar(); // Remove modified border style
ImGui::PopStyleColor(); // Remove modified border color
}
style_count = 1; // Override count
}
// Convert SNES color to ImGui format
ImVec4 col = ConvertSnesColorToImVec4(color);
// Draw color button
bool clicked = ImGui::ColorButton(label, col,
ImGuiColorEditFlags_NoAlpha |
ImGuiColorEditFlags_NoPicker |
ImGuiColorEditFlags_NoTooltip,
size);
// Cleanup styles
if (style_count > 0) {
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
return clicked;
}
// NOTE: PaletteColorButton moved to color.cc
void ColorInfoPanel(const yaze::gfx::SnesColor& color,
bool show_snes_format,

View File

@@ -181,18 +181,7 @@ void ThemedProgressBar(float fraction, const ImVec2& size = ImVec2(-1, 0),
// Palette Editor Widgets
// ============================================================================
/**
* @brief Palette color button with modified and selection indicators
* @param label Widget ID
* @param color SNES color to display
* @param is_selected Whether this color is currently selected
* @param is_modified Whether this color has unsaved changes
* @param size Button size (default 24x24)
* @return true if clicked
*/
bool PaletteColorButton(const char* label, const yaze::gfx::SnesColor& color,
bool is_selected, bool is_modified,
const ImVec2& size = ImVec2(24, 24));
// NOTE: PaletteColorButton moved to color.h for consistency with other color utilities
/**
* @brief Display color information with copy-to-clipboard functionality