< Summary

Information
Class: /home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/ops/window.rs
Assembly: Default
File(s): /home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/ops/window.rs
Line coverage
30%
Covered lines: 85
Uncovered lines: 194
Coverable lines: 279
Total lines: 411
Line coverage: 30.4%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

File(s)

/home/runner/work/TauriQt/TauriQt/tauri-runtime-qt/src/ops/window.rs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik
 2// SPDX-License-Identifier: MIT OR Apache-2.0
 3
 4//! Window operations (owned by the window worker).
 5//!
 6//! Everything here talks to `bridge::window::ffi` (`qt_window.cpp`). Functions
 7//! return `Err` when Qt cannot answer truthfully on this platform: an id that
 8//! is not in the C++ registry is `Error::WindowNotFound`, and an operation the
 9//! platform cannot honour (an unreportable colour scheme, a refused drag) is a
 10//! message-carrying error instead of a fabricated value.
 11//!
 12//! Window *positions* are deliberately not in that set. A Wayland compositor
 13//! owns placement and honours neither `move()` nor a stacking hint, but Qt keeps
 14//! the position an app asked for and reports it back, and callers treat the
 15//! position as always available: `tauri-plugin-window-state`, for one, aborts
 16//! its startup restore — including the `show()` that maps a hidden window —
 17//! on the first `Err` it gets from `outer_position`. So `inner_position`,
 18//! `outer_position` and `set_position` answer with Qt's own bookkeeping on every
 19//! platform, exactly like tao does, and report the app's *requested* position on
 20//! Wayland rather than failing. See `docs/wayland-ci.md`.
 21//!
 22//! Qt reports geometry in device-independent ("logical") pixels; tauri asks
 23//! for physical ones, so every geometry value crosses the boundary through the
 24//! window's own device pixel ratio (`qt_window_scale_factor`). The conversions
 25//! themselves (and Qt's 24-bit hint sentinels) live in
 26//! `tauri-runtime-qt-core::window`.
 27
 28use tauri_runtime::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
 29use tauri_runtime::window::WindowSizeConstraints;
 30use tauri_runtime::{Error, Icon, Result};
 31use tauri_runtime_qt_core as qtcore;
 32use tauri_utils::config::Color;
 33use tauri_utils::Theme;
 34
 35use crate::bridge::window::ffi;
 36
 37// ---- helpers ----
 38
 39/// Fails with `Error::WindowNotFound` for an id that is not in the C++ window
 40/// registry, instead of asking Qt about a window that is not there.
 52041fn require_window(native: u64) -> Result<()> {
 52042    if ffi::qt_window_alive(native) {
 52043        Ok(())
 44    } else {
 045        Err(Error::WindowNotFound)
 46    }
 52047}
 48
 49/// A truthful error for an operation Qt cannot honour here.
 50///
 51/// `tauri_runtime::Error` has no dedicated "unsupported" variant, so the
 52/// message-carrying variant is used (as `ops::unimplemented` does for the
 53/// operations that do not exist at all).
 054fn unavailable(what: &str, why: &str) -> Error {
 055    Error::CreateWebview(format!("tauri-runtime-qt: {what} is unavailable: {why}").into())
 056}
 57
 58/// Name of the Qt platform plugin in use (e.g. `"xcb"`, `"wayland"`).
 059fn platform() -> String {
 060    ffi::qt_window_platform()
 061}
 62
 63/// The window's device pixel ratio (physical pixels per logical pixel), with
 64/// Qt's "not on a screen yet" reading normalised to 1.
 16065fn device_pixel_ratio(native: u64) -> f64 {
 16066    qtcore::window::device_pixel_ratio(ffi::qt_window_scale_factor(native))
 16067}
 68
 69/// Qt logical pixels → tauri physical pixels.
 8070fn physical_size(native: u64, size: ffi::QtWindowSize) -> PhysicalSize<u32> {
 8071    qtcore::window::to_physical_size(device_pixel_ratio(native), size.width, size.height)
 8072}
 73
 74/// Qt logical pixels → tauri physical pixels.
 8075fn physical_position(native: u64, point: ffi::QtWindowPoint) -> PhysicalPosition<i32> {
 8076    qtcore::window::to_physical_position(device_pixel_ratio(native), point.x, point.y)
 8077}
 78
 79// ---- lifecycle ----
 80
 081pub(crate) fn show(native: u64) -> Result<()> {
 082    ffi::qt_window_show(native);
 083    Ok(())
 084}
 85
 086pub(crate) fn hide(native: u64) -> Result<()> {
 087    ffi::qt_window_hide(native);
 088    Ok(())
 089}
 90
 091pub(crate) fn close(native: u64) -> Result<()> {
 092    ffi::qt_window_close(native);
 093    Ok(())
 094}
 95
 096pub(crate) fn destroy(native: u64) -> Result<()> {
 097    ffi::qt_window_close(native);
 098    Ok(())
 099}
 100
 101// ---- getters ----
 102
 40103pub(crate) fn scale_factor(native: u64) -> Result<f64> {
 40104    Ok(ffi::qt_window_scale_factor(native))
 40105}
 106
 40107pub(crate) fn inner_size(native: u64) -> Result<PhysicalSize<u32>> {
 40108    require_window(native)?;
 40109    Ok(physical_size(native, ffi::qt_window_inner_size(native)))
 40110}
 111
 40112pub(crate) fn outer_size(native: u64) -> Result<PhysicalSize<u32>> {
 40113    require_window(native)?;
 40114    let size = ffi::qt_window_outer_size(native);
 40115    if size.width <= 0.0 || size.height <= 0.0 {
 116        // An empty frame rectangle means the platform does not report the
 117        // frame; a `0x0` outer size would be a fabricated value.
 0118        return Err(unavailable(
 0119            "the window frame size",
 0120            "the platform does not report the window frame geometry",
 0121        ));
 40122    }
 40123    Ok(physical_size(native, size))
 40124}
 125
 126/// Position of the window's client area.
 127///
 128/// On Wayland this is Qt's own record of where the window was asked to be (the
 129/// compositor decides where it really is and does not report it), which is what
 130/// tao answers there too — see the module docs.
 40131pub(crate) fn inner_position(native: u64) -> Result<PhysicalPosition<i32>> {
 40132    require_window(native)?;
 40133    Ok(physical_position(
 40134        native,
 40135        ffi::qt_window_inner_position(native),
 40136    ))
 40137}
 138
 139/// Position of the window's frame (its top-left corner on the desktop).
 140///
 141/// Same Wayland caveat as [`inner_position`]: the value is the requested
 142/// position, not a compositor-reported one.
 40143pub(crate) fn outer_position(native: u64) -> Result<PhysicalPosition<i32>> {
 40144    require_window(native)?;
 40145    Ok(physical_position(
 40146        native,
 40147        ffi::qt_window_outer_position(native),
 40148    ))
 40149}
 150
 40151pub(crate) fn title(native: u64) -> Result<String> {
 40152    require_window(native)?;
 40153    Ok(ffi::qt_window_title(native))
 40154}
 155
 0156pub(crate) fn theme(native: u64) -> Result<Theme> {
 0157    require_window(native)?;
 0158    let code = ffi::qt_window_theme(native);
 0159    qtcore::window::theme_from_code(code).ok_or_else(|| {
 0160        unavailable(
 0161            "the window theme",
 0162            &format!("Qt reports the colour scheme as unknown (code {code})"),
 163        )
 0164    })
 0165}
 166
 40167pub(crate) fn is_fullscreen(native: u64) -> Result<bool> {
 40168    require_window(native)?;
 40169    Ok(ffi::qt_window_is_fullscreen(native))
 40170}
 171
 40172pub(crate) fn is_minimized(native: u64) -> Result<bool> {
 40173    require_window(native)?;
 40174    Ok(ffi::qt_window_is_minimized(native))
 40175}
 176
 40177pub(crate) fn is_maximized(native: u64) -> Result<bool> {
 40178    require_window(native)?;
 40179    Ok(ffi::qt_window_is_maximized(native))
 40180}
 181
 40182pub(crate) fn is_focused(native: u64) -> Result<bool> {
 40183    require_window(native)?;
 40184    Ok(ffi::qt_window_is_focused(native))
 40185}
 186
 40187pub(crate) fn is_decorated(native: u64) -> Result<bool> {
 40188    require_window(native)?;
 40189    Ok(ffi::qt_window_is_decorated(native))
 40190}
 191
 40192pub(crate) fn is_resizable(native: u64) -> Result<bool> {
 40193    require_window(native)?;
 40194    Ok(ffi::qt_window_is_resizable(native))
 40195}
 196
 40197pub(crate) fn is_visible(native: u64) -> Result<bool> {
 40198    Ok(ffi::qt_window_is_visible(native))
 40199}
 200
 0201pub(crate) fn is_enabled(native: u64) -> Result<bool> {
 0202    require_window(native)?;
 0203    Ok(ffi::qt_window_is_enabled(native))
 0204}
 205
 40206pub(crate) fn is_always_on_top(native: u64) -> Result<bool> {
 40207    require_window(native)?;
 40208    Ok(ffi::qt_window_is_always_on_top(native))
 40209}
 210
 0211pub(crate) fn is_maximizable(native: u64) -> Result<bool> {
 0212    require_window(native)?;
 0213    Ok(ffi::qt_window_is_maximizable(native))
 0214}
 215
 0216pub(crate) fn is_minimizable(native: u64) -> Result<bool> {
 0217    require_window(native)?;
 0218    Ok(ffi::qt_window_is_minimizable(native))
 0219}
 220
 0221pub(crate) fn is_closable(native: u64) -> Result<bool> {
 0222    require_window(native)?;
 0223    Ok(ffi::qt_window_is_closable(native))
 0224}
 225
 226// ---- setters ----
 227
 20228pub(crate) fn set_title(native: u64, title: &str) -> Result<()> {
 20229    ffi::qt_window_set_title(native, title);
 20230    Ok(())
 20231}
 232
 0233pub(crate) fn set_size(native: u64, width: i32, height: i32) -> Result<()> {
 0234    ffi::qt_window_set_size(native, width, height);
 0235    Ok(())
 0236}
 237
 0238pub(crate) fn center(native: u64) -> Result<()> {
 0239    ffi::qt_window_center(native);
 0240    Ok(())
 0241}
 242
 0243pub(crate) fn set_decorations(native: u64, decorations: bool) -> Result<()> {
 0244    ffi::qt_window_set_decorations(native, decorations);
 0245    Ok(())
 0246}
 247
 0248pub(crate) fn set_maximized(native: u64, maximized: bool) -> Result<()> {
 0249    ffi::qt_window_set_maximized(native, maximized);
 0250    Ok(())
 0251}
 252
 0253pub(crate) fn minimize(native: u64) -> Result<()> {
 0254    require_window(native)?;
 0255    ffi::qt_window_minimize(native);
 0256    Ok(())
 0257}
 258
 0259pub(crate) fn unminimize(native: u64) -> Result<()> {
 0260    require_window(native)?;
 0261    ffi::qt_window_unminimize(native);
 0262    Ok(())
 0263}
 264
 40265pub(crate) fn set_fullscreen(native: u64, fullscreen: bool) -> Result<()> {
 40266    require_window(native)?;
 40267    ffi::qt_window_set_fullscreen(native, fullscreen);
 40268    Ok(())
 40269}
 270
 0271pub(crate) fn set_min_size(native: u64, size: Option<Size>) -> Result<()> {
 0272    require_window(native)?;
 0273    let (width, height) = match size {
 0274        Some(size) => qtcore::window::qt_size(device_pixel_ratio(native), size),
 275        // Qt's "no minimum size": QSize(0, 0).
 0276        None => qtcore::window::NO_MINIMUM,
 277    };
 0278    ffi::qt_window_set_min_size(native, width, height);
 0279    Ok(())
 0280}
 281
 0282pub(crate) fn set_max_size(native: u64, size: Option<Size>) -> Result<()> {
 0283    require_window(native)?;
 0284    let (width, height) = match size {
 0285        Some(size) => qtcore::window::qt_size(device_pixel_ratio(native), size),
 286        // Qt's "no maximum size": QWIDGETSIZE_MAX.
 0287        None => qtcore::window::NO_MAXIMUM,
 288    };
 0289    ffi::qt_window_set_max_size(native, width, height);
 0290    Ok(())
 0291}
 292
 0293pub(crate) fn set_size_constraints(native: u64, constraints: WindowSizeConstraints) -> Result<()> {
 0294    require_window(native)?;
 0295    let hints = qtcore::window::size_hints(device_pixel_ratio(native), constraints);
 296    // Both hints are written in one GUI-thread turn, so no resize can observe
 297    // half of the new constraints.
 0298    ffi::qt_window_set_min_size(native, hints.min_width, hints.min_height);
 0299    ffi::qt_window_set_max_size(native, hints.max_width, hints.max_height);
 0300    Ok(())
 0301}
 302
 0303pub(crate) fn set_icon(native: u64, icon: Icon) -> Result<()> {
 0304    require_window(native)?;
 0305    if let Some(message) =
 0306        qtcore::window::icon_buffer_error(icon.width, icon.height, icon.rgba.len())
 307    {
 0308        return Err(Error::InvalidIcon(message.into()));
 0309    }
 0310    if !ffi::qt_window_set_icon(native, icon.rgba.as_ref(), icon.width, icon.height) {
 0311        return Err(Error::InvalidIcon("Qt rejected the icon pixels".into()));
 0312    }
 0313    Ok(())
 0314}
 315
 0316pub(crate) fn set_focus(native: u64) -> Result<()> {
 0317    require_window(native)?;
 0318    ffi::qt_window_set_focus(native);
 0319    Ok(())
 0320}
 321
 0322pub(crate) fn set_resizable(native: u64, resizable: bool) -> Result<()> {
 0323    require_window(native)?;
 0324    ffi::qt_window_set_resizable(native, resizable);
 0325    Ok(())
 0326}
 327
 0328pub(crate) fn set_enabled(native: u64, enabled: bool) -> Result<()> {
 0329    require_window(native)?;
 0330    ffi::qt_window_set_enabled(native, enabled);
 0331    Ok(())
 0332}
 333
 0334pub(crate) fn set_always_on_bottom(native: u64, always_on_bottom: bool) -> Result<()> {
 0335    require_window(native)?;
 0336    let platform = platform();
 0337    if !qtcore::window::placement_supported(&platform) {
 0338        return Err(unavailable(
 0339            "always-on-bottom",
 0340            &format!(
 0341                "the '{platform}' Qt platform has no on-bottom state (a Wayland compositor does not expose one)"
 0342            ),
 0343        ));
 0344    }
 0345    ffi::qt_window_set_always_on_bottom(native, always_on_bottom);
 0346    Ok(())
 0347}
 348
 0349pub(crate) fn set_always_on_top(native: u64, always_on_top: bool) -> Result<()> {
 0350    require_window(native)?;
 0351    let platform = platform();
 0352    if !qtcore::window::placement_supported(&platform) {
 0353        return Err(unavailable(
 0354            "always-on-top",
 0355            &format!(
 0356                "the '{platform}' Qt platform has no on-top state (Wayland compositors ignore Qt::WindowStaysOnTopHint, 
 0357            ),
 0358        ));
 0359    }
 0360    ffi::qt_window_set_always_on_top(native, always_on_top);
 0361    Ok(())
 0362}
 363
 0364pub(crate) fn set_background_color(native: u64, color: Option<Color>) -> Result<()> {
 0365    require_window(native)?;
 0366    match color {
 0367        Some(Color(r, g, b, a)) => ffi::qt_window_set_background_color(native, r, g, b, a),
 0368        None => ffi::qt_window_reset_background_color(native),
 369    }
 0370    Ok(())
 0371}
 372
 373/// Moves the window to `position`, in the window's own pixel unit.
 374///
 375/// On X11 the window manager is asked to place the frame there. On Wayland the
 376/// compositor owns placement, so `move()` only records the position Qt reports
 377/// back through [`outer_position`]; the call still succeeds, as it does in tao.
 0378pub(crate) fn set_position(native: u64, position: Position) -> Result<()> {
 0379    require_window(native)?;
 0380    let (x, y) = qtcore::window::window_position(device_pixel_ratio(native), position);
 0381    ffi::qt_window_set_position(native, x, y);
 0382    Ok(())
 0383}
 384
 0385pub(crate) fn start_dragging(native: u64) -> Result<()> {
 0386    require_window(native)?;
 0387    if ffi::qt_window_start_dragging(native) {
 0388        return Ok(());
 0389    }
 0390    Err(unavailable(
 0391        "window dragging",
 0392        "QWindow::startSystemMove() returned false (no native window handle, or the call did not come from a mouse press
 0393    ))
 0394}
 395
 0396pub(crate) fn start_resize_dragging(native: u64, direction: i32) -> Result<()> {
 0397    require_window(native)?;
 0398    if !(1..=8).contains(&direction) {
 0399        return Err(unavailable(
 0400            "window resize dragging",
 0401            &format!("unknown resize direction {direction}"),
 0402        ));
 0403    }
 0404    if ffi::qt_window_start_resize_dragging(native, direction) {
 0405        return Ok(());
 0406    }
 0407    Err(unavailable(
 0408        "window resize dragging",
 0409        "QWindow::startSystemResize() returned false (no native window handle, or the call did not come from a mouse pre
 0410    ))
 0411}

Methods/Properties

_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window14require_window
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window11unavailable
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window8platform
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window18device_pixel_ratio
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window13physical_size
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window17physical_position
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window4show
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window4hide
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window5close
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window7destroy
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12scale_factor
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window10inner_size
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window10outer_size
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window14inner_position
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window14outer_position
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window5title
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window5theme
_RNCNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window5theme0B7_
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window13is_fullscreen
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12is_minimized
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12is_maximized
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window10is_focused
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12is_decorated
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12is_resizable
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window10is_visible
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window10is_enabled
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window16is_always_on_top
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window14is_maximizable
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window14is_minimizable
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window11is_closable
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window9set_title
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window8set_size
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window6center
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window15set_decorations
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window13set_maximized
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window8minimize
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window10unminimize
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window14set_fullscreen
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12set_min_size
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12set_max_size
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window20set_size_constraints
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window8set_icon
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window9set_focus
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window13set_resizable
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window11set_enabled
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window20set_always_on_bottom
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window17set_always_on_top
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window20set_background_color
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window12set_position
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window14start_dragging
_RNvNtNtCsdgr7Em4UC3i_16tauri_runtime_qt3ops6window21start_resize_dragging