feat: add workarea getter for monitor (#13276)

This commit is contained in:
Amr Bashir 2025-04-23 03:29:03 +02:00 committed by GitHub
parent 4e00b27913
commit 267368fd4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 153 additions and 85 deletions

View File

@ -0,0 +1,5 @@
---
"@tauri-apps/api": "minor:feat"
---
Add `Monitor.workArea` field.

View File

@ -0,0 +1,6 @@
---
"tauri": "minor:feat"
---
Add `Monitor::work_area` getter

View File

@ -0,0 +1,6 @@
---
"tauri": "minor:feat"
---
Added `tauri::PhysicalRect` and `tauri::LogicalRect` types.

View File

@ -475,7 +475,7 @@ pub fn build_wix_app_installer(
// when we're performing code signing, we'll sign some WiX DLLs, so we make a local copy
let wix_toolset_path = if settings.can_sign() {
let wix_path = output_path.join("wix");
crate::utils::fs_utils::copy_dir(&wix_toolset_path, &wix_path)
crate::utils::fs_utils::copy_dir(wix_toolset_path, &wix_path)
.context("failed to copy wix directory")?;
wix_path
} else {
@ -790,7 +790,7 @@ pub fn build_wix_app_installer(
// sign default extensions
if settings.can_sign() {
for path in &fragment_extensions {
try_sign(&path, settings)?;
try_sign(path, settings)?;
}
}

View File

@ -33,7 +33,7 @@ fn main() {
let job = win32job::Job::create().unwrap();
let mut info = job.query_extended_limit_info().unwrap();
info.limit_kill_on_job_close();
job.set_extended_limit_info(&mut info).unwrap();
job.set_extended_limit_info(&info).unwrap();
job.assign_current_process().unwrap();
job
};

View File

@ -12,9 +12,8 @@
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
)]
use self::monitor::MonitorExt;
use http::Request;
#[cfg(desktop)]
use monitor::MonitorExt;
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
use tauri_runtime::{
@ -131,7 +130,6 @@ use std::{
pub type WebviewId = u32;
type IpcHandler = dyn Fn(Request<String>) + 'static;
#[cfg(desktop)]
mod monitor;
#[cfg(any(
windows,
@ -462,8 +460,8 @@ impl From<DeviceEventFilter> for DeviceEventFilterWrapper {
}
pub struct RectWrapper(pub wry::Rect);
impl From<tauri_runtime::Rect> for RectWrapper {
fn from(value: tauri_runtime::Rect) -> Self {
impl From<tauri_runtime::dpi::Rect> for RectWrapper {
fn from(value: tauri_runtime::dpi::Rect) -> Self {
RectWrapper(wry::Rect {
position: value.position,
size: value.size,
@ -518,7 +516,7 @@ impl WindowEventWrapper {
if !*focused
&& focused_webview
.as_deref()
.map_or(false, |w| w != FOCUSED_WEBVIEW_MARKER)
.is_some_and(|w| w != FOCUSED_WEBVIEW_MARKER)
{
return Self(None);
}
@ -586,6 +584,7 @@ impl From<MonitorHandleWrapper> for Monitor {
name: monitor.0.name(),
position: PhysicalPositionWrapper(monitor.0.position()).into(),
size: PhysicalSizeWrapper(monitor.0.size()).into(),
work_area: monitor.0.work_area(),
scale_factor: monitor.0.scale_factor(),
}
}
@ -1403,7 +1402,7 @@ pub enum WebviewMessage {
Hide,
SetPosition(Position),
SetSize(Size),
SetBounds(tauri_runtime::Rect),
SetBounds(tauri_runtime::dpi::Rect),
SetFocus,
Reparent(WindowId, Sender<Result<()>>),
SetAutoResize(bool),
@ -1412,7 +1411,7 @@ pub enum WebviewMessage {
ClearAllBrowsingData,
// Getters
Url(Sender<Result<String>>),
Bounds(Sender<Result<tauri_runtime::Rect>>),
Bounds(Sender<Result<tauri_runtime::dpi::Rect>>),
Position(Sender<Result<PhysicalPosition<i32>>>),
Size(Sender<Result<PhysicalSize<u32>>>),
WithWebview(Box<dyn FnOnce(Webview) + Send>),
@ -1541,7 +1540,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
webview_getter!(self, WebviewMessage::Url)?
}
fn bounds(&self) -> Result<tauri_runtime::Rect> {
fn bounds(&self) -> Result<tauri_runtime::dpi::Rect> {
webview_getter!(self, WebviewMessage::Bounds)?
}
@ -1599,7 +1598,7 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
)
}
fn set_bounds(&self, bounds: tauri_runtime::Rect) -> Result<()> {
fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> {
send_user_message(
&self.context,
Message::Webview(
@ -3667,7 +3666,7 @@ fn handle_user_message<T: UserEvent>(
tx.send(
webview
.bounds()
.map(|bounds| tauri_runtime::Rect {
.map(|bounds| tauri_runtime::dpi::Rect {
size: bounds.size,
position: bounds.position,
})

View File

@ -2,15 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::PhysicalRect;
use gtk::prelude::MonitorExt;
use tao::{
dpi::{PhysicalPosition, PhysicalSize},
platform::unix::MonitorHandleExtUnix,
};
use tao::platform::unix::MonitorHandleExtUnix;
use tauri_runtime::dpi::{PhysicalPosition, PhysicalRect, PhysicalSize};
impl super::MonitorExt for tao::monitor::MonitorHandle {
fn work_area(&self) -> PhysicalRect {
fn work_area(&self) -> PhysicalRect<i32, u32> {
let rect = self.gdk_monitor().workarea();
PhysicalRect {
size: PhysicalSize::new(rect.width() as u32, rect.height() as u32),

View File

@ -2,11 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::PhysicalRect;
use tao::dpi::{LogicalPosition, LogicalSize, PhysicalPosition};
use tauri_runtime::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalRect};
impl super::MonitorExt for tao::monitor::MonitorHandle {
fn work_area(&self) -> PhysicalRect {
fn work_area(&self) -> PhysicalRect<i32, u32> {
use objc2_app_kit::NSScreen;
use tao::platform::macos::MonitorHandleExtMacOS;
if let Some(ns_screen) = self.ns_screen() {
@ -20,7 +19,7 @@ impl super::MonitorExt for tao::monitor::MonitorHandle {
} else {
PhysicalRect {
size: self.size(),
position: PhysicalPosition::default(),
position: self.position(),
}
}
}

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tao::dpi::{PhysicalPosition, PhysicalSize};
use tauri_runtime::dpi::PhysicalRect;
#[cfg(any(
target_os = "linux",
@ -17,16 +17,21 @@ mod macos;
#[cfg(windows)]
mod windows;
pub struct PhysicalRect {
pub size: PhysicalSize<u32>,
pub position: PhysicalPosition<i32>,
}
pub trait MonitorExt {
/// Get the work area of this monitor
///
/// ## Platform-specific:
///
/// - **Android / iOS**: Unsupported.
fn work_area(&self) -> PhysicalRect;
fn work_area(&self) -> PhysicalRect<i32, u32>;
}
#[cfg(mobile)]
impl MonitorExt for tao::monitor::MonitorHandle {
fn work_area(&self) -> PhysicalRect<i32, u32> {
PhysicalRect {
size: self.size(),
position: self.position(),
}
}
}

View File

@ -2,11 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::PhysicalRect;
use tao::dpi::{PhysicalPosition, PhysicalSize};
use tauri_runtime::dpi::PhysicalRect;
impl super::MonitorExt for tao::monitor::MonitorHandle {
fn work_area(&self) -> PhysicalRect {
fn work_area(&self) -> PhysicalRect<i32, u32> {
use tao::platform::windows::MonitorHandleExtWindows;
use windows::Win32::Graphics::Gdi::{GetMonitorInfoW, HMONITOR, MONITORINFO};
let mut monitor_info = MONITORINFO {
@ -25,7 +25,7 @@ impl super::MonitorExt for tao::monitor::MonitorHandle {
} else {
PhysicalRect {
size: self.size(),
position: PhysicalPosition::default(),
position: self.position(),
}
}
}

View File

@ -15,6 +15,8 @@ mod macos;
#[cfg(windows)]
mod windows;
use crate::monitor::MonitorExt;
pub trait WindowExt {
/// Enable or disable the window
///
@ -61,23 +63,10 @@ pub fn calculate_window_center_position(
window_size: tao::dpi::PhysicalSize<u32>,
target_monitor: tao::monitor::MonitorHandle,
) -> tao::dpi::PhysicalPosition<i32> {
let monitor_size: tao::dpi::PhysicalSize<u32>;
let monitor_position: tao::dpi::PhysicalPosition<i32>;
#[cfg(desktop)]
{
use crate::monitor::MonitorExt;
let work_area = target_monitor.work_area();
monitor_size = work_area.size;
monitor_position = work_area.position;
}
#[cfg(mobile)]
{
monitor_size = target_monitor.size();
monitor_position = target_monitor.position();
}
let work_area = target_monitor.work_area();
tao::dpi::PhysicalPosition::new(
(monitor_size.width as i32 - window_size.width as i32) / 2 + monitor_position.x,
(monitor_size.height as i32 - window_size.height as i32) / 2 + monitor_position.y,
(work_area.size.width as i32 - window_size.width as i32) / 2 + work_area.position.x,
(work_area.size.height as i32 - window_size.height as i32) / 2 + work_area.position.y,
)
}

View File

@ -0,0 +1,60 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
pub use dpi::*;
use serde::Serialize;
/// A rectangular region.
#[derive(Clone, Copy, Debug, Serialize)]
pub struct Rect {
/// Rect position.
pub position: dpi::Position,
/// Rect size.
pub size: dpi::Size,
}
impl Default for Rect {
fn default() -> Self {
Self {
position: Position::Logical((0, 0).into()),
size: Size::Logical((0, 0).into()),
}
}
}
/// A rectangular region in physical pixels.
#[derive(Clone, Copy, Debug, Serialize)]
pub struct PhysicalRect<P: dpi::Pixel, S: dpi::Pixel> {
/// Rect position.
pub position: dpi::PhysicalPosition<P>,
/// Rect size.
pub size: dpi::PhysicalSize<S>,
}
impl<P: dpi::Pixel, S: dpi::Pixel> Default for PhysicalRect<P, S> {
fn default() -> Self {
Self {
position: (0, 0).into(),
size: (0, 0).into(),
}
}
}
/// A rectangular region in logical pixels.
#[derive(Clone, Copy, Debug, Serialize)]
pub struct LogicalRect<P: dpi::Pixel, S: dpi::Pixel> {
/// Rect position.
pub position: dpi::LogicalPosition<P>,
/// Rect size.
pub size: dpi::LogicalSize<S>,
}
impl<P: dpi::Pixel, S: dpi::Pixel> Default for LogicalRect<P, S> {
fn default() -> Self {
Self {
position: (0, 0).into(),
size: (0, 0).into(),
}
}
}

View File

@ -14,19 +14,21 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
use raw_window_handle::DisplayHandle;
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use std::{borrow::Cow, fmt::Debug, sync::mpsc::Sender};
use tauri_utils::config::Color;
use tauri_utils::Theme;
use url::Url;
use webview::{DetachedWebview, PendingWebview};
/// UI scaling utilities.
pub mod dpi;
/// Types useful for interacting with a user's monitors.
pub mod monitor;
pub mod webview;
pub mod window;
use dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use dpi::{PhysicalPosition, PhysicalSize, Position, Rect, Size};
use monitor::Monitor;
use window::{
CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowEvent,
@ -40,33 +42,12 @@ use http::{
status::InvalidStatusCode,
};
/// UI scaling utilities.
pub use dpi;
/// Cookie extraction
pub use cookie::Cookie;
pub type WindowEventId = u32;
pub type WebviewEventId = u32;
/// A rectangular region.
#[derive(Clone, Copy, Debug, Serialize)]
pub struct Rect {
/// Rect position.
pub position: dpi::Position,
/// Rect size.
pub size: dpi::Size,
}
impl Default for Rect {
fn default() -> Self {
Self {
position: Position::Logical((0, 0).into()),
size: Size::Logical((0, 0).into()),
}
}
}
/// Progress bar status.
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "camelCase")]

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::dpi::{PhysicalPosition, PhysicalRect, PhysicalSize};
/// Monitor descriptor.
#[derive(Debug, Clone)]
@ -14,6 +14,8 @@ pub struct Monitor {
pub size: PhysicalSize<u32>,
/// The top-left corner position of the monitor relative to the larger full screen area.
pub position: PhysicalPosition<i32>,
/// The monitor's work_area.
pub work_area: PhysicalRect<i32, u32>,
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
pub scale_factor: f64,
}

File diff suppressed because one or more lines are too long

View File

@ -218,9 +218,12 @@ pub use {
},
self::manager::Asset,
self::runtime::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
dpi::{
LogicalPosition, LogicalRect, LogicalSize, PhysicalPosition, PhysicalRect, PhysicalSize,
Pixel, Position, Rect, Size,
},
window::{CursorIcon, DragDropEvent, WindowSizeConstraints},
DeviceEventFilter, Rect, UserAttentionType,
DeviceEventFilter, UserAttentionType,
},
self::state::{State, StateManager},
self::utils::{

View File

@ -574,8 +574,8 @@ impl<T: UserEvent> WebviewDispatch<T> for MockWebviewDispatcher {
Ok(self.url.lock().unwrap().clone())
}
fn bounds(&self) -> Result<tauri_runtime::Rect> {
Ok(tauri_runtime::Rect::default())
fn bounds(&self) -> Result<tauri_runtime::dpi::Rect> {
Ok(tauri_runtime::dpi::Rect::default())
}
fn position(&self) -> Result<PhysicalPosition<i32>> {
@ -606,7 +606,7 @@ impl<T: UserEvent> WebviewDispatch<T> for MockWebviewDispatcher {
Ok(())
}
fn set_bounds(&self, bounds: tauri_runtime::Rect) -> Result<()> {
fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> Result<()> {
Ok(())
}

View File

@ -604,7 +604,7 @@ tauri::Builder::default()
let mut pending = self.into_pending_webview(&window, window.label())?;
pending.webview_attributes.bounds = Some(tauri_runtime::Rect { size, position });
pending.webview_attributes.bounds = Some(tauri_runtime::dpi::Rect { size, position });
let use_https_scheme = pending.webview_attributes.use_https_scheme;
@ -1237,7 +1237,7 @@ impl<R: Runtime> Webview<R> {
}
/// Resizes this webview.
pub fn set_bounds(&self, bounds: tauri_runtime::Rect) -> crate::Result<()> {
pub fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> crate::Result<()> {
self
.webview
.dispatcher
@ -1302,7 +1302,7 @@ impl<R: Runtime> Webview<R> {
}
/// Returns the bounds of the webviews's client area.
pub fn bounds(&self) -> crate::Result<tauri_runtime::Rect> {
pub fn bounds(&self) -> crate::Result<tauri_runtime::dpi::Rect> {
self.webview.dispatcher.bounds().map_err(Into::into)
}

View File

@ -7,7 +7,7 @@
pub(crate) mod plugin;
use tauri_runtime::{
dpi::{PhysicalPosition, PhysicalSize},
dpi::{PhysicalPosition, PhysicalRect, PhysicalSize},
webview::PendingWebview,
};
pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
@ -61,6 +61,7 @@ pub struct Monitor {
pub(crate) name: Option<String>,
pub(crate) size: PhysicalSize<u32>,
pub(crate) position: PhysicalPosition<i32>,
pub(crate) work_area: PhysicalRect<i32, u32>,
pub(crate) scale_factor: f64,
}
@ -70,6 +71,7 @@ impl From<RuntimeMonitor> for Monitor {
name: monitor.name,
size: monitor.size,
position: monitor.position,
work_area: monitor.work_area,
scale_factor: monitor.scale_factor,
}
}
@ -92,6 +94,11 @@ impl Monitor {
&self.position
}
/// Returns the monitor's work_area.
pub fn work_area(&self) -> &PhysicalRect<i32, u32> {
&self.work_area
}
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
pub fn scale_factor(&self) -> f64 {
self.scale_factor

View File

@ -51,6 +51,11 @@ export interface Monitor {
size: PhysicalSize
/** the Top-left corner position of the monitor relative to the larger full screen area. */
position: PhysicalPosition
/** The monitor's work area. */
workArea: {
position: PhysicalPosition
size: PhysicalSize
}
/** The scale factor that can be used to map physical pixels to logical pixels. */
scaleFactor: number
}
@ -2427,7 +2432,11 @@ function mapMonitor(m: Monitor | null): Monitor | null {
name: m.name,
scaleFactor: m.scaleFactor,
position: new PhysicalPosition(m.position),
size: new PhysicalSize(m.size)
size: new PhysicalSize(m.size),
workArea: {
position: new PhysicalPosition(m.workArea.position),
size: new PhysicalSize(m.workArea.size)
}
}
}