From 4ef3a0f9d69c9b23b3e211f49762eb85853387d9 Mon Sep 17 00:00:00 2001 From: Jacky Lam Date: Sun, 9 Aug 2026 07:37:43 +0800 Subject: [PATCH 1/2] feat: allow resizing floating window width --- src-tauri/src/commands/window.rs | 16 ++++ src-tauri/src/lib.rs | 2 + src-tauri/src/storage.rs | 42 ++++++++ src-tauri/src/window.rs | 160 +++++++++++++++++++++++++------ src-tauri/tauri.conf.json | 4 +- src/App.svelte | 87 ++++++++++++++++- src/lib/backend.ts | 8 ++ src/lib/uiGeometry.test.ts | 11 ++- src/styles/components.css | 23 ++++- 9 files changed, 317 insertions(+), 36 deletions(-) diff --git a/src-tauri/src/commands/window.rs b/src-tauri/src/commands/window.rs index d328371..b0ed47e 100644 --- a/src-tauri/src/commands/window.rs +++ b/src-tauri/src/commands/window.rs @@ -74,6 +74,22 @@ pub fn lock_panel_resize_axis(app: AppHandle) -> Result<(), String> { lock_native_panel_resize_axis(&window) } +#[tauri::command] +pub fn current_panel_width(app: AppHandle) -> Result { + let window = app + .get_webview_window(MAIN_WINDOW) + .ok_or("OpenQuota window is unavailable.")?; + Ok(crate::window::panel_logical_width(&window)) +} + +#[tauri::command] +pub fn set_panel_width(app: AppHandle, width: f64) -> Result<(), String> { + let window = app + .get_webview_window(MAIN_WINDOW) + .ok_or("OpenQuota window is unavailable.")?; + crate::window::apply_panel_width(&window, width) +} + #[tauri::command] pub fn quit_app(app: AppHandle) { if let Some(window) = app.get_webview_window(MAIN_WINDOW) { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f822a48..5fa992d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -406,6 +406,8 @@ pub fn run() { commands::window::set_panel_height_manual, commands::window::begin_panel_resize, commands::window::lock_panel_resize_axis, + commands::window::current_panel_width, + commands::window::set_panel_width, commands::window::quit_app, updates::check_for_updates, updates::install_update, diff --git a/src-tauri/src/storage.rs b/src-tauri/src/storage.rs index a442466..448ac66 100644 --- a/src-tauri/src/storage.rs +++ b/src-tauri/src/storage.rs @@ -110,6 +110,9 @@ impl Storage { [], )?; } + if !Self::has_column(&connection, "panel_state", "width")? { + connection.execute("ALTER TABLE panel_state ADD COLUMN width INTEGER", [])?; + } Ok(Self { connection: Mutex::new(connection), }) @@ -436,6 +439,29 @@ impl Storage { Ok(()) } + pub fn load_panel_width(&self) -> Result, StorageError> { + let connection = self.connection()?; + let width = connection + .query_row("SELECT width FROM panel_state WHERE id = 1", [], |row| { + row.get::<_, Option>(0) + }) + .optional()? + .flatten(); + Ok(width.and_then(|value| u32::try_from(value).ok())) + } + + pub fn save_panel_width(&self, width: u32) -> Result<(), StorageError> { + // height is NOT NULL on panel_state, so preserve the stored height (or 0) when upserting the + // width on a row that does not exist yet. + let height = self.load_panel_height()?.unwrap_or(0) as i64; + self.connection()?.execute( + "INSERT INTO panel_state(id, height, width) VALUES (1, ?1, ?2) + ON CONFLICT(id) DO UPDATE SET width = excluded.width", + [height, i64::from(width)], + )?; + Ok(()) + } + fn insert_day( transaction: &rusqlite::Transaction<'_>, provider_id: &str, @@ -735,6 +761,22 @@ mod tests { assert_eq!(storage.load_panel_height().unwrap(), None); } + #[test] + fn panel_width_round_trip_preserves_height_and_is_independent_from_app_settings() { + let directory = tempdir().unwrap(); + let storage = Storage::open(&directory.path().join("openquota.db")).unwrap(); + let settings = AppSettings::default(); + storage.save_settings(&settings).unwrap(); + + assert_eq!(storage.load_panel_width().unwrap(), None); + storage.save_panel_height(734).unwrap(); + storage.save_panel_width(460).unwrap(); + + assert_eq!(storage.load_panel_width().unwrap(), Some(460)); + assert_eq!(storage.load_panel_height().unwrap(), Some(734)); + assert_eq!(storage.load_settings().unwrap(), Some(settings)); + } + #[test] fn log_cache_pruning_is_scoped_to_a_provider() { let directory = tempdir().unwrap(); diff --git a/src-tauri/src/window.rs b/src-tauri/src/window.rs index 0d763d3..285b0cb 100644 --- a/src-tauri/src/window.rs +++ b/src-tauri/src/window.rs @@ -24,7 +24,9 @@ use crate::{ }; pub const MAIN_WINDOW: &str = "main"; -pub const PANEL_WIDTH: f64 = 320.0; +pub const PANEL_MIN_WIDTH: f64 = 320.0; +pub const PANEL_MAX_WIDTH: f64 = 560.0; +pub const PANEL_DEFAULT_WIDTH: f64 = 380.0; pub const PANEL_MIN_HEIGHT: u32 = 240; const PANEL_SCREEN_FRACTION: f64 = 0.85; const PANEL_RESIZE_SAVE_DELAY: Duration = Duration::from_millis(120); @@ -177,6 +179,20 @@ impl PanelResizeSession { fn saved_height(&self) -> Option { self.storage.load_panel_height().ok().flatten() } + + pub fn save_width(&self, width: u32) -> Result<(), String> { + let _guard = self + .persistence + .lock() + .map_err(|_| "OpenQuota panel state is unavailable.")?; + self.storage + .save_panel_width(width) + .map_err(|_| "OpenQuota panel state could not be saved.".to_owned()) + } + + fn saved_width(&self) -> Option { + self.storage.load_panel_width().ok().flatten() + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] @@ -266,10 +282,10 @@ pub fn show_main_window(window: &WebviewWindow) { .is_floating() { let _ = window.unminimize(); - let _ = restore_manual_panel_height(window); + let _ = restore_manual_panel_size(window); } else { position_popup(window); - let _ = restore_manual_panel_height(window); + let _ = restore_manual_panel_size(window); } let _ = window.show(); let _ = window.set_focus(); @@ -317,7 +333,7 @@ pub fn apply_window_mode( } else { position_popup(window); } - let _ = restore_manual_panel_height(window); + let _ = restore_manual_panel_size(window); window .show() .and_then(|_| window.set_focus()) @@ -496,35 +512,52 @@ fn panel_maximum_height(window: &WebviewWindow) -> Result { fn configure_panel_size_constraints(window: &WebviewWindow) -> Result { let maximum = panel_maximum_height(window)?; let minimum = PANEL_MIN_HEIGHT.min(maximum); + // The tray popup keeps the original fixed width; only the floating window may be widened. + let width_max = if panel_floating(window) { + PANEL_MAX_WIDTH + } else { + PANEL_MIN_WIDTH + }; window - .set_max_size(Some(LogicalSize::new(PANEL_WIDTH, f64::from(maximum)))) - .and_then(|_| window.set_min_size(Some(LogicalSize::new(PANEL_WIDTH, f64::from(minimum))))) + .set_max_size(Some(LogicalSize::new(width_max, f64::from(maximum)))) + .and_then(|_| { + window.set_min_size(Some(LogicalSize::new(PANEL_MIN_WIDTH, f64::from(minimum)))) + }) .map_err(|_| "OpenQuota panel size limits could not be applied.".to_owned())?; Ok(maximum) } -fn restore_manual_panel_height(window: &WebviewWindow) -> Result<(), String> { +fn restore_manual_panel_size(window: &WebviewWindow) -> Result<(), String> { let maximum = panel_maximum_height(window)?; let minimum = PANEL_MIN_HEIGHT.min(maximum); - let saved = window - .app_handle() - .try_state::>() - .and_then(|session| session.saved_height()); - if let Some(saved) = saved { - resize_panel_for_context(window, saved.clamp(minimum, maximum))?; + let session = window.app_handle().try_state::>(); + let saved_height = session.as_ref().and_then(|session| session.saved_height()); + if let Some(height) = saved_height { + resize_panel_for_context(window, height.clamp(minimum, maximum))?; } + // Floating restores the user's saved width (or the wider default); the popup is always the + // original fixed width. + let floating = panel_floating(window); + let width = if floating { + session + .as_ref() + .and_then(|session| session.saved_width()) + .map(clamped_panel_width) + .unwrap_or(PANEL_DEFAULT_WIDTH) + } else { + PANEL_MIN_WIDTH + }; + let height = current_logical_height(&window.as_ref().window()).unwrap_or(PANEL_MIN_HEIGHT); + let _ = window.set_size(LogicalSize::new(width, f64::from(height))); configure_panel_size_constraints(window)?; Ok(()) } fn resize_panel_for_context(window: &WebviewWindow, height: u32) -> Result<(), String> { - if window - .app_handle() - .state::() - .is_floating() - { + if panel_floating(window) { + let width = effective_panel_width(window); return window - .set_size(LogicalSize::new(PANEL_WIDTH, f64::from(height))) + .set_size(LogicalSize::new(width, f64::from(height))) .map_err(|_| "OpenQuota window could not be resized.".to_owned()); } resize_popup_anchored(window, height) @@ -577,9 +610,9 @@ pub fn finish_native_panel_resize(window: &WebviewWindow) { } pub fn lock_native_panel_resize_axis(window: &WebviewWindow) -> Result<(), String> { - // Keep the system's invisible left/right resize borders disabled outside the explicit vertical - // gesture. Re-applying the logical width also repairs any transient WebView viewport change if a - // platform briefly reported a horizontal resize before the native constraint took effect. + // Keep the system's invisible resize borders disabled outside the explicit resize gesture. + // Floating windows keep the width the gesture reached (persisted); tray popups always settle + // back to the original fixed width. window .set_resizable(false) .map_err(|_| "OpenQuota panel resize could not be settled.".to_owned())?; @@ -590,8 +623,19 @@ pub fn lock_native_panel_resize_axis(window: &WebviewWindow) -> Result<(), Strin .scale_factor() .map_err(|_| "OpenQuota display scale is unavailable.")?; let height = f64::from(size.height) / scale; + let width = if panel_floating(window) { + let resolved = clamped_panel_width( + current_logical_width(&window.as_ref().window()).unwrap_or(PANEL_DEFAULT_WIDTH as u32), + ); + if let Some(session) = window.app_handle().try_state::>() { + let _ = session.save_width(resolved as u32); + } + resolved + } else { + PANEL_MIN_WIDTH + }; window - .set_size(LogicalSize::new(PANEL_WIDTH, height)) + .set_size(LogicalSize::new(width, height)) .map_err(|_| "OpenQuota panel resize could not be settled.".to_owned()) } @@ -605,6 +649,60 @@ fn current_logical_height(window: &Window) -> Option { ) } +fn current_logical_width(window: &Window) -> Option { + let size = window.inner_size().ok()?; + let scale = window.scale_factor().ok()?; + Some( + (f64::from(size.width) / scale) + .round() + .clamp(1.0, f64::from(u32::MAX)) as u32, + ) +} + +fn clamped_panel_width(raw: u32) -> f64 { + f64::from(raw).clamp(PANEL_MIN_WIDTH, PANEL_MAX_WIDTH) +} + +fn panel_floating(window: &WebviewWindow) -> bool { + window + .app_handle() + .state::() + .is_floating() +} + +/// Width to apply for the current mode: the user-chosen (clamped) width when floating, or the +/// original fixed width when acting as a tray popup. +fn effective_panel_width(window: &WebviewWindow) -> f64 { + if panel_floating(window) { + current_logical_width(&window.as_ref().window()) + .map(clamped_panel_width) + .unwrap_or(PANEL_DEFAULT_WIDTH) + } else { + PANEL_MIN_WIDTH + } +} + +/// Current logical panel width, clamped to the resizable range. Used to seed a manual resize drag. +pub fn panel_logical_width(window: &WebviewWindow) -> f64 { + current_logical_width(&window.as_ref().window()) + .map(clamped_panel_width) + .unwrap_or(PANEL_DEFAULT_WIDTH) +} + +/// Programmatically set the panel width (height preserved). Works regardless of the resizable +/// flag, so it drives a manual pointer-tracked resize even for the borderless window. +pub fn apply_panel_width(window: &WebviewWindow, width: f64) -> Result<(), String> { + if !panel_floating(window) { + return Err("OpenQuota tray popups have a fixed width.".to_owned()); + } + let width = width.clamp(PANEL_MIN_WIDTH, PANEL_MAX_WIDTH); + let height = current_logical_height(&window.as_ref().window()) + .ok_or("OpenQuota content size is unavailable.")?; + window + .set_size(LogicalSize::new(width, f64::from(height))) + .map_err(|_| "OpenQuota window could not be resized.".to_owned()) +} + #[cfg(target_os = "windows")] pub fn resize_popup_anchored(window: &WebviewWindow, height: u32) -> Result<(), String> { use windows_sys::Win32::UI::WindowsAndMessaging::{ @@ -694,8 +792,9 @@ pub fn resize_popup_anchored(window: &WebviewWindow, height: u32) -> Result<(), }, target_outer_height, ); + let width = effective_panel_width(window); window - .set_size(tauri::LogicalSize::new(320.0, f64::from(height))) + .set_size(tauri::LogicalSize::new(width, f64::from(height))) .and_then(|_| { window.set_position(tauri::PhysicalPosition::new(outer_position.x, anchored.top)) }) @@ -787,9 +886,9 @@ mod tests { use tempfile::tempdir; use super::{ - anchored_vertical_frame, panel_resize_edge_for_context, panel_resize_edge_for_frames, - panel_surface_color, PanelHeightMode, PanelResizeEdge, PanelResizeSession, VerticalFrame, - DARK_PANEL_SURFACE, LIGHT_PANEL_SURFACE, + anchored_vertical_frame, clamped_panel_width, panel_resize_edge_for_context, + panel_resize_edge_for_frames, panel_surface_color, PanelHeightMode, PanelResizeEdge, + PanelResizeSession, VerticalFrame, DARK_PANEL_SURFACE, LIGHT_PANEL_SURFACE, }; use crate::models::ThemePreference; use crate::storage::Storage; @@ -826,6 +925,13 @@ mod tests { assert_eq!(storage.load_panel_height().unwrap(), None); } + #[test] + fn panel_width_is_bounded_to_the_floating_window_range() { + assert_eq!(clamped_panel_width(1), 320.0); + assert_eq!(clamped_panel_width(420), 420.0); + assert_eq!(clamped_panel_width(10_000), 560.0); + } + #[test] fn panel_surface_follows_explicit_and_system_theme_preferences() { assert_eq!( diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 07bc781..9d5fd57 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -14,11 +14,11 @@ { "label": "main", "title": "OpenQuota", - "width": 320, + "width": 380, "height": 800, "minWidth": 320, "minHeight": 240, - "maxWidth": 320, + "maxWidth": 560, "resizable": false, "fullscreen": false, "decorations": false, diff --git a/src/App.svelte b/src/App.svelte index 412e8f7..e45c23c 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -25,6 +25,8 @@ resetProviderCustomization as resetProviderCustomizationCommand, setPanelHeightAutomatic, setPanelHeightManual, + setPanelWidth, + currentPanelWidth, type PanelHeightMode, type PanelResizeEdge, } from './lib/backend'; @@ -510,6 +512,60 @@ if (panelResizeOperation === operation) panelResizeOperation = null; }); } + function handlePanelWidthResizePointerDown(event: PointerEvent) { + if (event.button !== 0) return; + event.preventDefault(); + event.stopPropagation(); + const dragger = event.currentTarget as HTMLElement; + dragger.setPointerCapture(event.pointerId); + void (async () => { + try { + // Manual pointer-tracked resize: programmatic setSize on each move. Unlike the native + // startResizeDragging gesture (unreliable for borderless windows), this works everywhere. + const startWidth = await currentPanelWidth(); + const startX = event.clientX; + let latestWidth = startWidth; + let animationFrame: number | null = null; + let resizeOperation = Promise.resolve(); + const queueLatestWidth = () => { + if (animationFrame !== null) return; + animationFrame = requestAnimationFrame(() => { + animationFrame = null; + const width = latestWidth; + resizeOperation = resizeOperation + .then(() => setPanelWidth(width)) + .catch(() => undefined); + }); + }; + const onMove = (moveEvent: PointerEvent) => { + latestWidth = startWidth + (moveEvent.clientX - startX); + queueLatestWidth(); + }; + const finish = () => { + window.removeEventListener('pointermove', onMove); + window.removeEventListener('pointerup', finish); + window.removeEventListener('pointercancel', finish); + if (dragger.hasPointerCapture(event.pointerId)) { + dragger.releasePointerCapture(event.pointerId); + } + if (animationFrame !== null) { + cancelAnimationFrame(animationFrame); + animationFrame = null; + const width = latestWidth; + resizeOperation = resizeOperation + .then(() => setPanelWidth(width)) + .catch(() => undefined); + } + void resizeOperation.finally(() => lockPanelResizeAxis().catch(() => undefined)); + }; + window.addEventListener('pointermove', onMove); + window.addEventListener('pointerup', finish); + window.addEventListener('pointercancel', finish); + } catch { + settingsError = 'OpenQuota panel width could not be resized.'; + } + })(); + } function handleFloatingWindowPointerDown(event: PointerEvent) { if (event.button !== 0 || !('__TAURI_INTERNALS__' in window)) return; event.preventDefault(); @@ -1008,6 +1064,15 @@ onpointerdown={handlePanelResizePointerDown} > {/if} + {#if floatingWindow && (renderedResizeEdge === 'top' || renderedResizeEdge === 'bottom')} + + {/if} diff --git a/src/lib/backend.ts b/src/lib/backend.ts index 9320fd4..7ccf855 100644 --- a/src/lib/backend.ts +++ b/src/lib/backend.ts @@ -116,6 +116,14 @@ export function lockPanelResizeAxis() { return invoke('lock_panel_resize_axis'); } +export function currentPanelWidth() { + return invoke('current_panel_width'); +} + +export function setPanelWidth(width: number) { + return invoke('set_panel_width', { width }); +} + export function quitApplication() { return invoke('quit_app'); } diff --git a/src/lib/uiGeometry.test.ts b/src/lib/uiGeometry.test.ts index b757b2a..837d1fa 100644 --- a/src/lib/uiGeometry.test.ts +++ b/src/lib/uiGeometry.test.ts @@ -22,22 +22,23 @@ const tauriConfig = JSON.parse(tauriConfigSource) as { }; describe('popover geometry contract', () => { - it('keeps system resize borders locked and exposes only the native vertical grip', () => { + it('locks system resize at rest but allows a bounded width via the native grips', () => { expect(tauriConfig.app.windows[0]).toMatchObject({ - width: 320, + width: 380, height: 800, minWidth: 320, - maxWidth: 320, + maxWidth: 560, minHeight: 240, resizable: false, }); expect(css).toMatch(/\.panel-resize-dragger\s*{[^}]*height: 10px;[^}]*cursor: ns-resize;/s); expect(css).toMatch(/\.panel-resize-dragger::after\s*{[^}]*width: 36px;[^}]*height: 4px;/s); + expect(css).toMatch(/\.panel-resize-dragger--right\s*{[^}]*cursor: ew-resize;/s); }); - it('lets the webview shrink below its nominal width without creating horizontal focus scroll', () => { + it('lets the webview use the full panel width up to the wider maximum', () => { expect(componentCss).toMatch( - /html,\s*body,\s*#app,\s*\.popover\s*{[^}]*width: 100%;[^}]*min-width: 0;[^}]*max-width: 320px;/s, + /html,\s*body,\s*#app,\s*\.popover\s*{[^}]*width: 100%;[^}]*min-width: 0;[^}]*max-width: 560px;/s, ); expect(css).not.toMatch(/\.popover\s*{[^}]*\n\s*width: 320px;/s); }); diff --git a/src/styles/components.css b/src/styles/components.css index 9e9bb67..7c76068 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -6,7 +6,7 @@ .popover { width: 100%; min-width: 0; - max-width: 320px; + max-width: 560px; } .screen { @@ -20,6 +20,27 @@ margin-top: 14px; } + /* Floating window only grows wider than the tray popup. When it does, lay the dashboard out as a + responsive two-column grid so many subscriptions stay visible without scrolling. Banners and + the total-spend summary still span the full width. */ + @media (min-width: 480px) { + .screen-page[data-screen='dashboard'] { + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 12px; + row-gap: 14px; + align-content: start; + } + + .screen-page[data-screen='dashboard'] > :not(.provider-reorder-shell) { + grid-column: 1 / -1; + } + + .screen-page[data-screen='dashboard'] .provider-reorder-shell { + margin-top: 0; + } + } + .metric { padding: 10px 14px; } From fecef445e10dacccb0d7f28a9a2c2d2e39d0388d Mon Sep 17 00:00:00 2001 From: Jacky Lam Date: Wed, 12 Aug 2026 22:22:12 +0800 Subject: [PATCH 2/2] fix: keep floating windows draggable and on screen --- src-tauri/src/window.rs | 32 +++++++++++++++++++++++++------- src/App.svelte | 19 +++++-------------- src/App.test.ts | 5 ++--- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src-tauri/src/window.rs b/src-tauri/src/window.rs index 285b0cb..a25c21b 100644 --- a/src-tauri/src/window.rs +++ b/src-tauri/src/window.rs @@ -532,9 +532,6 @@ fn restore_manual_panel_size(window: &WebviewWindow) -> Result<(), String> { let minimum = PANEL_MIN_HEIGHT.min(maximum); let session = window.app_handle().try_state::>(); let saved_height = session.as_ref().and_then(|session| session.saved_height()); - if let Some(height) = saved_height { - resize_panel_for_context(window, height.clamp(minimum, maximum))?; - } // Floating restores the user's saved width (or the wider default); the popup is always the // original fixed width. let floating = panel_floating(window); @@ -547,12 +544,25 @@ fn restore_manual_panel_size(window: &WebviewWindow) -> Result<(), String> { } else { PANEL_MIN_WIDTH }; - let height = current_logical_height(&window.as_ref().window()).unwrap_or(PANEL_MIN_HEIGHT); - let _ = window.set_size(LogicalSize::new(width, f64::from(height))); + let current_height = current_logical_height(&window.as_ref().window()).unwrap_or(minimum); + let height = restored_panel_height(saved_height, current_height, minimum, maximum); + // Native constraints do not resize an already-created borderless window. Apply the clamped + // height directly so floating windows never open taller than the current display work area. + if floating { + window + .set_size(LogicalSize::new(width, f64::from(height))) + .map_err(|_| "OpenQuota window could not be resized.".to_owned())?; + } else { + resize_panel_for_context(window, height)?; + } configure_panel_size_constraints(window)?; Ok(()) } +fn restored_panel_height(saved: Option, current: u32, minimum: u32, maximum: u32) -> u32 { + saved.unwrap_or(current).clamp(minimum, maximum) +} + fn resize_panel_for_context(window: &WebviewWindow, height: u32) -> Result<(), String> { if panel_floating(window) { let width = effective_panel_width(window); @@ -887,8 +897,9 @@ mod tests { use super::{ anchored_vertical_frame, clamped_panel_width, panel_resize_edge_for_context, - panel_resize_edge_for_frames, panel_surface_color, PanelHeightMode, PanelResizeEdge, - PanelResizeSession, VerticalFrame, DARK_PANEL_SURFACE, LIGHT_PANEL_SURFACE, + panel_resize_edge_for_frames, panel_surface_color, restored_panel_height, PanelHeightMode, + PanelResizeEdge, PanelResizeSession, VerticalFrame, DARK_PANEL_SURFACE, + LIGHT_PANEL_SURFACE, }; use crate::models::ThemePreference; use crate::storage::Storage; @@ -932,6 +943,13 @@ mod tests { assert_eq!(clamped_panel_width(10_000), 560.0); } + #[test] + fn restored_height_clamps_the_startup_window_without_a_manual_preference() { + assert_eq!(restored_panel_height(None, 800, 240, 765), 765); + assert_eq!(restored_panel_height(Some(540), 800, 240, 765), 540); + assert_eq!(restored_panel_height(Some(1_000), 800, 240, 765), 765); + } + #[test] fn panel_surface_follows_explicit_and_system_theme_preferences() { assert_eq!( diff --git a/src/App.svelte b/src/App.svelte index e45c23c..af08036 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -439,12 +439,6 @@ if (restoreFocus) optionsMenuElement.querySelector('summary')?.focus(); } function handleWindowPointerDown(event: PointerEvent) { - if ( - event.target instanceof Element && - event.target.closest('.floating-chrome__drag') !== null - ) { - handleFloatingWindowPointerDown(event); - } if ( optionsMenuElement?.open && event.target instanceof Node && @@ -566,13 +560,6 @@ } })(); } - function handleFloatingWindowPointerDown(event: PointerEvent) { - if (event.button !== 0 || !('__TAURI_INTERNALS__' in window)) return; - event.preventDefault(); - void getCurrentWindow() - .startDragging() - .catch(() => (settingsError = 'OpenQuota window could not be moved.')); - } async function changePanelHeightMode(mode: PanelHeightMode) { if (!('__TAURI_INTERNALS__' in window)) return; panelHeightModeRequest += 1; @@ -759,7 +746,11 @@ {/if} {#if floatingWindow}
-
+
OpenQuota
diff --git a/src/App.test.ts b/src/App.test.ts index 1d53eb6..77e3b04 100644 --- a/src/App.test.ts +++ b/src/App.test.ts @@ -162,7 +162,7 @@ describe('OpenQuota dashboard', () => { ); }); - it('provides a native drag surface and hide control in floating window mode', async () => { + it('provides a native drag region and hide control in floating window mode', async () => { Object.defineProperty(window, '__TAURI_INTERNALS__', { configurable: true, value: {}, @@ -193,8 +193,7 @@ describe('OpenQuota dashboard', () => { 'panel-resize-dragger--bottom', ); - await fireEvent.pointerDown(dragSurface!, { button: 0 }); - expect(mocks.startDragging).toHaveBeenCalledOnce(); + expect(dragSurface).toHaveAttribute('data-tauri-drag-region'); } finally { delete (window as Window & { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__; Object.defineProperty(navigator, 'userAgent', { configurable: true, value: userAgent });