diff options
author | 2022-01-11 10:47:56 +0700 | |
---|---|---|
committer | 2022-01-11 10:47:56 +0700 | |
commit | 90c20ac46b72b6d8f735f7efd283b9d1dfecfb9d (patch) | |
tree | 3fd9ede535c633ef02156d6f82cbdbcd6a17bde0 | |
parent | 1a31aefab401712e44cd613fc1337ab90579d926 (diff) | |
download | iced-90c20ac46b72b6d8f735f7efd283b9d1dfecfb9d.tar.gz iced-90c20ac46b72b6d8f735f7efd283b9d1dfecfb9d.tar.bz2 iced-90c20ac46b72b6d8f735f7efd283b9d1dfecfb9d.zip |
Draft `Responsive` widget
-rw-r--r-- | examples/pane_grid/src/main.rs | 47 | ||||
-rw-r--r-- | glow/src/lib.rs | 2 | ||||
-rw-r--r-- | glow/src/widget.rs | 3 | ||||
-rw-r--r-- | glow/src/widget/responsive.rs | 6 | ||||
-rw-r--r-- | native/src/lib.rs | 2 | ||||
-rw-r--r-- | native/src/widget.rs | 3 | ||||
-rw-r--r-- | native/src/widget/responsive.rs | 188 | ||||
-rw-r--r-- | src/widget.rs | 9 | ||||
-rw-r--r-- | wgpu/src/lib.rs | 2 | ||||
-rw-r--r-- | wgpu/src/widget.rs | 3 | ||||
-rw-r--r-- | wgpu/src/widget/responsive.rs | 6 |
11 files changed, 248 insertions, 23 deletions
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 8225e9e7..59e5f3f9 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -1,7 +1,13 @@ +use iced::alignment::{self, Alignment}; +use iced::button::{self, Button}; +use iced::executor; +use iced::keyboard; +use iced::pane_grid::{self, PaneGrid}; +use iced::responsive::{self, Responsive}; +use iced::scrollable::{self, Scrollable}; use iced::{ - alignment, button, executor, keyboard, pane_grid, scrollable, Alignment, - Application, Button, Color, Column, Command, Container, Element, Length, - PaneGrid, Row, Scrollable, Settings, Subscription, Text, + Application, Color, Column, Command, Container, Element, Length, Row, + Settings, Size, Subscription, Text, }; use iced_native::{event, subscription, Event}; @@ -154,17 +160,24 @@ impl Application for Example { let pane_grid = PaneGrid::new(&mut self.panes, |id, pane| { let is_focused = focus == Some(id); - let text = if pane.is_pinned { "Unpin" } else { "Pin" }; - let pin_button = - Button::new(&mut pane.pin_button, Text::new(text).size(14)) - .on_press(Message::TogglePin(id)) - .style(style::Button::Pin) - .padding(3); + let Pane { + responsive, + pin_button, + is_pinned, + content, + .. + } = pane; + + let text = if *is_pinned { "Unpin" } else { "Pin" }; + let pin_button = Button::new(pin_button, Text::new(text).size(14)) + .on_press(Message::TogglePin(id)) + .style(style::Button::Pin) + .padding(3); let title = Row::with_children(vec![ pin_button.into(), Text::new("Pane").into(), - Text::new(pane.content.id.to_string()) + Text::new(content.id.to_string()) .color(if is_focused { PANE_ID_COLOR_FOCUSED } else { @@ -175,7 +188,7 @@ impl Application for Example { .spacing(5); let title_bar = pane_grid::TitleBar::new(title) - .controls(pane.controls.view(id, total_panes, pane.is_pinned)) + .controls(pane.controls.view(id, total_panes, *is_pinned)) .padding(10) .style(if is_focused { style::TitleBar::Focused @@ -183,11 +196,9 @@ impl Application for Example { style::TitleBar::Active }); - pane_grid::Content::new(pane.content.view( - id, - total_panes, - pane.is_pinned, - )) + pane_grid::Content::new(Responsive::new(responsive, move |size| { + content.view(id, total_panes, *is_pinned, size) + })) .title_bar(title_bar) .style(if is_focused { style::Pane::Focused @@ -242,6 +253,7 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> { } struct Pane { + pub responsive: responsive::State, pub is_pinned: bool, pub pin_button: button::State, pub content: Content, @@ -263,6 +275,7 @@ struct Controls { impl Pane { fn new(id: usize) -> Self { Self { + responsive: responsive::State::new(), is_pinned: false, pin_button: button::State::new(), content: Content::new(id), @@ -286,6 +299,7 @@ impl Content { pane: pane_grid::Pane, total_panes: usize, is_pinned: bool, + size: Size, ) -> Element<Message> { let Content { scroll, @@ -338,6 +352,7 @@ impl Content { .width(Length::Fill) .spacing(10) .align_items(Alignment::Center) + .push(Text::new(format!("{}x{}", size.width, size.height)).size(18)) .push(controls); Container::new(content) diff --git a/glow/src/lib.rs b/glow/src/lib.rs index 362933d4..7fde122c 100644 --- a/glow/src/lib.rs +++ b/glow/src/lib.rs @@ -7,7 +7,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(rust_2018_idioms)] diff --git a/glow/src/widget.rs b/glow/src/widget.rs index ee2810f9..85ef2f02 100644 --- a/glow/src/widget.rs +++ b/glow/src/widget.rs @@ -16,6 +16,7 @@ pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; +pub mod responsive; pub mod rule; pub mod scrollable; pub mod slider; @@ -38,6 +39,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 rule::Rule; #[doc(no_inline)] pub use scrollable::Scrollable; diff --git a/glow/src/widget/responsive.rs b/glow/src/widget/responsive.rs new file mode 100644 index 00000000..1a64ac62 --- /dev/null +++ b/glow/src/widget/responsive.rs @@ -0,0 +1,6 @@ +use crate::Renderer; + +pub use iced_native::widget::responsive::State; + +pub type Responsive<'a, Message> = + iced_native::widget::Responsive<'a, Message, Renderer>; 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<Size>, + 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<Internal<'a, Message, Renderer>>, +); + +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<Message, Renderer> + 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<Box<dyn FnOnce(Size) -> 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<Responsive<'a, Message, Renderer>> + for Element<'a, Message, Renderer> +where + Renderer: crate::Renderer + 'a, + Message: 'a, +{ + fn from(responsive: Responsive<'a, Message, Renderer>) -> Self { + Self::new(responsive) + } +} diff --git a/src/widget.rs b/src/widget.rs index 0f0b0325..c2790e4a 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -17,8 +17,8 @@ mod platform { pub use crate::renderer::widget::{ button, checkbox, container, pane_grid, pick_list, progress_bar, radio, - rule, scrollable, slider, text_input, toggler, tooltip, Column, Row, - Space, Text, + responsive, rule, scrollable, slider, text_input, toggler, tooltip, + Column, Row, Space, Text, }; #[cfg(any(feature = "canvas", feature = "glow_canvas"))] @@ -54,8 +54,9 @@ mod platform { pub use { button::Button, checkbox::Checkbox, container::Container, image::Image, pane_grid::PaneGrid, pick_list::PickList, progress_bar::ProgressBar, - radio::Radio, rule::Rule, scrollable::Scrollable, slider::Slider, - svg::Svg, text_input::TextInput, toggler::Toggler, tooltip::Tooltip, + radio::Radio, responsive::Responsive, rule::Rule, + scrollable::Scrollable, slider::Slider, svg::Svg, + text_input::TextInput, toggler::Toggler, tooltip::Tooltip, }; #[cfg(any(feature = "canvas", feature = "glow_canvas"))] diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index fb03854b..69763e66 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -23,7 +23,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)] #![deny(unsafe_code)] diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index 99ae0ac2..ac8653d4 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -16,6 +16,7 @@ pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; +pub mod responsive; pub mod rule; pub mod scrollable; pub mod slider; @@ -38,6 +39,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 rule::Rule; #[doc(no_inline)] pub use scrollable::Scrollable; diff --git a/wgpu/src/widget/responsive.rs b/wgpu/src/widget/responsive.rs new file mode 100644 index 00000000..1a64ac62 --- /dev/null +++ b/wgpu/src/widget/responsive.rs @@ -0,0 +1,6 @@ +use crate::Renderer; + +pub use iced_native::widget::responsive::State; + +pub type Responsive<'a, Message> = + iced_native::widget::Responsive<'a, Message, Renderer>; |