feat: Introduce CompactToolbar and EditorCard for Enhanced Editor Layout
- Added CompactToolbar class to provide a modern, ultra-compact toolbar for the editor, featuring mode buttons, property editing, and quick actions. - Implemented EditorCard class for draggable, dockable editor sub-windows, enhancing the user interface with responsive and themed cards. - Introduced EditorLayout class to manage the overall editor layout, integrating the toolbar and main canvas for a cohesive user experience. - Updated CMake configuration to include new source files, ensuring proper build integration.
This commit is contained in:
316
src/app/gui/editor_layout.cc
Normal file
316
src/app/gui/editor_layout.cc
Normal file
@@ -0,0 +1,316 @@
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#include "app/gui/editor_layout.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "app/gui/widgets/widget_id_registry.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// ============================================================================
|
||||
// CompactToolbar Implementation
|
||||
// ============================================================================
|
||||
|
||||
void CompactToolbar::Begin() {
|
||||
// Ultra-compact toolbar with no padding waste
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6, 3));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 6));
|
||||
|
||||
ImGui::BeginGroup();
|
||||
in_toolbar_ = true;
|
||||
button_count_ = 0;
|
||||
}
|
||||
|
||||
void CompactToolbar::End() {
|
||||
ImGui::EndGroup();
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::Separator();
|
||||
in_toolbar_ = false;
|
||||
}
|
||||
|
||||
void CompactToolbar::BeginModeGroup() {
|
||||
// Visual grouping with subtle background - taller for better button visibility
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.15f, 0.17f, 0.5f));
|
||||
ImGui::BeginChild("##ModeGroup", ImVec2(0, 40), true,
|
||||
ImGuiWindowFlags_NoScrollbar);
|
||||
}
|
||||
|
||||
bool CompactToolbar::ModeButton(const char* icon, bool selected, const char* tooltip) {
|
||||
if (selected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor());
|
||||
}
|
||||
|
||||
bool clicked = ImGui::Button(icon, ImVec2(32, 32)); // Larger buttons for better usability
|
||||
|
||||
if (selected) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// Register for test automation
|
||||
if (ImGui::GetItemID() != 0 && tooltip) {
|
||||
std::string button_path = absl::StrFormat("ModeButton:%s", tooltip);
|
||||
WidgetIdRegistry::Instance().RegisterWidget(
|
||||
button_path, "button", ImGui::GetItemID(), tooltip);
|
||||
}
|
||||
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
button_count_++;
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void CompactToolbar::EndModeGroup() {
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
AddSeparator();
|
||||
}
|
||||
|
||||
void CompactToolbar::AddSeparator() {
|
||||
ImGui::TextDisabled("|");
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void CompactToolbar::AddRomBadge(uint8_t version, std::function<void()> on_upgrade) {
|
||||
RomVersionBadge(version == 0xFF ? "Vanilla" :
|
||||
absl::StrFormat("v%d", version).c_str(),
|
||||
version == 0xFF);
|
||||
|
||||
if (on_upgrade && (version == 0xFF || version < 3)) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_UPGRADE)) {
|
||||
on_upgrade();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Upgrade to ZSCustomOverworld v3");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
AddSeparator();
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddProperty(const char* icon, const char* label,
|
||||
uint8_t* value,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(55);
|
||||
|
||||
bool changed = InputHexByte(label, value);
|
||||
if (changed && on_change) {
|
||||
on_change();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddProperty(const char* icon, const char* label,
|
||||
uint16_t* value,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(70);
|
||||
|
||||
bool changed = InputHexWord(label, value);
|
||||
if (changed && on_change) {
|
||||
on_change();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddCombo(const char* icon, int* current,
|
||||
const char* const items[], int count) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(110);
|
||||
|
||||
bool changed = ImGui::Combo("##combo", current, items, count);
|
||||
ImGui::SameLine();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddToggle(const char* icon, bool* state, const char* tooltip) {
|
||||
bool result = ToggleIconButton(icon, icon, state, tooltip);
|
||||
ImGui::SameLine();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddAction(const char* icon, const char* tooltip) {
|
||||
bool clicked = ImGui::SmallButton(icon);
|
||||
|
||||
// Register for test automation
|
||||
if (ImGui::GetItemID() != 0 && tooltip) {
|
||||
std::string button_path = absl::StrFormat("ToolbarAction:%s", tooltip);
|
||||
WidgetIdRegistry::Instance().RegisterWidget(
|
||||
button_path, "button", ImGui::GetItemID(), tooltip);
|
||||
}
|
||||
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
return clicked;
|
||||
}
|
||||
|
||||
bool CompactToolbar::BeginCollapsibleSection(const char* label, bool* p_open) {
|
||||
ImGui::NewLine(); // Start on new line
|
||||
bool is_open = ImGui::CollapsingHeader(label, ImGuiTreeNodeFlags_None);
|
||||
if (p_open) *p_open = is_open;
|
||||
in_section_ = is_open;
|
||||
return is_open;
|
||||
}
|
||||
|
||||
void CompactToolbar::EndCollapsibleSection() {
|
||||
in_section_ = false;
|
||||
}
|
||||
|
||||
void CompactToolbar::AddV3StatusBadge(uint8_t version, std::function<void()> on_settings) {
|
||||
if (version >= 3 && version != 0xFF) {
|
||||
StatusBadge("v3 Active", ButtonType::Success);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_TUNE " Settings") && on_settings) {
|
||||
on_settings();
|
||||
}
|
||||
} else {
|
||||
StatusBadge("v3 Available", ButtonType::Default);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_UPGRADE " Upgrade")) {
|
||||
ImGui::OpenPopup("UpgradeROMVersion");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddUsageStatsButton(const char* tooltip) {
|
||||
bool clicked = ImGui::SmallButton(ICON_MD_ANALYTICS " Usage");
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
return clicked;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// EditorCard Implementation
|
||||
// ============================================================================
|
||||
|
||||
EditorCard::EditorCard(const char* title, const char* icon)
|
||||
: title_(title), icon_(icon ? icon : ""), default_size_(400, 300) {}
|
||||
|
||||
void EditorCard::SetDefaultSize(float width, float height) {
|
||||
default_size_ = ImVec2(width, height);
|
||||
}
|
||||
|
||||
void EditorCard::SetPosition(Position pos) {
|
||||
position_ = pos;
|
||||
}
|
||||
|
||||
bool EditorCard::Begin(bool* p_open) {
|
||||
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
|
||||
|
||||
// Set initial position based on position enum
|
||||
if (first_draw_) {
|
||||
float display_width = ImGui::GetIO().DisplaySize.x;
|
||||
float display_height = ImGui::GetIO().DisplaySize.y;
|
||||
|
||||
switch (position_) {
|
||||
case Position::Right:
|
||||
ImGui::SetNextWindowPos(ImVec2(display_width - default_size_.x - 10, 30),
|
||||
ImGuiCond_FirstUseEver);
|
||||
break;
|
||||
case Position::Left:
|
||||
ImGui::SetNextWindowPos(ImVec2(10, 30), ImGuiCond_FirstUseEver);
|
||||
break;
|
||||
case Position::Bottom:
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(10, display_height - default_size_.y - 10),
|
||||
ImGuiCond_FirstUseEver);
|
||||
break;
|
||||
case Position::Floating:
|
||||
case Position::Free:
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(display_width * 0.5f - default_size_.x * 0.5f,
|
||||
display_height * 0.3f),
|
||||
ImGuiCond_FirstUseEver);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSize(default_size_, ImGuiCond_FirstUseEver);
|
||||
first_draw_ = false;
|
||||
}
|
||||
|
||||
// Create window title with icon
|
||||
std::string window_title = icon_.empty() ? title_ : icon_ + " " + title_;
|
||||
|
||||
// Modern card styling
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 8.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10, 10));
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, GetThemeColor(ImGuiCol_TitleBg));
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, GetAccentColor());
|
||||
|
||||
bool visible = ImGui::Begin(window_title.c_str(), p_open, flags);
|
||||
|
||||
// Register card window for test automation
|
||||
if (ImGui::GetCurrentWindow() && ImGui::GetCurrentWindow()->ID != 0) {
|
||||
std::string card_path = absl::StrFormat("EditorCard:%s", title_.c_str());
|
||||
WidgetIdRegistry::Instance().RegisterWidget(
|
||||
card_path, "window", ImGui::GetCurrentWindow()->ID,
|
||||
absl::StrFormat("Editor card: %s", title_.c_str()));
|
||||
}
|
||||
|
||||
return visible;
|
||||
}
|
||||
|
||||
void EditorCard::End() {
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// EditorLayout Implementation
|
||||
// ============================================================================
|
||||
|
||||
void EditorLayout::Begin() {
|
||||
toolbar_.Begin();
|
||||
in_layout_ = true;
|
||||
}
|
||||
|
||||
void EditorLayout::End() {
|
||||
toolbar_.End();
|
||||
in_layout_ = false;
|
||||
}
|
||||
|
||||
void EditorLayout::BeginMainCanvas() {
|
||||
// Main canvas takes remaining space
|
||||
ImGui::BeginChild("##MainCanvas", ImVec2(0, 0), false);
|
||||
}
|
||||
|
||||
void EditorLayout::EndMainCanvas() {
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void EditorLayout::RegisterCard(EditorCard* card) {
|
||||
cards_.push_back(card);
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
184
src/app/gui/editor_layout.h
Normal file
184
src/app/gui/editor_layout.h
Normal file
@@ -0,0 +1,184 @@
|
||||
#ifndef YAZE_APP_GUI_EDITOR_LAYOUT_H
|
||||
#define YAZE_APP_GUI_EDITOR_LAYOUT_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @class CompactToolbar
|
||||
* @brief Ultra-compact toolbar that merges mode buttons with settings
|
||||
*
|
||||
* Design Philosophy:
|
||||
* - Single horizontal bar with everything inline
|
||||
* - Small icon-only buttons for modes
|
||||
* - Inline property editing (InputHex with scroll)
|
||||
* - No wasted vertical space
|
||||
* - Beautiful, modern appearance
|
||||
*
|
||||
* Layout: [Mode Icons] | [ROM Badge] [World] [GFX] [Pal] [Spr] ... | [Quick Actions]
|
||||
*/
|
||||
class CompactToolbar {
|
||||
public:
|
||||
CompactToolbar() = default;
|
||||
|
||||
// Begin the toolbar
|
||||
void Begin();
|
||||
|
||||
// End the toolbar
|
||||
void End();
|
||||
|
||||
// Add mode button group
|
||||
void BeginModeGroup();
|
||||
bool ModeButton(const char* icon, bool selected, const char* tooltip);
|
||||
void EndModeGroup();
|
||||
|
||||
// Add separator
|
||||
void AddSeparator();
|
||||
|
||||
// Add ROM version badge
|
||||
void AddRomBadge(uint8_t version, std::function<void()> on_upgrade = nullptr);
|
||||
|
||||
// Add quick property (inline hex editing)
|
||||
bool AddProperty(const char* icon, const char* label, uint8_t* value,
|
||||
std::function<void()> on_change = nullptr);
|
||||
bool AddProperty(const char* icon, const char* label, uint16_t* value,
|
||||
std::function<void()> on_change = nullptr);
|
||||
|
||||
// Add combo selector
|
||||
bool AddCombo(const char* icon, int* current, const char* const items[], int count);
|
||||
|
||||
// Add toggle button
|
||||
bool AddToggle(const char* icon, bool* state, const char* tooltip);
|
||||
|
||||
// Add action button
|
||||
bool AddAction(const char* icon, const char* tooltip);
|
||||
|
||||
// Add collapsible settings section
|
||||
bool BeginCollapsibleSection(const char* label, bool* p_open);
|
||||
void EndCollapsibleSection();
|
||||
|
||||
// Add v3 settings indicator
|
||||
void AddV3StatusBadge(uint8_t version, std::function<void()> on_settings);
|
||||
|
||||
// Add usage statistics button
|
||||
bool AddUsageStatsButton(const char* tooltip);
|
||||
|
||||
// Get button count for widget registration
|
||||
int GetButtonCount() const { return button_count_; }
|
||||
|
||||
private:
|
||||
bool in_toolbar_ = false;
|
||||
bool in_section_ = false;
|
||||
int button_count_ = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class EditorCard
|
||||
* @brief Draggable, dockable card for editor sub-windows
|
||||
*
|
||||
* Replaces traditional child windows with modern cards that can be:
|
||||
* - Dragged and positioned freely
|
||||
* - Docked to edges (optional)
|
||||
* - Minimized to title bar
|
||||
* - Resized responsively
|
||||
* - Themed beautifully
|
||||
*
|
||||
* Usage:
|
||||
* ```cpp
|
||||
* EditorCard tile_card("Tile Selector", ICON_MD_GRID_VIEW);
|
||||
* tile_card.SetDefaultSize(300, 400);
|
||||
* tile_card.SetPosition(CardPosition::Right);
|
||||
*
|
||||
* if (tile_card.Begin()) {
|
||||
* // Draw tile selector content
|
||||
* tile_card.End();
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class EditorCard {
|
||||
public:
|
||||
enum class Position {
|
||||
Free, // Floating window
|
||||
Right, // Docked to right side
|
||||
Left, // Docked to left side
|
||||
Bottom, // Docked to bottom
|
||||
Floating, // Floating but position saved
|
||||
};
|
||||
|
||||
EditorCard(const char* title, const char* icon = nullptr);
|
||||
|
||||
// Set card properties
|
||||
void SetDefaultSize(float width, float height);
|
||||
void SetPosition(Position pos);
|
||||
void SetMinimizable(bool minimizable) { minimizable_ = minimizable; }
|
||||
void SetClosable(bool closable) { closable_ = closable; }
|
||||
|
||||
// Begin drawing the card
|
||||
bool Begin(bool* p_open = nullptr);
|
||||
|
||||
// End drawing
|
||||
void End();
|
||||
|
||||
// Minimize/maximize
|
||||
void SetMinimized(bool minimized) { minimized_ = minimized; }
|
||||
bool IsMinimized() const { return minimized_; }
|
||||
|
||||
private:
|
||||
std::string title_;
|
||||
std::string icon_;
|
||||
ImVec2 default_size_;
|
||||
Position position_ = Position::Free;
|
||||
bool minimizable_ = true;
|
||||
bool closable_ = true;
|
||||
bool minimized_ = false;
|
||||
bool first_draw_ = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class EditorLayout
|
||||
* @brief Modern layout manager for editor components
|
||||
*
|
||||
* Manages the overall editor layout with:
|
||||
* - Compact toolbar at top
|
||||
* - Main canvas in center
|
||||
* - Floating/docked cards for tools
|
||||
* - No redundant headers
|
||||
* - Responsive sizing
|
||||
*/
|
||||
class EditorLayout {
|
||||
public:
|
||||
EditorLayout() = default;
|
||||
|
||||
// Begin the editor layout
|
||||
void Begin();
|
||||
|
||||
// End the editor layout
|
||||
void End();
|
||||
|
||||
// Get toolbar reference
|
||||
CompactToolbar& GetToolbar() { return toolbar_; }
|
||||
|
||||
// Begin main canvas area
|
||||
void BeginMainCanvas();
|
||||
void EndMainCanvas();
|
||||
|
||||
// Register a card (for layout management)
|
||||
void RegisterCard(EditorCard* card);
|
||||
|
||||
private:
|
||||
CompactToolbar toolbar_;
|
||||
std::vector<EditorCard*> cards_;
|
||||
bool in_layout_ = false;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_EDITOR_LAYOUT_H
|
||||
|
||||
@@ -18,6 +18,7 @@ set(
|
||||
app/gui/ui_helpers.cc
|
||||
app/gui/toolset.cc
|
||||
app/gui/settings_bar.cc
|
||||
app/gui/editor_layout.cc
|
||||
# Canvas system components
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "app/gui/ui_helpers.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
@@ -43,21 +42,25 @@ ImVec4 GetAccentColor() {
|
||||
return ConvertColorToImVec4(theme.accent);
|
||||
}
|
||||
|
||||
// Entity/Map marker colors (normalized RGB + proper alpha)
|
||||
// Entity/Map marker colors (vibrant with good visibility)
|
||||
ImVec4 GetEntranceColor() {
|
||||
return ImVec4(1.0f, 1.0f, 0.0f, 0.39f); // Yellow, 100/255 alpha
|
||||
// Bright yellow with strong visibility
|
||||
return ImVec4(1.0f, 0.9f, 0.0f, 0.85f); // Yellow-gold, high visibility
|
||||
}
|
||||
|
||||
ImVec4 GetExitColor() {
|
||||
return ImVec4(1.0f, 1.0f, 1.0f, 0.59f); // White, 150/255 alpha
|
||||
// Bright cyan-white for contrast
|
||||
return ImVec4(0.9f, 1.0f, 1.0f, 0.85f); // Cyan-white, high visibility
|
||||
}
|
||||
|
||||
ImVec4 GetItemColor() {
|
||||
return ImVec4(1.0f, 0.0f, 0.0f, 0.59f); // Red, 150/255 alpha
|
||||
// Vibrant red for items
|
||||
return ImVec4(1.0f, 0.2f, 0.2f, 0.85f); // Bright red, high visibility
|
||||
}
|
||||
|
||||
ImVec4 GetSpriteColor() {
|
||||
return ImVec4(1.0f, 0.0f, 1.0f, 0.59f); // Magenta, 150/255 alpha
|
||||
// Bright magenta for sprites
|
||||
return ImVec4(1.0f, 0.3f, 1.0f, 0.85f); // Bright magenta, high visibility
|
||||
}
|
||||
|
||||
ImVec4 GetSelectedColor() {
|
||||
|
||||
Reference in New Issue
Block a user