refactor: Rename CompactToolbar to Toolset and Enhance Toolbar Functionality
- Renamed CompactToolbar class to Toolset for improved clarity and consistency in naming. - Introduced WidgetMeasurement for tracking widget dimensions, enabling debugging and test automation. - Updated toolbar methods to include measurement functionality, logging overflow warnings, and improved layout handling. - Removed the deprecated SettingsBar and Toolset classes to streamline the codebase. - Adjusted CMake configuration to reflect the new file structure and included widget measurement source files.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "app/gui/widget_measurement.h"
|
||||
#include "app/gui/widgets/widget_id_registry.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
@@ -14,35 +15,60 @@ namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// ============================================================================
|
||||
// CompactToolbar Implementation
|
||||
// Toolset Implementation
|
||||
// ============================================================================
|
||||
|
||||
void CompactToolbar::Begin() {
|
||||
void Toolset::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();
|
||||
// Don't use BeginGroup - it causes stretching. Just use direct layout.
|
||||
in_toolbar_ = true;
|
||||
button_count_ = 0;
|
||||
current_line_width_ = 0.0f;
|
||||
|
||||
// Begin measurement for debugging and test automation
|
||||
WidgetMeasurement::Instance().BeginToolbarMeasurement("OverworldToolbar");
|
||||
}
|
||||
|
||||
void CompactToolbar::End() {
|
||||
ImGui::EndGroup();
|
||||
void Toolset::End() {
|
||||
// End the current line
|
||||
ImGui::NewLine();
|
||||
|
||||
// End measurement and check for overflow
|
||||
WidgetMeasurement::Instance().EndToolbarMeasurement();
|
||||
|
||||
float toolbar_width = WidgetMeasurement::Instance().GetToolbarWidth("OverworldToolbar");
|
||||
float available_width = ImGui::GetContentRegionAvail().x;
|
||||
|
||||
// Log warning if toolbar overflows (for debugging)
|
||||
if (toolbar_width > available_width && toolbar_width > 0) {
|
||||
// Only log once per second to avoid spam
|
||||
static float last_warning_time = 0.0f;
|
||||
float current_time = ImGui::GetTime();
|
||||
if (current_time - last_warning_time > 1.0f) {
|
||||
ImGui::SetTooltip("⚠️ Toolbar overflow: %.0fpx / %.0fpx available",
|
||||
toolbar_width, available_width);
|
||||
last_warning_time = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::Separator();
|
||||
in_toolbar_ = false;
|
||||
current_line_width_ = 0.0f;
|
||||
}
|
||||
|
||||
void CompactToolbar::BeginModeGroup() {
|
||||
void Toolset::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) {
|
||||
bool Toolset::ModeButton(const char* icon, bool selected, const char* tooltip) {
|
||||
if (selected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor());
|
||||
}
|
||||
@@ -53,6 +79,10 @@ bool CompactToolbar::ModeButton(const char* icon, bool selected, const char* too
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// Measure this widget
|
||||
std::string widget_id = tooltip ? tooltip : absl::StrFormat("Button_%d", button_count_);
|
||||
WidgetMeasurement::Instance().MeasureLastItem(widget_id, "mode_button");
|
||||
|
||||
// Register for test automation
|
||||
if (ImGui::GetItemID() != 0 && tooltip) {
|
||||
std::string button_path = absl::StrFormat("ModeButton:%s", tooltip);
|
||||
@@ -70,25 +100,26 @@ bool CompactToolbar::ModeButton(const char* icon, bool selected, const char* too
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void CompactToolbar::EndModeGroup() {
|
||||
void Toolset::EndModeGroup() {
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
AddSeparator();
|
||||
}
|
||||
|
||||
void CompactToolbar::AddSeparator() {
|
||||
ImGui::TextDisabled("|");
|
||||
void Toolset::AddSeparator() {
|
||||
// Use a proper separator that doesn't stretch
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void CompactToolbar::AddRomBadge(uint8_t version, std::function<void()> on_upgrade) {
|
||||
void Toolset::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();
|
||||
ImGui::SameLine(0, 2); // Tighter spacing
|
||||
if (ImGui::SmallButton(ICON_MD_UPGRADE)) {
|
||||
on_upgrade();
|
||||
}
|
||||
@@ -98,12 +129,11 @@ void CompactToolbar::AddRomBadge(uint8_t version, std::function<void()> on_upgra
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
AddSeparator();
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddProperty(const char* icon, const char* label,
|
||||
uint8_t* value,
|
||||
std::function<void()> on_change) {
|
||||
bool Toolset::AddProperty(const char* icon, const char* label,
|
||||
uint8_t* value,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(55);
|
||||
@@ -117,9 +147,9 @@ bool CompactToolbar::AddProperty(const char* icon, const char* label,
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddProperty(const char* icon, const char* label,
|
||||
uint16_t* value,
|
||||
std::function<void()> on_change) {
|
||||
bool Toolset::AddProperty(const char* icon, const char* label,
|
||||
uint16_t* value,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(70);
|
||||
@@ -133,25 +163,29 @@ bool CompactToolbar::AddProperty(const char* icon, const char* label,
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddCombo(const char* icon, int* current,
|
||||
const char* const items[], int count) {
|
||||
bool Toolset::AddCombo(const char* icon, int* current,
|
||||
const char* const items[], int count) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(110);
|
||||
WidgetMeasurement::Instance().MeasureLastItem("combo_icon", "text");
|
||||
|
||||
ImGui::SameLine(0, 2); // Reduce spacing between icon and combo
|
||||
ImGui::SetNextItemWidth(100); // Slightly narrower for better fit
|
||||
|
||||
bool changed = ImGui::Combo("##combo", current, items, count);
|
||||
WidgetMeasurement::Instance().MeasureLastItem("combo_selector", "combo");
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddToggle(const char* icon, bool* state, const char* tooltip) {
|
||||
bool Toolset::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 Toolset::AddAction(const char* icon, const char* tooltip) {
|
||||
bool clicked = ImGui::SmallButton(icon);
|
||||
|
||||
// Register for test automation
|
||||
@@ -169,7 +203,7 @@ bool CompactToolbar::AddAction(const char* icon, const char* tooltip) {
|
||||
return clicked;
|
||||
}
|
||||
|
||||
bool CompactToolbar::BeginCollapsibleSection(const char* label, bool* p_open) {
|
||||
bool Toolset::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;
|
||||
@@ -177,11 +211,11 @@ bool CompactToolbar::BeginCollapsibleSection(const char* label, bool* p_open) {
|
||||
return is_open;
|
||||
}
|
||||
|
||||
void CompactToolbar::EndCollapsibleSection() {
|
||||
void Toolset::EndCollapsibleSection() {
|
||||
in_section_ = false;
|
||||
}
|
||||
|
||||
void CompactToolbar::AddV3StatusBadge(uint8_t version, std::function<void()> on_settings) {
|
||||
void Toolset::AddV3StatusBadge(uint8_t version, std::function<void()> on_settings) {
|
||||
if (version >= 3 && version != 0xFF) {
|
||||
StatusBadge("v3 Active", ButtonType::Success);
|
||||
ImGui::SameLine();
|
||||
@@ -198,7 +232,7 @@ void CompactToolbar::AddV3StatusBadge(uint8_t version, std::function<void()> on_
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
bool CompactToolbar::AddUsageStatsButton(const char* tooltip) {
|
||||
bool Toolset::AddUsageStatsButton(const char* tooltip) {
|
||||
bool clicked = ImGui::SmallButton(ICON_MD_ANALYTICS " Usage");
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @class CompactToolbar
|
||||
* @class Toolset
|
||||
* @brief Ultra-compact toolbar that merges mode buttons with settings
|
||||
*
|
||||
* Design Philosophy:
|
||||
@@ -23,9 +23,9 @@ namespace gui {
|
||||
*
|
||||
* Layout: [Mode Icons] | [ROM Badge] [World] [GFX] [Pal] [Spr] ... | [Quick Actions]
|
||||
*/
|
||||
class CompactToolbar {
|
||||
class Toolset {
|
||||
public:
|
||||
CompactToolbar() = default;
|
||||
Toolset() = default;
|
||||
|
||||
// Begin the toolbar
|
||||
void Begin();
|
||||
@@ -76,6 +76,7 @@ class CompactToolbar {
|
||||
bool in_toolbar_ = false;
|
||||
bool in_section_ = false;
|
||||
int button_count_ = 0;
|
||||
float current_line_width_ = 0.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -162,7 +163,7 @@ class EditorLayout {
|
||||
void End();
|
||||
|
||||
// Get toolbar reference
|
||||
CompactToolbar& GetToolbar() { return toolbar_; }
|
||||
Toolset& GetToolbar() { return toolbar_; }
|
||||
|
||||
// Begin main canvas area
|
||||
void BeginMainCanvas();
|
||||
@@ -172,7 +173,7 @@ class EditorLayout {
|
||||
void RegisterCard(EditorCard* card);
|
||||
|
||||
private:
|
||||
CompactToolbar toolbar_;
|
||||
Toolset toolbar_;
|
||||
std::vector<EditorCard*> cards_;
|
||||
bool in_layout_ = false;
|
||||
};
|
||||
|
||||
@@ -16,9 +16,8 @@ set(
|
||||
app/gui/widgets/widget_auto_register.cc
|
||||
app/gui/widgets/widget_state_capture.cc
|
||||
app/gui/ui_helpers.cc
|
||||
app/gui/toolset.cc
|
||||
app/gui/settings_bar.cc
|
||||
app/gui/editor_layout.cc
|
||||
app/gui/widget_measurement.cc
|
||||
# Canvas system components
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
#include "app/gui/settings_bar.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
void SettingsBar::SetRomVersion(uint8_t version) {
|
||||
rom_version_ = version;
|
||||
}
|
||||
|
||||
void SettingsBar::BeginDraw() {
|
||||
// Compact, modern settings bar
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 6));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 4));
|
||||
|
||||
// Single row layout using columns for alignment
|
||||
ImGui::BeginGroup();
|
||||
in_settings_bar_ = true;
|
||||
current_column_ = 0;
|
||||
}
|
||||
|
||||
void SettingsBar::EndDraw() {
|
||||
ImGui::EndGroup();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::Separator();
|
||||
in_settings_bar_ = false;
|
||||
}
|
||||
|
||||
void SettingsBar::AddVersionBadge(std::function<void()> on_upgrade) {
|
||||
if (IsVanilla()) {
|
||||
RomVersionBadge("Vanilla ROM", true);
|
||||
|
||||
if (on_upgrade) {
|
||||
ImGui::SameLine();
|
||||
if (IconButton(ICON_MD_UPGRADE, "Upgrade to v3", ImVec2(0, 0))) {
|
||||
on_upgrade();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::string version_text = absl::StrFormat("v%d", rom_version_);
|
||||
RomVersionBadge(version_text.c_str(), false);
|
||||
|
||||
if (rom_version_ < 3 && on_upgrade) {
|
||||
ImGui::SameLine();
|
||||
if (IconButton(ICON_MD_UPGRADE, "Upgrade to v3", ImVec2(0, 0))) {
|
||||
on_upgrade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
AddSeparator();
|
||||
}
|
||||
|
||||
void SettingsBar::AddWorldSelector(int* current_world) {
|
||||
ImGui::Text(ICON_MD_PUBLIC);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(120);
|
||||
|
||||
const char* worlds[] = {"Light World", "Dark World", "Extra World"};
|
||||
ImGui::Combo("##world", current_world, worlds, 3);
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void SettingsBar::AddHexByteInput(const char* icon, const char* label,
|
||||
uint8_t* value,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(60);
|
||||
|
||||
if (InputHexByte(label, value)) {
|
||||
if (on_change) on_change();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void SettingsBar::AddHexWordInput(const char* icon, const char* label,
|
||||
uint16_t* value,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(80);
|
||||
|
||||
if (InputHexWord(label, value)) {
|
||||
if (on_change) on_change();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void SettingsBar::AddCheckbox(const char* icon, const char* label, bool* value,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Checkbox(label, value)) {
|
||||
if (on_change) on_change();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void SettingsBar::AddCombo(const char* icon, const char* label, int* current,
|
||||
const char* const items[], int count,
|
||||
std::function<void()> on_change) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(120);
|
||||
|
||||
if (ImGui::Combo(label, current, items, count)) {
|
||||
if (on_change) on_change();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void SettingsBar::AddAreaSizeSelector(int* area_size,
|
||||
std::function<void()> on_change) {
|
||||
if (!IsV3()) return;
|
||||
|
||||
const char* sizes[] = {"Small (1x1)", "Large (2x2)", "Wide (2x1)", "Tall (1x2)"};
|
||||
|
||||
ImGui::Text(ICON_MD_ASPECT_RATIO);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(110);
|
||||
|
||||
if (ImGui::Combo("##areasize", area_size, sizes, 4)) {
|
||||
if (on_change) on_change();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void SettingsBar::AddButton(const char* icon, const char* label,
|
||||
std::function<void()> callback) {
|
||||
if (IconButton(icon, label)) {
|
||||
if (callback) callback();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void SettingsBar::AddSeparator() {
|
||||
ImGui::TextDisabled("|");
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
@@ -1,111 +0,0 @@
|
||||
#ifndef YAZE_APP_GUI_SETTINGS_BAR_H
|
||||
#define YAZE_APP_GUI_SETTINGS_BAR_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @class SettingsBar
|
||||
* @brief Modern, compact settings bar for editor properties
|
||||
*
|
||||
* A horizontal bar that consolidates mode selection, property editing,
|
||||
* and quick actions into a single, space-efficient component.
|
||||
*
|
||||
* Design Philosophy:
|
||||
* - Inline property editing with InputHex widgets
|
||||
* - Compact layout saves vertical space
|
||||
* - Visual ROM version indicator
|
||||
* - Theme-aware styling
|
||||
* - Responsive to different ROM versions
|
||||
*
|
||||
* Features:
|
||||
* - World selector
|
||||
* - Map properties (GFX, Palette, Sprites, Message ID)
|
||||
* - Version-specific properties (v2: Main Palette, v3: Animated GFX, Area Size)
|
||||
* - Visual ROM version badge
|
||||
* - Upgrade prompts for vanilla ROMs
|
||||
* - Mosaic effect toggle
|
||||
*
|
||||
* Usage:
|
||||
* ```cpp
|
||||
* SettingsBar settings;
|
||||
* settings.SetRomVersion(asm_version);
|
||||
* settings.BeginDraw();
|
||||
*
|
||||
* settings.AddWorldSelector(¤t_world);
|
||||
* settings.AddHexInput("Graphics", &map->area_graphics);
|
||||
* settings.AddHexInput("Palette", &map->area_palette);
|
||||
*
|
||||
* if (settings.IsV3()) {
|
||||
* settings.AddAreaSizeSelector(&map->area_size);
|
||||
* }
|
||||
*
|
||||
* settings.EndDraw();
|
||||
* ```
|
||||
*/
|
||||
class SettingsBar {
|
||||
public:
|
||||
SettingsBar() = default;
|
||||
|
||||
// Set ROM version to enable version-specific features
|
||||
void SetRomVersion(uint8_t version);
|
||||
|
||||
// Check ROM version
|
||||
bool IsVanilla() const { return rom_version_ == 0xFF; }
|
||||
bool IsV2() const { return rom_version_ >= 2 && rom_version_ != 0xFF; }
|
||||
bool IsV3() const { return rom_version_ >= 3 && rom_version_ != 0xFF; }
|
||||
|
||||
// Begin drawing the settings bar
|
||||
void BeginDraw();
|
||||
|
||||
// End drawing the settings bar
|
||||
void EndDraw();
|
||||
|
||||
// Add ROM version badge
|
||||
void AddVersionBadge(std::function<void()> on_upgrade = nullptr);
|
||||
|
||||
// Add world selector dropdown
|
||||
void AddWorldSelector(int* current_world);
|
||||
|
||||
// Add hex input for a property
|
||||
void AddHexByteInput(const char* icon, const char* label, uint8_t* value,
|
||||
std::function<void()> on_change = nullptr);
|
||||
void AddHexWordInput(const char* icon, const char* label, uint16_t* value,
|
||||
std::function<void()> on_change = nullptr);
|
||||
|
||||
// Add checkbox
|
||||
void AddCheckbox(const char* icon, const char* label, bool* value,
|
||||
std::function<void()> on_change = nullptr);
|
||||
|
||||
// Add combo box
|
||||
void AddCombo(const char* icon, const char* label, int* current,
|
||||
const char* const items[], int count,
|
||||
std::function<void()> on_change = nullptr);
|
||||
|
||||
// Add area size selector (v3+ only)
|
||||
void AddAreaSizeSelector(int* area_size,
|
||||
std::function<void()> on_change = nullptr);
|
||||
|
||||
// Add custom button
|
||||
void AddButton(const char* icon, const char* label,
|
||||
std::function<void()> callback);
|
||||
|
||||
// Add spacing
|
||||
void AddSeparator();
|
||||
|
||||
private:
|
||||
uint8_t rom_version_ = 0xFF;
|
||||
int current_column_ = 0;
|
||||
bool in_settings_bar_ = false;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_SETTINGS_BAR_H
|
||||
@@ -1,199 +0,0 @@
|
||||
#include "app/gui/toolset.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
void Toolset::AddTool(const std::string& id, const char* icon,
|
||||
const char* shortcut, std::function<void()> callback,
|
||||
const char* tooltip) {
|
||||
Tool tool;
|
||||
tool.id = id;
|
||||
tool.icon = icon;
|
||||
tool.shortcut = shortcut;
|
||||
tool.callback = callback;
|
||||
|
||||
if (tooltip) {
|
||||
tool.tooltip = std::string(icon) + " " + tooltip;
|
||||
if (shortcut && strlen(shortcut) > 0) {
|
||||
tool.tooltip += " (" + std::string(shortcut) + ")";
|
||||
}
|
||||
} else {
|
||||
tool.tooltip = id;
|
||||
if (shortcut && strlen(shortcut) > 0) {
|
||||
tool.tooltip += " (" + std::string(shortcut) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
tools_.push_back(tool);
|
||||
}
|
||||
|
||||
void Toolset::AddSeparator() {
|
||||
if (!tools_.empty()) {
|
||||
tools_.back().separator_after = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Toolset::SetSelected(const std::string& id) {
|
||||
selected_ = id;
|
||||
}
|
||||
|
||||
bool Toolset::Draw() {
|
||||
bool clicked = false;
|
||||
|
||||
// Calculate columns based on mode
|
||||
int columns = max_columns_;
|
||||
if (columns == 0) {
|
||||
// Auto-fit to available width
|
||||
float button_width = compact_mode_ ? 32.0f : 80.0f;
|
||||
columns = static_cast<int>(ImGui::GetContentRegionAvail().x / button_width);
|
||||
if (columns < 1) columns = 1;
|
||||
if (columns > static_cast<int>(tools_.size()))
|
||||
columns = static_cast<int>(tools_.size());
|
||||
}
|
||||
|
||||
// Modern toolset with reduced padding
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2, 2));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4));
|
||||
|
||||
if (ImGui::BeginTable("##Toolset", columns,
|
||||
ImGuiTableFlags_SizingFixedFit |
|
||||
ImGuiTableFlags_BordersInnerV)) {
|
||||
|
||||
for (const auto& tool : tools_) {
|
||||
if (!tool.enabled) continue;
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
bool is_selected = (tool.id == selected_);
|
||||
DrawTool(tool, is_selected);
|
||||
|
||||
if (tool.separator_after) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Dummy(ImVec2(1, 1)); // Small separator space
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void Toolset::DrawTool(const Tool& tool, bool is_selected) {
|
||||
ImVec2 button_size = compact_mode_ ? ImVec2(28, 28) : ImVec2(0, 0);
|
||||
|
||||
if (is_selected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor());
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(GetAccentColor().x * 1.1f, GetAccentColor().y * 1.1f,
|
||||
GetAccentColor().z * 1.1f, GetAccentColor().w));
|
||||
}
|
||||
|
||||
bool clicked = false;
|
||||
if (compact_mode_) {
|
||||
clicked = ImGui::Button(tool.icon.c_str(), button_size);
|
||||
} else {
|
||||
std::string label = tool.icon + " " + tool.id;
|
||||
clicked = ImGui::Button(label.c_str(), button_size);
|
||||
}
|
||||
|
||||
if (is_selected) {
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
if (clicked && tool.callback) {
|
||||
tool.callback();
|
||||
selected_ = tool.id;
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered() && !tool.tooltip.empty()) {
|
||||
ImGui::SetTooltip("%s", tool.tooltip.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Toolset::Clear() {
|
||||
tools_.clear();
|
||||
selected_.clear();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Editor Toolset Factories
|
||||
// ============================================================================
|
||||
|
||||
namespace EditorToolset {
|
||||
|
||||
Toolset CreateOverworldToolset() {
|
||||
Toolset toolset;
|
||||
toolset.SetCompactMode(true);
|
||||
|
||||
// Editing tools
|
||||
toolset.AddTool("Pan", ICON_MD_PAN_TOOL_ALT, "1", nullptr,
|
||||
"Pan - Middle click and drag");
|
||||
toolset.AddTool("Draw", ICON_MD_DRAW, "2", nullptr,
|
||||
"Draw Tile");
|
||||
toolset.AddTool("Entrances", ICON_MD_DOOR_FRONT, "3", nullptr,
|
||||
"Entrances");
|
||||
toolset.AddTool("Exits", ICON_MD_DOOR_BACK, "4", nullptr,
|
||||
"Exits");
|
||||
toolset.AddTool("Items", ICON_MD_GRASS, "5", nullptr,
|
||||
"Items");
|
||||
toolset.AddTool("Sprites", ICON_MD_PEST_CONTROL_RODENT, "6", nullptr,
|
||||
"Sprites");
|
||||
toolset.AddTool("Transports", ICON_MD_ADD_LOCATION, "7", nullptr,
|
||||
"Transports");
|
||||
toolset.AddTool("Music", ICON_MD_MUSIC_NOTE, "8", nullptr,
|
||||
"Music");
|
||||
|
||||
toolset.AddSeparator();
|
||||
|
||||
// View controls
|
||||
toolset.AddTool("ZoomOut", ICON_MD_ZOOM_OUT, "", nullptr,
|
||||
"Zoom Out");
|
||||
toolset.AddTool("ZoomIn", ICON_MD_ZOOM_IN, "", nullptr,
|
||||
"Zoom In");
|
||||
toolset.AddTool("Fullscreen", ICON_MD_OPEN_IN_FULL, "F11", nullptr,
|
||||
"Fullscreen Canvas");
|
||||
|
||||
toolset.AddSeparator();
|
||||
|
||||
// Quick access
|
||||
toolset.AddTool("Tile16", ICON_MD_GRID_VIEW, "Ctrl+T", nullptr,
|
||||
"Tile16 Editor");
|
||||
toolset.AddTool("Copy", ICON_MD_CONTENT_COPY, "", nullptr,
|
||||
"Copy Map");
|
||||
|
||||
return toolset;
|
||||
}
|
||||
|
||||
Toolset CreateDungeonToolset() {
|
||||
Toolset toolset;
|
||||
toolset.SetCompactMode(true);
|
||||
|
||||
toolset.AddTool("Pan", ICON_MD_PAN_TOOL_ALT, "1", nullptr,
|
||||
"Pan");
|
||||
toolset.AddTool("Draw", ICON_MD_DRAW, "2", nullptr,
|
||||
"Draw Object");
|
||||
toolset.AddTool("Select", ICON_MD_SELECT_ALL, "3", nullptr,
|
||||
"Select");
|
||||
|
||||
toolset.AddSeparator();
|
||||
|
||||
toolset.AddTool("ZoomOut", ICON_MD_ZOOM_OUT, "", nullptr,
|
||||
"Zoom Out");
|
||||
toolset.AddTool("ZoomIn", ICON_MD_ZOOM_IN, "", nullptr,
|
||||
"Zoom In");
|
||||
|
||||
return toolset;
|
||||
}
|
||||
|
||||
} // namespace EditorToolset
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
@@ -1,111 +0,0 @@
|
||||
#ifndef YAZE_APP_GUI_TOOLSET_H
|
||||
#define YAZE_APP_GUI_TOOLSET_H
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @class Toolset
|
||||
* @brief Modern, space-efficient toolset for editor mode switching
|
||||
*
|
||||
* A toolset is a horizontal bar of icon buttons for switching between
|
||||
* editor modes (Pan, Draw, Select, etc.). This provides a consistent,
|
||||
* beautiful interface across all editors.
|
||||
*
|
||||
* Features:
|
||||
* - Compact, icon-based design
|
||||
* - Visual selection indicator
|
||||
* - Keyboard shortcut hints
|
||||
* - Automatic tooltips
|
||||
* - Theme-aware styling
|
||||
* - Separators for logical grouping
|
||||
*
|
||||
* Usage:
|
||||
* ```cpp
|
||||
* Toolset toolset;
|
||||
* toolset.AddTool("Pan", ICON_MD_PAN_TOOL, "1", []() { mode = PAN; });
|
||||
* toolset.AddTool("Draw", ICON_MD_DRAW, "2", []() { mode = DRAW; });
|
||||
* toolset.AddSeparator();
|
||||
* toolset.AddTool("Zoom+", ICON_MD_ZOOM_IN, "", []() { ZoomIn(); });
|
||||
*
|
||||
* if (toolset.Draw()) {
|
||||
* // A tool was clicked
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Toolset {
|
||||
public:
|
||||
struct Tool {
|
||||
std::string id;
|
||||
std::string icon;
|
||||
std::string tooltip;
|
||||
std::string shortcut;
|
||||
std::function<void()> callback;
|
||||
bool enabled = true;
|
||||
bool separator_after = false;
|
||||
};
|
||||
|
||||
Toolset() = default;
|
||||
|
||||
// Add a tool to the toolset
|
||||
void AddTool(const std::string& id, const char* icon,
|
||||
const char* shortcut, std::function<void()> callback,
|
||||
const char* tooltip = nullptr);
|
||||
|
||||
// Add a separator for visual grouping
|
||||
void AddSeparator();
|
||||
|
||||
// Set the currently selected tool
|
||||
void SetSelected(const std::string& id);
|
||||
|
||||
// Get the currently selected tool ID
|
||||
const std::string& GetSelected() const { return selected_; }
|
||||
|
||||
// Draw the toolset (returns true if a tool was clicked)
|
||||
bool Draw();
|
||||
|
||||
// Compact mode (smaller buttons, no labels)
|
||||
void SetCompactMode(bool compact) { compact_mode_ = compact; }
|
||||
|
||||
// Set the number of columns (0 = auto-fit to window width)
|
||||
void SetMaxColumns(int columns) { max_columns_ = columns; }
|
||||
|
||||
// Clear all tools
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
void DrawTool(const Tool& tool, bool is_selected);
|
||||
|
||||
std::vector<Tool> tools_;
|
||||
std::string selected_;
|
||||
bool compact_mode_ = true;
|
||||
int max_columns_ = 0; // 0 = auto-fit
|
||||
};
|
||||
|
||||
/**
|
||||
* @namespace EditorToolset
|
||||
* @brief Helper functions for creating common editor toolsets
|
||||
*/
|
||||
namespace EditorToolset {
|
||||
|
||||
// Create standard editing mode toolset
|
||||
Toolset CreateStandardToolset();
|
||||
|
||||
// Create overworld-specific toolset
|
||||
Toolset CreateOverworldToolset();
|
||||
|
||||
// Create dungeon-specific toolset
|
||||
Toolset CreateDungeonToolset();
|
||||
|
||||
} // namespace EditorToolset
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_TOOLSET_H
|
||||
140
src/app/gui/widget_measurement.cc
Normal file
140
src/app/gui/widget_measurement.cc
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "app/gui/widget_measurement.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
WidgetMetrics WidgetMeasurement::MeasureLastItem(const std::string& widget_id,
|
||||
const std::string& type) {
|
||||
if (!enabled_) {
|
||||
return WidgetMetrics{};
|
||||
}
|
||||
|
||||
WidgetMetrics metrics;
|
||||
metrics.widget_id = widget_id;
|
||||
metrics.type = type;
|
||||
|
||||
// Get item rect (bounding box)
|
||||
metrics.rect_min = ImGui::GetItemRectMin();
|
||||
metrics.rect_max = ImGui::GetItemRectMax();
|
||||
|
||||
// Calculate size
|
||||
metrics.size = ImVec2(metrics.rect_max.x - metrics.rect_min.x,
|
||||
metrics.rect_max.y - metrics.rect_min.y);
|
||||
|
||||
// Store position
|
||||
metrics.position = metrics.rect_min;
|
||||
|
||||
// Get content region
|
||||
metrics.content_size = ImGui::GetContentRegionAvail();
|
||||
|
||||
// Get cursor position after item
|
||||
metrics.cursor_pos_x = ImGui::GetCursorPosX();
|
||||
|
||||
// Store in current toolbar if active
|
||||
if (!current_toolbar_id_.empty()) {
|
||||
toolbar_metrics_[current_toolbar_id_].push_back(metrics);
|
||||
}
|
||||
|
||||
// Store in frame metrics
|
||||
frame_metrics_.push_back(metrics);
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
void WidgetMeasurement::BeginToolbarMeasurement(const std::string& toolbar_id) {
|
||||
current_toolbar_id_ = toolbar_id;
|
||||
current_toolbar_start_x_ = ImGui::GetCursorPosX();
|
||||
current_toolbar_width_ = 0.0f;
|
||||
|
||||
// Clear previous measurements for this toolbar
|
||||
toolbar_metrics_[toolbar_id].clear();
|
||||
}
|
||||
|
||||
void WidgetMeasurement::EndToolbarMeasurement() {
|
||||
if (current_toolbar_id_.empty()) return;
|
||||
|
||||
// Calculate total width from cursor movement
|
||||
float end_x = ImGui::GetCursorPosX();
|
||||
current_toolbar_width_ = end_x - current_toolbar_start_x_;
|
||||
|
||||
// Store the width
|
||||
toolbar_widths_[current_toolbar_id_] = current_toolbar_width_;
|
||||
|
||||
// Reset state
|
||||
current_toolbar_id_.clear();
|
||||
current_toolbar_width_ = 0.0f;
|
||||
current_toolbar_start_x_ = 0.0f;
|
||||
}
|
||||
|
||||
float WidgetMeasurement::GetToolbarWidth(const std::string& toolbar_id) const {
|
||||
auto it = toolbar_widths_.find(toolbar_id);
|
||||
if (it != toolbar_widths_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
bool WidgetMeasurement::WouldToolbarOverflow(const std::string& toolbar_id,
|
||||
float available_width) const {
|
||||
float toolbar_width = GetToolbarWidth(toolbar_id);
|
||||
return toolbar_width > available_width;
|
||||
}
|
||||
|
||||
const std::vector<WidgetMetrics>& WidgetMeasurement::GetToolbarMetrics(
|
||||
const std::string& toolbar_id) const {
|
||||
static const std::vector<WidgetMetrics> empty;
|
||||
auto it = toolbar_metrics_.find(toolbar_id);
|
||||
if (it != toolbar_metrics_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
void WidgetMeasurement::ClearFrame() {
|
||||
frame_metrics_.clear();
|
||||
// Don't clear toolbar metrics - they persist across frames for debugging
|
||||
}
|
||||
|
||||
std::string WidgetMeasurement::ExportMetricsJSON() const {
|
||||
std::string json = "{\n \"toolbars\": {\n";
|
||||
|
||||
bool first_toolbar = true;
|
||||
for (const auto& [toolbar_id, metrics] : toolbar_metrics_) {
|
||||
if (!first_toolbar) json += ",\n";
|
||||
first_toolbar = false;
|
||||
|
||||
json += absl::StrFormat(" \"%s\": {\n", toolbar_id);
|
||||
json += absl::StrFormat(" \"total_width\": %.1f,\n",
|
||||
GetToolbarWidth(toolbar_id));
|
||||
json += " \"widgets\": [\n";
|
||||
|
||||
bool first_widget = true;
|
||||
for (const auto& metric : metrics) {
|
||||
if (!first_widget) json += ",\n";
|
||||
first_widget = false;
|
||||
|
||||
json += " {\n";
|
||||
json += absl::StrFormat(" \"id\": \"%s\",\n", metric.widget_id);
|
||||
json += absl::StrFormat(" \"type\": \"%s\",\n", metric.type);
|
||||
json += absl::StrFormat(" \"width\": %.1f,\n", metric.size.x);
|
||||
json += absl::StrFormat(" \"height\": %.1f,\n", metric.size.y);
|
||||
json += absl::StrFormat(" \"x\": %.1f,\n", metric.position.x);
|
||||
json += absl::StrFormat(" \"y\": %.1f\n", metric.position.y);
|
||||
json += " }";
|
||||
}
|
||||
|
||||
json += "\n ]\n";
|
||||
json += " }";
|
||||
}
|
||||
|
||||
json += "\n }\n}\n";
|
||||
return json;
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
119
src/app/gui/widget_measurement.h
Normal file
119
src/app/gui/widget_measurement.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef YAZE_APP_GUI_WIDGET_MEASUREMENT_H
|
||||
#define YAZE_APP_GUI_WIDGET_MEASUREMENT_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @class WidgetMeasurement
|
||||
* @brief Tracks widget dimensions for debugging and test automation
|
||||
*
|
||||
* Integrates with ImGui Test Engine to provide accurate measurements
|
||||
* of UI elements, helping prevent layout issues and enabling automated
|
||||
* testing of widget sizes and positions.
|
||||
*/
|
||||
struct WidgetMetrics {
|
||||
ImVec2 size; // Width and height
|
||||
ImVec2 position; // Screen position
|
||||
ImVec2 content_size; // Available content region
|
||||
ImVec2 rect_min; // Bounding box min
|
||||
ImVec2 rect_max; // Bounding box max
|
||||
float cursor_pos_x; // Cursor X after rendering
|
||||
std::string widget_id; // Widget identifier
|
||||
std::string type; // Widget type (button, input, combo, etc.)
|
||||
|
||||
std::string ToString() const {
|
||||
return absl::StrFormat(
|
||||
"Widget '%s' (%s): size=(%.1f,%.1f) pos=(%.1f,%.1f) content=(%.1f,%.1f) cursor_x=%.1f",
|
||||
widget_id, type, size.x, size.y, position.x, position.y,
|
||||
content_size.x, content_size.y, cursor_pos_x);
|
||||
}
|
||||
};
|
||||
|
||||
class WidgetMeasurement {
|
||||
public:
|
||||
static WidgetMeasurement& Instance() {
|
||||
static WidgetMeasurement instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Measure the last rendered ImGui item
|
||||
* @param widget_id Unique identifier for this widget
|
||||
* @param type Widget type (button, input, etc.)
|
||||
* @return WidgetMetrics containing all measurements
|
||||
*/
|
||||
WidgetMetrics MeasureLastItem(const std::string& widget_id,
|
||||
const std::string& type = "unknown");
|
||||
|
||||
/**
|
||||
* @brief Begin measuring a toolbar section
|
||||
*/
|
||||
void BeginToolbarMeasurement(const std::string& toolbar_id);
|
||||
|
||||
/**
|
||||
* @brief End measuring a toolbar section and store total width
|
||||
*/
|
||||
void EndToolbarMeasurement();
|
||||
|
||||
/**
|
||||
* @brief Get total measured width of a toolbar
|
||||
*/
|
||||
float GetToolbarWidth(const std::string& toolbar_id) const;
|
||||
|
||||
/**
|
||||
* @brief Check if toolbar would overflow given window width
|
||||
*/
|
||||
bool WouldToolbarOverflow(const std::string& toolbar_id,
|
||||
float available_width) const;
|
||||
|
||||
/**
|
||||
* @brief Get all measurements for a specific toolbar
|
||||
*/
|
||||
const std::vector<WidgetMetrics>& GetToolbarMetrics(
|
||||
const std::string& toolbar_id) const;
|
||||
|
||||
/**
|
||||
* @brief Clear all measurements (call once per frame)
|
||||
*/
|
||||
void ClearFrame();
|
||||
|
||||
/**
|
||||
* @brief Export measurements for test automation
|
||||
*/
|
||||
std::string ExportMetricsJSON() const;
|
||||
|
||||
/**
|
||||
* @brief Enable/disable measurement (performance option)
|
||||
*/
|
||||
void SetEnabled(bool enabled) { enabled_ = enabled; }
|
||||
bool IsEnabled() const { return enabled_; }
|
||||
|
||||
private:
|
||||
WidgetMeasurement() = default;
|
||||
|
||||
bool enabled_ = true;
|
||||
std::string current_toolbar_id_;
|
||||
float current_toolbar_width_ = 0.0f;
|
||||
float current_toolbar_start_x_ = 0.0f;
|
||||
|
||||
// Store measurements per toolbar
|
||||
std::unordered_map<std::string, std::vector<WidgetMetrics>> toolbar_metrics_;
|
||||
std::unordered_map<std::string, float> toolbar_widths_;
|
||||
|
||||
// All measurements from current frame
|
||||
std::vector<WidgetMetrics> frame_metrics_;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_WIDGET_MEASUREMENT_H
|
||||
|
||||
Reference in New Issue
Block a user