feat: Revamp Welcome Screen and EditorManager Menus for Improved User Interaction

- Updated the Welcome Screen to include a particle system for enhanced visual effects and a more engaging user experience.
- Reduced the number of triforces for a cleaner layout and adjusted animation settings for subtlety.
- Streamlined project card dimensions and layout for a more compact display, improving accessibility.
- Enhanced the EditorManager menus by adding new items for session management and editor selection, improving workflow efficiency.
- Improved the styling and functionality of buttons and inputs across the UI for better user interaction.
This commit is contained in:
scawful
2025-10-05 03:38:01 -04:00
parent 33242a8803
commit c495368d6a
4 changed files with 397 additions and 262 deletions

View File

@@ -553,17 +553,37 @@ void AgentChatWidget::Draw() {
if (ImGui::BeginTabBar("AgentChatTabs", ImGuiTabBarFlags_None)) {
// Main Chat Tab - with integrated controls
if (ImGui::BeginTabItem(ICON_MD_CHAT " Chat")) {
// Connection Status Bar at top
ImGui::BeginChild("ConnectionStatusBar", ImVec2(0, 80), true, ImGuiWindowFlags_NoScrollbar);
// Stylish connection status bar with gradient
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 bar_start = ImGui::GetCursorScreenPos();
ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 85);
// Gradient background
ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f));
ImU32 color_bottom = ImGui::GetColorU32(ImVec4(0.12f, 0.16f, 0.22f, 1.0f));
draw_list->AddRectFilledMultiColor(bar_start,
ImVec2(bar_start.x + bar_size.x, bar_start.y + bar_size.y),
color_top, color_top, color_bottom, color_bottom);
// Colored accent bar based on provider
ImVec4 accent_color = (agent_config_.ai_provider == "ollama") ? ImVec4(0.2f, 0.8f, 0.4f, 1.0f) :
(agent_config_.ai_provider == "gemini") ? ImVec4(0.196f, 0.6f, 0.8f, 1.0f) :
ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
draw_list->AddRectFilled(bar_start, ImVec2(bar_start.x + bar_size.x, bar_start.y + 3),
ImGui::GetColorU32(accent_color));
ImGui::BeginChild("ConnectionStatusBar", bar_size, false, ImGuiWindowFlags_NoScrollbar);
{
ImGui::Spacing();
// Provider selection and connection status in one row
ImGui::Text(ICON_MD_SMART_TOY " AI Provider:");
ImGui::TextColored(accent_color, ICON_MD_SMART_TOY " AI Provider:");
ImGui::SameLine();
ImGui::SetNextItemWidth(120);
const char* providers[] = { "Mock", "Ollama", "Gemini" };
int current_provider = (agent_config_.ai_provider == "mock") ? 0 :
(agent_config_.ai_provider == "ollama") ? 1 : 2;
if (ImGui::Combo("##provider", &current_provider, providers, 3)) {
if (ImGui::Combo("##chat_provider_combo", &current_provider, providers, 3)) {
agent_config_.ai_provider = (current_provider == 0) ? "mock" :
(current_provider == 1) ? "ollama" : "gemini";
strncpy(agent_config_.provider_buffer, agent_config_.ai_provider.c_str(),
@@ -572,46 +592,53 @@ void AgentChatWidget::Draw() {
ImGui::SameLine();
if (agent_config_.ai_provider == "ollama") {
ImGui::Text(ICON_MD_LINK " Host:");
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.4f, 1.0f), ICON_MD_LINK " Host:");
ImGui::SameLine();
ImGui::SetNextItemWidth(200);
if (ImGui::InputText("##ollama_host", agent_config_.ollama_host_buffer,
if (ImGui::InputText("##chat_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")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.6f, 0.3f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.8f, 0.4f, 1.0f));
if (ImGui::Button(ICON_MD_REFRESH " Test##test_ollama_conn")) {
if (toast_manager_) {
toast_manager_->Show("Testing Ollama connection...", ToastType::kInfo);
}
}
ImGui::PopStyleColor(2);
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::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_KEY " API Key:");
ImGui::SameLine();
ImGui::SetNextItemWidth(250);
if (ImGui::InputText("##gemini_key", agent_config_.gemini_key_buffer,
if (ImGui::InputText("##chat_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")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
if (ImGui::Button(ICON_MD_CHECK " Verify##verify_gemini_key")) {
if (toast_manager_) {
toast_manager_->Show("Verifying Gemini API key...", ToastType::kInfo);
}
}
ImGui::PopStyleColor(2);
} else {
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "(Mock mode - no external connection needed)");
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
ICON_MD_INFO " Mock mode - no external connection needed");
}
// Second row: Model selection and quick settings
ImGui::Text(ICON_MD_PSYCHOLOGY " Model:");
ImGui::TextColored(accent_color, ICON_MD_PSYCHOLOGY " Model:");
ImGui::SameLine();
ImGui::SetNextItemWidth(200);
ImGui::InputText("##model", agent_config_.model_buffer, sizeof(agent_config_.model_buffer));
ImGui::InputText("##chat_model_input", agent_config_.model_buffer, sizeof(agent_config_.model_buffer));
ImGui::SameLine();
ImGui::Checkbox(ICON_MD_VISIBILITY " Reasoning", &agent_config_.show_reasoning);
@@ -619,9 +646,11 @@ void AgentChatWidget::Draw() {
ImGui::SetTooltip("Show agent's reasoning process in responses");
}
ImGui::SameLine();
ImGui::TextColored(accent_color, ICON_MD_LOOP);
ImGui::SameLine();
ImGui::SetNextItemWidth(80);
ImGui::InputInt("Max Iterations", &agent_config_.max_tool_iterations);
ImGui::InputInt("##chat_max_iter", &agent_config_.max_tool_iterations);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Maximum tool call iterations per request");
}
@@ -770,46 +799,53 @@ void AgentChatWidget::RenderCollaborationPanel() {
ImGui::TableNextRow();
// LEFT COLUMN: Session Details
// LEFT COLUMN: Session Details with gradient background
ImGui::TableSetColumnIndex(0);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.15f, 0.18f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.2f, 0.18f, 0.4f));
ImGui::BeginChild("session_details", ImVec2(0, 180), true);
ImVec4 status_color = ImVec4(1.0f, 0.843f, 0.0f, 1.0f);
const bool connected = collaboration_state_.active;
ImGui::TextColored(connected ? ImVec4(0.4f, 1.0f, 0.4f, 1.0f)
: ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
"%s %s", connected ? "" : "",
connected ? "Connected" : "Not connected");
ImGui::Separator();
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_INFO " Session Status:");
ImGui::Spacing();
if (connected) {
ImGui::TextColored(status_color, ICON_MD_CHECK_CIRCLE " Connected");
} else {
ImGui::TextDisabled(ICON_MD_CANCEL " Not connected");
}
if (collaboration_state_.mode == CollaborationMode::kNetwork) {
ImGui::Text("Server:");
ImGui::TextWrapped("%s", collaboration_state_.server_url.c_str());
ImGui::Spacing();
ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_CLOUD " Server:");
ImGui::TextWrapped("%s", collaboration_state_.server_url.c_str());
}
if (!collaboration_state_.session_name.empty()) {
ImGui::Text("Session:");
ImGui::TextWrapped("%s", collaboration_state_.session_name.c_str());
ImGui::Spacing();
ImGui::TextColored(status_color, ICON_MD_LABEL " Session:");
ImGui::TextWrapped("%s", collaboration_state_.session_name.c_str());
}
if (!collaboration_state_.session_id.empty()) {
ImGui::Text("Code:");
ImGui::Spacing();
ImGui::TextColored(status_color, ICON_MD_KEY " Session Code:");
ImGui::TextWrapped("%s", collaboration_state_.session_id.c_str());
if (ImGui::SmallButton("Copy")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.6f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f));
if (ImGui::SmallButton(ICON_MD_CONTENT_COPY " Copy##copy_session_id")) {
ImGui::SetClipboardText(collaboration_state_.session_id.c_str());
if (toast_manager_) {
toast_manager_->Show("Code copied", ToastType::kInfo, 2.0f);
toast_manager_->Show("Session code copied!", ToastType::kSuccess, 2.0f);
}
}
ImGui::Spacing();
ImGui::PopStyleColor(2);
}
if (collaboration_state_.last_synced != absl::InfinitePast()) {
ImGui::TextDisabled("Last sync:");
ImGui::TextDisabled(
ImGui::Spacing();
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), ICON_MD_ACCESS_TIME " Last sync:");
ImGui::SameLine();
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
"%s", absl::FormatTime("%H:%M:%S", collaboration_state_.last_synced,
absl::LocalTimeZone())
.c_str());
@@ -819,18 +855,20 @@ void AgentChatWidget::RenderCollaborationPanel() {
ImGui::PopStyleColor();
// Show participants list below session details
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.16f, 0.14f, 0.4f));
ImGui::BeginChild("participants", ImVec2(0, 0), true);
if (collaboration_state_.participants.empty()) {
ImGui::TextDisabled("No participants");
ImGui::TextDisabled(ICON_MD_PEOPLE " No participants yet");
} else {
ImGui::Text("Participants (%zu):",
ImGui::TextColored(status_color, ICON_MD_PEOPLE " Participants (%zu):",
collaboration_state_.participants.size());
ImGui::Separator();
for (const auto& participant : collaboration_state_.participants) {
ImGui::BulletText("%s", participant.c_str());
ImGui::BulletText(ICON_MD_PERSON " %s", participant.c_str());
}
}
ImGui::EndChild();
ImGui::PopStyleColor();
// RIGHT COLUMN: Controls
ImGui::TableSetColumnIndex(1);
@@ -847,30 +885,39 @@ void AgentChatWidget::RenderCollaborationPanel() {
const bool can_refresh =
static_cast<bool>(collaboration_callbacks_.refresh_session);
// Network mode: Show server URL input
// Network mode: Show server URL input with styling
if (collaboration_state_.mode == CollaborationMode::kNetwork) {
ImGui::Text("Server URL:");
ImGui::InputText("##server_url", server_url_buffer_,
ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_CLOUD " Server URL:");
ImGui::SetNextItemWidth(-80);
ImGui::InputText("##collab_server_url", server_url_buffer_,
IM_ARRAYSIZE(server_url_buffer_));
if (ImGui::Button("Connect to Server")) {
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
if (ImGui::Button(ICON_MD_LINK "##connect_server_btn")) {
collaboration_state_.server_url = server_url_buffer_;
// TODO: Trigger network coordinator connection
if (toast_manager_) {
toast_manager_->Show("Network mode: connecting...", ToastType::kInfo,
3.0f);
toast_manager_->Show("Connecting to server...", ToastType::kInfo, 3.0f);
}
}
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Connect to collaboration server");
}
ImGui::Separator();
}
ImGui::Text("Host New Session:");
ImGui::InputTextWithHint("##session_name", "Session name",
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_ADD_CIRCLE " Host New Session:");
ImGui::SetNextItemWidth(-70);
ImGui::InputTextWithHint("##collab_session_name", "Enter session name...",
session_name_buffer_,
IM_ARRAYSIZE(session_name_buffer_));
ImGui::SameLine();
if (!can_host)
ImGui::BeginDisabled();
if (ImGui::Button("Host")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.5f, 0.0f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.843f, 0.0f, 1.0f));
if (ImGui::Button(ICON_MD_ROCKET_LAUNCH "##host_session_btn")) {
std::string name = session_name_buffer_;
if (name.empty()) {
if (toast_manager_) {
@@ -899,21 +946,27 @@ void AgentChatWidget::RenderCollaborationPanel() {
}
}
}
ImGui::PopStyleColor(2);
if (!can_host) {
if (ImGui::IsItemHovered()) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("Provide host_session callback to enable hosting");
}
ImGui::EndDisabled();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Host a new collaboration session");
}
ImGui::Spacing();
ImGui::Text("Join Existing Session:");
ImGui::InputTextWithHint("##join_code", "Session code", join_code_buffer_,
ImGui::TextColored(ImVec4(0.133f, 0.545f, 0.133f, 1.0f), ICON_MD_LOGIN " Join Existing Session:");
ImGui::SetNextItemWidth(-70);
ImGui::InputTextWithHint("##collab_join_code", "Enter session code...", join_code_buffer_,
IM_ARRAYSIZE(join_code_buffer_));
ImGui::SameLine();
if (!can_join)
ImGui::BeginDisabled();
if (ImGui::Button("Join")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.1f, 0.4f, 0.1f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.133f, 0.545f, 0.133f, 1.0f));
if (ImGui::Button(ICON_MD_MEETING_ROOM "##join_session_btn")) {
std::string code = join_code_buffer_;
if (code.empty()) {
if (toast_manager_) {
@@ -941,17 +994,25 @@ void AgentChatWidget::RenderCollaborationPanel() {
}
}
}
ImGui::PopStyleColor(2);
if (!can_join) {
if (ImGui::IsItemHovered()) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("Provide join_session callback to enable joining");
}
ImGui::EndDisabled();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Join an existing collaboration session");
}
if (connected) {
ImGui::Separator();
ImGui::Spacing();
if (!can_leave)
ImGui::BeginDisabled();
if (ImGui::Button("Leave Session")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.7f, 0.2f, 0.2f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.863f, 0.078f, 0.235f, 1.0f));
if (ImGui::Button(ICON_MD_LOGOUT " Leave Session##leave_session_btn", ImVec2(-1, 0))) {
absl::Status status = collaboration_callbacks_.leave_session
? collaboration_callbacks_.leave_session()
: absl::OkStatus();
@@ -978,10 +1039,13 @@ void AgentChatWidget::RenderCollaborationPanel() {
ImGui::Separator();
if (!can_refresh)
ImGui::BeginDisabled();
if (ImGui::Button("Refresh Session")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f));
if (ImGui::Button(ICON_MD_REFRESH " Refresh Session##refresh_collab_btn", ImVec2(-1, 0))) {
RefreshCollaboration();
}
if (!can_refresh && ImGui::IsItemHovered()) {
ImGui::PopStyleColor(2);
if (!can_refresh && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("Provide refresh_session callback to enable");
}
if (!can_refresh)
@@ -997,7 +1061,9 @@ void AgentChatWidget::RenderCollaborationPanel() {
}
void AgentChatWidget::RenderMultimodalPanel() {
if (!ImGui::CollapsingHeader("Gemini Multimodal (Preview)",
ImVec4 gemini_color = ImVec4(0.196f, 0.6f, 0.8f, 1.0f);
if (!ImGui::CollapsingHeader(ICON_MD_CAMERA " Gemini Multimodal Vision",
ImGuiTreeNodeFlags_DefaultOpen)) {
return;
}
@@ -1005,33 +1071,35 @@ void AgentChatWidget::RenderMultimodalPanel() {
bool can_capture = static_cast<bool>(multimodal_callbacks_.capture_snapshot);
bool can_send = static_cast<bool>(multimodal_callbacks_.send_to_gemini);
// Capture mode selection
ImGui::Text("Capture Mode:");
ImGui::RadioButton("Full Window",
// Capture mode selection with icons
ImGui::TextColored(gemini_color, ICON_MD_SCREENSHOT " Capture Mode:");
ImGui::RadioButton(ICON_MD_FULLSCREEN " Full Window##mm_radio_full",
reinterpret_cast<int*>(&multimodal_state_.capture_mode),
static_cast<int>(CaptureMode::kFullWindow));
ImGui::SameLine();
ImGui::RadioButton("Active Editor",
ImGui::RadioButton(ICON_MD_DASHBOARD " Active Editor##mm_radio_active",
reinterpret_cast<int*>(&multimodal_state_.capture_mode),
static_cast<int>(CaptureMode::kActiveEditor));
ImGui::SameLine();
ImGui::RadioButton("Specific Window",
ImGui::RadioButton(ICON_MD_WINDOW " Specific##mm_radio_specific",
reinterpret_cast<int*>(&multimodal_state_.capture_mode),
static_cast<int>(CaptureMode::kSpecificWindow));
// If specific window mode, show input for window name
if (multimodal_state_.capture_mode == CaptureMode::kSpecificWindow) {
ImGui::InputText("Window Name", multimodal_state_.specific_window_buffer,
ImGui::SetNextItemWidth(-1);
ImGui::InputTextWithHint("##mm_window_name_input", "Window name (e.g., Overworld Editor)...",
multimodal_state_.specific_window_buffer,
IM_ARRAYSIZE(multimodal_state_.specific_window_buffer));
ImGui::TextDisabled(
"Examples: Overworld Editor, Dungeon Editor, Sprite Editor");
}
ImGui::Separator();
if (!can_capture)
ImGui::BeginDisabled();
if (ImGui::Button("Capture Snapshot")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, gemini_color);
if (ImGui::Button(ICON_MD_PHOTO_CAMERA " Capture Snapshot##mm_capture_btn", ImVec2(-1, 0))) {
if (multimodal_callbacks_.capture_snapshot) {
std::filesystem::path captured_path;
absl::Status status =
@@ -1056,26 +1124,36 @@ void AgentChatWidget::RenderMultimodalPanel() {
}
}
}
ImGui::PopStyleColor(2);
if (!can_capture) {
if (ImGui::IsItemHovered()) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("Provide capture_snapshot callback to enable");
}
ImGui::EndDisabled();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Capture a screenshot for vision analysis");
}
ImGui::Spacing();
if (multimodal_state_.last_capture_path.has_value()) {
ImGui::TextDisabled("Last capture: %s",
multimodal_state_.last_capture_path->string().c_str());
ImGui::TextColored(gemini_color, ICON_MD_CHECK_CIRCLE " Last capture: %s",
multimodal_state_.last_capture_path->filename().string().c_str());
} else {
ImGui::TextDisabled("No capture yet");
ImGui::TextDisabled(ICON_MD_CAMERA_ALT " No capture yet");
}
ImGui::InputTextMultiline("##gemini_prompt", multimodal_prompt_buffer_,
ImGui::Spacing();
ImGui::TextColored(gemini_color, ICON_MD_EDIT " Vision Prompt:");
ImGui::InputTextMultiline("##mm_gemini_prompt_textarea", multimodal_prompt_buffer_,
IM_ARRAYSIZE(multimodal_prompt_buffer_),
ImVec2(-1, 60.0f));
ImVec2(-1, 80));
if (!can_send)
ImGui::BeginDisabled();
if (ImGui::Button("Send to Gemini")) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, gemini_color);
if (ImGui::Button(ICON_MD_SEND " Send to Gemini##mm_send_to_gemini_btn", ImVec2(-1, 0))) {
if (!multimodal_state_.last_capture_path.has_value()) {
if (toast_manager_) {
toast_manager_->Show("Capture a snapshot first", ToastType::kWarning,
@@ -1104,18 +1182,24 @@ void AgentChatWidget::RenderMultimodalPanel() {
}
}
}
ImGui::PopStyleColor(2);
if (!can_send) {
if (ImGui::IsItemHovered()) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("Provide send_to_gemini callback to enable");
}
ImGui::EndDisabled();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Send captured image to Gemini for analysis");
}
if (!multimodal_state_.status_message.empty()) {
ImGui::TextDisabled("Status: %s", multimodal_state_.status_message.c_str());
ImGui::Separator();
ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
ICON_MD_INFO " %s", multimodal_state_.status_message.c_str());
if (multimodal_state_.last_updated != absl::InfinitePast()) {
ImGui::TextDisabled(
"Updated: %s",
ImGui::SameLine();
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
ICON_MD_ACCESS_TIME " %s",
absl::FormatTime("%H:%M:%S", multimodal_state_.last_updated,
absl::LocalTimeZone())
.c_str());

View File

@@ -835,7 +835,7 @@ void EditorManager::BuildModernMenu() {
[this]() { show_global_search_ = true; }, "Ctrl+Shift+F")
.EndMenu();
// View Menu - editors and layout
// View Menu - editors only
menu_builder_.BeginMenu("View")
.Item("Editor Selection", ICON_MD_DASHBOARD,
[this]() { show_editor_selection_ = true; }, "Ctrl+E")
@@ -859,6 +859,35 @@ void EditorManager::BuildModernMenu() {
.Item("Hex Editor", ICON_MD_DATA_ARRAY,
[this]() { show_memory_editor_ = true; }, "Ctrl+0")
.Separator()
.Item("Welcome Screen", ICON_MD_HOME,
[this]() { show_welcome_screen_ = true; })
.Item("Command Palette", ICON_MD_TERMINAL,
[this]() { show_command_palette_ = true; }, "Ctrl+Shift+P")
.Item("Emulator", ICON_MD_GAMEPAD,
[this]() { show_emulator_ = true; },
nullptr, nullptr,
[this]() { return show_emulator_; })
.EndMenu();
// Window Menu - layout and session management
menu_builder_.BeginMenu("Window")
.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()
.Item("Save Layout", ICON_MD_SAVE,
[this]() { SaveWorkspaceLayout(); }, "Ctrl+Shift+S")
.Item("Load Layout", ICON_MD_FOLDER_OPEN,
@@ -874,52 +903,10 @@ void EditorManager::BuildModernMenu() {
[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("Command Palette", ICON_MD_TERMINAL,
[this]() { show_command_palette_ = true; }, "Ctrl+Shift+P")
.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; })
#ifdef YAZE_ENABLE_TESTING
.Item("Test Dashboard", ICON_MD_SCIENCE,
[this]() { show_test_dashboard_ = true; }, "Ctrl+T")
#endif
.Separator()
.Item("ImGui Demo", ICON_MD_HELP,
[this]() { show_imgui_demo_ = true; },
nullptr, nullptr,
[this]() { return show_imgui_demo_; })
.Item("ImGui Metrics", ICON_MD_ANALYTICS,
[this]() { show_imgui_metrics_ = true; },
nullptr, nullptr,
[this]() { return show_imgui_metrics_; })
.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("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; })
.Item("Restore All", ICON_MD_FULLSCREEN_EXIT,
[this]() { RestoreAllWindows(); })
.Item("Close All Floating", ICON_MD_CLOSE_FULLSCREEN,
[this]() { CloseAllFloatingWindows(); })
.EndMenu();
@@ -1004,12 +991,28 @@ void EditorManager::BuildModernMenu() {
.EndMenu();
#endif
// Debug Menu
// Debug Menu - comprehensive development tools
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; })
.Separator()
.Item("Performance Dashboard", ICON_MD_SPEED,
[this]() { show_performance_dashboard_ = true; })
#ifdef YAZE_ENABLE_TESTING
.Item("Test Dashboard", ICON_MD_SCIENCE,
[this]() { show_test_dashboard_ = true; }, "Ctrl+T")
#endif
.Separator()
.Item("ImGui Demo", ICON_MD_HELP,
[this]() { show_imgui_demo_ = true; },
nullptr, nullptr,
[this]() { return show_imgui_demo_; })
.Item("ImGui Metrics", ICON_MD_ANALYTICS,
[this]() { show_imgui_metrics_ = true; },
nullptr, nullptr,
[this]() { return show_imgui_metrics_; })
.EndMenu();
// Help Menu
@@ -1034,15 +1037,12 @@ void EditorManager::DrawMenuBar() {
status_ = DrawRomSelector();
// Calculate proper right-side positioning
// With the wider menu (8 top-level items), we need more compact right-side layout
std::string version_text = absl::StrFormat("v%s", version_.c_str());
float version_width = CalcTextSize(version_text.c_str()).x;
float settings_width = CalcTextSize(ICON_MD_DISPLAY_SETTINGS).x + 16;
float total_right_width = version_width + settings_width + 30; // Increased padding for visibility
// Allocate space for ROM status and sessions
float session_rom_area_width = 220.0f; // Increased slightly for better spacing
SameLine(GetWindowWidth() - total_right_width - session_rom_area_width);
// Allocate space for ROM status and sessions (wider for better ROM title display)
float session_rom_area_width = 280.0f; // Increased for wider ROM title
SameLine(GetWindowWidth() - version_width - 10 - session_rom_area_width);
// Multi-session indicator
if (sessions_.size() > 1) {
@@ -1056,25 +1056,42 @@ void EditorManager::DrawMenuBar() {
SameLine();
}
// Editor selection quick button (when ROM loaded)
// Editor selection and display settings quick buttons (when ROM loaded)
if (current_rom_ && current_rom_->is_loaded()) {
// Editor selection button
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.4f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.5f, 1.0f));
if (SmallButton(ICON_MD_APPS)) {
show_editor_selection_ = true;
}
ImGui::PopStyleColor(2);
if (IsItemHovered()) {
SetTooltip("Open Editor Selection (Ctrl+E)");
SetTooltip(ICON_MD_DASHBOARD " Editor Selection (Ctrl+E)");
}
SameLine();
// Display settings button
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.4f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.5f, 1.0f));
if (SmallButton(ICON_MD_DISPLAY_SETTINGS)) {
show_display_settings = !show_display_settings;
}
ImGui::PopStyleColor(2);
if (IsItemHovered()) {
SetTooltip(ICON_MD_TUNE " Display Settings");
}
SameLine();
ImGui::Separator();
SameLine();
}
// Compact ROM status with metadata popup
if (current_rom_ && current_rom_->is_loaded()) {
// Truncate long ROM titles for compact display
// Truncate long ROM titles for wider display
std::string rom_display = current_rom_->title();
if (rom_display.length() > 15) {
rom_display = rom_display.substr(0, 12) + "...";
if (rom_display.length() > 22) {
rom_display = rom_display.substr(0, 19) + "...";
}
ImVec4 status_color =
@@ -1187,18 +1204,8 @@ void EditorManager::DrawMenuBar() {
SameLine();
}
// Settings and version (using pre-calculated positioning)
SameLine(GetWindowWidth() - total_right_width);
ImGui::Separator();
SameLine();
PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
if (Button(ICON_MD_DISPLAY_SETTINGS)) {
show_display_settings = !show_display_settings;
}
PopStyleColor();
SameLine();
// Version display on far right
SameLine(GetWindowWidth() - version_width - 10);
Text("%s", version_text.c_str());
EndMenuBar();
}

View File

@@ -179,22 +179,12 @@ bool WelcomeScreen::Show(bool* p_open) {
};
TriforceConfig triforce_configs[] = {
{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
{0.08f, 0.12f, 36.0f, 0.025f, 50.0f}, // Top left corner
{0.92f, 0.15f, 34.0f, 0.022f, 50.0f}, // Top right corner
{0.06f, 0.88f, 32.0f, 0.020f, 45.0f}, // Bottom left
{0.94f, 0.85f, 34.0f, 0.023f, 50.0f}, // Bottom right
{0.50f, 0.08f, 38.0f, 0.028f, 55.0f}, // Top center
{0.50f, 0.92f, 32.0f, 0.020f, 45.0f}, // Bottom center
};
// Initialize base positions on first frame
@@ -215,20 +205,16 @@ bool WelcomeScreen::Show(bool* p_open) {
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_;
// Slow, subtle floating animation
float time_offset = i * 1.2f; // Offset each triforce's animation
float float_speed_x = (0.15f + (i % 2) * 0.1f) * triforce_speed_multiplier_; // Very slow
float float_speed_y = (0.12f + ((i + 1) % 2) * 0.08f) * triforce_speed_multiplier_;
float float_amount_x = (20.0f + (i % 2) * 10.0f) * triforce_size_multiplier_; // Smaller amplitude
float float_amount_y = (25.0f + ((i + 1) % 2) * 15.0f) * triforce_size_multiplier_;
// Create complex orbital/figure-8 motion with more variation
// Create gentle orbital motion
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);
float float_y = std::cos(animation_time_ * float_speed_y + time_offset * 1.2f) * float_amount_y;
// Calculate distance from mouse
float dx = triforce_base_positions_[i].x - mouse_pos.x;
@@ -269,6 +255,61 @@ bool WelcomeScreen::Show(bool* p_open) {
adjusted_size, adjusted_alpha, 0.0f);
}
// Update and draw particle system
if (particles_enabled_) {
// Spawn new particles
static float spawn_accumulator = 0.0f;
spawn_accumulator += ImGui::GetIO().DeltaTime * particle_spawn_rate_;
while (spawn_accumulator >= 1.0f && active_particle_count_ < kMaxParticles) {
// Find inactive particle slot
for (int i = 0; i < kMaxParticles; ++i) {
if (particles_[i].lifetime <= 0.0f) {
// Spawn from random triforce
int source_triforce = rand() % kNumTriforces;
particles_[i].position = triforce_positions_[source_triforce];
// Random direction and speed
float angle = (rand() % 360) * (M_PI / 180.0f);
float speed = 20.0f + (rand() % 40);
particles_[i].velocity = ImVec2(std::cos(angle) * speed, std::sin(angle) * speed);
particles_[i].size = 2.0f + (rand() % 4);
particles_[i].alpha = 0.4f + (rand() % 40) / 100.0f;
particles_[i].max_lifetime = 2.0f + (rand() % 30) / 10.0f;
particles_[i].lifetime = particles_[i].max_lifetime;
active_particle_count_++;
break;
}
}
spawn_accumulator -= 1.0f;
}
// Update and draw particles
float dt = ImGui::GetIO().DeltaTime;
for (int i = 0; i < kMaxParticles; ++i) {
if (particles_[i].lifetime > 0.0f) {
// Update lifetime
particles_[i].lifetime -= dt;
if (particles_[i].lifetime <= 0.0f) {
active_particle_count_--;
continue;
}
// Update position
particles_[i].position.x += particles_[i].velocity.x * dt;
particles_[i].position.y += particles_[i].velocity.y * dt;
// Fade out near end of life
float life_ratio = particles_[i].lifetime / particles_[i].max_lifetime;
float alpha = particles_[i].alpha * life_ratio * triforce_alpha_multiplier_;
// Draw particle as small golden circle
ImU32 particle_color = ImGui::GetColorU32(ImVec4(1.0f, 0.843f, 0.0f, alpha));
bg_draw_list->AddCircleFilled(particles_[i].position, particles_[i].size, particle_color, 8);
}
}
}
DrawHeader();
ImGui::Spacing();
@@ -348,57 +389,6 @@ 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();
@@ -416,7 +406,7 @@ void WelcomeScreen::UpdateAnimations() {
card_hover_scale_[i] += (target - card_hover_scale_[i]) * ImGui::GetIO().DeltaTime * 10.0f;
}
// Note: Triforce positions are updated in Show() based on mouse position
// Note: Triforce positions and particles are updated in Show() based on mouse position
}
void WelcomeScreen::RefreshRecentProjects() {
@@ -499,7 +489,7 @@ void WelcomeScreen::DrawQuickActions() {
float button_width = ImGui::GetContentRegionAvail().x;
// Animated button colors
// Animated button colors (compact height)
auto draw_action_button = [&](const char* icon, const char* text,
const ImVec4& color, bool enabled,
std::function<void()> callback) {
@@ -510,7 +500,7 @@ void WelcomeScreen::DrawQuickActions() {
if (!enabled) ImGui::BeginDisabled();
bool clicked = ImGui::Button(absl::StrFormat("%s %s", icon, text).c_str(),
ImVec2(button_width, 45));
ImVec2(button_width, 38)); // Reduced from 45 to 38
if (!enabled) ImGui::EndDisabled();
@@ -540,14 +530,6 @@ void WelcomeScreen::DrawQuickActions() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(ICON_MD_INFO " Create a new ROM hacking project");
}
ImGui::Spacing();
// Clone Project button - Blue like water/ice dungeon
draw_action_button(ICON_MD_CLOUD_DOWNLOAD, "Clone Project", kMasterSwordBlue, false, nullptr);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip(ICON_MD_CONSTRUCTION " Clone a project from git (Coming soon)");
}
}
void WelcomeScreen::DrawRecentProjects() {
@@ -569,10 +551,10 @@ void WelcomeScreen::DrawRecentProjects() {
return;
}
// Grid layout for project cards
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)));
// Grid layout for project cards (compact)
float card_width = 200.0f; // Reduced for compactness
float card_height = 95.0f; // Reduced for less scrolling
int columns = std::max(1, (int)(ImGui::GetContentRegionAvail().x / (card_width + 12)));
for (size_t i = 0; i < recent_projects_.size(); ++i) {
if (i % columns != 0) {
@@ -585,7 +567,7 @@ void WelcomeScreen::DrawRecentProjects() {
void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
ImGui::BeginGroup();
ImVec2 card_size(260, 120); // Reduced height from 140 to 120
ImVec2 card_size(200, 95); // Compact size
ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
// Subtle hover scale (only on actual hover, no animation)
@@ -635,39 +617,41 @@ void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
hover_color, 6.0f);
}
// Draw content
ImVec2 content_pos(cursor_pos.x + 12, cursor_pos.y + 12);
// Draw content (tighter layout)
ImVec2 content_pos(cursor_pos.x + 8, cursor_pos.y + 8);
// Icon with colored background circle (smaller and centered)
ImVec2 icon_center(content_pos.x + 16, content_pos.y + 16);
// Icon with colored background circle (compact)
ImVec2 icon_center(content_pos.x + 13, content_pos.y + 13);
ImU32 icon_bg = ImGui::GetColorU32(border_color_base);
draw_list->AddCircleFilled(icon_center, 18, icon_bg, 32); // Reduced from 25 to 18
draw_list->AddCircleFilled(icon_center, 15, icon_bg, 24);
// 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 + 40, content_pos.y + 12));
ImGui::PushTextWrapPos(cursor_pos.x + card_size.x - 12);
// Project name (compact)
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 32, content_pos.y + 8));
ImGui::PushTextWrapPos(cursor_pos.x + card_size.x - 8);
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]); // Default font
ImGui::TextColored(kTriforceGold, "%s", project.name.c_str());
ImGui::PopFont();
ImGui::PopTextWrapPos();
// ROM title
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());
// ROM title (compact)
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 4, content_pos.y + 35));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.65f, 0.65f, 0.65f, 1.0f));
ImGui::Text(ICON_MD_GAMEPAD " %s", project.rom_title.c_str());
ImGui::PopStyleColor();
// Path in card (condensed)
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 5, content_pos.y + 70));
// Path in card (compact)
ImGui::SetCursorScreenPos(ImVec2(content_pos.x + 4, content_pos.y + 58));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
std::string short_path = project.filepath;
if (short_path.length() > 38) {
short_path = "..." + short_path.substr(short_path.length() - 35);
if (short_path.length() > 28) {
short_path = "..." + short_path.substr(short_path.length() - 25);
}
ImGui::Text(ICON_MD_FOLDER " %s", short_path.c_str());
ImGui::PopStyleColor();
@@ -694,7 +678,53 @@ void WelcomeScreen::DrawProjectCard(const RecentProject& project, int index) {
}
void WelcomeScreen::DrawTemplatesSection() {
// Header with visual settings button
float content_width = ImGui::GetContentRegionAvail().x;
ImGui::TextColored(kGanonPurple, ICON_MD_LAYERS " Templates");
ImGui::SameLine(content_width - 25);
if (ImGui::SmallButton(show_triforce_settings_ ? ICON_MD_CLOSE : ICON_MD_TUNE)) {
show_triforce_settings_ = !show_triforce_settings_;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(ICON_MD_AUTO_AWESOME " Visual Effects Settings");
}
ImGui::Spacing();
// Visual effects settings panel (when opened)
if (show_triforce_settings_) {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.15f, 0.22f, 0.4f));
ImGui::BeginChild("VisualSettingsCompact", ImVec2(0, 115), true, ImGuiWindowFlags_NoScrollbar);
{
ImGui::TextColored(kGanonPurple, ICON_MD_AUTO_AWESOME " Visual Effects");
ImGui::Spacing();
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.05f, 1.0f, "%.2fx");
ImGui::Checkbox(ICON_MD_MOUSE " Mouse Interaction", &triforce_mouse_repel_enabled_);
ImGui::SameLine();
ImGui::Checkbox(ICON_MD_AUTO_FIX_HIGH " Particles", &particles_enabled_);
if (ImGui::SmallButton(ICON_MD_REFRESH " Reset")) {
triforce_alpha_multiplier_ = 1.0f;
triforce_speed_multiplier_ = 0.3f;
triforce_size_multiplier_ = 1.0f;
triforce_mouse_repel_enabled_ = true;
particles_enabled_ = true;
particle_spawn_rate_ = 2.0f;
}
}
ImGui::EndChild();
ImGui::PopStyleColor();
ImGui::Spacing();
}
ImGui::Spacing();
struct Template {
@@ -706,10 +736,9 @@ void WelcomeScreen::DrawTemplatesSection() {
Template templates[] = {
{ICON_MD_COTTAGE, "Vanilla ALTTP", kHyruleGreen},
{ICON_MD_MAP, "ZSCustomOverworld v3", kMasterSwordBlue},
{ICON_MD_PUBLIC, "Parallel Worlds Base", kGanonPurple},
};
for (int i = 0; i < 3; ++i) {
for (int i = 0; i < 2; ++i) {
bool is_selected = (selected_template_ == i);
// Subtle selection highlight (no animation)
@@ -738,7 +767,7 @@ void WelcomeScreen::DrawTemplatesSection() {
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, kSpiritOrange);
ImGui::BeginDisabled(true);
ImGui::Button(absl::StrFormat("%s Use Template", ICON_MD_ROCKET_LAUNCH).c_str(),
ImVec2(-1, 35));
ImVec2(-1, 30)); // Reduced from 35 to 30
ImGui::EndDisabled();
ImGui::PopStyleColor(2);
}

View File

@@ -106,17 +106,32 @@ class WelcomeScreen {
int hovered_card_ = -1;
// Interactive triforce positions (smooth interpolation)
static constexpr int kNumTriforces = 16;
static constexpr int kNumTriforces = 6;
ImVec2 triforce_positions_[kNumTriforces] = {};
ImVec2 triforce_base_positions_[kNumTriforces] = {};
bool triforce_positions_initialized_ = false;
// Particle system
static constexpr int kMaxParticles = 50;
struct Particle {
ImVec2 position;
ImVec2 velocity;
float size;
float alpha;
float lifetime;
float max_lifetime;
};
Particle particles_[kMaxParticles] = {};
int active_particle_count_ = 0;
// Triforce animation settings
bool show_triforce_settings_ = false;
float triforce_alpha_multiplier_ = 1.0f;
float triforce_speed_multiplier_ = 1.0f;
float triforce_speed_multiplier_ = 0.3f; // Default slower speed
float triforce_size_multiplier_ = 1.0f;
bool triforce_mouse_repel_enabled_ = true;
bool particles_enabled_ = true;
float particle_spawn_rate_ = 2.0f; // Particles per second
};
} // namespace editor