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 // Modern tabbed interface
if (ImGui::BeginTabBar("AgentChatTabs", ImGuiTabBarFlags_None)) { if (ImGui::BeginTabBar("AgentChatTabs", ImGuiTabBarFlags_None)) {
// Main Chat Tab // Main Chat Tab - with integrated controls
if (ImGui::BeginTabItem(ICON_MD_CHAT " Chat")) { if (ImGui::BeginTabItem(ICON_MD_CHAT " Chat")) {
if (ImGui::BeginTable("#agent_chat_table", 2, // Connection Status Bar at top
ImGuiTableFlags_BordersInnerV | ImGui::BeginChild("ConnectionStatusBar", ImVec2(0, 80), true, ImGuiWindowFlags_NoScrollbar);
ImGuiTableFlags_Resizable | {
ImGuiTableFlags_SizingStretchProp)) { // Provider selection and connection status in one row
ImGui::TableSetupColumn("Panels", ImGuiTableColumnFlags_WidthFixed, 400.0f); ImGui::Text(ICON_MD_SMART_TOY " AI Provider:");
ImGui::TableSetupColumn("Conversation", ImGuiTableColumnFlags_WidthStretch); ImGui::SameLine();
ImGui::SetNextItemWidth(120);
ImGui::TableNextRow(); const char* providers[] = { "Mock", "Ollama", "Gemini" };
int current_provider = (agent_config_.ai_provider == "mock") ? 0 :
ImGui::TableSetColumnIndex(0); (agent_config_.ai_provider == "ollama") ? 1 : 2;
ImGui::BeginChild("LeftPanels", ImVec2(0, 0), false); if (ImGui::Combo("##provider", &current_provider, providers, 3)) {
agent_config_.ai_provider = (current_provider == 0) ? "mock" :
if (show_agent_config_) { (current_provider == 1) ? "ollama" : "gemini";
RenderAgentConfigPanel(); strncpy(agent_config_.provider_buffer, agent_config_.ai_provider.c_str(),
} sizeof(agent_config_.provider_buffer) - 1);
RenderMultimodalPanel();
if (show_z3ed_commands_) {
RenderZ3EDCommandPanel();
} }
ImGui::EndChild(); ImGui::SameLine();
if (agent_config_.ai_provider == "ollama") {
ImGui::TableSetColumnIndex(1); ImGui::Text(ICON_MD_LINK " Host:");
RenderHistory(); ImGui::SameLine();
RenderInputBox(); ImGui::SetNextItemWidth(200);
ImGui::EndTable(); 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(); ImGui::EndTabItem();
} }
// Collaboration Tab // Collaboration Tab
if (ImGui::BeginTabItem(ICON_MD_PEOPLE " Collaboration")) { if (ImGui::BeginTabItem(ICON_MD_PEOPLE " Collaboration")) {
RenderCollaborationPanel(); RenderCollaborationPanel();
if (show_rom_sync_) { ImGui::Separator();
ImGui::Separator(); RenderRomSyncPanel();
RenderRomSyncPanel();
}
if (show_snapshot_preview_) { if (show_snapshot_preview_) {
ImGui::Separator(); ImGui::Separator();
RenderSnapshotPreviewPanel(); RenderSnapshotPreviewPanel();
@@ -597,6 +657,12 @@ void AgentChatWidget::Draw() {
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
// Advanced Settings Tab
if (ImGui::BeginTabItem(ICON_MD_SETTINGS " Advanced")) {
RenderAgentConfigPanel();
ImGui::EndTabItem();
}
// Proposals Tab // Proposals Tab
if (ImGui::BeginTabItem(ICON_MD_PREVIEW " Proposals")) { if (ImGui::BeginTabItem(ICON_MD_PREVIEW " Proposals")) {
RenderProposalManagerPanel(); RenderProposalManagerPanel();

View File

@@ -60,12 +60,13 @@ enum class EditorType {
kScreen, kScreen,
kSprite, kSprite,
kMessage, kMessage,
kHex,
kSettings, kSettings,
}; };
constexpr std::array<const char*, 10> kEditorNames = { constexpr std::array<const char*, 11> kEditorNames = {
"Assembly", "Dungeon", "Graphics", "Music", "Overworld", "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 // Initialize welcome screen callbacks
welcome_screen_.SetOpenRomCallback([this]() { welcome_screen_.SetOpenRomCallback([this]() {
status_ = LoadRom(); status_ = LoadRom();
if (status_.ok()) { // LoadRom() already handles closing welcome screen and showing editor selection
show_welcome_screen_ = false;
welcome_screen_manually_closed_ = true;
}
}); });
welcome_screen_.SetNewProjectCallback([this]() { welcome_screen_.SetNewProjectCallback([this]() {
@@ -753,22 +750,66 @@ absl::Status EditorManager::DrawRomSelector() {
void EditorManager::BuildModernMenu() { void EditorManager::BuildModernMenu() {
menu_builder_.Clear(); menu_builder_.Clear();
// File Menu (no icon to avoid cutoff) // File Menu - enhanced with ROM features
menu_builder_.BeginMenu("File") menu_builder_.BeginMenu("File")
.Item("Open", ICON_MD_FILE_OPEN, .Item("Open ROM", ICON_MD_FILE_OPEN,
[this]() { status_ = LoadRom(); }, "Ctrl+O") [this]() { status_ = LoadRom(); }, "Ctrl+O")
.Item("Save", ICON_MD_SAVE, .Item("Save ROM", ICON_MD_SAVE,
[this]() { status_ = SaveRom(); }, "Ctrl+S", [this]() { status_ = SaveRom(); }, "Ctrl+S",
[this]() { return current_rom_ && current_rom_->is_loaded(); }) [this]() { return current_rom_ && current_rom_->is_loaded(); })
.Item("Save As...", ICON_MD_SAVE_AS, .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() .Separator()
.BeginSubMenu("Project", ICON_MD_FOLDER_SPECIAL) .Item("New Project", ICON_MD_CREATE_NEW_FOLDER,
.Item("New Project", ICON_MD_CREATE_NEW_FOLDER, [this]() { status_ = CreateNewProject(); })
[this]() { status_ = CreateNewProject(); }) .Item("Open Project", ICON_MD_FOLDER_OPEN,
.Item("Open Project", ICON_MD_FOLDER_OPEN, [this]() { status_ = OpenProject(); })
[this]() { status_ = OpenProject(); }) .Item("Save Project", ICON_MD_SAVE,
.EndMenu() [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() .Separator()
.Item("Quit", ICON_MD_EXIT_TO_APP, .Item("Quit", ICON_MD_EXIT_TO_APP,
[this]() { quit_ = true; }, "Ctrl+Q") [this]() { quit_ = true; }, "Ctrl+Q")
@@ -790,203 +831,68 @@ void EditorManager::BuildModernMenu() {
.Separator() .Separator()
.Item("Find", ICON_MD_SEARCH, .Item("Find", ICON_MD_SEARCH,
[this]() { if (current_editor_) status_ = current_editor_->Find(); }, "Ctrl+F") [this]() { if (current_editor_) status_ = current_editor_->Find(); }, "Ctrl+F")
.Separator() .Item("Find in Files", ICON_MD_SEARCH,
.Item("Settings", ICON_MD_SETTINGS, [this]() { show_global_search_ = true; }, "Ctrl+Shift+F")
[this]() { current_editor_set_->settings_editor_.set_active(true); })
.EndMenu(); .EndMenu();
// View Menu // View Menu - editors and layout
menu_builder_.BeginMenu("View") menu_builder_.BeginMenu("View")
.BeginSubMenu("Editors", ICON_MD_APPS) .Item("Editor Selection", ICON_MD_DASHBOARD,
.Item("Show Editor Selection", ICON_MD_DASHBOARD, [this]() { show_editor_selection_ = true; }, "Ctrl+E")
[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()
.Separator() .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, .Item("Welcome Screen", ICON_MD_HOME,
[this]() { show_welcome_screen_ = true; }) [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, .Item("Command Palette", ICON_MD_TERMINAL,
[this]() { show_command_palette_ = true; }, "Ctrl+Shift+P") [this]() { show_command_palette_ = true; }, "Ctrl+Shift+P")
#ifdef YAZE_WITH_GRPC .Item("Emulator", ICON_MD_GAMEPAD,
.Separator() [this]() { show_emulator_ = true; },
.Item("AI Agent Editor", ICON_MD_SMART_TOY, nullptr, nullptr,
[this]() { agent_editor_.SetChatActive(true); }, "Ctrl+Shift+A", [this]() { return show_emulator_; })
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()
.Separator() .Separator()
.Item("Performance Dashboard", ICON_MD_SPEED, .Item("Performance Dashboard", ICON_MD_SPEED,
[this]() { show_performance_dashboard_ = true; }, "Ctrl+Shift+P") [this]() { show_performance_dashboard_ = true; })
.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")
#ifdef YAZE_ENABLE_TESTING #ifdef YAZE_ENABLE_TESTING
.Item("Test Dashboard", ICON_MD_SCIENCE, .Item("Test Dashboard", ICON_MD_SCIENCE,
[this]() { show_test_dashboard_ = true; }, "Ctrl+T") [this]() { show_test_dashboard_ = true; }, "Ctrl+T")
.Separator()
#endif #endif
.Item("Memory Editor", ICON_MD_MEMORY,
[this]() { show_memory_editor_ = true; })
.Item("Assembly Editor", ICON_MD_CODE,
[this]() { show_asm_editor_ = true; })
.Separator() .Separator()
.Item("ImGui Demo", ICON_MD_HELP, .Item("ImGui Demo", ICON_MD_HELP,
[this]() { show_imgui_demo_ = true; }, [this]() { show_imgui_demo_ = true; },
@@ -996,13 +902,114 @@ void EditorManager::BuildModernMenu() {
[this]() { show_imgui_metrics_ = true; }, [this]() { show_imgui_metrics_ = true; },
nullptr, nullptr, nullptr, nullptr,
[this]() { return show_imgui_metrics_; }) [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() .Separator()
.Item("Agent Chat", ICON_MD_CHAT, .Item("Session Switcher", ICON_MD_SWITCH_ACCOUNT,
[this]() { agent_editor_.ToggleChat(); }, [this]() { show_session_switcher_ = true; }, "Ctrl+Tab",
nullptr, nullptr, [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(); }) [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 #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(); .EndMenu();
// Help Menu // Help Menu
@@ -1842,9 +1849,8 @@ absl::Status EditorManager::LoadRom() {
// Hide welcome screen when ROM is successfully loaded - don't reset manual close state // Hide welcome screen when ROM is successfully loaded - don't reset manual close state
show_welcome_screen_ = false; show_welcome_screen_ = false;
// Optionally show editor selection dialog after ROM loads // Show editor selection dialog after ROM loads
// (Can be disabled via settings if users prefer to manually select editors) show_editor_selection_ = true;
// show_editor_selection_ = true; // Uncomment to auto-show after ROM load
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -1967,8 +1973,9 @@ absl::Status EditorManager::OpenRomOrProject(const std::string& filename) {
current_editor_set_ = &session.editors; current_editor_set_ = &session.editors;
RETURN_IF_ERROR(LoadAssets()); RETURN_IF_ERROR(LoadAssets());
// Reset welcome screen state when ROM is loaded // Hide welcome screen and show editor selection when ROM is loaded
welcome_screen_manually_closed_ = false; show_welcome_screen_ = false;
show_editor_selection_ = true;
} }
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -2044,6 +2051,10 @@ absl::Status EditorManager::OpenProject() {
} }
RETURN_IF_ERROR(LoadAssets()); 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 // Apply workspace settings

View File

@@ -14,41 +14,51 @@ namespace yaze {
namespace editor { namespace editor {
EditorSelectionDialog::EditorSelectionDialog() { EditorSelectionDialog::EditorSelectionDialog() {
// Initialize editor metadata // Initialize editor metadata with distinct colors
editors_ = { editors_ = {
{EditorType::kOverworld, "Overworld", ICON_MD_MAP, {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, {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, {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, {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, {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, {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, {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, {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, {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,
// {EditorType::kHex, "Hex Editor", ICON_MD_DATA_ARRAY, "Direct ROM memory editing and comparison", "Ctrl+0", false, true,
// "Direct ROM memory editing", "Ctrl+0", false, true}, ImVec4(0.2f, 0.8f, 0.4f, 1.0f)}, // Matrix green
{EditorType::kSettings, "Settings", ICON_MD_SETTINGS, {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(); LoadRecentEditors();
@@ -66,13 +76,13 @@ bool EditorSelectionDialog::Show(bool* p_open) {
} }
bool editor_selected = false; 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(); ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); 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, if (ImGui::Begin("Editor Selection", window_open,
ImGuiWindowFlags_NoCollapse)) { ImGuiWindowFlags_NoCollapse)) {
DrawWelcomeHeader(); DrawWelcomeHeader();
@@ -131,19 +141,26 @@ bool EditorSelectionDialog::Show(bool* p_open) {
} }
void EditorSelectionDialog::DrawWelcomeHeader() { 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), ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]); // Large font
ICON_MD_EDIT " Select an Editor");
// 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::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."); "You can open multiple editors simultaneously.");
} }
void EditorSelectionDialog::DrawQuickAccessButtons() { 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(); ImGui::Spacing();
for (EditorType type : recent_editors_) { for (EditorType type : recent_editors_) {
@@ -154,14 +171,20 @@ void EditorSelectionDialog::DrawQuickAccessButtons() {
}); });
if (it != editors_.end()) { 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(), if (ImGui::Button(absl::StrCat(it->icon, " ", it->name).c_str(),
ImVec2(150, 30))) { ImVec2(150, 35))) {
selected_editor_ = type; selected_editor_ = type;
} }
ImGui::PopStyleColor(); ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", it->description); ImGui::SetTooltip("%s", it->description);
@@ -177,44 +200,119 @@ void EditorSelectionDialog::DrawQuickAccessButtons() {
void EditorSelectionDialog::DrawEditorCard(const EditorInfo& info, int index) { void EditorSelectionDialog::DrawEditorCard(const EditorInfo& info, int index) {
ImGui::PushID(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(), bool is_recent = std::find(recent_editors_.begin(), recent_editors_.end(),
info.type) != 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) { if (is_recent) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.25f, 0.55f, 0.85f, 0.4f)); ImVec2 badge_pos(cursor_pos.x + button_size.x - 25, cursor_pos.y + 5);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 0.9f, 0.6f)); draw_list->AddCircleFilled(badge_pos, 12, ImGui::GetColorU32(base_color), 16);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.35f, 0.65f, 0.95f, 0.8f)); 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 // Make button transparent (we draw our own background)
ImVec2 button_size(180, 100); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
if (ImGui::Button(absl::StrCat(info.icon, "\n", info.name).c_str(), ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(base_color.x * 0.3f, base_color.y * 0.3f,
button_size)) { base_color.z * 0.3f, 0.5f));
selected_editor_ = info.type; 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) { // Hover glow effect
ImGui::PopStyleColor(3); 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 // Enhanced tooltip with fixed sizing
if (ImGui::IsItemHovered()) { if (is_hovered) {
// Force tooltip to have a fixed max width to prevent flickering
ImGui::SetNextWindowSize(ImVec2(300, 0), ImGuiCond_Always);
ImGui::BeginTooltip(); 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::Separator();
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 280);
ImGui::TextWrapped("%s", info.description); ImGui::TextWrapped("%s", info.description);
ImGui::PopTextWrapPos();
if (info.shortcut && info.shortcut[0]) { if (info.shortcut && info.shortcut[0]) {
ImGui::Spacing(); 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(); ImGui::EndTooltip();
} }
// Recent indicator if (clicked) {
if (is_recent) { selected_editor_ = info.type;
ImGui::SameLine();
ImGui::TextDisabled(ICON_MD_STAR);
} }
ImGui::PopID(); ImGui::PopID();

View File

@@ -6,6 +6,7 @@
#include <functional> #include <functional>
#include "app/editor/editor.h" #include "app/editor/editor.h"
#include "imgui/imgui.h"
namespace yaze { namespace yaze {
namespace editor { namespace editor {
@@ -22,6 +23,7 @@ struct EditorInfo {
const char* shortcut; const char* shortcut;
bool recently_used = false; bool recently_used = false;
bool requires_rom = true; 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[] = { TriforceConfig triforce_configs[] = {
{0.10f, 0.15f, 32.0f, 0.035f, 60.0f}, // Top left corner {0.08f, 0.12f, 32.0f, 0.065f, 80.0f}, // Top left corner
{0.90f, 0.18f, 28.0f, 0.028f, 55.0f}, // Top right corner {0.92f, 0.15f, 30.0f, 0.055f, 75.0f}, // Top right corner
{0.08f, 0.82f, 24.0f, 0.022f, 50.0f}, // Bottom left {0.06f, 0.85f, 28.0f, 0.050f, 70.0f}, // Bottom left
{0.92f, 0.78f, 28.0f, 0.030f, 55.0f}, // Bottom right {0.94f, 0.82f, 30.0f, 0.058f, 75.0f}, // Bottom right
{0.18f, 0.45f, 24.0f, 0.020f, 45.0f}, // Mid left {0.15f, 0.48f, 26.0f, 0.048f, 65.0f}, // Mid left
{0.82f, 0.52f, 24.0f, 0.020f, 45.0f}, // Mid right {0.85f, 0.52f, 26.0f, 0.048f, 65.0f}, // Mid right
{0.50f, 0.88f, 20.0f, 0.018f, 40.0f}, // Bottom center {0.50f, 0.92f, 24.0f, 0.040f, 60.0f}, // Bottom center
{0.30f, 0.25f, 20.0f, 0.015f, 40.0f}, // Upper mid-left {0.28f, 0.22f, 22.0f, 0.038f, 55.0f}, // Upper mid-left
{0.70f, 0.28f, 20.0f, 0.015f, 40.0f}, // Upper mid-right {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 // Initialize base positions on first frame
@@ -201,42 +208,65 @@ bool WelcomeScreen::Show(bool* p_open) {
triforce_positions_initialized_ = true; 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) { for (int i = 0; i < kNumTriforces; ++i) {
// Update base position in case window moved/resized // Update base position in case window moved/resized
float base_x = window_pos.x + window_size.x * triforce_configs[i].x_pct; 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; float base_y = window_pos.y + window_size.y * triforce_configs[i].y_pct;
triforce_base_positions_[i] = ImVec2(base_x, base_y); 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 // Calculate distance from mouse
float dx = triforce_base_positions_[i].x - mouse_pos.x; float dx = triforce_base_positions_[i].x - mouse_pos.x;
float dy = triforce_base_positions_[i].y - mouse_pos.y; float dy = triforce_base_positions_[i].y - mouse_pos.y;
float dist = std::sqrt(dx * dx + dy * dy); float dist = std::sqrt(dx * dx + dy * dy);
// Calculate repulsion offset // Calculate repulsion offset with stronger effect
ImVec2 target_pos = triforce_base_positions_[i]; 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 // Normalize direction away from mouse
float dir_x = dx / dist; float dir_x = dx / dist;
float dir_y = dy / dist; float dir_y = dy / dist;
// Stronger repulsion when closer // Much stronger repulsion when closer with exponential falloff
float repel_strength = (1.0f - dist / repel_radius) * triforce_configs[i].repel_distance; 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.x += dir_x * repel_strength;
target_pos.y += dir_y * repel_strength; target_pos.y += dir_y * repel_strength;
} }
// Smooth interpolation to target position // Smooth interpolation to target position (faster response)
float lerp_speed = 6.0f * ImGui::GetIO().DeltaTime; float lerp_speed = 8.0f * ImGui::GetIO().DeltaTime;
triforce_positions_[i].x += (target_pos.x - triforce_positions_[i].x) * lerp_speed; 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; 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], DrawTriforceBackground(bg_draw_list, triforce_positions_[i],
triforce_configs[i].size, triforce_configs[i].alpha, 0.0f); adjusted_size, adjusted_alpha, 0.0f);
} }
DrawHeader(); DrawHeader();
@@ -318,6 +348,57 @@ bool WelcomeScreen::Show(bool* p_open) {
ImGui::Dummy(ImVec2(0, 5)); ImGui::Dummy(ImVec2(0, 5));
DrawTipsSection(); 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(); ImGui::End();
@@ -489,8 +570,8 @@ void WelcomeScreen::DrawRecentProjects() {
} }
// Grid layout for project cards // Grid layout for project cards
float card_width = 220.0f; float card_width = 260.0f; // Increased from 220.0f
float card_height = 140.0f; float card_height = 120.0f; // Reduced from 140.0f
int columns = std::max(1, (int)(ImGui::GetContentRegionAvail().x / (card_width + 15))); int columns = std::max(1, (int)(ImGui::GetContentRegionAvail().x / (card_width + 15)));
for (size_t i = 0; i < recent_projects_.size(); ++i) { 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) { void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
ImGui::BeginGroup(); ImGui::BeginGroup();
ImVec2 card_size(220, 140); ImVec2 card_size(260, 120); // Reduced height from 140 to 120
ImVec2 cursor_pos = ImGui::GetCursorScreenPos(); ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
// Subtle hover scale (only on actual hover, no animation) // Subtle hover scale (only on actual hover, no animation)
@@ -555,39 +636,38 @@ void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
} }
// Draw content // 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 // Icon with colored background circle (smaller and centered)
ImVec2 icon_center(content_pos.x + 20, content_pos.y + 20); ImVec2 icon_center(content_pos.x + 16, content_pos.y + 16);
ImU32 icon_bg = ImGui::GetColorU32(border_color_base); 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::PushStyleColor(ImGuiCol_Text, ImVec4(1, 1, 1, 1));
ImGui::Text(ICON_MD_VIDEOGAME_ASSET); ImGui::Text(ICON_MD_VIDEOGAME_ASSET);
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::PopFont();
// Project name // Project name
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 50, content_pos.y + 15)); ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 40, content_pos.y + 12));
ImGui::PushTextWrapPos(cursor_pos.x + card_size.x - 15); ImGui::PushTextWrapPos(cursor_pos.x + card_size.x - 12);
ImGui::TextColored(kTriforceGold, "%s", project.name.c_str()); ImGui::TextColored(kTriforceGold, "%s", project.name.c_str());
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
// ROM title // 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()); 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 // Path in card (condensed)
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 80)); ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 70));
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)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
std::string short_path = project.filepath; std::string short_path = project.filepath;
if (short_path.length() > 35) { if (short_path.length() > 38) {
short_path = "..." + short_path.substr(short_path.length() - 32); short_path = "..." + short_path.substr(short_path.length() - 35);
} }
ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str()); ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str());
ImGui::PopStyleColor(); ImGui::PopStyleColor();
@@ -598,8 +678,8 @@ void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
ImGui::TextColored(kMasterSwordBlue, ICON_MD_INFO " Project Details"); ImGui::TextColored(kMasterSwordBlue, ICON_MD_INFO " Project Details");
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Name: %s", project.name.c_str()); 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("Path: %s", project.filepath.c_str());
ImGui::Text("Modified: %s", project.last_modified.c_str());
ImGui::Separator(); ImGui::Separator();
ImGui::TextColored(kTriforceGold, ICON_MD_TOUCH_APP " Click to open"); ImGui::TextColored(kTriforceGold, ICON_MD_TOUCH_APP " Click to open");
ImGui::EndTooltip(); ImGui::EndTooltip();
@@ -666,8 +746,8 @@ void WelcomeScreen::DrawTemplatesSection() {
void WelcomeScreen::DrawTipsSection() { void WelcomeScreen::DrawTipsSection() {
// Static tip (or could rotate based on session start time rather than animation) // Static tip (or could rotate based on session start time rather than animation)
const char* tips[] = { const char* tips[] = {
"Press Ctrl+P to open the command palette", "Press Ctrl+Shift+P to open the command palette",
"Use z3ed agent for AI-powered ROM editing", "Use z3ed agent for AI-powered ROM editing (Ctrl+Shift+A)",
"Enable ZSCustomOverworld in Debug menu for expanded features", "Enable ZSCustomOverworld in Debug menu for expanded features",
"Check the Performance Dashboard for optimization insights", "Check the Performance Dashboard for optimization insights",
"Collaborate in real-time with yaze-server" "Collaborate in real-time with yaze-server"
@@ -693,7 +773,7 @@ void WelcomeScreen::DrawWhatsNew() {
ImGui::Spacing(); ImGui::Spacing();
// Version badge (no animation) // 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(); ImGui::Spacing();
// Feature list with icons and colors // Feature list with icons and colors

View File

@@ -106,10 +106,17 @@ class WelcomeScreen {
int hovered_card_ = -1; int hovered_card_ = -1;
// Interactive triforce positions (smooth interpolation) // Interactive triforce positions (smooth interpolation)
static constexpr int kNumTriforces = 9; static constexpr int kNumTriforces = 16;
ImVec2 triforce_positions_[kNumTriforces] = {}; ImVec2 triforce_positions_[kNumTriforces] = {};
ImVec2 triforce_base_positions_[kNumTriforces] = {}; ImVec2 triforce_base_positions_[kNumTriforces] = {};
bool triforce_positions_initialized_ = false; 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 } // namespace editor