feat: Introduce UI Helper Functions and Refactor Color Management
- Added ui_helpers.cc and ui_helpers.h to centralize UI-related helper functions, improving code organization and reducing boilerplate in ImGui usage. - Refactored color management in BppFormatUI to utilize new helper functions for success, warning, and error colors, enhancing theming consistency. - Updated AgentChatWidget and CanvasContextMenu to streamline UI rendering and improve maintainability by removing redundant code and enhancing functionality. - Removed canvas_utils_moved.cc and canvas_utils_moved.h to clean up the project structure, consolidating utility functions into more appropriate locations.
This commit is contained in:
@@ -1045,8 +1045,7 @@ void AgentChatWidget::Draw() {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
|
||||
ImVec2(4, 3)); // Compact padding
|
||||
|
||||
// Removed RenderAgentConfigPanel - duplicates connection header
|
||||
if (ImGui::BeginTable("##commands_and_multimodal", 2, ImGuiTableFlags_BordersInnerV)) {
|
||||
if (ImGui::BeginTable("##commands_and_multimodal", 2)) {
|
||||
ImGui::TableSetupColumn("Commands", ImGuiTableColumnFlags_WidthFixed, 180);
|
||||
ImGui::TableSetupColumn("Multimodal", ImGuiTableColumnFlags_WidthFixed, ImGui::GetContentRegionAvail().x - 180);
|
||||
ImGui::TableNextRow();
|
||||
@@ -1674,8 +1673,8 @@ void AgentChatWidget::RenderZ3EDCommandPanel() {
|
||||
|
||||
// Dense header (no collapsing)
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.14f, 0.12f, 0.18f, 0.95f));
|
||||
ImGui::BeginChild("Z3ED_CommandsChild", ImVec2(0, 90),
|
||||
true); // Reduced from 120
|
||||
ImGui::BeginChild("Z3ED_CommandsChild", ImVec2(0, 100),
|
||||
true);
|
||||
|
||||
ImGui::TextColored(command_color, ICON_MD_TERMINAL " Commands");
|
||||
ImGui::Separator();
|
||||
@@ -1755,7 +1754,7 @@ void AgentChatWidget::RenderZ3EDCommandPanel() {
|
||||
|
||||
void AgentChatWidget::RenderRomSyncPanel() {
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.14f, 0.12f, 1.0f));
|
||||
ImGui::BeginChild("RomSync", ImVec2(0, 100), true);
|
||||
ImGui::BeginChild("RomSync", ImVec2(0, 130), true);
|
||||
|
||||
ImGui::Text(ICON_MD_STORAGE " ROM State");
|
||||
ImGui::Separator();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "app/gfx/bpp_format_manager.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -58,11 +59,11 @@ bool BppFormatUI::RenderFormatSelector(gfx::Bitmap* bitmap, const gfx::SnesPalet
|
||||
|
||||
ImVec4 efficiency_color;
|
||||
if (efficiency >= 80) {
|
||||
efficiency_color = ImVec4(0, 1, 0, 1); // Green
|
||||
efficiency_color = GetSuccessColor(); // Green
|
||||
} else if (efficiency >= 60) {
|
||||
efficiency_color = ImVec4(1, 1, 0, 1); // Yellow
|
||||
efficiency_color = GetWarningColor(); // Yellow
|
||||
} else {
|
||||
efficiency_color = ImVec4(1, 0, 0, 1); // Red
|
||||
efficiency_color = GetErrorColor(); // Red
|
||||
}
|
||||
ImGui::TextColored(efficiency_color, "Quality: %s",
|
||||
efficiency >= 80 ? "Excellent" :
|
||||
@@ -140,13 +141,13 @@ void BppFormatUI::RenderAnalysisPanel(const gfx::Bitmap& bitmap, const gfx::Snes
|
||||
ImGui::Text("Format Recommendations:");
|
||||
|
||||
if (used_colors <= 4) {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), "✓ 2BPP format would be optimal");
|
||||
ImGui::TextColored(GetSuccessColor(), "✓ 2BPP format would be optimal");
|
||||
} else if (used_colors <= 8) {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), "✓ 3BPP format would be optimal");
|
||||
ImGui::TextColored(GetSuccessColor(), "✓ 3BPP format would be optimal");
|
||||
} else if (used_colors <= 16) {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), "✓ 4BPP format would be optimal");
|
||||
ImGui::TextColored(GetSuccessColor(), "✓ 4BPP format would be optimal");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1), "⚠ 8BPP format is necessary");
|
||||
ImGui::TextColored(GetWarningColor(), "⚠ 8BPP format is necessary");
|
||||
}
|
||||
|
||||
// Memory usage comparison
|
||||
@@ -257,10 +258,10 @@ void BppFormatUI::RenderSheetAnalysis(const std::vector<uint8_t>& sheet_data, in
|
||||
gfx::BppFormatManager::Get().GetFormatInfo(analysis.current_format).name.c_str());
|
||||
|
||||
if (analysis.was_converted) {
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1), "⚠ This sheet was converted");
|
||||
ImGui::TextColored(GetWarningColor(), "⚠ This sheet was converted");
|
||||
ImGui::Text("Conversion History: %s", analysis.conversion_history.c_str());
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), "✓ Original format preserved");
|
||||
ImGui::TextColored(GetSuccessColor(), "✓ Original format preserved");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -295,13 +296,13 @@ void BppFormatUI::RenderSheetAnalysis(const std::vector<uint8_t>& sheet_data, in
|
||||
ImGui::Text("Recommendations:");
|
||||
|
||||
if (analysis.was_converted && analysis.palette_entries_used <= 16) {
|
||||
ImGui::TextColored(ImVec4(0, 1, 0, 1),
|
||||
ImGui::TextColored(GetSuccessColor(),
|
||||
"✓ Consider reverting to %s format for better compression",
|
||||
gfx::BppFormatManager::Get().GetFormatInfo(analysis.original_format).name.c_str());
|
||||
}
|
||||
|
||||
if (analysis.palette_entries_used < static_cast<int>(palette.size()) / 2) {
|
||||
ImGui::TextColored(ImVec4(1, 1, 0, 1),
|
||||
ImGui::TextColored(GetWarningColor(),
|
||||
"⚠ Palette is underutilized - consider optimization");
|
||||
}
|
||||
|
||||
|
||||
@@ -405,7 +405,7 @@ void Canvas::DrawContextMenu() {
|
||||
enable_context_menu_, draggable_, config_.auto_resize, scrolling_);
|
||||
|
||||
context_menu_->Render(
|
||||
context_id_, mouse_pos, bitmap_,
|
||||
context_id_, mouse_pos, rom_, bitmap_,
|
||||
bitmap_ ? bitmap_->mutable_palette() : nullptr,
|
||||
[this](canvas::CanvasContextMenu::Command command,
|
||||
const canvas::CanvasConfig& updated_config) {
|
||||
@@ -497,187 +497,7 @@ void Canvas::DrawContextMenu() {
|
||||
return;
|
||||
}
|
||||
|
||||
static bool show_bitmap_data = false;
|
||||
if (show_bitmap_data && bitmap_ != nullptr) {
|
||||
static MemoryEditor mem_edit;
|
||||
mem_edit.DrawWindow("Bitmap Data", (void*)bitmap_->data(), bitmap_->size(),
|
||||
0);
|
||||
}
|
||||
|
||||
// Context menu (under default mouse threshold)
|
||||
if (ImVec2 drag_delta = GetMouseDragDelta(ImGuiMouseButton_Right);
|
||||
enable_context_menu_ && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
|
||||
OpenPopupOnItemClick(context_id_.c_str(), ImGuiPopupFlags_MouseButtonRight);
|
||||
|
||||
// Contents of the Context Menu
|
||||
if (ImGui::BeginPopup(context_id_.c_str())) {
|
||||
// Draw custom context menu items first
|
||||
for (const auto& item : context_menu_items_) {
|
||||
DrawContextMenuItem(item);
|
||||
}
|
||||
|
||||
// Add separator if there are custom items
|
||||
if (!context_menu_items_.empty()) {
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
// Default canvas menu items
|
||||
if (MenuItem("Reset View", nullptr, false)) {
|
||||
ResetView();
|
||||
}
|
||||
if (MenuItem("Zoom to Fit", nullptr, false) && bitmap_) {
|
||||
SetZoomToFit(*bitmap_);
|
||||
}
|
||||
if (MenuItem("Advanced Properties", nullptr, false)) {
|
||||
ImGui::OpenPopup("Advanced Canvas Properties");
|
||||
}
|
||||
ImGui::Separator();
|
||||
MenuItem("Show Grid", nullptr, &enable_grid_);
|
||||
Selectable("Show Position Labels", &enable_hex_tile_labels_);
|
||||
if (MenuItem("Edit Palette", nullptr, false) && bitmap_) {
|
||||
ShowPaletteEditor();
|
||||
}
|
||||
if (MenuItem("Color Analysis", nullptr, false) && bitmap_) {
|
||||
ShowColorAnalysis();
|
||||
}
|
||||
if (MenuItem("Scaling Controls", nullptr, false)) {
|
||||
ImGui::OpenPopup("Scaling Controls");
|
||||
}
|
||||
if (BeginMenu("Canvas Properties")) {
|
||||
Text("Canvas Size: %.0f x %.0f", canvas_sz_.x, canvas_sz_.y);
|
||||
Text("Global Scale: %.1f", global_scale_);
|
||||
Text("Mouse Position: %.0f x %.0f", mouse_pos.x, mouse_pos.y);
|
||||
EndMenu();
|
||||
}
|
||||
if (bitmap_ != nullptr) {
|
||||
if (BeginMenu("Bitmap Properties")) {
|
||||
Text("Size: %.0f x %.0f", scaled_sz.x, scaled_sz.y);
|
||||
Text("Pitch: %d", bitmap_->surface()->pitch);
|
||||
Text("BitsPerPixel: %d", bitmap_->surface()->format->BitsPerPixel);
|
||||
Text("BytesPerPixel: %d", bitmap_->surface()->format->BytesPerPixel);
|
||||
MenuItem("Data", nullptr, &show_bitmap_data);
|
||||
if (BeginMenu("Format")) {
|
||||
if (MenuItem("Indexed")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::kIndexed);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
if (MenuItem("4BPP")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::k4bpp);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
if (MenuItem("8BPP")) {
|
||||
bitmap_->Reformat(gfx::BitmapFormat::k8bpp);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
}
|
||||
|
||||
EndMenu();
|
||||
}
|
||||
if (BeginMenu("ROM Palette Selection") && rom_) {
|
||||
Text("Select ROM Palette Group:");
|
||||
|
||||
// Enhanced ROM palette group selection
|
||||
if (palette_editor_) {
|
||||
// Use our enhanced palette editor's ROM selection
|
||||
if (MenuItem("Open Enhanced Palette Manager")) {
|
||||
palette_editor_->ShowROMPaletteManager();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Quick palette group selection
|
||||
const char* palette_groups[] = {
|
||||
"Overworld Main", "Overworld Aux", "Overworld Animated",
|
||||
"Dungeon Main", "Global Sprites", "Armor",
|
||||
"Swords"};
|
||||
|
||||
if (ImGui::Combo("Quick Palette Group",
|
||||
(int*)&edit_palette_group_name_index_,
|
||||
palette_groups, IM_ARRAYSIZE(palette_groups))) {
|
||||
// Group selection changed
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
if (ImGui::SliderInt("Palette Index", (int*)&edit_palette_index_, 0,
|
||||
7)) {
|
||||
// Palette index changed
|
||||
}
|
||||
|
||||
// Apply button with enhanced functionality
|
||||
if (ImGui::Button("Apply to Canvas") && bitmap_) {
|
||||
if (palette_editor_->ApplyROMPalette(
|
||||
bitmap_, edit_palette_group_name_index_,
|
||||
edit_palette_index_)) {
|
||||
// LOG_INFO(
|
||||
// "Applied ROM palette group %d, index %d via context menu",
|
||||
// edit_palette_group_name_index_, edit_palette_index_);
|
||||
}
|
||||
}
|
||||
|
||||
// Direct palette editing with SelectablePalettePipeline
|
||||
if (ImGui::TreeNode("Interactive Palette Editor")) {
|
||||
if (rom_ && bitmap_) {
|
||||
ImGui::Text("Interactive ROM Palette Editing");
|
||||
ImGui::Text("Selected Group: %s",
|
||||
palette_groups[edit_palette_group_name_index_]);
|
||||
|
||||
// Get the enhanced palette editor's ROM palette if available
|
||||
if (const auto* rom_palette =
|
||||
palette_editor_->GetSelectedROMPalette()) {
|
||||
auto editable_palette =
|
||||
const_cast<gfx::SnesPalette&>(*rom_palette);
|
||||
|
||||
if (ImGui::BeginChild("SelectablePalette", ImVec2(0, 200),
|
||||
true)) {
|
||||
// Use the existing SelectablePalettePipeline for interactive editing
|
||||
gui::SelectablePalettePipeline(edit_palette_sub_index_,
|
||||
refresh_graphics_,
|
||||
editable_palette);
|
||||
|
||||
if (refresh_graphics_) {
|
||||
bitmap_->SetPaletteWithTransparent(
|
||||
editable_palette, edit_palette_sub_index_);
|
||||
Renderer::Get().UpdateBitmap(bitmap_);
|
||||
refresh_graphics_ = false;
|
||||
// LOG_INFO is undefined or missing required arguments; removed or fix as needed.
|
||||
}
|
||||
ImGui::Text(
|
||||
"Load ROM palettes first using Enhanced Palette "
|
||||
"Manager");
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
EndMenu();
|
||||
}
|
||||
if (BeginMenu("View Palette")) {
|
||||
(void)DisplayEditablePalette(*bitmap_->mutable_palette(), "Palette",
|
||||
true, 8);
|
||||
EndMenu();
|
||||
}
|
||||
EndMenu();
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (BeginMenu("Grid Tile Size")) {
|
||||
if (MenuItem("8x8", nullptr, custom_step_ == 8.0f)) {
|
||||
custom_step_ = 8.0f;
|
||||
}
|
||||
if (MenuItem("16x16", nullptr, custom_step_ == 16.0f)) {
|
||||
custom_step_ = 16.0f;
|
||||
}
|
||||
if (MenuItem("32x32", nullptr, custom_step_ == 32.0f)) {
|
||||
custom_step_ = 32.0f;
|
||||
}
|
||||
if (MenuItem("64x64", nullptr, custom_step_ == 64.0f)) {
|
||||
custom_step_ = 64.0f;
|
||||
}
|
||||
EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Draw enhanced property dialogs
|
||||
ShowAdvancedCanvasProperties();
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gfx/performance_dashboard.h"
|
||||
#include "app/gui/widgets/palette_widget.h"
|
||||
#include "app/gui/bpp_format_ui.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/canvas/canvas_modals.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
@@ -30,6 +32,7 @@ void CanvasContextMenu::Initialize(const std::string& canvas_id) {
|
||||
canvas_id_ = canvas_id;
|
||||
enabled_ = true;
|
||||
current_usage_ = CanvasUsage::kTilePainting;
|
||||
palette_editor_ = std::make_unique<PaletteWidget>();
|
||||
|
||||
// Initialize canvas state
|
||||
canvas_size_ = ImVec2(0, 0);
|
||||
@@ -65,11 +68,10 @@ void CanvasContextMenu::ClearMenuItems() {
|
||||
usage_specific_items_.clear();
|
||||
}
|
||||
|
||||
void CanvasContextMenu::Render(
|
||||
const std::string& context_id, const ImVec2& mouse_pos,
|
||||
const gfx::Bitmap* bitmap, const gfx::SnesPalette* palette,
|
||||
const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||
CanvasConfig current_config) {
|
||||
void CanvasContextMenu::Render(const std::string& context_id, const ImVec2& mouse_pos, Rom* rom,
|
||||
const gfx::Bitmap* bitmap, const gfx::SnesPalette* palette,
|
||||
const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||
CanvasConfig current_config) {
|
||||
if (!enabled_) return;
|
||||
|
||||
// Context menu (under default mouse threshold)
|
||||
@@ -98,13 +100,13 @@ void CanvasContextMenu::Render(
|
||||
|
||||
// Render bitmap operations if bitmap is available
|
||||
if (bitmap) {
|
||||
RenderBitmapOperationsMenu(bitmap);
|
||||
RenderBitmapOperationsMenu(const_cast<gfx::Bitmap*>(bitmap));
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
// Render palette operations if palette is available
|
||||
if (palette) {
|
||||
RenderPaletteOperationsMenu(palette);
|
||||
// Render palette operations if bitmap is available
|
||||
if (bitmap) {
|
||||
RenderPaletteOperationsMenu(rom, const_cast<gfx::Bitmap*>(bitmap));
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
@@ -151,11 +153,11 @@ void CanvasContextMenu::SetCanvasState(const ImVec2& canvas_size,
|
||||
global_scale_ = global_scale;
|
||||
grid_step_ = grid_step;
|
||||
enable_grid_ = enable_grid;
|
||||
enable_hex_labels_ = enable_hex_labels;
|
||||
enable_custom_labels_ = enable_custom_labels;
|
||||
enable_context_menu_ = enable_context_menu;
|
||||
is_draggable_ = is_draggable;
|
||||
auto_resize_ = auto_resize;
|
||||
enable_hex_labels_ = enable_hex_labels_;
|
||||
enable_custom_labels_ = enable_custom_labels_;
|
||||
enable_context_menu_ = enable_context_menu_;
|
||||
is_draggable_ = is_draggable_;
|
||||
auto_resize_ = auto_resize_;
|
||||
scrolling_ = scrolling;
|
||||
}
|
||||
|
||||
@@ -289,43 +291,74 @@ void CanvasContextMenu::RenderCanvasPropertiesMenu(
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContextMenu::RenderBitmapOperationsMenu(const gfx::Bitmap* bitmap) {
|
||||
if (ImGui::BeginMenu(ICON_MD_IMAGE " Bitmap Operations")) {
|
||||
void CanvasContextMenu::RenderBitmapOperationsMenu(gfx::Bitmap* bitmap) {
|
||||
if (!bitmap) return;
|
||||
|
||||
if (ImGui::BeginMenu(ICON_MD_IMAGE " Bitmap Properties")) {
|
||||
ImGui::Text("Size: %d x %d", bitmap->width(), bitmap->height());
|
||||
ImGui::Text("Format: %s", "Unknown"); // Would need format detection
|
||||
|
||||
if (ImGui::MenuItem("Edit Bitmap Data...")) {
|
||||
// Open bitmap data editor
|
||||
ImGui::Text("Pitch: %d", bitmap->surface()->pitch);
|
||||
ImGui::Text("BitsPerPixel: %d", bitmap->surface()->format->BitsPerPixel);
|
||||
ImGui::Text("BytesPerPixel: %d", bitmap->surface()->format->BytesPerPixel);
|
||||
|
||||
if (ImGui::BeginMenu("Format")) {
|
||||
if (ImGui::MenuItem("Indexed")) {
|
||||
bitmap->Reformat(gfx::BitmapFormat::kIndexed);
|
||||
core::Renderer::Get().UpdateBitmap(bitmap);
|
||||
}
|
||||
if (ImGui::MenuItem("4BPP")) {
|
||||
bitmap->Reformat(gfx::BitmapFormat::k4bpp);
|
||||
core::Renderer::Get().UpdateBitmap(bitmap);
|
||||
}
|
||||
if (ImGui::MenuItem("8BPP")) {
|
||||
bitmap->Reformat(gfx::BitmapFormat::k8bpp);
|
||||
core::Renderer::Get().UpdateBitmap(bitmap);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem("Export Bitmap...")) {
|
||||
// Export bitmap
|
||||
}
|
||||
if (ImGui::MenuItem("Import Bitmap...")) {
|
||||
// Import bitmap
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContextMenu::RenderPaletteOperationsMenu(const gfx::SnesPalette* palette) {
|
||||
void CanvasContextMenu::RenderPaletteOperationsMenu(Rom* rom, gfx::Bitmap* bitmap) {
|
||||
if (!bitmap) return;
|
||||
|
||||
if (ImGui::BeginMenu(ICON_MD_PALETTE " Palette Operations")) {
|
||||
ImGui::Text("Colors: %zu", palette->size());
|
||||
|
||||
if (ImGui::MenuItem("Edit Palette...")) {
|
||||
// Open palette editor
|
||||
palette_editor_->ShowPaletteEditor(*bitmap->mutable_palette(), "Palette Editor");
|
||||
}
|
||||
if (ImGui::MenuItem("Color Analysis...")) {
|
||||
// Open color analysis
|
||||
palette_editor_->ShowColorAnalysis(*bitmap, "Color Analysis");
|
||||
}
|
||||
if (ImGui::MenuItem("Apply ROM Palette...")) {
|
||||
// Apply ROM palette
|
||||
|
||||
if (rom && ImGui::BeginMenu("ROM Palette Selection")) {
|
||||
palette_editor_->Initialize(rom);
|
||||
|
||||
// Render palette selector inline
|
||||
ImGui::Text("Group:"); ImGui::SameLine();
|
||||
ImGui::InputScalar("##group", ImGuiDataType_U64, &edit_palette_group_name_index_);
|
||||
ImGui::Text("Palette:"); ImGui::SameLine();
|
||||
ImGui::InputScalar("##palette", ImGuiDataType_U64, &edit_palette_index_);
|
||||
|
||||
if (ImGui::Button("Apply to Canvas")) {
|
||||
palette_editor_->ApplyROMPalette(bitmap, edit_palette_group_name_index_, edit_palette_index_);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View Palette")) {
|
||||
DisplayEditablePalette(*bitmap->mutable_palette(), "Palette", true, 8);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContextMenu::DrawROMPaletteSelector() {
|
||||
if (!palette_editor_) return;
|
||||
|
||||
palette_editor_->DrawROMPaletteSelector();
|
||||
}
|
||||
|
||||
void CanvasContextMenu::RenderBppOperationsMenu(const gfx::Bitmap* bitmap) {
|
||||
if (ImGui::BeginMenu(ICON_MD_SWAP_HORIZ " BPP Operations")) {
|
||||
if (ImGui::MenuItem("Format Analysis...")) {
|
||||
@@ -506,31 +539,31 @@ void CanvasContextMenu::CreateDefaultMenuItems() {
|
||||
usage_specific_items_[CanvasUsage::kPerformanceMode].push_back(perf_item);
|
||||
}
|
||||
|
||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateViewMenuItem(const std::string& label,
|
||||
canvas::CanvasContextMenu::ContextMenuItem canvas::CanvasContextMenu::CreateViewMenuItem(const std::string& label,
|
||||
const std::string& icon,
|
||||
std::function<void()> callback) {
|
||||
return ContextMenuItem(label, icon, callback);
|
||||
}
|
||||
|
||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateBitmapMenuItem(const std::string& label,
|
||||
canvas::CanvasContextMenu::ContextMenuItem canvas::CanvasContextMenu::CreateBitmapMenuItem(const std::string& label,
|
||||
const std::string& icon,
|
||||
std::function<void()> callback) {
|
||||
return ContextMenuItem(label, icon, callback);
|
||||
}
|
||||
|
||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreatePaletteMenuItem(const std::string& label,
|
||||
canvas::CanvasContextMenu::ContextMenuItem canvas::CanvasContextMenu::CreatePaletteMenuItem(const std::string& label,
|
||||
const std::string& icon,
|
||||
std::function<void()> callback) {
|
||||
return ContextMenuItem(label, icon, callback);
|
||||
}
|
||||
|
||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreateBppMenuItem(const std::string& label,
|
||||
canvas::CanvasContextMenu::ContextMenuItem canvas::CanvasContextMenu::CreateBppMenuItem(const std::string& label,
|
||||
const std::string& icon,
|
||||
std::function<void()> callback) {
|
||||
return ContextMenuItem(label, icon, callback);
|
||||
}
|
||||
|
||||
CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreatePerformanceMenuItem(const std::string& label,
|
||||
canvas::CanvasContextMenu::ContextMenuItem canvas::CanvasContextMenu::CreatePerformanceMenuItem(const std::string& label,
|
||||
const std::string& icon,
|
||||
std::function<void()> callback) {
|
||||
return ContextMenuItem(label, icon, callback);
|
||||
@@ -538,4 +571,4 @@ CanvasContextMenu::ContextMenuItem CanvasContextMenu::CreatePerformanceMenuItem(
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
} // namespace yaze
|
||||
@@ -15,6 +15,10 @@
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// Forward declarations
|
||||
class PaletteWidget;
|
||||
|
||||
namespace canvas {
|
||||
|
||||
class CanvasContextMenu {
|
||||
@@ -64,6 +68,7 @@ class CanvasContextMenu {
|
||||
|
||||
void Render(const std::string& context_id,
|
||||
const ImVec2& mouse_pos,
|
||||
Rom* rom,
|
||||
const gfx::Bitmap* bitmap,
|
||||
const gfx::SnesPalette* palette,
|
||||
const std::function<void(Command, const canvas::CanvasConfig&)>& command_handler,
|
||||
@@ -103,6 +108,14 @@ class CanvasContextMenu {
|
||||
bool auto_resize_ = false;
|
||||
ImVec2 scrolling_;
|
||||
|
||||
std::unique_ptr<PaletteWidget> palette_editor_;
|
||||
uint64_t edit_palette_group_name_index_ = 0;
|
||||
uint64_t edit_palette_index_ = 0;
|
||||
uint64_t edit_palette_sub_index_ = 0;
|
||||
bool refresh_graphics_ = false;
|
||||
|
||||
void DrawROMPaletteSelector();
|
||||
|
||||
std::unordered_map<CanvasUsage, std::vector<ContextMenuItem>> usage_specific_items_;
|
||||
std::vector<ContextMenuItem> global_items_;
|
||||
|
||||
@@ -114,8 +127,8 @@ class CanvasContextMenu {
|
||||
CanvasConfig current_config);
|
||||
void RenderCanvasPropertiesMenu(const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||
CanvasConfig current_config);
|
||||
void RenderBitmapOperationsMenu(const gfx::Bitmap* bitmap);
|
||||
void RenderPaletteOperationsMenu(const gfx::SnesPalette* palette);
|
||||
void RenderBitmapOperationsMenu(gfx::Bitmap* bitmap);
|
||||
void RenderPaletteOperationsMenu(Rom* rom, gfx::Bitmap* bitmap);
|
||||
void RenderBppOperationsMenu(const gfx::Bitmap* bitmap);
|
||||
void RenderPerformanceMenu();
|
||||
void RenderGridControlsMenu(const std::function<void(Command, const CanvasConfig&)>& command_handler,
|
||||
|
||||
@@ -1,398 +0,0 @@
|
||||
#include "canvas_utils_moved.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
namespace canvas {
|
||||
namespace CanvasUtils {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
ImVec2 AlignToGrid(ImVec2 pos, float grid_step) {
|
||||
return ImVec2(std::floor(pos.x / grid_step) * grid_step,
|
||||
std::floor(pos.y / grid_step) * grid_step);
|
||||
}
|
||||
|
||||
float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size,
|
||||
float global_scale) {
|
||||
if (content_size.x <= 0 || content_size.y <= 0)
|
||||
return global_scale;
|
||||
|
||||
float scale_x = (canvas_size.x * global_scale) / content_size.x;
|
||||
float scale_y = (canvas_size.y * global_scale) / content_size.y;
|
||||
return std::min(scale_x, scale_y);
|
||||
}
|
||||
|
||||
int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale,
|
||||
int tiles_per_row) {
|
||||
float scaled_tile_size = tile_size * scale;
|
||||
int tile_x = static_cast<int>(mouse_pos.x / scaled_tile_size);
|
||||
int tile_y = static_cast<int>(mouse_pos.y / scaled_tile_size);
|
||||
|
||||
return tile_x + (tile_y * tiles_per_row);
|
||||
}
|
||||
|
||||
bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
|
||||
if (!rom || palette_manager.palettes_loaded) {
|
||||
return palette_manager.palettes_loaded;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto& palette_groups = rom->palette_group();
|
||||
palette_manager.rom_palette_groups.clear();
|
||||
palette_manager.palette_group_names.clear();
|
||||
|
||||
// Overworld palettes
|
||||
if (palette_groups.overworld_main.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.overworld_main[0]);
|
||||
palette_manager.palette_group_names.push_back("Overworld Main");
|
||||
}
|
||||
if (palette_groups.overworld_aux.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.overworld_aux[0]);
|
||||
palette_manager.palette_group_names.push_back("Overworld Aux");
|
||||
}
|
||||
if (palette_groups.overworld_animated.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.overworld_animated[0]);
|
||||
palette_manager.palette_group_names.push_back("Overworld Animated");
|
||||
}
|
||||
|
||||
// Dungeon palettes
|
||||
if (palette_groups.dungeon_main.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.dungeon_main[0]);
|
||||
palette_manager.palette_group_names.push_back("Dungeon Main");
|
||||
}
|
||||
|
||||
// Sprite palettes
|
||||
if (palette_groups.global_sprites.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(
|
||||
palette_groups.global_sprites[0]);
|
||||
palette_manager.palette_group_names.push_back("Global Sprites");
|
||||
}
|
||||
if (palette_groups.armors.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.armors[0]);
|
||||
palette_manager.palette_group_names.push_back("Armor");
|
||||
}
|
||||
if (palette_groups.swords.size() > 0) {
|
||||
palette_manager.rom_palette_groups.push_back(palette_groups.swords[0]);
|
||||
palette_manager.palette_group_names.push_back("Swords");
|
||||
}
|
||||
|
||||
palette_manager.palettes_loaded = true;
|
||||
LOG_INFO("Canvas", "Loaded %zu ROM palette groups",
|
||||
palette_manager.rom_palette_groups.size());
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("Canvas", "Failed to load ROM palette groups");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplyPaletteGroup(gfx::Bitmap* bitmap,
|
||||
const CanvasPaletteManager& palette_manager,
|
||||
int group_index, int palette_index) {
|
||||
if (!bitmap || group_index < 0 ||
|
||||
group_index >=
|
||||
static_cast<int>(palette_manager.rom_palette_groups.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto& selected_palette =
|
||||
palette_manager.rom_palette_groups[group_index];
|
||||
|
||||
// Apply the palette based on the index
|
||||
if (palette_index >= 0 && palette_index < 8) {
|
||||
bitmap->SetPaletteWithTransparent(selected_palette, palette_index);
|
||||
} else {
|
||||
bitmap->SetPalette(selected_palette);
|
||||
}
|
||||
|
||||
Renderer::Get().UpdateBitmap(bitmap);
|
||||
LOG_INFO("Canvas", "Applied palette group %d, index %d to bitmap",
|
||||
group_index, palette_index);
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("Canvas", "Failed to apply palette");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Drawing utility functions
|
||||
void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, ImVec4 color,
|
||||
float global_scale) {
|
||||
// Apply global scale to position and size
|
||||
float scaled_x = x * global_scale;
|
||||
float scaled_y = y * global_scale;
|
||||
float scaled_w = w * global_scale;
|
||||
float scaled_h = h * global_scale;
|
||||
|
||||
ImVec2 origin(canvas_p0.x + scrolling.x + scaled_x,
|
||||
canvas_p0.y + scrolling.y + scaled_y);
|
||||
ImVec2 size(canvas_p0.x + scrolling.x + scaled_x + scaled_w,
|
||||
canvas_p0.y + scrolling.y + scaled_y + scaled_h);
|
||||
|
||||
uint32_t color_u32 = IM_COL32(color.x, color.y, color.z, color.w);
|
||||
draw_list->AddRectFilled(origin, size, color_u32);
|
||||
|
||||
// Add a black outline
|
||||
ImVec2 outline_origin(origin.x - 1, origin.y - 1);
|
||||
ImVec2 outline_size(size.x + 1, size.y + 1);
|
||||
draw_list->AddRect(outline_origin, outline_size, IM_COL32(0, 0, 0, 255));
|
||||
}
|
||||
|
||||
void DrawCanvasText(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
const std::string& text, int x, int y, float global_scale) {
|
||||
// Apply global scale to text position
|
||||
float scaled_x = x * global_scale;
|
||||
float scaled_y = y * global_scale;
|
||||
|
||||
ImVec2 text_pos(canvas_p0.x + scrolling.x + scaled_x,
|
||||
canvas_p0.y + scrolling.y + scaled_y);
|
||||
|
||||
// Draw text with black shadow for better visibility
|
||||
draw_list->AddText(ImVec2(text_pos.x + 1, text_pos.y + 1),
|
||||
IM_COL32(0, 0, 0, 255), text.c_str());
|
||||
draw_list->AddText(text_pos, IM_COL32(255, 255, 255, 255), text.c_str());
|
||||
}
|
||||
|
||||
void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, int x, int y, int w, int h,
|
||||
uint32_t color) {
|
||||
ImVec2 origin(canvas_p0.x + scrolling.x + x, canvas_p0.y + scrolling.y + y);
|
||||
ImVec2 size(canvas_p0.x + scrolling.x + x + w,
|
||||
canvas_p0.y + scrolling.y + y + h);
|
||||
draw_list->AddRect(origin, size, color, 0, 0, 1.5f);
|
||||
}
|
||||
|
||||
void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, int x, int y, int w, int h,
|
||||
ImVec4 color) {
|
||||
uint32_t color_u32 =
|
||||
IM_COL32(color.x * 255, color.y * 255, color.z * 255, color.w * 255);
|
||||
DrawCanvasOutline(draw_list, canvas_p0, scrolling, x, y, w, h, color_u32);
|
||||
}
|
||||
|
||||
// Grid utility functions
|
||||
void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 canvas_p1, ImVec2 scrolling, float grid_step,
|
||||
float global_scale) {
|
||||
const uint32_t grid_color = IM_COL32(200, 200, 200, 50);
|
||||
const float grid_thickness = 0.5f;
|
||||
|
||||
float scaled_grid_step = grid_step * global_scale;
|
||||
|
||||
for (float x = fmodf(scrolling.x, scaled_grid_step);
|
||||
x < (canvas_p1.x - canvas_p0.x); x += scaled_grid_step) {
|
||||
draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y),
|
||||
ImVec2(canvas_p0.x + x, canvas_p1.y), grid_color,
|
||||
grid_thickness);
|
||||
}
|
||||
|
||||
for (float y = fmodf(scrolling.y, scaled_grid_step);
|
||||
y < (canvas_p1.y - canvas_p0.y); y += scaled_grid_step) {
|
||||
draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y),
|
||||
ImVec2(canvas_p1.x, canvas_p0.y + y), grid_color,
|
||||
grid_thickness);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, int highlight_tile_id,
|
||||
float grid_step) {
|
||||
if (highlight_tile_id == -1)
|
||||
return;
|
||||
|
||||
int tile_x = highlight_tile_id % 8;
|
||||
int tile_y = highlight_tile_id / 8;
|
||||
ImVec2 tile_pos(canvas_p0.x + scrolling.x + tile_x * grid_step,
|
||||
canvas_p0.y + scrolling.y + tile_y * grid_step);
|
||||
ImVec2 tile_pos_end(tile_pos.x + grid_step, tile_pos.y + grid_step);
|
||||
|
||||
draw_list->AddRectFilled(tile_pos, tile_pos_end, IM_COL32(255, 0, 255, 255));
|
||||
}
|
||||
|
||||
void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0,
|
||||
ImVec2 scrolling, ImVec2 canvas_sz, float grid_step,
|
||||
float global_scale) {
|
||||
float scaled_grid_step = grid_step * global_scale;
|
||||
|
||||
for (float x = fmodf(scrolling.x, scaled_grid_step);
|
||||
x < canvas_sz.x * global_scale; x += scaled_grid_step) {
|
||||
for (float y = fmodf(scrolling.y, scaled_grid_step);
|
||||
y < canvas_sz.y * global_scale; y += scaled_grid_step) {
|
||||
int tile_x = (x - scrolling.x) / scaled_grid_step;
|
||||
int tile_y = (y - scrolling.y) / scaled_grid_step;
|
||||
int tile_id = tile_x + (tile_y * 16);
|
||||
|
||||
char hex_id[8];
|
||||
snprintf(hex_id, sizeof(hex_id), "%02X", tile_id);
|
||||
|
||||
draw_list->AddText(ImVec2(canvas_p0.x + x + (scaled_grid_step / 2) - 4,
|
||||
canvas_p0.y + y + (scaled_grid_step / 2) - 4),
|
||||
IM_COL32(255, 255, 255, 255), hex_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Layout and interaction utilities
|
||||
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size,
|
||||
bool use_custom) {
|
||||
return use_custom ? custom_size : content_region;
|
||||
}
|
||||
|
||||
ImVec2 CalculateScaledCanvasSize(ImVec2 canvas_size, float global_scale) {
|
||||
return ImVec2(canvas_size.x * global_scale, canvas_size.y * global_scale);
|
||||
}
|
||||
|
||||
bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1) {
|
||||
return point.x >= canvas_p0.x && point.x <= canvas_p1.x &&
|
||||
point.y >= canvas_p0.y && point.y <= canvas_p1.y;
|
||||
}
|
||||
|
||||
// Size reporting for ImGui table integration
|
||||
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale,
|
||||
float padding) {
|
||||
// Calculate minimum size needed to display content with padding
|
||||
ImVec2 min_size = ImVec2(content_size.x * global_scale + padding * 2,
|
||||
content_size.y * global_scale + padding * 2);
|
||||
|
||||
// Ensure minimum practical size
|
||||
min_size.x = std::max(min_size.x, 64.0f);
|
||||
min_size.y = std::max(min_size.y, 64.0f);
|
||||
|
||||
return min_size;
|
||||
}
|
||||
|
||||
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale,
|
||||
float min_scale) {
|
||||
// Calculate preferred size with minimum scale constraint
|
||||
float effective_scale = std::max(global_scale, min_scale);
|
||||
ImVec2 preferred_size = ImVec2(content_size.x * effective_scale + 8.0f,
|
||||
content_size.y * effective_scale + 8.0f);
|
||||
|
||||
// Cap to reasonable maximum sizes for table integration
|
||||
preferred_size.x = std::min(preferred_size.x, 800.0f);
|
||||
preferred_size.y = std::min(preferred_size.y, 600.0f);
|
||||
|
||||
return preferred_size;
|
||||
}
|
||||
|
||||
void ReserveCanvasSpace(ImVec2 canvas_size, const std::string& label) {
|
||||
// Reserve space in ImGui layout so tables know the size
|
||||
if (!label.empty()) {
|
||||
ImGui::Text("%s", label.c_str());
|
||||
}
|
||||
ImGui::Dummy(canvas_size);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() -
|
||||
canvas_size.x); // Move back to start
|
||||
}
|
||||
|
||||
void SetNextCanvasSize(ImVec2 size, bool auto_resize) {
|
||||
if (auto_resize) {
|
||||
// Use auto-sizing child window for table integration
|
||||
ImGui::SetNextWindowContentSize(size);
|
||||
} else {
|
||||
// Fixed size
|
||||
ImGui::SetNextWindowSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
// High-level composite operations
|
||||
void DrawCanvasGrid(const CanvasRenderContext& ctx, int highlight_tile_id) {
|
||||
if (!ctx.enable_grid)
|
||||
return;
|
||||
|
||||
ctx.draw_list->PushClipRect(ctx.canvas_p0, ctx.canvas_p1, true);
|
||||
|
||||
// Draw grid lines
|
||||
DrawCanvasGridLines(ctx.draw_list, ctx.canvas_p0, ctx.canvas_p1,
|
||||
ctx.scrolling, ctx.grid_step, ctx.global_scale);
|
||||
|
||||
// Draw highlight if specified
|
||||
if (highlight_tile_id != -1) {
|
||||
DrawCustomHighlight(ctx.draw_list, ctx.canvas_p0, ctx.scrolling,
|
||||
highlight_tile_id, ctx.grid_step * ctx.global_scale);
|
||||
}
|
||||
|
||||
// Draw hex labels if enabled
|
||||
if (ctx.enable_hex_labels) {
|
||||
DrawHexTileLabels(ctx.draw_list, ctx.canvas_p0, ctx.scrolling,
|
||||
ImVec2(ctx.canvas_p1.x - ctx.canvas_p0.x,
|
||||
ctx.canvas_p1.y - ctx.canvas_p0.y),
|
||||
ctx.grid_step, ctx.global_scale);
|
||||
}
|
||||
|
||||
ctx.draw_list->PopClipRect();
|
||||
}
|
||||
|
||||
void DrawCanvasOverlay(const CanvasRenderContext& ctx,
|
||||
const ImVector<ImVec2>& points,
|
||||
const ImVector<ImVec2>& selected_points) {
|
||||
const ImVec2 origin(ctx.canvas_p0.x + ctx.scrolling.x,
|
||||
ctx.canvas_p0.y + ctx.scrolling.y);
|
||||
|
||||
// Draw hover points
|
||||
for (int n = 0; n < points.Size; n += 2) {
|
||||
ctx.draw_list->AddRect(
|
||||
ImVec2(origin.x + points[n].x, origin.y + points[n].y),
|
||||
ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y),
|
||||
IM_COL32(255, 255, 255, 255), 1.0f);
|
||||
}
|
||||
|
||||
// Draw selection rectangles
|
||||
if (!selected_points.empty()) {
|
||||
for (int n = 0; n < selected_points.size(); n += 2) {
|
||||
ctx.draw_list->AddRect(ImVec2(origin.x + selected_points[n].x,
|
||||
origin.y + selected_points[n].y),
|
||||
ImVec2(origin.x + selected_points[n + 1].x + 0x10,
|
||||
origin.y + selected_points[n + 1].y + 0x10),
|
||||
IM_COL32(255, 255, 255, 255), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCanvasLabels(const CanvasRenderContext& ctx,
|
||||
const ImVector<ImVector<std::string>>& labels,
|
||||
int current_labels, int tile_id_offset) {
|
||||
if (current_labels >= labels.size())
|
||||
return;
|
||||
|
||||
float scaled_grid_step = ctx.grid_step * ctx.global_scale;
|
||||
|
||||
for (float x = fmodf(ctx.scrolling.x, scaled_grid_step);
|
||||
x < (ctx.canvas_p1.x - ctx.canvas_p0.x); x += scaled_grid_step) {
|
||||
for (float y = fmodf(ctx.scrolling.y, scaled_grid_step);
|
||||
y < (ctx.canvas_p1.y - ctx.canvas_p0.y); y += scaled_grid_step) {
|
||||
int tile_x = (x - ctx.scrolling.x) / scaled_grid_step;
|
||||
int tile_y = (y - ctx.scrolling.y) / scaled_grid_step;
|
||||
int tile_id = tile_x + (tile_y * tile_id_offset);
|
||||
|
||||
if (tile_id >= labels[current_labels].size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string& label = labels[current_labels][tile_id];
|
||||
ctx.draw_list->AddText(
|
||||
ImVec2(ctx.canvas_p0.x + x + (scaled_grid_step / 2) - tile_id_offset,
|
||||
ctx.canvas_p0.y + y + (scaled_grid_step / 2) - tile_id_offset),
|
||||
IM_COL32(255, 255, 255, 255), label.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace CanvasUtils
|
||||
} // namespace canvas
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
@@ -1,198 +0,0 @@
|
||||
#ifndef YAZE_APP_GUI_CANVAS_CANVAS_UTILS_H
|
||||
#define YAZE_APP_GUI_CANVAS_CANVAS_UTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
namespace canvas {
|
||||
|
||||
/**
|
||||
* @brief Configuration for canvas display and interaction
|
||||
*
|
||||
* Modern single-source-of-truth configuration structure.
|
||||
* Replaces the dual state management pattern.
|
||||
*/
|
||||
struct CanvasConfig {
|
||||
bool enable_grid = true;
|
||||
bool enable_hex_labels = false;
|
||||
bool enable_custom_labels = false;
|
||||
bool enable_context_menu = true;
|
||||
bool is_draggable = false;
|
||||
bool auto_resize = false;
|
||||
float grid_step = 32.0f;
|
||||
float global_scale = 1.0f;
|
||||
ImVec2 canvas_size = ImVec2(0, 0);
|
||||
ImVec2 content_size = ImVec2(0, 0); // Size of actual content (bitmap, etc.)
|
||||
bool custom_canvas_size = false;
|
||||
ImVec2 scrolling = ImVec2(0, 0);
|
||||
|
||||
// Modern callbacks for config updates
|
||||
std::function<void(const CanvasConfig&)> on_config_changed;
|
||||
std::function<void(const CanvasConfig&)> on_scale_changed;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Selection state for canvas interactions
|
||||
*/
|
||||
struct CanvasSelection {
|
||||
std::vector<ImVec2> selected_tiles;
|
||||
std::vector<ImVec2> selected_points;
|
||||
ImVec2 selected_tile_pos = ImVec2(-1, -1);
|
||||
bool select_rect_active = false;
|
||||
|
||||
void Clear() {
|
||||
selected_tiles.clear();
|
||||
selected_points.clear();
|
||||
selected_tile_pos = ImVec2(-1, -1);
|
||||
select_rect_active = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Palette management state for canvas
|
||||
*/
|
||||
struct CanvasPaletteManager {
|
||||
std::vector<gfx::SnesPalette> rom_palette_groups;
|
||||
std::vector<std::string> palette_group_names;
|
||||
gfx::SnesPalette original_palette;
|
||||
bool palettes_loaded = false;
|
||||
int current_group_index = 0;
|
||||
int current_palette_index = 0;
|
||||
|
||||
void Clear() {
|
||||
rom_palette_groups.clear();
|
||||
palette_group_names.clear();
|
||||
original_palette.clear();
|
||||
palettes_loaded = false;
|
||||
current_group_index = 0;
|
||||
current_palette_index = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Context menu item configuration
|
||||
*/
|
||||
struct CanvasContextMenuItem {
|
||||
std::string label;
|
||||
std::string shortcut;
|
||||
std::function<void()> callback;
|
||||
std::function<bool()> enabled_condition = []() { return true; };
|
||||
std::vector<CanvasContextMenuItem> subitems;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Render context for canvas drawing operations
|
||||
*/
|
||||
struct CanvasRenderContext {
|
||||
ImDrawList* draw_list;
|
||||
ImVec2 canvas_p0;
|
||||
ImVec2 canvas_p1;
|
||||
ImVec2 scrolling;
|
||||
float global_scale;
|
||||
bool enable_grid;
|
||||
bool enable_hex_labels;
|
||||
float grid_step;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Utility functions for canvas operations
|
||||
*
|
||||
* Stateless utilities for common canvas operations.
|
||||
* Following ImGui design pattern of pure helper functions.
|
||||
*/
|
||||
namespace CanvasUtils {
|
||||
|
||||
// ==================== Core Utilities (Stateless) ====================
|
||||
|
||||
/**
|
||||
* @brief Align position to grid
|
||||
* Pure function - no side effects
|
||||
*/
|
||||
ImVec2 AlignToGrid(ImVec2 pos, float grid_step);
|
||||
|
||||
/**
|
||||
* @brief Calculate effective scale for content
|
||||
*/
|
||||
float CalculateEffectiveScale(ImVec2 canvas_size, ImVec2 content_size, float global_scale);
|
||||
|
||||
/**
|
||||
* @brief Get tile ID from mouse position
|
||||
*/
|
||||
int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale, int tiles_per_row);
|
||||
|
||||
// ==================== Palette Management ====================
|
||||
|
||||
bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager);
|
||||
bool ApplyPaletteGroup(gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_manager,
|
||||
int group_index, int palette_index);
|
||||
|
||||
// ==================== Drawing Utilities ====================
|
||||
|
||||
void DrawCanvasRect(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, ImVec4 color, float global_scale);
|
||||
|
||||
void DrawCanvasText(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
const std::string& text, int x, int y, float global_scale);
|
||||
|
||||
void DrawCanvasOutline(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, uint32_t color = IM_COL32(255, 255, 255, 200));
|
||||
|
||||
void DrawCanvasOutlineWithColor(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int x, int y, int w, int h, ImVec4 color);
|
||||
|
||||
// ==================== Grid Utilities ====================
|
||||
|
||||
void DrawCanvasGridLines(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 canvas_p1,
|
||||
ImVec2 scrolling, float grid_step, float global_scale);
|
||||
|
||||
void DrawCustomHighlight(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
int highlight_tile_id, float grid_step);
|
||||
|
||||
void DrawHexTileLabels(ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
ImVec2 canvas_sz, float grid_step, float global_scale);
|
||||
|
||||
// ==================== Layout Utilities ====================
|
||||
|
||||
ImVec2 CalculateCanvasSize(ImVec2 content_region, ImVec2 custom_size, bool use_custom);
|
||||
ImVec2 CalculateScaledCanvasSize(ImVec2 canvas_size, float global_scale);
|
||||
bool IsPointInCanvas(ImVec2 point, ImVec2 canvas_p0, ImVec2 canvas_p1);
|
||||
|
||||
// ==================== Size Reporting for Tables ====================
|
||||
|
||||
ImVec2 CalculateMinimumCanvasSize(ImVec2 content_size, float global_scale, float padding = 4.0f);
|
||||
ImVec2 CalculatePreferredCanvasSize(ImVec2 content_size, float global_scale, float min_scale = 1.0f);
|
||||
void ReserveCanvasSpace(ImVec2 canvas_size, const std::string& label = "");
|
||||
void SetNextCanvasSize(ImVec2 size, bool auto_resize = false);
|
||||
|
||||
// ==================== Composite Operations ====================
|
||||
|
||||
void DrawCanvasGrid(const CanvasRenderContext& ctx, int highlight_tile_id = -1);
|
||||
void DrawCanvasOverlay(const CanvasRenderContext& ctx, const ImVector<ImVec2>& points,
|
||||
const ImVector<ImVec2>& selected_points);
|
||||
void DrawCanvasLabels(const CanvasRenderContext& ctx, const ImVector<ImVector<std::string>>& labels,
|
||||
int current_labels, int tile_id_offset);
|
||||
|
||||
} // namespace CanvasUtils
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
// ==================== Compatibility Aliases (gui namespace) ====================
|
||||
|
||||
// For backward compatibility, provide aliases in gui namespace
|
||||
using CanvasConfig = canvas::CanvasConfig;
|
||||
using CanvasSelection = canvas::CanvasSelection;
|
||||
using CanvasPaletteManager = canvas::CanvasPaletteManager;
|
||||
using CanvasContextMenuItem = canvas::CanvasContextMenuItem;
|
||||
|
||||
namespace CanvasUtils = canvas::CanvasUtils;
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_CANVAS_CANVAS_UTILS_H
|
||||
@@ -15,6 +15,7 @@ set(
|
||||
app/gui/widgets/widget_id_registry.cc
|
||||
app/gui/widgets/widget_auto_register.cc
|
||||
app/gui/widgets/widget_state_capture.cc
|
||||
app/gui/ui_helpers.cc
|
||||
# Canvas system components
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
|
||||
72
src/app/gui/ui_helpers.cc
Normal file
72
src/app/gui/ui_helpers.cc
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
ImVec4 GetThemeColor(ImGuiCol idx) {
|
||||
return ImGui::GetStyle().Colors[idx];
|
||||
}
|
||||
|
||||
ImVec4 GetSuccessColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.success);
|
||||
}
|
||||
|
||||
ImVec4 GetWarningColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.warning);
|
||||
}
|
||||
|
||||
ImVec4 GetErrorColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.error);
|
||||
}
|
||||
|
||||
ImVec4 GetInfoColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.info);
|
||||
}
|
||||
|
||||
ImVec4 GetAccentColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.accent);
|
||||
}
|
||||
|
||||
void BeginField(const char* label) {
|
||||
ImGui::BeginGroup();
|
||||
ImGui::TextUnformatted(label);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.6f);
|
||||
}
|
||||
|
||||
void EndField() {
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
bool IconButton(const char* icon, const char* label, const ImVec2& size) {
|
||||
std::string button_text = std::string(icon) + " " + std::string(label);
|
||||
return ImGui::Button(button_text.c_str(), size);
|
||||
}
|
||||
|
||||
void HelpMarker(const char* desc) {
|
||||
ImGui::TextDisabled(ICON_MD_HELP_OUTLINE);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void SeparatorText(const char* label) {
|
||||
ImGui::SeparatorText(label);
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
46
src/app/gui/ui_helpers.h
Normal file
46
src/app/gui/ui_helpers.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef YAZE_APP_GUI_UI_HELPERS_H
|
||||
#define YAZE_APP_GUI_UI_HELPERS_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include <string>
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// A collection of helper functions and widgets to standardize UI development
|
||||
// and reduce boilerplate ImGui code.
|
||||
|
||||
// --- Theming and Colors ---
|
||||
|
||||
// Gets a color from the current theme.
|
||||
ImVec4 GetThemeColor(ImGuiCol idx);
|
||||
|
||||
// Gets a semantic color from the current theme.
|
||||
ImVec4 GetSuccessColor();
|
||||
ImVec4 GetWarningColor();
|
||||
ImVec4 GetErrorColor();
|
||||
ImVec4 GetInfoColor();
|
||||
ImVec4 GetAccentColor();
|
||||
|
||||
// --- Layout Helpers ---
|
||||
|
||||
// Begins a standard row for a label and a widget.
|
||||
void BeginField(const char* label);
|
||||
// Ends a field row.
|
||||
void EndField();
|
||||
|
||||
// --- Widget Wrappers ---
|
||||
|
||||
// A button with an icon from the Material Design icon font.
|
||||
bool IconButton(const char* icon, const char* label, const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
// A help marker that shows a tooltip on hover.
|
||||
void HelpMarker(const char* desc);
|
||||
|
||||
// A separator with centered text.
|
||||
void SeparatorText(const char* label);
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_UI_HELPERS_H
|
||||
@@ -69,10 +69,10 @@ public:
|
||||
bool IsROMLoaded() const { return rom_ != nullptr; }
|
||||
int GetCurrentGroupIndex() const { return current_group_index_; }
|
||||
int GetCurrentPaletteIndex() const { return current_palette_index_; }
|
||||
|
||||
void DrawROMPaletteSelector();
|
||||
|
||||
private:
|
||||
void DrawPaletteGrid(gfx::SnesPalette& palette, int cols = 8);
|
||||
void DrawROMPaletteSelector();
|
||||
void DrawColorEditControls(gfx::SnesColor& color, int color_index);
|
||||
void DrawPaletteAnalysis(const gfx::SnesPalette& palette);
|
||||
void LoadROMPalettes();
|
||||
|
||||
Reference in New Issue
Block a user