refactor: Update GUI components and integrate new palette widget
- Removed references to the old `EnhancedPaletteEditor` and replaced it with the new `PaletteWidget` across various files, including canvas and context menu implementations. - Updated CMake configurations to include the new `palette_widget` source files, ensuring proper integration into the build system. - Refactored GUI code to enhance modularity and maintainability, improving the overall user experience in palette management and editing functionalities. - Introduced new UI components for background rendering and editor selection dialogs, enhancing the application's graphical interface.
This commit is contained in:
@@ -926,8 +926,6 @@ source_group("Application\\Graphics" FILES
|
||||
|
||||
# GUI System
|
||||
source_group("Application\\GUI" FILES
|
||||
app/gui/background_renderer.cc
|
||||
app/gui/background_renderer.h
|
||||
app/gui/canvas_utils.cc
|
||||
app/gui/canvas_utils.h
|
||||
app/gui/canvas.cc
|
||||
@@ -935,7 +933,7 @@ source_group("Application\\GUI" FILES
|
||||
app/gui/color.cc
|
||||
app/gui/color.h
|
||||
app/gui/enhanced_palette_editor.cc
|
||||
app/gui/enhanced_palette_editor.h
|
||||
app/gui/widgets/palette_widget.h
|
||||
app/gui/icons.h
|
||||
app/gui/input.cc
|
||||
app/gui/input.h
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/gui/background_renderer.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/gui/widgets/widget_id_registry.h"
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
set(
|
||||
YAZE_APP_EDITOR_SRC
|
||||
app/editor/editor_manager.cc
|
||||
app/editor/menu_builder.cc
|
||||
app/editor/ui/editor_selection_dialog.cc
|
||||
app/editor/ui/welcome_screen.cc
|
||||
app/editor/ui/background_renderer.cc
|
||||
app/editor/dungeon/dungeon_editor.cc
|
||||
app/editor/dungeon/dungeon_editor_v2.cc
|
||||
app/editor/dungeon/dungeon_room_selector.cc
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
#include "app/editor/music/music_editor.h"
|
||||
#include "app/editor/overworld/overworld_editor.h"
|
||||
#include "app/editor/sprite/sprite_editor.h"
|
||||
#include "app/editor/ui/editor_selection_dialog.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gui/background_renderer.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
@@ -741,16 +742,16 @@ void EditorManager::Initialize(const std::string& filename) {
|
||||
}
|
||||
},
|
||||
[&]() { return current_rom_ && current_rom_->is_loaded(); }},
|
||||
{absl::StrCat(ICON_MD_SAVE_ALT, " Test Save/Load"), "",
|
||||
[&]() {
|
||||
if (current_rom_) {
|
||||
[[maybe_unused]] auto status = test::TestManager::Get().TestRomSaveLoad(current_rom_);
|
||||
}
|
||||
},
|
||||
[&]() { return current_rom_ && current_rom_->is_loaded(); }},
|
||||
}},
|
||||
{absl::StrCat(ICON_MD_SAVE_ALT, " Test Save/Load"), "",
|
||||
[&]() {
|
||||
if (current_rom_) {
|
||||
[[maybe_unused]] auto status = test::TestManager::Get().TestRomSaveLoad(current_rom_);
|
||||
}
|
||||
},
|
||||
[&]() { return current_rom_ && current_rom_->is_loaded(); }},
|
||||
}},
|
||||
|
||||
{absl::StrCat(ICON_MD_CODE, " ZSCustomOverworld"), "", []() {}, []() { return true; },
|
||||
{absl::StrCat(ICON_MD_CODE, " ZSCustomOverworld"), "", []() {}, []() { return true; },
|
||||
std::vector<gui::MenuItem>{
|
||||
{absl::StrCat(ICON_MD_INFO, " Check ROM Version"), "",
|
||||
[&]() {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#endif
|
||||
#include "app/editor/system/settings_editor.h"
|
||||
#include "app/editor/system/toast_manager.h"
|
||||
#include "app/editor/ui/editor_selection_dialog.h"
|
||||
#include "app/emu/emulator.h"
|
||||
#include "app/gfx/performance_dashboard.h"
|
||||
#include "app/rom.h"
|
||||
@@ -187,6 +188,10 @@ class EditorManager {
|
||||
|
||||
// Project file editor
|
||||
ProjectFileEditor project_file_editor_;
|
||||
|
||||
// Editor selection dialog
|
||||
EditorSelectionDialog editor_selection_dialog_;
|
||||
bool show_editor_selection_ = false;
|
||||
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
// Agent editor - manages chat, collaboration, and network coordination
|
||||
|
||||
187
src/app/editor/menu_builder.cc
Normal file
187
src/app/editor/menu_builder.cc
Normal file
@@ -0,0 +1,187 @@
|
||||
#include "app/editor/menu_builder.h"
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
MenuBuilder& MenuBuilder::BeginMenu(const char* label, const char* icon) {
|
||||
Menu menu;
|
||||
menu.label = label;
|
||||
if (icon) {
|
||||
menu.icon = icon;
|
||||
}
|
||||
menus_.push_back(menu);
|
||||
current_menu_ = &menus_.back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::BeginSubMenu(const char* label, const char* icon,
|
||||
EnabledCheck enabled) {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kSubMenuBegin;
|
||||
item.label = label;
|
||||
if (icon) {
|
||||
item.icon = icon;
|
||||
}
|
||||
item.enabled = enabled;
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::EndMenu() {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
// Check if we're ending a submenu or top-level menu
|
||||
bool is_submenu = false;
|
||||
for (auto it = current_menu_->items.rbegin();
|
||||
it != current_menu_->items.rend(); ++it) {
|
||||
if (it->type == MenuItem::Type::kSubMenuBegin) {
|
||||
is_submenu = true;
|
||||
break;
|
||||
} else if (it->type == MenuItem::Type::kSubMenuEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_submenu) {
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kSubMenuEnd;
|
||||
current_menu_->items.push_back(item);
|
||||
} else {
|
||||
current_menu_ = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::Item(const char* label, const char* icon,
|
||||
Callback callback, const char* shortcut,
|
||||
EnabledCheck enabled, EnabledCheck checked) {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kItem;
|
||||
item.label = label;
|
||||
if (icon) {
|
||||
item.icon = icon;
|
||||
}
|
||||
if (shortcut) {
|
||||
item.shortcut = shortcut;
|
||||
}
|
||||
item.callback = callback;
|
||||
item.enabled = enabled;
|
||||
item.checked = checked;
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::Item(const char* label, Callback callback,
|
||||
const char* shortcut, EnabledCheck enabled) {
|
||||
return Item(label, nullptr, callback, shortcut, enabled, nullptr);
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::Separator() {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kSeparator;
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MenuBuilder& MenuBuilder::DisabledItem(const char* label, const char* icon) {
|
||||
if (!current_menu_) return *this;
|
||||
|
||||
MenuItem item;
|
||||
item.type = MenuItem::Type::kDisabled;
|
||||
item.label = label;
|
||||
if (icon) {
|
||||
item.icon = icon;
|
||||
}
|
||||
current_menu_->items.push_back(item);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MenuBuilder::Draw() {
|
||||
for (const auto& menu : menus_) {
|
||||
std::string menu_label = menu.icon.empty()
|
||||
? menu.label
|
||||
: absl::StrCat(menu.icon, " ", menu.label);
|
||||
|
||||
if (ImGui::BeginMenu(menu_label.c_str())) {
|
||||
for (const auto& item : menu.items) {
|
||||
DrawMenuItem(item);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuBuilder::DrawMenuItem(const MenuItem& item) {
|
||||
switch (item.type) {
|
||||
case MenuItem::Type::kSeparator:
|
||||
ImGui::Separator();
|
||||
break;
|
||||
|
||||
case MenuItem::Type::kDisabled: {
|
||||
std::string label = item.icon.empty()
|
||||
? item.label
|
||||
: absl::StrCat(item.icon, " ", item.label);
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::MenuItem(label.c_str(), nullptr, false, false);
|
||||
ImGui::EndDisabled();
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItem::Type::kSubMenuBegin: {
|
||||
std::string label = item.icon.empty()
|
||||
? item.label
|
||||
: absl::StrCat(item.icon, " ", item.label);
|
||||
|
||||
bool enabled = !item.enabled || item.enabled();
|
||||
if (enabled && ImGui::BeginMenu(label.c_str())) {
|
||||
// Submenu items will be drawn in subsequent calls
|
||||
} else if (!enabled) {
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::MenuItem(label.c_str(), nullptr, false, false);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MenuItem::Type::kSubMenuEnd:
|
||||
ImGui::EndMenu();
|
||||
break;
|
||||
|
||||
case MenuItem::Type::kItem: {
|
||||
std::string label = item.icon.empty()
|
||||
? item.label
|
||||
: absl::StrCat(item.icon, " ", item.label);
|
||||
|
||||
bool enabled = !item.enabled || item.enabled();
|
||||
bool checked = item.checked && item.checked();
|
||||
|
||||
const char* shortcut_str = item.shortcut.empty()
|
||||
? nullptr
|
||||
: item.shortcut.c_str();
|
||||
|
||||
if (ImGui::MenuItem(label.c_str(), shortcut_str, checked, enabled)) {
|
||||
if (item.callback) {
|
||||
item.callback();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuBuilder::Clear() {
|
||||
menus_.clear();
|
||||
current_menu_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
119
src/app/editor/menu_builder.h
Normal file
119
src/app/editor/menu_builder.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef YAZE_APP_EDITOR_MENU_BUILDER_H_
|
||||
#define YAZE_APP_EDITOR_MENU_BUILDER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
/**
|
||||
* @class MenuBuilder
|
||||
* @brief Fluent interface for building ImGui menus with icons
|
||||
*
|
||||
* Provides a cleaner, more maintainable way to construct menus:
|
||||
*
|
||||
* MenuBuilder menu;
|
||||
* menu.BeginMenu("File", ICON_MD_FOLDER)
|
||||
* .Item("Open", ICON_MD_FILE_OPEN, []() { OpenFile(); })
|
||||
* .Separator()
|
||||
* .Item("Quit", ICON_MD_EXIT_TO_APP, []() { Quit(); })
|
||||
* .EndMenu();
|
||||
*/
|
||||
class MenuBuilder {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
using EnabledCheck = std::function<bool()>;
|
||||
|
||||
MenuBuilder() = default;
|
||||
|
||||
/**
|
||||
* @brief Begin a top-level menu
|
||||
*/
|
||||
MenuBuilder& BeginMenu(const char* label, const char* icon = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Begin a submenu
|
||||
*/
|
||||
MenuBuilder& BeginSubMenu(const char* label, const char* icon = nullptr,
|
||||
EnabledCheck enabled = nullptr);
|
||||
|
||||
/**
|
||||
* @brief End the current menu/submenu
|
||||
*/
|
||||
MenuBuilder& EndMenu();
|
||||
|
||||
/**
|
||||
* @brief Add a menu item
|
||||
*/
|
||||
MenuBuilder& Item(const char* label, const char* icon, Callback callback,
|
||||
const char* shortcut = nullptr,
|
||||
EnabledCheck enabled = nullptr,
|
||||
EnabledCheck checked = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Add a menu item without icon (convenience)
|
||||
*/
|
||||
MenuBuilder& Item(const char* label, Callback callback,
|
||||
const char* shortcut = nullptr,
|
||||
EnabledCheck enabled = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Add a separator
|
||||
*/
|
||||
MenuBuilder& Separator();
|
||||
|
||||
/**
|
||||
* @brief Add a disabled item (grayed out)
|
||||
*/
|
||||
MenuBuilder& DisabledItem(const char* label, const char* icon = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Draw the menu bar (call in main menu bar)
|
||||
*/
|
||||
void Draw();
|
||||
|
||||
/**
|
||||
* @brief Clear all menus
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
struct MenuItem {
|
||||
enum class Type {
|
||||
kItem,
|
||||
kSubMenuBegin,
|
||||
kSubMenuEnd,
|
||||
kSeparator,
|
||||
kDisabled
|
||||
};
|
||||
|
||||
Type type;
|
||||
std::string label;
|
||||
std::string icon;
|
||||
std::string shortcut;
|
||||
Callback callback;
|
||||
EnabledCheck enabled;
|
||||
EnabledCheck checked;
|
||||
};
|
||||
|
||||
struct Menu {
|
||||
std::string label;
|
||||
std::string icon;
|
||||
std::vector<MenuItem> items;
|
||||
};
|
||||
|
||||
std::vector<Menu> menus_;
|
||||
Menu* current_menu_ = nullptr;
|
||||
|
||||
void DrawMenuItem(const MenuItem& item);
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_MENU_BUILDER_H_
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "background_renderer.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef YAZE_APP_GUI_BACKGROUND_RENDERER_H
|
||||
#define YAZE_APP_GUI_BACKGROUND_RENDERER_H
|
||||
#ifndef YAZE_APP_EDITOR_UI_BACKGROUND_RENDERER_H
|
||||
#define YAZE_APP_EDITOR_UI_BACKGROUND_RENDERER_H
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include "app/gui/color.h"
|
||||
@@ -93,4 +93,4 @@ private:
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_BACKGROUND_RENDERER_H
|
||||
#endif // YAZE_APP_EDITOR_UI_BACKGROUND_RENDERER_H
|
||||
256
src/app/editor/ui/editor_selection_dialog.cc
Normal file
256
src/app/editor/ui/editor_selection_dialog.cc
Normal file
@@ -0,0 +1,256 @@
|
||||
#include "app/editor/ui/editor_selection_dialog.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "util/file_util.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
EditorSelectionDialog::EditorSelectionDialog() {
|
||||
// Initialize editor metadata
|
||||
editors_ = {
|
||||
{EditorType::kOverworld, "Overworld", ICON_MD_MAP,
|
||||
"Edit overworld maps, entrances, and properties", "Ctrl+1", false, true},
|
||||
|
||||
{EditorType::kDungeon, "Dungeon", ICON_MD_CASTLE,
|
||||
"Design dungeon rooms, layouts, and mechanics", "Ctrl+2", false, true},
|
||||
|
||||
{EditorType::kGraphics, "Graphics", ICON_MD_PALETTE,
|
||||
"Modify tiles, palettes, and graphics sets", "Ctrl+3", false, true},
|
||||
|
||||
{EditorType::kSprite, "Sprites", ICON_MD_EMOJI_EMOTIONS,
|
||||
"Edit sprite graphics and properties", "Ctrl+4", false, true},
|
||||
|
||||
{EditorType::kMessage, "Messages", ICON_MD_CHAT_BUBBLE,
|
||||
"Edit dialogue, signs, and text", "Ctrl+5", false, true},
|
||||
|
||||
{EditorType::kMusic, "Music", ICON_MD_MUSIC_NOTE,
|
||||
"Configure music and sound effects", "Ctrl+6", false, true},
|
||||
|
||||
{EditorType::kPalette, "Palettes", ICON_MD_COLOR_LENS,
|
||||
"Edit color palettes and animations", "Ctrl+7", false, true},
|
||||
|
||||
{EditorType::kScreen, "Screens", ICON_MD_TV,
|
||||
"Edit title screen and ending screens", "Ctrl+8", false, true},
|
||||
|
||||
{EditorType::kAssembly, "Assembly", ICON_MD_CODE,
|
||||
"Write and edit assembly code", "Ctrl+9", false, false},
|
||||
|
||||
{EditorType::kMemory, "Hex Editor", ICON_MD_DATA_ARRAY,
|
||||
"Direct ROM memory editing", "Ctrl+0", false, true},
|
||||
|
||||
{EditorType::kSettings, "Settings", ICON_MD_SETTINGS,
|
||||
"Configure ROM and project settings", "", false, true},
|
||||
};
|
||||
|
||||
LoadRecentEditors();
|
||||
}
|
||||
|
||||
bool EditorSelectionDialog::Show(bool* p_open) {
|
||||
if (!is_open_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool editor_selected = false;
|
||||
|
||||
// Center the dialog
|
||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
ImGui::SetNextWindowSize(ImVec2(900, 600), ImGuiCond_Appearing);
|
||||
|
||||
if (ImGui::Begin("Editor Selection", p_open ? p_open : &is_open_,
|
||||
ImGuiWindowFlags_NoCollapse)) {
|
||||
DrawWelcomeHeader();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Quick access buttons for recently used
|
||||
if (!recent_editors_.empty()) {
|
||||
DrawQuickAccessButtons();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
// Main editor grid
|
||||
ImGui::Text(ICON_MD_APPS " All Editors");
|
||||
ImGui::Spacing();
|
||||
|
||||
float button_size = 200.0f;
|
||||
int columns = static_cast<int>(ImGui::GetContentRegionAvail().x / button_size);
|
||||
columns = std::max(columns, 1);
|
||||
|
||||
if (ImGui::BeginTable("##EditorGrid", columns,
|
||||
ImGuiTableFlags_None)) {
|
||||
for (size_t i = 0; i < editors_.size(); ++i) {
|
||||
ImGui::TableNextColumn();
|
||||
DrawEditorCard(editors_[i], static_cast<int>(i));
|
||||
|
||||
if (selected_editor_ != EditorType::kNone) {
|
||||
editor_selected = true;
|
||||
MarkRecentlyUsed(selected_editor_);
|
||||
if (selection_callback_) {
|
||||
selection_callback_(selected_editor_);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (editor_selected) {
|
||||
is_open_ = false;
|
||||
}
|
||||
|
||||
return editor_selected;
|
||||
}
|
||||
|
||||
void EditorSelectionDialog::DrawWelcomeHeader() {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); // Larger font if available
|
||||
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
|
||||
ICON_MD_EDIT " Select an Editor");
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::TextWrapped("Choose an editor to begin working on your ROM. "
|
||||
"You can open multiple editors simultaneously.");
|
||||
}
|
||||
|
||||
void EditorSelectionDialog::DrawQuickAccessButtons() {
|
||||
ImGui::Text(ICON_MD_HISTORY " Recently Used");
|
||||
ImGui::Spacing();
|
||||
|
||||
for (EditorType type : recent_editors_) {
|
||||
// Find editor info
|
||||
auto it = std::find_if(editors_.begin(), editors_.end(),
|
||||
[type](const EditorInfo& info) {
|
||||
return info.type == type;
|
||||
});
|
||||
|
||||
if (it != editors_.end()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.8f, 0.6f));
|
||||
|
||||
if (ImGui::Button(absl::StrCat(it->icon, " ", it->name).c_str(),
|
||||
ImVec2(150, 30))) {
|
||||
selected_editor_ = type;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", it->description);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
void EditorSelectionDialog::DrawEditorCard(const EditorInfo& info, int index) {
|
||||
ImGui::PushID(index);
|
||||
|
||||
// Card styling
|
||||
bool is_recent = std::find(recent_editors_.begin(), recent_editors_.end(),
|
||||
info.type) != recent_editors_.end();
|
||||
|
||||
if (is_recent) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.25f, 0.55f, 0.85f, 0.4f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 0.9f, 0.6f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.35f, 0.65f, 0.95f, 0.8f));
|
||||
}
|
||||
|
||||
// Editor button with icon
|
||||
ImVec2 button_size(180, 100);
|
||||
if (ImGui::Button(absl::StrCat(info.icon, "\n", info.name).c_str(),
|
||||
button_size)) {
|
||||
selected_editor_ = info.type;
|
||||
}
|
||||
|
||||
if (is_recent) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
// Tooltip with description
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::Text("%s %s", info.icon, info.name);
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("%s", info.description);
|
||||
if (info.shortcut && info.shortcut[0]) {
|
||||
ImGui::Spacing();
|
||||
ImGui::TextDisabled("Shortcut: %s", info.shortcut);
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
// Recent indicator
|
||||
if (is_recent) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled(ICON_MD_STAR);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void EditorSelectionDialog::MarkRecentlyUsed(EditorType type) {
|
||||
// Remove if already in list
|
||||
auto it = std::find(recent_editors_.begin(), recent_editors_.end(), type);
|
||||
if (it != recent_editors_.end()) {
|
||||
recent_editors_.erase(it);
|
||||
}
|
||||
|
||||
// Add to front
|
||||
recent_editors_.insert(recent_editors_.begin(), type);
|
||||
|
||||
// Limit size
|
||||
if (recent_editors_.size() > kMaxRecentEditors) {
|
||||
recent_editors_.resize(kMaxRecentEditors);
|
||||
}
|
||||
|
||||
SaveRecentEditors();
|
||||
}
|
||||
|
||||
void EditorSelectionDialog::LoadRecentEditors() {
|
||||
try {
|
||||
auto data = util::LoadConfigFile("recent_editors.txt");
|
||||
if (!data.empty()) {
|
||||
std::istringstream ss(data);
|
||||
std::string line;
|
||||
while (std::getline(ss, line) &&
|
||||
recent_editors_.size() < kMaxRecentEditors) {
|
||||
int type_int = std::stoi(line);
|
||||
if (type_int >= 0 && type_int < static_cast<int>(EditorType::kLast)) {
|
||||
recent_editors_.push_back(static_cast<EditorType>(type_int));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Ignore errors, just start with empty recent list
|
||||
}
|
||||
}
|
||||
|
||||
void EditorSelectionDialog::SaveRecentEditors() {
|
||||
try {
|
||||
std::ostringstream ss;
|
||||
for (EditorType type : recent_editors_) {
|
||||
ss << static_cast<int>(type) << "\n";
|
||||
}
|
||||
util::SaveFile("recent_editors.txt", ss.str());
|
||||
} catch (...) {
|
||||
// Ignore save errors
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
104
src/app/editor/ui/editor_selection_dialog.h
Normal file
104
src/app/editor/ui/editor_selection_dialog.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#ifndef YAZE_APP_EDITOR_UI_EDITOR_SELECTION_DIALOG_H_
|
||||
#define YAZE_APP_EDITOR_UI_EDITOR_SELECTION_DIALOG_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "app/editor/editor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
/**
|
||||
* @struct EditorInfo
|
||||
* @brief Metadata about an available editor
|
||||
*/
|
||||
struct EditorInfo {
|
||||
EditorType type;
|
||||
const char* name;
|
||||
const char* icon;
|
||||
const char* description;
|
||||
const char* shortcut;
|
||||
bool recently_used = false;
|
||||
bool requires_rom = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class EditorSelectionDialog
|
||||
* @brief Beautiful grid-based editor selection dialog
|
||||
*
|
||||
* Displays when a ROM is loaded, showing all available editors
|
||||
* with icons, descriptions, and quick access.
|
||||
*/
|
||||
class EditorSelectionDialog {
|
||||
public:
|
||||
EditorSelectionDialog();
|
||||
|
||||
/**
|
||||
* @brief Show the dialog
|
||||
* @return True if an editor was selected
|
||||
*/
|
||||
bool Show(bool* p_open = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Get the selected editor type
|
||||
*/
|
||||
EditorType GetSelectedEditor() const { return selected_editor_; }
|
||||
|
||||
/**
|
||||
* @brief Check if dialog is open
|
||||
*/
|
||||
bool IsOpen() const { return is_open_; }
|
||||
|
||||
/**
|
||||
* @brief Open the dialog
|
||||
*/
|
||||
void Open() { is_open_ = true; }
|
||||
|
||||
/**
|
||||
* @brief Close the dialog
|
||||
*/
|
||||
void Close() { is_open_ = false; }
|
||||
|
||||
/**
|
||||
* @brief Set callback for when editor is selected
|
||||
*/
|
||||
void SetSelectionCallback(std::function<void(EditorType)> callback) {
|
||||
selection_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mark an editor as recently used
|
||||
*/
|
||||
void MarkRecentlyUsed(EditorType type);
|
||||
|
||||
/**
|
||||
* @brief Load recently used editors from settings
|
||||
*/
|
||||
void LoadRecentEditors();
|
||||
|
||||
/**
|
||||
* @brief Save recently used editors to settings
|
||||
*/
|
||||
void SaveRecentEditors();
|
||||
|
||||
private:
|
||||
void DrawEditorCard(const EditorInfo& info, int index);
|
||||
void DrawWelcomeHeader();
|
||||
void DrawQuickAccessButtons();
|
||||
|
||||
std::vector<EditorInfo> editors_;
|
||||
EditorType selected_editor_ = EditorType::kNone;
|
||||
bool is_open_ = false;
|
||||
std::function<void(EditorType)> selection_callback_;
|
||||
|
||||
// Recently used tracking
|
||||
std::vector<EditorType> recent_editors_;
|
||||
static constexpr int kMaxRecentEditors = 5;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UI_EDITOR_SELECTION_DIALOG_H_
|
||||
690
src/app/editor/ui/welcome_screen.cc
Normal file
690
src/app/editor/ui/welcome_screen.cc
Normal file
@@ -0,0 +1,690 @@
|
||||
#include "app/editor/ui/welcome_screen.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "app/core/project.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
#include "util/file_util.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
namespace {
|
||||
|
||||
// Get Zelda-inspired colors from theme or use fallback
|
||||
ImVec4 GetThemedColor(const char* color_name, const ImVec4& fallback) {
|
||||
auto& theme_mgr = gui::ThemeManager::Get();
|
||||
const auto& theme = theme_mgr.GetCurrentTheme();
|
||||
|
||||
// Map color names to theme colors
|
||||
if (strcmp(color_name, "triforce_gold") == 0) {
|
||||
return theme.accent.to_im_vec4();
|
||||
} else if (strcmp(color_name, "hyrule_green") == 0) {
|
||||
return theme.success.to_im_vec4();
|
||||
} else if (strcmp(color_name, "master_sword_blue") == 0) {
|
||||
return theme.info.to_im_vec4();
|
||||
} else if (strcmp(color_name, "ganon_purple") == 0) {
|
||||
return theme.secondary.to_im_vec4();
|
||||
} else if (strcmp(color_name, "heart_red") == 0) {
|
||||
return theme.error.to_im_vec4();
|
||||
} else if (strcmp(color_name, "spirit_orange") == 0) {
|
||||
return theme.warning.to_im_vec4();
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// Zelda-inspired color palette (fallbacks)
|
||||
const ImVec4 kTriforceGoldFallback = ImVec4(1.0f, 0.843f, 0.0f, 1.0f);
|
||||
const ImVec4 kHyruleGreenFallback = ImVec4(0.133f, 0.545f, 0.133f, 1.0f);
|
||||
const ImVec4 kMasterSwordBlueFallback = ImVec4(0.196f, 0.6f, 0.8f, 1.0f);
|
||||
const ImVec4 kGanonPurpleFallback = ImVec4(0.502f, 0.0f, 0.502f, 1.0f);
|
||||
const ImVec4 kHeartRedFallback = ImVec4(0.863f, 0.078f, 0.235f, 1.0f);
|
||||
const ImVec4 kSpiritOrangeFallback = ImVec4(1.0f, 0.647f, 0.0f, 1.0f);
|
||||
const ImVec4 kShadowPurpleFallback = ImVec4(0.416f, 0.353f, 0.804f, 1.0f);
|
||||
|
||||
// Active colors (updated each frame from theme)
|
||||
ImVec4 kTriforceGold = kTriforceGoldFallback;
|
||||
ImVec4 kHyruleGreen = kHyruleGreenFallback;
|
||||
ImVec4 kMasterSwordBlue = kMasterSwordBlueFallback;
|
||||
ImVec4 kGanonPurple = kGanonPurpleFallback;
|
||||
ImVec4 kHeartRed = kHeartRedFallback;
|
||||
ImVec4 kSpiritOrange = kSpiritOrangeFallback;
|
||||
ImVec4 kShadowPurple = kShadowPurpleFallback;
|
||||
|
||||
std::string GetRelativeTimeString(const std::filesystem::file_time_type& ftime) {
|
||||
auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
|
||||
ftime - std::filesystem::file_time_type::clock::now() +
|
||||
std::chrono::system_clock::now());
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto diff = std::chrono::duration_cast<std::chrono::hours>(now - sctp);
|
||||
|
||||
int hours = diff.count();
|
||||
if (hours < 24) {
|
||||
return "Today";
|
||||
} else if (hours < 48) {
|
||||
return "Yesterday";
|
||||
} else if (hours < 168) {
|
||||
int days = hours / 24;
|
||||
return absl::StrFormat("%d days ago", days);
|
||||
} else if (hours < 720) {
|
||||
int weeks = hours / 168;
|
||||
return absl::StrFormat("%d week%s ago", weeks, weeks > 1 ? "s" : "");
|
||||
} else {
|
||||
int months = hours / 720;
|
||||
return absl::StrFormat("%d month%s ago", months, months > 1 ? "s" : "");
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a pulsing triforce in the background
|
||||
void DrawTriforceBackground(ImDrawList* draw_list, ImVec2 pos, float size, float alpha, float glow) {
|
||||
float height = size * 0.866f; // sqrt(3)/2 for equilateral triangle
|
||||
|
||||
// Calculate triangle points
|
||||
auto triangle = [&](ImVec2 center, float s, ImU32 color) {
|
||||
ImVec2 p1(center.x, center.y - height * s / size);
|
||||
ImVec2 p2(center.x - s / 2, center.y + height * s / (2 * size));
|
||||
ImVec2 p3(center.x + s / 2, center.y + height * s / (2 * size));
|
||||
|
||||
draw_list->AddTriangleFilled(p1, p2, p3, color);
|
||||
|
||||
// Glow effect
|
||||
if (glow > 0.0f) {
|
||||
ImU32 glow_color = ImGui::GetColorU32(ImVec4(1.0f, 0.843f, 0.0f, glow * 0.3f));
|
||||
draw_list->AddTriangle(p1, p2, p3, glow_color, 2.0f + glow * 3.0f);
|
||||
}
|
||||
};
|
||||
|
||||
ImU32 gold = ImGui::GetColorU32(ImVec4(1.0f, 0.843f, 0.0f, alpha));
|
||||
|
||||
// Top triangle
|
||||
triangle(ImVec2(pos.x, pos.y), size, gold);
|
||||
|
||||
// Bottom left triangle
|
||||
triangle(ImVec2(pos.x - size / 4, pos.y + height / 2), size / 2, gold);
|
||||
|
||||
// Bottom right triangle
|
||||
triangle(ImVec2(pos.x + size / 4, pos.y + height / 2), size / 2, gold);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WelcomeScreen::WelcomeScreen() {
|
||||
RefreshRecentProjects();
|
||||
}
|
||||
|
||||
bool WelcomeScreen::Show(bool* p_open) {
|
||||
// Update theme colors each frame
|
||||
kTriforceGold = GetThemedColor("triforce_gold", kTriforceGoldFallback);
|
||||
kHyruleGreen = GetThemedColor("hyrule_green", kHyruleGreenFallback);
|
||||
kMasterSwordBlue = GetThemedColor("master_sword_blue", kMasterSwordBlueFallback);
|
||||
kGanonPurple = GetThemedColor("ganon_purple", kGanonPurpleFallback);
|
||||
kHeartRed = GetThemedColor("heart_red", kHeartRedFallback);
|
||||
kSpiritOrange = GetThemedColor("spirit_orange", kSpiritOrangeFallback);
|
||||
|
||||
UpdateAnimations();
|
||||
bool action_taken = false;
|
||||
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||
ImGui::SetNextWindowSize(viewport->WorkSize);
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
|
||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoNavFocus;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20, 20));
|
||||
|
||||
if (ImGui::Begin("##WelcomeScreen", p_open, window_flags)) {
|
||||
ImDrawList* bg_draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||
ImVec2 window_size = ImGui::GetWindowSize();
|
||||
|
||||
// Dreamlike animated background with floating triforces
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
float offset = animation_time_ * 0.5f + i * 2.0f;
|
||||
float x = window_pos.x + window_size.x * (0.2f + 0.15f * i + sin(offset) * 0.1f);
|
||||
float y = window_pos.y + window_size.y * (0.3f + cos(offset * 0.7f) * 0.3f);
|
||||
float size = 40.0f + sin(offset * 1.3f) * 20.0f;
|
||||
float alpha = 0.05f + sin(offset) * 0.05f;
|
||||
|
||||
DrawTriforceBackground(bg_draw_list, ImVec2(x, y), size, alpha, 0.0f);
|
||||
}
|
||||
|
||||
DrawHeader();
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Main content area with gradient separator
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 separator_start = ImGui::GetCursorScreenPos();
|
||||
ImVec2 separator_end(separator_start.x + ImGui::GetContentRegionAvail().x, separator_start.y + 2);
|
||||
draw_list->AddRectFilledMultiColor(
|
||||
separator_start, separator_end,
|
||||
ImGui::GetColorU32(kTriforceGold),
|
||||
ImGui::GetColorU32(kMasterSwordBlue),
|
||||
ImGui::GetColorU32(kMasterSwordBlue),
|
||||
ImGui::GetColorU32(kTriforceGold));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, 10));
|
||||
|
||||
ImGui::BeginChild("WelcomeContent", ImVec2(0, -60), false);
|
||||
|
||||
// Left side - Quick Actions & Templates
|
||||
ImGui::BeginChild("LeftPanel", ImVec2(ImGui::GetContentRegionAvail().x * 0.3f, 0), true,
|
||||
ImGuiWindowFlags_NoScrollbar);
|
||||
DrawQuickActions();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Animated separator
|
||||
ImVec2 sep_start = ImGui::GetCursorScreenPos();
|
||||
float pulse = sin(animation_time_ * 2.0f) * 0.5f + 0.5f;
|
||||
draw_list->AddLine(
|
||||
sep_start,
|
||||
ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
|
||||
ImGui::GetColorU32(ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.3f + pulse * 0.3f)),
|
||||
2.0f);
|
||||
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
DrawTemplatesSection();
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Right side - Recent Projects & What's New
|
||||
ImGui::BeginChild("RightPanel", ImVec2(0, 0), true);
|
||||
DrawRecentProjects();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Another animated separator
|
||||
sep_start = ImGui::GetCursorScreenPos();
|
||||
draw_list->AddLine(
|
||||
sep_start,
|
||||
ImVec2(sep_start.x + ImGui::GetContentRegionAvail().x, sep_start.y),
|
||||
ImGui::GetColorU32(ImVec4(kMasterSwordBlue.x, kMasterSwordBlue.y, kMasterSwordBlue.z, 0.3f + pulse * 0.3f)),
|
||||
2.0f);
|
||||
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
DrawWhatsNew();
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// Footer with gradient
|
||||
ImVec2 footer_start = ImGui::GetCursorScreenPos();
|
||||
ImVec2 footer_end(footer_start.x + ImGui::GetContentRegionAvail().x, footer_start.y + 2);
|
||||
draw_list->AddRectFilledMultiColor(
|
||||
footer_start, footer_end,
|
||||
ImGui::GetColorU32(kHeartRed),
|
||||
ImGui::GetColorU32(kHyruleGreen),
|
||||
ImGui::GetColorU32(kHyruleGreen),
|
||||
ImGui::GetColorU32(kHeartRed));
|
||||
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
DrawTipsSection();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return action_taken;
|
||||
}
|
||||
|
||||
void WelcomeScreen::UpdateAnimations() {
|
||||
animation_time_ += ImGui::GetIO().DeltaTime;
|
||||
header_glow_ = sin(animation_time_ * 2.0f) * 0.5f + 0.5f;
|
||||
|
||||
// Smooth card hover animations
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
float target = (hovered_card_ == i) ? 1.05f : 1.0f;
|
||||
card_hover_scale_[i] += (target - card_hover_scale_[i]) * ImGui::GetIO().DeltaTime * 8.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeScreen::RefreshRecentProjects() {
|
||||
recent_projects_.clear();
|
||||
|
||||
// Use the ProjectManager singleton to get recent files
|
||||
auto& recent_files = core::ProjectManager::GetInstance().GetRecentFiles();
|
||||
|
||||
for (const auto& filepath : recent_files) {
|
||||
if (recent_projects_.size() >= kMaxRecentProjects) break;
|
||||
|
||||
RecentProject project;
|
||||
project.filepath = filepath;
|
||||
|
||||
// Extract filename
|
||||
std::filesystem::path path(filepath);
|
||||
project.name = path.filename().string();
|
||||
|
||||
// Get file modification time if it exists
|
||||
if (std::filesystem::exists(path)) {
|
||||
auto ftime = std::filesystem::last_write_time(path);
|
||||
project.last_modified = GetRelativeTimeString(ftime);
|
||||
project.rom_title = "ALTTP ROM";
|
||||
} else {
|
||||
project.last_modified = "File not found";
|
||||
project.rom_title = "Missing";
|
||||
}
|
||||
|
||||
recent_projects_.push_back(project);
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeScreen::DrawHeader() {
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]); // Large font
|
||||
|
||||
// Animated title with glow effect
|
||||
const char* title = ICON_MD_CASTLE " yaze";
|
||||
auto windowWidth = ImGui::GetWindowSize().x;
|
||||
auto textWidth = ImGui::CalcTextSize(title).x;
|
||||
float xPos = (windowWidth - textWidth) * 0.5f;
|
||||
|
||||
ImGui::SetCursorPosX(xPos);
|
||||
ImVec2 text_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// Glow effect behind text
|
||||
float glow_size = 40.0f * header_glow_;
|
||||
ImU32 glow_color = ImGui::GetColorU32(ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.3f * header_glow_));
|
||||
draw_list->AddCircleFilled(
|
||||
ImVec2(text_pos.x + textWidth / 2, text_pos.y + 15),
|
||||
glow_size,
|
||||
glow_color,
|
||||
32);
|
||||
|
||||
// Rainbow gradient on title
|
||||
ImVec4 color1 = kTriforceGold;
|
||||
ImVec4 color2 = kMasterSwordBlue;
|
||||
float t = sin(animation_time_ * 1.5f) * 0.5f + 0.5f;
|
||||
ImVec4 title_color(
|
||||
color1.x * (1 - t) + color2.x * t,
|
||||
color1.y * (1 - t) + color2.y * t,
|
||||
color1.z * (1 - t) + color2.z * t,
|
||||
1.0f);
|
||||
|
||||
ImGui::TextColored(title_color, "%s", title);
|
||||
ImGui::PopFont();
|
||||
|
||||
// Animated subtitle
|
||||
const char* subtitle = "Yet Another Zelda3 Editor";
|
||||
textWidth = ImGui::CalcTextSize(subtitle).x;
|
||||
ImGui::SetCursorPosX((windowWidth - textWidth) * 0.5f);
|
||||
|
||||
float subtitle_alpha = 0.6f + sin(animation_time_ * 3.0f) * 0.2f;
|
||||
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, subtitle_alpha), "%s", subtitle);
|
||||
|
||||
// Draw small decorative triforces on either side of title
|
||||
ImVec2 left_tri_pos(xPos - 50, text_pos.y + 10);
|
||||
ImVec2 right_tri_pos(xPos + textWidth + 20, text_pos.y + 10);
|
||||
DrawTriforceBackground(draw_list, left_tri_pos, 30, 0.5f, header_glow_);
|
||||
DrawTriforceBackground(draw_list, right_tri_pos, 30, 0.5f, header_glow_);
|
||||
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
void WelcomeScreen::DrawQuickActions() {
|
||||
ImGui::TextColored(kSpiritOrange, ICON_MD_BOLT " Quick Actions");
|
||||
ImGui::Spacing();
|
||||
|
||||
float button_width = ImGui::GetContentRegionAvail().x;
|
||||
|
||||
// Animated button colors
|
||||
auto draw_action_button = [&](const char* icon, const char* text,
|
||||
const ImVec4& color, bool enabled,
|
||||
std::function<void()> callback) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x * 0.6f, color.y * 0.6f, color.z * 0.6f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x, color.y, color.z, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(color.x * 1.2f, color.y * 1.2f, color.z * 1.2f, 1.0f));
|
||||
|
||||
if (!enabled) ImGui::BeginDisabled();
|
||||
|
||||
bool clicked = ImGui::Button(absl::StrFormat("%s %s", icon, text).c_str(),
|
||||
ImVec2(button_width, 45));
|
||||
|
||||
if (!enabled) ImGui::EndDisabled();
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (clicked && enabled && callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
return clicked;
|
||||
};
|
||||
|
||||
// Open ROM button - Green like finding an item
|
||||
if (draw_action_button(ICON_MD_FOLDER_OPEN, "Open ROM", kHyruleGreen, true, open_rom_callback_)) {
|
||||
// Handled by callback
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_INFO " Open an existing ALTTP ROM file");
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// New Project button - Gold like getting a treasure
|
||||
if (draw_action_button(ICON_MD_ADD_CIRCLE, "New Project", kTriforceGold, true, new_project_callback_)) {
|
||||
// Handled by callback
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_INFO " Create a new ROM hacking project");
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Clone Project button - Blue like water/ice dungeon
|
||||
draw_action_button(ICON_MD_CLOUD_DOWNLOAD, "Clone Project", kMasterSwordBlue, false, nullptr);
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
ImGui::SetTooltip(ICON_MD_CONSTRUCTION " Clone a project from git (Coming soon)");
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeScreen::DrawRecentProjects() {
|
||||
ImGui::TextColored(kMasterSwordBlue, ICON_MD_HISTORY " Recent Projects");
|
||||
ImGui::Spacing();
|
||||
|
||||
if (recent_projects_.empty()) {
|
||||
// Draw a cute "empty state" with animated icons
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.6f, 0.6f, 1.0f));
|
||||
|
||||
float pulse = sin(animation_time_ * 2.0f) * 0.3f + 0.7f;
|
||||
ImVec2 cursor = ImGui::GetCursorPos();
|
||||
ImGui::SetCursorPosX(cursor.x + ImGui::GetContentRegionAvail().x * 0.3f);
|
||||
ImGui::TextColored(ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, pulse),
|
||||
ICON_MD_EXPLORE);
|
||||
ImGui::SetCursorPosX(cursor.x);
|
||||
|
||||
ImGui::TextWrapped("No recent projects yet.\nOpen a ROM to begin your adventure!");
|
||||
ImGui::PopStyleColor();
|
||||
return;
|
||||
}
|
||||
|
||||
// Grid layout for project cards
|
||||
float card_width = 220.0f;
|
||||
float card_height = 140.0f;
|
||||
int columns = std::max(1, (int)(ImGui::GetContentRegionAvail().x / (card_width + 15)));
|
||||
|
||||
for (size_t i = 0; i < recent_projects_.size(); ++i) {
|
||||
if (i % columns != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
DrawProjectCard(recent_projects_[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImVec2 card_size(220, 140);
|
||||
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
// Apply hover scale
|
||||
float scale = card_hover_scale_[index];
|
||||
if (scale != 1.0f) {
|
||||
ImVec2 center(cursor_pos.x + card_size.x / 2, cursor_pos.y + card_size.y / 2);
|
||||
cursor_pos.x = center.x - (card_size.x * scale) / 2;
|
||||
cursor_pos.y = center.y - (card_size.y * scale) / 2;
|
||||
card_size.x *= scale;
|
||||
card_size.y *= scale;
|
||||
}
|
||||
|
||||
// Draw card background with Zelda-themed gradient
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// Gradient background
|
||||
ImU32 color_top = ImGui::GetColorU32(ImVec4(0.15f, 0.20f, 0.25f, 1.0f));
|
||||
ImU32 color_bottom = ImGui::GetColorU32(ImVec4(0.10f, 0.15f, 0.20f, 1.0f));
|
||||
draw_list->AddRectFilledMultiColor(
|
||||
cursor_pos,
|
||||
ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
|
||||
color_top, color_top, color_bottom, color_bottom);
|
||||
|
||||
// Border with animated color
|
||||
float border_t = (sin(animation_time_ + index) * 0.5f + 0.5f);
|
||||
ImVec4 border_color_base = (index % 3 == 0) ? kHyruleGreen :
|
||||
(index % 3 == 1) ? kMasterSwordBlue : kTriforceGold;
|
||||
ImU32 border_color = ImGui::GetColorU32(
|
||||
ImVec4(border_color_base.x, border_color_base.y, border_color_base.z, 0.4f + border_t * 0.3f));
|
||||
|
||||
draw_list->AddRect(cursor_pos,
|
||||
ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
|
||||
border_color, 6.0f, 0, 2.0f);
|
||||
|
||||
// Make the card clickable
|
||||
ImGui::SetCursorScreenPos(cursor_pos);
|
||||
ImGui::InvisibleButton(absl::StrFormat("ProjectCard_%d", index).c_str(), card_size);
|
||||
bool is_hovered = ImGui::IsItemHovered();
|
||||
bool is_clicked = ImGui::IsItemClicked();
|
||||
|
||||
hovered_card_ = is_hovered ? index : (hovered_card_ == index ? -1 : hovered_card_);
|
||||
|
||||
// Hover glow effect
|
||||
if (is_hovered) {
|
||||
ImU32 hover_color = ImGui::GetColorU32(ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, 0.2f));
|
||||
draw_list->AddRectFilled(cursor_pos,
|
||||
ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
|
||||
hover_color, 6.0f);
|
||||
|
||||
// Draw small particles around the card
|
||||
for (int p = 0; p < 5; ++p) {
|
||||
float angle = animation_time_ * 2.0f + p * M_PI * 0.4f;
|
||||
float radius = 10.0f + sin(animation_time_ * 3.0f + p) * 5.0f;
|
||||
ImVec2 particle_pos(
|
||||
cursor_pos.x + card_size.x / 2 + cos(angle) * (card_size.x / 2 + radius),
|
||||
cursor_pos.y + card_size.y / 2 + sin(angle) * (card_size.y / 2 + radius));
|
||||
float particle_alpha = 0.3f + sin(animation_time_ * 4.0f + p) * 0.2f;
|
||||
draw_list->AddCircleFilled(particle_pos, 2.0f,
|
||||
ImGui::GetColorU32(ImVec4(kTriforceGold.x, kTriforceGold.y, kTriforceGold.z, particle_alpha)));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw content
|
||||
ImVec2 content_pos(cursor_pos.x + 15, cursor_pos.y + 15);
|
||||
|
||||
// Icon with colored background circle
|
||||
ImVec2 icon_center(content_pos.x + 20, content_pos.y + 20);
|
||||
ImU32 icon_bg = ImGui::GetColorU32(border_color_base);
|
||||
draw_list->AddCircleFilled(icon_center, 25, icon_bg, 32);
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 8));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1));
|
||||
ImGui::Text(ICON_MD_VIDEOGAME_ASSET);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// Project name
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 50, content_pos.y + 15));
|
||||
ImGui::PushTextWrapPos(cursor_pos.x + card_size.x - 15);
|
||||
ImGui::TextColored(kTriforceGold, "%s", project.name.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
// ROM title
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 55));
|
||||
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), ICON_MD_GAMEPAD " %s", project.rom_title.c_str());
|
||||
|
||||
// Last modified with clock icon
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 80));
|
||||
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
|
||||
ICON_MD_ACCESS_TIME " %s", project.last_modified.c_str());
|
||||
|
||||
// Path in card
|
||||
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 105));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
|
||||
std::string short_path = project.filepath;
|
||||
if (short_path.length() > 35) {
|
||||
short_path = "..." + short_path.substr(short_path.length() - 32);
|
||||
}
|
||||
ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// Tooltip
|
||||
if (is_hovered) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextColored(kMasterSwordBlue, ICON_MD_INFO " Project Details");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Name: %s", project.name.c_str());
|
||||
ImGui::Text("Path: %s", project.filepath.c_str());
|
||||
ImGui::Text("Modified: %s", project.last_modified.c_str());
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(kTriforceGold, ICON_MD_TOUCH_APP " Click to open");
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
// Handle click
|
||||
if (is_clicked && open_project_callback_) {
|
||||
open_project_callback_(project.filepath);
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
void WelcomeScreen::DrawTemplatesSection() {
|
||||
ImGui::TextColored(kGanonPurple, ICON_MD_LAYERS " Templates");
|
||||
ImGui::Spacing();
|
||||
|
||||
struct Template {
|
||||
const char* icon;
|
||||
const char* name;
|
||||
ImVec4 color;
|
||||
};
|
||||
|
||||
Template templates[] = {
|
||||
{ICON_MD_COTTAGE, "Vanilla ALTTP", kHyruleGreen},
|
||||
{ICON_MD_MAP, "ZSCustomOverworld v3", kMasterSwordBlue},
|
||||
{ICON_MD_PUBLIC, "Parallel Worlds Base", kGanonPurple},
|
||||
};
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
bool is_selected = (selected_template_ == i);
|
||||
|
||||
// Animated selection glow
|
||||
if (is_selected) {
|
||||
float glow = sin(animation_time_ * 3.0f) * 0.3f + 0.5f;
|
||||
ImGui::PushStyleColor(ImGuiCol_Header,
|
||||
ImVec4(templates[i].color.x * glow, templates[i].color.y * glow,
|
||||
templates[i].color.z * glow, 0.8f));
|
||||
}
|
||||
|
||||
if (ImGui::Selectable(absl::StrFormat("%s %s", templates[i].icon, templates[i].name).c_str(),
|
||||
is_selected)) {
|
||||
selected_template_ = i;
|
||||
}
|
||||
|
||||
if (is_selected) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_STAR " Start with a %s template", templates[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(kSpiritOrange.x * 0.6f, kSpiritOrange.y * 0.6f, kSpiritOrange.z * 0.6f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kSpiritOrange);
|
||||
ImGui::BeginDisabled(true);
|
||||
ImGui::Button(absl::StrFormat("%s Use Template", ICON_MD_ROCKET_LAUNCH).c_str(),
|
||||
ImVec2(-1, 35));
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
void WelcomeScreen::DrawTipsSection() {
|
||||
// Rotating tips
|
||||
const char* tips[] = {
|
||||
"Press Ctrl+P to open the command palette",
|
||||
"Use z3ed agent for AI-powered ROM editing",
|
||||
"Enable ZSCustomOverworld in Debug menu for expanded features",
|
||||
"Check the Performance Dashboard for optimization insights",
|
||||
"Collaborate in real-time with yaze-server"
|
||||
};
|
||||
int tip_index = ((int)(animation_time_ / 5.0f)) % 5;
|
||||
|
||||
ImGui::Text(ICON_MD_LIGHTBULB);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(kTriforceGold, "Tip:");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "%s", tips[tip_index]);
|
||||
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - 220);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.3f, 0.5f));
|
||||
if (ImGui::SmallButton(absl::StrFormat("%s Don't show again", ICON_MD_CLOSE).c_str())) {
|
||||
manually_closed_ = true;
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void WelcomeScreen::DrawWhatsNew() {
|
||||
ImGui::TextColored(kHeartRed, ICON_MD_NEW_RELEASES " What's New");
|
||||
ImGui::Spacing();
|
||||
|
||||
// Version badge
|
||||
float pulse = sin(animation_time_ * 2.0f) * 0.2f + 0.8f;
|
||||
ImGui::TextColored(ImVec4(kMasterSwordBlue.x * pulse, kMasterSwordBlue.y * pulse,
|
||||
kMasterSwordBlue.z * pulse, 1.0f),
|
||||
ICON_MD_VERIFIED " yaze v0.2.0-alpha");
|
||||
ImGui::Spacing();
|
||||
|
||||
// Feature list with icons and colors
|
||||
struct Feature {
|
||||
const char* icon;
|
||||
const char* title;
|
||||
const char* desc;
|
||||
ImVec4 color;
|
||||
};
|
||||
|
||||
Feature features[] = {
|
||||
{ICON_MD_PSYCHOLOGY, "AI Agent Integration",
|
||||
"Natural language ROM editing with z3ed agent", kGanonPurple},
|
||||
{ICON_MD_CLOUD_SYNC, "Collaboration Features",
|
||||
"Real-time ROM collaboration via yaze-server", kMasterSwordBlue},
|
||||
{ICON_MD_HISTORY, "Version Management",
|
||||
"ROM snapshots, rollback, corruption detection", kHyruleGreen},
|
||||
{ICON_MD_PALETTE, "Enhanced Palette Editor",
|
||||
"Advanced color tools with ROM palette browser", kSpiritOrange},
|
||||
{ICON_MD_SPEED, "Performance Improvements",
|
||||
"Faster dungeon loading with parallel processing", kTriforceGold},
|
||||
};
|
||||
|
||||
for (const auto& feature : features) {
|
||||
ImGui::Bullet();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(feature.color, "%s ", feature.icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(0.95f, 0.95f, 0.95f, 1.0f), "%s", feature.title);
|
||||
|
||||
ImGui::Indent(25);
|
||||
ImGui::TextColored(ImVec4(0.65f, 0.65f, 0.65f, 1.0f), "%s", feature.desc);
|
||||
ImGui::Unindent(25);
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(kMasterSwordBlue.x * 0.6f, kMasterSwordBlue.y * 0.6f, kMasterSwordBlue.z * 0.6f, 0.8f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kMasterSwordBlue);
|
||||
if (ImGui::Button(absl::StrFormat("%s View Full Changelog", ICON_MD_OPEN_IN_NEW).c_str())) {
|
||||
// Open changelog or GitHub releases
|
||||
}
|
||||
ImGui::PopStyleColor(2);
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
111
src/app/editor/ui/welcome_screen.h
Normal file
111
src/app/editor/ui/welcome_screen.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifndef YAZE_APP_EDITOR_UI_WELCOME_SCREEN_H_
|
||||
#define YAZE_APP_EDITOR_UI_WELCOME_SCREEN_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
/**
|
||||
* @struct RecentProject
|
||||
* @brief Information about a recently used project
|
||||
*/
|
||||
struct RecentProject {
|
||||
std::string name;
|
||||
std::string filepath;
|
||||
std::string rom_title;
|
||||
std::string last_modified;
|
||||
std::string thumbnail_path; // Optional screenshot
|
||||
int days_ago = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class WelcomeScreen
|
||||
* @brief Modern welcome screen with project grid and quick actions
|
||||
*/
|
||||
class WelcomeScreen {
|
||||
public:
|
||||
WelcomeScreen();
|
||||
|
||||
/**
|
||||
* @brief Show the welcome screen
|
||||
* @param p_open Pointer to open state
|
||||
* @return True if an action was taken (ROM opened, etc.)
|
||||
*/
|
||||
bool Show(bool* p_open);
|
||||
|
||||
/**
|
||||
* @brief Set callback for opening ROM
|
||||
*/
|
||||
void SetOpenRomCallback(std::function<void()> callback) {
|
||||
open_rom_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set callback for creating new project
|
||||
*/
|
||||
void SetNewProjectCallback(std::function<void()> callback) {
|
||||
new_project_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set callback for opening project
|
||||
*/
|
||||
void SetOpenProjectCallback(std::function<void(const std::string&)> callback) {
|
||||
open_project_callback_ = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Refresh recent projects list from the project manager
|
||||
*/
|
||||
void RefreshRecentProjects();
|
||||
|
||||
/**
|
||||
* @brief Update animation time for dynamic effects
|
||||
*/
|
||||
void UpdateAnimations();
|
||||
|
||||
/**
|
||||
* @brief Check if screen should be shown
|
||||
*/
|
||||
bool ShouldShow() const { return !manually_closed_; }
|
||||
|
||||
/**
|
||||
* @brief Mark as manually closed (don't show again this session)
|
||||
*/
|
||||
void MarkManuallyClosed() { manually_closed_ = true; }
|
||||
|
||||
private:
|
||||
void DrawHeader();
|
||||
void DrawQuickActions();
|
||||
void DrawRecentProjects();
|
||||
void DrawProjectCard(const RecentProject& project, int index);
|
||||
void DrawTemplatesSection();
|
||||
void DrawTipsSection();
|
||||
void DrawWhatsNew();
|
||||
|
||||
std::vector<RecentProject> recent_projects_;
|
||||
bool manually_closed_ = false;
|
||||
|
||||
// Callbacks
|
||||
std::function<void()> open_rom_callback_;
|
||||
std::function<void()> new_project_callback_;
|
||||
std::function<void(const std::string&)> open_project_callback_;
|
||||
|
||||
// UI state
|
||||
int selected_template_ = 0;
|
||||
static constexpr int kMaxRecentProjects = 6;
|
||||
|
||||
// Animation state
|
||||
float animation_time_ = 0.0f;
|
||||
float header_glow_ = 0.0f;
|
||||
float card_hover_scale_[6] = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
|
||||
int hovered_card_ = -1;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UI_WELCOME_SCREEN_H_
|
||||
@@ -67,7 +67,7 @@ void Canvas::InitializeDefaults() {
|
||||
selection_.Clear();
|
||||
|
||||
// Initialize palette editor
|
||||
palette_editor_ = std::make_unique<EnhancedPaletteEditor>();
|
||||
palette_editor_ = std::make_unique<PaletteWidget>();
|
||||
|
||||
// Initialize interaction handler
|
||||
interaction_handler_.Initialize(canvas_id_);
|
||||
@@ -1744,7 +1744,7 @@ void Canvas::ShowAdvancedCanvasProperties() {
|
||||
}
|
||||
}
|
||||
|
||||
// Old ShowPaletteManager method removed - now handled by EnhancedPaletteEditor
|
||||
// Old ShowPaletteManager method removed - now handled by PaletteWidget
|
||||
|
||||
void Canvas::ShowScalingControls() {
|
||||
// Use the new modal system if available
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/gui/canvas_utils.h"
|
||||
#include "app/gui/enhanced_palette_editor.h"
|
||||
#include "app/gui/widgets/palette_widget.h"
|
||||
#include "app/gfx/bpp_format_manager.h"
|
||||
#include "app/gui/bpp_format_ui.h"
|
||||
#include "app/gui/canvas/canvas_modals.h"
|
||||
@@ -412,7 +412,7 @@ class Canvas {
|
||||
// Modular configuration and state
|
||||
CanvasConfig config_;
|
||||
CanvasSelection selection_;
|
||||
std::unique_ptr<EnhancedPaletteEditor> palette_editor_;
|
||||
std::unique_ptr<PaletteWidget> palette_editor_;
|
||||
|
||||
// Core canvas state
|
||||
bool is_hovered_ = false;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gfx/performance_dashboard.h"
|
||||
#include "app/gui/enhanced_palette_editor.h"
|
||||
#include "app/gui/widgets/palette_widget.h"
|
||||
#include "app/gui/bpp_format_ui.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/canvas/canvas_modals.h"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gfx/performance_dashboard.h"
|
||||
#include "app/gui/enhanced_palette_editor.h"
|
||||
#include "app/gui/widgets/palette_widget.h"
|
||||
#include "app/gui/bpp_format_ui.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
@@ -421,9 +421,9 @@ void CanvasModals::RenderPaletteEditorModal(const std::string& canvas_id,
|
||||
ImGui::Text("%s %s", ICON_MD_PALETTE, modal_title.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
// Use the existing EnhancedPaletteEditor
|
||||
static std::unique_ptr<gui::EnhancedPaletteEditor> palette_editor =
|
||||
std::make_unique<gui::EnhancedPaletteEditor>();
|
||||
// Use the existing PaletteWidget
|
||||
static std::unique_ptr<gui::PaletteWidget> palette_editor =
|
||||
std::make_unique<gui::PaletteWidget>();
|
||||
|
||||
if (options.palette) {
|
||||
palette_editor->ShowPaletteEditor(*options.palette, modal_title);
|
||||
@@ -454,9 +454,9 @@ void CanvasModals::RenderColorAnalysisModal(const std::string& canvas_id,
|
||||
ImGui::Text("%s %s", ICON_MD_ZOOM_IN, modal_title.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
// Use the existing EnhancedPaletteEditor for color analysis
|
||||
static std::unique_ptr<gui::EnhancedPaletteEditor> palette_editor =
|
||||
std::make_unique<gui::EnhancedPaletteEditor>();
|
||||
// Use the existing PaletteWidget for color analysis
|
||||
static std::unique_ptr<gui::PaletteWidget> palette_editor =
|
||||
std::make_unique<gui::PaletteWidget>();
|
||||
|
||||
if (options.bitmap) {
|
||||
palette_editor->ShowColorAnalysis(*options.bitmap, modal_title);
|
||||
|
||||
@@ -6,15 +6,14 @@ set(
|
||||
app/gui/widgets/collaboration_panel.cc
|
||||
app/gui/canvas.cc
|
||||
app/gui/canvas_utils.cc
|
||||
app/gui/enhanced_palette_editor.cc
|
||||
app/gui/widgets/palette_widget.cc
|
||||
app/gui/input.cc
|
||||
app/gui/style.cc
|
||||
app/gui/color.cc
|
||||
app/gui/theme_manager.cc
|
||||
app/gui/background_renderer.cc
|
||||
app/gui/bpp_format_ui.cc
|
||||
app/gui/widget_id_registry.cc
|
||||
app/gui/widget_auto_register.cc
|
||||
app/gui/widgets/widget_id_registry.cc
|
||||
app/gui/widgets/widget_auto_register.cc
|
||||
# Canvas system components
|
||||
app/gui/canvas/canvas_modals.cc
|
||||
app/gui/canvas/canvas_context_menu.cc
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "util/file_util.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/gui/background_renderer.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
#include "app/core/platform/font_loader.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "enhanced_palette_editor.h"
|
||||
#include "app/gui/widgets/palette_widget.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@@ -11,7 +11,7 @@ namespace gui {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
void EnhancedPaletteEditor::Initialize(Rom* rom) {
|
||||
void PaletteWidget::Initialize(Rom* rom) {
|
||||
rom_ = rom;
|
||||
rom_palettes_loaded_ = false;
|
||||
if (rom_) {
|
||||
@@ -19,7 +19,7 @@ void EnhancedPaletteEditor::Initialize(Rom* rom) {
|
||||
}
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::ShowPaletteEditor(gfx::SnesPalette& palette, const std::string& title) {
|
||||
void PaletteWidget::ShowPaletteEditor(gfx::SnesPalette& palette, const std::string& title) {
|
||||
if (ImGui::BeginPopupModal(title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Enhanced Palette Editor");
|
||||
ImGui::Separator();
|
||||
@@ -63,7 +63,7 @@ void EnhancedPaletteEditor::ShowPaletteEditor(gfx::SnesPalette& palette, const s
|
||||
}
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::ShowROMPaletteManager() {
|
||||
void PaletteWidget::ShowROMPaletteManager() {
|
||||
if (!show_rom_manager_) return;
|
||||
|
||||
if (ImGui::Begin("ROM Palette Manager", &show_rom_manager_)) {
|
||||
@@ -92,7 +92,7 @@ void EnhancedPaletteEditor::ShowROMPaletteManager() {
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::ShowColorAnalysis(const gfx::Bitmap& bitmap, const std::string& title) {
|
||||
void PaletteWidget::ShowColorAnalysis(const gfx::Bitmap& bitmap, const std::string& title) {
|
||||
if (!show_color_analysis_) return;
|
||||
|
||||
if (ImGui::Begin(title.c_str(), &show_color_analysis_)) {
|
||||
@@ -150,7 +150,7 @@ void EnhancedPaletteEditor::ShowColorAnalysis(const gfx::Bitmap& bitmap, const s
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool EnhancedPaletteEditor::ApplyROMPalette(gfx::Bitmap* bitmap, int group_index, int palette_index) {
|
||||
bool PaletteWidget::ApplyROMPalette(gfx::Bitmap* bitmap, int group_index, int palette_index) {
|
||||
if (!bitmap || !rom_palettes_loaded_ ||
|
||||
group_index < 0 || group_index >= static_cast<int>(rom_palette_groups_.size())) {
|
||||
return false;
|
||||
@@ -181,7 +181,7 @@ bool EnhancedPaletteEditor::ApplyROMPalette(gfx::Bitmap* bitmap, int group_index
|
||||
}
|
||||
}
|
||||
|
||||
const gfx::SnesPalette* EnhancedPaletteEditor::GetSelectedROMPalette() const {
|
||||
const gfx::SnesPalette* PaletteWidget::GetSelectedROMPalette() const {
|
||||
if (!rom_palettes_loaded_ || current_group_index_ < 0 ||
|
||||
current_group_index_ >= static_cast<int>(rom_palette_groups_.size())) {
|
||||
return nullptr;
|
||||
@@ -190,11 +190,11 @@ const gfx::SnesPalette* EnhancedPaletteEditor::GetSelectedROMPalette() const {
|
||||
return &rom_palette_groups_[current_group_index_];
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::SavePaletteBackup(const gfx::SnesPalette& palette) {
|
||||
void PaletteWidget::SavePaletteBackup(const gfx::SnesPalette& palette) {
|
||||
backup_palette_ = palette;
|
||||
}
|
||||
|
||||
bool EnhancedPaletteEditor::RestorePaletteBackup(gfx::SnesPalette& palette) {
|
||||
bool PaletteWidget::RestorePaletteBackup(gfx::SnesPalette& palette) {
|
||||
if (backup_palette_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -203,7 +203,7 @@ bool EnhancedPaletteEditor::RestorePaletteBackup(gfx::SnesPalette& palette) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::DrawPaletteGrid(gfx::SnesPalette& palette, int cols) {
|
||||
void PaletteWidget::DrawPaletteGrid(gfx::SnesPalette& palette, int cols) {
|
||||
for (int i = 0; i < static_cast<int>(palette.size()); i++) {
|
||||
if (i % cols != 0) ImGui::SameLine();
|
||||
|
||||
@@ -281,7 +281,7 @@ void EnhancedPaletteEditor::DrawPaletteGrid(gfx::SnesPalette& palette, int cols)
|
||||
}
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::DrawROMPaletteSelector() {
|
||||
void PaletteWidget::DrawROMPaletteSelector() {
|
||||
if (!rom_palettes_loaded_) {
|
||||
LoadROMPalettes();
|
||||
}
|
||||
@@ -323,7 +323,7 @@ void EnhancedPaletteEditor::DrawROMPaletteSelector() {
|
||||
}
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::DrawColorEditControls(gfx::SnesColor& color, int color_index) {
|
||||
void PaletteWidget::DrawColorEditControls(gfx::SnesColor& color, int color_index) {
|
||||
ImVec4 rgba = color.rgb();
|
||||
|
||||
ImGui::PushID(color_index);
|
||||
@@ -359,7 +359,7 @@ void EnhancedPaletteEditor::DrawColorEditControls(gfx::SnesColor& color, int col
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::DrawPaletteAnalysis(const gfx::SnesPalette& palette) {
|
||||
void PaletteWidget::DrawPaletteAnalysis(const gfx::SnesPalette& palette) {
|
||||
ImGui::Text("Palette Information:");
|
||||
ImGui::Text("Size: %zu colors", palette.size());
|
||||
|
||||
@@ -413,7 +413,7 @@ void EnhancedPaletteEditor::DrawPaletteAnalysis(const gfx::SnesPalette& palette)
|
||||
ImGui::ProgressBar(avg_brightness, ImVec2(-1, 0), "Avg");
|
||||
}
|
||||
|
||||
void EnhancedPaletteEditor::LoadROMPalettes() {
|
||||
void PaletteWidget::LoadROMPalettes() {
|
||||
if (!rom_ || rom_palettes_loaded_) return;
|
||||
|
||||
try {
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef YAZE_APP_GUI_ENHANCED_PALETTE_EDITOR_H
|
||||
#define YAZE_APP_GUI_ENHANCED_PALETTE_EDITOR_H
|
||||
#ifndef YAZE_APP_GUI_PALETTE_WIDGET_H
|
||||
#define YAZE_APP_GUI_PALETTE_WIDGET_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@@ -12,11 +12,18 @@ namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
* @brief Enhanced palette editor with ROM integration and analysis tools
|
||||
* @brief Palette widget with ROM integration, analysis tools, and AI tool call support
|
||||
*
|
||||
* This widget provides comprehensive palette editing capabilities including:
|
||||
* - Grid-based color editing with preview
|
||||
* - ROM palette browser and manager
|
||||
* - Color analysis and statistics
|
||||
* - Export/import functionality
|
||||
* - AI agent tool call integration for programmatic palette access
|
||||
*/
|
||||
class EnhancedPaletteEditor {
|
||||
class PaletteWidget {
|
||||
public:
|
||||
EnhancedPaletteEditor() = default;
|
||||
PaletteWidget() = default;
|
||||
|
||||
/**
|
||||
* @brief Initialize the palette editor with ROM data
|
||||
@@ -89,4 +96,4 @@ private:
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_ENHANCED_PALETTE_EDITOR_H
|
||||
#endif // YAZE_APP_GUI_WIDGETS_PALETTE_WIDGET_H
|
||||
Reference in New Issue
Block a user