diff --git a/index.html b/index.html index 3d0dbc1..4799fcb 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,6 @@
- + diff --git a/src-tauri/src/frontend_commands.rs b/src-tauri/src/frontend_commands.rs index 042b9f6..8d06243 100644 --- a/src-tauri/src/frontend_commands.rs +++ b/src-tauri/src/frontend_commands.rs @@ -1,17 +1,7 @@ -use std::sync::Mutex; -use tauri::{AppHandle, Manager, State, WebviewWindow}; - -pub struct SetupState { - pub frontend_ready: bool, -} +use tauri::{AppHandle, Manager, WebviewWindow}; #[tauri::command] -pub async fn set_frontend_ready( - app: AppHandle, - state: State<'_, Mutex>, -) -> Result<(), ()> { - let mut state_lock = state.lock().unwrap(); - state_lock.frontend_ready = true; +pub async fn set_frontend_ready(app: AppHandle) -> Result<(), ()> { let main_window = app.get_webview_window("main").unwrap(); main_window.show().unwrap(); Ok(()) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 1218038..e749b8e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,16 +2,15 @@ mod frontend_commands; mod lua_commands; mod lua_setup; mod lua_types; -use frontend_commands::{resize_window, set_frontend_ready, window_scale, SetupState}; +use frontend_commands::{resize_window, set_frontend_ready, window_scale}; use lua_commands::{ delete_entity, duplicate_entity, get_entity_string, handle_inspector_save, load_scene, new_entity, run_script, save_scene, tick, update_entity, }; use lua_setup::init_lua_thread; -use std::sync::Mutex; use tauri::{ menu::{Menu, MenuItem, SubmenuBuilder}, - Emitter, Listener, Manager, + Emitter, Manager, }; #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -20,9 +19,6 @@ pub fn run() { .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_dialog::init()) - .manage(Mutex::new(SetupState { - frontend_ready: false, - })) .setup(|app| { let window = app.get_webview_window("main").unwrap(); let state = init_lua_thread(window.clone()).expect("Error initializing lua thread"); @@ -63,41 +59,35 @@ pub fn run() { handle, "save_entity", "Save Entity", - false, - Some("CmdOrCtrl+S"), + true, + Some("CmdOrCtrl+alt+S"), )?) .item(&MenuItem::with_id( handle, "revert_entity", "Revert Changes", - false, - Some("CmdOrCtrl+R"), + true, + Some("CmdOrCtrl+alt+R"), )?) .build()?; menu.append(&file_menu)?; - let window_clone = window.clone(); app.set_menu(menu)?; app.on_menu_event(move |app_handle: &tauri::AppHandle, event| { match event.id().0.as_str() { file_op @ ("save_scene" | "open_scene") => { - if window_clone - .is_focused() - .expect("Couldn't find main window focus status") - { - app_handle - .emit_to("main", "file_operation", file_op) - .expect(&format!("Failed to emit {}", file_op)); - } + app_handle + .emit_to("main", "file_operation", file_op) + .expect(&format!("Failed to emit {}", file_op)); } "save_entity" => { app_handle - .emit_to("inspector", "save_entity", ()) + .emit_to("main", "save_entity", ()) .expect("Failed to emit save_entity to inspector"); } "revert_entity" => { app_handle - .emit_to("inspector", "revert_entity", ()) + .emit_to("main", "revert_entity", ()) .expect("Failed to emit revert_entity to inspector"); } "quit" => { @@ -107,57 +97,6 @@ pub fn run() { } }); - // enable/disable menu items based on focused window - fn on_focus_change(handle: &tauri::AppHandle, focus_window: String) { - if let Some(menu) = handle.menu() { - if let Some(file_menu) = menu.get("file") { - if let Some(submenu) = file_menu.as_submenu() { - if let Some(save_item) = submenu.get("save_scene") { - if let Some(menu_item) = save_item.as_menuitem() { - let _ = menu_item.set_enabled(focus_window == "main"); - } - } - if let Some(open_item) = submenu.get("open_scene") { - if let Some(menu_item) = open_item.as_menuitem() { - let _ = menu_item.set_enabled(focus_window == "main"); - } - } - if let Some(save_item) = submenu.get("save_entity") { - if let Some(menu_item) = save_item.as_menuitem() { - let _ = menu_item.set_enabled(focus_window == "inspector"); - } - } - if let Some(save_item) = submenu.get("revert_entity") { - if let Some(menu_item) = save_item.as_menuitem() { - let _ = menu_item.set_enabled(focus_window == "inspector"); - } - } - } - } - } - } - - let handle_for_main = handle.clone(); - window.listen("tauri://focus", move |_| { - on_focus_change(&handle_for_main, "main".to_string()) - }); - - let handle_for_created = handle.clone(); - app.listen("tauri://window-created", move |event| { - if let Ok(payload) = serde_json::from_str::(event.payload()) { - if let Some(label) = payload.get("label").and_then(|l| l.as_str()) { - let label = label.to_string(); - if let Some(w) = handle_for_created.get_webview_window(label.as_str()) { - let handle_for_focus = handle_for_created.clone(); - let label_for_focus = label.clone(); - w.listen("tauri://focus", move |_| { - on_focus_change(&handle_for_focus, label_for_focus.clone()); - }); - } - } - } - }); - Ok(()) }) .plugin(tauri_plugin_opener::init()) diff --git a/src/app.tsx b/src/app.tsx new file mode 100644 index 0000000..68d56b1 --- /dev/null +++ b/src/app.tsx @@ -0,0 +1,70 @@ +import { render } from "preact"; +import "./style.css"; +import Scene from "./scene-window"; +import Inspector from "./inspector/inspector"; +import { useEffect, useRef, useState } from "preact/hooks"; +import { getCurrentWindow } from "@tauri-apps/api/window"; +import { MoreVertical } from "preact-feather"; + +export default function App() { + const [theme, setTheme] = useState<"light" | "dark">("light"); + const [sidebarWidth, setSidebarWidth] = useState(300); + const sidebarRef = useRef(null); + + useEffect(() => { + let listeners: (() => void)[] = []; + + (async () => { + listeners.push( + await getCurrentWindow().onThemeChanged(({ payload: theme }) => + setTheme(theme), + ), + ); + })().then(async () => + setTheme((await getCurrentWindow().theme()) || "light"), + ); + + return () => listeners.forEach((unsubscribe) => unsubscribe()); + }); + + return ( +
+
+ +
+
+
+
{ + const startWidth = sidebarWidth || 128; + const startX = startEvent.clientX; + + function onMouseMove(moveEvent: MouseEvent) { + setSidebarWidth( + Math.max(64, startWidth + (startX - moveEvent.clientX)), // TODO why is this not moving enough??? + ); + } + + function onMouseUp() { + document.removeEventListener("mousemove", onMouseMove); + document.removeEventListener("mouseup", onMouseUp); + } + + document.addEventListener("mousemove", onMouseMove); + document.addEventListener("mouseup", onMouseUp); + }} + > + +
+ +
+
+ ); +} + +render(, document.getElementById("root")!); diff --git a/src/components/tab-bar/tab-bar.tsx b/src/components/tab-bar/tab-bar.tsx index 8210fa8..f8b6e42 100644 --- a/src/components/tab-bar/tab-bar.tsx +++ b/src/components/tab-bar/tab-bar.tsx @@ -23,7 +23,7 @@ export default function TabBar(props: TabBarProps) { return (
); } diff --git a/src/main.tsx b/src/main.tsx deleted file mode 100644 index a2b4c5b..0000000 --- a/src/main.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { render } from "preact"; -import "./style.css"; -import Scene from "./scene-window"; - -render(, document.getElementById("root")!); diff --git a/src/scene-window.tsx b/src/scene-window.tsx index f9f3326..cdaf93d 100644 --- a/src/scene-window.tsx +++ b/src/scene-window.tsx @@ -1,13 +1,12 @@ import { invoke } from "@tauri-apps/api/core"; -import { emit, listen } from "@tauri-apps/api/event"; +import { listen } from "@tauri-apps/api/event"; import EntityComponent from "./entity/entity-component"; import Moveable, { OnDrag, OnRotate } from "preact-moveable"; import { Menu } from "@tauri-apps/api/menu"; import { save, open, message } from "@tauri-apps/plugin-dialog"; import { WebviewWindow } from "@tauri-apps/api/webviewWindow"; -import { useEffect, useState } from "preact/hooks"; +import { useEffect, useRef, useState } from "preact/hooks"; import { Entity } from "./entity/entity-type"; -import { getCurrentWindow } from "@tauri-apps/api/window"; const SCENE_BASE_SIZE = { width: 1280, @@ -15,6 +14,7 @@ const SCENE_BASE_SIZE = { }; export default function Scene() { + const boundingRef = useRef(null); const [entities, setEntities] = useState>(new Map()); const [transformScale, setTransformScale] = useState(1); const [lastTime, setLastTime] = useState(performance.now()); @@ -30,6 +30,22 @@ export default function Scene() { const selectedEntity = selectedId ? entities.get(selectedId) : null; useEffect(() => { + let observer: ResizeObserver; + if (boundingRef.current) { + observer = new ResizeObserver((entries) => + entries.forEach(async (entry) => { + const newScale = entry.contentRect.width / SCENE_BASE_SIZE.width; + setTransformScale(1 / newScale); + document.documentElement.style.setProperty( + `--scene-scale`, + newScale.toString(), + ); + }), + ); + + observer.observe(boundingRef.current); + } + let listeners: (() => void)[] = []; (async () => @@ -46,47 +62,6 @@ export default function Scene() { }), ))(); - (async () => - listeners.push( - await listen("select_entity", (e) => - setSelectedId(e.payload), - ), - ))(); - - (async () => { - listeners.push( - await getCurrentWindow().listen<{ - width: number; - height: number; - }>("tauri://resize", async (e) => { - setSelectedId(undefined); - - const scaleFactor: number = await invoke("window_scale"); - const contentHeight = document.documentElement.clientHeight; // content area dimensions (excluding title bar) - const windowHeight = e.payload.height; // full window dimensions - const titleBarHeight = windowHeight / scaleFactor - contentHeight; // calculate title bar height dynamically - - const newScale = e.payload.width / SCENE_BASE_SIZE.width; - setTransformScale(scaleFactor / newScale); - - invoke("resize_window", { - width: Math.round(SCENE_BASE_SIZE.width * newScale), - height: Math.round( - SCENE_BASE_SIZE.height * newScale + titleBarHeight * scaleFactor, - ), - }); - document.documentElement.style.setProperty( - `--scene-scale`, - (newScale / scaleFactor).toString(), - ); - }), - ); - - emit("tauri://resize", await WebviewWindow.getCurrent().size()).then(() => - invoke("set_frontend_ready"), - ); - })(); - (async () => { listeners.push( await listen("file_operation", async (e) => { @@ -131,9 +106,12 @@ export default function Scene() { }; setAnimationFrameId(requestAnimationFrame(tick)); + invoke("set_frontend_ready"); + return () => { listeners.forEach((unsubscribe) => unsubscribe()); if (animationFrameId) cancelAnimationFrame(animationFrameId); + if (observer) observer.disconnect(); }; }, []); @@ -141,11 +119,6 @@ export default function Scene() { if (selectedEntity && !selectedEntity.selectable) setSelectedId(undefined); }, [entities]); - const handleEntitySelect = (id: string) => { - if (id == selectedId) return; - setSelectedId(id); - }; - const handleDrag = (e: OnDrag) => { const ang = (selectedEntity?.rotation || 0) * (Math.PI / 180); const cos = Math.cos(ang); @@ -209,7 +182,8 @@ export default function Scene() { return (
{ if (e.target === e.currentTarget) setSelectedId(undefined); }} @@ -287,7 +261,7 @@ export default function Scene() { handleEntitySelect(id)} + onSelect={(select) => setSelectedId(select ? id : undefined)} isSelected={id === selectedId} /> ))} diff --git a/vite.config.ts b/vite.config.ts index 394d8ce..80a679a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,7 +12,6 @@ export default defineConfig(async () => ({ rollupOptions: { input: { main: "index.html", - inspector: "src/inspector/inspector.html", }, }, },