diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 4880eed..3478b98 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1789,6 +1789,17 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mouse_position" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd272ac3ad817af3d158eecd4d4ba6f92a1fa5f7255e3c0d63b72d4dac12edd" +dependencies = [ + "core-graphics", + "winapi", + "x11-dl", +] + [[package]] name = "ndk" version = "0.6.0" @@ -3774,6 +3785,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" name = "window_pet" version = "0.0.0" dependencies = [ + "mouse_position", "serde", "serde_json", "tauri", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7b7375f..1d8f590 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -16,6 +16,7 @@ tauri-build = { version = "1.4", features = [] } tauri = { version = "1.4.0", features = [ "window-all", "dialog-all", "fs-all", "path-all", "macos-private-api", "system-tray", "shell-open"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +mouse_position = "0.1.3" tauri-plugin-autostart = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } diff --git a/src-tauri/src/app/cmd.rs b/src-tauri/src/app/cmd.rs new file mode 100644 index 0000000..f5f4454 --- /dev/null +++ b/src-tauri/src/app/cmd.rs @@ -0,0 +1,23 @@ +use mouse_position::mouse_position::Mouse; +use serde_json::json; + +#[tauri::command] +pub fn get_mouse_position() -> serde_json::Value { + /* + * because we set the window to ignore cursor events, we cannot use + * javascript to get the mouse position, so we use get mouse position manually + */ + let position = Mouse::get_mouse_position(); + match position { + Mouse::Position { x, y } => { + json!({ + "clientX": x, + "clientY": y + }) + } + Mouse::Error => { + println!("Error getting mouse position"); + json!(null) + } + } +} diff --git a/src-tauri/src/app/default/settings.json b/src-tauri/src/app/default/settings.json index bac9334..59c19a5 100644 --- a/src-tauri/src/app/default/settings.json +++ b/src-tauri/src/app/default/settings.json @@ -1,5 +1,6 @@ { "theme": "dark", "language": "en", - "isPetAboveTaskbar": false + "isPetAboveTaskbar": false, + "isAllowHoverOnPet": true } \ No newline at end of file diff --git a/src-tauri/src/app/mod.rs b/src-tauri/src/app/mod.rs index fdeea04..c4df169 100644 --- a/src-tauri/src/app/mod.rs +++ b/src-tauri/src/app/mod.rs @@ -1,3 +1,4 @@ pub mod conf; pub mod tray; -pub mod utils; \ No newline at end of file +pub mod utils; +pub mod cmd; \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 100d3e0..17fe4bc 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -2,6 +2,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod app; +use app::cmd::get_mouse_position; use app::conf::{combine_config_path, convert_path, if_app_config_does_not_exist_create_default}; use app::tray::{handle_tray_event, init_system_tray}; use tauri::Manager; @@ -39,7 +40,11 @@ fn main() { }) .system_tray(init_system_tray()) .on_system_tray_event(handle_tray_event) - .invoke_handler(tauri::generate_handler![convert_path, combine_config_path]) + .invoke_handler(tauri::generate_handler![ + convert_path, + combine_config_path, + get_mouse_position + ]) .build(tauri::generate_context!()) .expect("error while running tauri application") .run(|_app_handle, event| { diff --git a/src/Canvas.tsx b/src/Canvas.tsx index 8991aff..ff642b0 100644 --- a/src/Canvas.tsx +++ b/src/Canvas.tsx @@ -3,6 +3,7 @@ import { usePetStore } from "./hooks/usePetStore"; import { listen } from '@tauri-apps/api/event'; import { clonePetsFromSettings } from "./utils/clonePetsFromSettings"; import { useSettingStore } from "./hooks/useSettingStore"; +import { TRenderEventListener } from "./types/IEvents"; function Canvas() { // credit: https://stackoverflow.com/questions/16277383/javascript-screen-height-and-screen-width-returns-incorrect-values @@ -14,7 +15,7 @@ function Canvas() { const requestAnimateFrameId = useRef(0); const { pets, isPetsInitialized, setIsPetsInitialized } = usePetStore(); - const { setIsPetAboveTaskbar } = useSettingStore(); + const { setIsPetAboveTaskbar, setIsAllowHoverOnPet } = useSettingStore(); // disable right click (context menu) for build version only. uncomment for development // credit: https://github.com/tauri-apps/wry/issues/30 @@ -22,8 +23,17 @@ function Canvas() { useEffect(() => { let unListen: () => void; - listen('render', (event) => { - setIsPetAboveTaskbar(event.payload!.isPetAboveTaskbar); + listen('render', (event: TRenderEventListener) => { + switch (event.payload.dispatchType) { + case 'switchPetAboveTaskBar': + setIsPetAboveTaskbar(event.payload!.value as boolean); + break; + case 'switchAllowHoverOnPet': + setIsAllowHoverOnPet(event.payload!.value as boolean); + break; + default: + return; + } clonePetsFromSettings(); }).then((unListenFn) => { unListen = unListenFn; diff --git a/src/SettingWindow.tsx b/src/SettingWindow.tsx index d8d424b..ff63e5e 100644 --- a/src/SettingWindow.tsx +++ b/src/SettingWindow.tsx @@ -28,7 +28,6 @@ function SettingWindow() { // disable right click (context menu) for build version only. uncomment for development // credit: https://github.com/tauri-apps/wry/issues/30 document.addEventListener('contextmenu', event => event.preventDefault()); - // get object theme change it name to colorScheme for readability const { theme: colorScheme, language } = useSettingStore(); const { t, i18n } = useTranslation(); @@ -50,6 +49,7 @@ function SettingWindow() { } let CurrentSettingTab = SettingTabComponent[page]; + if (!CurrentSettingTab) { CurrentSettingTab = memo(() => {t("Seem like the content of this page doesn't exist or has not been updated")}); } @@ -102,4 +102,4 @@ function SettingWindow() { ); } -export default SettingWindow; \ No newline at end of file +export default memo(SettingWindow); \ No newline at end of file diff --git a/src/__tests__/functions/function.test.ts b/src/__tests__/functions/function.test.ts index f4857bd..da175f6 100644 --- a/src/__tests__/functions/function.test.ts +++ b/src/__tests__/functions/function.test.ts @@ -4,7 +4,7 @@ */ import { mockIPC, mockWindows, clearMocks } from '@tauri-apps/api/mocks' -import { getAppSettings } from "../../utils/settingsHelper"; +import { getAppSettings } from "../../utils/settings"; import { expect, test, afterEach } from "vitest"; afterEach(() => { diff --git a/src/class/Pet.ts b/src/class/Pet.ts index d5d40a5..3610bba 100644 --- a/src/class/Pet.ts +++ b/src/class/Pet.ts @@ -2,9 +2,10 @@ * A good resource to learn about canvas, the project is based on this tutorial. * credit: https://www.youtube.com/watch?v=vyqbNFMDRGQ&t=8593s&ab_channel=ChrisCourses */ +import { invoke } from "@tauri-apps/api"; import { useSettingStore } from "../hooks/useSettingStore"; import { IPetParams, TStates, TCurrentPetState } from "../types/IPet"; - +import { IMousePosition } from "../types/IMousePosition"; export default class Pet { position: { x: number; y: number }; name: string; @@ -130,16 +131,39 @@ export default class Pet { } - if (this.isBeingSelected) { - context.strokeStyle = "red"; - context.lineWidth = 1; - context.strokeRect( - dx_start_draw_position, - dy_start_draw_position - this.offset.y * this.scale, - dWidth_draw_width, - dHeight_draw_height - ) + const isAllowHoverOnPet = useSettingStore.getState().isAllowHoverOnPet; + if (isAllowHoverOnPet) { + invoke('get_mouse_position').then((mousePosition) => { + // if hover on the pet set isBeingSelected to true + if ( + mousePosition.clientX >= dx_start_draw_position && + mousePosition.clientX <= dx_start_draw_position + dWidth_draw_width && + mousePosition.clientY >= dy_start_draw_position - this.offset.y * this.scale && + mousePosition.clientY <= dy_start_draw_position + dHeight_draw_height - this.offset.y * this.scale + ) { + this.isBeingSelected = true; + // check state before switch because not all pets have the same states + if (this.states.meow) this.switchState('meow'); + else if (this.states.attack) this.switchState('attack'); + return + } + + this.isBeingSelected = false; + if (this.states.walk) this.switchState('walk'); + else if (this.states.run) this.switchState('run'); + }); } + + // if (this.isBeingSelected) { + // context.strokeStyle = "red"; + // context.lineWidth = 1; + // context.strokeRect( + // dx_start_draw_position, + // dy_start_draw_position - this.offset.y * this.scale, + // dWidth_draw_width, + // dHeight_draw_height + // ) + // } } // used to control the animation speed, diff --git a/src/default/defaultPetExport.ts b/src/default/defaultPetExport.ts new file mode 100644 index 0000000..6266298 --- /dev/null +++ b/src/default/defaultPetExport.ts @@ -0,0 +1,4 @@ +import { IPetParams } from '../types/IPet'; +import defaultPetConfig from './pets.json'; + +export const defaultPetOptions: IPetParams[] = JSON.parse(JSON.stringify(defaultPetConfig)); diff --git a/src/default/pets.json b/src/default/pets.json new file mode 100644 index 0000000..c67f4c9 --- /dev/null +++ b/src/default/pets.json @@ -0,0 +1,598 @@ +[ + { + "name": "Orange Cat", + "position": { + "x": 0, + "y": 64 + }, + "velocity": { + "x": 0, + "y": 0 + }, + "offset": { + "x": 0, + "y": 18 + }, + "scale": 2, + "walkSpeed": 0.3, + "runSpeed": 1.2, + "states": { + "idle": { + "imageSrc": "media/Cat-1/Cat-1-Idle.png", + "framesMax": 10, + "stateHold": 500, + "framesHold": 8 + }, + "itch": { + "imageSrc": "media/Cat-1/Cat-1-Itch.png", + "framesMax": 2, + "stateHold": 1000, + "framesHold": 8 + }, + "licking": { + "imageSrc": "media/Cat-1/Cat-1-Licking 1.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "licking2": { + "imageSrc": "media/Cat-1/Cat-1-Licking 2.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "meow": { + "imageSrc": "media/Cat-1/Cat-1-Meow.png", + "framesMax": 4, + "stateHold": 400, + "framesHold": 8 + }, + "sitting": { + "imageSrc": "media/Cat-1/Cat-1-Sitting.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "laying": { + "imageSrc": "media/Cat-1/Cat-1-Laying.png", + "framesMax": 8, + "stateHold": 100, + "framesHold": 8 + }, + "sleeping": { + "imageSrc": "media/Cat-1/Cat-1-Sleeping1.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "sleeping2": { + "imageSrc": "media/Cat-1/Cat-1-Sleeping2.png", + "framesMax": 1, + "stateHold": 1000, + "framesHold": 8 + }, + "stretching": { + "imageSrc": "media/Cat-1/Cat-1-Stretching.png", + "framesMax": 13, + "stateHold": 250, + "framesHold": 8 + }, + "walk": { + "imageSrc": "media/Cat-1/Cat-1-Walk.png", + "framesMax": 8, + "stateHold": 1750, + "framesHold": 8 + }, + "run": { + "imageSrc": "media/Cat-1/Cat-1-Run.png", + "framesMax": 8, + "stateHold": 2000, + "framesHold": 5 + } + } + }, + { + "name": "Black Cat", + "position": { + "x": 0, + "y": 64 + }, + "velocity": { + "x": 0, + "y": 0 + }, + "offset": { + "x": 0, + "y": 18 + }, + "scale": 2, + "walkSpeed": 0.3, + "runSpeed": 1.2, + "states": { + "idle": { + "imageSrc": "media/Cat-2/Cat-2-Idle.png", + "framesMax": 10, + "stateHold": 500, + "framesHold": 8 + }, + "itch": { + "imageSrc": "media/Cat-2/Cat-2-Itch.png", + "framesMax": 2, + "stateHold": 1000, + "framesHold": 8 + }, + "licking": { + "imageSrc": "media/Cat-2/Cat-2-Licking 1.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "licking2": { + "imageSrc": "media/Cat-2/Cat-2-Licking 2.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "meow": { + "imageSrc": "media/Cat-2/Cat-2-Meow.png", + "framesMax": 4, + "stateHold": 400, + "framesHold": 8 + }, + "sitting": { + "imageSrc": "media/Cat-2/Cat-2-Sitting.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "laying": { + "imageSrc": "media/Cat-2/Cat-2-Laying.png", + "framesMax": 8, + "stateHold": 100, + "framesHold": 8 + }, + "sleeping": { + "imageSrc": "media/Cat-2/Cat-2-Sleeping1.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "sleeping2": { + "imageSrc": "media/Cat-2/Cat-2-Sleeping2.png", + "framesMax": 1, + "stateHold": 1000, + "framesHold": 8 + }, + "stretching": { + "imageSrc": "media/Cat-2/Cat-2-Stretching.png", + "framesMax": 13, + "stateHold": 250, + "framesHold": 8 + }, + "walk": { + "imageSrc": "media/Cat-2/Cat-2-Walk.png", + "framesMax": 8, + "stateHold": 1750, + "framesHold": 8 + }, + "run": { + "imageSrc": "media/Cat-2/Cat-2-Run.png", + "framesMax": 8, + "stateHold": 2000, + "framesHold": 5 + } + } + }, + { + "name": "White-Brown Cat", + "position": { + "x": 0, + "y": 64 + }, + "velocity": { + "x": 0, + "y": 0 + }, + "offset": { + "x": 0, + "y": 18 + }, + "scale": 2, + "walkSpeed": 0.3, + "runSpeed": 1.2, + "states": { + "idle": { + "imageSrc": "media/Cat-3/Cat-3-Idle.png", + "framesMax": 10, + "stateHold": 500, + "framesHold": 8 + }, + "itch": { + "imageSrc": "media/Cat-3/Cat-3-Itch.png", + "framesMax": 2, + "stateHold": 1000, + "framesHold": 8 + }, + "licking": { + "imageSrc": "media/Cat-3/Cat-3-Licking 1.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "licking2": { + "imageSrc": "media/Cat-3/Cat-3-Licking 2.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "meow": { + "imageSrc": "media/Cat-3/Cat-3-Meow.png", + "framesMax": 4, + "stateHold": 400, + "framesHold": 8 + }, + "sitting": { + "imageSrc": "media/Cat-3/Cat-3-Sitting.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "laying": { + "imageSrc": "media/Cat-3/Cat-3-Laying.png", + "framesMax": 8, + "stateHold": 100, + "framesHold": 8 + }, + "sleeping": { + "imageSrc": "media/Cat-3/Cat-3-Sleeping1.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "sleeping2": { + "imageSrc": "media/Cat-3/Cat-3-Sleeping2.png", + "framesMax": 1, + "stateHold": 1000, + "framesHold": 8 + }, + "stretching": { + "imageSrc": "media/Cat-3/Cat-3-Stretching.png", + "framesMax": 13, + "stateHold": 250, + "framesHold": 8 + }, + "walk": { + "imageSrc": "media/Cat-3/Cat-3-Walk.png", + "framesMax": 8, + "stateHold": 1750, + "framesHold": 8 + }, + "run": { + "imageSrc": "media/Cat-3/Cat-3-Run.png", + "framesMax": 8, + "stateHold": 2000, + "framesHold": 5 + } + } + }, + { + "name": "Brown-Gray Cat", + "position": { + "x": 0, + "y": 64 + }, + "velocity": { + "x": 0, + "y": 0 + }, + "offset": { + "x": 0, + "y": 18 + }, + "scale": 2, + "walkSpeed": 0.3, + "runSpeed": 1.2, + "states": { + "idle": { + "imageSrc": "media/Cat-4/Cat-4-Idle.png", + "framesMax": 10, + "stateHold": 500, + "framesHold": 8 + }, + "itch": { + "imageSrc": "media/Cat-4/Cat-4-Itch.png", + "framesMax": 2, + "stateHold": 1000, + "framesHold": 8 + }, + "licking": { + "imageSrc": "media/Cat-4/Cat-4-Licking 1.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "licking2": { + "imageSrc": "media/Cat-4/Cat-4-Licking 2.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "meow": { + "imageSrc": "media/Cat-4/Cat-4-Meow.png", + "framesMax": 4, + "stateHold": 400, + "framesHold": 8 + }, + "sitting": { + "imageSrc": "media/Cat-4/Cat-4-Sitting.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "laying": { + "imageSrc": "media/Cat-4/Cat-4-Laying.png", + "framesMax": 8, + "stateHold": 100, + "framesHold": 8 + }, + "sleeping": { + "imageSrc": "media/Cat-4/Cat-4-Sleeping1.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "sleeping2": { + "imageSrc": "media/Cat-4/Cat-4-Sleeping2.png", + "framesMax": 1, + "stateHold": 1000, + "framesHold": 8 + }, + "stretching": { + "imageSrc": "media/Cat-4/Cat-4-Stretching.png", + "framesMax": 13, + "stateHold": 250, + "framesHold": 8 + }, + "walk": { + "imageSrc": "media/Cat-4/Cat-4-Walk.png", + "framesMax": 8, + "stateHold": 1750, + "framesHold": 8 + }, + "run": { + "imageSrc": "media/Cat-4/Cat-4-Run.png", + "framesMax": 8, + "stateHold": 2000, + "framesHold": 5 + } + } + }, + { + "name": "White-Gray Cat", + "position": { + "x": 0, + "y": 64 + }, + "velocity": { + "x": 0, + "y": 0 + }, + "offset": { + "x": 0, + "y": 18 + }, + "scale": 2, + "walkSpeed": 0.3, + "runSpeed": 1.2, + "states": { + "idle": { + "imageSrc": "media/Cat-5/Cat-5-Idle.png", + "framesMax": 10, + "stateHold": 500, + "framesHold": 8 + }, + "itch": { + "imageSrc": "media/Cat-5/Cat-5-Itch.png", + "framesMax": 2, + "stateHold": 1000, + "framesHold": 8 + }, + "licking": { + "imageSrc": "media/Cat-5/Cat-5-Licking 1.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "licking2": { + "imageSrc": "media/Cat-5/Cat-5-Licking 2.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "meow": { + "imageSrc": "media/Cat-5/Cat-5-Meow.png", + "framesMax": 4, + "stateHold": 400, + "framesHold": 8 + }, + "sitting": { + "imageSrc": "media/Cat-5/Cat-5-Sitting.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "laying": { + "imageSrc": "media/Cat-5/Cat-5-Laying.png", + "framesMax": 8, + "stateHold": 100, + "framesHold": 8 + }, + "sleeping": { + "imageSrc": "media/Cat-5/Cat-5-Sleeping1.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "sleeping2": { + "imageSrc": "media/Cat-5/Cat-5-Sleeping2.png", + "framesMax": 1, + "stateHold": 1000, + "framesHold": 8 + }, + "stretching": { + "imageSrc": "media/Cat-5/Cat-5-Stretching.png", + "framesMax": 13, + "stateHold": 250, + "framesHold": 8 + }, + "walk": { + "imageSrc": "media/Cat-5/Cat-5-Walk.png", + "framesMax": 8, + "stateHold": 1750, + "framesHold": 8 + }, + "run": { + "imageSrc": "media/Cat-5/Cat-5-Run.png", + "framesMax": 8, + "stateHold": 2000, + "framesHold": 5 + } + } + }, + { + "name": "Tiger-Gray Cat", + "position": { + "x": 0, + "y": 64 + }, + "velocity": { + "x": 0, + "y": 0 + }, + "offset": { + "x": 0, + "y": 18 + }, + "scale": 2, + "walkSpeed": 0.3, + "runSpeed": 1.2, + "states": { + "idle": { + "imageSrc": "media/Cat-6/Cat-6-Idle.png", + "framesMax": 10, + "stateHold": 500, + "framesHold": 8 + }, + "itch": { + "imageSrc": "media/Cat-6/Cat-6-Itch.png", + "framesMax": 2, + "stateHold": 1000, + "framesHold": 8 + }, + "licking": { + "imageSrc": "media/Cat-6/Cat-6-Licking 1.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "licking2": { + "imageSrc": "media/Cat-6/Cat-6-Licking 2.png", + "framesMax": 5, + "stateHold": 500, + "framesHold": 8 + }, + "meow": { + "imageSrc": "media/Cat-6/Cat-6-Meow.png", + "framesMax": 4, + "stateHold": 400, + "framesHold": 8 + }, + "sitting": { + "imageSrc": "media/Cat-6/Cat-6-Sitting.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "laying": { + "imageSrc": "media/Cat-6/Cat-6-Laying.png", + "framesMax": 8, + "stateHold": 100, + "framesHold": 8 + }, + "sleeping": { + "imageSrc": "media/Cat-6/Cat-6-Sleeping1.png", + "framesMax": 1, + "stateHold": 500, + "framesHold": 8 + }, + "sleeping2": { + "imageSrc": "media/Cat-6/Cat-6-Sleeping2.png", + "framesMax": 1, + "stateHold": 1000, + "framesHold": 8 + }, + "stretching": { + "imageSrc": "media/Cat-6/Cat-6-Stretching.png", + "framesMax": 13, + "stateHold": 250, + "framesHold": 8 + }, + "walk": { + "imageSrc": "media/Cat-6/Cat-6-Walk.png", + "framesMax": 8, + "stateHold": 1750, + "framesHold": 8 + }, + "run": { + "imageSrc": "media/Cat-6/Cat-6-Run.png", + "framesMax": 8, + "stateHold": 2000, + "framesHold": 5 + } + } + }, + { + "name": "Fire Wizard", + "position": { + "x": 0, + "y": 103 + }, + "velocity": { + "x": 0, + "y": 0 + }, + "offset": { + "x": 0, + "y": 18 + }, + "scale": 1, + "walkSpeed": 0.3, + "runSpeed": 1.2, + "states": { + "idle": { + "imageSrc": "media/Evil-Wizard/Idle.png", + "framesMax": 8, + "stateHold": 500, + "framesHold": 8 + }, + "run": { + "imageSrc": "media/Evil-Wizard/Move.png", + "framesMax": 8, + "stateHold": 2000, + "framesHold": 5 + }, + "attack": { + "imageSrc": "media/Evil-Wizard/Attack.png", + "framesMax": 8, + "stateHold": 500, + "framesHold": 8 + }, + "death": { + "imageSrc": "media/Evil-Wizard/Death.png", + "framesMax": 5, + "stateHold": 48, + "framesHold": 8 + } + } + } +] \ No newline at end of file diff --git a/src/default/readme.md b/src/default/readme.md new file mode 100644 index 0000000..3579d4b --- /dev/null +++ b/src/default/readme.md @@ -0,0 +1,3 @@ +# Guide + +`pets.json` in this directory is for adding default pets. \ No newline at end of file diff --git a/src/hooks/useSettingStore.tsx b/src/hooks/useSettingStore.tsx index 5c85714..556d8a1 100644 --- a/src/hooks/useSettingStore.tsx +++ b/src/hooks/useSettingStore.tsx @@ -1,6 +1,6 @@ import { create } from "zustand"; import { ColorScheme } from "@mantine/core"; -import { getAppSettings } from "../utils/settingsHelper"; +import { getAppSettings } from "../utils/settings"; import { TAppSetting } from "../types/ISetting"; import { isEnabled } from "tauri-plugin-autostart-api"; @@ -13,6 +13,8 @@ interface ISettingStore { setIsPetAboveTaskbar: (newBoolean: boolean) => void; isAutoStartUp: boolean; setIsAutoStartUp: (newBoolean: boolean) => void; + isAllowHoverOnPet: boolean; + setIsAllowHoverOnPet: (newBoolean: boolean) => void; petConfig: any[]; setPetConfig: (newConfig: any[]) => void; } @@ -24,15 +26,15 @@ let [settings, autoStartUpEnabled] = await Promise.all([ // initialize settings export const useSettingStore = create()((set) => ({ - language: settings.language, + language: settings.language || "en", setLanguage: (newLanguage) => { set({ language: newLanguage }) }, - theme: settings.theme, + theme: settings.theme || "dark", setTheme: (newTheme) => { set({ theme: newTheme }) }, - isPetAboveTaskBar: settings.isPetAboveTaskbar, + isPetAboveTaskBar: settings.isPetAboveTaskbar || false, setIsPetAboveTaskbar: (newBoolean) => { set({ isPetAboveTaskBar: newBoolean }) }, @@ -40,6 +42,10 @@ export const useSettingStore = create()((set) => ({ setIsAutoStartUp: (newBoolean) => { set({ isAutoStartUp: newBoolean }) }, + isAllowHoverOnPet: settings.isAllowHoverOnPet || true, + setIsAllowHoverOnPet: (newBoolean) => { + set({ isAllowHoverOnPet: newBoolean }) + }, petConfig: [], setPetConfig: (newConfig) => { set({ petConfig: newConfig }); diff --git a/src/i18next.ts b/src/i18next.ts index d476928..a660c30 100644 --- a/src/i18next.ts +++ b/src/i18next.ts @@ -2,7 +2,7 @@ import English from "./locale/en/translation.json"; import Khmer from "./locale/kh/translation.json"; import i18next from "i18next"; import { initReactI18next } from "react-i18next"; -import { getAppSettings } from "./utils/settingsHelper"; +import { getAppSettings } from "./utils/settings"; const defaultLanguage = 'en'; getAppSettings({}).then((settings) => { diff --git a/src/locale/en/translation.json b/src/locale/en/translation.json index 542f4d6..6a236f9 100644 --- a/src/locale/en/translation.json +++ b/src/locale/en/translation.json @@ -22,5 +22,10 @@ "Pet Name": "Pet Name", "Pet States": "Pet States", "Animate duration": "Animate duration", - "It is the time it takes for an animation to complete one cycle. If the animate duration exceeds the default animation time, it will reset to the default animation and repeat": "It is the time it takes for an animation to complete one cycle. If the animate duration exceeds the default animation time, it will reset to the default animation and repeat" + "It is the time it takes for an animation to complete one cycle. If the animate duration exceeds the default animation time, it will reset to the default animation and repeat": "It is the time it takes for an animation to complete one cycle. If the animate duration exceeds the default animation time, it will reset to the default animation and repeat", + "Animation speed, the smaller the value, the faster the animation": "Animation speed, the smaller the value, the faster the animation", + "Attention": "Attention", + "We have already tested and fine-tuned the default pet configuration to provide the best user experience. You only need to change the name of your pet. Making other changes to the configuration may prevent you from seeing your pet after adding it": "We have already tested and fine-tuned the default pet configuration to provide the best user experience. You only need to change the name of your pet. Making other changes to the configuration may prevent you from seeing your pet after adding it", + "Allow hover on pet": "Allow hover on pet", + "Pets will switch state when u hover on them": "Pets will switch state when u hover on them" } \ No newline at end of file diff --git a/src/locale/kh/translation.json b/src/locale/kh/translation.json index 7aef9cb..ce3b89a 100644 --- a/src/locale/kh/translation.json +++ b/src/locale/kh/translation.json @@ -22,5 +22,10 @@ "Pet Name": "ឈ្មោះសត្វចិញ្ចឹម", "Pet States": "ចលនាសត្វចិញ្ចឹម", "Animate duration": "រយៈពេលនៃចលនាសត្វចិញ្ចឹម", - "It is the time it takes for an animation to complete one cycle. If the animate duration exceeds the default animation time, it will reset to the default animation and repeat": "វាជាពេលវេលាដែលវាត្រូវការសម្រាប់ចលនាដើម្បីបញ្ចប់វដ្តមួយ។ ប្រសិនបើរយៈពេលចលនាលើសពីពេលវេលាចលនាលំនាំដើម វានឹងកំណត់ឡើងវិញទៅចលនាលំនាំដើម ហើយធ្វើម្តងទៀត" + "It is the time it takes for an animation to complete one cycle. If the animate duration exceeds the default animation time, it will reset to the default animation and repeat": "វាជាពេលវេលាដែលវាត្រូវការសម្រាប់ចលនាដើម្បីបញ្ចប់វដ្តមួយ។ ប្រសិនបើរយៈពេលចលនាលើសពីពេលវេលាចលនាលំនាំដើម វានឹងកំណត់ឡើងវិញទៅចលនាលំនាំដើម ហើយធ្វើម្តងទៀត", + "Animation speed, the smaller the value, the faster the animation": "ល្បឿនចលនា តម្លៃកាន់តែតូច ចលនាកាន់តែលឿន", + "Attention": "យកចិត្តទុកដាក់", + "We have already tested and fine-tuned the default pet configuration to provide the best user experience. You only need to change the name of your pet. Making other changes to the configuration may prevent you from seeing your pet after adding it": "យើងបានសាកល្បងនិងកែសម្រួលការកំណត់រចនាសម្ព័ន្ធសត្វចិញ្ចឹមលំនាំដើមរួចហើយដើម្បីផ្តល់នូវបទពិសោធន៍អ្នកប្រើប្រាស់ដ៏ល្អបំផុត។ អ្នកគ្រាន់តែត្រូវប្តូរឈ្មោះសត្វចិញ្ចឹមរបស់អ្នកប៉ុណ្ណោះ។ ការផ្លាស់ប្តូរផ្សេងទៀតចំពោះការកំណត់អាចរារាំងអ្នកមិនឱ្យឃើញសត្វចិញ្ចឹមរបស់អ្នកបន្ទាប់ពីបន្ថែមវា។", + "Allow hover on pet": "អនុញ្ញាតឱ្យដាក់លើសត្វចិញ្ចឹម", + "Pets will switch state when u hover on them": "សត្វចិញ្ចឹមនឹងប្តូរស្ថានភាពនៅពេលអ្នកដាក់លើពួកវា" } \ No newline at end of file diff --git a/src/types/IEvents.ts b/src/types/IEvents.ts new file mode 100644 index 0000000..98cb801 --- /dev/null +++ b/src/types/IEvents.ts @@ -0,0 +1,15 @@ +export type TRenderEventListener = { + event: string, + windowLabel: string, + payload: { + dispatchType: string, + message: string, + value: boolean | string, + }, + id: number, +} + +export interface IEmitReRenderPetsEvent { + dispatchType: string; + newValue?: boolean | string; +} \ No newline at end of file diff --git a/src/types/IMousePosition.ts b/src/types/IMousePosition.ts new file mode 100644 index 0000000..8273c2d --- /dev/null +++ b/src/types/IMousePosition.ts @@ -0,0 +1,4 @@ +export interface IMousePosition { + clientX: number; + clientY: number; +} \ No newline at end of file diff --git a/src/types/IPet.ts b/src/types/IPet.ts index f31a0ee..465a777 100644 --- a/src/types/IPet.ts +++ b/src/types/IPet.ts @@ -30,4 +30,11 @@ export interface IPetParams { states: TStates; walkSpeed?: number; runSpeed?: number; +} + +export interface IStateInput { + framesHold?: number; + stateHold?: number; + state: string; + exclude?: boolean; } \ No newline at end of file diff --git a/src/types/ISetting.ts b/src/types/ISetting.ts index 12e20e7..66d01af 100644 --- a/src/types/ISetting.ts +++ b/src/types/ISetting.ts @@ -15,6 +15,7 @@ export interface ISetSetting extends IGetAppSetting { export type TAppSetting = { isPetAboveTaskbar: boolean, + isAllowHoverOnPet: boolean, language: string, theme: ColorScheme } diff --git a/src/ui/setting_tabs/AddPet.tsx b/src/ui/setting_tabs/AddPet.tsx index 46bf23d..b89244b 100644 --- a/src/ui/setting_tabs/AddPet.tsx +++ b/src/ui/setting_tabs/AddPet.tsx @@ -1,24 +1,42 @@ import { Center, Group, - Image, NumberInput, Flex, Button, Accordion, Divider, + Alert, TextInput, } from "@mantine/core"; import { Carousel } from '@mantine/carousel'; -import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { memo } from "react"; +import { memo, useEffect, useState } from "react"; +import StateInput from "./add_pet/StateInput"; +import { IPetParams } from "../../types/IPet"; +import PetSlide from "./add_pet/PetSlide"; +import { defaultPetOptions } from "../../default/defaultPetExport"; +import { IconAlertCircle } from '@tabler/icons-react'; function AddPet() { const { t } = useTranslation(); - const [selectedPet, setSelectedPet] = useState(0); + const [selectedPetIndex, setSelectedPetIndex] = useState(0); + const [currentPet, setCurrentPet] = useState(defaultPetOptions[selectedPetIndex]); + const [currentPetName, setCurrentPetName] = useState(currentPet.name); - console.log("AddPet render time"); + const PetOptionImages = defaultPetOptions.map((pet, index) => { + const imageSrc = pet.states[Object.keys(currentPet.states)[0]]?.imageSrc; + return + }); + + useEffect(() => { + setCurrentPet(defaultPetOptions[selectedPetIndex]); + setCurrentPetName(defaultPetOptions[selectedPetIndex].name); + }, [selectedPetIndex]); + + const CurrentPetStateInputs: any = Object.keys(currentPet.states).map((state, index) => { + return ; + }); return ( <> @@ -28,89 +46,82 @@ function AddPet() { mx="auto" height={200} loop - initialSlide={selectedPet} - onSlideChange={(index) => setSelectedPet(index)} + initialSlide={selectedPetIndex} + onSlideChange={(index) => setSelectedPetIndex(index)} > - - - - - - + {PetOptionImages} + } title={t("Attention")} variant="outline"> + {t("We have already tested and fine-tuned the default pet configuration to provide the best user experience. You only need to change the name of your pet. Making other changes to the configuration may prevent you from seeing your pet after adding it")} + { + setCurrentPetName(event.currentTarget.value); + }} /> Math.max(1000 / t ** 2, 25)} + value={currentPet.position.y} /> Math.max(1000 / t ** 2, 25)} + value={currentPet.scale} + min={1} /> Math.max(1000 / t ** 2, 25)} + value={currentPet.walkSpeed} + min={0} /> Math.max(1000 / t ** 2, 25)} + value={currentPet.runSpeed} + min={0} /> - - Idle - - Math.max(1000 / t ** 2, 25)} - /> - Math.max(1000 / t ** 2, 25)} - /> - - + {CurrentPetStateInputs} - - diff --git a/src/ui/setting_tabs/Settings.tsx b/src/ui/setting_tabs/Settings.tsx index 68b01aa..b8fc901 100644 --- a/src/ui/setting_tabs/Settings.tsx +++ b/src/ui/setting_tabs/Settings.tsx @@ -10,7 +10,7 @@ import { memo } from "react"; function Settings() { const { t, i18n } = useTranslation(); - const { isAutoStartUp, isPetAboveTaskBar } = useSettingStore(); + const { isAutoStartUp, isPetAboveTaskBar, isAllowHoverOnPet } = useSettingStore(); const settings: ISettingsContent = { parent: { @@ -30,6 +30,12 @@ function Settings() { checked: isPetAboveTaskBar, dispatchType: "switchPetAboveTaskBar", }, + { + title: t("Allow hover on pet"), + description: t("Pets will switch state when u hover on them"), + checked: isAllowHoverOnPet, + dispatchType: "switchAllowHoverOnPet", + }, ] } @@ -54,14 +60,6 @@ function Settings() { value={i18n.language} onChange={(value) => handleSettingChange("changeAppLanguage", value as string)} /> - {/* - - - */} ) } diff --git a/src/ui/setting_tabs/add_pet/PetSlide.tsx b/src/ui/setting_tabs/add_pet/PetSlide.tsx new file mode 100644 index 0000000..7e94455 --- /dev/null +++ b/src/ui/setting_tabs/add_pet/PetSlide.tsx @@ -0,0 +1,22 @@ +import { memo } from "react"; +import { + Image, +} from "@mantine/core"; +import { Carousel } from '@mantine/carousel'; + +interface IPetSlideProps { + imageSrc: string | undefined; +}; + +function PetSlide({ imageSrc } : IPetSlideProps) { + console.log("will - i render?") + return ( + <> + + + + + ) +}; + +export default memo(PetSlide); \ No newline at end of file diff --git a/src/ui/setting_tabs/add_pet/StateInput.tsx b/src/ui/setting_tabs/add_pet/StateInput.tsx new file mode 100644 index 0000000..779bfd1 --- /dev/null +++ b/src/ui/setting_tabs/add_pet/StateInput.tsx @@ -0,0 +1,46 @@ +import { + NumberInput, + Accordion, +} from "@mantine/core"; +import { memo } from "react"; +import { useTranslation } from "react-i18next"; +import { IStateInput } from "../../../types/IPet"; + +function StateInput({ framesHold, stateHold, state, exclude }: IStateInput) { + const { t } = useTranslation(); + + return ( + <> + + {state} + + {/* stateHold */} + Math.max(1000 / t ** 2, 25)} + value={stateHold} + /> + {/* framesHold */} + Math.max(1000 / t ** 2, 25)} + min={1} + value={framesHold} + /> + + + + ) +} + +export default memo(StateInput); \ No newline at end of file diff --git a/src/utils/clonePetsFromSettings.ts b/src/utils/clonePetsFromSettings.ts index 6228aed..e6888e8 100644 --- a/src/utils/clonePetsFromSettings.ts +++ b/src/utils/clonePetsFromSettings.ts @@ -1,6 +1,6 @@ import Pet from "../class/Pet"; import { usePetStore } from "../hooks/usePetStore"; -import { getAppSettings } from "./settingsHelper"; +import { getAppSettings } from "./settings"; export const clonePetsFromSettings = async () => { const clonePets = usePetStore.getState().clonePets; diff --git a/src/utils/event.ts b/src/utils/event.ts new file mode 100644 index 0000000..225fc5a --- /dev/null +++ b/src/utils/event.ts @@ -0,0 +1,15 @@ +import { WebviewWindow } from '@tauri-apps/api/window' +import { IEmitReRenderPetsEvent } from '../types/IEvents'; + +export const emitReRenderPetsEvent = async ({dispatchType, newValue}: IEmitReRenderPetsEvent) => { + // get the window instance by its label + const mainWindow = WebviewWindow.getByLabel('main'); + + if (mainWindow) { + await mainWindow.emit('render', { + message: 'Hey, re-render pets! :)', + dispatchType: dispatchType, + value: newValue + }); + } +}; \ No newline at end of file diff --git a/src/utils/handleSettingChange.ts b/src/utils/handleSettingChange.ts index 9db16ec..e91deec 100644 --- a/src/utils/handleSettingChange.ts +++ b/src/utils/handleSettingChange.ts @@ -1,14 +1,14 @@ -import { setSettings, toggleAutoStartUp } from "./settingsHelper"; +import { setSettings, toggleAutoStartUp } from "./settings"; import { IHandleSettingChange, Theme } from "../types/ISetting"; import { useSettingStore } from "../hooks/useSettingStore"; -import { WebviewWindow } from '@tauri-apps/api/window' +import { emitReRenderPetsEvent } from "./event"; export const handleSettingChange: IHandleSettingChange = (dispatchType, newValue) => { /* * Reading/writing state and reacting to changes outside of components, for more detail read * zustand docs here: https://docs.pmnd.rs/zustand/recipes/recipes */ - const { setLanguage, setTheme, setIsAutoStartUp, setIsPetAboveTaskbar } = useSettingStore.getState(); + const { setLanguage, setTheme, setIsAutoStartUp, setIsPetAboveTaskbar, setIsAllowHoverOnPet } = useSettingStore.getState(); switch (dispatchType) { case 'changeAppLanguage': @@ -27,18 +27,12 @@ export const handleSettingChange: IHandleSettingChange = (dispatchType, newValue case 'switchPetAboveTaskBar': setSettings({ setKey: "isPetAboveTaskbar", newValue: newValue }); setIsPetAboveTaskbar(newValue as boolean); - - (async () => { - // get the window instance by its label - const mainWindow = WebviewWindow.getByLabel('main'); - - if (mainWindow) { - await mainWindow.emit('render', { - message: 'Hey, re-render pets! :)', - isPetAboveTaskbar: newValue, - }); - } - })(); + emitReRenderPetsEvent({ dispatchType, newValue }); + return + case 'switchAllowHoverOnPet': + setSettings({ setKey: "isAllowHoverOnPet", newValue: newValue }); + setIsAllowHoverOnPet(newValue as boolean); + emitReRenderPetsEvent({ dispatchType, newValue }); default: return; } diff --git a/src/utils/settingsHelper.ts b/src/utils/settings.ts similarity index 100% rename from src/utils/settingsHelper.ts rename to src/utils/settings.ts