From 1b1d7ba8d8b9845c2eb614a75576d7c43dc02e17 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:43:19 +1000 Subject: [PATCH 01/14] Add "open_file_dialoge()" function to browser window class This will open a file browser window that can be customised via the "FileDialogOptions" and "FileFilters". --- src/browser_window.rs | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/browser_window.rs b/src/browser_window.rs index 108954d..4c7b6fb 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -8,6 +8,7 @@ use tao::{ event_loop::EventLoop, window::{Fullscreen, ProgressBarState, Window, WindowBuilder, WindowId}, }; +use rfd::FileDialog; #[cfg(not(target_os = "android"))] use muda::Menu; @@ -131,6 +132,26 @@ pub struct BrowserWindowOptions { pub fullscreen: Option, } +#[napi(object)] +pub struct FileDialogOptions { + /// Whether to allow selecting multiple files. + pub multiple: Option, + /// The title of the file dialog. + pub title: Option, + /// The initial directory of the file dialog. + pub default_path: Option, + /// The file types that can be selected in the file dialog. + pub filters: Option>, +} + +#[napi(object)] +pub struct FileFilter { + /// The name of the file filter. + pub name: String, + /// The extensions of the file filter. + pub extensions: Vec, +} + impl Default for BrowserWindowOptions { fn default() -> Self { Self { @@ -444,6 +465,51 @@ impl BrowserWindow { self.window.set_inner_size(LogicalSize::new(width, height)); } + #[napi] + /// Opens a file select dialog + pub fn open_file_dialog(&self, options: Option) -> Result> { + let mut dialog = FileDialog::new(); + + if let Some(opts) = options.as_ref() { + if let Some(title) = &opts.title { + dialog = dialog.set_title(title); + } + + if let Some(path) = &opts.default_path { + dialog = dialog.set_directory(path); + } + + if let Some(filters) = &opts.filters { + for filter in filters { + dialog = dialog.add_filter( + &filter.name, + &filter.extensions, + ); + } + } + } + + dialog = dialog.add_filter("All Files", &["*"]); + + let files = if options + .as_ref() + .and_then(|o| o.multiple) + .unwrap_or(false) + { + dialog.pick_files() + } else { + dialog.pick_file().map(|f| vec![f]) + }; + + Ok( + files + .unwrap_or_default() + .into_iter() + .map(|f| f.as_path().to_string_lossy().to_string()) + .collect() + ) + } + #[napi] /// Gets the window ID. pub fn id(&self) -> u32 { From 3bb32f145f046b4224a1f9db14f06e2243c2fc83 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:46:38 +1000 Subject: [PATCH 02/14] Add "browserWindow.openFileDialog()", "FileDialogOptions" and "FileFilter" to index.d.ts --- index.d.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/index.d.ts b/index.d.ts index 7c63bf2..1b6408c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -57,6 +57,8 @@ export declare class BrowserWindow { setResizable(resizable: boolean): void /** Sets the window inner size (width and height). */ setSize(width: number, height: number): void + /** Opens a file select dialog */ + openFileDialog(options?: FileDialogOptions | undefined | null): Array /** Gets the window ID. */ id(): number /** Gets whether the window has a menu. */ @@ -231,6 +233,24 @@ export interface Dimensions { height: number } +export interface FileDialogOptions { + /** Whether to allow selecting multiple files. */ + multiple?: boolean + /** The title of the file dialog. */ + title?: string + /** The initial directory of the file dialog. */ + defaultPath?: string + /** The file types that can be selected in the file dialog. */ + filters?: Array +} + +export interface FileFilter { + /** The name of the file filter. */ + name: string + /** The extensions of the file filter. */ + extensions: Array +} + export declare const enum FullscreenType { /** Exclusive fullscreen. */ Exclusive = 0, From 934a8dad66f3010528f62c97ae92097371044015 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:50:58 +1000 Subject: [PATCH 03/14] Add "rfd" dependency for file dialoge --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 7f120d3..04a463f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ napi = { version = "3.8.2", default-features = true, features = ["napi9"] napi-derive = "3.5.1" tao = { version = "0.34.5", features = ["rwh_06"] } wry = { version = "0.53.5", features = ["devtools", "fullscreen"] } +rfd = "0.15.4" [target.'cfg(not(target_os = "android"))'.dependencies] muda = { version = "0.17", features = ["libxdo"] } From e65349c17e58155154552c9f5a8d61c21b507cf2 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:18:06 +1000 Subject: [PATCH 04/14] Update Cargo.toml --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 04a463f..7f120d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ napi = { version = "3.8.2", default-features = true, features = ["napi9"] napi-derive = "3.5.1" tao = { version = "0.34.5", features = ["rwh_06"] } wry = { version = "0.53.5", features = ["devtools", "fullscreen"] } -rfd = "0.15.4" [target.'cfg(not(target_os = "android"))'.dependencies] muda = { version = "0.17", features = ["libxdo"] } From 47faec54ad4db125e4647133abb4be4efede6b9e Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:21:56 +1000 Subject: [PATCH 05/14] Implemented a way to choose logical or physical sizing/position Also added other functions such as, "get_size", "set_position" and "get_position" all suporting both logical and physical sizing/positioning. --- src/browser_window.rs | 137 +++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 62 deletions(-) diff --git a/src/browser_window.rs b/src/browser_window.rs index 4c7b6fb..13d4125 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -4,11 +4,10 @@ use std::sync::{Arc, Mutex}; use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; use tao::{ - dpi::{LogicalPosition, LogicalSize, PhysicalSize}, + dpi::{LogicalPosition, PhysicalPosition, LogicalSize, PhysicalSize}, event_loop::EventLoop, window::{Fullscreen, ProgressBarState, Window, WindowBuilder, WindowId}, }; -use rfd::FileDialog; #[cfg(not(target_os = "android"))] use muda::Menu; @@ -98,6 +97,8 @@ pub struct BrowserWindowOptions { pub resizable: Option, /// The window title. pub title: Option, + /// Whether to use logical sizing (DPI-aware) instead of physical sizing for width, height, x, and y. + pub logical: Option, /// The width of the window. pub width: Option, /// The height of the window. @@ -132,26 +133,6 @@ pub struct BrowserWindowOptions { pub fullscreen: Option, } -#[napi(object)] -pub struct FileDialogOptions { - /// Whether to allow selecting multiple files. - pub multiple: Option, - /// The title of the file dialog. - pub title: Option, - /// The initial directory of the file dialog. - pub default_path: Option, - /// The file types that can be selected in the file dialog. - pub filters: Option>, -} - -#[napi(object)] -pub struct FileFilter { - /// The name of the file filter. - pub name: String, - /// The extensions of the file filter. - pub extensions: Vec, -} - impl Default for BrowserWindowOptions { fn default() -> Self { Self { @@ -159,6 +140,7 @@ impl Default for BrowserWindowOptions { show_menu: Some(true), resizable: Some(true), title: Some("WebviewJS".to_owned()), + logical: Some(false), width: Some(800.0), height: Some(600.0), x: Some(0.0), @@ -208,11 +190,27 @@ impl BrowserWindow { } if let Some(width) = options.width { - window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); + if let Some(logical) = options.logical { + if logical { + window = window.with_inner_size(LogicalSize::new(width, options.height.unwrap())); + } else { + window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); + } + } else { + window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); + } } if let Some(x) = options.x { - window = window.with_position(LogicalPosition::new(x, options.y.unwrap())); + if let Some(logical) = options.logical { + if logical { + window = window.with_position(LogicalPosition::new(x, options.y.unwrap())); + } else { + window = window.with_position(PhysicalPosition::new(x, options.y.unwrap())); + } + } else { + window = window.with_position(PhysicalPosition::new(x, options.y.unwrap())); + } } if let Some(visible) = options.visible { @@ -461,53 +459,68 @@ impl BrowserWindow { #[napi] /// Sets the window inner size (width and height). - pub fn set_size(&self, width: u32, height: u32) { - self.window.set_inner_size(LogicalSize::new(width, height)); + pub fn set_size(&self, width: u32, height: u32, logical: Option) { + if let Some(logical) = logical { + if logical { + self.window.set_inner_size(LogicalSize::new(width, height)); + } else { + self.window.set_inner_size(PhysicalSize::new(width, height)); + } + } else { + self.window.set_inner_size(PhysicalSize::new(width, height)); + } } #[napi] - /// Opens a file select dialog - pub fn open_file_dialog(&self, options: Option) -> Result> { - let mut dialog = FileDialog::new(); - - if let Some(opts) = options.as_ref() { - if let Some(title) = &opts.title { - dialog = dialog.set_title(title); + /// Gets the window inner size. + pub fn get_size(&self, logical: Option) -> Dimensions { + let size = self.window.inner_size(); + if let Some(logical) = logical { + if logical { + let logical_size = size.to_logical::(self.window.scale_factor()); + return Dimensions { + width: logical_size.width as u32, + height: logical_size.height as u32, + }; } + } + Dimensions { + width: size.width, + height: size.height, + } + } - if let Some(path) = &opts.default_path { - dialog = dialog.set_directory(path); + #[napi] + /// Sets the window position (x and y). + pub fn set_position(&self, x: i32, y: i32, logical: Option) { + if let Some(logical) = logical { + if logical { + self.window.set_outer_position(LogicalPosition::new(x, y)); + } else { + self.window.set_outer_position(PhysicalPosition::new(x, y)); } + } else { + self.window.set_outer_position(PhysicalPosition::new(x, y)); + } + } - if let Some(filters) = &opts.filters { - for filter in filters { - dialog = dialog.add_filter( - &filter.name, - &filter.extensions, - ); - } + #[napi] + /// Gets the window position. + pub fn get_position(&self, logical: Option) -> Position { + let position = self.window.outer_position().unwrap_or(PhysicalPosition::new(0, 0)); + if let Some(logical) = logical { + if logical { + let logical_position = position.to_logical::(self.window.scale_factor()); + return Position { + x: logical_position.x as i32, + y: logical_position.y as i32, + }; } } - - dialog = dialog.add_filter("All Files", &["*"]); - - let files = if options - .as_ref() - .and_then(|o| o.multiple) - .unwrap_or(false) - { - dialog.pick_files() - } else { - dialog.pick_file().map(|f| vec![f]) - }; - - Ok( - files - .unwrap_or_default() - .into_iter() - .map(|f| f.as_path().to_string_lossy().to_string()) - .collect() - ) + Position { + x: position.x, + y: position.y, + } } #[napi] From f87109416a3631ebde9e3c142d4c54b2ef7a7212 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:23:48 +1000 Subject: [PATCH 06/14] Implemented a way to choose logical or physical sizing/position Also added other functions such as, "getSize", "setPosition" and "getPosition" all suporting both logical and physical sizing/positioning. --- index.d.ts | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/index.d.ts b/index.d.ts index 1b6408c..f70de83 100644 --- a/index.d.ts +++ b/index.d.ts @@ -56,9 +56,13 @@ export declare class BrowserWindow { /** Sets resizable. */ setResizable(resizable: boolean): void /** Sets the window inner size (width and height). */ - setSize(width: number, height: number): void - /** Opens a file select dialog */ - openFileDialog(options?: FileDialogOptions | undefined | null): Array + setSize(width: number, height: number, logical?: boolean | undefined | null): void + /** Gets the window inner size. */ + getSize(logical?: boolean | undefined | null): Dimensions + /** Sets the window position (x and y). */ + setPosition(x: number, y: number, logical?: boolean | undefined | null): void + /** Gets the window position. */ + getPosition(logical?: boolean | undefined | null): Position /** Gets the window ID. */ id(): number /** Gets whether the window has a menu. */ @@ -171,6 +175,8 @@ export interface BrowserWindowOptions { resizable?: boolean /** The window title. */ title?: string + /** Whether to use logical sizing (DPI-aware) instead of physical sizing for width, height, x, and y. */ + logical?: boolean /** The width of the window. */ width?: number /** The height of the window. */ @@ -233,24 +239,6 @@ export interface Dimensions { height: number } -export interface FileDialogOptions { - /** Whether to allow selecting multiple files. */ - multiple?: boolean - /** The title of the file dialog. */ - title?: string - /** The initial directory of the file dialog. */ - defaultPath?: string - /** The file types that can be selected in the file dialog. */ - filters?: Array -} - -export interface FileFilter { - /** The name of the file filter. */ - name: string - /** The extensions of the file filter. */ - extensions: Array -} - export declare const enum FullscreenType { /** Exclusive fullscreen. */ Exclusive = 0, From 585741fbf035288a7151882e0e389c026c69c985 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 23:56:25 +1000 Subject: [PATCH 07/14] Do not include rfd in android builds --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 04a463f..20b4b04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,10 @@ napi = { version = "3.8.2", default-features = true, features = ["napi9"] napi-derive = "3.5.1" tao = { version = "0.34.5", features = ["rwh_06"] } wry = { version = "0.53.5", features = ["devtools", "fullscreen"] } -rfd = "0.15.4" [target.'cfg(not(target_os = "android"))'.dependencies] muda = { version = "0.17", features = ["libxdo"] } +rfd = "0.15.4" [target.'cfg(target_os = "linux")'.dependencies] gtk = "0.18" From 8063322e538d35d0e29bbfa304aea7f6e4e6f14f Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Fri, 19 Jun 2026 23:56:46 +1000 Subject: [PATCH 08/14] Do not include rfd:FileDialog or any features using it in android builds --- src/browser_window.rs | 93 +++++-------------------------------------- 1 file changed, 9 insertions(+), 84 deletions(-) diff --git a/src/browser_window.rs b/src/browser_window.rs index a2bd873..ad2f081 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -4,10 +4,11 @@ use std::sync::{Arc, Mutex}; use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; use tao::{ - dpi::{LogicalPosition, PhysicalPosition, LogicalSize, PhysicalSize}, + dpi::{LogicalPosition, LogicalSize, PhysicalSize}, event_loop::EventLoop, window::{Fullscreen, ProgressBarState, Window, WindowBuilder, WindowId}, }; +#[cfg(not(target_os = "android"))] use rfd::FileDialog; #[cfg(not(target_os = "android"))] use muda::Menu; @@ -98,8 +99,6 @@ pub struct BrowserWindowOptions { pub resizable: Option, /// The window title. pub title: Option, - /// Whether to use logical sizing (DPI-aware) instead of physical sizing for width, height, x, and y. - pub logical: Option, /// The width of the window. pub width: Option, /// The height of the window. @@ -134,6 +133,7 @@ pub struct BrowserWindowOptions { pub fullscreen: Option, } +#[cfg(not(target_os = "android"))] #[napi(object)] pub struct FileDialogOptions { /// Whether to allow selecting multiple files. @@ -146,6 +146,7 @@ pub struct FileDialogOptions { pub filters: Option>, } +#[cfg(not(target_os = "android"))] #[napi(object)] pub struct FileFilter { /// The name of the file filter. @@ -161,7 +162,6 @@ impl Default for BrowserWindowOptions { show_menu: Some(true), resizable: Some(true), title: Some("WebviewJS".to_owned()), - logical: Some(false), width: Some(800.0), height: Some(600.0), x: Some(0.0), @@ -211,27 +211,11 @@ impl BrowserWindow { } if let Some(width) = options.width { - if let Some(logical) = options.logical { - if logical { - window = window.with_inner_size(LogicalSize::new(width, options.height.unwrap())); - } else { - window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); - } - } else { - window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); - } + window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); } if let Some(x) = options.x { - if let Some(logical) = options.logical { - if logical { - window = window.with_position(LogicalPosition::new(x, options.y.unwrap())); - } else { - window = window.with_position(PhysicalPosition::new(x, options.y.unwrap())); - } - } else { - window = window.with_position(PhysicalPosition::new(x, options.y.unwrap())); - } + window = window.with_position(LogicalPosition::new(x, options.y.unwrap())); } if let Some(visible) = options.visible { @@ -480,70 +464,11 @@ impl BrowserWindow { #[napi] /// Sets the window inner size (width and height). - pub fn set_size(&self, width: u32, height: u32, logical: Option) { - if let Some(logical) = logical { - if logical { - self.window.set_inner_size(LogicalSize::new(width, height)); - } else { - self.window.set_inner_size(PhysicalSize::new(width, height)); - } - } else { - self.window.set_inner_size(PhysicalSize::new(width, height)); - } - } - - #[napi] - /// Gets the window inner size. - pub fn get_size(&self, logical: Option) -> Dimensions { - let size = self.window.inner_size(); - if let Some(logical) = logical { - if logical { - let logical_size = size.to_logical::(self.window.scale_factor()); - return Dimensions { - width: logical_size.width as u32, - height: logical_size.height as u32, - }; - } - } - Dimensions { - width: size.width, - height: size.height, - } - } - - #[napi] - /// Sets the window position (x and y). - pub fn set_position(&self, x: i32, y: i32, logical: Option) { - if let Some(logical) = logical { - if logical { - self.window.set_outer_position(LogicalPosition::new(x, y)); - } else { - self.window.set_outer_position(PhysicalPosition::new(x, y)); - } - } else { - self.window.set_outer_position(PhysicalPosition::new(x, y)); - } - } - - #[napi] - /// Gets the window position. - pub fn get_position(&self, logical: Option) -> Position { - let position = self.window.outer_position().unwrap_or(PhysicalPosition::new(0, 0)); - if let Some(logical) = logical { - if logical { - let logical_position = position.to_logical::(self.window.scale_factor()); - return Position { - x: logical_position.x as i32, - y: logical_position.y as i32, - }; - } - } - Position { - x: position.x, - y: position.y, - } + pub fn set_size(&self, width: u32, height: u32) { + self.window.set_inner_size(LogicalSize::new(width, height)); } + #[cfg(not(target_os = "android"))] #[napi] /// Opens a file select dialog pub fn open_file_dialog(&self, options: Option) -> Result> { From 7910b4adb09a702caaf70c28e87b4cfca09ce88e Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Sat, 20 Jun 2026 00:04:51 +1000 Subject: [PATCH 09/14] Fix open_file_dialog being missing by making it throw and error on android when called --- src/browser_window.rs | 76 ++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/src/browser_window.rs b/src/browser_window.rs index ad2f081..7eaffc7 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -468,50 +468,60 @@ impl BrowserWindow { self.window.set_inner_size(LogicalSize::new(width, height)); } - #[cfg(not(target_os = "android"))] #[napi] /// Opens a file select dialog pub fn open_file_dialog(&self, options: Option) -> Result> { - let mut dialog = FileDialog::new(); + #[cfg(target_os = "android")] + { + return Err(napi::Error::new( + napi::Status::GenericFailure, + "File dialogs are not supported on Android", + )); + } + + #[cfg(not(target_os = "android"))] + { + let mut dialog = FileDialog::new(); - if let Some(opts) = options.as_ref() { - if let Some(title) = &opts.title { - dialog = dialog.set_title(title); - } + if let Some(opts) = options.as_ref() { + if let Some(title) = &opts.title { + dialog = dialog.set_title(title); + } - if let Some(path) = &opts.default_path { - dialog = dialog.set_directory(path); - } + if let Some(path) = &opts.default_path { + dialog = dialog.set_directory(path); + } - if let Some(filters) = &opts.filters { - for filter in filters { - dialog = dialog.add_filter( - &filter.name, - &filter.extensions, - ); + if let Some(filters) = &opts.filters { + for filter in filters { + dialog = dialog.add_filter( + &filter.name, + &filter.extensions, + ); + } } } - } - dialog = dialog.add_filter("All Files", &["*"]); + dialog = dialog.add_filter("All Files", &["*"]); - let files = if options - .as_ref() - .and_then(|o| o.multiple) - .unwrap_or(false) - { - dialog.pick_files() - } else { - dialog.pick_file().map(|f| vec![f]) - }; + let files = if options + .as_ref() + .and_then(|o| o.multiple) + .unwrap_or(false) + { + dialog.pick_files() + } else { + dialog.pick_file().map(|f| vec![f]) + }; - Ok( - files - .unwrap_or_default() - .into_iter() - .map(|f| f.as_path().to_string_lossy().to_string()) - .collect() - ) + Ok( + files + .unwrap_or_default() + .into_iter() + .map(|f| f.as_path().to_string_lossy().to_string()) + .collect() + ) + } } #[napi] From 8f09557a0371470ae6f1e5279b77c093e01ee2b2 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Sat, 20 Jun 2026 00:11:18 +1000 Subject: [PATCH 10/14] Remove parts FileDialogOptions fields so it still compiles on android --- src/browser_window.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/browser_window.rs b/src/browser_window.rs index 7eaffc7..828be22 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -133,16 +133,18 @@ pub struct BrowserWindowOptions { pub fullscreen: Option, } -#[cfg(not(target_os = "android"))] +// This whole thing isnt supported/needed on android but we can just exclude the parts that arent supported from the build so it compiles. #[napi(object)] pub struct FileDialogOptions { /// Whether to allow selecting multiple files. pub multiple: Option, /// The title of the file dialog. pub title: Option, - /// The initial directory of the file dialog. + /// The initial directory of the file dialog. (Not supported on Android) + #[cfg(not(target_os = "android"))] pub default_path: Option, - /// The file types that can be selected in the file dialog. + /// The file types that can be selected in the file dialog. (Not supported on Android) + #[cfg(not(target_os = "android"))] pub filters: Option>, } From 69c27fe0437496d1820db4a15053b81577576195 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Sat, 20 Jun 2026 00:16:52 +1000 Subject: [PATCH 11/14] Leave structs as they are for android builds as they don't use anything not supported on android why didn't I do this from the start? I have no idea what I was doing before. --- src/browser_window.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/browser_window.rs b/src/browser_window.rs index 828be22..82b6418 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -140,15 +140,12 @@ pub struct FileDialogOptions { pub multiple: Option, /// The title of the file dialog. pub title: Option, - /// The initial directory of the file dialog. (Not supported on Android) - #[cfg(not(target_os = "android"))] + /// The initial directory of the file dialog. pub default_path: Option, - /// The file types that can be selected in the file dialog. (Not supported on Android) - #[cfg(not(target_os = "android"))] + /// The file types that can be selected in the file dialog. pub filters: Option>, } -#[cfg(not(target_os = "android"))] #[napi(object)] pub struct FileFilter { /// The name of the file filter. From 85f948c5d2504a5e344b85a09bef515340e3ded9 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Sat, 20 Jun 2026 00:41:55 +1000 Subject: [PATCH 12/14] Reimplement the stuff I accidentally removed --- src/browser_window.rs | 91 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 6 deletions(-) diff --git a/src/browser_window.rs b/src/browser_window.rs index 82b6418..8624811 100644 --- a/src/browser_window.rs +++ b/src/browser_window.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex}; use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; use tao::{ - dpi::{LogicalPosition, LogicalSize, PhysicalSize}, + dpi::{LogicalPosition, PhysicalPosition, LogicalSize, PhysicalSize}, event_loop::EventLoop, window::{Fullscreen, ProgressBarState, Window, WindowBuilder, WindowId}, }; @@ -99,6 +99,8 @@ pub struct BrowserWindowOptions { pub resizable: Option, /// The window title. pub title: Option, + /// Whether to use logical sizing (DPI-aware) instead of physical sizing for width, height, x, and y. + pub logical: Option, /// The width of the window. pub width: Option, /// The height of the window. @@ -161,6 +163,7 @@ impl Default for BrowserWindowOptions { show_menu: Some(true), resizable: Some(true), title: Some("WebviewJS".to_owned()), + logical: Some(false), width: Some(800.0), height: Some(600.0), x: Some(0.0), @@ -204,17 +207,33 @@ impl BrowserWindow { let options = options.unwrap_or_default(); let mut window = WindowBuilder::new(); - + if let Some(resizable) = options.resizable { window = window.with_resizable(resizable); } if let Some(width) = options.width { - window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); + if let Some(logical) = options.logical { + if logical { + window = window.with_inner_size(LogicalSize::new(width, options.height.unwrap())); + } else { + window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); + } + } else { + window = window.with_inner_size(PhysicalSize::new(width, options.height.unwrap())); + } } if let Some(x) = options.x { - window = window.with_position(LogicalPosition::new(x, options.y.unwrap())); + if let Some(logical) = options.logical { + if logical { + window = window.with_position(LogicalPosition::new(x, options.y.unwrap())); + } else { + window = window.with_position(PhysicalPosition::new(x, options.y.unwrap())); + } + } else { + window = window.with_position(PhysicalPosition::new(x, options.y.unwrap())); + } } if let Some(visible) = options.visible { @@ -463,8 +482,68 @@ impl BrowserWindow { #[napi] /// Sets the window inner size (width and height). - pub fn set_size(&self, width: u32, height: u32) { - self.window.set_inner_size(LogicalSize::new(width, height)); + pub fn set_size(&self, width: u32, height: u32, logical: Option) { + if let Some(logical) = logical { + if logical { + self.window.set_inner_size(LogicalSize::new(width, height)); + } else { + self.window.set_inner_size(PhysicalSize::new(width, height)); + } + } else { + self.window.set_inner_size(PhysicalSize::new(width, height)); + } + } + + #[napi] + /// Gets the window inner size. + pub fn get_size(&self, logical: Option) -> Dimensions { + let size = self.window.inner_size(); + if let Some(logical) = logical { + if logical { + let logical_size = size.to_logical::(self.window.scale_factor()); + return Dimensions { + width: logical_size.width as u32, + height: logical_size.height as u32, + }; + } + } + Dimensions { + width: size.width, + height: size.height, + } + } + + #[napi] + /// Sets the window position (x and y). + pub fn set_position(&self, x: i32, y: i32, logical: Option) { + if let Some(logical) = logical { + if logical { + self.window.set_outer_position(LogicalPosition::new(x, y)); + } else { + self.window.set_outer_position(PhysicalPosition::new(x, y)); + } + } else { + self.window.set_outer_position(PhysicalPosition::new(x, y)); + } + } + + #[napi] + /// Gets the window position. + pub fn get_position(&self, logical: Option) -> Position { + let position = self.window.outer_position().unwrap_or(PhysicalPosition::new(0, 0)); + if let Some(logical) = logical { + if logical { + let logical_position = position.to_logical::(self.window.scale_factor()); + return Position { + x: logical_position.x as i32, + y: logical_position.y as i32, + }; + } + } + Position { + x: position.x, + y: position.y, + } } #[napi] From e03f24797cd882249ff7d2354409e17eb1b05884 Mon Sep 17 00:00:00 2001 From: Lawtro <98205608+Lawtro37@users.noreply.github.com> Date: Sat, 20 Jun 2026 11:35:51 +1000 Subject: [PATCH 13/14] Change bindings in new file --- js-bindings.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js-bindings.d.ts b/js-bindings.d.ts index 553e905..4f586d3 100644 --- a/js-bindings.d.ts +++ b/js-bindings.d.ts @@ -49,6 +49,9 @@ export declare class BrowserWindow { setMinimizable(minimizable: boolean): void setResizable(resizable: boolean): void setSize(width: number, height: number): void + getSize(width: number, height: number): Dimensions + setPosition(x: number, y: number): void + getPosition(x: number, y: number): Position openFileDialog(options?: FileDialogOptions | undefined | null): Array id(): number hasMenu(): boolean From cb11f1cb62aee229433bf035408020c28ef796c8 Mon Sep 17 00:00:00 2001 From: Twilight <46562212+twlite@users.noreply.github.com> Date: Sat, 20 Jun 2026 11:27:58 +0545 Subject: [PATCH 14/14] chore: update Cargo.toml --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 277ab69..1b9c624 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ serde_json = "1" [target.'cfg(not(target_os = "android"))'.dependencies] rfd = "0.15.4" muda = { version = "0.17", features = ["libxdo"] } -rfd = "0.15.4" [build-dependencies] napi-build = "2"