mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-11-28 03:47:37 +03:00
feat: add granular size constraints APIs (#10242)
This commit is contained in:
parent
26f2e19a4f
commit
da25f73530
7
.changes/split-min-max-constraints-apis-runtime-js.md
Normal file
7
.changes/split-min-max-constraints-apis-runtime-js.md
Normal 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`
|
8
.changes/split-min-max-constraints-apis-runtime.md
Normal file
8
.changes/split-min-max-constraints-apis-runtime.md
Normal 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`
|
6
.changes/split-min-max-constraints-apis.md
Normal file
6
.changes/split-min-max-constraints-apis.md
Normal 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.
|
6
.changes/split-min-max-constraints.md
Normal file
6
.changes/split-min-max-constraints.md
Normal 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.
|
@ -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)
|
||||
}
|
||||
|
@ -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<()>;
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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),
|
||||
|
@ -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
@ -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},
|
||||
|
@ -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)]
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user