| | | 1 | | // SPDX-FileCopyrightText: 2026 Alper Çelik |
| | | 2 | | // SPDX-License-Identifier: MIT OR Apache-2.0 |
| | | 3 | | |
| | | 4 | | //! A Qt runtime for Tauri 2 on Linux: Qt windowing replaces tao, QtWebEngine |
| | | 5 | | //! replaces WebKitGTK. The crate implements `tauri_runtime::Runtime`, so an app |
| | | 6 | | //! selects it at the type level (`tauri::Builder::<QtRuntime>::new()`) and does |
| | | 7 | | //! not enable tauri's `wry` feature on Linux. See README.md for the wiring an |
| | | 8 | | //! app has to add, and RUNTIME-TRAITS.md for the Qt mapping of every trait |
| | | 9 | | //! method. |
| | | 10 | | //! |
| | | 11 | | //! Two items outside the stock trait surface exist for the app to use: |
| | | 12 | | //! |
| | | 13 | | //! - [`INVOKE_SYSTEM_SCRIPT`]: QtWebEngine never delivers custom-protocol POST |
| | | 14 | | //! bodies, so the invoke payload travels in a header instead. Pass it to |
| | | 15 | | //! `tauri::Builder::invoke_system`. |
| | | 16 | | //! - [`platform_name`]: which Qt platform plugin is running (`"wayland"`, |
| | | 17 | | //! `"xcb"`, `"offscreen"`, …), for apps that must adapt to platform gaps — |
| | | 18 | | //! a Wayland compositor owns window placement and ignores the stacking hints, |
| | | 19 | | //! while positions themselves are answered with Qt's record of the requested |
| | | 20 | | //! value on every platform (docs/wayland-ci.md). |
| | | 21 | | |
| | | 22 | | use std::collections::HashMap; |
| | | 23 | | use std::sync::atomic::{AtomicU32, Ordering}; |
| | | 24 | | |
| | | 25 | | use parking_lot::Mutex; |
| | | 26 | | use raw_window_handle::{DisplayHandle, HandleError, WindowHandle}; |
| | | 27 | | use tauri_runtime as rt; |
| | | 28 | | use tauri_runtime::{ |
| | | 29 | | monitor::Monitor, |
| | | 30 | | webview::{DetachedWebview, PendingWebview}, |
| | | 31 | | window::{ |
| | | 32 | | CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowEvent, WindowId, |
| | | 33 | | WindowSizeConstraints, |
| | | 34 | | }, |
| | | 35 | | Cookie, DeviceEventFilter, Error, EventLoopProxy, Icon, ProgressBarState, ResizeDirection, |
| | | 36 | | Result, RunEvent, RuntimeInitArgs, UserAttentionType, UserEvent, WebviewEventId, WindowEventId, |
| | | 37 | | }; |
| | | 38 | | use tauri_runtime_qt_core as qtcore; |
| | | 39 | | use tauri_utils::config::{Color, WindowConfig}; |
| | | 40 | | use tauri_utils::Theme; |
| | | 41 | | |
| | | 42 | | pub mod bridge; |
| | | 43 | | pub mod trace; |
| | | 44 | | |
| | | 45 | | /// Name of the platform plugin Qt is running on ("wayland", "xcb", "offscreen", |
| | | 46 | | /// …), or an empty string before the application exists. |
| | | 47 | | /// |
| | | 48 | | /// Apps use this to adapt to platform limitations rather than guess: a Wayland |
| | | 49 | | /// compositor owns window placement (so `always_on_top`/`always_on_bottom` are |
| | | 50 | | /// refused there) and ignores `set_position`'s `move()`, while positions are |
| | | 51 | | /// still answered with Qt's record of the requested value (see |
| | | 52 | | /// `docs/wayland-ci.md`). |
| | 21 | 53 | | pub fn platform_name() -> String { |
| | 21 | 54 | | ops::runtime::platform_name() |
| | 21 | 55 | | } |
| | | 56 | | pub mod ops; |
| | | 57 | | pub mod qt; |
| | | 58 | | |
| | | 59 | | /// Invoke system for this runtime. |
| | | 60 | | /// |
| | | 61 | | /// QtWebEngine never delivers custom-protocol POST bodies, so tauri's default |
| | | 62 | | /// invoke system (payload in the request body) cannot work here. This script |
| | | 63 | | /// keeps the custom-protocol path but moves the payload into the |
| | | 64 | | /// `X-Tauriqt-Payload` header (base64), which the runtime turns back into the |
| | | 65 | | /// request body before dispatching to tauri's IPC handler. |
| | | 66 | | /// |
| | | 67 | | /// Use it with `tauri::Builder::invoke_system(tauri_runtime_qt::INVOKE_SYSTEM_SCRIPT)`. |
| | | 68 | | pub const INVOKE_SYSTEM_SCRIPT: &str = include_str!("invoke_system.js"); |
| | | 69 | | |
| | | 70 | | /// The Qt runtime. Constructed on the main thread; owns the `QApplication`. |
| | | 71 | | #[derive(Debug, Clone, Copy, Default)] |
| | | 72 | | pub struct QtRuntime; |
| | | 73 | | |
| | | 74 | | /// Thread-safe handle to the runtime. |
| | | 75 | | #[derive(Debug, Clone, Copy)] |
| | | 76 | | pub struct QtRuntimeHandle; |
| | | 77 | | |
| | | 78 | | /// Cross-thread user-event proxy. |
| | | 79 | | #[derive(Debug, Clone, Copy)] |
| | | 80 | | pub struct QtEventLoopProxy; |
| | | 81 | | |
| | | 82 | | /// Per-window dispatcher: native Qt window handle (from `qt_window_create`). |
| | | 83 | | #[derive(Debug, Clone, Copy)] |
| | | 84 | | pub struct QtWindowDispatcher { |
| | | 85 | | pub(crate) native: u64, |
| | | 86 | | } |
| | | 87 | | |
| | | 88 | | /// Per-webview dispatcher: native Qt webview handle (from `qt_webview_create`). |
| | | 89 | | #[derive(Debug, Clone)] |
| | | 90 | | pub struct QtWebviewDispatcher { |
| | | 91 | | pub(crate) native: u64, |
| | | 92 | | pub(crate) label: String, |
| | | 93 | | } |
| | | 94 | | |
| | | 95 | | /// Window event handler as registered through `on_window_event`. |
| | | 96 | | type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send + 'static>; |
| | | 97 | | /// Webview event handler as registered through `on_webview_event`. |
| | | 98 | | type WebviewEventHandler = Box<dyn Fn(&WebviewEvent) + Send + 'static>; |
| | | 99 | | |
| | | 100 | | /// `(event id, native window handle, handler)`. |
| | | 101 | | static WINDOW_HANDLERS: Mutex<Vec<(WindowEventId, u64, WindowEventHandler)>> = |
| | | 102 | | Mutex::new(Vec::new()); |
| | | 103 | | /// `(event id, native webview handle, handler)`. |
| | | 104 | | static WEBVIEW_HANDLERS: Mutex<Vec<(WebviewEventId, u64, WebviewEventHandler)>> = |
| | | 105 | | Mutex::new(Vec::new()); |
| | | 106 | | /// Monotonic id shared by both registries. |
| | | 107 | | static NEXT_EVENT_ID: AtomicU32 = AtomicU32::new(1); |
| | | 108 | | |
| | | 109 | | /// Registers a window event handler for a native window handle. |
| | 21 | 110 | | fn register_window_handler<F: Fn(&WindowEvent) + Send + 'static>( |
| | 21 | 111 | | native: u64, |
| | 21 | 112 | | f: F, |
| | 21 | 113 | | ) -> WindowEventId { |
| | 21 | 114 | | let id = NEXT_EVENT_ID.fetch_add(1, Ordering::Relaxed); |
| | 21 | 115 | | WINDOW_HANDLERS.lock().push((id, native, Box::new(f))); |
| | 21 | 116 | | id |
| | 21 | 117 | | } |
| | | 118 | | |
| | | 119 | | /// Registers a webview event handler for a native webview handle. |
| | 21 | 120 | | fn register_webview_handler<F: Fn(&WebviewEvent) + Send + 'static>( |
| | 21 | 121 | | native: u64, |
| | 21 | 122 | | f: F, |
| | 21 | 123 | | ) -> WebviewEventId { |
| | 21 | 124 | | let id = NEXT_EVENT_ID.fetch_add(1, Ordering::Relaxed); |
| | 21 | 125 | | WEBVIEW_HANDLERS.lock().push((id, native, Box::new(f))); |
| | 21 | 126 | | id |
| | 21 | 127 | | } |
| | | 128 | | |
| | | 129 | | /// Native handle of the Qt window backing each `WindowId`. |
| | | 130 | | static NATIVE_WINDOWS: Mutex<Vec<(WindowId, u64)>> = Mutex::new(Vec::new()); |
| | | 131 | | |
| | | 132 | | /// Custom-protocol handler as tauri registers it on a pending webview. |
| | | 133 | | type SchemeHandler = Box< |
| | | 134 | | dyn Fn( |
| | | 135 | | &str, |
| | | 136 | | http::Request<Vec<u8>>, |
| | | 137 | | Box<dyn FnOnce(http::Response<std::borrow::Cow<'static, [u8]>>) + Send>, |
| | | 138 | | ) + Send |
| | | 139 | | + Sync |
| | | 140 | | + 'static, |
| | | 141 | | >; |
| | | 142 | | |
| | | 143 | | /// Per native webview handle: its tauri label plus the `uri_scheme_protocols` |
| | | 144 | | /// tauri registered for it (scheme name → handler). Tauri's handlers take the |
| | | 145 | | /// webview *label*, not the scheme, as their first argument. |
| | | 146 | | static SCHEME_PROTOCOLS: Mutex<Vec<(u64, String, HashMap<String, SchemeHandler>)>> = |
| | | 147 | | Mutex::new(Vec::new()); |
| | | 148 | | |
| | | 149 | | /// Creates the Qt window for a pending window and records its handle. |
| | 21 | 150 | | fn build_window<T: UserEvent>( |
| | 21 | 151 | | label: String, |
| | 21 | 152 | | builder: QtWindowBuilder, |
| | 21 | 153 | | ) -> Result<(WindowId, QtWindowDispatcher)> { |
| | 21 | 154 | | let native = bridge::window::ffi::qt_window_create( |
| | 21 | 155 | | &label, |
| | 21 | 156 | | &builder.title, |
| | 21 | 157 | | builder.width as i32, |
| | 21 | 158 | | builder.height as i32, |
| | 21 | 159 | | builder.visible, |
| | 21 | 160 | | builder.decorations, |
| | | 161 | | ); |
| | 21 | 162 | | if native == 0 { |
| | 0 | 163 | | return Err(Error::CreateWindow); |
| | 21 | 164 | | } |
| | 21 | 165 | | if builder.maximized { |
| | 0 | 166 | | bridge::window::ffi::qt_window_set_maximized(native, true); |
| | 21 | 167 | | } |
| | 21 | 168 | | if builder.center { |
| | 0 | 169 | | bridge::window::ffi::qt_window_center(native); |
| | 21 | 170 | | } |
| | 21 | 171 | | if let Some((x, y)) = builder.position { |
| | 0 | 172 | | let _ = (x, y); // M4: absolute positioning is a no-op on Wayland. |
| | 21 | 173 | | } |
| | 21 | 174 | | let id = WindowId::from(native as u32); |
| | 21 | 175 | | NATIVE_WINDOWS.lock().push((id, native)); |
| | 21 | 176 | | crate::qtrace!("[tauri-runtime-qt] window '{label}' created (native {native})"); |
| | 21 | 177 | | Ok((id, QtWindowDispatcher { native })) |
| | 21 | 178 | | } |
| | | 179 | | |
| | | 180 | | /// Creates the Qt webview inside an existing window and installs the schemes. |
| | 21 | 181 | | fn build_webview<T: UserEvent>( |
| | 21 | 182 | | window_native: u64, |
| | 21 | 183 | | pending: PendingWebview<T, QtRuntime>, |
| | 21 | 184 | | ) -> Result<DetachedWebview<T, QtRuntime>> { |
| | 21 | 185 | | let label = pending.label.clone(); |
| | 21 | 186 | | let pending_url = pending.url.clone(); |
| | | 187 | | // Create the view without navigating, inject tauri's initialization scripts |
| | | 188 | | // (they define `window.__TAURI_INTERNALS__`, i.e. `invoke`), then navigate. |
| | 21 | 189 | | let native = bridge::webview::ffi::qt_webview_create(window_native, ""); |
| | 21 | 190 | | if native == 0 { |
| | 0 | 191 | | return Err(Error::CreateWebview( |
| | 0 | 192 | | "qt_webview_create returned a null handle".into(), |
| | 0 | 193 | | )); |
| | 21 | 194 | | } |
| | 21 | 195 | | let view_ptr = bridge::webview::ffi::qt_webview_native_handle(native); |
| | 21 | 196 | | bridge::window::ffi::qt_window_attach_webview(window_native, view_ptr); |
| | 21 | 197 | | SCHEME_PROTOCOLS |
| | 21 | 198 | | .lock() |
| | 21 | 199 | | .push((native, label.clone(), pending.uri_scheme_protocols)); |
| | 21 | 200 | | qt::ffi::qt_scheme_install_handler(native); |
| | | 201 | | // Mirror the profile's cookie store (Qt can only enumerate asynchronously). |
| | 21 | 202 | | ops::cookies::register_webview(native, view_ptr); |
| | | 203 | | |
| | 189 | 204 | | for script in &pending.webview_attributes.initialization_scripts { |
| | 189 | 205 | | bridge::webview::ffi::qt_webview_add_init_script( |
| | 189 | 206 | | native, |
| | 189 | 207 | | &script.script, |
| | 189 | 208 | | script.for_main_frame_only, |
| | 189 | 209 | | ); |
| | 189 | 210 | | } |
| | 21 | 211 | | if !pending_url.is_empty() { |
| | 21 | 212 | | bridge::webview::ffi::qt_webview_navigate(native, &pending_url); |
| | 21 | 213 | | } |
| | 21 | 214 | | crate::qtrace!( |
| | | 215 | | "[tauri-runtime-qt] webview '{label}' created in window {window_native} (native {native}), url {}", |
| | | 216 | | pending_url |
| | | 217 | | ); |
| | 21 | 218 | | Ok(DetachedWebview { |
| | 21 | 219 | | label: label.clone(), |
| | 21 | 220 | | dispatcher: QtWebviewDispatcher { native, label }, |
| | 21 | 221 | | }) |
| | 21 | 222 | | } |
| | | 223 | | |
| | | 224 | | /// Creates a window, attaching its webview when tauri supplied one. |
| | 21 | 225 | | fn create_window_impl<T: UserEvent>( |
| | 21 | 226 | | pending: PendingWindow<T, QtRuntime>, |
| | 21 | 227 | | ) -> Result<DetachedWindow<T, QtRuntime>> { |
| | 21 | 228 | | let label = pending.label.clone(); |
| | 21 | 229 | | let (id, dispatcher) = build_window::<T>(label.clone(), pending.window_builder)?; |
| | | 230 | | |
| | 21 | 231 | | let webview = match pending.webview { |
| | 21 | 232 | | Some(pending_webview) => { |
| | 21 | 233 | | let webview = build_webview::<T>(dispatcher.native, pending_webview)?; |
| | 21 | 234 | | Some(rt::window::DetachedWindowWebview { |
| | 21 | 235 | | webview, |
| | 21 | 236 | | use_https_scheme: false, |
| | 21 | 237 | | }) |
| | | 238 | | } |
| | 0 | 239 | | None => None, |
| | | 240 | | }; |
| | | 241 | | |
| | 21 | 242 | | Ok(DetachedWindow { |
| | 21 | 243 | | id, |
| | 21 | 244 | | label, |
| | 21 | 245 | | dispatcher, |
| | 21 | 246 | | webview, |
| | 21 | 247 | | }) |
| | 21 | 248 | | } |
| | | 249 | | |
| | | 250 | | /// Native handle of a window previously created by this runtime. |
| | 0 | 251 | | fn window_native_of(id: WindowId) -> Result<u64> { |
| | 0 | 252 | | NATIVE_WINDOWS |
| | 0 | 253 | | .lock() |
| | 0 | 254 | | .iter() |
| | 0 | 255 | | .find(|(window_id, _)| *window_id == id) |
| | 0 | 256 | | .map(|(_, native)| *native) |
| | 0 | 257 | | .ok_or(Error::WindowNotFound) |
| | 0 | 258 | | } |
| | | 259 | | |
| | | 260 | | /// Window attributes builder: records the values the Qt window is created with. |
| | | 261 | | #[derive(Debug, Clone)] |
| | | 262 | | pub struct QtWindowBuilder { |
| | | 263 | | theme: Option<Theme>, |
| | | 264 | | has_icon: bool, |
| | | 265 | | title: String, |
| | | 266 | | width: f64, |
| | | 267 | | height: f64, |
| | | 268 | | visible: bool, |
| | | 269 | | decorations: bool, |
| | | 270 | | maximized: bool, |
| | | 271 | | center: bool, |
| | | 272 | | pub(crate) position: Option<(f64, f64)>, |
| | | 273 | | } |
| | | 274 | | |
| | | 275 | | impl Default for QtWindowBuilder { |
| | 0 | 276 | | fn default() -> Self { |
| | 0 | 277 | | Self { |
| | 0 | 278 | | theme: None, |
| | 0 | 279 | | has_icon: false, |
| | 0 | 280 | | title: String::new(), |
| | 0 | 281 | | width: 800.0, |
| | 0 | 282 | | height: 600.0, |
| | 0 | 283 | | visible: true, |
| | 0 | 284 | | decorations: true, |
| | 0 | 285 | | maximized: false, |
| | 0 | 286 | | center: false, |
| | 0 | 287 | | position: None, |
| | 0 | 288 | | } |
| | 0 | 289 | | } |
| | | 290 | | } |
| | | 291 | | |
| | | 292 | | impl rt::window::WindowBuilderBase for QtWindowBuilder {} |
| | | 293 | | |
| | | 294 | | impl rt::window::WindowBuilder for QtWindowBuilder { |
| | 0 | 295 | | fn new() -> Self { |
| | 0 | 296 | | Self::default() |
| | 0 | 297 | | } |
| | | 298 | | |
| | 21 | 299 | | fn with_config(config: &WindowConfig) -> Self { |
| | 21 | 300 | | let params = qtcore::window::window_params(config); |
| | 21 | 301 | | Self { |
| | 21 | 302 | | theme: params.theme, |
| | 21 | 303 | | has_icon: false, |
| | 21 | 304 | | title: params.title, |
| | 21 | 305 | | width: params.width, |
| | 21 | 306 | | height: params.height, |
| | 21 | 307 | | visible: params.visible, |
| | 21 | 308 | | decorations: params.decorations, |
| | 21 | 309 | | maximized: params.maximized, |
| | 21 | 310 | | center: params.center, |
| | 21 | 311 | | position: None, |
| | 21 | 312 | | } |
| | 21 | 313 | | } |
| | | 314 | | |
| | 0 | 315 | | fn center(self) -> Self { |
| | 0 | 316 | | Self { |
| | 0 | 317 | | center: true, |
| | 0 | 318 | | ..self |
| | 0 | 319 | | } |
| | 0 | 320 | | } |
| | | 321 | | |
| | 0 | 322 | | fn position(self, x: f64, y: f64) -> Self { |
| | 0 | 323 | | Self { |
| | 0 | 324 | | position: Some((x, y)), |
| | 0 | 325 | | ..self |
| | 0 | 326 | | } |
| | 0 | 327 | | } |
| | | 328 | | |
| | 0 | 329 | | fn inner_size(self, width: f64, height: f64) -> Self { |
| | 0 | 330 | | Self { |
| | 0 | 331 | | width, |
| | 0 | 332 | | height, |
| | 0 | 333 | | ..self |
| | 0 | 334 | | } |
| | 0 | 335 | | } |
| | | 336 | | |
| | 0 | 337 | | fn min_inner_size(self, _min_width: f64, _min_height: f64) -> Self { |
| | 0 | 338 | | self |
| | 0 | 339 | | } |
| | | 340 | | |
| | 0 | 341 | | fn max_inner_size(self, _max_width: f64, _max_height: f64) -> Self { |
| | 0 | 342 | | self |
| | 0 | 343 | | } |
| | | 344 | | |
| | 0 | 345 | | fn inner_size_constraints(self, _constraints: WindowSizeConstraints) -> Self { |
| | 0 | 346 | | self |
| | 0 | 347 | | } |
| | | 348 | | |
| | 0 | 349 | | fn prevent_overflow(self) -> Self { |
| | 0 | 350 | | self |
| | 0 | 351 | | } |
| | | 352 | | |
| | 0 | 353 | | fn prevent_overflow_with_margin(self, _margin: rt::dpi::Size) -> Self { |
| | 0 | 354 | | self |
| | 0 | 355 | | } |
| | | 356 | | |
| | 0 | 357 | | fn resizable(self, _resizable: bool) -> Self { |
| | 0 | 358 | | self |
| | 0 | 359 | | } |
| | | 360 | | |
| | 0 | 361 | | fn maximizable(self, _maximizable: bool) -> Self { |
| | 0 | 362 | | self |
| | 0 | 363 | | } |
| | | 364 | | |
| | 0 | 365 | | fn minimizable(self, _minimizable: bool) -> Self { |
| | 0 | 366 | | self |
| | 0 | 367 | | } |
| | | 368 | | |
| | 0 | 369 | | fn closable(self, _closable: bool) -> Self { |
| | 0 | 370 | | self |
| | 0 | 371 | | } |
| | | 372 | | |
| | 0 | 373 | | fn title<S: Into<String>>(self, title: S) -> Self { |
| | 0 | 374 | | Self { |
| | 0 | 375 | | title: title.into(), |
| | 0 | 376 | | ..self |
| | 0 | 377 | | } |
| | 0 | 378 | | } |
| | | 379 | | |
| | 0 | 380 | | fn fullscreen(self, _fullscreen: bool) -> Self { |
| | 0 | 381 | | self |
| | 0 | 382 | | } |
| | | 383 | | |
| | 0 | 384 | | fn focused(self, _focused: bool) -> Self { |
| | 0 | 385 | | self |
| | 0 | 386 | | } |
| | | 387 | | |
| | 0 | 388 | | fn focusable(self, _focusable: bool) -> Self { |
| | 0 | 389 | | self |
| | 0 | 390 | | } |
| | | 391 | | |
| | 0 | 392 | | fn maximized(self, maximized: bool) -> Self { |
| | 0 | 393 | | Self { maximized, ..self } |
| | 0 | 394 | | } |
| | | 395 | | |
| | 0 | 396 | | fn visible(self, visible: bool) -> Self { |
| | 0 | 397 | | Self { visible, ..self } |
| | 0 | 398 | | } |
| | | 399 | | |
| | 0 | 400 | | fn transparent(self, _transparent: bool) -> Self { |
| | 0 | 401 | | self |
| | 0 | 402 | | } |
| | | 403 | | |
| | 0 | 404 | | fn decorations(self, decorations: bool) -> Self { |
| | 0 | 405 | | Self { |
| | 0 | 406 | | decorations, |
| | 0 | 407 | | ..self |
| | 0 | 408 | | } |
| | 0 | 409 | | } |
| | | 410 | | |
| | 0 | 411 | | fn always_on_bottom(self, _always_on_bottom: bool) -> Self { |
| | 0 | 412 | | self |
| | 0 | 413 | | } |
| | | 414 | | |
| | 0 | 415 | | fn always_on_top(self, _always_on_top: bool) -> Self { |
| | 0 | 416 | | self |
| | 0 | 417 | | } |
| | | 418 | | |
| | 0 | 419 | | fn visible_on_all_workspaces(self, _visible_on_all_workspaces: bool) -> Self { |
| | 0 | 420 | | self |
| | 0 | 421 | | } |
| | | 422 | | |
| | 0 | 423 | | fn content_protected(self, _protected: bool) -> Self { |
| | 0 | 424 | | self |
| | 0 | 425 | | } |
| | | 426 | | |
| | 21 | 427 | | fn icon(self, _icon: Icon) -> crate::Result<Self> { |
| | 21 | 428 | | Ok(self) |
| | 21 | 429 | | } |
| | | 430 | | |
| | 0 | 431 | | fn skip_taskbar(self, _skip: bool) -> Self { |
| | 0 | 432 | | self |
| | 0 | 433 | | } |
| | | 434 | | |
| | 0 | 435 | | fn background_color(self, _color: Color) -> Self { |
| | 0 | 436 | | self |
| | 0 | 437 | | } |
| | | 438 | | |
| | 0 | 439 | | fn shadow(self, _enable: bool) -> Self { |
| | 0 | 440 | | self |
| | 0 | 441 | | } |
| | | 442 | | |
| | | 443 | | #[cfg(target_os = "linux")] |
| | 0 | 444 | | fn transient_for(self, _parent: &impl gtk::glib::IsA<gtk::Window>) -> Self { |
| | 0 | 445 | | self |
| | 0 | 446 | | } |
| | | 447 | | |
| | | 448 | | #[cfg(target_os = "macos")] |
| | | 449 | | fn title_bar_style(self, _style: tauri_utils::TitleBarStyle) -> Self { |
| | | 450 | | self |
| | | 451 | | } |
| | | 452 | | |
| | | 453 | | #[cfg(target_os = "macos")] |
| | | 454 | | fn traffic_light_position<P: Into<rt::dpi::Position>>(self, _position: P) -> Self { |
| | | 455 | | self |
| | | 456 | | } |
| | | 457 | | |
| | | 458 | | #[cfg(target_os = "macos")] |
| | | 459 | | fn hidden_title(self, _hidden: bool) -> Self { |
| | | 460 | | self |
| | | 461 | | } |
| | | 462 | | |
| | | 463 | | #[cfg(target_os = "macos")] |
| | | 464 | | fn tabbing_identifier(self, _identifier: &str) -> Self { |
| | | 465 | | self |
| | | 466 | | } |
| | | 467 | | |
| | 0 | 468 | | fn theme(self, theme: Option<Theme>) -> Self { |
| | 0 | 469 | | Self { theme, ..self } |
| | 0 | 470 | | } |
| | | 471 | | |
| | 21 | 472 | | fn has_icon(&self) -> bool { |
| | 21 | 473 | | self.has_icon |
| | 21 | 474 | | } |
| | | 475 | | |
| | 21 | 476 | | fn get_theme(&self) -> Option<Theme> { |
| | 21 | 477 | | self.theme |
| | 21 | 478 | | } |
| | | 479 | | |
| | 0 | 480 | | fn window_classname<S: Into<String>>(self, _window_classname: S) -> Self { |
| | 0 | 481 | | self |
| | 0 | 482 | | } |
| | | 483 | | |
| | 0 | 484 | | fn no_redirection_bitmap(self, _enable: bool) -> Self { |
| | 0 | 485 | | self |
| | 0 | 486 | | } |
| | | 487 | | |
| | | 488 | | #[cfg(target_os = "android")] |
| | | 489 | | fn activity_name<S: Into<String>>(self, _class_name: S) -> Self { |
| | | 490 | | self |
| | | 491 | | } |
| | | 492 | | |
| | | 493 | | #[cfg(target_os = "android")] |
| | | 494 | | fn created_by_activity_name<S: Into<String>>(self, _class_name: S) -> Self { |
| | | 495 | | self |
| | | 496 | | } |
| | | 497 | | |
| | | 498 | | #[cfg(target_os = "ios")] |
| | | 499 | | fn requested_by_scene_identifier<S: Into<String>>(self, _identifier: S) -> Self { |
| | | 500 | | self |
| | | 501 | | } |
| | | 502 | | } |
| | | 503 | | |
| | | 504 | | impl<T: UserEvent> EventLoopProxy<T> for QtEventLoopProxy { |
| | 0 | 505 | | fn send_event(&self, event: T) -> Result<()> { |
| | 0 | 506 | | ops::runtime::proxy_send_event(event) |
| | 0 | 507 | | } |
| | | 508 | | } |
| | | 509 | | |
| | | 510 | | impl<T: UserEvent> rt::RuntimeHandle<T> for QtRuntimeHandle { |
| | | 511 | | type Runtime = QtRuntime; |
| | | 512 | | |
| | 0 | 513 | | fn create_proxy(&self) -> <Self::Runtime as rt::Runtime<T>>::EventLoopProxy { |
| | 0 | 514 | | QtEventLoopProxy |
| | 0 | 515 | | } |
| | | 516 | | |
| | 0 | 517 | | fn request_exit(&self, code: i32) -> Result<()> { |
| | 0 | 518 | | ops::runtime::request_exit(code) |
| | 0 | 519 | | } |
| | | 520 | | |
| | 21 | 521 | | fn create_window<F: Fn(RawWindow) + Send + 'static>( |
| | 21 | 522 | | &self, |
| | 21 | 523 | | pending: PendingWindow<T, Self::Runtime>, |
| | 21 | 524 | | _after_window_creation: Option<F>, |
| | 21 | 525 | | ) -> Result<DetachedWindow<T, Self::Runtime>> { |
| | 21 | 526 | | create_window_impl::<T>(pending) |
| | 21 | 527 | | } |
| | | 528 | | |
| | 0 | 529 | | fn create_webview( |
| | 0 | 530 | | &self, |
| | 0 | 531 | | window_id: WindowId, |
| | 0 | 532 | | pending: PendingWebview<T, Self::Runtime>, |
| | 0 | 533 | | ) -> Result<DetachedWebview<T, Self::Runtime>> { |
| | 0 | 534 | | build_webview::<T>(window_native_of(window_id)?, pending) |
| | 0 | 535 | | } |
| | | 536 | | |
| | 0 | 537 | | fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, _f: F) -> Result<()> { |
| | 0 | 538 | | Ok(()) |
| | 0 | 539 | | } |
| | | 540 | | |
| | 0 | 541 | | fn display_handle(&self) -> std::result::Result<DisplayHandle<'_>, HandleError> { |
| | 0 | 542 | | ops::runtime::display_handle() |
| | 0 | 543 | | } |
| | | 544 | | |
| | 0 | 545 | | fn primary_monitor(&self) -> Result<Option<Monitor>> { |
| | 0 | 546 | | ops::screen::primary_monitor() |
| | 0 | 547 | | } |
| | | 548 | | |
| | 0 | 549 | | fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> { |
| | 0 | 550 | | ops::screen::monitor_from_point(x, y) |
| | 0 | 551 | | } |
| | | 552 | | |
| | 0 | 553 | | fn available_monitors(&self) -> Result<Vec<Monitor>> { |
| | 0 | 554 | | ops::screen::available_monitors() |
| | 0 | 555 | | } |
| | | 556 | | |
| | 0 | 557 | | fn cursor_position(&self) -> Result<rt::dpi::PhysicalPosition<f64>> { |
| | 0 | 558 | | ops::screen::cursor_position() |
| | 0 | 559 | | } |
| | | 560 | | |
| | 0 | 561 | | fn set_theme(&self, _theme: Option<Theme>) {} |
| | | 562 | | |
| | 0 | 563 | | fn set_device_event_filter(&self, _filter: DeviceEventFilter) {} |
| | | 564 | | } |
| | | 565 | | |
| | | 566 | | impl<T: UserEvent> rt::Runtime<T> for QtRuntime { |
| | | 567 | | type WindowDispatcher = QtWindowDispatcher; |
| | | 568 | | type WebviewDispatcher = QtWebviewDispatcher; |
| | | 569 | | type Handle = QtRuntimeHandle; |
| | | 570 | | type EventLoopProxy = QtEventLoopProxy; |
| | | 571 | | |
| | 21 | 572 | | fn new(_args: RuntimeInitArgs) -> Result<Self> { |
| | | 573 | | // Qt requires the QApplication before any GUI object; schemes must be |
| | | 574 | | // registered in the same window of opportunity (see PLAN.md M1). |
| | 21 | 575 | | if qt::ffi::qt_init(0) != 0 { |
| | 0 | 576 | | return Err(Error::CreateWindow); |
| | 21 | 577 | | } |
| | 21 | 578 | | Ok(Self) |
| | 21 | 579 | | } |
| | | 580 | | |
| | 0 | 581 | | fn new_any_thread(args: RuntimeInitArgs) -> Result<Self> { |
| | | 582 | | // tauri core constructs the runtime through this entry point on Linux; |
| | | 583 | | // Qt still requires the QApplication on the process' main thread, which |
| | | 584 | | // is where tauri calls it from. |
| | 0 | 585 | | <Self as rt::Runtime<T>>::new(args) |
| | 0 | 586 | | } |
| | | 587 | | |
| | 21 | 588 | | fn create_proxy(&self) -> Self::EventLoopProxy { |
| | 21 | 589 | | QtEventLoopProxy |
| | 21 | 590 | | } |
| | | 591 | | |
| | 21 | 592 | | fn handle(&self) -> Self::Handle { |
| | 21 | 593 | | QtRuntimeHandle |
| | 21 | 594 | | } |
| | | 595 | | |
| | 0 | 596 | | fn create_window<F: Fn(RawWindow) + Send + 'static>( |
| | 0 | 597 | | &self, |
| | 0 | 598 | | pending: PendingWindow<T, Self>, |
| | 0 | 599 | | _after_window_creation: Option<F>, |
| | 0 | 600 | | ) -> Result<DetachedWindow<T, Self>> { |
| | 0 | 601 | | create_window_impl::<T>(pending) |
| | 0 | 602 | | } |
| | | 603 | | |
| | 0 | 604 | | fn create_webview( |
| | 0 | 605 | | &self, |
| | 0 | 606 | | window_id: WindowId, |
| | 0 | 607 | | pending: PendingWebview<T, Self>, |
| | 0 | 608 | | ) -> Result<DetachedWebview<T, Self>> { |
| | 0 | 609 | | build_webview::<T>(window_native_of(window_id)?, pending) |
| | 0 | 610 | | } |
| | | 611 | | |
| | 0 | 612 | | fn primary_monitor(&self) -> Option<Monitor> { |
| | 0 | 613 | | ops::screen::primary_monitor().ok().flatten() |
| | 0 | 614 | | } |
| | | 615 | | |
| | 0 | 616 | | fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> { |
| | 0 | 617 | | ops::screen::monitor_from_point(x, y).ok().flatten() |
| | 0 | 618 | | } |
| | | 619 | | |
| | 0 | 620 | | fn available_monitors(&self) -> Vec<Monitor> { |
| | 0 | 621 | | ops::screen::available_monitors().unwrap_or_default() |
| | 0 | 622 | | } |
| | | 623 | | |
| | 0 | 624 | | fn cursor_position(&self) -> Result<rt::dpi::PhysicalPosition<f64>> { |
| | 0 | 625 | | ops::screen::cursor_position() |
| | 0 | 626 | | } |
| | | 627 | | |
| | 0 | 628 | | fn set_theme(&self, _theme: Option<Theme>) {} |
| | | 629 | | |
| | 21 | 630 | | fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) {} |
| | | 631 | | |
| | 0 | 632 | | fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, _callback: F) { |
| | | 633 | | // Qt owns the thread inside exec(); the pump delivers user events. |
| | 0 | 634 | | ops::runtime::run_iteration(); |
| | 0 | 635 | | } |
| | | 636 | | |
| | 0 | 637 | | fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) -> i32 { |
| | 0 | 638 | | callback(RunEvent::Ready); |
| | | 639 | | // Qt owns the thread inside exec(), so user events are delivered from |
| | | 640 | | // the pump tick. The run callback is not required to be `Send`, hence |
| | | 641 | | // the main-thread-only sink wrapper. |
| | 0 | 642 | | let callback = std::rc::Rc::new(std::cell::RefCell::new(callback)); |
| | 0 | 643 | | let user_callback = std::rc::Rc::clone(&callback); |
| | 0 | 644 | | ops::runtime::set_event_sink(ops::runtime::main_thread_sink(move |event| { |
| | 0 | 645 | | if let Ok(event) = event.downcast::<T>() { |
| | 0 | 646 | | user_callback.borrow_mut()(RunEvent::UserEvent(*event)); |
| | 0 | 647 | | } |
| | 0 | 648 | | })); |
| | 0 | 649 | | ops::runtime::start_pump(); |
| | 0 | 650 | | let code = qt::ffi::qt_run(); |
| | 0 | 651 | | callback.borrow_mut()(RunEvent::Exit); |
| | 0 | 652 | | code |
| | 0 | 653 | | } |
| | | 654 | | |
| | 21 | 655 | | fn run<F: FnMut(RunEvent<T>) + 'static>(self, mut callback: F) { |
| | 21 | 656 | | crate::qtrace!("[tauri-runtime-qt] run: emitting Ready"); |
| | 21 | 657 | | callback(RunEvent::Ready); |
| | 21 | 658 | | let callback = std::rc::Rc::new(std::cell::RefCell::new(callback)); |
| | 21 | 659 | | let user_callback = std::rc::Rc::clone(&callback); |
| | 21 | 660 | | ops::runtime::set_event_sink(ops::runtime::main_thread_sink(move |event| { |
| | 0 | 661 | | if let Ok(event) = event.downcast::<T>() { |
| | 0 | 662 | | user_callback.borrow_mut()(RunEvent::UserEvent(*event)); |
| | 0 | 663 | | } |
| | 0 | 664 | | })); |
| | 21 | 665 | | ops::runtime::start_pump(); |
| | 21 | 666 | | crate::qtrace!("[tauri-runtime-qt] run: entering Qt event loop"); |
| | 21 | 667 | | qt::ffi::qt_run(); |
| | 21 | 668 | | callback.borrow_mut()(RunEvent::Exit); |
| | 21 | 669 | | } |
| | | 670 | | } |
| | | 671 | | |
| | | 672 | | impl<T: UserEvent> rt::WebviewDispatch<T> for QtWebviewDispatcher { |
| | | 673 | | type Runtime = QtRuntime; |
| | | 674 | | |
| | 21 | 675 | | fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> { |
| | | 676 | | // Qt runs everything on the GUI thread; callers reach us through the |
| | | 677 | | // event loop, so executing inline is correct for the MVP. |
| | 21 | 678 | | f(); |
| | 21 | 679 | | Ok(()) |
| | 21 | 680 | | } |
| | | 681 | | |
| | 21 | 682 | | fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) -> WebviewEventId { |
| | 21 | 683 | | register_webview_handler(self.native, f) |
| | 21 | 684 | | } |
| | | 685 | | |
| | 0 | 686 | | fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(&self, f: F) -> Result<()> { |
| | | 687 | | // M2: hand out the real QWebEngineView pointer once the C++ side can |
| | | 688 | | // vend it as an Any-safe value. |
| | 0 | 689 | | f(Box::new(self.native)); |
| | 0 | 690 | | Ok(()) |
| | 0 | 691 | | } |
| | | 692 | | |
| | | 693 | | #[cfg(any(debug_assertions, feature = "devtools"))] |
| | 0 | 694 | | fn open_devtools(&self) { |
| | 0 | 695 | | ops::webview::open_devtools(self.native); |
| | 0 | 696 | | } |
| | | 697 | | |
| | | 698 | | #[cfg(any(debug_assertions, feature = "devtools"))] |
| | 0 | 699 | | fn close_devtools(&self) { |
| | 0 | 700 | | ops::webview::close_devtools(self.native); |
| | 0 | 701 | | } |
| | | 702 | | |
| | | 703 | | #[cfg(any(debug_assertions, feature = "devtools"))] |
| | 0 | 704 | | fn is_devtools_open(&self) -> Result<bool> { |
| | 0 | 705 | | ops::webview::is_devtools_open(self.native) |
| | 0 | 706 | | } |
| | | 707 | | |
| | 0 | 708 | | fn url(&self) -> Result<String> { |
| | 0 | 709 | | ops::webview::url(self.native) |
| | 0 | 710 | | } |
| | | 711 | | |
| | 0 | 712 | | fn bounds(&self) -> Result<rt::dpi::Rect> { |
| | 0 | 713 | | ops::webview::bounds(self.native) |
| | 0 | 714 | | } |
| | | 715 | | |
| | 0 | 716 | | fn position(&self) -> Result<rt::dpi::PhysicalPosition<i32>> { |
| | 0 | 717 | | ops::webview::position(self.native) |
| | 0 | 718 | | } |
| | | 719 | | |
| | 0 | 720 | | fn size(&self) -> Result<rt::dpi::PhysicalSize<u32>> { |
| | 0 | 721 | | ops::webview::size(self.native) |
| | 0 | 722 | | } |
| | | 723 | | |
| | 0 | 724 | | fn navigate(&self, url: url::Url) -> Result<()> { |
| | 0 | 725 | | ops::webview::navigate(self.native, url.as_str()) |
| | 0 | 726 | | } |
| | | 727 | | |
| | 0 | 728 | | fn reload(&self) -> Result<()> { |
| | 0 | 729 | | ops::webview::reload(self.native) |
| | 0 | 730 | | } |
| | | 731 | | |
| | 0 | 732 | | fn print(&self) -> Result<()> { |
| | 0 | 733 | | ops::webview::print(self.native) |
| | 0 | 734 | | } |
| | | 735 | | |
| | 0 | 736 | | fn close(&self) -> Result<()> { |
| | 0 | 737 | | ops::webview::close(self.native) |
| | 0 | 738 | | } |
| | | 739 | | |
| | 0 | 740 | | fn set_bounds(&self, bounds: rt::dpi::Rect) -> Result<()> { |
| | 0 | 741 | | ops::webview::set_bounds(self.native, bounds) |
| | 0 | 742 | | } |
| | | 743 | | |
| | 0 | 744 | | fn set_size(&self, size: rt::dpi::Size) -> Result<()> { |
| | 0 | 745 | | ops::webview::set_size(self.native, size) |
| | 0 | 746 | | } |
| | | 747 | | |
| | 0 | 748 | | fn set_position(&self, position: rt::dpi::Position) -> Result<()> { |
| | 0 | 749 | | ops::webview::set_position(self.native, position) |
| | 0 | 750 | | } |
| | | 751 | | |
| | 0 | 752 | | fn set_focus(&self) -> Result<()> { |
| | 0 | 753 | | ops::webview::set_focus(self.native) |
| | 0 | 754 | | } |
| | | 755 | | |
| | 0 | 756 | | fn hide(&self) -> Result<()> { |
| | 0 | 757 | | ops::webview::hide(self.native) |
| | 0 | 758 | | } |
| | | 759 | | |
| | 0 | 760 | | fn show(&self) -> Result<()> { |
| | 0 | 761 | | ops::webview::show(self.native) |
| | 0 | 762 | | } |
| | | 763 | | |
| | 0 | 764 | | fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> { |
| | 0 | 765 | | ops::webview::eval(self.native, &script.into()) |
| | 0 | 766 | | } |
| | | 767 | | |
| | 0 | 768 | | fn eval_script_with_callback<S: Into<String>>( |
| | 0 | 769 | | &self, |
| | 0 | 770 | | script: S, |
| | 0 | 771 | | callback: impl Fn(String) + Send + 'static, |
| | 0 | 772 | | ) -> Result<()> { |
| | 0 | 773 | | ops::webview::eval_with_callback(self.native, &script.into(), callback) |
| | 0 | 774 | | } |
| | | 775 | | |
| | 0 | 776 | | fn reparent(&self, window_id: WindowId) -> Result<()> { |
| | 0 | 777 | | ops::webview::reparent(self.native, window_native_of(window_id)?) |
| | 0 | 778 | | } |
| | | 779 | | |
| | 84 | 780 | | fn cookies_for_url(&self, url: url::Url) -> Result<Vec<Cookie<'static>>> { |
| | 84 | 781 | | ops::cookies::cookies_for_url(self.native, &url) |
| | 84 | 782 | | } |
| | | 783 | | |
| | 147 | 784 | | fn cookies(&self) -> Result<Vec<Cookie<'static>>> { |
| | 147 | 785 | | ops::cookies::cookies(self.native) |
| | 147 | 786 | | } |
| | | 787 | | |
| | 42 | 788 | | fn set_cookie(&self, cookie: cookie::Cookie<'_>) -> Result<()> { |
| | 42 | 789 | | ops::cookies::set_cookie(self.native, cookie) |
| | 42 | 790 | | } |
| | | 791 | | |
| | 21 | 792 | | fn delete_cookie(&self, cookie: cookie::Cookie<'_>) -> Result<()> { |
| | 21 | 793 | | ops::cookies::delete_cookie(self.native, cookie) |
| | 21 | 794 | | } |
| | | 795 | | |
| | 0 | 796 | | fn set_auto_resize(&self, _auto_resize: bool) -> Result<()> { |
| | | 797 | | // The view fills the window through Qt's layout; nothing to toggle yet. |
| | 0 | 798 | | Ok(()) |
| | 0 | 799 | | } |
| | | 800 | | |
| | 0 | 801 | | fn set_zoom(&self, scale_factor: f64) -> Result<()> { |
| | 0 | 802 | | ops::webview::set_zoom(self.native, scale_factor) |
| | 0 | 803 | | } |
| | | 804 | | |
| | 0 | 805 | | fn set_background_color(&self, color: Option<Color>) -> Result<()> { |
| | 0 | 806 | | ops::webview::set_background_color(self.native, color) |
| | 0 | 807 | | } |
| | | 808 | | |
| | 21 | 809 | | fn clear_all_browsing_data(&self) -> Result<()> { |
| | 21 | 810 | | ops::cookies::clear_all_browsing_data(self.native) |
| | 21 | 811 | | } |
| | | 812 | | } |
| | | 813 | | |
| | | 814 | | impl<T: UserEvent> rt::WindowDispatch<T> for QtWindowDispatcher { |
| | | 815 | | type Runtime = QtRuntime; |
| | | 816 | | type WindowBuilder = QtWindowBuilder; |
| | | 817 | | |
| | 42 | 818 | | fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> { |
| | 42 | 819 | | f(); |
| | 42 | 820 | | Ok(()) |
| | 42 | 821 | | } |
| | | 822 | | |
| | 21 | 823 | | fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> WindowEventId { |
| | 21 | 824 | | register_window_handler(self.native, f) |
| | 21 | 825 | | } |
| | | 826 | | |
| | 42 | 827 | | fn scale_factor(&self) -> Result<f64> { |
| | 42 | 828 | | ops::window::scale_factor(self.native) |
| | 42 | 829 | | } |
| | | 830 | | |
| | 42 | 831 | | fn inner_position(&self) -> Result<rt::dpi::PhysicalPosition<i32>> { |
| | 42 | 832 | | ops::window::inner_position(self.native) |
| | 42 | 833 | | } |
| | | 834 | | |
| | 42 | 835 | | fn outer_position(&self) -> Result<rt::dpi::PhysicalPosition<i32>> { |
| | 42 | 836 | | ops::window::outer_position(self.native) |
| | 42 | 837 | | } |
| | | 838 | | |
| | 42 | 839 | | fn inner_size(&self) -> Result<rt::dpi::PhysicalSize<u32>> { |
| | 42 | 840 | | ops::window::inner_size(self.native) |
| | 42 | 841 | | } |
| | | 842 | | |
| | 42 | 843 | | fn outer_size(&self) -> Result<rt::dpi::PhysicalSize<u32>> { |
| | 42 | 844 | | ops::window::outer_size(self.native) |
| | 42 | 845 | | } |
| | | 846 | | |
| | 42 | 847 | | fn is_fullscreen(&self) -> Result<bool> { |
| | 42 | 848 | | ops::window::is_fullscreen(self.native) |
| | 42 | 849 | | } |
| | | 850 | | |
| | 42 | 851 | | fn is_minimized(&self) -> Result<bool> { |
| | 42 | 852 | | ops::window::is_minimized(self.native) |
| | 42 | 853 | | } |
| | | 854 | | |
| | 42 | 855 | | fn is_maximized(&self) -> Result<bool> { |
| | 42 | 856 | | ops::window::is_maximized(self.native) |
| | 42 | 857 | | } |
| | | 858 | | |
| | 42 | 859 | | fn is_focused(&self) -> Result<bool> { |
| | 42 | 860 | | ops::window::is_focused(self.native) |
| | 42 | 861 | | } |
| | | 862 | | |
| | 42 | 863 | | fn is_decorated(&self) -> Result<bool> { |
| | 42 | 864 | | ops::window::is_decorated(self.native) |
| | 42 | 865 | | } |
| | | 866 | | |
| | 42 | 867 | | fn is_resizable(&self) -> Result<bool> { |
| | 42 | 868 | | ops::window::is_resizable(self.native) |
| | 42 | 869 | | } |
| | | 870 | | |
| | 42 | 871 | | fn is_visible(&self) -> Result<bool> { |
| | 42 | 872 | | ops::window::is_visible(self.native) |
| | 42 | 873 | | } |
| | | 874 | | |
| | 0 | 875 | | fn is_enabled(&self) -> Result<bool> { |
| | 0 | 876 | | ops::window::is_enabled(self.native) |
| | 0 | 877 | | } |
| | | 878 | | |
| | 42 | 879 | | fn is_always_on_top(&self) -> Result<bool> { |
| | 42 | 880 | | ops::window::is_always_on_top(self.native) |
| | 42 | 881 | | } |
| | | 882 | | |
| | 42 | 883 | | fn title(&self) -> Result<String> { |
| | 42 | 884 | | ops::window::title(self.native) |
| | 42 | 885 | | } |
| | | 886 | | |
| | 0 | 887 | | fn theme(&self) -> Result<Theme> { |
| | 0 | 888 | | ops::window::theme(self.native) |
| | 0 | 889 | | } |
| | | 890 | | |
| | 0 | 891 | | fn center(&self) -> Result<()> { |
| | 0 | 892 | | ops::window::center(self.native) |
| | 0 | 893 | | } |
| | | 894 | | |
| | 0 | 895 | | fn minimize(&self) -> Result<()> { |
| | 0 | 896 | | ops::window::minimize(self.native) |
| | 0 | 897 | | } |
| | | 898 | | |
| | 0 | 899 | | fn unminimize(&self) -> Result<()> { |
| | 0 | 900 | | ops::window::unminimize(self.native) |
| | 0 | 901 | | } |
| | | 902 | | |
| | 0 | 903 | | fn show(&self) -> Result<()> { |
| | 0 | 904 | | ops::window::show(self.native) |
| | 0 | 905 | | } |
| | | 906 | | |
| | 0 | 907 | | fn hide(&self) -> Result<()> { |
| | 0 | 908 | | ops::window::hide(self.native) |
| | 0 | 909 | | } |
| | | 910 | | |
| | 0 | 911 | | fn close(&self) -> Result<()> { |
| | 0 | 912 | | ops::window::close(self.native) |
| | 0 | 913 | | } |
| | | 914 | | |
| | 0 | 915 | | fn destroy(&self) -> Result<()> { |
| | 0 | 916 | | ops::window::destroy(self.native) |
| | 0 | 917 | | } |
| | | 918 | | |
| | 0 | 919 | | fn set_focus(&self) -> Result<()> { |
| | 0 | 920 | | ops::window::set_focus(self.native) |
| | 0 | 921 | | } |
| | | 922 | | |
| | 0 | 923 | | fn start_dragging(&self) -> Result<()> { |
| | 0 | 924 | | ops::window::start_dragging(self.native) |
| | 0 | 925 | | } |
| | | 926 | | |
| | 42 | 927 | | fn is_maximizable(&self) -> Result<bool> { |
| | | 928 | | // The trait documents this as unsupported on Linux (no native maximize |
| | | 929 | | // button state), so this reports the capability, not a measured state. |
| | 42 | 930 | | Ok(true) |
| | 42 | 931 | | } |
| | | 932 | | |
| | 42 | 933 | | fn is_minimizable(&self) -> Result<bool> { |
| | | 934 | | // Linux: unsupported by the trait's own documentation. |
| | 42 | 935 | | Ok(true) |
| | 42 | 936 | | } |
| | | 937 | | |
| | 42 | 938 | | fn is_closable(&self) -> Result<bool> { |
| | | 939 | | // Linux: unsupported by the trait's own documentation. |
| | 42 | 940 | | Ok(true) |
| | 42 | 941 | | } |
| | | 942 | | |
| | 21 | 943 | | fn current_monitor(&self) -> Result<Option<Monitor>> { |
| | 21 | 944 | | ops::screen::monitor_for_window(self.native) |
| | 21 | 945 | | } |
| | | 946 | | |
| | 21 | 947 | | fn primary_monitor(&self) -> Result<Option<Monitor>> { |
| | 21 | 948 | | ops::screen::primary_monitor() |
| | 21 | 949 | | } |
| | | 950 | | |
| | 0 | 951 | | fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> { |
| | 0 | 952 | | ops::screen::monitor_from_point(x, y) |
| | 0 | 953 | | } |
| | | 954 | | |
| | 21 | 955 | | fn available_monitors(&self) -> Result<Vec<Monitor>> { |
| | 21 | 956 | | ops::screen::available_monitors() |
| | 21 | 957 | | } |
| | | 958 | | |
| | | 959 | | #[cfg(any( |
| | | 960 | | target_os = "linux", |
| | | 961 | | target_os = "dragonfly", |
| | | 962 | | target_os = "freebsd", |
| | | 963 | | target_os = "netbsd", |
| | | 964 | | target_os = "openbsd" |
| | | 965 | | ))] |
| | 0 | 966 | | fn gtk_window(&self) -> Result<gtk::ApplicationWindow> { |
| | | 967 | | // No GTK window exists in a Qt runtime; use `window_handle`. |
| | 0 | 968 | | Err(Error::CreateWindow) |
| | 0 | 969 | | } |
| | | 970 | | |
| | | 971 | | #[cfg(any( |
| | | 972 | | target_os = "linux", |
| | | 973 | | target_os = "dragonfly", |
| | | 974 | | target_os = "freebsd", |
| | | 975 | | target_os = "netbsd", |
| | | 976 | | target_os = "openbsd" |
| | | 977 | | ))] |
| | 0 | 978 | | fn default_vbox(&self) -> Result<gtk::Box> { |
| | 0 | 979 | | Err(Error::CreateWindow) |
| | 0 | 980 | | } |
| | | 981 | | |
| | 0 | 982 | | fn window_handle(&self) -> std::result::Result<WindowHandle<'_>, HandleError> { |
| | 0 | 983 | | ops::runtime::window_handle(self.native) |
| | 0 | 984 | | } |
| | | 985 | | |
| | 0 | 986 | | fn request_user_attention(&self, _request_type: Option<UserAttentionType>) -> Result<()> { |
| | 0 | 987 | | Err(ops::unimplemented("WindowDispatch::request_user_attention")) |
| | 0 | 988 | | } |
| | | 989 | | |
| | 0 | 990 | | fn create_window<F: Fn(RawWindow) + Send + 'static>( |
| | 0 | 991 | | &mut self, |
| | 0 | 992 | | pending: PendingWindow<T, Self::Runtime>, |
| | 0 | 993 | | _after_window_creation: Option<F>, |
| | 0 | 994 | | ) -> Result<DetachedWindow<T, Self::Runtime>> { |
| | 0 | 995 | | create_window_impl::<T>(pending) |
| | 0 | 996 | | } |
| | | 997 | | |
| | 0 | 998 | | fn create_webview( |
| | 0 | 999 | | &mut self, |
| | 0 | 1000 | | pending: PendingWebview<T, Self::Runtime>, |
| | 0 | 1001 | | ) -> Result<DetachedWebview<T, Self::Runtime>> { |
| | 0 | 1002 | | build_webview::<T>(self.native, pending) |
| | 0 | 1003 | | } |
| | | 1004 | | |
| | 0 | 1005 | | fn set_resizable(&self, resizable: bool) -> Result<()> { |
| | 0 | 1006 | | ops::window::set_resizable(self.native, resizable) |
| | 0 | 1007 | | } |
| | | 1008 | | |
| | 0 | 1009 | | fn set_enabled(&self, enabled: bool) -> Result<()> { |
| | 0 | 1010 | | ops::window::set_enabled(self.native, enabled) |
| | 0 | 1011 | | } |
| | | 1012 | | |
| | 0 | 1013 | | fn set_maximizable(&self, _maximizable: bool) -> Result<()> { |
| | | 1014 | | // Linux: unsupported by the trait's own documentation. |
| | 0 | 1015 | | Ok(()) |
| | 0 | 1016 | | } |
| | | 1017 | | |
| | 0 | 1018 | | fn set_minimizable(&self, _minimizable: bool) -> Result<()> { |
| | | 1019 | | // Linux: unsupported by the trait's own documentation. |
| | 0 | 1020 | | Ok(()) |
| | 0 | 1021 | | } |
| | | 1022 | | |
| | 0 | 1023 | | fn set_closable(&self, _closable: bool) -> Result<()> { |
| | | 1024 | | // Linux: unsupported by the trait's own documentation. |
| | 0 | 1025 | | Ok(()) |
| | 0 | 1026 | | } |
| | | 1027 | | |
| | 21 | 1028 | | fn set_title<S: Into<String>>(&self, title: S) -> Result<()> { |
| | 21 | 1029 | | ops::window::set_title(self.native, &title.into()) |
| | 21 | 1030 | | } |
| | | 1031 | | |
| | 0 | 1032 | | fn maximize(&self) -> Result<()> { |
| | 0 | 1033 | | ops::window::set_maximized(self.native, true) |
| | 0 | 1034 | | } |
| | | 1035 | | |
| | 0 | 1036 | | fn unmaximize(&self) -> Result<()> { |
| | 0 | 1037 | | ops::window::set_maximized(self.native, false) |
| | 0 | 1038 | | } |
| | | 1039 | | |
| | 0 | 1040 | | fn set_decorations(&self, decorations: bool) -> Result<()> { |
| | 0 | 1041 | | ops::window::set_decorations(self.native, decorations) |
| | 0 | 1042 | | } |
| | | 1043 | | |
| | 0 | 1044 | | fn set_shadow(&self, _enable: bool) -> Result<()> { |
| | | 1045 | | // Linux: unsupported by the trait's own documentation. |
| | 0 | 1046 | | Ok(()) |
| | 0 | 1047 | | } |
| | | 1048 | | |
| | 0 | 1049 | | fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> { |
| | 0 | 1050 | | ops::window::set_always_on_bottom(self.native, always_on_bottom) |
| | 0 | 1051 | | } |
| | | 1052 | | |
| | 0 | 1053 | | fn set_always_on_top(&self, always_on_top: bool) -> Result<()> { |
| | 0 | 1054 | | ops::window::set_always_on_top(self.native, always_on_top) |
| | 0 | 1055 | | } |
| | | 1056 | | |
| | 0 | 1057 | | fn set_visible_on_all_workspaces(&self, _visible_on_all_workspaces: bool) -> Result<()> { |
| | 0 | 1058 | | Err(ops::unimplemented( |
| | 0 | 1059 | | "WindowDispatch::set_visible_on_all_workspaces", |
| | 0 | 1060 | | )) |
| | 0 | 1061 | | } |
| | | 1062 | | |
| | 0 | 1063 | | fn set_background_color(&self, color: Option<Color>) -> Result<()> { |
| | 0 | 1064 | | ops::window::set_background_color(self.native, color) |
| | 0 | 1065 | | } |
| | | 1066 | | |
| | 0 | 1067 | | fn set_content_protected(&self, _protected: bool) -> Result<()> { |
| | 0 | 1068 | | Err(ops::unimplemented("WindowDispatch::set_content_protected")) |
| | 0 | 1069 | | } |
| | | 1070 | | |
| | 0 | 1071 | | fn set_size(&self, size: rt::dpi::Size) -> Result<()> { |
| | 0 | 1072 | | let (w, h) = qtcore::window::size_to_i32(size); |
| | 0 | 1073 | | ops::window::set_size(self.native, w, h) |
| | 0 | 1074 | | } |
| | | 1075 | | |
| | 0 | 1076 | | fn set_min_size(&self, size: Option<rt::dpi::Size>) -> Result<()> { |
| | 0 | 1077 | | ops::window::set_min_size(self.native, size) |
| | 0 | 1078 | | } |
| | | 1079 | | |
| | 0 | 1080 | | fn set_max_size(&self, size: Option<rt::dpi::Size>) -> Result<()> { |
| | 0 | 1081 | | ops::window::set_max_size(self.native, size) |
| | 0 | 1082 | | } |
| | | 1083 | | |
| | 0 | 1084 | | fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()> { |
| | 0 | 1085 | | ops::window::set_size_constraints(self.native, constraints) |
| | 0 | 1086 | | } |
| | | 1087 | | |
| | 0 | 1088 | | fn set_position(&self, position: rt::dpi::Position) -> Result<()> { |
| | 0 | 1089 | | ops::window::set_position(self.native, position) |
| | 0 | 1090 | | } |
| | | 1091 | | |
| | 42 | 1092 | | fn set_fullscreen(&self, fullscreen: bool) -> Result<()> { |
| | 42 | 1093 | | ops::window::set_fullscreen(self.native, fullscreen) |
| | 42 | 1094 | | } |
| | | 1095 | | |
| | | 1096 | | /// Only exists on `tauri-runtime` >= 2.12 (see the `tauri-runtime-2-12` |
| | | 1097 | | /// feature); older versions have no such slot in the trait. |
| | | 1098 | | #[cfg(feature = "tauri-runtime-2-12")] |
| | 0 | 1099 | | fn set_fullscreen_on_monitor(&self, _position: rt::dpi::PhysicalPosition<f64>) -> Result<()> { |
| | 0 | 1100 | | Err(ops::unimplemented( |
| | 0 | 1101 | | "WindowDispatch::set_fullscreen_on_monitor", |
| | 0 | 1102 | | )) |
| | 0 | 1103 | | } |
| | | 1104 | | |
| | 0 | 1105 | | fn set_focusable(&self, _focusable: bool) -> Result<()> { |
| | 0 | 1106 | | Err(ops::unimplemented("WindowDispatch::set_focusable")) |
| | 0 | 1107 | | } |
| | | 1108 | | |
| | 0 | 1109 | | fn set_icon(&self, icon: Icon) -> Result<()> { |
| | 0 | 1110 | | ops::window::set_icon(self.native, icon) |
| | 0 | 1111 | | } |
| | | 1112 | | |
| | 0 | 1113 | | fn set_skip_taskbar(&self, _skip: bool) -> Result<()> { |
| | 0 | 1114 | | Err(ops::unimplemented("WindowDispatch::set_skip_taskbar")) |
| | 0 | 1115 | | } |
| | | 1116 | | |
| | 0 | 1117 | | fn set_cursor_grab(&self, _grab: bool) -> Result<()> { |
| | 0 | 1118 | | Err(ops::unimplemented("WindowDispatch::set_cursor_grab")) |
| | 0 | 1119 | | } |
| | | 1120 | | |
| | 0 | 1121 | | fn set_cursor_visible(&self, _visible: bool) -> Result<()> { |
| | 0 | 1122 | | Err(ops::unimplemented("WindowDispatch::set_cursor_visible")) |
| | 0 | 1123 | | } |
| | | 1124 | | |
| | 0 | 1125 | | fn set_cursor_icon(&self, _icon: CursorIcon) -> Result<()> { |
| | 0 | 1126 | | Err(ops::unimplemented("WindowDispatch::set_cursor_icon")) |
| | 0 | 1127 | | } |
| | | 1128 | | |
| | 0 | 1129 | | fn set_cursor_position<Pos: Into<rt::dpi::Position>>(&self, _position: Pos) -> Result<()> { |
| | 0 | 1130 | | Err(ops::unimplemented("WindowDispatch::set_cursor_position")) |
| | 0 | 1131 | | } |
| | | 1132 | | |
| | 0 | 1133 | | fn set_ignore_cursor_events(&self, _ignore: bool) -> Result<()> { |
| | 0 | 1134 | | Err(ops::unimplemented( |
| | 0 | 1135 | | "WindowDispatch::set_ignore_cursor_events", |
| | 0 | 1136 | | )) |
| | 0 | 1137 | | } |
| | | 1138 | | |
| | 0 | 1139 | | fn start_resize_dragging(&self, direction: ResizeDirection) -> Result<()> { |
| | 0 | 1140 | | ops::window::start_resize_dragging( |
| | 0 | 1141 | | self.native, |
| | 0 | 1142 | | qtcore::window::resize_direction_code(direction), |
| | | 1143 | | ) |
| | 0 | 1144 | | } |
| | | 1145 | | |
| | 0 | 1146 | | fn set_badge_count( |
| | 0 | 1147 | | &self, |
| | 0 | 1148 | | _count: Option<i64>, |
| | 0 | 1149 | | _desktop_filename: Option<String>, |
| | 0 | 1150 | | ) -> Result<()> { |
| | | 1151 | | // Linux: unsupported by the trait's own documentation. |
| | 0 | 1152 | | Ok(()) |
| | 0 | 1153 | | } |
| | | 1154 | | |
| | 0 | 1155 | | fn set_badge_label(&self, _label: Option<String>) -> Result<()> { |
| | | 1156 | | // macOS-only per the trait documentation. |
| | 0 | 1157 | | Ok(()) |
| | 0 | 1158 | | } |
| | | 1159 | | |
| | 0 | 1160 | | fn set_overlay_icon(&self, _icon: Option<Icon>) -> Result<()> { |
| | | 1161 | | // Windows-only per the trait documentation. |
| | 0 | 1162 | | Ok(()) |
| | 0 | 1163 | | } |
| | | 1164 | | |
| | 0 | 1165 | | fn set_progress_bar(&self, _progress_state: ProgressBarState) -> Result<()> { |
| | | 1166 | | // Needs libunity on Linux; unsupported by the trait documentation. |
| | 0 | 1167 | | Ok(()) |
| | 0 | 1168 | | } |
| | | 1169 | | |
| | 0 | 1170 | | fn set_title_bar_style(&self, _style: tauri_utils::TitleBarStyle) -> Result<()> { |
| | | 1171 | | // macOS-only per the trait documentation. |
| | 0 | 1172 | | Ok(()) |
| | 0 | 1173 | | } |
| | | 1174 | | |
| | 0 | 1175 | | fn set_traffic_light_position(&self, _position: rt::dpi::Position) -> Result<()> { |
| | | 1176 | | // macOS-only per the trait documentation. |
| | 0 | 1177 | | Ok(()) |
| | 0 | 1178 | | } |
| | | 1179 | | |
| | 0 | 1180 | | fn set_theme(&self, _theme: Option<Theme>) -> Result<()> { |
| | | 1181 | | // Linux: app-wide; handled by `Runtime::set_theme`. |
| | 0 | 1182 | | Ok(()) |
| | 0 | 1183 | | } |
| | | 1184 | | } |
| | | 1185 | | |
| | | 1186 | | /// Placeholder for webview platform handle exposure (`with_webview`). |
| | | 1187 | | #[derive(Debug)] |
| | | 1188 | | pub struct QtWebviewHandle; |