feat: add granular size constraints APIs (#10242)

This commit is contained in:
Amr Bashir 2024-07-17 04:05:56 +03:00 committed by GitHub
parent 26f2e19a4f
commit da25f73530
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 220 additions and 12 deletions

View File

@ -0,0 +1,7 @@
---
"@tauri-apps/api": patch:feat
---
Add APIs to enable setting window size constraints separately:
- Added `WindowSizeConstraints` interface in `window` and `webviewWindow` modules.
- Added `Window.setSizeConstraints` and `WebviewWindow.setSizeConstraints`

View File

@ -0,0 +1,8 @@
---
"tauri": patch:feat
---
Add APIs to enable setting window size constraints separately:
- Added `WindowBuilder::inner_size_constraints` and `WebviewWindowBuilder::inner_size_constraints` which can be used for setting granular constraints.
- Added `WindowSizeConstraints` struct
- Added `Window::set_size_constraints` and `WebviewWindow::set_size_constraints`

View File

@ -0,0 +1,6 @@
---
"tauri-runtime": "patch"
"tauri-runtime-wry": "patch"
---
Add `inner_size_constraints` method on `WindowBuilder` trait and `set_size_constraints` method on `WindowDispatch` trait.

View File

@ -0,0 +1,6 @@
---
"tauri": "patch:bug"
"@tauri-apps/api": "patch:bug"
---
Apply `minWidth`, `minHieght`, `maxWidth` and `maxHeight` constraints separately, which fixes a long standing bug where these constraints were never applied unless width and height were constrained together.

View File

@ -20,7 +20,7 @@ use tauri_runtime::{
webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler},
window::{
CursorIcon, DetachedWindow, DragDropEvent, PendingWindow, RawWindow, WebviewEvent,
WindowBuilder, WindowBuilderBase, WindowEvent, WindowId,
WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, WindowSizeConstraints,
},
DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState,
ProgressBarStatus, Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType,
@ -43,8 +43,8 @@ use wry::WebViewBuilderExtWindows;
use tao::{
dpi::{
LogicalPosition as TaoLogicalPosition, LogicalSize as TaoLogicalSize,
PhysicalPosition as TaoPhysicalPosition, PhysicalSize as TaoPhysicalSize,
Position as TaoPosition, Size as TaoSize,
LogicalUnit as ToaLogicalUnit, PhysicalPosition as TaoPhysicalPosition,
PhysicalSize as TaoPhysicalSize, Position as TaoPosition, Size as TaoSize,
},
event::{Event, StartCause, WindowEvent as TaoWindowEvent},
event_loop::{
@ -774,12 +774,22 @@ impl WindowBuilder for WindowBuilderWrapper {
.minimizable(config.minimizable)
.shadow(config.shadow);
if let (Some(min_width), Some(min_height)) = (config.min_width, config.min_height) {
window = window.min_inner_size(min_width, min_height);
let mut constraints = WindowSizeConstraints::default();
if let Some(min_width) = config.min_width {
constraints.min_width = Some(ToaLogicalUnit::new(min_width).into());
}
if let (Some(max_width), Some(max_height)) = (config.max_width, config.max_height) {
window = window.max_inner_size(max_width, max_height);
if let Some(min_height) = config.min_height {
constraints.min_height = Some(ToaLogicalUnit::new(min_height).into());
}
if let Some(max_width) = config.max_width {
constraints.max_width = Some(ToaLogicalUnit::new(max_width).into());
}
if let Some(max_height) = config.max_height {
constraints.max_height = Some(ToaLogicalUnit::new(max_height).into());
}
window = window.inner_size_constraints(constraints);
if let (Some(x), Some(y)) = (config.x, config.y) {
window = window.position(x, y);
}
@ -823,6 +833,16 @@ impl WindowBuilder for WindowBuilderWrapper {
self
}
fn inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self {
self.inner.window.inner_size_constraints = tao::window::WindowSizeConstraints {
min_width: constraints.min_width,
min_height: constraints.min_height,
max_width: constraints.max_width,
max_height: constraints.max_height,
};
self
}
fn resizable(mut self, resizable: bool) -> Self {
self.inner = self.inner.with_resizable(resizable);
self
@ -1144,6 +1164,7 @@ pub enum WindowMessage {
SetSize(Size),
SetMinSize(Option<Size>),
SetMaxSize(Option<Size>),
SetSizeConstraints(WindowSizeConstraints),
SetPosition(Position),
SetFullscreen(bool),
SetFocus,
@ -1850,6 +1871,16 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
)
}
fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()> {
send_user_message(
&self.context,
Message::Window(
self.window_id,
WindowMessage::SetSizeConstraints(constraints),
),
)
}
fn set_position(&self, position: Position) -> Result<()> {
send_user_message(
&self.context,
@ -2831,6 +2862,14 @@ fn handle_user_message<T: UserEvent>(
WindowMessage::SetMaxSize(size) => {
window.set_max_inner_size(size.map(|s| SizeWrapper::from(s).0));
}
WindowMessage::SetSizeConstraints(constraints) => {
window.set_inner_size_constraints(tao::window::WindowSizeConstraints {
min_width: constraints.min_width,
min_height: constraints.min_height,
max_width: constraints.max_width,
max_height: constraints.max_height,
});
}
WindowMessage::SetPosition(position) => {
window.set_outer_position(PositionWrapper::from(position).0)
}

View File

@ -26,7 +26,10 @@ pub mod window;
use dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use monitor::Monitor;
use window::{CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowEvent};
use window::{
CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowEvent,
WindowSizeConstraints,
};
use window::{WindowBuilder, WindowId};
use http::{
@ -735,6 +738,9 @@ pub trait WindowDispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 's
/// Updates the window max inner size.
fn set_max_size(&self, size: Option<Size>) -> Result<()>;
/// Sets this window's minimum inner width.
fn set_size_constraints(&self, constraints: WindowSizeConstraints) -> Result<()>;
/// Updates the window position.
fn set_position(&self, position: Position) -> Result<()>;

View File

@ -9,7 +9,8 @@ use crate::{
Icon, Runtime, UserEvent, WindowDispatch,
};
use serde::{Deserialize, Deserializer};
use dpi::PixelUnit;
use serde::{Deserialize, Deserializer, Serialize};
use tauri_utils::{config::WindowConfig, Theme};
#[cfg(windows)]
use windows::Win32::Foundation::HWND;
@ -201,6 +202,28 @@ impl<'de> Deserialize<'de> for CursorIcon {
}
}
/// Window size constraints
#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WindowSizeConstraints {
/// The minimum width a window can be, If this is `None`, the window will have no minimum width.
///
/// The default is `None`.
pub min_width: Option<PixelUnit>,
/// The minimum height a window can be, If this is `None`, the window will have no minimum height.
///
/// The default is `None`.
pub min_height: Option<PixelUnit>,
/// The maximum width a window can be, If this is `None`, the window will have no maximum width.
///
/// The default is `None`.
pub max_width: Option<PixelUnit>,
/// The maximum height a window can be, If this is `None`, the window will have no maximum height.
///
/// The default is `None`.
pub max_height: Option<PixelUnit>,
}
/// Do **NOT** implement this trait except for use in a custom [`Runtime`]
///
/// This trait is separate from [`WindowBuilder`] to prevent "accidental" implementation.
@ -237,6 +260,10 @@ pub trait WindowBuilder: WindowBuilderBase {
#[must_use]
fn max_inner_size(self, max_width: f64, max_height: f64) -> Self;
/// Window inner size constraints.
#[must_use]
fn inner_size_constraints(self, constraints: WindowSizeConstraints) -> Self;
/// Whether the window is resizable or not.
/// When resizable is set to false, native window's maximize button is automatically disabled.
#[must_use]

View File

@ -92,6 +92,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("set_content_protected", false),
("set_size", false),
("set_min_size", false),
("set_size_constraints", false),
("set_max_size", false),
("set_position", false),
("set_fullscreen", false),

View File

@ -1390,6 +1390,32 @@ Denies the set_size command without any pre-configured scope.
<tr>
<td>
`window:allow-set-size-constraints`
</td>
<td>
Enables the set_size_constraints command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`window:deny-set-size-constraints`
</td>
<td>
Denies the set_size_constraints command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`window:allow-set-skip-taskbar`
</td>

File diff suppressed because one or more lines are too long

View File

@ -222,7 +222,7 @@ pub use {
self::runtime::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
webview::WebviewAttributes,
window::{CursorIcon, DragDropEvent},
window::{CursorIcon, DragDropEvent, WindowSizeConstraints},
DeviceEventFilter, Rect, UserAttentionType,
},
self::state::{State, StateManager},

View File

@ -332,6 +332,13 @@ impl WindowBuilder for MockWindowBuilder {
self
}
fn inner_size_constraints(
self,
constraints: tauri_runtime::window::WindowSizeConstraints,
) -> Self {
self
}
fn resizable(self, resizable: bool) -> Self {
self
}
@ -937,6 +944,13 @@ impl<T: UserEvent> WindowDispatch<T> for MockWindowDispatcher {
fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
Ok(())
}
fn set_size_constraints(
&self,
constraints: tauri_runtime::window::WindowSizeConstraints,
) -> Result<()> {
Ok(())
}
}
#[derive(Debug, Clone)]

View File

@ -27,6 +27,7 @@ use crate::{
},
};
use serde::Serialize;
use tauri_runtime::window::WindowSizeConstraints;
use tauri_utils::config::{WebviewUrl, WindowConfig};
use url::Url;
@ -406,6 +407,13 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
self
}
/// Window inner size constraints.
#[must_use]
pub fn inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self {
self.window_builder = self.window_builder.inner_size_constraints(constraints);
self
}
/// Whether the window is resizable or not.
/// When resizable is set to false, native window's maximize button is automatically disabled.
#[must_use]
@ -1469,6 +1477,11 @@ impl<R: Runtime> WebviewWindow<R> {
self.webview.window().set_max_size(size.map(|s| s.into()))
}
/// Sets this window's minimum inner width.
pub fn set_size_constraints(&self, constriants: WindowSizeConstraints) -> crate::Result<()> {
self.webview.window().set_size_constraints(constriants)
}
/// Sets this window's position.
pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self.webview.window().set_position(position)

View File

@ -9,6 +9,7 @@ pub(crate) mod plugin;
use tauri_runtime::{
dpi::{PhysicalPosition, PhysicalSize},
webview::PendingWebview,
window::WindowSizeConstraints,
};
pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
@ -492,6 +493,13 @@ impl<'a, R: Runtime, M: Manager<R>> WindowBuilder<'a, R, M> {
self
}
/// Window inner size constraints.
#[must_use]
pub fn inner_size_constraints(mut self, constraints: WindowSizeConstraints) -> Self {
self.window_builder = self.window_builder.inner_size_constraints(constraints);
self
}
/// Whether the window is resizable or not.
/// When resizable is set to false, native window's maximize button is automatically disabled.
#[must_use]
@ -1855,6 +1863,15 @@ tauri::Builder::default()
.map_err(Into::into)
}
/// Sets this window's minimum inner width.
pub fn set_size_constraints(&self, constriants: WindowSizeConstraints) -> crate::Result<()> {
self
.window
.dispatcher
.set_size_constraints(constriants)
.map_err(Into::into)
}
/// Sets this window's position.
pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self

View File

@ -11,7 +11,7 @@ use crate::{
#[cfg(desktop)]
mod desktop_commands {
use tauri_runtime::ResizeDirection;
use tauri_runtime::{window::WindowSizeConstraints, ResizeDirection};
use tauri_utils::TitleBarStyle;
use super::*;
@ -132,6 +132,7 @@ mod desktop_commands {
setter!(set_progress_bar, ProgressBarState);
setter!(set_visible_on_all_workspaces, bool);
setter!(set_title_bar_style, TitleBarStyle);
setter!(set_size_constraints, WindowSizeConstraints);
#[command(root = "crate")]
pub async fn set_icon<R: Runtime>(
@ -264,6 +265,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
desktop_commands::set_size,
desktop_commands::set_min_size,
desktop_commands::set_max_size,
desktop_commands::set_size_constraints,
desktop_commands::set_position,
desktop_commands::set_fullscreen,
desktop_commands::set_focus,

View File

@ -183,6 +183,13 @@ export enum ProgressBarStatus {
Error = 'error'
}
export interface WindowSizeConstraints {
minWidth?: number
minHeight?: number
maxWidth?: number
maxHeight?: number
}
export interface ProgressBarState {
/**
* The progress bar status.
@ -1311,6 +1318,35 @@ class Window {
})
}
/**
* Sets the window inner size constraints.
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
* await getCurrentWindow().setSizeConstraints({ minWidth: 300 });
* ```
*
* @param size The logical or physical inner size, or `null` to unset the constraint.
* @returns A promise indicating the success or failure of the operation.
*/
async setSizeConstraints(
constraints: WindowSizeConstraints | null | undefined
): Promise<void> {
function logical(pixel?: number): { Logical: number } | null {
return pixel ? { Logical: pixel } : null
}
return invoke('plugin:window|set_size_constraints', {
label: this.label,
value: {
minWidth: logical(constraints?.minWidth),
minHeight: logical(constraints?.minHeight),
maxWidth: logical(constraints?.maxWidth),
maxHeight: logical(constraints?.maxHeight)
}
})
}
/**
* Sets the window outer position.
* @example