From 90c20ac46b72b6d8f735f7efd283b9d1dfecfb9d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Jan 2022 10:47:56 +0700 Subject: Draft `Responsive` widget --- native/src/lib.rs | 2 +- native/src/widget.rs | 3 + native/src/widget/responsive.rs | 188 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 native/src/widget/responsive.rs (limited to 'native') diff --git a/native/src/lib.rs b/native/src/lib.rs index 4753bba2..4211995f 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -31,7 +31,7 @@ #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] diff --git a/native/src/widget.rs b/native/src/widget.rs index caa3f0bc..d1619bf3 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -20,6 +20,7 @@ pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; +pub mod responsive; pub mod row; pub mod rule; pub mod scrollable; @@ -50,6 +51,8 @@ pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] +pub use responsive::Responsive; +#[doc(no_inline)] pub use row::Row; #[doc(no_inline)] pub use rule::Rule; diff --git a/native/src/widget/responsive.rs b/native/src/widget/responsive.rs new file mode 100644 index 00000000..0cb85d45 --- /dev/null +++ b/native/src/widget/responsive.rs @@ -0,0 +1,188 @@ +use crate::event::{self, Event}; +use crate::layout::{self, Layout}; +use crate::renderer; +use crate::{ + Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Size, Widget, +}; + +use std::cell::RefCell; +use std::hash::Hasher as _; + +#[derive(Debug, Clone, Default)] +pub struct State { + last_size: Option, + last_layout: layout::Node, + last_layout_hash: u64, +} + +impl State { + pub fn new() -> State { + State::default() + } +} + +#[allow(missing_debug_implementations)] +pub struct Responsive<'a, Message, Renderer>( + RefCell>, +); + +impl<'a, Message, Renderer> Responsive<'a, Message, Renderer> { + pub fn new( + state: &'a mut State, + view: impl FnOnce(Size) -> Element<'a, Message, Renderer> + 'a, + ) -> Self { + Self(RefCell::new(Internal { + state, + content: Content::Pending(Some(Box::new(view))), + })) + } +} + +impl<'a, Message, Renderer> Widget + for Responsive<'a, Message, Renderer> +where + Renderer: crate::Renderer, +{ + fn width(&self) -> Length { + Length::Fill + } + + fn height(&self) -> Length { + Length::Fill + } + + fn hash_layout(&self, _hasher: &mut Hasher) {} + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let size = limits.max(); + + self.0.borrow_mut().state.last_size = Some(size); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + use std::ops::DerefMut; + + let mut internal = self.0.borrow_mut(); + + let Internal { content, state } = internal.deref_mut(); + + let content = content.resolve(state, renderer); + + let content_layout = Layout::with_offset( + layout.position() - Point::ORIGIN, + &state.last_layout, + ); + + content.on_event( + event, + content_layout, + cursor_position, + renderer, + clipboard, + shell, + ) + } + + fn draw( + &self, + renderer: &mut Renderer, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + ) { + use std::ops::DerefMut; + + let mut internal = self.0.borrow_mut(); + + let Internal { content, state } = internal.deref_mut(); + + let content = content.resolve(state, renderer); + + let content_layout = Layout::with_offset( + layout.position() - Point::ORIGIN, + &state.last_layout, + ); + + content.draw(renderer, style, content_layout, cursor_position, viewport) + } +} + +struct Internal<'a, Message, Renderer> { + state: &'a mut State, + content: Content<'a, Message, Renderer>, +} + +enum Content<'a, Message, Renderer> { + Pending( + Option Element<'a, Message, Renderer> + 'a>>, + ), + Ready(Element<'a, Message, Renderer>), +} + +impl<'a, Message, Renderer> Content<'a, Message, Renderer> +where + Renderer: crate::Renderer, +{ + fn resolve( + &mut self, + state: &mut State, + renderer: &Renderer, + ) -> &mut Element<'a, Message, Renderer> { + match self { + Content::Ready(element) => element, + Content::Pending(view) => { + let element = + view.take().unwrap()(state.last_size.unwrap_or(Size::ZERO)); + + let new_layout_hash = { + let mut hasher = Hasher::default(); + element.hash_layout(&mut hasher); + + hasher.finish() + }; + + if new_layout_hash != state.last_layout_hash { + state.last_layout = element.layout( + renderer, + &layout::Limits::new( + Size::ZERO, + state.last_size.unwrap_or(Size::ZERO), + ), + ); + + state.last_layout_hash = new_layout_hash; + } + + *self = Content::Ready(element); + + self.resolve(state, renderer) + } + } + } +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Renderer: crate::Renderer + 'a, + Message: 'a, +{ + fn from(responsive: Responsive<'a, Message, Renderer>) -> Self { + Self::new(responsive) + } +} -- cgit From 6ab4611a6eec9c4bb4ca1ff1bb580bb7edf49add Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Jan 2022 13:47:43 +0700 Subject: Invalidate widget tree from `Responsive` widget ... by introducing a new `invalidate_widgets` method to `Shell` --- native/src/lib.rs | 4 ++-- native/src/program/state.rs | 12 +++++------ native/src/shell.rs | 20 +++++++++++++++++- native/src/user_interface.rs | 45 ++++++++++++++++++++++++++++++----------- native/src/widget/responsive.rs | 4 ++++ 5 files changed, 63 insertions(+), 22 deletions(-) (limited to 'native') diff --git a/native/src/lib.rs b/native/src/lib.rs index 4211995f..c527a69a 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -50,6 +50,7 @@ pub mod subscription; pub mod svg; pub mod text; pub mod touch; +pub mod user_interface; pub mod widget; pub mod window; @@ -57,7 +58,6 @@ mod element; mod hasher; mod runtime; mod shell; -mod user_interface; // We disable debug capabilities on release builds unless the `debug` feature // is explicitly enabled. @@ -91,5 +91,5 @@ pub use renderer::Renderer; pub use runtime::Runtime; pub use shell::Shell; pub use subscription::Subscription; -pub use user_interface::{Cache, UserInterface}; +pub use user_interface::UserInterface; pub use widget::Widget; diff --git a/native/src/program/state.rs b/native/src/program/state.rs index 26c0eb21..cb87a628 100644 --- a/native/src/program/state.rs +++ b/native/src/program/state.rs @@ -1,8 +1,6 @@ use crate::mouse; -use crate::{ - Cache, Clipboard, Command, Debug, Event, Point, Program, Size, - UserInterface, -}; +use crate::user_interface::{self, UserInterface}; +use crate::{Clipboard, Command, Debug, Event, Point, Program, Size}; /// The execution state of a [`Program`]. It leverages caching, event /// processing, and rendering primitive storage. @@ -12,7 +10,7 @@ where P: Program + 'static, { program: P, - cache: Option, + cache: Option, queued_events: Vec, queued_messages: Vec, mouse_interaction: mouse::Interaction, @@ -32,7 +30,7 @@ where ) -> Self { let user_interface = build_user_interface( &mut program, - Cache::default(), + user_interface::Cache::default(), renderer, bounds, debug, @@ -161,7 +159,7 @@ where fn build_user_interface<'a, P: Program>( program: &'a mut P, - cache: Cache, + cache: user_interface::Cache, renderer: &mut P::Renderer, size: Size, debug: &mut Debug, diff --git a/native/src/shell.rs b/native/src/shell.rs index e916f52d..4a0aa9c6 100644 --- a/native/src/shell.rs +++ b/native/src/shell.rs @@ -8,6 +8,7 @@ pub struct Shell<'a, Message> { messages: &'a mut Vec, is_layout_invalid: bool, + are_widgets_invalid: bool, } impl<'a, Message> Shell<'a, Message> { @@ -16,12 +17,13 @@ impl<'a, Message> Shell<'a, Message> { Self { messages, is_layout_invalid: false, + are_widgets_invalid: false, } } /// Triggers the given function if the layout is invalid, cleaning it in the /// process. - pub fn with_invalid_layout(&mut self, f: impl FnOnce()) { + pub fn revalidate_layout(&mut self, f: impl FnOnce()) { if self.is_layout_invalid { self.is_layout_invalid = false; @@ -41,6 +43,13 @@ impl<'a, Message> Shell<'a, Message> { self.is_layout_invalid = true; } + /// Invalidates the current application widgets. + /// + /// The shell will rebuild and relayout the widget tree. + pub fn invalidate_widgets(&mut self) { + self.are_widgets_invalid = true; + } + /// Merges the current [`Shell`] with another one by applying the given /// function to the messages of the latter. /// @@ -50,5 +59,14 @@ impl<'a, Message> Shell<'a, Message> { self.is_layout_invalid = self.is_layout_invalid || other.is_layout_invalid; + + self.are_widgets_invalid = + self.are_widgets_invalid || other.are_widgets_invalid; + } + + /// Returns whether the widgets of the current application have been + /// invalidated. + pub fn are_widgets_invalid(&self) -> bool { + self.are_widgets_invalid } } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 40f7a204..1c78b754 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -42,7 +42,8 @@ where /// is naive way to set up our application loop: /// /// ```no_run - /// use iced_native::{UserInterface, Cache, Size}; + /// use iced_native::Size; + /// use iced_native::user_interface::{self, UserInterface}; /// use iced_wgpu::Renderer; /// /// # mod iced_wgpu { @@ -61,7 +62,7 @@ where /// # } /// // Initialization /// let mut counter = Counter::new(); - /// let mut cache = Cache::new(); + /// let mut cache = user_interface::Cache::new(); /// let mut renderer = Renderer::new(); /// let mut window_size = Size::new(1024.0, 768.0); /// @@ -136,7 +137,8 @@ where /// completing [the previous example](#example): /// /// ```no_run - /// use iced_native::{clipboard, UserInterface, Cache, Size, Point}; + /// use iced_native::{clipboard, Size, Point}; + /// use iced_native::user_interface::{self, UserInterface}; /// use iced_wgpu::Renderer; /// /// # mod iced_wgpu { @@ -155,7 +157,7 @@ where /// # pub fn update(&mut self, message: ()) {} /// # } /// let mut counter = Counter::new(); - /// let mut cache = Cache::new(); + /// let mut cache = user_interface::Cache::new(); /// let mut renderer = Renderer::new(); /// let mut window_size = Size::new(1024.0, 768.0); /// let mut cursor_position = Point::default(); @@ -176,7 +178,7 @@ where /// ); /// /// // Update the user interface - /// let event_statuses = user_interface.update( + /// let (state, event_statuses) = user_interface.update( /// &events, /// cursor_position, /// &mut renderer, @@ -199,7 +201,9 @@ where renderer: &mut Renderer, clipboard: &mut dyn Clipboard, messages: &mut Vec, - ) -> Vec { + ) -> (State, Vec) { + let mut state = State::Updated; + let (base_cursor, overlay_statuses) = if let Some(mut overlay) = self.root.overlay(Layout::new(&self.base.layout)) { @@ -227,7 +231,7 @@ where &mut shell, ); - shell.with_invalid_layout(|| { + shell.revalidate_layout(|| { layer = Self::overlay_layer( None, bounds, @@ -236,6 +240,10 @@ where ); }); + if shell.are_widgets_invalid() { + state = State::Outdated; + } + event_status }) .collect(); @@ -255,7 +263,7 @@ where (cursor_position, vec![event::Status::Ignored; events.len()]) }; - events + let event_statuses = events .iter() .cloned() .zip(overlay_statuses.into_iter()) @@ -271,7 +279,7 @@ where &mut shell, ); - shell.with_invalid_layout(|| { + shell.revalidate_layout(|| { let hash = { let hasher = &mut crate::Hasher::default(); self.root.hash_layout(hasher); @@ -288,9 +296,15 @@ where self.overlay = None; }); + if shell.are_widgets_invalid() { + state = State::Outdated; + } + event_status.merge(overlay_status) }) - .collect() + .collect(); + + (state, event_statuses) } /// Draws the [`UserInterface`] with the provided [`Renderer`]. @@ -306,7 +320,8 @@ where /// [completing the last example](#example-1): /// /// ```no_run - /// use iced_native::{clipboard, UserInterface, Cache, Size, Point}; + /// use iced_native::{clipboard, Size, Point}; + /// use iced_native::user_interface::{self, UserInterface}; /// use iced_wgpu::Renderer; /// /// # mod iced_wgpu { @@ -325,7 +340,7 @@ where /// # pub fn update(&mut self, message: ()) {} /// # } /// let mut counter = Counter::new(); - /// let mut cache = Cache::new(); + /// let mut cache = user_interface::Cache::new(); /// let mut renderer = Renderer::new(); /// let mut window_size = Size::new(1024.0, 768.0); /// let mut cursor_position = Point::default(); @@ -548,3 +563,9 @@ impl Default for Cache { Cache::new() } } + +#[derive(Debug, Clone, Copy)] +pub enum State { + Outdated, + Updated, +} diff --git a/native/src/widget/responsive.rs b/native/src/widget/responsive.rs index 0cb85d45..becaa980 100644 --- a/native/src/widget/responsive.rs +++ b/native/src/widget/responsive.rs @@ -80,6 +80,10 @@ where let Internal { content, state } = internal.deref_mut(); + if state.last_size != Some(state.last_layout.size()) { + shell.invalidate_widgets(); + } + let content = content.resolve(state, renderer); let content_layout = Layout::with_offset( -- cgit From 810e086728e938d1d12758c7b486c1e371127349 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Jan 2022 14:12:28 +0700 Subject: Introduce `Renderer` argument to `mouse_interaction` and `on_event` --- native/src/element.rs | 37 +++++++++++++++++------- native/src/overlay.rs | 1 + native/src/overlay/element.rs | 18 +++++++++--- native/src/overlay/menu.rs | 10 +++++-- native/src/user_interface.rs | 48 +++++++++++++++++--------------- native/src/widget.rs | 2 ++ native/src/widget/button.rs | 5 +++- native/src/widget/checkbox.rs | 1 + native/src/widget/column.rs | 7 ++++- native/src/widget/container.rs | 6 +++- native/src/widget/image/viewer.rs | 1 + native/src/widget/pane_grid.rs | 11 ++++++-- native/src/widget/pane_grid/content.rs | 11 +++++--- native/src/widget/pane_grid/title_bar.rs | 14 ++++++++-- native/src/widget/pick_list.rs | 2 ++ native/src/widget/radio.rs | 1 + native/src/widget/row.rs | 7 ++++- native/src/widget/scrollable.rs | 5 +++- native/src/widget/slider.rs | 1 + native/src/widget/text_input.rs | 1 + native/src/widget/toggler.rs | 1 + native/src/widget/tooltip.rs | 9 ++++-- 22 files changed, 145 insertions(+), 54 deletions(-) (limited to 'native') diff --git a/native/src/element.rs b/native/src/element.rs index 7e806b08..6afa3f62 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -259,9 +259,14 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { - self.widget - .mouse_interaction(layout, cursor_position, viewport) + self.widget.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) } /// Computes the _layout_ hash of the [`Element`]. @@ -273,8 +278,9 @@ where pub fn overlay<'b>( &'b mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { - self.widget.overlay(layout) + self.widget.overlay(layout, renderer) } } @@ -363,9 +369,14 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { - self.widget - .mouse_interaction(layout, cursor_position, viewport) + self.widget.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) } fn hash_layout(&self, state: &mut Hasher) { @@ -375,11 +386,12 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { let mapper = &self.mapper; self.widget - .overlay(layout) + .overlay(layout, renderer) .map(move |overlay| overlay.map(mapper)) } } @@ -482,10 +494,14 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { - self.element - .widget - .mouse_interaction(layout, cursor_position, viewport) + self.element.widget.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) } fn hash_layout(&self, state: &mut Hasher) { @@ -495,7 +511,8 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { - self.element.overlay(layout) + self.element.overlay(layout, renderer) } } diff --git a/native/src/overlay.rs b/native/src/overlay.rs index e66d421a..d4b641af 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -84,6 +84,7 @@ where _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { mouse::Interaction::Idle } diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index 70cb417e..e7621600 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -80,9 +80,14 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { - self.overlay - .mouse_interaction(layout, cursor_position, viewport) + self.overlay.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) } /// Draws the [`Element`] and its children using the given [`Layout`]. @@ -160,9 +165,14 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { - self.content - .mouse_interaction(layout, cursor_position, viewport) + self.content.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) } fn draw( diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index c0282aae..2deef551 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -239,9 +239,14 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { - self.container - .mouse_interaction(layout, cursor_position, viewport) + self.container.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) } fn draw( @@ -392,6 +397,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { let is_mouse_over = layout.bounds().contains(cursor_position); diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 1c78b754..b6bed5ed 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -205,7 +205,7 @@ where let mut state = State::Updated; let (base_cursor, overlay_statuses) = if let Some(mut overlay) = - self.root.overlay(Layout::new(&self.base.layout)) + self.root.overlay(Layout::new(&self.base.layout), renderer) { let bounds = self.bounds; @@ -391,7 +391,7 @@ where let viewport = Rectangle::with_size(self.bounds); if let Some(mut overlay) = - self.root.overlay(Layout::new(&self.base.layout)) + self.root.overlay(Layout::new(&self.base.layout), renderer) { let layer = Self::overlay_layer( self.overlay.take(), @@ -432,6 +432,7 @@ where Layout::new(&self.base.layout), cursor_position, &viewport, + renderer, ); let Self { @@ -449,30 +450,33 @@ where overlay .as_ref() .and_then(|layer| { - root.overlay(Layout::new(&base.layout)).map(|overlay| { - let overlay_interaction = overlay.mouse_interaction( - Layout::new(&layer.layout), - cursor_position, - &viewport, - ); - - let overlay_bounds = layer.layout.bounds(); - - renderer.with_layer(overlay_bounds, |renderer| { - overlay.draw( - renderer, - &renderer::Style::default(), + root.overlay(Layout::new(&base.layout), renderer).map( + |overlay| { + let overlay_interaction = overlay.mouse_interaction( Layout::new(&layer.layout), cursor_position, + &viewport, + renderer, ); - }); - if overlay_bounds.contains(cursor_position) { - overlay_interaction - } else { - base_interaction - } - }) + let overlay_bounds = layer.layout.bounds(); + + renderer.with_layer(overlay_bounds, |renderer| { + overlay.draw( + renderer, + &renderer::Style::default(), + Layout::new(&layer.layout), + cursor_position, + ); + }); + + if overlay_bounds.contains(cursor_position) { + overlay_interaction + } else { + base_interaction + } + }, + ) }) .unwrap_or(base_interaction) } diff --git a/native/src/widget.rs b/native/src/widget.rs index d1619bf3..30405346 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -179,6 +179,7 @@ where _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { mouse::Interaction::Idle } @@ -187,6 +188,7 @@ where fn overlay( &mut self, _layout: Layout<'_>, + _renderer: &Renderer, ) -> Option> { None } diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 686289e4..b4a3adc3 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -253,6 +253,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { let is_mouse_over = layout.bounds().contains(cursor_position); let is_disabled = self.on_press.is_none(); @@ -343,8 +344,10 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { - self.content.overlay(layout.children().next().unwrap()) + self.content + .overlay(layout.children().next().unwrap(), renderer) } } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 95856928..a314140c 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -195,6 +195,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { if layout.bounds().contains(cursor_position) { mouse::Interaction::Pointer diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 4c43c8c8..66ed5e23 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -169,6 +169,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { self.children .iter() @@ -178,6 +179,7 @@ where layout, cursor_position, viewport, + renderer, ) }) .max() @@ -217,11 +219,14 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { self.children .iter_mut() .zip(layout.children()) - .filter_map(|(child, layout)| child.widget.overlay(layout)) + .filter_map(|(child, layout)| { + child.widget.overlay(layout, renderer) + }) .next() } } diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 0c291788..4444732a 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -184,11 +184,13 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { self.content.widget.mouse_interaction( layout.children().next().unwrap(), cursor_position, viewport, + renderer, ) } @@ -235,8 +237,10 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { - self.content.overlay(layout.children().next().unwrap()) + self.content + .overlay(layout.children().next().unwrap(), renderer) } } diff --git a/native/src/widget/image/viewer.rs b/native/src/widget/image/viewer.rs index 645ce852..486d5fa5 100644 --- a/native/src/widget/image/viewer.rs +++ b/native/src/widget/image/viewer.rs @@ -286,6 +286,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { let bounds = layout.bounds(); let is_mouse_over = bounds.contains(cursor_position); diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index b3d8b819..5f293fa6 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -472,6 +472,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { if self.state.picked_pane().is_some() { return mouse::Interaction::Grab; @@ -511,7 +512,12 @@ where .iter() .zip(layout.children()) .map(|((_pane, content), layout)| { - content.mouse_interaction(layout, cursor_position, viewport) + content.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) }) .max() .unwrap_or_default() @@ -678,11 +684,12 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { self.elements .iter_mut() .zip(layout.children()) - .filter_map(|((_, pane), layout)| pane.overlay(layout)) + .filter_map(|((_, pane), layout)| pane.overlay(layout, renderer)) .next() } } diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index 533827b7..6d12aa2d 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -205,6 +205,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { let (body_layout, title_bar_interaction) = if let Some(title_bar) = &self.title_bar { @@ -222,6 +223,7 @@ where title_bar_layout, cursor_position, viewport, + renderer, ); (children.next().unwrap(), mouse_interaction) @@ -230,7 +232,7 @@ where }; self.body - .mouse_interaction(body_layout, cursor_position, viewport) + .mouse_interaction(body_layout, cursor_position, viewport, renderer) .max(title_bar_interaction) } @@ -245,17 +247,18 @@ where pub(crate) fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { if let Some(title_bar) = self.title_bar.as_mut() { let mut children = layout.children(); let title_bar_layout = children.next()?; - match title_bar.overlay(title_bar_layout) { + match title_bar.overlay(title_bar_layout, renderer) { Some(overlay) => Some(overlay), - None => self.body.overlay(children.next()?), + None => self.body.overlay(children.next()?, renderer), } } else { - self.body.overlay(layout) + self.body.overlay(layout, renderer) } } } diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 353e1ce9..ea9ebfc4 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -258,6 +258,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { let mut children = layout.children(); let padded = children.next().unwrap(); @@ -269,13 +270,19 @@ where title_layout, cursor_position, viewport, + renderer, ); if let Some(controls) = &self.controls { let controls_layout = children.next().unwrap(); controls - .mouse_interaction(controls_layout, cursor_position, viewport) + .mouse_interaction( + controls_layout, + cursor_position, + viewport, + renderer, + ) .max(title_interaction) } else { title_interaction @@ -285,6 +292,7 @@ where pub(crate) fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { let mut children = layout.children(); let padded = children.next()?; @@ -296,11 +304,11 @@ where content, controls, .. } = self; - content.overlay(title_layout).or_else(move || { + content.overlay(title_layout, renderer).or_else(move || { controls.as_mut().and_then(|controls| { let controls_layout = children.next()?; - controls.overlay(controls_layout) + controls.overlay(controls_layout, renderer) }) }) } diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 1f79e07a..a200fb13 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -331,6 +331,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { let bounds = layout.bounds(); let is_mouse_over = bounds.contains(cursor_position); @@ -415,6 +416,7 @@ where fn overlay( &mut self, layout: Layout<'_>, + _renderer: &Renderer, ) -> Option> { if *self.is_open { let bounds = layout.bounds(); diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 1dbe7cf5..c6cc46ed 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -209,6 +209,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { if layout.bounds().contains(cursor_position) { mouse::Interaction::Pointer diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index a0174f79..c423d31f 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -168,6 +168,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { self.children .iter() @@ -177,6 +178,7 @@ where layout, cursor_position, viewport, + renderer, ) }) .max() @@ -216,11 +218,14 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { self.children .iter_mut() .zip(layout.children()) - .filter_map(|(child, layout)| child.widget.overlay(layout)) + .filter_map(|(child, layout)| { + child.widget.overlay(layout, renderer) + }) .next() } } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 3ef737c0..4b005c6b 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -427,6 +427,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { let bounds = layout.bounds(); let content_layout = layout.children().next().unwrap(); @@ -457,6 +458,7 @@ where y: bounds.y + offset as f32, ..bounds }, + renderer, ) } } @@ -581,11 +583,12 @@ where fn overlay( &mut self, layout: Layout<'_>, + renderer: &Renderer, ) -> Option> { let Self { content, state, .. } = self; content - .overlay(layout.children().next().unwrap()) + .overlay(layout.children().next().unwrap(), renderer) .map(|overlay| { let bounds = layout.bounds(); let content_layout = layout.children().next().unwrap(); diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 65632a5c..917bfc14 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -361,6 +361,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { let bounds = layout.bounds(); let is_mouse_over = bounds.contains(cursor_position); diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index f06b6aa6..03adeb12 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -764,6 +764,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { if layout.bounds().contains(cursor_position) { mouse::Interaction::Text diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 7eca58d2..002e0a4f 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -196,6 +196,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, + _renderer: &Renderer, ) -> mouse::Interaction { if layout.bounds().contains(cursor_position) { mouse::Interaction::Pointer diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs index 79a57824..f6630864 100644 --- a/native/src/widget/tooltip.rs +++ b/native/src/widget/tooltip.rs @@ -147,9 +147,14 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, + renderer: &Renderer, ) -> mouse::Interaction { - self.content - .mouse_interaction(layout, cursor_position, viewport) + self.content.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) } fn draw( -- cgit From 2e255b7b91264cf1e53e89dfdefd95270957b2e2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Jan 2022 14:21:38 +0700 Subject: Implement `Widget::mouse_interaction` for `Responsive` --- native/src/widget/responsive.rs | 62 +++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 21 deletions(-) (limited to 'native') diff --git a/native/src/widget/responsive.rs b/native/src/widget/responsive.rs index becaa980..fd9e490a 100644 --- a/native/src/widget/responsive.rs +++ b/native/src/widget/responsive.rs @@ -1,5 +1,6 @@ use crate::event::{self, Event}; use crate::layout::{self, Layout}; +use crate::mouse; use crate::renderer; use crate::{ Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Size, Widget, @@ -74,22 +75,13 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { - use std::ops::DerefMut; - let mut internal = self.0.borrow_mut(); - let Internal { content, state } = internal.deref_mut(); - - if state.last_size != Some(state.last_layout.size()) { + if internal.state.last_size != Some(internal.state.last_layout.size()) { shell.invalidate_widgets(); } - let content = content.resolve(state, renderer); - - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - &state.last_layout, - ); + let (content, content_layout) = internal.content(layout, renderer); content.on_event( event, @@ -109,20 +101,28 @@ where cursor_position: Point, viewport: &Rectangle, ) { - use std::ops::DerefMut; - let mut internal = self.0.borrow_mut(); + let (content, content_layout) = internal.content(layout, renderer); - let Internal { content, state } = internal.deref_mut(); - - let content = content.resolve(state, renderer); + content.draw(renderer, style, content_layout, cursor_position, viewport) + } - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - &state.last_layout, - ); + fn mouse_interaction( + &self, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + let mut internal = self.0.borrow_mut(); + let (content, content_layout) = internal.content(layout, renderer); - content.draw(renderer, style, content_layout, cursor_position, viewport) + content.mouse_interaction( + content_layout, + cursor_position, + viewport, + renderer, + ) } } @@ -131,6 +131,26 @@ struct Internal<'a, Message, Renderer> { content: Content<'a, Message, Renderer>, } +impl<'a, Message, Renderer> Internal<'a, Message, Renderer> +where + Renderer: crate::Renderer, +{ + fn content( + &mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> (&mut Element<'a, Message, Renderer>, Layout<'_>) { + let content = self.content.resolve(&mut self.state, renderer); + + let content_layout = Layout::with_offset( + layout.position() - Point::ORIGIN, + &self.state.last_layout, + ); + + (content, content_layout) + } +} + enum Content<'a, Message, Renderer> { Pending( Option Element<'a, Message, Renderer> + 'a>>, -- cgit From 870d651f35c4dad12c805951fca70213816983de Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 12 Jan 2022 11:15:05 +0700 Subject: Implement `Widget::overlay` for `Responsive` widget --- native/src/widget.rs | 3 - native/src/widget/responsive.rs | 212 ---------------------------------------- 2 files changed, 215 deletions(-) delete mode 100644 native/src/widget/responsive.rs (limited to 'native') diff --git a/native/src/widget.rs b/native/src/widget.rs index 30405346..acd2e580 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -20,7 +20,6 @@ pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; -pub mod responsive; pub mod row; pub mod rule; pub mod scrollable; @@ -51,8 +50,6 @@ pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] -pub use responsive::Responsive; -#[doc(no_inline)] pub use row::Row; #[doc(no_inline)] pub use rule::Rule; diff --git a/native/src/widget/responsive.rs b/native/src/widget/responsive.rs deleted file mode 100644 index fd9e490a..00000000 --- a/native/src/widget/responsive.rs +++ /dev/null @@ -1,212 +0,0 @@ -use crate::event::{self, Event}; -use crate::layout::{self, Layout}; -use crate::mouse; -use crate::renderer; -use crate::{ - Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Size, Widget, -}; - -use std::cell::RefCell; -use std::hash::Hasher as _; - -#[derive(Debug, Clone, Default)] -pub struct State { - last_size: Option, - last_layout: layout::Node, - last_layout_hash: u64, -} - -impl State { - pub fn new() -> State { - State::default() - } -} - -#[allow(missing_debug_implementations)] -pub struct Responsive<'a, Message, Renderer>( - RefCell>, -); - -impl<'a, Message, Renderer> Responsive<'a, Message, Renderer> { - pub fn new( - state: &'a mut State, - view: impl FnOnce(Size) -> Element<'a, Message, Renderer> + 'a, - ) -> Self { - Self(RefCell::new(Internal { - state, - content: Content::Pending(Some(Box::new(view))), - })) - } -} - -impl<'a, Message, Renderer> Widget - for Responsive<'a, Message, Renderer> -where - Renderer: crate::Renderer, -{ - fn width(&self) -> Length { - Length::Fill - } - - fn height(&self) -> Length { - Length::Fill - } - - fn hash_layout(&self, _hasher: &mut Hasher) {} - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let size = limits.max(); - - self.0.borrow_mut().state.last_size = Some(size); - - layout::Node::new(size) - } - - fn on_event( - &mut self, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let mut internal = self.0.borrow_mut(); - - if internal.state.last_size != Some(internal.state.last_layout.size()) { - shell.invalidate_widgets(); - } - - let (content, content_layout) = internal.content(layout, renderer); - - content.on_event( - event, - content_layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - renderer: &mut Renderer, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - let mut internal = self.0.borrow_mut(); - let (content, content_layout) = internal.content(layout, renderer); - - content.draw(renderer, style, content_layout, cursor_position, viewport) - } - - fn mouse_interaction( - &self, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - let mut internal = self.0.borrow_mut(); - let (content, content_layout) = internal.content(layout, renderer); - - content.mouse_interaction( - content_layout, - cursor_position, - viewport, - renderer, - ) - } -} - -struct Internal<'a, Message, Renderer> { - state: &'a mut State, - content: Content<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Internal<'a, Message, Renderer> -where - Renderer: crate::Renderer, -{ - fn content( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - ) -> (&mut Element<'a, Message, Renderer>, Layout<'_>) { - let content = self.content.resolve(&mut self.state, renderer); - - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - &self.state.last_layout, - ); - - (content, content_layout) - } -} - -enum Content<'a, Message, Renderer> { - Pending( - Option Element<'a, Message, Renderer> + 'a>>, - ), - Ready(Element<'a, Message, Renderer>), -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: crate::Renderer, -{ - fn resolve( - &mut self, - state: &mut State, - renderer: &Renderer, - ) -> &mut Element<'a, Message, Renderer> { - match self { - Content::Ready(element) => element, - Content::Pending(view) => { - let element = - view.take().unwrap()(state.last_size.unwrap_or(Size::ZERO)); - - let new_layout_hash = { - let mut hasher = Hasher::default(); - element.hash_layout(&mut hasher); - - hasher.finish() - }; - - if new_layout_hash != state.last_layout_hash { - state.last_layout = element.layout( - renderer, - &layout::Limits::new( - Size::ZERO, - state.last_size.unwrap_or(Size::ZERO), - ), - ); - - state.last_layout_hash = new_layout_hash; - } - - *self = Content::Ready(element); - - self.resolve(state, renderer) - } - } - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Renderer: crate::Renderer + 'a, - Message: 'a, -{ - fn from(responsive: Responsive<'a, Message, Renderer>) -> Self { - Self::new(responsive) - } -} -- cgit From 7ef0259a2cb7116496f438266a00b73d482eff8a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 12 Jan 2022 11:22:57 +0700 Subject: Write missing docs for `user_interface` module --- native/src/lib.rs | 2 +- native/src/user_interface.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'native') diff --git a/native/src/lib.rs b/native/src/lib.rs index c527a69a..a5526e6d 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -31,7 +31,7 @@ #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] -//#![deny(missing_docs)] +#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index b6bed5ed..4b1194be 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -1,3 +1,4 @@ +//! Implement your own event loop to drive a user interface. use crate::event::{self, Event}; use crate::layout; use crate::mouse; @@ -568,8 +569,13 @@ impl Default for Cache { } } +/// The resulting state after updating a [`UserInterface`]. #[derive(Debug, Clone, Copy)] pub enum State { + /// The [`UserInterface`] is outdated and needs to be rebuilt. Outdated, + + /// The [`UserInterface`] is up-to-date and can be reused without + /// rebuilding. Updated, } -- cgit