scene configuration, neko opacity & scale

This commit is contained in:
2026-03-24 22:52:49 +08:00
parent 4093b0eb0c
commit 75ab799a7f
19 changed files with 427 additions and 34 deletions

View File

@@ -1,8 +1,9 @@
use crate::{
lock_r,
models::app_data::UserData,
models::{app_data::UserData, app_state::{AppState, NekoPosition}},
services::{
app_data::{init_app_data_scoped, AppDataRefreshScope},
app_state,
friends,
presence_modules::models::ModuleMetadata,
sprite,
@@ -45,3 +46,27 @@ pub fn get_friend_active_doll_sprites_base64() -> Result<friends::FriendActiveDo
friends::sync_active_doll_sprites_from_app_data();
Ok(friends::get_active_doll_sprites_snapshot())
}
#[tauri::command]
#[specta::specta]
pub fn get_app_state() -> Result<AppState, String> {
Ok(app_state::get_snapshot())
}
#[tauri::command]
#[specta::specta]
pub fn set_scene_setup_nekos_position(nekos_position: Option<NekoPosition>) {
app_state::set_scene_setup_nekos_position(nekos_position);
}
#[tauri::command]
#[specta::specta]
pub fn set_scene_setup_nekos_opacity(nekos_opacity: f32) {
app_state::set_scene_setup_nekos_opacity(nekos_opacity);
}
#[tauri::command]
#[specta::specta]
pub fn set_scene_setup_nekos_scale(nekos_scale: f32) {
app_state::set_scene_setup_nekos_scale(nekos_scale);
}

View File

@@ -1,14 +1,12 @@
use crate::{
commands::app_state::get_modules,
services::{
doll_editor::open_doll_editor_window,
scene::{get_scene_interactive, set_pet_menu_state, set_scene_interactive},
},
use crate::services::{
doll_editor::open_doll_editor_window,
scene::{get_scene_interactive, set_pet_menu_state, set_scene_interactive},
};
use commands::app::{quit_app, restart_app, retry_connection};
use commands::app_state::{
get_active_doll_sprite_base64, get_app_data, get_friend_active_doll_sprites_base64,
refresh_app_data,
get_active_doll_sprite_base64, get_app_data, get_app_state,
get_friend_active_doll_sprites_base64, get_modules, refresh_app_data,
set_scene_setup_nekos_opacity, set_scene_setup_nekos_position, set_scene_setup_nekos_scale,
};
use commands::auth::{logout_and_restart, start_discord_auth, start_google_auth};
use commands::config::{get_client_config, open_client_config, save_client_config};
@@ -27,11 +25,12 @@ use tauri::async_runtime;
use tauri_specta::{collect_commands, collect_events, Builder as SpectaBuilder, ErrorHandlingMode};
use crate::services::app_events::{
ActiveDollSpriteChanged, AppDataRefreshed, AuthFlowUpdated, CreateDoll, CursorMoved, EditDoll,
FriendActiveDollChanged, FriendActiveDollSpritesUpdated, FriendCursorPositionsUpdated,
FriendDisconnected, FriendRequestAccepted, FriendRequestDenied, FriendRequestReceived,
FriendUserStatusChanged, InteractionDeliveryFailed, InteractionReceived,
SceneInteractiveChanged, SetInteractionOverlay, Unfriended, UserStatusChanged,
ActiveDollSpriteChanged, AppDataRefreshed, AppStateChanged, AuthFlowUpdated, CreateDoll,
CursorMoved, EditDoll, FriendActiveDollChanged, FriendActiveDollSpritesUpdated,
FriendCursorPositionsUpdated, FriendDisconnected, FriendRequestAccepted,
FriendRequestDenied, FriendRequestReceived, FriendUserStatusChanged,
InteractionDeliveryFailed, InteractionReceived, SceneInteractiveChanged,
SetInteractionOverlay, Unfriended, UserStatusChanged,
};
static APP_HANDLE: std::sync::OnceLock<tauri::AppHandle<tauri::Wry>> = std::sync::OnceLock::new();
@@ -68,6 +67,7 @@ pub fn run() {
.error_handling(ErrorHandlingMode::Throw)
.commands(collect_commands![
get_app_data,
get_app_state,
get_active_doll_sprite_base64,
get_friend_active_doll_sprites_base64,
refresh_app_data,
@@ -102,12 +102,16 @@ pub fn run() {
start_discord_auth,
logout_and_restart,
send_interaction_cmd,
get_modules
get_modules,
set_scene_setup_nekos_position,
set_scene_setup_nekos_opacity,
set_scene_setup_nekos_scale
])
.events(collect_events![
CursorMoved,
SceneInteractiveChanged,
AppDataRefreshed,
AppStateChanged,
ActiveDollSpriteChanged,
SetInteractionOverlay,
EditDoll,

View File

@@ -0,0 +1,39 @@
use serde::{Deserialize, Serialize};
use specta::Type;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
#[serde(rename_all = "kebab-case")]
pub enum NekoPosition {
TopLeft,
Top,
TopRight,
Left,
Right,
BottomLeft,
Bottom,
BottomRight,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
#[serde(rename_all = "camelCase")]
pub struct SceneSetup {
pub nekos_position: Option<NekoPosition>,
pub nekos_opacity: f32,
pub nekos_scale: f32,
}
impl Default for SceneSetup {
fn default() -> Self {
Self {
nekos_position: None,
nekos_opacity: 1.0,
nekos_scale: 1.0,
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type, Default)]
#[serde(rename_all = "camelCase")]
pub struct AppState {
pub scene_setup: SceneSetup,
}

View File

@@ -1,4 +1,5 @@
pub mod app_data;
pub mod app_state;
pub mod dolls;
pub mod event_payloads;
pub mod friends;

View File

@@ -5,6 +5,7 @@ use tauri_specta::Event;
use crate::{
models::{
app_data::UserData,
app_state::AppState,
event_payloads::{
FriendActiveDollChangedPayload, FriendDisconnectedPayload,
FriendRequestAcceptedPayload, FriendRequestDeniedPayload, FriendRequestReceivedPayload,
@@ -47,6 +48,10 @@ pub struct SceneInteractiveChanged(pub bool);
#[tauri_specta(event_name = "app-data-refreshed")]
pub struct AppDataRefreshed(pub UserData);
#[derive(Debug, Clone, Serialize, Deserialize, Type, Event)]
#[tauri_specta(event_name = "app-state-changed")]
pub struct AppStateChanged(pub AppState);
#[derive(Debug, Clone, Serialize, Deserialize, Type, Event)]
#[tauri_specta(event_name = "active-doll-sprite-changed")]
pub struct ActiveDollSpriteChanged(pub Option<String>);

View File

@@ -0,0 +1,42 @@
use std::sync::{Arc, LazyLock, RwLock};
use tauri_specta::Event as _;
use tracing::warn;
use crate::{
get_app_handle,
models::app_state::{AppState, NekoPosition},
services::app_events::AppStateChanged,
};
static APP_STATE: LazyLock<Arc<RwLock<AppState>>> =
LazyLock::new(|| Arc::new(RwLock::new(AppState::default())));
pub fn get_snapshot() -> AppState {
let guard = APP_STATE.read().expect("app state lock poisoned");
guard.clone()
}
pub fn set_scene_setup_nekos_position(nekos_position: Option<NekoPosition>) {
let mut guard = APP_STATE.write().expect("app state lock poisoned");
guard.scene_setup.nekos_position = nekos_position;
emit_snapshot(&guard);
}
pub fn set_scene_setup_nekos_opacity(nekos_opacity: f32) {
let mut guard = APP_STATE.write().expect("app state lock poisoned");
guard.scene_setup.nekos_opacity = nekos_opacity.clamp(0.1, 1.0);
emit_snapshot(&guard);
}
pub fn set_scene_setup_nekos_scale(nekos_scale: f32) {
let mut guard = APP_STATE.write().expect("app state lock poisoned");
guard.scene_setup.nekos_scale = nekos_scale.clamp(0.5, 2.0);
emit_snapshot(&guard);
}
fn emit_snapshot(app_state: &AppState) {
if let Err(error) = AppStateChanged(app_state.clone()).emit(get_app_handle()) {
warn!("Failed to emit app-state-changed event: {}", error);
}
}

View File

@@ -1,5 +1,6 @@
pub mod app_data;
pub mod app_events;
pub mod app_state;
pub mod app_menu;
pub mod app_update;
pub mod accelerators;