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:
@@ -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", ¤t_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();
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user