From 77472d43f8c1412fba7418283fe4676e6fe79e0c Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Wed, 7 Jan 2026 01:46:46 +0800 Subject: [PATCH] pet menu persistence --- src-tauri/src/lib.rs | 3 +- src-tauri/src/services/scene.rs | 60 +++++++++++++++++++++++++++++- src/routes/scene/+page.svelte | 1 + src/routes/scene/DesktopPet.svelte | 35 +++++++++++++---- 4 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 7862141..b8e64d8 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -14,7 +14,7 @@ use crate::{ }, cursor::start_cursor_tracking, doll_editor::open_doll_editor_window, - scene::{open_splash_window, set_scene_interactive}, + scene::{open_splash_window, set_pet_menu_state, set_scene_interactive}, }, state::{init_app_data, init_app_data_scoped, AppDataRefreshScope, FDOLL}, }; @@ -425,6 +425,7 @@ pub fn run() { open_client_config_manager, open_doll_editor_window, set_scene_interactive, + set_pet_menu_state, start_auth_flow, logout_and_restart ]) diff --git a/src-tauri/src/services/scene.rs b/src-tauri/src/services/scene.rs index 4402236..71649bf 100644 --- a/src-tauri/src/services/scene.rs +++ b/src-tauri/src/services/scene.rs @@ -16,6 +16,16 @@ pub static SPLASH_WINDOW_LABEL: &str = "splash"; static SCENE_INTERACTIVE_STATE: OnceCell> = OnceCell::new(); static MODIFIER_LISTENER_INIT: OnceCell<()> = OnceCell::new(); +// New: Track which pets have open menus +static OPEN_PET_MENUS: OnceCell>>> = + OnceCell::new(); + +fn get_open_pet_menus() -> Arc>> { + OPEN_PET_MENUS + .get_or_init(|| Arc::new(std::sync::Mutex::new(std::collections::HashSet::new()))) + .clone() +} + fn scene_interactive_state() -> Arc { SCENE_INTERACTIVE_STATE .get_or_init(|| Arc::new(AtomicBool::new(false))) @@ -25,6 +35,14 @@ fn scene_interactive_state() -> Arc { pub fn update_scene_interactive(interactive: bool) { let app_handle = get_app_handle(); + // If we are forcing interactive to false (e.g. background click), clear any open menus + // This prevents the loop from immediately re-enabling it if the frontend hasn't updated yet + if !interactive { + if let Ok(mut menus) = get_open_pet_menus().lock() { + menus.clear(); + } + } + if let Some(window) = app_handle.get_window(SCENE_WINDOW_LABEL) { if let Err(e) = window.set_ignore_cursor_events(!interactive) { error!("Failed to toggle scene cursor events: {}", e); @@ -43,6 +61,33 @@ pub fn set_scene_interactive(interactive: bool) { update_scene_interactive(interactive); } +#[tauri::command] +pub fn set_pet_menu_state(id: String, open: bool) { + let menus_arc = get_open_pet_menus(); + let should_update = { + if let Ok(mut menus) = menus_arc.lock() { + if open { + menus.insert(id); + } else { + menus.remove(&id); + } + !menus.is_empty() + } else { + false + } + }; + + // After updating state, re-evaluate interactivity immediately + // We don't have direct access to key state here easily without recalculating everything, + // but the loop will pick it up shortly. + // HOWEVER, if we just closed the last menu and keys aren't held, we might want to ensure it closes fast. + // For now, let the loop handle it to avoid race conditions with key states. + // But if we just OPENED a menu, we definitely want to ensure interactive is TRUE. + if should_update { + update_scene_interactive(true); + } +} + #[cfg(target_os = "macos")] #[link(name = "ApplicationServices", kind = "framework")] extern "C" { @@ -96,11 +141,22 @@ fn start_scene_modifier_listener() { loop { let keys = device_state.get_keys(); // Check for Alt key (Option on Mac) - let interactive = (keys.contains(&Keycode::LAlt) || keys.contains(&Keycode::RAlt)) || keys.contains(&Keycode::Command); + let keys_interactive = (keys.contains(&Keycode::LAlt) || keys.contains(&Keycode::RAlt)) || keys.contains(&Keycode::Command); + + // Check if any pet menus are open + let menus_open = { + if let Ok(menus) = get_open_pet_menus().lock() { + !menus.is_empty() + } else { + false + } + }; + + let interactive = keys_interactive || menus_open; if interactive != last_interactive { // State changed - info!("Key down state chanegd!"); + info!("Interactive state changed to: {}", interactive); let previous = state.swap(interactive, Ordering::SeqCst); if previous != interactive { update_scene_interactive(interactive); diff --git a/src/routes/scene/+page.svelte b/src/routes/scene/+page.svelte index cca04e7..4934457 100644 --- a/src/routes/scene/+page.svelte +++ b/src/routes/scene/+page.svelte @@ -88,6 +88,7 @@ {@const config = getFriendDollConfig(userId)} {#if config} import { onMount, onDestroy } from "svelte"; + import { invoke } from "@tauri-apps/api/core"; import { usePetState } from "$lib/composables/usePetState"; import { getSpriteSheetUrl } from "$lib/utils/sprite-utils"; import PetSprite from "$lib/components/PetSprite.svelte"; import onekoGif from "../../assets/oneko/oneko.gif"; + export let id = ""; export let targetX = 0; export let targetY = 0; export let name = ""; @@ -21,9 +23,19 @@ let lastFrameTimestamp: number; let spriteSheetUrl = onekoGif; + let isPetMenuOpen = false; + // Watch for color changes to regenerate sprite $: updateSprite(bodyColor, outlineColor); + $: (isInteractive, (isPetMenuOpen = false)); + + $: { + if (id) { + invoke("set_pet_menu_state", { id, open: isPetMenuOpen }); + } + } + async function updateSprite( body: string | undefined, outline: string | undefined, @@ -65,23 +77,32 @@ }); - {name} - +