feat: Revamp EditorManager and UI Components for Enhanced User Experience

- Updated the EditorManager to streamline the editor selection process, integrating new menu items for ROM and project management.
- Enhanced the welcome screen logic to automatically show the editor selection dialog after loading a ROM, improving user flow.
- Introduced new menu options for saving and validating ROMs, creating backups, and accessing ROM information.
- Improved the editor selection dialog with distinct colors for each editor type, enhancing visual clarity and user interaction.
- Added advanced settings and collaboration features in the Agent Chat Widget, including improved connection management and model selection.
- Expanded the welcome screen with additional project card features and tips for user guidance.
This commit is contained in:
scawful
2025-10-05 03:19:36 -04:00
parent 81c67728a9
commit 33242a8803
7 changed files with 590 additions and 325 deletions

View File

@@ -551,45 +551,105 @@ void AgentChatWidget::Draw() {
// Modern tabbed interface
if (ImGui::BeginTabBar("AgentChatTabs", ImGuiTabBarFlags_None)) {
// Main Chat Tab
// Main Chat Tab - with integrated controls
if (ImGui::BeginTabItem(ICON_MD_CHAT " Chat")) {
if (ImGui::BeginTable("#agent_chat_table", 2,
ImGuiTableFlags_BordersInnerV |
ImGuiTableFlags_Resizable |
ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("Panels", ImGuiTableColumnFlags_WidthFixed, 400.0f);
ImGui::TableSetupColumn("Conversation", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::BeginChild("LeftPanels", ImVec2(0, 0), false);
if (show_agent_config_) {
RenderAgentConfigPanel();
}
RenderMultimodalPanel();
if (show_z3ed_commands_) {
RenderZ3EDCommandPanel();
// Connection Status Bar at top
ImGui::BeginChild("ConnectionStatusBar", ImVec2(0, 80), true, ImGuiWindowFlags_NoScrollbar);
{
// Provider selection and connection status in one row
ImGui::Text(ICON_MD_SMART_TOY " AI Provider:");
ImGui::SameLine();
ImGui::SetNextItemWidth(120);
const char* providers[] = { "Mock", "Ollama", "Gemini" };
int current_provider = (agent_config_.ai_provider == "mock") ? 0 :
(agent_config_.ai_provider == "ollama") ? 1 : 2;
if (ImGui::Combo("##provider", &current_provider, providers, 3)) {
agent_config_.ai_provider = (current_provider == 0) ? "mock" :
(current_provider == 1) ? "ollama" : "gemini";
strncpy(agent_config_.provider_buffer, agent_config_.ai_provider.c_str(),
sizeof(agent_config_.provider_buffer) - 1);
}
ImGui::EndChild();
ImGui::TableSetColumnIndex(1);
RenderHistory();
RenderInputBox();
ImGui::EndTable();
ImGui::SameLine();
if (agent_config_.ai_provider == "ollama") {
ImGui::Text(ICON_MD_LINK " Host:");
ImGui::SameLine();
ImGui::SetNextItemWidth(200);
if (ImGui::InputText("##ollama_host", agent_config_.ollama_host_buffer,
sizeof(agent_config_.ollama_host_buffer))) {
agent_config_.ollama_host = agent_config_.ollama_host_buffer;
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_REFRESH " Test Connection")) {
if (toast_manager_) {
toast_manager_->Show("Testing Ollama connection...", ToastType::kInfo);
}
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Test connection to Ollama server");
}
} else if (agent_config_.ai_provider == "gemini") {
ImGui::Text(ICON_MD_KEY " API Key:");
ImGui::SameLine();
ImGui::SetNextItemWidth(250);
if (ImGui::InputText("##gemini_key", agent_config_.gemini_key_buffer,
sizeof(agent_config_.gemini_key_buffer),
ImGuiInputTextFlags_Password)) {
agent_config_.gemini_api_key = agent_config_.gemini_key_buffer;
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_CHECK " Verify")) {
if (toast_manager_) {
toast_manager_->Show("Verifying Gemini API key...", ToastType::kInfo);
}
}
} else {
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "(Mock mode - no external connection needed)");
}
// Second row: Model selection and quick settings
ImGui::Text(ICON_MD_PSYCHOLOGY " Model:");
ImGui::SameLine();
ImGui::SetNextItemWidth(200);
ImGui::InputText("##model", agent_config_.model_buffer, sizeof(agent_config_.model_buffer));
ImGui::SameLine();
ImGui::Checkbox(ICON_MD_VISIBILITY " Reasoning", &agent_config_.show_reasoning);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Show agent's reasoning process in responses");
}
ImGui::SameLine();
ImGui::SetNextItemWidth(80);
ImGui::InputInt("Max Iterations", &agent_config_.max_tool_iterations);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Maximum tool call iterations per request");
}
}
ImGui::EndChild();
ImGui::Spacing();
// Main chat area
RenderHistory();
RenderInputBox();
ImGui::EndTabItem();
}
// Commands Tab - Z3ED and Multimodal
if (ImGui::BeginTabItem(ICON_MD_TERMINAL " Commands")) {
RenderZ3EDCommandPanel();
ImGui::Separator();
RenderMultimodalPanel();
ImGui::EndTabItem();
}
// Collaboration Tab
if (ImGui::BeginTabItem(ICON_MD_PEOPLE " Collaboration")) {
RenderCollaborationPanel();
if (show_rom_sync_) {
ImGui::Separator();
RenderRomSyncPanel();
}
ImGui::Separator();
RenderRomSyncPanel();
if (show_snapshot_preview_) {
ImGui::Separator();
RenderSnapshotPreviewPanel();
@@ -597,6 +657,12 @@ void AgentChatWidget::Draw() {
ImGui::EndTabItem();
}
// Advanced Settings Tab
if (ImGui::BeginTabItem(ICON_MD_SETTINGS " Advanced")) {
RenderAgentConfigPanel();
ImGui::EndTabItem();
}
// Proposals Tab
if (ImGui::BeginTabItem(ICON_MD_PREVIEW " Proposals")) {
RenderProposalManagerPanel();

View File

@@ -60,12 +60,13 @@ enum class EditorType {
kScreen,
kSprite,
kMessage,
kHex,
kSettings,
};
constexpr std::array<const char*, 10> kEditorNames = {
constexpr std::array<const char*, 11> kEditorNames = {
"Assembly", "Dungeon", "Graphics", "Music", "Overworld",
"Palette", "Screen", "Sprite", "Message", "Settings",
"Palette", "Screen", "Sprite", "Message", "Hex", "Settings",
};
/**

View File

@@ -327,10 +327,7 @@ void EditorManager::Initialize(const std::string& filename) {
// Initialize welcome screen callbacks
welcome_screen_.SetOpenRomCallback([this]() {
status_ = LoadRom();
if (status_.ok()) {
show_welcome_screen_ = false;
welcome_screen_manually_closed_ = true;
}
// LoadRom() already handles closing welcome screen and showing editor selection
});
welcome_screen_.SetNewProjectCallback([this]() {
@@ -753,22 +750,66 @@ absl::Status EditorManager::DrawRomSelector() {
void EditorManager::BuildModernMenu() {
menu_builder_.Clear();
// File Menu (no icon to avoid cutoff)
// File Menu - enhanced with ROM features
menu_builder_.BeginMenu("File")
.Item("Open", ICON_MD_FILE_OPEN,
.Item("Open ROM", ICON_MD_FILE_OPEN,
[this]() { status_ = LoadRom(); }, "Ctrl+O")
.Item("Save", ICON_MD_SAVE,
.Item("Save ROM", ICON_MD_SAVE,
[this]() { status_ = SaveRom(); }, "Ctrl+S",
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item("Save As...", ICON_MD_SAVE_AS,
[this]() { popup_manager_->Show("Save As.."); })
[this]() { popup_manager_->Show("Save As.."); },
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Separator()
.BeginSubMenu("Project", ICON_MD_FOLDER_SPECIAL)
.Item("New Project", ICON_MD_CREATE_NEW_FOLDER,
[this]() { status_ = CreateNewProject(); })
.Item("Open Project", ICON_MD_FOLDER_OPEN,
[this]() { status_ = OpenProject(); })
.EndMenu()
.Item("New Project", ICON_MD_CREATE_NEW_FOLDER,
[this]() { status_ = CreateNewProject(); })
.Item("Open Project", ICON_MD_FOLDER_OPEN,
[this]() { status_ = OpenProject(); })
.Item("Save Project", ICON_MD_SAVE,
[this]() { status_ = SaveProject(); },
nullptr,
[this]() { return !current_project_.filepath.empty(); })
.Item("Save Project As...", ICON_MD_SAVE_AS,
[this]() { status_ = SaveProjectAs(); },
nullptr,
[this]() { return !current_project_.filepath.empty(); })
.Separator()
.Item("ROM Information", ICON_MD_INFO,
[this]() { popup_manager_->Show("ROM Info"); },
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item("Create Backup", ICON_MD_BACKUP,
[this]() {
if (current_rom_ && current_rom_->is_loaded()) {
Rom::SaveSettings settings;
settings.backup = true;
settings.filename = current_rom_->filename();
status_ = current_rom_->SaveToFile(settings);
if (status_.ok()) {
toast_manager_.Show("Backup created successfully", ToastType::kSuccess);
}
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item("Validate ROM", ICON_MD_CHECK_CIRCLE,
[this]() {
if (current_rom_ && current_rom_->is_loaded()) {
auto result = current_project_.Validate();
if (result.ok()) {
toast_manager_.Show("ROM validation passed", ToastType::kSuccess);
} else {
toast_manager_.Show("ROM validation failed: " + std::string(result.message()),
ToastType::kError);
}
}
},
nullptr,
[this]() { return current_rom_ && current_rom_->is_loaded(); })
.Separator()
.Item("Settings", ICON_MD_SETTINGS,
[this]() { current_editor_set_->settings_editor_.set_active(true); })
.Separator()
.Item("Quit", ICON_MD_EXIT_TO_APP,
[this]() { quit_ = true; }, "Ctrl+Q")
@@ -790,203 +831,68 @@ void EditorManager::BuildModernMenu() {
.Separator()
.Item("Find", ICON_MD_SEARCH,
[this]() { if (current_editor_) status_ = current_editor_->Find(); }, "Ctrl+F")
.Separator()
.Item("Settings", ICON_MD_SETTINGS,
[this]() { current_editor_set_->settings_editor_.set_active(true); })
.Item("Find in Files", ICON_MD_SEARCH,
[this]() { show_global_search_ = true; }, "Ctrl+Shift+F")
.EndMenu();
// View Menu
// View Menu - editors and layout
menu_builder_.BeginMenu("View")
.BeginSubMenu("Editors", ICON_MD_APPS)
.Item("Show Editor Selection", ICON_MD_DASHBOARD,
[this]() { show_editor_selection_ = true; }, "Ctrl+E")
.Separator()
.Item("Overworld", ICON_MD_MAP,
[this]() { current_editor_set_->overworld_editor_.set_active(true); })
.Item("Dungeon", ICON_MD_CASTLE,
[this]() { current_editor_set_->dungeon_editor_.set_active(true); })
.Item("Graphics", ICON_MD_IMAGE,
[this]() { current_editor_set_->graphics_editor_.set_active(true); })
.Item("Sprite", ICON_MD_TOYS,
[this]() { current_editor_set_->sprite_editor_.set_active(true); })
.Item("Palette", ICON_MD_PALETTE,
[this]() { current_editor_set_->palette_editor_.set_active(true); })
.Item("Message", ICON_MD_CHAT_BUBBLE,
[this]() { current_editor_set_->message_editor_.set_active(true); })
.Item("Music", ICON_MD_MUSIC_NOTE,
[this]() { current_editor_set_->music_editor_.set_active(true); })
.EndMenu()
.Item("Editor Selection", ICON_MD_DASHBOARD,
[this]() { show_editor_selection_ = true; }, "Ctrl+E")
.Separator()
.Item("Overworld", ICON_MD_MAP,
[this]() { current_editor_set_->overworld_editor_.set_active(true); }, "Ctrl+1")
.Item("Dungeon", ICON_MD_CASTLE,
[this]() { current_editor_set_->dungeon_editor_.set_active(true); }, "Ctrl+2")
.Item("Graphics", ICON_MD_IMAGE,
[this]() { current_editor_set_->graphics_editor_.set_active(true); }, "Ctrl+3")
.Item("Sprites", ICON_MD_TOYS,
[this]() { current_editor_set_->sprite_editor_.set_active(true); }, "Ctrl+4")
.Item("Messages", ICON_MD_CHAT_BUBBLE,
[this]() { current_editor_set_->message_editor_.set_active(true); }, "Ctrl+5")
.Item("Music", ICON_MD_MUSIC_NOTE,
[this]() { current_editor_set_->music_editor_.set_active(true); }, "Ctrl+6")
.Item("Palettes", ICON_MD_PALETTE,
[this]() { current_editor_set_->palette_editor_.set_active(true); }, "Ctrl+7")
.Item("Screens", ICON_MD_TV,
[this]() { current_editor_set_->screen_editor_.set_active(true); }, "Ctrl+8")
.Item("Hex Editor", ICON_MD_DATA_ARRAY,
[this]() { show_memory_editor_ = true; }, "Ctrl+0")
.Separator()
.Item("Save Layout", ICON_MD_SAVE,
[this]() { SaveWorkspaceLayout(); }, "Ctrl+Shift+S")
.Item("Load Layout", ICON_MD_FOLDER_OPEN,
[this]() { LoadWorkspaceLayout(); }, "Ctrl+Shift+O")
.Item("Reset Layout", ICON_MD_RESET_TV,
[this]() { ResetWorkspaceLayout(); })
.Item("Layout Presets", ICON_MD_BOOKMARK,
[this]() { show_layout_presets_ = true; })
.Separator()
.Item("Show All Windows", ICON_MD_VISIBILITY,
[this]() { ShowAllWindows(); })
.Item("Hide All Windows", ICON_MD_VISIBILITY_OFF,
[this]() { HideAllWindows(); })
.Item("Maximize Current", ICON_MD_FULLSCREEN,
[this]() { MaximizeCurrentWindow(); }, "F11")
.EndMenu();
// Tools Menu - developer tools and utilities
menu_builder_.BeginMenu("Tools")
.Item("Welcome Screen", ICON_MD_HOME,
[this]() { show_welcome_screen_ = true; })
.Item("Emulator", ICON_MD_GAMEPAD,
[this]() { show_emulator_ = true; },
nullptr,
nullptr,
[this]() { return show_emulator_; })
.Item("Command Palette", ICON_MD_TERMINAL,
[this]() { show_command_palette_ = true; }, "Ctrl+Shift+P")
#ifdef YAZE_WITH_GRPC
.Separator()
.Item("AI Agent Editor", ICON_MD_SMART_TOY,
[this]() { agent_editor_.SetChatActive(true); }, "Ctrl+Shift+A",
nullptr,
[this]() { return agent_editor_.IsChatActive(); })
#endif
.EndMenu();
// Workspace Menu
menu_builder_.BeginMenu("Workspace")
.BeginSubMenu("Sessions", ICON_MD_TAB)
.Item("New Session", ICON_MD_ADD,
[this]() { CreateNewSession(); }, "Ctrl+Shift+N")
.Item("Duplicate Session", ICON_MD_CONTENT_COPY,
[this]() { DuplicateCurrentSession(); },
nullptr, [this]() { return current_rom_ != nullptr; })
.Item("Close Session", ICON_MD_CLOSE,
[this]() { CloseCurrentSession(); }, "Ctrl+Shift+W",
[this]() { return sessions_.size() > 1; })
.Separator()
.Item("Session Switcher", ICON_MD_SWITCH_ACCOUNT,
[this]() { show_session_switcher_ = true; }, "Ctrl+Tab",
[this]() { return sessions_.size() > 1; })
.Item("Session Manager", ICON_MD_VIEW_LIST,
[this]() { show_session_manager_ = true; })
.EndMenu()
.Separator()
.BeginSubMenu("Layout", ICON_MD_DASHBOARD)
.Item("Save Layout", ICON_MD_SAVE,
[this]() { SaveWorkspaceLayout(); }, "Ctrl+Shift+S")
.Item("Load Layout", ICON_MD_FOLDER_OPEN,
[this]() { LoadWorkspaceLayout(); }, "Ctrl+Shift+O")
.Item("Reset Layout", ICON_MD_RESET_TV,
[this]() { ResetWorkspaceLayout(); })
.Separator()
.Item("Layout Presets", ICON_MD_BOOKMARK,
[this]() { show_layout_presets_ = true; })
.EndMenu()
.BeginSubMenu("Windows", ICON_MD_WINDOW)
.Item("Show All", ICON_MD_VISIBILITY,
[this]() { ShowAllWindows(); })
.Item("Hide All", ICON_MD_VISIBILITY_OFF,
[this]() { HideAllWindows(); })
.Separator()
.Item("Maximize Current", ICON_MD_FULLSCREEN,
[this]() { MaximizeCurrentWindow(); }, "F11")
.Item("Restore All", ICON_MD_FULLSCREEN_EXIT,
[this]() { RestoreAllWindows(); })
.Separator()
.Item("Close All Floating", ICON_MD_CLOSE_FULLSCREEN,
[this]() { CloseAllFloatingWindows(); })
.EndMenu()
.Item("Emulator", ICON_MD_GAMEPAD,
[this]() { show_emulator_ = true; },
nullptr, nullptr,
[this]() { return show_emulator_; })
.Separator()
.Item("Performance Dashboard", ICON_MD_SPEED,
[this]() { show_performance_dashboard_ = true; }, "Ctrl+Shift+P")
.EndMenu();
#ifdef YAZE_WITH_GRPC
// AI Agent Menu
menu_builder_.BeginMenu("Agent")
.Item("Open Agent Chat", ICON_MD_CHAT,
[this]() { agent_editor_.SetChatActive(true); }, "Ctrl+Shift+A")
.Separator()
.BeginSubMenu("Vision & Multimodal", ICON_MD_CAMERA)
.Item("Capture Active Editor", ICON_MD_SCREENSHOT,
[this]() {
std::filesystem::path output;
AgentEditor::CaptureConfig config;
config.mode = AgentEditor::CaptureConfig::CaptureMode::kActiveEditor;
status_ = agent_editor_.CaptureSnapshot(&output, config);
})
.Item("Capture Full Window", ICON_MD_FULLSCREEN,
[this]() {
std::filesystem::path output;
AgentEditor::CaptureConfig config;
config.mode = AgentEditor::CaptureConfig::CaptureMode::kFullWindow;
status_ = agent_editor_.CaptureSnapshot(&output, config);
})
.EndMenu()
.Separator()
.Item("Proposal Drawer", ICON_MD_RATE_REVIEW,
[this]() { show_proposal_drawer_ = !show_proposal_drawer_; },
nullptr, nullptr,
[this]() { return show_proposal_drawer_; })
.EndMenu();
// Collaboration Menu
menu_builder_.BeginMenu("Network")
.BeginSubMenu("Session", ICON_MD_MEETING_ROOM)
.Item("Host Session", ICON_MD_ADD_CIRCLE,
[this]() {
auto result = agent_editor_.HostSession("New Session");
if (result.ok()) {
toast_manager_.Show("Hosted session: " + result->session_name,
ToastType::kSuccess);
} else {
toast_manager_.Show("Failed to host session: " + std::string(result.status().message()),
ToastType::kError);
}
})
.Item("Join Session", ICON_MD_LOGIN,
[this]() { popup_manager_->Show("Join Collaboration Session"); })
.Separator()
.Item("Leave Session", ICON_MD_LOGOUT,
[this]() {
status_ = agent_editor_.LeaveSession();
if (status_.ok()) {
toast_manager_.Show("Left collaboration session", ToastType::kInfo);
}
},
nullptr,
[this]() { return agent_editor_.IsInSession(); })
.Item("Refresh Session", ICON_MD_REFRESH,
[this]() {
auto result = agent_editor_.RefreshSession();
if (result.ok()) {
toast_manager_.Show("Session refreshed: " + std::to_string(result->participants.size()) + " participants",
ToastType::kSuccess);
}
},
nullptr,
[this]() { return agent_editor_.IsInSession(); })
.EndMenu()
.Separator()
.BeginSubMenu("Server", ICON_MD_CLOUD)
.Item("Connect to Server", ICON_MD_CLOUD_UPLOAD,
[this]() { popup_manager_->Show("Connect to Server"); })
.Item("Disconnect", ICON_MD_CLOUD_OFF,
[this]() {
agent_editor_.DisconnectFromServer();
toast_manager_.Show("Disconnected from server", ToastType::kInfo);
},
nullptr,
[this]() { return agent_editor_.IsConnectedToServer(); })
.EndMenu()
.Separator()
.BeginSubMenu("Mode", ICON_MD_SETTINGS_ETHERNET)
.Item("Local (Filesystem)", ICON_MD_FOLDER,
[this]() { /* Set local mode */ },
nullptr, nullptr,
[this]() { return agent_editor_.GetCurrentMode() == AgentEditor::CollaborationMode::kLocal; })
.Item("Network (WebSocket)", ICON_MD_WIFI,
[this]() { /* Set network mode */ },
nullptr, nullptr,
[this]() { return agent_editor_.GetCurrentMode() == AgentEditor::CollaborationMode::kNetwork; })
.EndMenu()
.EndMenu();
#endif
// Debug Menu
menu_builder_.BeginMenu("Debug")
[this]() { show_performance_dashboard_ = true; })
#ifdef YAZE_ENABLE_TESTING
.Item("Test Dashboard", ICON_MD_SCIENCE,
[this]() { show_test_dashboard_ = true; }, "Ctrl+T")
.Separator()
#endif
.Item("Memory Editor", ICON_MD_MEMORY,
[this]() { show_memory_editor_ = true; })
.Item("Assembly Editor", ICON_MD_CODE,
[this]() { show_asm_editor_ = true; })
.Separator()
.Item("ImGui Demo", ICON_MD_HELP,
[this]() { show_imgui_demo_ = true; },
@@ -996,13 +902,114 @@ void EditorManager::BuildModernMenu() {
[this]() { show_imgui_metrics_ = true; },
nullptr, nullptr,
[this]() { return show_imgui_metrics_; })
#ifdef YAZE_WITH_GRPC
.EndMenu();
// Session Menu - workspace session management
menu_builder_.BeginMenu("Session")
.Item("New Session", ICON_MD_ADD,
[this]() { CreateNewSession(); }, "Ctrl+Shift+N")
.Item("Duplicate Session", ICON_MD_CONTENT_COPY,
[this]() { DuplicateCurrentSession(); },
nullptr, [this]() { return current_rom_ != nullptr; })
.Item("Close Session", ICON_MD_CLOSE,
[this]() { CloseCurrentSession(); }, "Ctrl+Shift+W",
[this]() { return sessions_.size() > 1; })
.Separator()
.Item("Agent Chat", ICON_MD_CHAT,
[this]() { agent_editor_.ToggleChat(); },
nullptr, nullptr,
.Item("Session Switcher", ICON_MD_SWITCH_ACCOUNT,
[this]() { show_session_switcher_ = true; }, "Ctrl+Tab",
[this]() { return sessions_.size() > 1; })
.Item("Session Manager", ICON_MD_VIEW_LIST,
[this]() { show_session_manager_ = true; })
.EndMenu();
#ifdef YAZE_WITH_GRPC
// Collaboration Menu - combined Agent + Network features
menu_builder_.BeginMenu("Collaborate")
.Item("AI Agent Chat", ICON_MD_SMART_TOY,
[this]() { agent_editor_.SetChatActive(true); }, "Ctrl+Shift+A",
nullptr,
[this]() { return agent_editor_.IsChatActive(); })
.Item("Proposal Drawer", ICON_MD_RATE_REVIEW,
[this]() { show_proposal_drawer_ = !show_proposal_drawer_; },
nullptr, nullptr,
[this]() { return show_proposal_drawer_; })
.Separator()
.Item("Host Session", ICON_MD_ADD_CIRCLE,
[this]() {
auto result = agent_editor_.HostSession("New Session");
if (result.ok()) {
toast_manager_.Show("Hosted session: " + result->session_name,
ToastType::kSuccess);
} else {
toast_manager_.Show("Failed to host session: " + std::string(result.status().message()),
ToastType::kError);
}
})
.Item("Join Session", ICON_MD_LOGIN,
[this]() { popup_manager_->Show("Join Collaboration Session"); })
.Item("Leave Session", ICON_MD_LOGOUT,
[this]() {
status_ = agent_editor_.LeaveSession();
if (status_.ok()) {
toast_manager_.Show("Left collaboration session", ToastType::kInfo);
}
},
nullptr,
[this]() { return agent_editor_.IsInSession(); })
.Item("Refresh Session", ICON_MD_REFRESH,
[this]() {
auto result = agent_editor_.RefreshSession();
if (result.ok()) {
toast_manager_.Show("Session refreshed: " + std::to_string(result->participants.size()) + " participants",
ToastType::kSuccess);
}
},
nullptr,
[this]() { return agent_editor_.IsInSession(); })
.Separator()
.Item("Connect to Server", ICON_MD_CLOUD_UPLOAD,
[this]() { popup_manager_->Show("Connect to Server"); })
.Item("Disconnect from Server", ICON_MD_CLOUD_OFF,
[this]() {
agent_editor_.DisconnectFromServer();
toast_manager_.Show("Disconnected from server", ToastType::kInfo);
},
nullptr,
[this]() { return agent_editor_.IsConnectedToServer(); })
.Separator()
.Item("Capture Active Editor", ICON_MD_SCREENSHOT,
[this]() {
std::filesystem::path output;
AgentEditor::CaptureConfig config;
config.mode = AgentEditor::CaptureConfig::CaptureMode::kActiveEditor;
status_ = agent_editor_.CaptureSnapshot(&output, config);
})
.Item("Capture Full Window", ICON_MD_FULLSCREEN,
[this]() {
std::filesystem::path output;
AgentEditor::CaptureConfig config;
config.mode = AgentEditor::CaptureConfig::CaptureMode::kFullWindow;
status_ = agent_editor_.CaptureSnapshot(&output, config);
})
.Separator()
.Item("Local Mode", ICON_MD_FOLDER,
[this]() { /* Set local mode */ },
nullptr, nullptr,
[this]() { return agent_editor_.GetCurrentMode() == AgentEditor::CollaborationMode::kLocal; })
.Item("Network Mode", ICON_MD_WIFI,
[this]() { /* Set network mode */ },
nullptr, nullptr,
[this]() { return agent_editor_.GetCurrentMode() == AgentEditor::CollaborationMode::kNetwork; })
.EndMenu();
#endif
// Debug Menu
menu_builder_.BeginMenu("Debug")
.Item("Memory Editor", ICON_MD_MEMORY,
[this]() { show_memory_editor_ = true; })
.Item("Assembly Editor", ICON_MD_CODE,
[this]() { show_asm_editor_ = true; })
.EndMenu();
// Help Menu
@@ -1842,9 +1849,8 @@ absl::Status EditorManager::LoadRom() {
// Hide welcome screen when ROM is successfully loaded - don't reset manual close state
show_welcome_screen_ = false;
// Optionally show editor selection dialog after ROM loads
// (Can be disabled via settings if users prefer to manually select editors)
// show_editor_selection_ = true; // Uncomment to auto-show after ROM load
// Show editor selection dialog after ROM loads
show_editor_selection_ = true;
return absl::OkStatus();
}
@@ -1967,8 +1973,9 @@ absl::Status EditorManager::OpenRomOrProject(const std::string& filename) {
current_editor_set_ = &session.editors;
RETURN_IF_ERROR(LoadAssets());
// Reset welcome screen state when ROM is loaded
welcome_screen_manually_closed_ = false;
// Hide welcome screen and show editor selection when ROM is loaded
show_welcome_screen_ = false;
show_editor_selection_ = true;
}
return absl::OkStatus();
}
@@ -2044,6 +2051,10 @@ absl::Status EditorManager::OpenProject() {
}
RETURN_IF_ERROR(LoadAssets());
// Hide welcome screen and show editor selection when project ROM is loaded
show_welcome_screen_ = false;
show_editor_selection_ = true;
}
// Apply workspace settings

View File

@@ -14,41 +14,51 @@ namespace yaze {
namespace editor {
EditorSelectionDialog::EditorSelectionDialog() {
// Initialize editor metadata
// Initialize editor metadata with distinct colors
editors_ = {
{EditorType::kOverworld, "Overworld", ICON_MD_MAP,
"Edit overworld maps, entrances, and properties", "Ctrl+1", false, true},
"Edit overworld maps, entrances, and properties", "Ctrl+1", false, true,
ImVec4(0.133f, 0.545f, 0.133f, 1.0f)}, // Hyrule green
{EditorType::kDungeon, "Dungeon", ICON_MD_CASTLE,
"Design dungeon rooms, layouts, and mechanics", "Ctrl+2", false, true},
"Design dungeon rooms, layouts, and mechanics", "Ctrl+2", false, true,
ImVec4(0.502f, 0.0f, 0.502f, 1.0f)}, // Ganon purple
{EditorType::kGraphics, "Graphics", ICON_MD_PALETTE,
"Modify tiles, palettes, and graphics sets", "Ctrl+3", false, true},
"Modify tiles, palettes, and graphics sets", "Ctrl+3", false, true,
ImVec4(1.0f, 0.843f, 0.0f, 1.0f)}, // Triforce gold
{EditorType::kSprite, "Sprites", ICON_MD_EMOJI_EMOTIONS,
"Edit sprite graphics and properties", "Ctrl+4", false, true},
"Edit sprite graphics and properties", "Ctrl+4", false, true,
ImVec4(1.0f, 0.647f, 0.0f, 1.0f)}, // Spirit orange
{EditorType::kMessage, "Messages", ICON_MD_CHAT_BUBBLE,
"Edit dialogue, signs, and text", "Ctrl+5", false, true},
"Edit dialogue, signs, and text", "Ctrl+5", false, true,
ImVec4(0.196f, 0.6f, 0.8f, 1.0f)}, // Master sword blue
{EditorType::kMusic, "Music", ICON_MD_MUSIC_NOTE,
"Configure music and sound effects", "Ctrl+6", false, true},
"Configure music and sound effects", "Ctrl+6", false, true,
ImVec4(0.416f, 0.353f, 0.804f, 1.0f)}, // Shadow purple
{EditorType::kPalette, "Palettes", ICON_MD_COLOR_LENS,
"Edit color palettes and animations", "Ctrl+7", false, true},
"Edit color palettes and animations", "Ctrl+7", false, true,
ImVec4(0.863f, 0.078f, 0.235f, 1.0f)}, // Heart red
{EditorType::kScreen, "Screens", ICON_MD_TV,
"Edit title screen and ending screens", "Ctrl+8", false, true},
"Edit title screen and ending screens", "Ctrl+8", false, true,
ImVec4(0.4f, 0.8f, 1.0f, 1.0f)}, // Sky blue
{EditorType::kAssembly, "Assembly", ICON_MD_CODE,
"Write and edit assembly code", "Ctrl+9", false, false},
"Write and edit assembly code", "Ctrl+9", false, false,
ImVec4(0.8f, 0.8f, 0.8f, 1.0f)}, // Silver
// TODO: Fix this
// {EditorType::kHex, "Hex Editor", ICON_MD_DATA_ARRAY,
// "Direct ROM memory editing", "Ctrl+0", false, true},
{EditorType::kHex, "Hex Editor", ICON_MD_DATA_ARRAY,
"Direct ROM memory editing and comparison", "Ctrl+0", false, true,
ImVec4(0.2f, 0.8f, 0.4f, 1.0f)}, // Matrix green
{EditorType::kSettings, "Settings", ICON_MD_SETTINGS,
"Configure ROM and project settings", "", false, true},
"Configure ROM and project settings", "", false, true,
ImVec4(0.6f, 0.6f, 0.6f, 1.0f)}, // Gray
};
LoadRecentEditors();
@@ -66,13 +76,13 @@ bool EditorSelectionDialog::Show(bool* p_open) {
}
bool editor_selected = false;
bool* window_open = p_open ? p_open : &is_open_;
// Center the dialog
// Set window properties immediately before Begin to prevent them from affecting tooltips
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(900, 600), ImGuiCond_Appearing);
ImGui::SetNextWindowSize(ImVec2(950, 650), ImGuiCond_Appearing); // Slightly larger for better layout
bool* window_open = p_open ? p_open : &is_open_;
if (ImGui::Begin("Editor Selection", window_open,
ImGuiWindowFlags_NoCollapse)) {
DrawWelcomeHeader();
@@ -131,19 +141,26 @@ bool EditorSelectionDialog::Show(bool* p_open) {
}
void EditorSelectionDialog::DrawWelcomeHeader() {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); // Larger font if available
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 header_start = ImGui::GetCursorScreenPos();
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
ICON_MD_EDIT " Select an Editor");
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]); // Large font
// Colorful gradient title
ImVec4 title_color = ImVec4(1.0f, 0.843f, 0.0f, 1.0f); // Triforce gold
ImGui::TextColored(title_color, ICON_MD_EDIT " Select an Editor");
ImGui::PopFont();
ImGui::TextWrapped("Choose an editor to begin working on your ROM. "
// Subtitle with gradient separator
ImVec2 subtitle_pos = ImGui::GetCursorScreenPos();
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f),
"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::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_HISTORY " Recently Used");
ImGui::Spacing();
for (EditorType type : recent_editors_) {
@@ -154,14 +171,20 @@ void EditorSelectionDialog::DrawQuickAccessButtons() {
});
if (it != editors_.end()) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.8f, 0.6f));
// Use editor's theme color for button
ImVec4 color = it->color;
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(color.x * 0.5f, color.y * 0.5f,
color.z * 0.5f, 0.7f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(color.x * 0.7f, color.y * 0.7f,
color.z * 0.7f, 0.9f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, color);
if (ImGui::Button(absl::StrCat(it->icon, " ", it->name).c_str(),
ImVec2(150, 30))) {
ImVec2(150, 35))) {
selected_editor_ = type;
}
ImGui::PopStyleColor();
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", it->description);
@@ -177,44 +200,119 @@ void EditorSelectionDialog::DrawQuickAccessButtons() {
void EditorSelectionDialog::DrawEditorCard(const EditorInfo& info, int index) {
ImGui::PushID(index);
// Card styling
ImVec2 button_size(180, 120);
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
// Card styling with gradients
bool is_recent = std::find(recent_editors_.begin(), recent_editors_.end(),
info.type) != recent_editors_.end();
// Create gradient background
ImVec4 base_color = info.color;
ImU32 color_top = ImGui::GetColorU32(ImVec4(base_color.x * 0.4f, base_color.y * 0.4f,
base_color.z * 0.4f, 0.8f));
ImU32 color_bottom = ImGui::GetColorU32(ImVec4(base_color.x * 0.2f, base_color.y * 0.2f,
base_color.z * 0.2f, 0.9f));
// Draw gradient card background
draw_list->AddRectFilledMultiColor(
cursor_pos,
ImVec2(cursor_pos.x + button_size.x, cursor_pos.y + button_size.y),
color_top, color_top, color_bottom, color_bottom);
// Colored border
ImU32 border_color = is_recent
? ImGui::GetColorU32(ImVec4(base_color.x, base_color.y, base_color.z, 1.0f))
: ImGui::GetColorU32(ImVec4(base_color.x * 0.6f, base_color.y * 0.6f,
base_color.z * 0.6f, 0.7f));
draw_list->AddRect(cursor_pos,
ImVec2(cursor_pos.x + button_size.x, cursor_pos.y + button_size.y),
border_color, 4.0f, 0, is_recent ? 3.0f : 2.0f);
// Recent indicator badge
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));
ImVec2 badge_pos(cursor_pos.x + button_size.x - 25, cursor_pos.y + 5);
draw_list->AddCircleFilled(badge_pos, 12, ImGui::GetColorU32(base_color), 16);
ImGui::SetCursorScreenPos(ImVec2(badge_pos.x - 6, badge_pos.y - 8));
ImGui::TextColored(ImVec4(1, 1, 1, 1), ICON_MD_STAR);
}
// 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;
// Make button transparent (we draw our own background)
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(base_color.x * 0.3f, base_color.y * 0.3f,
base_color.z * 0.3f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(base_color.x * 0.5f, base_color.y * 0.5f,
base_color.z * 0.5f, 0.7f));
ImGui::SetCursorScreenPos(cursor_pos);
bool clicked = ImGui::Button(absl::StrCat("##", info.name).c_str(), button_size);
bool is_hovered = ImGui::IsItemHovered();
ImGui::PopStyleColor(3);
// Draw icon with colored background circle
ImVec2 icon_center(cursor_pos.x + button_size.x / 2, cursor_pos.y + 30);
ImU32 icon_bg = ImGui::GetColorU32(base_color);
draw_list->AddCircleFilled(icon_center, 22, icon_bg, 32);
// Draw icon
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]); // Larger font for icon
ImVec2 icon_size = ImGui::CalcTextSize(info.icon);
ImGui::SetCursorScreenPos(ImVec2(icon_center.x - icon_size.x / 2, icon_center.y - icon_size.y / 2));
ImGui::TextColored(ImVec4(1, 1, 1, 1), "%s", info.icon);
ImGui::PopFont();
// Draw name
ImGui::SetCursorScreenPos(ImVec2(cursor_pos.x + 10, cursor_pos.y + 65));
ImGui::PushTextWrapPos(cursor_pos.x + button_size.x - 10);
ImVec2 name_size = ImGui::CalcTextSize(info.name);
ImGui::SetCursorScreenPos(ImVec2(cursor_pos.x + (button_size.x - name_size.x) / 2,
cursor_pos.y + 65));
ImGui::TextColored(base_color, "%s", info.name);
ImGui::PopTextWrapPos();
// Draw shortcut hint if available
if (info.shortcut && info.shortcut[0]) {
ImGui::SetCursorScreenPos(ImVec2(cursor_pos.x + 10, cursor_pos.y + button_size.y - 20));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.6f, 0.6f, 1.0f));
ImGui::Text("%s", info.shortcut);
ImGui::PopStyleColor();
}
if (is_recent) {
ImGui::PopStyleColor(3);
// Hover glow effect
if (is_hovered) {
ImU32 glow_color = ImGui::GetColorU32(ImVec4(base_color.x, base_color.y, base_color.z, 0.2f));
draw_list->AddRectFilled(cursor_pos,
ImVec2(cursor_pos.x + button_size.x, cursor_pos.y + button_size.y),
glow_color, 4.0f);
}
// Tooltip with description
if (ImGui::IsItemHovered()) {
// Enhanced tooltip with fixed sizing
if (is_hovered) {
// Force tooltip to have a fixed max width to prevent flickering
ImGui::SetNextWindowSize(ImVec2(300, 0), ImGuiCond_Always);
ImGui::BeginTooltip();
ImGui::Text("%s %s", info.icon, info.name);
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); // Medium font
ImGui::TextColored(base_color, "%s %s", info.icon, info.name);
ImGui::PopFont();
ImGui::Separator();
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 280);
ImGui::TextWrapped("%s", info.description);
ImGui::PopTextWrapPos();
if (info.shortcut && info.shortcut[0]) {
ImGui::Spacing();
ImGui::TextDisabled("Shortcut: %s", info.shortcut);
ImGui::TextColored(base_color, ICON_MD_KEYBOARD " %s", info.shortcut);
}
if (is_recent) {
ImGui::Spacing();
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_STAR " Recently used");
}
ImGui::EndTooltip();
}
// Recent indicator
if (is_recent) {
ImGui::SameLine();
ImGui::TextDisabled(ICON_MD_STAR);
if (clicked) {
selected_editor_ = info.type;
}
ImGui::PopID();

View File

@@ -6,6 +6,7 @@
#include <functional>
#include "app/editor/editor.h"
#include "imgui/imgui.h"
namespace yaze {
namespace editor {
@@ -22,6 +23,7 @@ struct EditorInfo {
const char* shortcut;
bool recently_used = false;
bool requires_rom = true;
ImVec4 color = ImVec4(0.5f, 0.5f, 0.5f, 1.0f); // Theme color for this editor
};
/**

View File

@@ -179,15 +179,22 @@ bool WelcomeScreen::Show(bool* p_open) {
};
TriforceConfig triforce_configs[] = {
{0.10f, 0.15f, 32.0f, 0.035f, 60.0f}, // Top left corner
{0.90f, 0.18f, 28.0f, 0.028f, 55.0f}, // Top right corner
{0.08f, 0.82f, 24.0f, 0.022f, 50.0f}, // Bottom left
{0.92f, 0.78f, 28.0f, 0.030f, 55.0f}, // Bottom right
{0.18f, 0.45f, 24.0f, 0.020f, 45.0f}, // Mid left
{0.82f, 0.52f, 24.0f, 0.020f, 45.0f}, // Mid right
{0.50f, 0.88f, 20.0f, 0.018f, 40.0f}, // Bottom center
{0.30f, 0.25f, 20.0f, 0.015f, 40.0f}, // Upper mid-left
{0.70f, 0.28f, 20.0f, 0.015f, 40.0f}, // Upper mid-right
{0.08f, 0.12f, 32.0f, 0.065f, 80.0f}, // Top left corner
{0.92f, 0.15f, 30.0f, 0.055f, 75.0f}, // Top right corner
{0.06f, 0.85f, 28.0f, 0.050f, 70.0f}, // Bottom left
{0.94f, 0.82f, 30.0f, 0.058f, 75.0f}, // Bottom right
{0.15f, 0.48f, 26.0f, 0.048f, 65.0f}, // Mid left
{0.85f, 0.52f, 26.0f, 0.048f, 65.0f}, // Mid right
{0.50f, 0.92f, 24.0f, 0.040f, 60.0f}, // Bottom center
{0.28f, 0.22f, 22.0f, 0.038f, 55.0f}, // Upper mid-left
{0.72f, 0.25f, 22.0f, 0.038f, 55.0f}, // Upper mid-right
{0.50f, 0.08f, 28.0f, 0.052f, 70.0f}, // Top center
{0.22f, 0.65f, 20.0f, 0.035f, 50.0f}, // Mid-lower left
{0.78f, 0.68f, 20.0f, 0.035f, 50.0f}, // Mid-lower right
{0.12f, 0.35f, 18.0f, 0.030f, 45.0f}, // Upper-mid left
{0.88f, 0.38f, 18.0f, 0.030f, 45.0f}, // Upper-mid right
{0.38f, 0.75f, 16.0f, 0.028f, 40.0f}, // Lower left-center
{0.62f, 0.77f, 16.0f, 0.028f, 40.0f}, // Lower right-center
};
// Initialize base positions on first frame
@@ -201,42 +208,65 @@ bool WelcomeScreen::Show(bool* p_open) {
triforce_positions_initialized_ = true;
}
// Update triforce positions based on mouse interaction
// Update triforce positions based on mouse interaction + floating animation
for (int i = 0; i < kNumTriforces; ++i) {
// Update base position in case window moved/resized
float base_x = window_pos.x + window_size.x * triforce_configs[i].x_pct;
float base_y = window_pos.y + window_size.y * triforce_configs[i].y_pct;
triforce_base_positions_[i] = ImVec2(base_x, base_y);
// Add floating animation using sine waves with unique frequencies per triforce
float time_offset = i * 0.5f; // Offset each triforce's animation
float float_speed_x = (0.6f + (i % 4) * 0.25f) * triforce_speed_multiplier_; // Apply speed multiplier
float float_speed_y = (0.5f + ((i + 1) % 4) * 0.2f) * triforce_speed_multiplier_;
float float_amount_x = (35.0f + (i % 3) * 20.0f) * triforce_size_multiplier_; // Apply size multiplier
float float_amount_y = (40.0f + ((i + 1) % 3) * 25.0f) * triforce_size_multiplier_;
// Create complex orbital/figure-8 motion with more variation
float float_x = std::sin(animation_time_ * float_speed_x + time_offset) * float_amount_x;
float float_y = std::cos(animation_time_ * float_speed_y + time_offset * 1.5f) * float_amount_y;
// Add secondary wave for more complex movement
float_x += std::cos(animation_time_ * float_speed_x * 0.7f + time_offset * 2.0f) * (float_amount_x * 0.3f);
float_y += std::sin(animation_time_ * float_speed_y * 0.6f + time_offset * 1.8f) * (float_amount_y * 0.3f);
// Calculate distance from mouse
float dx = triforce_base_positions_[i].x - mouse_pos.x;
float dy = triforce_base_positions_[i].y - mouse_pos.y;
float dist = std::sqrt(dx * dx + dy * dy);
// Calculate repulsion offset
// Calculate repulsion offset with stronger effect
ImVec2 target_pos = triforce_base_positions_[i];
float repel_radius = 150.0f; // Start repelling within this radius
float repel_radius = 200.0f; // Larger radius for more visible interaction
if (dist < repel_radius && dist > 0.1f) {
// Add floating motion to base position
target_pos.x += float_x;
target_pos.y += float_y;
// Apply mouse repulsion if enabled
if (triforce_mouse_repel_enabled_ && dist < repel_radius && dist > 0.1f) {
// Normalize direction away from mouse
float dir_x = dx / dist;
float dir_y = dy / dist;
// Stronger repulsion when closer
float repel_strength = (1.0f - dist / repel_radius) * triforce_configs[i].repel_distance;
// Much stronger repulsion when closer with exponential falloff
float normalized_dist = dist / repel_radius;
float repel_strength = (1.0f - normalized_dist * normalized_dist) * triforce_configs[i].repel_distance;
target_pos.x += dir_x * repel_strength;
target_pos.y += dir_y * repel_strength;
}
// Smooth interpolation to target position
float lerp_speed = 6.0f * ImGui::GetIO().DeltaTime;
// Smooth interpolation to target position (faster response)
float lerp_speed = 8.0f * ImGui::GetIO().DeltaTime;
triforce_positions_[i].x += (target_pos.x - triforce_positions_[i].x) * lerp_speed;
triforce_positions_[i].y += (target_pos.y - triforce_positions_[i].y) * lerp_speed;
// Draw at current position
// Draw at current position with alpha multiplier
float adjusted_alpha = triforce_configs[i].alpha * triforce_alpha_multiplier_;
float adjusted_size = triforce_configs[i].size * triforce_size_multiplier_;
DrawTriforceBackground(bg_draw_list, triforce_positions_[i],
triforce_configs[i].size, triforce_configs[i].alpha, 0.0f);
adjusted_size, adjusted_alpha, 0.0f);
}
DrawHeader();
@@ -318,6 +348,57 @@ bool WelcomeScreen::Show(bool* p_open) {
ImGui::Dummy(ImVec2(0, 5));
DrawTipsSection();
// Triforce animation settings panel
ImGui::SameLine(ImGui::GetWindowWidth() - 50);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 5);
if (ImGui::SmallButton(show_triforce_settings_ ? ICON_MD_CLOSE : ICON_MD_TUNE)) {
show_triforce_settings_ = !show_triforce_settings_;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Triforce Animation Settings");
}
if (show_triforce_settings_) {
ImGui::Separator();
ImGui::Spacing();
ImGui::BeginChild("TriforceSettings", ImVec2(0, 120), true, ImGuiWindowFlags_NoScrollbar);
{
ImGui::TextColored(kTriforceGold, ICON_MD_AUTO_AWESOME " Triforce Animation");
ImGui::Spacing();
ImGui::Columns(2, nullptr, false);
// Left column
ImGui::Text(ICON_MD_OPACITY " Visibility");
ImGui::SetNextItemWidth(-1);
ImGui::SliderFloat("##visibility", &triforce_alpha_multiplier_, 0.0f, 3.0f, "%.1fx");
ImGui::Text(ICON_MD_SPEED " Speed");
ImGui::SetNextItemWidth(-1);
ImGui::SliderFloat("##speed", &triforce_speed_multiplier_, 0.1f, 3.0f, "%.1fx");
ImGui::NextColumn();
// Right column
ImGui::Text(ICON_MD_ASPECT_RATIO " Size");
ImGui::SetNextItemWidth(-1);
ImGui::SliderFloat("##size", &triforce_size_multiplier_, 0.5f, 2.0f, "%.1fx");
ImGui::Checkbox(ICON_MD_MOUSE " Mouse Interaction", &triforce_mouse_repel_enabled_);
ImGui::Columns(1);
ImGui::Spacing();
if (ImGui::SmallButton(ICON_MD_REFRESH " Reset to Defaults")) {
triforce_alpha_multiplier_ = 1.0f;
triforce_speed_multiplier_ = 1.0f;
triforce_size_multiplier_ = 1.0f;
triforce_mouse_repel_enabled_ = true;
}
}
ImGui::EndChild();
}
}
ImGui::End();
@@ -489,8 +570,8 @@ void WelcomeScreen::DrawRecentProjects() {
}
// Grid layout for project cards
float card_width = 220.0f;
float card_height = 140.0f;
float card_width = 260.0f; // Increased from 220.0f
float card_height = 120.0f; // Reduced from 140.0f
int columns = std::max(1, (int)(ImGui::GetContentRegionAvail().x / (card_width + 15)));
for (size_t i = 0; i < recent_projects_.size(); ++i) {
@@ -504,7 +585,7 @@ void WelcomeScreen::DrawRecentProjects() {
void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
ImGui::BeginGroup();
ImVec2 card_size(220, 140);
ImVec2 card_size(260, 120); // Reduced height from 140 to 120
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
// Subtle hover scale (only on actual hover, no animation)
@@ -555,39 +636,38 @@ void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
}
// Draw content
ImVec2 content_pos(cursor_pos.x + 15, cursor_pos.y + 15);
ImVec2 content_pos(cursor_pos.x + 12, cursor_pos.y + 12);
// Icon with colored background circle
ImVec2 icon_center(content_pos.x + 20, content_pos.y + 20);
// Icon with colored background circle (smaller and centered)
ImVec2 icon_center(content_pos.x + 16, content_pos.y + 16);
ImU32 icon_bg = ImGui::GetColorU32(border_color_base);
draw_list->AddCircleFilled(icon_center, 25, icon_bg, 32);
draw_list->AddCircleFilled(icon_center, 18, icon_bg, 32); // Reduced from 25 to 18
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 8));
// Center the icon properly
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); // Medium font for icon
ImVec2 icon_size = ImGui::CalcTextSize(ICON_MD_VIDEOGAME_ASSET);
ImGui::SetCursorScreenPos(ImVec2(icon_center.x - icon_size.x / 2, icon_center.y - icon_size.y / 2));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1));
ImGui::Text(ICON_MD_VIDEOGAME_ASSET);
ImGui::PopStyleColor();
ImGui::PopFont();
// Project name
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 50, content_pos.y + 15));
ImGui::PushTextWrapPos(cursor_pos.x + card_size.x - 15);
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 40, content_pos.y + 12));
ImGui::PushTextWrapPos(cursor_pos.x + card_size.x - 12);
ImGui::TextColored(kTriforceGold, "%s", project.name.c_str());
ImGui::PopTextWrapPos();
// ROM title
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 55));
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 45));
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));
// Path in card (condensed)
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 70));
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);
if (short_path.length() > 38) {
short_path = "..." + short_path.substr(short_path.length() - 35);
}
ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str());
ImGui::PopStyleColor();
@@ -598,8 +678,8 @@ void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
ImGui::TextColored(kMasterSwordBlue, ICON_MD_INFO " Project Details");
ImGui::Separator();
ImGui::Text("Name: %s", project.name.c_str());
ImGui::Text("ROM: %s", project.rom_title.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();
@@ -666,8 +746,8 @@ void WelcomeScreen::DrawTemplatesSection() {
void WelcomeScreen::DrawTipsSection() {
// Static tip (or could rotate based on session start time rather than animation)
const char* tips[] = {
"Press Ctrl+P to open the command palette",
"Use z3ed agent for AI-powered ROM editing",
"Press Ctrl+Shift+P to open the command palette",
"Use z3ed agent for AI-powered ROM editing (Ctrl+Shift+A)",
"Enable ZSCustomOverworld in Debug menu for expanded features",
"Check the Performance Dashboard for optimization insights",
"Collaborate in real-time with yaze-server"
@@ -693,7 +773,7 @@ void WelcomeScreen::DrawWhatsNew() {
ImGui::Spacing();
// Version badge (no animation)
ImGui::TextColored(kMasterSwordBlue, ICON_MD_VERIFIED " yaze v0.2.0-alpha");
ImGui::TextColored(kMasterSwordBlue, ICON_MD_VERIFIED "yaze v%s", YAZE_VERSION_STRING);
ImGui::Spacing();
// Feature list with icons and colors

View File

@@ -106,10 +106,17 @@ class WelcomeScreen {
int hovered_card_ = -1;
// Interactive triforce positions (smooth interpolation)
static constexpr int kNumTriforces = 9;
static constexpr int kNumTriforces = 16;
ImVec2 triforce_positions_[kNumTriforces] = {};
ImVec2 triforce_base_positions_[kNumTriforces] = {};
bool triforce_positions_initialized_ = false;
// Triforce animation settings
bool show_triforce_settings_ = false;
float triforce_alpha_multiplier_ = 1.0f;
float triforce_speed_multiplier_ = 1.0f;
float triforce_size_multiplier_ = 1.0f;
bool triforce_mouse_repel_enabled_ = true;
};
} // namespace editor