diff options
Diffstat (limited to 'pure/src')
28 files changed, 0 insertions, 5781 deletions
diff --git a/pure/src/element.rs b/pure/src/element.rs deleted file mode 100644 index 35c68716..00000000 --- a/pure/src/element.rs +++ /dev/null @@ -1,346 +0,0 @@ -use crate::overlay; -use crate::widget::tree::{self, Tree}; -use crate::widget::Widget; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -use std::borrow::Borrow; - -/// A generic [`Widget`]. -/// -/// It is useful to build composable user interfaces that do not leak -/// implementation details in their __view logic__. -/// -/// If you have a [built-in widget], you should be able to use `Into<Element>` -/// to turn it into an [`Element`]. -/// -/// [built-in widget]: crate::widget -pub struct Element<'a, Message, Renderer> { - widget: Box<dyn Widget<Message, Renderer> + 'a>, -} - -impl<'a, Message, Renderer> Element<'a, Message, Renderer> { - /// Creates a new [`Element`] containing the given [`Widget`]. - pub fn new(widget: impl Widget<Message, Renderer> + 'a) -> Self - where - Renderer: iced_native::Renderer, - { - Self { - widget: Box::new(widget), - } - } - - /// Returns a reference to the [`Widget`] of the [`Element`], - pub fn as_widget(&self) -> &dyn Widget<Message, Renderer> { - self.widget.as_ref() - } - - /// Returns a mutable reference to the [`Widget`] of the [`Element`], - pub fn as_widget_mut(&mut self) -> &mut dyn Widget<Message, Renderer> { - self.widget.as_mut() - } - - /// Applies a transformation to the produced message of the [`Element`]. - /// - /// This method is useful when you want to decouple different parts of your - /// UI and make them __composable__. - /// - /// # Example - /// Imagine we want to use [our counter](index.html#usage). But instead of - /// showing a single counter, we want to display many of them. We can reuse - /// the `Counter` type as it is! - /// - /// We use composition to model the __state__ of our new application: - /// - /// ``` - /// # mod counter { - /// # pub struct Counter; - /// # } - /// use counter::Counter; - /// - /// struct ManyCounters { - /// counters: Vec<Counter>, - /// } - /// ``` - /// - /// We can store the state of multiple counters now. However, the - /// __messages__ we implemented before describe the user interactions - /// of a __single__ counter. Right now, we need to also identify which - /// counter is receiving user interactions. Can we use composition again? - /// Yes. - /// - /// ``` - /// # mod counter { - /// # #[derive(Debug, Clone, Copy)] - /// # pub enum Message {} - /// # } - /// #[derive(Debug, Clone, Copy)] - /// pub enum Message { - /// Counter(usize, counter::Message) - /// } - /// ``` - /// - /// We compose the previous __messages__ with the index of the counter - /// producing them. Let's implement our __view logic__ now: - /// - /// ``` - /// # mod counter { - /// # type Text = iced_pure::widget::Text<iced_native::renderer::Null>; - /// # - /// # #[derive(Debug, Clone, Copy)] - /// # pub enum Message {} - /// # pub struct Counter; - /// # - /// # impl Counter { - /// # pub fn view(&mut self) -> Text { - /// # Text::new("") - /// # } - /// # } - /// # } - /// # - /// # mod iced_wgpu { - /// # pub use iced_native::renderer::Null as Renderer; - /// # } - /// # - /// # use counter::Counter; - /// # - /// # struct ManyCounters { - /// # counters: Vec<Counter>, - /// # } - /// # - /// # #[derive(Debug, Clone, Copy)] - /// # pub enum Message { - /// # Counter(usize, counter::Message) - /// # } - /// use iced_pure::Element; - /// use iced_pure::widget::Row; - /// use iced_wgpu::Renderer; - /// - /// impl ManyCounters { - /// pub fn view(&mut self) -> Row<Message, Renderer> { - /// // We can quickly populate a `Row` by folding over our counters - /// self.counters.iter_mut().enumerate().fold( - /// Row::new().spacing(20), - /// |row, (index, counter)| { - /// // We display the counter - /// let element: Element<counter::Message, Renderer> = - /// counter.view().into(); - /// - /// row.push( - /// // Here we turn our `Element<counter::Message>` into - /// // an `Element<Message>` by combining the `index` and the - /// // message of the `element`. - /// element.map(move |message| Message::Counter(index, message)) - /// ) - /// } - /// ) - /// } - /// } - /// ``` - /// - /// Finally, our __update logic__ is pretty straightforward: simple - /// delegation. - /// - /// ``` - /// # mod counter { - /// # #[derive(Debug, Clone, Copy)] - /// # pub enum Message {} - /// # pub struct Counter; - /// # - /// # impl Counter { - /// # pub fn update(&mut self, _message: Message) {} - /// # } - /// # } - /// # - /// # use counter::Counter; - /// # - /// # struct ManyCounters { - /// # counters: Vec<Counter>, - /// # } - /// # - /// # #[derive(Debug, Clone, Copy)] - /// # pub enum Message { - /// # Counter(usize, counter::Message) - /// # } - /// impl ManyCounters { - /// pub fn update(&mut self, message: Message) { - /// match message { - /// Message::Counter(index, counter_msg) => { - /// if let Some(counter) = self.counters.get_mut(index) { - /// counter.update(counter_msg); - /// } - /// } - /// } - /// } - /// } - /// ``` - pub fn map<B>( - self, - f: impl Fn(Message) -> B + 'a, - ) -> Element<'a, B, Renderer> - where - Message: 'a, - Renderer: iced_native::Renderer + 'a, - B: 'a, - { - Element::new(Map::new(self.widget, f)) - } -} - -struct Map<'a, A, B, Renderer> { - widget: Box<dyn Widget<A, Renderer> + 'a>, - mapper: Box<dyn Fn(A) -> B + 'a>, -} - -impl<'a, A, B, Renderer> Map<'a, A, B, Renderer> { - pub fn new<F>( - widget: Box<dyn Widget<A, Renderer> + 'a>, - mapper: F, - ) -> Map<'a, A, B, Renderer> - where - F: 'a + Fn(A) -> B, - { - Map { - widget, - mapper: Box::new(mapper), - } - } -} - -impl<'a, A, B, Renderer> Widget<B, Renderer> for Map<'a, A, B, Renderer> -where - Renderer: iced_native::Renderer + 'a, - A: 'a, - B: 'a, -{ - fn tag(&self) -> tree::Tag { - self.widget.tag() - } - - fn state(&self) -> tree::State { - self.widget.state() - } - - fn children(&self) -> Vec<Tree> { - self.widget.children() - } - - fn diff(&self, tree: &mut Tree) { - self.widget.diff(tree) - } - - fn width(&self) -> Length { - self.widget.width() - } - - fn height(&self) -> Length { - self.widget.height() - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - self.widget.layout(renderer, limits) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, B>, - ) -> event::Status { - let mut local_messages = Vec::new(); - let mut local_shell = Shell::new(&mut local_messages); - - let status = self.widget.on_event( - tree, - event, - layout, - cursor_position, - renderer, - clipboard, - &mut local_shell, - ); - - shell.merge(local_shell, &self.mapper); - - status - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - self.widget.draw( - tree, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.widget.mouse_interaction( - tree, - layout, - cursor_position, - viewport, - renderer, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, B, Renderer>> { - let mapper = &self.mapper; - - self.widget - .overlay(tree, layout, renderer) - .map(move |overlay| overlay.map(mapper)) - } -} - -impl<'a, Message, Renderer> Borrow<dyn Widget<Message, Renderer> + 'a> - for Element<'a, Message, Renderer> -{ - fn borrow(&self) -> &(dyn Widget<Message, Renderer> + 'a) { - self.widget.borrow() - } -} - -impl<'a, Message, Renderer> Borrow<dyn Widget<Message, Renderer> + 'a> - for &Element<'a, Message, Renderer> -{ - fn borrow(&self) -> &(dyn Widget<Message, Renderer> + 'a) { - self.widget.borrow() - } -} diff --git a/pure/src/flex.rs b/pure/src/flex.rs deleted file mode 100644 index 3f65a28b..00000000 --- a/pure/src/flex.rs +++ /dev/null @@ -1,232 +0,0 @@ -//! Distribute elements using a flex-based layout. -// This code is heavily inspired by the [`druid`] codebase. -// -// [`druid`]: https://github.com/xi-editor/druid -// -// Copyright 2018 The xi-editor Authors, Héctor Ramón -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use crate::Element; - -use iced_native::layout::{Limits, Node}; -use iced_native::{Alignment, Padding, Point, Size}; - -/// The main axis of a flex layout. -#[derive(Debug)] -pub enum Axis { - /// The horizontal axis - Horizontal, - - /// The vertical axis - Vertical, -} - -impl Axis { - fn main(&self, size: Size) -> f32 { - match self { - Axis::Horizontal => size.width, - Axis::Vertical => size.height, - } - } - - fn cross(&self, size: Size) -> f32 { - match self { - Axis::Horizontal => size.height, - Axis::Vertical => size.width, - } - } - - fn pack(&self, main: f32, cross: f32) -> (f32, f32) { - match self { - Axis::Horizontal => (main, cross), - Axis::Vertical => (cross, main), - } - } -} - -/// Computes the flex layout with the given axis and limits, applying spacing, -/// padding and alignment to the items as needed. -/// -/// It returns a new layout [`Node`]. -pub fn resolve<Message, Renderer>( - axis: Axis, - renderer: &Renderer, - limits: &Limits, - padding: Padding, - spacing: f32, - align_items: Alignment, - items: &[Element<'_, Message, Renderer>], -) -> Node -where - Renderer: iced_native::Renderer, -{ - let limits = limits.pad(padding); - let total_spacing = spacing * items.len().saturating_sub(1) as f32; - let max_cross = axis.cross(limits.max()); - - let mut fill_sum = 0; - let mut cross = axis.cross(limits.min()).max(axis.cross(limits.fill())); - let mut available = axis.main(limits.max()) - total_spacing; - - let mut nodes: Vec<Node> = Vec::with_capacity(items.len()); - nodes.resize(items.len(), Node::default()); - - if align_items == Alignment::Fill { - let mut fill_cross = axis.cross(limits.min()); - - items.iter().for_each(|child| { - let cross_fill_factor = match axis { - Axis::Horizontal => child.as_widget().height(), - Axis::Vertical => child.as_widget().width(), - } - .fill_factor(); - - if cross_fill_factor == 0 { - let (max_width, max_height) = axis.pack(available, max_cross); - - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); - - let layout = child.as_widget().layout(renderer, &child_limits); - let size = layout.size(); - - fill_cross = fill_cross.max(axis.cross(size)); - } - }); - - cross = fill_cross; - } - - for (i, child) in items.iter().enumerate() { - let fill_factor = match axis { - Axis::Horizontal => child.as_widget().width(), - Axis::Vertical => child.as_widget().height(), - } - .fill_factor(); - - if fill_factor == 0 { - let (min_width, min_height) = if align_items == Alignment::Fill { - axis.pack(0.0, cross) - } else { - axis.pack(0.0, 0.0) - }; - - let (max_width, max_height) = if align_items == Alignment::Fill { - axis.pack(available, cross) - } else { - axis.pack(available, max_cross) - }; - - let child_limits = Limits::new( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - ); - - let layout = child.as_widget().layout(renderer, &child_limits); - let size = layout.size(); - - available -= axis.main(size); - - if align_items != Alignment::Fill { - cross = cross.max(axis.cross(size)); - } - - nodes[i] = layout; - } else { - fill_sum += fill_factor; - } - } - - let remaining = available.max(0.0); - - for (i, child) in items.iter().enumerate() { - let fill_factor = match axis { - Axis::Horizontal => child.as_widget().width(), - Axis::Vertical => child.as_widget().height(), - } - .fill_factor(); - - if fill_factor != 0 { - let max_main = remaining * fill_factor as f32 / fill_sum as f32; - let min_main = if max_main.is_infinite() { - 0.0 - } else { - max_main - }; - - let (min_width, min_height) = if align_items == Alignment::Fill { - axis.pack(min_main, cross) - } else { - axis.pack(min_main, axis.cross(limits.min())) - }; - - let (max_width, max_height) = if align_items == Alignment::Fill { - axis.pack(max_main, cross) - } else { - axis.pack(max_main, max_cross) - }; - - let child_limits = Limits::new( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - ); - - let layout = child.as_widget().layout(renderer, &child_limits); - - if align_items != Alignment::Fill { - cross = cross.max(axis.cross(layout.size())); - } - - nodes[i] = layout; - } - } - - let pad = axis.pack(padding.left as f32, padding.top as f32); - let mut main = pad.0; - - for (i, node) in nodes.iter_mut().enumerate() { - if i > 0 { - main += spacing; - } - - let (x, y) = axis.pack(main, pad.1); - - node.move_to(Point::new(x, y)); - - match axis { - Axis::Horizontal => { - node.align( - Alignment::Start, - align_items, - Size::new(0.0, cross), - ); - } - Axis::Vertical => { - node.align( - align_items, - Alignment::Start, - Size::new(cross, 0.0), - ); - } - } - - let size = node.size(); - - main += axis.main(size); - } - - let (width, height) = axis.pack(main - pad.0, cross); - let size = limits.resolve(Size::new(width, height)); - - Node::with_children(size.pad(padding), nodes) -} diff --git a/pure/src/helpers.rs b/pure/src/helpers.rs deleted file mode 100644 index 88598f9b..00000000 --- a/pure/src/helpers.rs +++ /dev/null @@ -1,247 +0,0 @@ -//! Helper functions to create pure widgets. -use crate::widget; -use crate::Element; - -use iced_native::Length; -use std::borrow::Cow; -use std::ops::RangeInclusive; - -/// Creates a new [`Container`] with the provided content. -/// -/// [`Container`]: widget::Container -pub fn container<'a, Message, Renderer>( - content: impl Into<Element<'a, Message, Renderer>>, -) -> widget::Container<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: widget::container::StyleSheet, -{ - widget::Container::new(content) -} - -/// Creates a new [`Column`]. -/// -/// [`Column`]: widget::Column -pub fn column<'a, Message, Renderer>() -> widget::Column<'a, Message, Renderer> -{ - widget::Column::new() -} - -/// Creates a new [`Row`]. -/// -/// [`Row`]: widget::Row -pub fn row<'a, Message, Renderer>() -> widget::Row<'a, Message, Renderer> { - widget::Row::new() -} - -/// Creates a new [`Scrollable`] with the provided content. -/// -/// [`Scrollable`]: widget::Scrollable -pub fn scrollable<'a, Message, Renderer>( - content: impl Into<Element<'a, Message, Renderer>>, -) -> widget::Scrollable<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: widget::scrollable::StyleSheet, -{ - widget::Scrollable::new(content) -} - -/// Creates a new [`Button`] with the provided content. -/// -/// [`Button`]: widget::Button -pub fn button<'a, Message, Renderer>( - content: impl Into<Element<'a, Message, Renderer>>, -) -> widget::Button<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: widget::button::StyleSheet, -{ - widget::Button::new(content) -} - -/// Creates a new [`Tooltip`] with the provided content, tooltip text, and [`tooltip::Position`]. -/// -/// [`Tooltip`]: widget::Tooltip -/// [`tooltip::Position`]: widget::tooltip::Position -pub fn tooltip<'a, Message, Renderer>( - content: impl Into<Element<'a, Message, Renderer>>, - tooltip: impl ToString, - position: widget::tooltip::Position, -) -> widget::Tooltip<'a, Message, Renderer> -where - Renderer: iced_native::text::Renderer, - Renderer::Theme: widget::container::StyleSheet + widget::text::StyleSheet, -{ - widget::Tooltip::new(content, tooltip, position) -} - -/// Creates a new [`Text`] widget with the provided content. -/// -/// [`Text`]: widget::Text -pub fn text<Renderer>(text: impl Into<String>) -> widget::Text<Renderer> -where - Renderer: iced_native::text::Renderer, - Renderer::Theme: widget::text::StyleSheet, -{ - widget::Text::new(text) -} - -/// Creates a new [`Checkbox`]. -/// -/// [`Checkbox`]: widget::Checkbox -pub fn checkbox<'a, Message, Renderer>( - label: impl Into<String>, - is_checked: bool, - f: impl Fn(bool) -> Message + 'a, -) -> widget::Checkbox<'a, Message, Renderer> -where - Renderer: iced_native::text::Renderer, - Renderer::Theme: widget::checkbox::StyleSheet + widget::text::StyleSheet, -{ - widget::Checkbox::new(is_checked, label, f) -} - -/// Creates a new [`Radio`]. -/// -/// [`Radio`]: widget::Radio -pub fn radio<Message, Renderer, V>( - label: impl Into<String>, - value: V, - selected: Option<V>, - on_click: impl FnOnce(V) -> Message, -) -> widget::Radio<Message, Renderer> -where - Message: Clone, - Renderer: iced_native::text::Renderer, - Renderer::Theme: widget::radio::StyleSheet, - V: Copy + Eq, -{ - widget::Radio::new(value, label, selected, on_click) -} - -/// Creates a new [`Toggler`]. -/// -/// [`Toggler`]: widget::Toggler -pub fn toggler<'a, Message, Renderer>( - label: impl Into<Option<String>>, - is_checked: bool, - f: impl Fn(bool) -> Message + 'a, -) -> widget::Toggler<'a, Message, Renderer> -where - Renderer: iced_native::text::Renderer, - Renderer::Theme: widget::toggler::StyleSheet, -{ - widget::Toggler::new(is_checked, label, f) -} - -/// Creates a new [`TextInput`]. -/// -/// [`TextInput`]: widget::TextInput -pub fn text_input<'a, Message, Renderer>( - placeholder: &str, - value: &str, - on_change: impl Fn(String) -> Message + 'a, -) -> widget::TextInput<'a, Message, Renderer> -where - Message: Clone, - Renderer: iced_native::text::Renderer, - Renderer::Theme: widget::text_input::StyleSheet, -{ - widget::TextInput::new(placeholder, value, on_change) -} - -/// Creates a new [`Slider`]. -/// -/// [`Slider`]: widget::Slider -pub fn slider<'a, T, Message, Renderer>( - range: std::ops::RangeInclusive<T>, - value: T, - on_change: impl Fn(T) -> Message + 'a, -) -> widget::Slider<'a, T, Message, Renderer> -where - T: Copy + From<u8> + std::cmp::PartialOrd, - Message: Clone, - Renderer: iced_native::Renderer, - Renderer::Theme: widget::slider::StyleSheet, -{ - widget::Slider::new(range, value, on_change) -} - -/// Creates a new [`PickList`]. -/// -/// [`PickList`]: widget::PickList -pub fn pick_list<'a, Message, Renderer, T>( - options: impl Into<Cow<'a, [T]>>, - selected: Option<T>, - on_selected: impl Fn(T) -> Message + 'a, -) -> widget::PickList<'a, T, Message, Renderer> -where - T: ToString + Eq + 'static, - [T]: ToOwned<Owned = Vec<T>>, - Renderer: iced_native::text::Renderer, - Renderer::Theme: widget::pick_list::StyleSheet, -{ - widget::PickList::new(options, selected, on_selected) -} - -/// Creates a new [`Image`]. -/// -/// [`Image`]: widget::Image -pub fn image<Handle>(handle: impl Into<Handle>) -> widget::Image<Handle> { - widget::Image::new(handle.into()) -} - -/// Creates a new horizontal [`Space`] with the given [`Length`]. -/// -/// [`Space`]: widget::Space -pub fn horizontal_space(width: Length) -> widget::Space { - widget::Space::with_width(width) -} - -/// Creates a new vertical [`Space`] with the given [`Length`]. -/// -/// [`Space`]: widget::Space -pub fn vertical_space(height: Length) -> widget::Space { - widget::Space::with_height(height) -} - -/// Creates a horizontal [`Rule`] with the given height. -/// -/// [`Rule`]: widget::Rule -pub fn horizontal_rule<Renderer>(height: u16) -> widget::Rule<Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: widget::rule::StyleSheet, -{ - widget::Rule::horizontal(height) -} - -/// Creates a vertical [`Rule`] with the given width. -/// -/// [`Rule`]: widget::Rule -pub fn vertical_rule<Renderer>(width: u16) -> widget::Rule<Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: widget::rule::StyleSheet, -{ - widget::Rule::vertical(width) -} - -/// Creates a new [`ProgressBar`]. -/// -/// It expects: -/// * an inclusive range of possible values, and -/// * the current value of the [`ProgressBar`]. -/// -/// [`ProgressBar`]: widget::ProgressBar -pub fn progress_bar<Renderer>( - range: RangeInclusive<f32>, - value: f32, -) -> widget::ProgressBar<Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: widget::progress_bar::StyleSheet, -{ - widget::ProgressBar::new(range, value) -} diff --git a/pure/src/lib.rs b/pure/src/lib.rs deleted file mode 100644 index 9c5f3bc8..00000000 --- a/pure/src/lib.rs +++ /dev/null @@ -1,292 +0,0 @@ -//! Stateless, pure widgets for iced. -//! -//! # The Elm Architecture, purity, and continuity -//! As you may know, applications made with `iced` use [The Elm Architecture]. -//! -//! In a nutshell, this architecture defines the initial state of the application, a way to `view` it, and a way to `update` it after a user interaction. The `update` logic is called after a meaningful user interaction, which in turn updates the state of the application. Then, the `view` logic is executed to redisplay the application. -//! -//! Since `view` logic is only run after an `update`, all of the mutations to the application state must only happen in the `update` logic. If the application state changes anywhere else, the `view` logic will not be rerun and, therefore, the previously generated `view` may stay outdated. -//! -//! However, the `Application` trait in `iced` defines `view` as: -//! -//! ```ignore -//! pub trait Application { -//! fn view(&mut self) -> Element<Self::Message>; -//! } -//! ``` -//! -//! As a consequence, the application state can be mutated in `view` logic. The `view` logic in `iced` is __impure__. -//! -//! This impurity is necessary because `iced` puts the burden of widget __continuity__ on its users. In other words, it's up to you to provide `iced` with the internal state of each widget every time `view` is called. -//! -//! If we take a look at the classic `counter` example: -//! -//! ```ignore -//! struct Counter { -//! value: i32, -//! increment_button: button::State, -//! decrement_button: button::State, -//! } -//! -//! // ... -//! -//! impl Counter { -//! pub fn view(&mut self) -> Column<Message> { -//! Column::new() -//! .push( -//! Button::new(&mut self.increment_button, Text::new("+")) -//! .on_press(Message::IncrementPressed), -//! ) -//! .push(Text::new(self.value.to_string()).size(50)) -//! .push( -//! Button::new(&mut self.decrement_button, Text::new("-")) -//! .on_press(Message::DecrementPressed), -//! ) -//! } -//! } -//! ``` -//! -//! We can see how we need to keep track of the `button::State` of each `Button` in our `Counter` state and provide a mutable reference to the widgets in our `view` logic. The widgets produced by `view` are __stateful__. -//! -//! While this approach forces users to keep track of widget state and causes impurity, I originally chose it because it allows `iced` to directly consume the widget tree produced by `view`. Since there is no internal state decoupled from `view` maintained by the runtime, `iced` does not need to compare (e.g. reconciliate) widget trees in order to ensure continuity. -//! -//! # Stateless widgets -//! As the library matures, the need for some kind of persistent widget data (see #553) between `view` calls becomes more apparent (e.g. incremental rendering, animations, accessibility, etc.). -//! -//! If we are going to end up having persistent widget data anyways... There is no reason to have impure, stateful widgets anymore! -//! -//! And so I started exploring and ended up creating a new subcrate called `iced_pure`, which introduces a completely stateless implementation for every widget in `iced`. -//! -//! With the help of this crate, we can now write a pure `counter` example: -//! -//! ```ignore -//! struct Counter { -//! value: i32, -//! } -//! -//! // ... -//! -//! impl Counter { -//! fn view(&self) -> Column<Message> { -//! Column::new() -//! .push(Button::new("Increment").on_press(Message::IncrementPressed)) -//! .push(Text::new(self.value.to_string()).size(50)) -//! .push(Button::new("Decrement").on_press(Message::DecrementPressed)) -//! } -//! } -//! ``` -//! -//! Notice how we no longer need to keep track of the `button::State`! The widgets in `iced_pure` do not take any mutable application state in `view`. They are __stateless__ widgets. As a consequence, we do not need mutable access to `self` in `view` anymore. `view` becomes __pure__. -//! -//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/ -#![doc( - html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" -)] -#![deny( - missing_docs, - unused_results, - clippy::extra_unused_lifetimes, - clippy::from_over_into, - clippy::needless_borrow, - clippy::new_without_default, - clippy::useless_conversion -)] -#![forbid(rust_2018_idioms, unsafe_code)] -#![allow(clippy::inherent_to_string, clippy::type_complexity)] -#![cfg_attr(docsrs, feature(doc_cfg))] - -pub mod flex; -pub mod helpers; -pub mod overlay; -pub mod widget; - -mod element; - -pub use element::Element; -pub use helpers::*; -pub use widget::Widget; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -/// A bridge between impure and pure widgets. -/// -/// If you already have an existing `iced` application, you do not need to switch completely to the new traits in order to benefit from the `pure` module. Instead, you can leverage the new `Pure` widget to include `pure` widgets in your impure `Application`. -/// -/// For instance, let's say we want to use our pure `Counter` in an impure application: -/// -/// ```ignore -/// use iced_pure::{self, Pure}; -/// -/// struct Impure { -/// state: pure::State, -/// counter: Counter, -/// } -/// -/// impl Sandbox for Impure { -/// // ... -/// -/// pub fn view(&mut self) -> Element<Self::Message> { -/// Pure::new(&mut self.state, self.counter.view()).into() -/// } -/// } -/// ``` -/// -/// [`Pure`] acts as a bridge between pure and impure widgets. It is completely opt-in and can be used to slowly migrate your application to the new architecture. -/// -/// The purification of your application may trigger a bunch of important refactors, since it's far easier to keep your data decoupled from the GUI state with stateless widgets. For this reason, I recommend starting small in the most nested views of your application and slowly expand the purity upwards. -pub struct Pure<'a, Message, Renderer> { - state: &'a mut State, - element: Element<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Pure<'a, Message, Renderer> -where - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - /// Creates a new [`Pure`] widget with the given [`State`] and impure [`Element`]. - pub fn new( - state: &'a mut State, - content: impl Into<Element<'a, Message, Renderer>>, - ) -> Self { - let element = content.into(); - state.diff(&element); - - Self { state, element } - } -} - -/// The internal state of a [`Pure`] widget. -pub struct State { - state_tree: widget::Tree, -} - -impl Default for State { - fn default() -> Self { - Self::new() - } -} - -impl State { - /// Creates a new [`State`] for a [`Pure`] widget. - pub fn new() -> Self { - Self { - state_tree: widget::Tree::empty(), - } - } - - fn diff<Message, Renderer>( - &mut self, - new_element: &Element<'_, Message, Renderer>, - ) where - Renderer: iced_native::Renderer, - { - self.state_tree.diff(new_element); - } -} - -impl<'a, Message, Renderer> iced_native::Widget<Message, Renderer> - for Pure<'a, Message, Renderer> -where - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - fn width(&self) -> Length { - self.element.as_widget().width() - } - - fn height(&self) -> Length { - self.element.as_widget().height() - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - self.element.as_widget().layout(renderer, limits) - } - - fn on_event( - &mut self, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.element.as_widget_mut().on_event( - &mut self.state.state_tree, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - self.element.as_widget().draw( - &self.state.state_tree, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.element.as_widget().mouse_interaction( - &self.state.state_tree, - layout, - cursor_position, - viewport, - renderer, - ) - } - - fn overlay( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'_, Message, Renderer>> { - self.element.as_widget_mut().overlay( - &mut self.state.state_tree, - layout, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Pure<'a, Message, Renderer>> - for iced_native::Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - fn from(pure: Pure<'a, Message, Renderer>) -> Self { - Self::new(pure) - } -} diff --git a/pure/src/overlay.rs b/pure/src/overlay.rs deleted file mode 100644 index b82d8a67..00000000 --- a/pure/src/overlay.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Display interactive elements on top of other widgets. -use crate::widget::Tree; - -use iced_native::Layout; - -pub use iced_native::overlay::*; - -/// Obtains the first overlay [`Element`] found in the given children. -/// -/// This method will generally only be used by advanced users that are -/// implementing the [`Widget`](crate::Widget) trait. -pub fn from_children<'a, Message, Renderer>( - children: &'a [crate::Element<'_, Message, Renderer>], - tree: &'a mut Tree, - layout: Layout<'_>, - renderer: &Renderer, -) -> Option<Element<'a, Message, Renderer>> -where - Renderer: iced_native::Renderer, -{ - children - .iter() - .zip(&mut tree.children) - .zip(layout.children()) - .filter_map(|((child, state), layout)| { - child.as_widget().overlay(state, layout, renderer) - }) - .next() -} diff --git a/pure/src/widget.rs b/pure/src/widget.rs deleted file mode 100644 index cd825ad8..00000000 --- a/pure/src/widget.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Use the built-in widgets or create your own. -pub mod button; -pub mod checkbox; -pub mod container; -pub mod image; -pub mod pane_grid; -pub mod pick_list; -pub mod progress_bar; -pub mod radio; -pub mod rule; -pub mod scrollable; -pub mod slider; -pub mod svg; -pub mod text; -pub mod text_input; -pub mod toggler; -pub mod tooltip; -pub mod tree; - -mod column; -mod row; -mod space; - -pub use button::Button; -pub use checkbox::Checkbox; -pub use column::Column; -pub use container::Container; -pub use image::Image; -pub use pane_grid::PaneGrid; -pub use pick_list::PickList; -pub use progress_bar::ProgressBar; -pub use radio::Radio; -pub use row::Row; -pub use rule::Rule; -pub use scrollable::Scrollable; -pub use slider::Slider; -pub use space::Space; -pub use svg::Svg; -pub use text::Text; -pub use text_input::TextInput; -pub use toggler::Toggler; -pub use tooltip::{Position, Tooltip}; -pub use tree::Tree; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -/// A component that displays information and allows interaction. -/// -/// If you want to build your own widgets, you will need to implement this -/// trait. -pub trait Widget<Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - /// Returns the width of the [`Widget`]. - fn width(&self) -> Length; - - /// Returns the height of the [`Widget`]. - fn height(&self) -> Length; - - /// Returns the [`layout::Node`] of the [`Widget`]. - /// - /// This [`layout::Node`] is used by the runtime to compute the [`Layout`] of the - /// user interface. - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node; - - /// Draws the [`Widget`] using the associated `Renderer`. - fn draw( - &self, - state: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ); - - /// Returns the [`Tag`] of the [`Widget`]. - /// - /// [`Tag`]: tree::Tag - fn tag(&self) -> tree::Tag { - tree::Tag::stateless() - } - - /// Returns the [`State`] of the [`Widget`]. - /// - /// [`State`]: tree::State - fn state(&self) -> tree::State { - tree::State::None - } - - /// Returns the state [`Tree`] of the children of the [`Widget`]. - fn children(&self) -> Vec<Tree> { - Vec::new() - } - - /// Reconciliates the [`Widget`] with the provided [`Tree`]. - fn diff(&self, _tree: &mut Tree) {} - - /// Processes a runtime [`Event`]. - /// - /// By default, it does nothing. - fn on_event( - &mut self, - _state: &mut Tree, - _event: Event, - _layout: Layout<'_>, - _cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - _shell: &mut Shell<'_, Message>, - ) -> event::Status { - event::Status::Ignored - } - - /// Returns the current [`mouse::Interaction`] of the [`Widget`]. - /// - /// By default, it returns [`mouse::Interaction::Idle`]. - fn mouse_interaction( - &self, - _state: &Tree, - _layout: Layout<'_>, - _cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - mouse::Interaction::Idle - } - - /// Returns the overlay of the [`Widget`], if there is any. - fn overlay<'a>( - &'a self, - _state: &'a mut Tree, - _layout: Layout<'_>, - _renderer: &Renderer, - ) -> Option<overlay::Element<'a, Message, Renderer>> { - None - } -} diff --git a/pure/src/widget/button.rs b/pure/src/widget/button.rs deleted file mode 100644 index eb174e57..00000000 --- a/pure/src/widget/button.rs +++ /dev/null @@ -1,274 +0,0 @@ -//! Allow your users to perform actions by pressing a button. -use crate::overlay; -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::button; -use iced_native::{ - Clipboard, Layout, Length, Padding, Point, Rectangle, Shell, -}; - -pub use iced_style::button::{Appearance, StyleSheet}; - -use button::State; - -/// A generic widget that produces a message when pressed. -/// -/// ``` -/// # type Button<'a, Message> = -/// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>; -/// # -/// #[derive(Clone)] -/// enum Message { -/// ButtonPressed, -/// } -/// -/// let button = Button::new("Press me!").on_press(Message::ButtonPressed); -/// ``` -/// -/// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will -/// be disabled: -/// -/// ``` -/// # type Button<'a, Message> = -/// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>; -/// # -/// #[derive(Clone)] -/// enum Message { -/// ButtonPressed, -/// } -/// -/// fn disabled_button<'a>() -> Button<'a, Message> { -/// Button::new("I'm disabled!") -/// } -/// -/// fn enabled_button<'a>() -> Button<'a, Message> { -/// disabled_button().on_press(Message::ButtonPressed) -/// } -/// ``` -pub struct Button<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - content: Element<'a, Message, Renderer>, - on_press: Option<Message>, - width: Length, - height: Length, - padding: Padding, - style: <Renderer::Theme as StyleSheet>::Style, -} - -impl<'a, Message, Renderer> Button<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`Button`] with the given content. - pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self { - Button { - content: content.into(), - on_press: None, - width: Length::Shrink, - height: Length::Shrink, - padding: Padding::new(5), - style: <Renderer::Theme as StyleSheet>::Style::default(), - } - } - - /// Sets the width of the [`Button`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Button`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the [`Padding`] of the [`Button`]. - pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the message that will be produced when the [`Button`] is pressed. - /// - /// Unless `on_press` is called, the [`Button`] will be disabled. - pub fn on_press(mut self, msg: Message) -> Self { - self.on_press = Some(msg); - self - } - - /// Sets the style variant of this [`Button`]. - pub fn style( - mut self, - style: <Renderer::Theme as StyleSheet>::Style, - ) -> Self { - self.style = style; - self - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Button<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<State>() - } - - fn state(&self) -> tree::State { - tree::State::new(State::new()) - } - - fn children(&self) -> Vec<Tree> { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - button::layout( - renderer, - limits, - self.width, - self.height, - self.padding, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - if let event::Status::Captured = self.content.as_widget_mut().on_event( - &mut tree.children[0], - event.clone(), - layout.children().next().unwrap(), - cursor_position, - renderer, - clipboard, - shell, - ) { - return event::Status::Captured; - } - - button::update( - event, - layout, - cursor_position, - shell, - &self.on_press, - || tree.state.downcast_mut::<State>(), - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - let bounds = layout.bounds(); - let content_layout = layout.children().next().unwrap(); - - let styling = button::draw( - renderer, - bounds, - cursor_position, - self.on_press.is_some(), - theme, - self.style, - || tree.state.downcast_ref::<State>(), - ); - - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &renderer::Style { - text_color: styling.text_color, - }, - content_layout, - cursor_position, - &bounds, - ); - } - - fn mouse_interaction( - &self, - _tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - button::mouse_interaction( - layout, - cursor_position, - self.on_press.is_some(), - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - self.content.as_widget().overlay( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(button: Button<'a, Message, Renderer>) -> Self { - Self::new(button) - } -} diff --git a/pure/src/widget/checkbox.rs b/pure/src/widget/checkbox.rs deleted file mode 100644 index e0f9b764..00000000 --- a/pure/src/widget/checkbox.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Show toggle controls using checkboxes. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::checkbox::{Appearance, Checkbox, StyleSheet}; - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Checkbox<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - <Self as iced_native::Widget<Message, Renderer>>::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - <Self as iced_native::Widget<Message, Renderer>>::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Checkbox<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn from(checkbox: Checkbox<'a, Message, Renderer>) -> Self { - Self::new(checkbox) - } -} diff --git a/pure/src/widget/column.rs b/pure/src/widget/column.rs deleted file mode 100644 index 027eff0a..00000000 --- a/pure/src/widget/column.rs +++ /dev/null @@ -1,246 +0,0 @@ -use crate::flex; -use crate::overlay; -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{ - Alignment, Clipboard, Length, Padding, Point, Rectangle, Shell, -}; - -use std::u32; - -/// A container that distributes its contents vertically. -pub struct Column<'a, Message, Renderer> { - spacing: u16, - padding: Padding, - width: Length, - height: Length, - max_width: u32, - align_items: Alignment, - children: Vec<Element<'a, Message, Renderer>>, -} - -impl<'a, Message, Renderer> Column<'a, Message, Renderer> { - /// Creates an empty [`Column`]. - pub fn new() -> Self { - Self::with_children(Vec::new()) - } - - /// Creates a [`Column`] with the given elements. - pub fn with_children( - children: Vec<Element<'a, Message, Renderer>>, - ) -> Self { - Column { - spacing: 0, - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - max_width: u32::MAX, - align_items: Alignment::Start, - children, - } - } - - /// Sets the vertical spacing _between_ elements. - /// - /// Custom margins per element do not exist in iced. You should use this - /// method instead! While less flexible, it helps you keep spacing between - /// elements consistent. - pub fn spacing(mut self, units: u16) -> Self { - self.spacing = units; - self - } - - /// Sets the [`Padding`] of the [`Column`]. - pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`Column`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Column`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the maximum width of the [`Column`]. - pub fn max_width(mut self, max_width: u32) -> Self { - self.max_width = max_width; - self - } - - /// Sets the horizontal alignment of the contents of the [`Column`] . - pub fn align_items(mut self, align: Alignment) -> Self { - self.align_items = align; - self - } - - /// Adds an element to the [`Column`]. - pub fn push( - mut self, - child: impl Into<Element<'a, Message, Renderer>>, - ) -> Self { - self.children.push(child.into()); - self - } -} - -impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Column<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn children(&self) -> Vec<Tree> { - self.children.iter().map(Tree::new).collect() - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children); - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits - .max_width(self.max_width) - .width(self.width) - .height(self.height); - - flex::resolve( - flex::Axis::Vertical, - renderer, - &limits, - self.padding, - self.spacing as f32, - self.align_items, - &self.children, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.children - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget_mut().on_event( - state, - event.clone(), - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }) - .fold(event::Status::Ignored, event::Status::merge) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.children - .iter() - .zip(&tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget().mouse_interaction( - state, - layout, - cursor_position, - viewport, - renderer, - ) - }) - .max() - .unwrap_or_default() - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - for ((child, state), layout) in self - .children - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - child.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ); - } - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - overlay::from_children(&self.children, tree, layout, renderer) - } -} - -impl<'a, Message, Renderer> From<Column<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - fn from(column: Column<'a, Message, Renderer>) -> Self { - Self::new(column) - } -} diff --git a/pure/src/widget/container.rs b/pure/src/widget/container.rs deleted file mode 100644 index 44ffff8c..00000000 --- a/pure/src/widget/container.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! Decorate content and apply alignment. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::alignment; -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::widget::container; -use iced_native::{ - Clipboard, Layout, Length, Padding, Point, Rectangle, Shell, -}; - -use std::u32; - -pub use iced_style::container::{Appearance, StyleSheet}; - -/// An element decorating some content. -/// -/// It is normally used for alignment purposes. -#[allow(missing_debug_implementations)] -pub struct Container<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - padding: Padding, - width: Length, - height: Length, - max_width: u32, - max_height: u32, - horizontal_alignment: alignment::Horizontal, - vertical_alignment: alignment::Vertical, - style: <Renderer::Theme as StyleSheet>::Style, - content: Element<'a, Message, Renderer>, -} - -impl<'a, Message, Renderer> Container<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates an empty [`Container`]. - pub fn new<T>(content: T) -> Self - where - T: Into<Element<'a, Message, Renderer>>, - { - Container { - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - max_width: u32::MAX, - max_height: u32::MAX, - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Top, - style: Default::default(), - content: content.into(), - } - } - - /// Sets the [`Padding`] of the [`Container`]. - pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`Container`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Container`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the maximum width of the [`Container`]. - pub fn max_width(mut self, max_width: u32) -> Self { - self.max_width = max_width; - self - } - - /// Sets the maximum height of the [`Container`] in pixels. - pub fn max_height(mut self, max_height: u32) -> Self { - self.max_height = max_height; - self - } - - /// Sets the content alignment for the horizontal axis of the [`Container`]. - pub fn align_x(mut self, alignment: alignment::Horizontal) -> Self { - self.horizontal_alignment = alignment; - self - } - - /// Sets the content alignment for the vertical axis of the [`Container`]. - pub fn align_y(mut self, alignment: alignment::Vertical) -> Self { - self.vertical_alignment = alignment; - self - } - - /// Centers the contents in the horizontal axis of the [`Container`]. - pub fn center_x(mut self) -> Self { - self.horizontal_alignment = alignment::Horizontal::Center; - self - } - - /// Centers the contents in the vertical axis of the [`Container`]. - pub fn center_y(mut self) -> Self { - self.vertical_alignment = alignment::Vertical::Center; - self - } - - /// Sets the style of the [`Container`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Container<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn children(&self) -> Vec<Tree> { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - container::layout( - renderer, - limits, - self.width, - self.height, - self.max_width, - self.max_height, - self.padding, - self.horizontal_alignment, - self.vertical_alignment, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout.children().next().unwrap(), - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.content.as_widget().mouse_interaction( - &tree.children[0], - layout.children().next().unwrap(), - cursor_position, - viewport, - renderer, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - renderer_style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - let style = theme.appearance(self.style); - - container::draw_background(renderer, &style, layout.bounds()); - - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &renderer::Style { - text_color: style - .text_color - .unwrap_or(renderer_style.text_color), - }, - layout.children().next().unwrap(), - cursor_position, - viewport, - ); - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - self.content.as_widget().overlay( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - column: Container<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(column) - } -} diff --git a/pure/src/widget/image.rs b/pure/src/widget/image.rs deleted file mode 100644 index 58f81a6f..00000000 --- a/pure/src/widget/image.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! Display images in your user interface. -use crate::widget::{Tree, Widget}; -use crate::Element; - -use iced_native::layout::{self, Layout}; -use iced_native::renderer; -use iced_native::widget::image; -use iced_native::{Length, Point, Rectangle}; - -use std::hash::Hash; - -pub use image::Image; - -impl<Message, Renderer, Handle> Widget<Message, Renderer> for Image<Handle> -where - Handle: Clone + Hash, - Renderer: iced_native::image::Renderer<Handle = Handle>, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } -} - -impl<'a, Message, Renderer, Handle> From<Image<Handle>> - for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_native::image::Renderer<Handle = Handle> + 'a, - Handle: Clone + Hash + 'a, -{ - fn from(image: Image<Handle>) -> Self { - Self::new(image) - } -} diff --git a/pure/src/widget/pane_grid.rs b/pure/src/widget/pane_grid.rs deleted file mode 100644 index 69150aa8..00000000 --- a/pure/src/widget/pane_grid.rs +++ /dev/null @@ -1,414 +0,0 @@ -//! Let your users split regions of your application and organize layout dynamically. -//! -//! [](https://gfycat.com/mixedflatjellyfish) -//! -//! # Example -//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, -//! drag and drop, and hotkey support. -//! -//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid -mod content; -mod title_bar; - -pub use content::Content; -pub use title_bar::TitleBar; - -pub use iced_native::widget::pane_grid::{ - Axis, Configuration, Direction, DragEvent, Node, Pane, ResizeEvent, Split, - State, -}; - -use crate::overlay; -use crate::widget::container; -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::pane_grid; -use iced_native::widget::pane_grid::state; -use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell}; - -pub use iced_style::pane_grid::{Line, StyleSheet}; - -/// A collection of panes distributed using either vertical or horizontal splits -/// to completely fill the space available. -/// -/// [](https://gfycat.com/frailfreshairedaleterrier) -/// -/// This distribution of space is common in tiling window managers (like -/// [`awesome`](https://awesomewm.org/), [`i3`](https://i3wm.org/), or even -/// [`tmux`](https://github.com/tmux/tmux)). -/// -/// A [`PaneGrid`] supports: -/// -/// * Vertical and horizontal splits -/// * Tracking of the last active pane -/// * Mouse-based resizing -/// * Drag and drop to reorganize panes -/// * Hotkey support -/// * Configurable modifier keys -/// * [`State`] API to perform actions programmatically (`split`, `swap`, `resize`, etc.) -/// -/// ## Example -/// -/// ``` -/// # use iced_pure::widget::pane_grid; -/// # use iced_pure::text; -/// # -/// # type PaneGrid<'a, Message> = -/// # iced_pure::widget::PaneGrid<'a, Message, iced_native::renderer::Null>; -/// # -/// enum PaneState { -/// SomePane, -/// AnotherKindOfPane, -/// } -/// -/// enum Message { -/// PaneDragged(pane_grid::DragEvent), -/// PaneResized(pane_grid::ResizeEvent), -/// } -/// -/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane); -/// -/// let pane_grid = -/// PaneGrid::new(&state, |pane, state| { -/// pane_grid::Content::new(match state { -/// PaneState::SomePane => text("This is some pane"), -/// PaneState::AnotherKindOfPane => text("This is another kind of pane"), -/// }) -/// }) -/// .on_drag(Message::PaneDragged) -/// .on_resize(10, Message::PaneResized); -/// ``` -#[allow(missing_debug_implementations)] -pub struct PaneGrid<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - state: &'a state::Internal, - elements: Vec<(Pane, Content<'a, Message, Renderer>)>, - width: Length, - height: Length, - spacing: u16, - on_click: Option<Box<dyn Fn(Pane) -> Message + 'a>>, - on_drag: Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, - on_resize: Option<(u16, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>, - style: <Renderer::Theme as StyleSheet>::Style, -} - -impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - /// Creates a [`PaneGrid`] with the given [`State`] and view function. - /// - /// The view function will be called to display each [`Pane`] present in the - /// [`State`]. - pub fn new<T>( - state: &'a State<T>, - view: impl Fn(Pane, &'a T) -> Content<'a, Message, Renderer>, - ) -> Self { - let elements = { - state - .panes - .iter() - .map(|(pane, pane_state)| (*pane, view(*pane, pane_state))) - .collect() - }; - - Self { - elements, - state: &state.internal, - width: Length::Fill, - height: Length::Fill, - spacing: 0, - on_click: None, - on_drag: None, - on_resize: None, - style: Default::default(), - } - } - - /// Sets the width of the [`PaneGrid`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`PaneGrid`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the spacing _between_ the panes of the [`PaneGrid`]. - pub fn spacing(mut self, units: u16) -> Self { - self.spacing = units; - self - } - - /// Sets the message that will be produced when a [`Pane`] of the - /// [`PaneGrid`] is clicked. - pub fn on_click<F>(mut self, f: F) -> Self - where - F: 'a + Fn(Pane) -> Message, - { - self.on_click = Some(Box::new(f)); - self - } - - /// Enables the drag and drop interactions of the [`PaneGrid`], which will - /// use the provided function to produce messages. - pub fn on_drag<F>(mut self, f: F) -> Self - where - F: 'a + Fn(DragEvent) -> Message, - { - self.on_drag = Some(Box::new(f)); - self - } - - /// Enables the resize interactions of the [`PaneGrid`], which will - /// use the provided function to produce messages. - /// - /// The `leeway` describes the amount of space around a split that can be - /// used to grab it. - /// - /// The grabbable area of a split will have a length of `spacing + leeway`, - /// properly centered. In other words, a length of - /// `(spacing + leeway) / 2.0` on either side of the split line. - pub fn on_resize<F>(mut self, leeway: u16, f: F) -> Self - where - F: 'a + Fn(ResizeEvent) -> Message, - { - self.on_resize = Some((leeway, Box::new(f))); - self - } - - /// Sets the style of the [`PaneGrid`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for PaneGrid<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<state::Action>() - } - - fn state(&self) -> tree::State { - tree::State::new(state::Action::Idle) - } - - fn children(&self) -> Vec<Tree> { - self.elements - .iter() - .map(|(_, content)| content.state()) - .collect() - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children_custom( - &self.elements, - |state, (_, content)| content.diff(state), - |(_, content)| content.state(), - ) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - pane_grid::layout( - renderer, - limits, - self.state, - self.width, - self.height, - self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), - |element, renderer, limits| element.layout(renderer, limits), - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let action = tree.state.downcast_mut::<state::Action>(); - - let event_status = pane_grid::update( - action, - self.state, - &event, - layout, - cursor_position, - shell, - self.spacing, - self.elements.iter().map(|(pane, content)| (*pane, content)), - &self.on_click, - &self.on_drag, - &self.on_resize, - ); - - let picked_pane = action.picked_pane().map(|(pane, _)| pane); - - self.elements - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|(((pane, content), tree), layout)| { - let is_picked = picked_pane == Some(*pane); - - content.on_event( - tree, - event.clone(), - layout, - cursor_position, - renderer, - clipboard, - shell, - is_picked, - ) - }) - .fold(event_status, event::Status::merge) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - pane_grid::mouse_interaction( - tree.state.downcast_ref(), - self.state, - layout, - cursor_position, - self.spacing, - self.on_resize.as_ref().map(|(leeway, _)| *leeway), - ) - .unwrap_or_else(|| { - self.elements - .iter() - .zip(&tree.children) - .zip(layout.children()) - .map(|(((_pane, content), tree), layout)| { - content.mouse_interaction( - tree, - layout, - cursor_position, - viewport, - renderer, - ) - }) - .max() - .unwrap_or_default() - }) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - pane_grid::draw( - tree.state.downcast_ref(), - self.state, - layout, - cursor_position, - renderer, - theme, - style, - viewport, - self.spacing, - self.on_resize.as_ref().map(|(leeway, _)| *leeway), - self.style, - self.elements - .iter() - .zip(&tree.children) - .map(|((pane, content), tree)| (*pane, (content, tree))), - |(content, tree), - renderer, - style, - layout, - cursor_position, - rectangle| { - content.draw( - tree, - renderer, - theme, - style, - layout, - cursor_position, - rectangle, - ); - }, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'_, Message, Renderer>> { - self.elements - .iter() - .zip(&mut tree.children) - .zip(layout.children()) - .filter_map(|(((_, pane), tree), layout)| { - pane.overlay(tree, layout, renderer) - }) - .next() - } -} - -impl<'a, Message, Renderer> From<PaneGrid<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet + container::StyleSheet, -{ - fn from( - pane_grid: PaneGrid<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(pane_grid) - } -} diff --git a/pure/src/widget/pane_grid/content.rs b/pure/src/widget/pane_grid/content.rs deleted file mode 100644 index 9c2a0f4a..00000000 --- a/pure/src/widget/pane_grid/content.rs +++ /dev/null @@ -1,345 +0,0 @@ -use crate::widget::pane_grid::TitleBar; -use crate::widget::tree::Tree; -use crate::Element; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::widget::container; -use iced_native::widget::pane_grid::Draggable; -use iced_native::{Clipboard, Layout, Point, Rectangle, Shell, Size}; - -/// The content of a [`Pane`]. -/// -/// [`Pane`]: crate::widget::pane_grid::Pane -#[allow(missing_debug_implementations)] -pub struct Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - title_bar: Option<TitleBar<'a, Message, Renderer>>, - body: Element<'a, Message, Renderer>, - style: <Renderer::Theme as container::StyleSheet>::Style, -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates a new [`Content`] with the provided body. - pub fn new(body: impl Into<Element<'a, Message, Renderer>>) -> Self { - Self { - title_bar: None, - body: body.into(), - style: Default::default(), - } - } - - /// Sets the [`TitleBar`] of this [`Content`]. - pub fn title_bar( - mut self, - title_bar: TitleBar<'a, Message, Renderer>, - ) -> Self { - self.title_bar = Some(title_bar); - self - } - - /// Sets the style of the [`Content`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as container::StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - pub(super) fn state(&self) -> Tree { - let children = if let Some(title_bar) = self.title_bar.as_ref() { - vec![Tree::new(&self.body), title_bar.state()] - } else { - vec![Tree::new(&self.body), Tree::empty()] - }; - - Tree { - children, - ..Tree::empty() - } - } - - pub(super) fn diff(&self, tree: &mut Tree) { - if tree.children.len() == 2 { - if let Some(title_bar) = self.title_bar.as_ref() { - title_bar.diff(&mut tree.children[1]); - } - - tree.children[0].diff(&self.body); - } else { - *tree = self.state(); - } - } - - /// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`]. - /// - /// [`Renderer`]: iced_native::Renderer - pub fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - use container::StyleSheet; - - let bounds = layout.bounds(); - - { - let style = theme.appearance(self.style); - - container::draw_background(renderer, &style, bounds); - } - - if let Some(title_bar) = &self.title_bar { - let mut children = layout.children(); - let title_bar_layout = children.next().unwrap(); - let body_layout = children.next().unwrap(); - - let show_controls = bounds.contains(cursor_position); - - title_bar.draw( - &tree.children[1], - renderer, - theme, - style, - title_bar_layout, - cursor_position, - viewport, - show_controls, - ); - - self.body.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - body_layout, - cursor_position, - viewport, - ); - } else { - self.body.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ); - } - } - - pub(crate) fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - if let Some(title_bar) = &self.title_bar { - let max_size = limits.max(); - - let title_bar_layout = title_bar - .layout(renderer, &layout::Limits::new(Size::ZERO, max_size)); - - let title_bar_size = title_bar_layout.size(); - - let mut body_layout = self.body.as_widget().layout( - renderer, - &layout::Limits::new( - Size::ZERO, - Size::new( - max_size.width, - max_size.height - title_bar_size.height, - ), - ), - ); - - body_layout.move_to(Point::new(0.0, title_bar_size.height)); - - layout::Node::with_children( - max_size, - vec![title_bar_layout, body_layout], - ) - } else { - self.body.as_widget().layout(renderer, limits) - } - } - - pub(crate) fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - is_picked: bool, - ) -> event::Status { - let mut event_status = event::Status::Ignored; - - let body_layout = if let Some(title_bar) = &mut self.title_bar { - let mut children = layout.children(); - - event_status = title_bar.on_event( - &mut tree.children[1], - event.clone(), - children.next().unwrap(), - cursor_position, - renderer, - clipboard, - shell, - ); - - children.next().unwrap() - } else { - layout - }; - - let body_status = if is_picked { - event::Status::Ignored - } else { - self.body.as_widget_mut().on_event( - &mut tree.children[0], - event, - body_layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }; - - event_status.merge(body_status) - } - - pub(crate) fn mouse_interaction( - &self, - tree: &Tree, - 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 { - let mut children = layout.children(); - let title_bar_layout = children.next().unwrap(); - - let is_over_pick_area = title_bar - .is_over_pick_area(title_bar_layout, cursor_position); - - if is_over_pick_area { - return mouse::Interaction::Grab; - } - - let mouse_interaction = title_bar.mouse_interaction( - &tree.children[1], - title_bar_layout, - cursor_position, - viewport, - renderer, - ); - - (children.next().unwrap(), mouse_interaction) - } else { - (layout, mouse::Interaction::default()) - }; - - self.body - .as_widget() - .mouse_interaction( - &tree.children[0], - body_layout, - cursor_position, - viewport, - renderer, - ) - .max(title_bar_interaction) - } - - pub(crate) fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - if let Some(title_bar) = self.title_bar.as_ref() { - let mut children = layout.children(); - let title_bar_layout = children.next()?; - - let mut states = tree.children.iter_mut(); - let body_state = states.next().unwrap(); - let title_bar_state = states.next().unwrap(); - - match title_bar.overlay(title_bar_state, title_bar_layout, renderer) - { - Some(overlay) => Some(overlay), - None => self.body.as_widget().overlay( - body_state, - children.next()?, - renderer, - ), - } - } else { - self.body.as_widget().overlay( - &mut tree.children[0], - layout, - renderer, - ) - } - } -} - -impl<'a, Message, Renderer> Draggable for &Content<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - fn can_be_dragged_at( - &self, - layout: Layout<'_>, - cursor_position: Point, - ) -> bool { - if let Some(title_bar) = &self.title_bar { - let mut children = layout.children(); - let title_bar_layout = children.next().unwrap(); - - title_bar.is_over_pick_area(title_bar_layout, cursor_position) - } else { - false - } - } -} - -impl<'a, T, Message, Renderer> From<T> for Content<'a, Message, Renderer> -where - T: Into<Element<'a, Message, Renderer>>, - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - fn from(element: T) -> Self { - Self::new(element) - } -} diff --git a/pure/src/widget/pane_grid/title_bar.rs b/pure/src/widget/pane_grid/title_bar.rs deleted file mode 100644 index de9591a2..00000000 --- a/pure/src/widget/pane_grid/title_bar.rs +++ /dev/null @@ -1,388 +0,0 @@ -use crate::widget::Tree; -use crate::Element; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::widget::container; -use iced_native::{Clipboard, Layout, Padding, Point, Rectangle, Shell, Size}; - -/// The title bar of a [`Pane`]. -/// -/// [`Pane`]: crate::widget::pane_grid::Pane -#[allow(missing_debug_implementations)] -pub struct TitleBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - content: Element<'a, Message, Renderer>, - controls: Option<Element<'a, Message, Renderer>>, - padding: Padding, - always_show_controls: bool, - style: <Renderer::Theme as container::StyleSheet>::Style, -} - -impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - /// Creates a new [`TitleBar`] with the given content. - pub fn new<E>(content: E) -> Self - where - E: Into<Element<'a, Message, Renderer>>, - { - Self { - content: content.into(), - controls: None, - padding: Padding::ZERO, - always_show_controls: false, - style: Default::default(), - } - } - - /// Sets the controls of the [`TitleBar`]. - pub fn controls( - mut self, - controls: impl Into<Element<'a, Message, Renderer>>, - ) -> Self { - self.controls = Some(controls.into()); - self - } - - /// Sets the [`Padding`] of the [`TitleBar`]. - pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the style of the [`TitleBar`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as container::StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } - - /// Sets whether or not the [`controls`] attached to this [`TitleBar`] are - /// always visible. - /// - /// By default, the controls are only visible when the [`Pane`] of this - /// [`TitleBar`] is hovered. - /// - /// [`controls`]: Self::controls - /// [`Pane`]: crate::widget::pane_grid::Pane - pub fn always_show_controls(mut self) -> Self { - self.always_show_controls = true; - self - } -} - -impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: container::StyleSheet, -{ - pub(super) fn state(&self) -> Tree { - let children = if let Some(controls) = self.controls.as_ref() { - vec![Tree::new(&self.content), Tree::new(controls)] - } else { - vec![Tree::new(&self.content), Tree::empty()] - }; - - Tree { - children, - ..Tree::empty() - } - } - - pub(super) fn diff(&self, tree: &mut Tree) { - if tree.children.len() == 2 { - if let Some(controls) = self.controls.as_ref() { - tree.children[1].diff(controls); - } - - tree.children[0].diff(&self.content); - } else { - *tree = self.state(); - } - } - - /// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`]. - /// - /// [`Renderer`]: iced_native::Renderer - pub fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - inherited_style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - show_controls: bool, - ) { - use container::StyleSheet; - - let bounds = layout.bounds(); - let style = theme.appearance(self.style); - let inherited_style = renderer::Style { - text_color: style.text_color.unwrap_or(inherited_style.text_color), - }; - - container::draw_background(renderer, &style, bounds); - - let mut children = layout.children(); - let padded = children.next().unwrap(); - - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - let mut show_title = true; - - if let Some(controls) = &self.controls { - let controls_layout = children.next().unwrap(); - if title_layout.bounds().width + controls_layout.bounds().width - > padded.bounds().width - { - show_title = false; - } - - if show_controls || self.always_show_controls { - controls.as_widget().draw( - &tree.children[1], - renderer, - theme, - &inherited_style, - controls_layout, - cursor_position, - viewport, - ); - } - } - - if show_title { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &inherited_style, - title_layout, - cursor_position, - viewport, - ); - } - } - - /// Returns whether the mouse cursor is over the pick area of the - /// [`TitleBar`] or not. - /// - /// The whole [`TitleBar`] is a pick area, except its controls. - pub fn is_over_pick_area( - &self, - layout: Layout<'_>, - cursor_position: Point, - ) -> bool { - if layout.bounds().contains(cursor_position) { - let mut children = layout.children(); - let padded = children.next().unwrap(); - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - - if self.controls.is_some() { - let controls_layout = children.next().unwrap(); - - !controls_layout.bounds().contains(cursor_position) - && !title_layout.bounds().contains(cursor_position) - } else { - !title_layout.bounds().contains(cursor_position) - } - } else { - false - } - } - - pub(crate) fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.pad(self.padding); - let max_size = limits.max(); - - let title_layout = self - .content - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, max_size)); - - let title_size = title_layout.size(); - - let mut node = if let Some(controls) = &self.controls { - let mut controls_layout = controls - .as_widget() - .layout(renderer, &layout::Limits::new(Size::ZERO, max_size)); - - let controls_size = controls_layout.size(); - let space_before_controls = max_size.width - controls_size.width; - - let height = title_size.height.max(controls_size.height); - - controls_layout.move_to(Point::new(space_before_controls, 0.0)); - - layout::Node::with_children( - Size::new(max_size.width, height), - vec![title_layout, controls_layout], - ) - } else { - layout::Node::with_children( - Size::new(max_size.width, title_size.height), - vec![title_layout], - ) - }; - - node.move_to(Point::new( - self.padding.left.into(), - self.padding.top.into(), - )); - - layout::Node::with_children(node.size().pad(self.padding), vec![node]) - } - - pub(crate) fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let mut children = layout.children(); - let padded = children.next().unwrap(); - - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - let mut show_title = true; - - let control_status = if let Some(controls) = &mut self.controls { - let controls_layout = children.next().unwrap(); - if title_layout.bounds().width + controls_layout.bounds().width - > padded.bounds().width - { - show_title = false; - } - - controls.as_widget_mut().on_event( - &mut tree.children[1], - event.clone(), - controls_layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } else { - event::Status::Ignored - }; - - let title_status = if show_title { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - title_layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } else { - event::Status::Ignored - }; - - control_status.merge(title_status) - } - - pub(crate) fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - let mut children = layout.children(); - let padded = children.next().unwrap(); - - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - - let title_interaction = self.content.as_widget().mouse_interaction( - &tree.children[0], - title_layout, - cursor_position, - viewport, - renderer, - ); - - if let Some(controls) = &self.controls { - let controls_layout = children.next().unwrap(); - let controls_interaction = controls.as_widget().mouse_interaction( - &tree.children[1], - controls_layout, - cursor_position, - viewport, - renderer, - ); - - if title_layout.bounds().width + controls_layout.bounds().width - > padded.bounds().width - { - controls_interaction - } else { - controls_interaction.max(title_interaction) - } - } else { - title_interaction - } - } - - pub(crate) fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - let mut children = layout.children(); - let padded = children.next()?; - - let mut children = padded.children(); - let title_layout = children.next()?; - - let Self { - content, controls, .. - } = self; - - let mut states = tree.children.iter_mut(); - let title_state = states.next().unwrap(); - let controls_state = states.next().unwrap(); - - content - .as_widget() - .overlay(title_state, title_layout, renderer) - .or_else(move || { - controls.as_ref().and_then(|controls| { - let controls_layout = children.next()?; - - controls.as_widget().overlay( - controls_state, - controls_layout, - renderer, - ) - }) - }) - } -} diff --git a/pure/src/widget/pick_list.rs b/pure/src/widget/pick_list.rs deleted file mode 100644 index 9264544a..00000000 --- a/pure/src/widget/pick_list.rs +++ /dev/null @@ -1,240 +0,0 @@ -//! Display a dropdown list of selectable values. -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget::pick_list; -use iced_native::{ - Clipboard, Layout, Length, Padding, Point, Rectangle, Shell, -}; - -use std::borrow::Cow; - -pub use iced_style::pick_list::{Appearance, StyleSheet}; - -/// A widget for selecting a single value from a list of options. -#[allow(missing_debug_implementations)] -pub struct PickList<'a, T, Message, Renderer> -where - [T]: ToOwned<Owned = Vec<T>>, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - on_selected: Box<dyn Fn(T) -> Message + 'a>, - options: Cow<'a, [T]>, - placeholder: Option<String>, - selected: Option<T>, - width: Length, - padding: Padding, - text_size: Option<u16>, - font: Renderer::Font, - style: <Renderer::Theme as StyleSheet>::Style, -} - -impl<'a, T: 'a, Message, Renderer> PickList<'a, T, Message, Renderer> -where - T: ToString + Eq, - [T]: ToOwned<Owned = Vec<T>>, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - /// The default padding of a [`PickList`]. - pub const DEFAULT_PADDING: Padding = Padding::new(5); - - /// Creates a new [`PickList`] with the given list of options, the current - /// selected value, and the message to produce when an option is selected. - pub fn new( - options: impl Into<Cow<'a, [T]>>, - selected: Option<T>, - on_selected: impl Fn(T) -> Message + 'a, - ) -> Self { - Self { - on_selected: Box::new(on_selected), - options: options.into(), - placeholder: None, - selected, - width: Length::Shrink, - text_size: None, - padding: Self::DEFAULT_PADDING, - font: Default::default(), - style: Default::default(), - } - } - - /// Sets the placeholder of the [`PickList`]. - pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self { - self.placeholder = Some(placeholder.into()); - self - } - - /// Sets the width of the [`PickList`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the [`Padding`] of the [`PickList`]. - pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the text size of the [`PickList`]. - pub fn text_size(mut self, size: u16) -> Self { - self.text_size = Some(size); - self - } - - /// Sets the font of the [`PickList`]. - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; - self - } - - /// Sets the style of the [`PickList`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, T: 'a, Message, Renderer> Widget<Message, Renderer> - for PickList<'a, T, Message, Renderer> -where - T: Clone + ToString + Eq + 'static, - [T]: ToOwned<Owned = Vec<T>>, - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<pick_list::State<T>>() - } - - fn state(&self) -> tree::State { - tree::State::new(pick_list::State::<T>::new()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - pick_list::layout( - renderer, - limits, - self.width, - self.padding, - self.text_size, - &self.font, - self.placeholder.as_deref(), - &self.options, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - pick_list::update( - event, - layout, - cursor_position, - shell, - self.on_selected.as_ref(), - self.selected.as_ref(), - &self.options, - || tree.state.downcast_mut::<pick_list::State<T>>(), - ) - } - - fn mouse_interaction( - &self, - _tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - pick_list::mouse_interaction(layout, cursor_position) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - pick_list::draw( - renderer, - theme, - layout, - cursor_position, - self.padding, - self.text_size, - &self.font, - self.placeholder.as_deref(), - self.selected.as_ref(), - self.style, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - _renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - let state = tree.state.downcast_mut::<pick_list::State<T>>(); - - pick_list::overlay( - layout, - state, - self.padding, - self.text_size, - self.font.clone(), - &self.options, - self.style, - ) - } -} - -impl<'a, T: 'a, Message, Renderer> From<PickList<'a, T, Message, Renderer>> - for Element<'a, Message, Renderer> -where - T: Clone + ToString + Eq + 'static, - [T]: ToOwned<Owned = Vec<T>>, - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(pick_list: PickList<'a, T, Message, Renderer>) -> Self { - Self::new(pick_list) - } -} diff --git a/pure/src/widget/progress_bar.rs b/pure/src/widget/progress_bar.rs deleted file mode 100644 index c9644853..00000000 --- a/pure/src/widget/progress_bar.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! Provide progress feedback to your users. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::progress_bar::*; - -impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar<Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - <Self as iced_native::Widget<Message, Renderer>>::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - <Self as iced_native::Widget<Message, Renderer>>::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<ProgressBar<Renderer>> - for Element<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(progress_bar: ProgressBar<Renderer>) -> Self { - Self::new(progress_bar) - } -} diff --git a/pure/src/widget/radio.rs b/pure/src/widget/radio.rs deleted file mode 100644 index 604c2785..00000000 --- a/pure/src/widget/radio.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Create choices using radio buttons. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::radio::{Appearance, Radio, StyleSheet}; - -impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message, Renderer> -where - Message: Clone, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - <Self as iced_native::Widget<Message, Renderer>>::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - <Self as iced_native::Widget<Message, Renderer>>::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Radio<Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn from(radio: Radio<Message, Renderer>) -> Self { - Self::new(radio) - } -} diff --git a/pure/src/widget/row.rs b/pure/src/widget/row.rs deleted file mode 100644 index a288a68d..00000000 --- a/pure/src/widget/row.rs +++ /dev/null @@ -1,233 +0,0 @@ -use crate::flex; -use crate::overlay; -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{ - Alignment, Clipboard, Length, Padding, Point, Rectangle, Shell, -}; - -/// A container that distributes its contents horizontally. -pub struct Row<'a, Message, Renderer> { - spacing: u16, - padding: Padding, - width: Length, - height: Length, - align_items: Alignment, - children: Vec<Element<'a, Message, Renderer>>, -} - -impl<'a, Message, Renderer> Row<'a, Message, Renderer> { - /// Creates an empty [`Row`]. - pub fn new() -> Self { - Self::with_children(Vec::new()) - } - - /// Creates a [`Row`] with the given elements. - pub fn with_children( - children: Vec<Element<'a, Message, Renderer>>, - ) -> Self { - Row { - spacing: 0, - padding: Padding::ZERO, - width: Length::Shrink, - height: Length::Shrink, - align_items: Alignment::Start, - children, - } - } - - /// Sets the horizontal spacing _between_ elements. - /// - /// Custom margins per element do not exist in iced. You should use this - /// method instead! While less flexible, it helps you keep spacing between - /// elements consistent. - pub fn spacing(mut self, units: u16) -> Self { - self.spacing = units; - self - } - - /// Sets the [`Padding`] of the [`Row`]. - pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the width of the [`Row`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Row`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the vertical alignment of the contents of the [`Row`] . - pub fn align_items(mut self, align: Alignment) -> Self { - self.align_items = align; - self - } - - /// Adds an [`Element`] to the [`Row`]. - pub fn push( - mut self, - child: impl Into<Element<'a, Message, Renderer>>, - ) -> Self { - self.children.push(child.into()); - self - } -} - -impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Row<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, -{ - fn children(&self) -> Vec<Tree> { - self.children.iter().map(Tree::new).collect() - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(&self.children) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - - flex::resolve( - flex::Axis::Horizontal, - renderer, - &limits, - self.padding, - self.spacing as f32, - self.align_items, - &self.children, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.children - .iter_mut() - .zip(&mut tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget_mut().on_event( - state, - event.clone(), - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }) - .fold(event::Status::Ignored, event::Status::merge) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.children - .iter() - .zip(&tree.children) - .zip(layout.children()) - .map(|((child, state), layout)| { - child.as_widget().mouse_interaction( - state, - layout, - cursor_position, - viewport, - renderer, - ) - }) - .max() - .unwrap_or_default() - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - for ((child, state), layout) in self - .children - .iter() - .zip(&tree.children) - .zip(layout.children()) - { - child.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ); - } - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - overlay::from_children(&self.children, tree, layout, renderer) - } -} - -impl<'a, Message, Renderer> From<Row<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: iced_native::Renderer + 'a, -{ - fn from(row: Row<'a, Message, Renderer>) -> Self { - Self::new(row) - } -} diff --git a/pure/src/widget/rule.rs b/pure/src/widget/rule.rs deleted file mode 100644 index 0fb4ebab..00000000 --- a/pure/src/widget/rule.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! Display a horizontal or vertical rule for dividing content. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::rule::*; - -impl<Message, Renderer> Widget<Message, Renderer> for Rule<Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - <Self as iced_native::Widget<Message, Renderer>>::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - <Self as iced_native::Widget<Message, Renderer>>::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Rule<Renderer>> - for Element<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(rule: Rule<Renderer>) -> Self { - Self::new(rule) - } -} diff --git a/pure/src/widget/scrollable.rs b/pure/src/widget/scrollable.rs deleted file mode 100644 index 4118b67e..00000000 --- a/pure/src/widget/scrollable.rs +++ /dev/null @@ -1,278 +0,0 @@ -//! Navigate an endless amount of content with a scrollbar. -use crate::overlay; -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::scrollable; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell, Vector}; - -pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; - -/// A widget that can vertically display an infinite amount of content with a -/// scrollbar. -#[allow(missing_debug_implementations)] -pub struct Scrollable<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - height: Length, - scrollbar_width: u16, - scrollbar_margin: u16, - scroller_width: u16, - content: Element<'a, Message, Renderer>, - on_scroll: Option<Box<dyn Fn(f32) -> Message + 'a>>, - style: <Renderer::Theme as StyleSheet>::Style, -} - -impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`Scrollable`]. - pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self { - Scrollable { - height: Length::Shrink, - scrollbar_width: 10, - scrollbar_margin: 0, - scroller_width: 10, - content: content.into(), - on_scroll: None, - style: Default::default(), - } - } - - /// Sets the height of the [`Scrollable`]. - pub fn height(mut self, height: Length) -> Self { - self.height = height; - self - } - - /// Sets the scrollbar width of the [`Scrollable`] . - /// Silently enforces a minimum value of 1. - pub fn scrollbar_width(mut self, scrollbar_width: u16) -> Self { - self.scrollbar_width = scrollbar_width.max(1); - self - } - - /// Sets the scrollbar margin of the [`Scrollable`] . - pub fn scrollbar_margin(mut self, scrollbar_margin: u16) -> Self { - self.scrollbar_margin = scrollbar_margin; - self - } - - /// Sets the scroller width of the [`Scrollable`] . - /// - /// It silently enforces a minimum value of 1. - pub fn scroller_width(mut self, scroller_width: u16) -> Self { - self.scroller_width = scroller_width.max(1); - self - } - - /// Sets a function to call when the [`Scrollable`] is scrolled. - /// - /// The function takes the new relative offset of the [`Scrollable`] - /// (e.g. `0` means top, while `1` means bottom). - pub fn on_scroll(mut self, f: impl Fn(f32) -> Message + 'a) -> Self { - self.on_scroll = Some(Box::new(f)); - self - } - - /// Sets the style of the [`Scrollable`] . - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Scrollable<'a, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<scrollable::State>() - } - - fn state(&self) -> tree::State { - tree::State::new(scrollable::State::new()) - } - - fn children(&self) -> Vec<Tree> { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.content.as_widget().width() - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - scrollable::layout( - renderer, - limits, - Widget::<Message, Renderer>::width(self), - self.height, - u32::MAX, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - scrollable::update( - tree.state.downcast_mut::<scrollable::State>(), - event, - layout, - cursor_position, - clipboard, - shell, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - &self.on_scroll, - |event, layout, cursor_position, clipboard, shell| { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - }, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - scrollable::draw( - tree.state.downcast_ref::<scrollable::State>(), - renderer, - theme, - layout, - cursor_position, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - self.style, - |renderer, layout, cursor_position, viewport| { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - }, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - scrollable::mouse_interaction( - tree.state.downcast_ref::<scrollable::State>(), - layout, - cursor_position, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - |layout, cursor_position, viewport| { - self.content.as_widget().mouse_interaction( - &tree.children[0], - layout, - cursor_position, - viewport, - renderer, - ) - }, - ) - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - self.content - .as_widget() - .overlay( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - ) - .map(|overlay| { - let bounds = layout.bounds(); - let content_layout = layout.children().next().unwrap(); - let content_bounds = content_layout.bounds(); - let offset = tree - .state - .downcast_ref::<scrollable::State>() - .offset(bounds, content_bounds); - - overlay.translate(Vector::new(0.0, -(offset as f32))) - }) - } -} - -impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - text_input: Scrollable<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(text_input) - } -} diff --git a/pure/src/widget/slider.rs b/pure/src/widget/slider.rs deleted file mode 100644 index fed979e5..00000000 --- a/pure/src/widget/slider.rs +++ /dev/null @@ -1,255 +0,0 @@ -//! Display an interactive selector of a single value from a range of values. -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::widget::slider; -use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell, Size}; - -use std::ops::RangeInclusive; - -pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; - -/// An horizontal bar and a handle that selects a single value from a range of -/// values. -/// -/// A [`Slider`] will try to fill the horizontal space of its container. -/// -/// The [`Slider`] range of numeric values is generic and its step size defaults -/// to 1 unit. -/// -/// # Example -/// ``` -/// # use iced_pure::widget::slider; -/// # use iced_native::renderer::Null; -/// # -/// # type Slider<'a, T, Message> = slider::Slider<'a, T, Message, Null>; -/// # -/// #[derive(Clone)] -/// pub enum Message { -/// SliderChanged(f32), -/// } -/// -/// let value = 50.0; -/// -/// Slider::new(0.0..=100.0, value, Message::SliderChanged); -/// ``` -/// -///  -#[allow(missing_debug_implementations)] -pub struct Slider<'a, T, Message, Renderer> -where - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - range: RangeInclusive<T>, - step: T, - value: T, - on_change: Box<dyn Fn(T) -> Message + 'a>, - on_release: Option<Message>, - width: Length, - height: u16, - style: <Renderer::Theme as StyleSheet>::Style, -} - -impl<'a, T, Message, Renderer> Slider<'a, T, Message, Renderer> -where - T: Copy + From<u8> + std::cmp::PartialOrd, - Message: Clone, - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - /// The default height of a [`Slider`]. - pub const DEFAULT_HEIGHT: u16 = 22; - - /// Creates a new [`Slider`]. - /// - /// It expects: - /// * an inclusive range of possible values - /// * the current value of the [`Slider`] - /// * a function that will be called when the [`Slider`] is dragged. - /// It receives the new value of the [`Slider`] and must produce a - /// `Message`. - pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self - where - F: 'a + Fn(T) -> Message, - { - let value = if value >= *range.start() { - value - } else { - *range.start() - }; - - let value = if value <= *range.end() { - value - } else { - *range.end() - }; - - Slider { - value, - range, - step: T::from(1), - on_change: Box::new(on_change), - on_release: None, - width: Length::Fill, - height: Self::DEFAULT_HEIGHT, - style: Default::default(), - } - } - - /// Sets the release message of the [`Slider`]. - /// This is called when the mouse is released from the slider. - /// - /// Typically, the user's interaction with the slider is finished when this message is produced. - /// This is useful if you need to spawn a long-running task from the slider's result, where - /// the default on_change message could create too many events. - pub fn on_release(mut self, on_release: Message) -> Self { - self.on_release = Some(on_release); - self - } - - /// Sets the width of the [`Slider`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the height of the [`Slider`]. - pub fn height(mut self, height: u16) -> Self { - self.height = height; - self - } - - /// Sets the style of the [`Slider`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } - - /// Sets the step size of the [`Slider`]. - pub fn step(mut self, step: T) -> Self { - self.step = step; - self - } -} - -impl<'a, T, Message, Renderer> Widget<Message, Renderer> - for Slider<'a, T, Message, Renderer> -where - T: Copy + Into<f64> + num_traits::FromPrimitive, - Message: Clone, - Renderer: iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<slider::State>() - } - - fn state(&self) -> tree::State { - tree::State::new(slider::State::new()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink - } - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = - limits.width(self.width).height(Length::Units(self.height)); - - let size = limits.resolve(Size::ZERO); - - layout::Node::new(size) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - slider::update( - event, - layout, - cursor_position, - shell, - tree.state.downcast_mut::<slider::State>(), - &mut self.value, - &self.range, - self.step, - self.on_change.as_ref(), - &self.on_release, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - slider::draw( - renderer, - layout, - cursor_position, - tree.state.downcast_ref::<slider::State>(), - self.value, - &self.range, - theme, - self.style, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - slider::mouse_interaction( - layout, - cursor_position, - tree.state.downcast_ref::<slider::State>(), - ) - } -} - -impl<'a, T, Message, Renderer> From<Slider<'a, T, Message, Renderer>> - for Element<'a, Message, Renderer> -where - T: 'a + Copy + Into<f64> + num_traits::FromPrimitive, - Message: 'a + Clone, - Renderer: 'a + iced_native::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - slider: Slider<'a, T, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(slider) - } -} diff --git a/pure/src/widget/space.rs b/pure/src/widget/space.rs deleted file mode 100644 index 408cb647..00000000 --- a/pure/src/widget/space.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::Space; - -impl<Message, Renderer> Widget<Message, Renderer> for Space -where - Renderer: iced_native::Renderer, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - <Self as iced_native::Widget<Message, Renderer>>::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - <Self as iced_native::Widget<Message, Renderer>>::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Space> for Element<'a, Message, Renderer> -where - Renderer: iced_native::Renderer + 'a, -{ - fn from(space: Space) -> Self { - Self::new(space) - } -} diff --git a/pure/src/widget/svg.rs b/pure/src/widget/svg.rs deleted file mode 100644 index ae4e8cff..00000000 --- a/pure/src/widget/svg.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Display vector graphics in your application. -use crate::widget::{Tree, Widget}; -use crate::Element; - -use iced_native::layout::{self, Layout}; -use iced_native::renderer; -use iced_native::widget::svg; -use iced_native::{Length, Point, Rectangle}; - -pub use iced_native::svg::Handle; -pub use svg::Svg; - -impl<Message, Renderer> Widget<Message, Renderer> for Svg -where - Renderer: iced_native::svg::Renderer, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } -} - -impl<'a, Message, Renderer> From<Svg> for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_native::svg::Renderer + 'a, -{ - fn from(svg: Svg) -> Self { - Self::new(svg) - } -} diff --git a/pure/src/widget/text.rs b/pure/src/widget/text.rs deleted file mode 100644 index 7c6f6ce9..00000000 --- a/pure/src/widget/text.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! Write some text for your users to read. -use crate::widget::Tree; -use crate::{Element, Widget}; - -use iced_native::layout::{self, Layout}; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Length, Point, Rectangle}; - -pub use iced_native::widget::text::{Appearance, StyleSheet, Text}; - -impl<Message, Renderer> Widget<Message, Renderer> for Text<Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: widget::text::StyleSheet, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } -} - -impl<'a, Message, Renderer> From<Text<Renderer>> - for Element<'a, Message, Renderer> -where - Renderer: text::Renderer + 'a, - Renderer::Theme: widget::text::StyleSheet, -{ - fn from(text: Text<Renderer>) -> Self { - Self::new(text) - } -} - -impl<'a, Message, Renderer> From<&'a str> for Element<'a, Message, Renderer> -where - Renderer: text::Renderer + 'a, - Renderer::Theme: widget::text::StyleSheet, -{ - fn from(contents: &'a str) -> Self { - Text::new(contents).into() - } -} diff --git a/pure/src/widget/text_input.rs b/pure/src/widget/text_input.rs deleted file mode 100644 index 514a6795..00000000 --- a/pure/src/widget/text_input.rs +++ /dev/null @@ -1,285 +0,0 @@ -//! Display fields that can be filled with text. -use crate::widget::tree::{self, Tree}; -use crate::{Element, Widget}; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget::text_input; -use iced_native::{Clipboard, Length, Padding, Point, Rectangle, Shell}; - -pub use iced_style::text_input::{Appearance, StyleSheet}; - -/// A field that can be filled with text. -/// -/// # Example -/// ``` -/// # pub type TextInput<'a, Message> = iced_pure::widget::TextInput<'a, Message, iced_native::renderer::Null>; -/// #[derive(Debug, Clone)] -/// enum Message { -/// TextInputChanged(String), -/// } -/// -/// let value = "Some text"; -/// -/// let input = TextInput::new( -/// "This is the placeholder...", -/// value, -/// Message::TextInputChanged, -/// ) -/// .padding(10); -/// ``` -///  -#[allow(missing_debug_implementations)] -pub struct TextInput<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - placeholder: String, - value: text_input::Value, - is_secure: bool, - font: Renderer::Font, - width: Length, - padding: Padding, - size: Option<u16>, - on_change: Box<dyn Fn(String) -> Message + 'a>, - on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>, - on_submit: Option<Message>, - style: <Renderer::Theme as StyleSheet>::Style, -} - -impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> -where - Message: Clone, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`TextInput`]. - /// - /// It expects: - /// - a placeholder, - /// - the current value, and - /// - a function that produces a message when the [`TextInput`] changes. - pub fn new<F>(placeholder: &str, value: &str, on_change: F) -> Self - where - F: 'a + Fn(String) -> Message, - { - TextInput { - placeholder: String::from(placeholder), - value: text_input::Value::new(value), - is_secure: false, - font: Default::default(), - width: Length::Fill, - padding: Padding::ZERO, - size: None, - on_change: Box::new(on_change), - on_paste: None, - on_submit: None, - style: Default::default(), - } - } - - /// Converts the [`TextInput`] into a secure password input. - pub fn password(mut self) -> Self { - self.is_secure = true; - self - } - - /// Sets the message that should be produced when some text is pasted into - /// the [`TextInput`]. - pub fn on_paste( - mut self, - on_paste: impl Fn(String) -> Message + 'a, - ) -> Self { - self.on_paste = Some(Box::new(on_paste)); - self - } - - /// Sets the [`Font`] of the [`TextInput`]. - /// - /// [`Font`]: text::Renderer::Font - pub fn font(mut self, font: Renderer::Font) -> Self { - self.font = font; - self - } - /// Sets the width of the [`TextInput`]. - pub fn width(mut self, width: Length) -> Self { - self.width = width; - self - } - - /// Sets the [`Padding`] of the [`TextInput`]. - pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self { - self.padding = padding.into(); - self - } - - /// Sets the text size of the [`TextInput`]. - pub fn size(mut self, size: u16) -> Self { - self.size = Some(size); - self - } - - /// Sets the message that should be produced when the [`TextInput`] is - /// focused and the enter key is pressed. - pub fn on_submit(mut self, message: Message) -> Self { - self.on_submit = Some(message); - self - } - - /// Sets the style of the [`TextInput`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } - - /// Draws the [`TextInput`] with the given [`Renderer`], overriding its - /// [`text_input::Value`] if provided. - /// - /// [`Renderer`]: text::Renderer - pub fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - layout: Layout<'_>, - cursor_position: Point, - value: Option<&text_input::Value>, - ) { - text_input::draw( - renderer, - theme, - layout, - cursor_position, - tree.state.downcast_ref::<text_input::State>(), - value.unwrap_or(&self.value), - &self.placeholder, - self.size, - &self.font, - self.is_secure, - self.style, - ) - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for TextInput<'a, Message, Renderer> -where - Message: Clone, - Renderer: text::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::<text_input::State>() - } - - fn state(&self) -> tree::State { - tree::State::new(text_input::State::new()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - Length::Shrink - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - text_input::layout( - renderer, - limits, - self.width, - self.padding, - self.size, - ) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - text_input::update( - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - &mut self.value, - self.size, - &self.font, - self.is_secure, - self.on_change.as_ref(), - self.on_paste.as_deref(), - &self.on_submit, - || tree.state.downcast_mut::<text_input::State>(), - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - ) { - text_input::draw( - renderer, - theme, - layout, - cursor_position, - tree.state.downcast_ref::<text_input::State>(), - &self.value, - &self.placeholder, - self.size, - &self.font, - self.is_secure, - self.style, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - _viewport: &Rectangle, - _renderer: &Renderer, - ) -> mouse::Interaction { - text_input::mouse_interaction(layout, cursor_position) - } -} - -impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + text::Renderer, - Renderer::Theme: StyleSheet, -{ - fn from( - text_input: TextInput<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(text_input) - } -} diff --git a/pure/src/widget/toggler.rs b/pure/src/widget/toggler.rs deleted file mode 100644 index 8d0044d2..00000000 --- a/pure/src/widget/toggler.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Show toggle controls using togglers. -use crate::widget::{Tree, Widget}; -use crate::Element; - -use iced_native::event::{self, Event}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget; -use iced_native::{Clipboard, Length, Point, Rectangle, Shell}; - -pub use iced_native::widget::toggler::{Appearance, StyleSheet, Toggler}; - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Toggler<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn width(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::width(self) - } - - fn height(&self) -> Length { - <Self as iced_native::Widget<Message, Renderer>>::height(self) - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - <Self as iced_native::Widget<Message, Renderer>>::layout( - self, renderer, limits, - ) - } - - fn draw( - &self, - _state: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - <Self as iced_native::Widget<Message, Renderer>>::draw( - self, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) - } - - fn mouse_interaction( - &self, - _state: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - <Self as iced_native::Widget<Message, Renderer>>::mouse_interaction( - self, - layout, - cursor_position, - viewport, - renderer, - ) - } - - fn on_event( - &mut self, - _state: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - <Self as iced_native::Widget<Message, Renderer>>::on_event( - self, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } -} - -impl<'a, Message, Renderer> From<Toggler<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: text::Renderer + 'a, - Renderer::Theme: StyleSheet + widget::text::StyleSheet, -{ - fn from(toggler: Toggler<'a, Message, Renderer>) -> Self { - Self::new(toggler) - } -} diff --git a/pure/src/widget/tooltip.rs b/pure/src/widget/tooltip.rs deleted file mode 100644 index cbc34722..00000000 --- a/pure/src/widget/tooltip.rs +++ /dev/null @@ -1,240 +0,0 @@ -//! Display a widget over another. -use crate::widget::Tree; -use crate::{Element, Widget}; -use iced_native::event::{self, Event}; -use iced_native::layout; -use iced_native::mouse; -use iced_native::overlay; -use iced_native::renderer; -use iced_native::text; -use iced_native::widget::container; -use iced_native::widget::tooltip; -use iced_native::widget::{self, Text}; -use iced_native::{Clipboard, Layout, Length, Point, Rectangle, Shell}; - -pub use iced_style::container::{Appearance, StyleSheet}; -pub use tooltip::Position; - -/// An element to display a widget over another. -#[allow(missing_debug_implementations)] -pub struct Tooltip<'a, Message, Renderer: text::Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - content: Element<'a, Message, Renderer>, - tooltip: Text<Renderer>, - position: Position, - gap: u16, - padding: u16, - style: <Renderer::Theme as container::StyleSheet>::Style, -} - -impl<'a, Message, Renderer> Tooltip<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - /// The default padding of a [`Tooltip`] drawn by this renderer. - const DEFAULT_PADDING: u16 = 5; - - /// Creates a new [`Tooltip`]. - /// - /// [`Tooltip`]: struct.Tooltip.html - pub fn new( - content: impl Into<Element<'a, Message, Renderer>>, - tooltip: impl ToString, - position: Position, - ) -> Self { - Tooltip { - content: content.into(), - tooltip: Text::new(tooltip.to_string()), - position, - gap: 0, - padding: Self::DEFAULT_PADDING, - style: Default::default(), - } - } - - /// Sets the size of the text of the [`Tooltip`]. - pub fn size(mut self, size: u16) -> Self { - self.tooltip = self.tooltip.size(size); - self - } - - /// Sets the font of the [`Tooltip`]. - /// - /// [`Font`]: Renderer::Font - pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self { - self.tooltip = self.tooltip.font(font); - self - } - - /// Sets the gap between the content and its [`Tooltip`]. - pub fn gap(mut self, gap: u16) -> Self { - self.gap = gap; - self - } - - /// Sets the padding of the [`Tooltip`]. - pub fn padding(mut self, padding: u16) -> Self { - self.padding = padding; - self - } - - /// Sets the style of the [`Tooltip`]. - pub fn style( - mut self, - style: impl Into<<Renderer::Theme as container::StyleSheet>::Style>, - ) -> Self { - self.style = style.into(); - self - } -} - -impl<'a, Message, Renderer> Widget<Message, Renderer> - for Tooltip<'a, Message, Renderer> -where - Renderer: text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - fn children(&self) -> Vec<Tree> { - vec![Tree::new(&self.content)] - } - - fn diff(&self, tree: &mut Tree) { - tree.diff_children(std::slice::from_ref(&self.content)) - } - - fn width(&self) -> Length { - self.content.as_widget().width() - } - - fn height(&self) -> Length { - self.content.as_widget().height() - } - - fn layout( - &self, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - self.content.as_widget().layout(renderer, limits) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor_position: Point, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.content.as_widget_mut().on_event( - &mut tree.children[0], - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.content.as_widget().mouse_interaction( - &tree.children[0], - layout.children().next().unwrap(), - cursor_position, - viewport, - renderer, - ) - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - inherited_style: &renderer::Style, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - inherited_style, - layout, - cursor_position, - viewport, - ); - - let tooltip = &self.tooltip; - - tooltip::draw( - renderer, - theme, - inherited_style, - layout, - cursor_position, - viewport, - self.position, - self.gap, - self.padding, - self.style, - |renderer, limits| { - Widget::<(), Renderer>::layout(tooltip, renderer, limits) - }, - |renderer, defaults, layout, cursor_position, viewport| { - Widget::<(), Renderer>::draw( - tooltip, - &Tree::empty(), - renderer, - theme, - defaults, - layout, - cursor_position, - viewport, - ); - }, - ); - } - - fn overlay<'b>( - &'b self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - ) -> Option<overlay::Element<'b, Message, Renderer>> { - self.content.as_widget().overlay( - &mut tree.children[0], - layout, - renderer, - ) - } -} - -impl<'a, Message, Renderer> From<Tooltip<'a, Message, Renderer>> - for Element<'a, Message, Renderer> -where - Message: 'a, - Renderer: 'a + text::Renderer, - Renderer::Theme: container::StyleSheet + widget::text::StyleSheet, -{ - fn from( - tooltip: Tooltip<'a, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { - Element::new(tooltip) - } -} diff --git a/pure/src/widget/tree.rs b/pure/src/widget/tree.rs deleted file mode 100644 index 2f876523..00000000 --- a/pure/src/widget/tree.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! Store internal widget state in a state tree to ensure continuity. -use crate::Widget; - -use std::any::{self, Any}; -use std::borrow::Borrow; - -/// A persistent state widget tree. -/// -/// A [`Tree`] is normally associated with a specific widget in the widget tree. -pub struct Tree { - /// The tag of the [`Tree`]. - pub tag: Tag, - - /// The [`State`] of the [`Tree`]. - pub state: State, - - /// The children of the root widget of the [`Tree`]. - pub children: Vec<Tree>, -} - -impl Tree { - /// Creates an empty, stateless [`Tree`] with no children. - pub fn empty() -> Self { - Self { - tag: Tag::stateless(), - state: State::None, - children: Vec::new(), - } - } - - /// Creates a new [`Tree`] for the provided [`Element`]. - pub fn new<'a, Message, Renderer>( - widget: impl Borrow<dyn Widget<Message, Renderer> + 'a>, - ) -> Self - where - Renderer: iced_native::Renderer, - { - let widget = widget.borrow(); - - Self { - tag: widget.tag(), - state: widget.state(), - children: widget.children(), - } - } - - /// Reconciliates the current tree with the provided [`Element`]. - /// - /// If the tag of the [`Element`] matches the tag of the [`Tree`], then the - /// [`Element`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called). - /// - /// Otherwise, the whole [`Tree`] is recreated. - /// - /// [`Widget::diff`]: crate::Widget::diff - pub fn diff<'a, Message, Renderer>( - &mut self, - new: impl Borrow<dyn Widget<Message, Renderer> + 'a>, - ) where - Renderer: iced_native::Renderer, - { - if self.tag == new.borrow().tag() { - new.borrow().diff(self) - } else { - *self = Self::new(new); - } - } - - /// Reconciliates the children of the tree with the provided list of [`Element`]. - pub fn diff_children<'a, Message, Renderer>( - &mut self, - new_children: &[impl Borrow<dyn Widget<Message, Renderer> + 'a>], - ) where - Renderer: iced_native::Renderer, - { - self.diff_children_custom( - new_children, - |tree, widget| tree.diff(widget.borrow()), - |widget| Self::new(widget.borrow()), - ) - } - - /// Reconciliates the children of the tree with the provided list of [`Element`] using custom - /// logic both for diffing and creating new widget state. - pub fn diff_children_custom<T>( - &mut self, - new_children: &[T], - diff: impl Fn(&mut Tree, &T), - new_state: impl Fn(&T) -> Self, - ) { - if self.children.len() > new_children.len() { - self.children.truncate(new_children.len()); - } - - for (child_state, new) in - self.children.iter_mut().zip(new_children.iter()) - { - diff(child_state, new); - } - - if self.children.len() < new_children.len() { - self.children.extend( - new_children[self.children.len()..].iter().map(new_state), - ); - } - } -} - -/// The identifier of some widget state. -#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct Tag(any::TypeId); - -impl Tag { - /// Creates a [`Tag`] for a state of type `T`. - pub fn of<T>() -> Self - where - T: 'static, - { - Self(any::TypeId::of::<T>()) - } - - /// Creates a [`Tag`] for a stateless widget. - pub fn stateless() -> Self { - Self::of::<()>() - } -} - -/// The internal [`State`] of a widget. -pub enum State { - /// No meaningful internal state. - None, - - /// Some meaningful internal state. - Some(Box<dyn Any>), -} - -impl State { - /// Creates a new [`State`]. - pub fn new<T>(state: T) -> Self - where - T: 'static, - { - State::Some(Box::new(state)) - } - - /// Downcasts the [`State`] to `T` and returns a reference to it. - /// - /// # Panics - /// This method will panic if the downcast fails or the [`State`] is [`State::None`]. - pub fn downcast_ref<T>(&self) -> &T - where - T: 'static, - { - match self { - State::None => panic!("Downcast on stateless state"), - State::Some(state) => { - state.downcast_ref().expect("Downcast widget state") - } - } - } - - /// Downcasts the [`State`] to `T` and returns a mutable reference to it. - /// - /// # Panics - /// This method will panic if the downcast fails or the [`State`] is [`State::None`]. - pub fn downcast_mut<T>(&mut self) -> &mut T - where - T: 'static, - { - match self { - State::None => panic!("Downcast on stateless state"), - State::Some(state) => { - state.downcast_mut().expect("Downcast widget state") - } - } - } -} |