diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/src/overlay/nested.rs | 76 | ||||
-rw-r--r-- | runtime/src/task.rs | 63 | ||||
-rw-r--r-- | runtime/src/user_interface.rs | 10 | ||||
-rw-r--r-- | runtime/src/window.rs | 81 | ||||
-rw-r--r-- | runtime/src/window/screenshot.rs | 108 |
5 files changed, 146 insertions, 192 deletions
diff --git a/runtime/src/overlay/nested.rs b/runtime/src/overlay/nested.rs index da3e6929..342ad70c 100644 --- a/runtime/src/overlay/nested.rs +++ b/runtime/src/overlay/nested.rs @@ -158,7 +158,7 @@ where } /// Processes a runtime [`Event`]. - pub fn on_event( + pub fn update( &mut self, event: Event, layout: Layout<'_>, @@ -166,7 +166,7 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - ) -> event::Status { + ) { fn recurse<Message, Theme, Renderer>( element: &mut overlay::Element<'_, Message, Theme, Renderer>, layout: Layout<'_>, @@ -175,31 +175,30 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - ) -> (event::Status, bool) + ) -> bool where Renderer: renderer::Renderer, { let mut layouts = layout.children(); if let Some(layout) = layouts.next() { - let (nested_status, nested_is_over) = - if let Some((mut nested, nested_layout)) = - element.overlay(layout, renderer).zip(layouts.next()) - { - recurse( - &mut nested, - nested_layout, - event.clone(), - cursor, - renderer, - clipboard, - shell, - ) - } else { - (event::Status::Ignored, false) - }; + let nested_is_over = if let Some((mut nested, nested_layout)) = + element.overlay(layout, renderer).zip(layouts.next()) + { + recurse( + &mut nested, + nested_layout, + event.clone(), + cursor, + renderer, + clipboard, + shell, + ) + } else { + false + }; - if matches!(nested_status, event::Status::Ignored) { + if shell.event_status() == event::Status::Ignored { let is_over = nested_is_over || cursor .position() @@ -212,30 +211,29 @@ where }) .unwrap_or_default(); - ( - element.on_event( - event, - layout, - if nested_is_over { - mouse::Cursor::Unavailable - } else { - cursor - }, - renderer, - clipboard, - shell, - ), - is_over, - ) + element.update( + event, + layout, + if nested_is_over { + mouse::Cursor::Unavailable + } else { + cursor + }, + renderer, + clipboard, + shell, + ); + + is_over } else { - (nested_status, nested_is_over) + nested_is_over } } else { - (event::Status::Ignored, false) + false } } - let (status, _) = recurse( + let _ = recurse( &mut self.overlay, layout, event, @@ -244,8 +242,6 @@ where clipboard, shell, ); - - status } /// Returns the current [`mouse::Interaction`] of the [`Nested`] overlay. diff --git a/runtime/src/task.rs b/runtime/src/task.rs index 4554c74b..22cfb63e 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -9,6 +9,7 @@ use crate::futures::{boxed_stream, BoxStream, MaybeSend}; use crate::Action; use std::future::Future; +use std::sync::Arc; /// A set of concurrent actions to be performed by the iced runtime. /// @@ -183,16 +184,16 @@ impl<T> Task<T> { ( Self(Some(boxed_stream(stream))), Handle { - raw: Some(handle), - abort_on_drop: false, + internal: InternalHandle::Manual(handle), }, ) } None => ( Self(None), Handle { - raw: None, - abort_on_drop: false, + internal: InternalHandle::Manual( + stream::AbortHandle::new_pair().0, + ), }, ), } @@ -220,44 +221,64 @@ impl<T> Task<T> { /// A handle to a [`Task`] that can be used for aborting it. #[derive(Debug, Clone)] pub struct Handle { - raw: Option<stream::AbortHandle>, - abort_on_drop: bool, + internal: InternalHandle, +} + +#[derive(Debug, Clone)] +enum InternalHandle { + Manual(stream::AbortHandle), + AbortOnDrop(Arc<stream::AbortHandle>), +} + +impl InternalHandle { + pub fn as_ref(&self) -> &stream::AbortHandle { + match self { + InternalHandle::Manual(handle) => handle, + InternalHandle::AbortOnDrop(handle) => handle.as_ref(), + } + } } impl Handle { /// Aborts the [`Task`] of this [`Handle`]. pub fn abort(&self) { - if let Some(handle) = &self.raw { - handle.abort(); - } + self.internal.as_ref().abort(); } /// Returns a new [`Handle`] that will call [`Handle::abort`] whenever - /// it is dropped. + /// all of its instances are dropped. + /// + /// If a [`Handle`] is cloned, [`Handle::abort`] will only be called + /// once all of the clones are dropped. /// /// This can be really useful if you do not want to worry about calling /// [`Handle::abort`] yourself. - pub fn abort_on_drop(mut self) -> Self { - Self { - raw: self.raw.take(), - abort_on_drop: true, + pub fn abort_on_drop(self) -> Self { + match &self.internal { + InternalHandle::Manual(handle) => Self { + internal: InternalHandle::AbortOnDrop(Arc::new(handle.clone())), + }, + InternalHandle::AbortOnDrop(_) => self, } } /// Returns `true` if the [`Task`] of this [`Handle`] has been aborted. pub fn is_aborted(&self) -> bool { - if let Some(handle) = &self.raw { - handle.is_aborted() - } else { - true - } + self.internal.as_ref().is_aborted() } } impl Drop for Handle { fn drop(&mut self) { - if self.abort_on_drop { - self.abort(); + if let InternalHandle::AbortOnDrop(handle) = &mut self.internal { + let handle = std::mem::replace( + handle, + Arc::new(stream::AbortHandle::new_pair().0), + ); + + if let Some(handle) = Arc::into_inner(handle) { + handle.abort(); + } } } } diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 8dfc97a7..b2826f71 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -210,7 +210,7 @@ where for event in events.iter().cloned() { let mut shell = Shell::new(messages); - let event_status = overlay.on_event( + overlay.update( event, Layout::new(&layout), cursor, @@ -219,7 +219,7 @@ where &mut shell, ); - event_statuses.push(event_status); + event_statuses.push(shell.event_status()); match (redraw_request, shell.redraw_request()) { (None, Some(at)) => { @@ -308,7 +308,7 @@ where let mut shell = Shell::new(messages); - let event_status = self.root.as_widget_mut().on_event( + self.root.as_widget_mut().update( &mut self.state, event, Layout::new(&self.base), @@ -319,7 +319,7 @@ where &viewport, ); - if matches!(event_status, event::Status::Captured) { + if shell.event_status() == event::Status::Captured { self.overlay = None; } @@ -347,7 +347,7 @@ where outdated = true; } - event_status.merge(overlay_status) + shell.event_status().merge(overlay_status) }) .collect(); diff --git a/runtime/src/window.rs b/runtime/src/window.rs index 382f4518..183fab97 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -1,11 +1,8 @@ //! Build window-based GUI applications. -pub mod screenshot; - -pub use screenshot::Screenshot; - use crate::core::time::Instant; use crate::core::window::{ - Event, Icon, Id, Level, Mode, Settings, UserAttention, + Direction, Event, Icon, Id, Level, Mode, Screenshot, Settings, + UserAttention, }; use crate::core::{Point, Size}; use crate::futures::event; @@ -35,10 +32,17 @@ pub enum Action { /// Move the window with the left mouse button until the button is /// released. /// - /// There’s no guarantee that this will work unless the left mouse + /// There's no guarantee that this will work unless the left mouse /// button was pressed immediately before this function is called. Drag(Id), + /// Resize the window with the left mouse button until the button is + /// released. + /// + /// There's no guarantee that this will work unless the left mouse + /// button was pressed immediately before this function is called. + DragResize(Id, Direction), + /// Resize the window to the given logical dimensions. Resize(Id, Size), @@ -72,7 +76,7 @@ pub enum Action { Move(Id, Point), /// Change the [`Mode`] of the window. - ChangeMode(Id, Mode), + SetMode(Id, Mode), /// Get the current [`Mode`] of the window. GetMode(Id, oneshot::Sender<Mode>), @@ -115,7 +119,7 @@ pub enum Action { GainFocus(Id), /// Change the window [`Level`]. - ChangeLevel(Id, Level), + SetLevel(Id, Level), /// Show the system menu at cursor position. /// @@ -140,7 +144,7 @@ pub enum Action { /// /// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That /// said, it's usually in the same ballpark as on Windows. - ChangeIcon(Id, Icon), + SetIcon(Id, Icon), /// Runs the closure with the native window handle of the window with the given [`Id`]. RunWithHandle(Id, Box<dyn FnOnce(WindowHandle<'_>) + Send>), @@ -159,6 +163,18 @@ pub enum Action { /// This enables mouse events for the window and stops mouse events /// from being passed to whatever is underneath. DisableMousePassthrough(Id), + + /// Set the minimum inner window size. + SetMinSize(Id, Option<Size>), + + /// Set the maximum inner window size. + SetMaxSize(Id, Option<Size>), + + /// Set the window to be resizable or not. + SetResizable(Id, bool), + + /// Set the window size increment. + SetResizeIncrements(Id, Option<Size>), } /// Subscribes to the frames of the window of the running application. @@ -264,11 +280,40 @@ pub fn drag<T>(id: Id) -> Task<T> { task::effect(crate::Action::Window(Action::Drag(id))) } +/// Begins resizing the window while the left mouse button is held. +pub fn drag_resize<T>(id: Id, direction: Direction) -> Task<T> { + task::effect(crate::Action::Window(Action::DragResize(id, direction))) +} + /// Resizes the window to the given logical dimensions. pub fn resize<T>(id: Id, new_size: Size) -> Task<T> { task::effect(crate::Action::Window(Action::Resize(id, new_size))) } +/// Set the window to be resizable or not. +pub fn set_resizable<T>(id: Id, resizable: bool) -> Task<T> { + task::effect(crate::Action::Window(Action::SetResizable(id, resizable))) +} + +/// Set the inner maximum size of the window. +pub fn set_max_size<T>(id: Id, size: Option<Size>) -> Task<T> { + task::effect(crate::Action::Window(Action::SetMaxSize(id, size))) +} + +/// Set the inner minimum size of the window. +pub fn set_min_size<T>(id: Id, size: Option<Size>) -> Task<T> { + task::effect(crate::Action::Window(Action::SetMinSize(id, size))) +} + +/// Set the window size increment. +/// +/// This is usually used by apps such as terminal emulators that need "blocky" resizing. +pub fn set_resize_increments<T>(id: Id, increments: Option<Size>) -> Task<T> { + task::effect(crate::Action::Window(Action::SetResizeIncrements( + id, increments, + ))) +} + /// Get the window's size in logical dimensions. pub fn get_size(id: Id) -> Task<Size> { task::oneshot(move |channel| { @@ -319,11 +364,6 @@ pub fn move_to<T>(id: Id, position: Point) -> Task<T> { task::effect(crate::Action::Window(Action::Move(id, position))) } -/// Changes the [`Mode`] of the window. -pub fn change_mode<T>(id: Id, mode: Mode) -> Task<T> { - task::effect(crate::Action::Window(Action::ChangeMode(id, mode))) -} - /// Gets the current [`Mode`] of the window. pub fn get_mode(id: Id) -> Task<Mode> { task::oneshot(move |channel| { @@ -331,6 +371,11 @@ pub fn get_mode(id: Id) -> Task<Mode> { }) } +/// Changes the [`Mode`] of the window. +pub fn set_mode<T>(id: Id, mode: Mode) -> Task<T> { + task::effect(crate::Action::Window(Action::SetMode(id, mode))) +} + /// Toggles the window to maximized or back. pub fn toggle_maximize<T>(id: Id) -> Task<T> { task::effect(crate::Action::Window(Action::ToggleMaximize(id))) @@ -368,8 +413,8 @@ pub fn gain_focus<T>(id: Id) -> Task<T> { } /// Changes the window [`Level`]. -pub fn change_level<T>(id: Id, level: Level) -> Task<T> { - task::effect(crate::Action::Window(Action::ChangeLevel(id, level))) +pub fn set_level<T>(id: Id, level: Level) -> Task<T> { + task::effect(crate::Action::Window(Action::SetLevel(id, level))) } /// Show the [system menu] at cursor position. @@ -388,8 +433,8 @@ pub fn get_raw_id<Message>(id: Id) -> Task<u64> { } /// Changes the [`Icon`] of the window. -pub fn change_icon<T>(id: Id, icon: Icon) -> Task<T> { - task::effect(crate::Action::Window(Action::ChangeIcon(id, icon))) +pub fn set_icon<T>(id: Id, icon: Icon) -> Task<T> { + task::effect(crate::Action::Window(Action::SetIcon(id, icon))) } /// Runs the given callback with the native window handle for the window with the given id. diff --git a/runtime/src/window/screenshot.rs b/runtime/src/window/screenshot.rs deleted file mode 100644 index d9adbc01..00000000 --- a/runtime/src/window/screenshot.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! Take screenshots of a window. -use crate::core::{Rectangle, Size}; - -use bytes::Bytes; -use std::fmt::{Debug, Formatter}; - -/// Data of a screenshot, captured with `window::screenshot()`. -/// -/// The `bytes` of this screenshot will always be ordered as `RGBA` in the `sRGB` color space. -#[derive(Clone)] -pub struct Screenshot { - /// The bytes of the [`Screenshot`]. - pub bytes: Bytes, - /// The size of the [`Screenshot`] in physical pixels. - pub size: Size<u32>, - /// The scale factor of the [`Screenshot`]. This can be useful when converting between widget - /// bounds (which are in logical pixels) to crop screenshots. - pub scale_factor: f64, -} - -impl Debug for Screenshot { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Screenshot: {{ \n bytes: {}\n scale: {}\n size: {:?} }}", - self.bytes.len(), - self.scale_factor, - self.size - ) - } -} - -impl Screenshot { - /// Creates a new [`Screenshot`]. - pub fn new( - bytes: impl Into<Bytes>, - size: Size<u32>, - scale_factor: f64, - ) -> Self { - Self { - bytes: bytes.into(), - size, - scale_factor, - } - } - - /// Crops a [`Screenshot`] to the provided `region`. This will always be relative to the - /// top-left corner of the [`Screenshot`]. - pub fn crop(&self, region: Rectangle<u32>) -> Result<Self, CropError> { - if region.width == 0 || region.height == 0 { - return Err(CropError::Zero); - } - - if region.x + region.width > self.size.width - || region.y + region.height > self.size.height - { - return Err(CropError::OutOfBounds); - } - - // Image is always RGBA8 = 4 bytes per pixel - const PIXEL_SIZE: usize = 4; - - let bytes_per_row = self.size.width as usize * PIXEL_SIZE; - let row_range = region.y as usize..(region.y + region.height) as usize; - let column_range = region.x as usize * PIXEL_SIZE - ..(region.x + region.width) as usize * PIXEL_SIZE; - - let chopped = self.bytes.chunks(bytes_per_row).enumerate().fold( - vec![], - |mut acc, (row, bytes)| { - if row_range.contains(&row) { - acc.extend(&bytes[column_range.clone()]); - } - - acc - }, - ); - - Ok(Self { - bytes: Bytes::from(chopped), - size: Size::new(region.width, region.height), - scale_factor: self.scale_factor, - }) - } -} - -impl AsRef<[u8]> for Screenshot { - fn as_ref(&self) -> &[u8] { - &self.bytes - } -} - -impl From<Screenshot> for Bytes { - fn from(screenshot: Screenshot) -> Self { - screenshot.bytes - } -} - -#[derive(Debug, thiserror::Error)] -/// Errors that can occur when cropping a [`Screenshot`]. -pub enum CropError { - #[error("The cropped region is out of bounds.")] - /// The cropped region's size is out of bounds. - OutOfBounds, - #[error("The cropped region is not visible.")] - /// The cropped region's size is zero. - Zero, -} |