From 42c423b4a89613c4e1c552c891c1391a34837122 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 15 Jul 2023 10:04:25 -0700 Subject: Add viewport to Widget::on_event --- core/src/element.rs | 9 ++++++--- core/src/widget.rs | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/src/element.rs b/core/src/element.rs index 3268f14b..b9b76247 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -380,6 +380,7 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, B>, + viewport: &Rectangle, ) -> event::Status { let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); @@ -392,6 +393,7 @@ where renderer, clipboard, &mut local_shell, + viewport, ); shell.merge(local_shell, &self.mapper); @@ -511,10 +513,11 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, + viewport: &Rectangle, ) -> event::Status { - self.element - .widget - .on_event(state, event, layout, cursor, renderer, clipboard, shell) + self.element.widget.on_event( + state, event, layout, cursor, renderer, clipboard, shell, viewport, + ) } fn draw( diff --git a/core/src/widget.rs b/core/src/widget.rs index 79d86444..25c1cae8 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -115,6 +115,7 @@ where _renderer: &Renderer, _clipboard: &mut dyn Clipboard, _shell: &mut Shell<'_, Message>, + _viewport: &Rectangle, ) -> event::Status { event::Status::Ignored } -- cgit From e2ba7ece83f141c149659747977147392df008f4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 27 Jul 2023 01:02:28 +0200 Subject: Introduce `visible_bounds` operation for `Container` --- core/src/element.rs | 11 ++++++++--- core/src/overlay/element.rs | 7 +++++-- core/src/overlay/group.rs | 2 +- core/src/rectangle.rs | 15 +++++++++++++++ core/src/widget/operation.rs | 32 ++++++++++++++++++++++++++------ core/src/widget/operation/focusable.rs | 6 ++++++ core/src/widget/operation/scrollable.rs | 19 +++++++++++++++++-- core/src/widget/operation/text_input.rs | 5 +++++ 8 files changed, 83 insertions(+), 14 deletions(-) (limited to 'core') diff --git a/core/src/element.rs b/core/src/element.rs index b9b76247..d2c6358b 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -5,7 +5,9 @@ use crate::overlay; use crate::renderer; use crate::widget; use crate::widget::tree::{self, Tree}; -use crate::{Clipboard, Color, Layout, Length, Rectangle, Shell, Widget}; +use crate::{ + Clipboard, Color, Layout, Length, Rectangle, Shell, Vector, Widget, +}; use std::any::Any; use std::borrow::Borrow; @@ -325,11 +327,12 @@ where fn container( &mut self, id: Option<&widget::Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut( &mut dyn widget::Operation, ), ) { - self.operation.container(id, &mut |operation| { + self.operation.container(id, bounds, &mut |operation| { operate_on_children(&mut MapOperation { operation }); }); } @@ -346,8 +349,10 @@ where &mut self, state: &mut dyn widget::operation::Scrollable, id: Option<&widget::Id>, + bounds: Rectangle, + translation: Vector, ) { - self.operation.scrollable(state, id); + self.operation.scrollable(state, id, bounds, translation); } fn text_input( diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index c2134343..29b404b8 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -172,11 +172,12 @@ where fn container( &mut self, id: Option<&widget::Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut( &mut dyn widget::Operation, ), ) { - self.operation.container(id, &mut |operation| { + self.operation.container(id, bounds, &mut |operation| { operate_on_children(&mut MapOperation { operation }); }); } @@ -193,8 +194,10 @@ where &mut self, state: &mut dyn widget::operation::Scrollable, id: Option<&widget::Id>, + bounds: Rectangle, + translation: Vector, ) { - self.operation.scrollable(state, id); + self.operation.scrollable(state, id, bounds, translation); } fn text_input( diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index deffaad0..691686cd 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -138,7 +138,7 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.children.iter_mut().zip(layout.children()).for_each( |(child, layout)| { child.operate(layout, renderer, operation); diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 7ff324cb..db56aa18 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -197,3 +197,18 @@ where } } } + +impl std::ops::Sub> for Rectangle +where + T: std::ops::Sub, +{ + type Output = Rectangle; + + fn sub(self, translation: Vector) -> Self { + Rectangle { + x: self.x - translation.x, + y: self.y - translation.y, + ..self + } + } +} diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs index ad188c36..b91cf9ac 100644 --- a/core/src/widget/operation.rs +++ b/core/src/widget/operation.rs @@ -8,6 +8,7 @@ pub use scrollable::Scrollable; pub use text_input::TextInput; use crate::widget::Id; +use crate::{Rectangle, Vector}; use std::any::Any; use std::fmt; @@ -23,6 +24,7 @@ pub trait Operation { fn container( &mut self, id: Option<&Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ); @@ -30,7 +32,14 @@ pub trait Operation { fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {} /// Operates on a widget that can be scrolled. - fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {} + fn scrollable( + &mut self, + _state: &mut dyn Scrollable, + _id: Option<&Id>, + _bounds: Rectangle, + _translation: Vector, + ) { + } /// Operates on a widget that has text input. fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {} @@ -92,6 +101,7 @@ where fn container( &mut self, id: Option<&Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { struct MapRef<'a, A> { @@ -102,11 +112,12 @@ where fn container( &mut self, id: Option<&Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { let Self { operation, .. } = self; - operation.container(id, &mut |operation| { + operation.container(id, bounds, &mut |operation| { operate_on_children(&mut MapRef { operation }); }); } @@ -115,8 +126,10 @@ where &mut self, state: &mut dyn Scrollable, id: Option<&Id>, + bounds: Rectangle, + translation: Vector, ) { - self.operation.scrollable(state, id); + self.operation.scrollable(state, id, bounds, translation); } fn focusable( @@ -145,15 +158,21 @@ where MapRef { operation: operation.as_mut(), } - .container(id, operate_on_children); + .container(id, bounds, operate_on_children); } fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { self.operation.focusable(state, id); } - fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { - self.operation.scrollable(state, id); + fn scrollable( + &mut self, + state: &mut dyn Scrollable, + id: Option<&Id>, + bounds: Rectangle, + translation: Vector, + ) { + self.operation.scrollable(state, id, bounds, translation); } fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { @@ -197,6 +216,7 @@ pub fn scope( fn container( &mut self, id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { if id == Some(&self.target) { diff --git a/core/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs index 312e4894..ab1b677e 100644 --- a/core/src/widget/operation/focusable.rs +++ b/core/src/widget/operation/focusable.rs @@ -1,6 +1,7 @@ //! Operate on widgets that can be focused. use crate::widget::operation::{Operation, Outcome}; use crate::widget::Id; +use crate::Rectangle; /// The internal state of a widget that can be focused. pub trait Focusable { @@ -45,6 +46,7 @@ pub fn focus(target: Id) -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) @@ -80,6 +82,7 @@ where fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) @@ -126,6 +129,7 @@ pub fn focus_previous() -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) @@ -159,6 +163,7 @@ pub fn focus_next() -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) @@ -185,6 +190,7 @@ pub fn find_focused() -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) diff --git a/core/src/widget/operation/scrollable.rs b/core/src/widget/operation/scrollable.rs index f947344d..4f8b2a98 100644 --- a/core/src/widget/operation/scrollable.rs +++ b/core/src/widget/operation/scrollable.rs @@ -1,5 +1,6 @@ //! Operate on widgets that can be scrolled. use crate::widget::{Id, Operation}; +use crate::{Rectangle, Vector}; /// The internal state of a widget that can be scrolled. pub trait Scrollable { @@ -22,12 +23,19 @@ pub fn snap_to(target: Id, offset: RelativeOffset) -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) } - fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { + fn scrollable( + &mut self, + state: &mut dyn Scrollable, + id: Option<&Id>, + _bounds: Rectangle, + _translation: Vector, + ) { if Some(&self.target) == id { state.snap_to(self.offset); } @@ -49,12 +57,19 @@ pub fn scroll_to(target: Id, offset: AbsoluteOffset) -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) } - fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { + fn scrollable( + &mut self, + state: &mut dyn Scrollable, + id: Option<&Id>, + _bounds: Rectangle, + _translation: Vector, + ) { if Some(&self.target) == id { state.scroll_to(self.offset); } diff --git a/core/src/widget/operation/text_input.rs b/core/src/widget/operation/text_input.rs index 4c773e99..a9ea2e81 100644 --- a/core/src/widget/operation/text_input.rs +++ b/core/src/widget/operation/text_input.rs @@ -1,6 +1,7 @@ //! Operate on widgets that have text input. use crate::widget::operation::Operation; use crate::widget::Id; +use crate::Rectangle; /// The internal state of a widget that has text input. pub trait TextInput { @@ -34,6 +35,7 @@ pub fn move_cursor_to_front(target: Id) -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) @@ -63,6 +65,7 @@ pub fn move_cursor_to_end(target: Id) -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) @@ -93,6 +96,7 @@ pub fn move_cursor_to(target: Id, position: usize) -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) @@ -121,6 +125,7 @@ pub fn select_all(target: Id) -> impl Operation { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { operate_on_children(self) -- cgit From 126aef88e7647c4690055b4c96aee46ecadcf60e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 28 Jul 2023 19:48:39 +0200 Subject: Bump versions :tada: --- core/Cargo.toml | 2 +- core/src/lib.rs | 5 +---- core/src/widget.rs | 10 +++++----- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 55f2e85f..8bb37309 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_core" -version = "0.9.0" +version = "0.10.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "The essential concepts of Iced" diff --git a/core/src/lib.rs b/core/src/lib.rs index 76d775e7..c1c8424b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,14 +1,11 @@ //! The core library of [Iced]. //! //! This library holds basic types that can be reused and re-exported in -//! different runtime implementations. For instance, both [`iced_native`] and -//! [`iced_web`] are built on top of `iced_core`. +//! different runtime implementations. //! //! ![The foundations of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true) //! //! [Iced]: https://github.com/iced-rs/iced -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native -//! [`iced_web`]: https://github.com/iced-rs/iced_web #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] diff --git a/core/src/widget.rs b/core/src/widget.rs index 25c1cae8..d6a99208 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -33,12 +33,12 @@ use crate::{Clipboard, Length, Rectangle, Shell}; /// - [`geometry`], a custom widget showcasing how to draw geometry with the /// `Mesh2D` primitive in [`iced_wgpu`]. /// -/// [examples]: https://github.com/iced-rs/iced/tree/0.9/examples -/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.9/examples/bezier_tool -/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.9/examples/custom_widget -/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.9/examples/geometry +/// [examples]: https://github.com/iced-rs/iced/tree/0.10/examples +/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.10/examples/bezier_tool +/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.10/examples/custom_widget +/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.10/examples/geometry /// [`lyon`]: https://github.com/nical/lyon -/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.9/wgpu +/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.10/wgpu pub trait Widget where Renderer: crate::Renderer, -- cgit From f5b95629009ecde8c6f6388c8f33ec43d30d17d1 Mon Sep 17 00:00:00 2001 From: "Andrew Wheeler(Genusis)" Date: Tue, 15 Aug 2023 01:47:53 -0400 Subject: Bounds Contains update. (#2017) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changed the way contains works to exclude <= for point.y and point.x on width and height check to avoid multiple selects * update changelog * Update `CHANGELOG` --------- Co-authored-by: Héctor Ramón Jiménez --- core/src/rectangle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index db56aa18..c1c2eeac 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -74,9 +74,9 @@ impl Rectangle { /// Returns true if the given [`Point`] is contained in the [`Rectangle`]. pub fn contains(&self, point: Point) -> bool { self.x <= point.x - && point.x <= self.x + self.width + && point.x < self.x + self.width && self.y <= point.y - && point.y <= self.y + self.height + && point.y < self.y + self.height } /// Returns true if the current [`Rectangle`] is completely within the given -- cgit From e86363837d8e3a6241a90cb5b895034f07106059 Mon Sep 17 00:00:00 2001 From: lufte Date: Fri, 18 Aug 2023 18:46:22 -0300 Subject: Make the style attribute available on Font --- core/src/font.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'core') diff --git a/core/src/font.rs b/core/src/font.rs index bb425fd6..7f647847 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -10,6 +10,8 @@ pub struct Font { pub weight: Weight, /// The [`Stretch`] of the [`Font`]. pub stretch: Stretch, + /// The [`Style`] of the [`Font`]. + pub style: Style, /// Whether if the [`Font`] is monospaced or not. pub monospaced: bool, } @@ -20,6 +22,7 @@ impl Font { family: Family::SansSerif, weight: Weight::Normal, stretch: Stretch::Normal, + style: Style::Normal, monospaced: false, }; @@ -100,3 +103,13 @@ pub enum Stretch { ExtraExpanded, UltraExpanded, } + +/// The style of some text. +#[allow(missing_docs)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum Style { + #[default] + Normal, + Italic, + Oblique, +} -- cgit From ed3454301e663a7cb7d73cd56b57b188f4d14a2f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 30 Aug 2023 04:31:21 +0200 Subject: Implement explicit text caching in the widget state tree --- core/src/element.rs | 6 +- core/src/layout.rs | 28 +++++++- core/src/layout/flex.rs | 12 ++-- core/src/renderer.rs | 15 +---- core/src/renderer/null.rs | 96 ++++++++++++++++++++------- core/src/text.rs | 161 +++++++++++++++++++++++++++++----------------- core/src/widget.rs | 3 +- core/src/widget/text.rs | 136 +++++++++++++++++++++++++-------------- 8 files changed, 305 insertions(+), 152 deletions(-) (limited to 'core') diff --git a/core/src/element.rs b/core/src/element.rs index d2c6358b..0d23a9e7 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -306,10 +306,11 @@ where fn layout( &self, + tree: &Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.widget.layout(renderer, limits) + self.widget.layout(tree, renderer, limits) } fn operate( @@ -491,10 +492,11 @@ where fn layout( &self, + tree: &Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.element.widget.layout(renderer, limits) + self.element.widget.layout(tree, renderer, limits) } fn operate( diff --git a/core/src/layout.rs b/core/src/layout.rs index 04954fb9..50ccf1f4 100644 --- a/core/src/layout.rs +++ b/core/src/layout.rs @@ -7,7 +7,7 @@ pub mod flex; pub use limits::Limits; pub use node::Node; -use crate::{Point, Rectangle, Vector}; +use crate::{Point, Rectangle, Size, Vector}; /// The bounds of a [`Node`] and its children, using absolute coordinates. #[derive(Debug, Clone, Copy)] @@ -63,3 +63,29 @@ impl<'a> Layout<'a> { }) } } + +/// Produces a [`Node`] with two children nodes one right next to each other. +pub fn next_to_each_other( + limits: &Limits, + spacing: f32, + left: impl FnOnce(&Limits) -> Node, + right: impl FnOnce(&Limits) -> Node, +) -> Node { + let left_node = left(limits); + let left_size = left_node.size(); + + let right_limits = limits.shrink(Size::new(left_size.width + spacing, 0.0)); + + let mut right_node = right(&right_limits); + let right_size = right_node.size(); + + right_node.move_to(Point::new(left_size.width + spacing, 0.0)); + + Node::with_children( + Size::new( + left_size.width + spacing + right_size.width, + left_size.height.max(right_size.height), + ), + vec![left_node, right_node], + ) +} diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index 8b967849..86b1a45b 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -19,6 +19,7 @@ use crate::Element; use crate::layout::{Limits, Node}; +use crate::widget; use crate::{Alignment, Padding, Point, Size}; /// The main axis of a flex layout. @@ -66,6 +67,7 @@ pub fn resolve( spacing: f32, align_items: Alignment, items: &[Element<'_, Message, Renderer>], + trees: &[widget::Tree], ) -> Node where Renderer: crate::Renderer, @@ -81,7 +83,7 @@ where let mut nodes: Vec = Vec::with_capacity(items.len()); nodes.resize(items.len(), Node::default()); - for (i, child) in items.iter().enumerate() { + for (i, (child, tree)) in items.iter().zip(trees).enumerate() { let fill_factor = match axis { Axis::Horizontal => child.as_widget().width(), Axis::Vertical => child.as_widget().height(), @@ -94,7 +96,8 @@ where let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height)); - let layout = child.as_widget().layout(renderer, &child_limits); + let layout = + child.as_widget().layout(tree, renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -108,7 +111,7 @@ where let remaining = available.max(0.0); - for (i, child) in items.iter().enumerate() { + for (i, (child, tree)) in items.iter().zip(trees).enumerate() { let fill_factor = match axis { Axis::Horizontal => child.as_widget().width(), Axis::Vertical => child.as_widget().height(), @@ -133,7 +136,8 @@ where Size::new(max_width, max_height), ); - let layout = child.as_widget().layout(renderer, &child_limits); + let layout = + child.as_widget().layout(tree, renderer, &child_limits); cross = cross.max(axis.cross(layout.size())); nodes[i] = layout; diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 7c73d2e4..1b327e56 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -5,26 +5,13 @@ mod null; #[cfg(debug_assertions)] pub use null::Null; -use crate::layout; -use crate::{Background, BorderRadius, Color, Element, Rectangle, Vector}; +use crate::{Background, BorderRadius, Color, Rectangle, Vector}; /// A component that can be used by widgets to draw themselves on a screen. pub trait Renderer: Sized { /// The supported theme of the [`Renderer`]. type Theme; - /// Lays out the elements of a user interface. - /// - /// You should override this if you need to perform any operations before or - /// after layouting. For instance, trimming the measurements cache. - fn layout( - &mut self, - element: &Element<'_, Message, Self>, - limits: &layout::Limits, - ) -> layout::Node { - element.as_widget().layout(self, limits) - } - /// Draws the primitives recorded in the given closure in a new layer. /// /// The layer will clip its contents to the provided `bounds`. diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 5d49699e..55d58a59 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -1,6 +1,7 @@ +use crate::alignment; use crate::renderer::{self, Renderer}; use crate::text::{self, Text}; -use crate::{Background, Font, Point, Rectangle, Size, Vector}; +use crate::{Background, Color, Font, Pixels, Point, Rectangle, Size, Vector}; use std::borrow::Cow; @@ -41,6 +42,7 @@ impl Renderer for Null { impl text::Renderer for Null { type Font = Font; + type Paragraph = (); const ICON_FONT: Font = Font::DEFAULT; const CHECKMARK_ICON: char = '0'; @@ -50,37 +52,83 @@ impl text::Renderer for Null { Font::default() } - fn default_size(&self) -> f32 { - 16.0 + fn default_size(&self) -> Pixels { + Pixels(16.0) } fn load_font(&mut self, _font: Cow<'static, [u8]>) {} - fn measure( - &self, - _content: &str, - _size: f32, - _line_height: text::LineHeight, - _font: Font, - _bounds: Size, - _shaping: text::Shaping, - ) -> Size { - Size::new(0.0, 20.0) + fn create_paragraph(&self, _text: Text<'_, Self::Font>) -> Self::Paragraph { } - fn hit_test( + fn resize_paragraph( &self, - _contents: &str, - _size: f32, - _line_height: text::LineHeight, - _font: Self::Font, - _bounds: Size, - _shaping: text::Shaping, - _point: Point, - _nearest_only: bool, - ) -> Option { + _paragraph: &mut Self::Paragraph, + _new_bounds: Size, + ) { + } + + fn fill_paragraph( + &mut self, + _paragraph: &Self::Paragraph, + _position: Point, + _color: Color, + ) { + } + + fn fill_text( + &mut self, + _paragraph: Text<'_, Self::Font>, + _position: Point, + _color: Color, + ) { + } +} + +impl text::Paragraph for () { + type Font = Font; + + fn content(&self) -> &str { + "" + } + + fn text_size(&self) -> Pixels { + Pixels(16.0) + } + + fn font(&self) -> Self::Font { + Font::default() + } + + fn line_height(&self) -> text::LineHeight { + text::LineHeight::default() + } + + fn shaping(&self) -> text::Shaping { + text::Shaping::default() + } + + fn horizontal_alignment(&self) -> alignment::Horizontal { + alignment::Horizontal::Left + } + + fn vertical_alignment(&self) -> alignment::Vertical { + alignment::Vertical::Top + } + + fn grapheme_position(&self, _line: usize, _index: usize) -> Option { None } - fn fill_text(&mut self, _text: Text<'_, Self::Font>) {} + fn bounds(&self) -> Size { + Size::ZERO + } + + fn min_bounds(&self) -> Size { + Size::ZERO + } + + fn hit_test(&self, _point: Point) -> Option { + None + } } diff --git a/core/src/text.rs b/core/src/text.rs index fc8aa20e..c59c683a 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -1,6 +1,6 @@ //! Draw and interact with text. use crate::alignment; -use crate::{Color, Pixels, Point, Rectangle, Size}; +use crate::{Color, Pixels, Point, Size}; use std::borrow::Cow; use std::hash::{Hash, Hasher}; @@ -12,17 +12,14 @@ pub struct Text<'a, Font> { pub content: &'a str, /// The bounds of the paragraph. - pub bounds: Rectangle, + pub bounds: Size, /// The size of the [`Text`] in logical pixels. - pub size: f32, + pub size: Pixels, /// The line height of the [`Text`]. pub line_height: LineHeight, - /// The color of the [`Text`]. - pub color: Color, - /// The font of the [`Text`]. pub font: Font, @@ -132,7 +129,10 @@ impl Hit { /// A renderer capable of measuring and drawing [`Text`]. pub trait Renderer: crate::Renderer { /// The font type used. - type Font: Copy; + type Font: Copy + PartialEq; + + /// The [`Paragraph`] of this [`Renderer`]. + type Paragraph: Paragraph + 'static; /// The icon font of the backend. const ICON_FONT: Self::Font; @@ -151,62 +151,107 @@ pub trait Renderer: crate::Renderer { fn default_font(&self) -> Self::Font; /// Returns the default size of [`Text`]. - fn default_size(&self) -> f32; + fn default_size(&self) -> Pixels; - /// Measures the text in the given bounds and returns the minimum boundaries - /// that can fit the contents. - fn measure( + /// Loads a [`Self::Font`] from its bytes. + fn load_font(&mut self, font: Cow<'static, [u8]>); + + /// Creates a new [`Paragraph`] laid out with the given [`Text`]. + fn create_paragraph(&self, text: Text<'_, Self::Font>) -> Self::Paragraph; + + /// Lays out the given [`Paragraph`] with some new boundaries. + fn resize_paragraph( &self, - content: &str, - size: f32, - line_height: LineHeight, - font: Self::Font, - bounds: Size, - shaping: Shaping, - ) -> Size; - - /// Measures the width of the text as if it were laid out in a single line. - fn measure_width( + paragraph: &mut Self::Paragraph, + new_bounds: Size, + ); + + /// Updates a [`Paragraph`] to match the given [`Text`], if needed. + fn update_paragraph( &self, - content: &str, - size: f32, - font: Self::Font, - shaping: Shaping, - ) -> f32 { - let bounds = self.measure( - content, - size, - LineHeight::Absolute(Pixels(size)), - font, - Size::INFINITY, - shaping, - ); - - bounds.width + paragraph: &mut Self::Paragraph, + text: Text<'_, Self::Font>, + ) { + if paragraph.content() != text.content + || paragraph.text_size() != text.size + || paragraph.line_height().to_absolute(text.size) + != text.line_height.to_absolute(text.size) + || paragraph.font() != text.font + || paragraph.shaping() != text.shaping + || paragraph.horizontal_alignment() != text.horizontal_alignment + || paragraph.vertical_alignment() != text.vertical_alignment + { + *paragraph = self.create_paragraph(text); + } else if paragraph.bounds() != text.bounds { + self.resize_paragraph(paragraph, text.bounds); + } } - /// Tests whether the provided point is within the boundaries of text - /// laid out with the given parameters, returning information about - /// the nearest character. - /// - /// If `nearest_only` is true, the hit test does not consider whether the - /// the point is interior to any glyph bounds, returning only the character - /// with the nearest centeroid. - fn hit_test( - &self, - contents: &str, - size: f32, - line_height: LineHeight, - font: Self::Font, - bounds: Size, - shaping: Shaping, - point: Point, - nearest_only: bool, - ) -> Option; + /// Draws the given [`Paragraph`] at the given position and with the given + /// [`Color`]. + fn fill_paragraph( + &mut self, + text: &Self::Paragraph, + position: Point, + color: Color, + ); + + /// Draws the given [`Text`] at the given position and with the given + /// [`Color`]. + fn fill_text( + &mut self, + text: Text<'_, Self::Font>, + position: Point, + color: Color, + ); +} +/// A text paragraph. +pub trait Paragraph: Default { + /// The font of this [`Paragraph`]. + type Font; - /// Loads a [`Self::Font`] from its bytes. - fn load_font(&mut self, font: Cow<'static, [u8]>); + /// Returns the content of the [`Paragraph`]. + fn content(&self) -> &str; - /// Draws the given [`Text`]. - fn fill_text(&mut self, text: Text<'_, Self::Font>); + /// Returns the text size of the [`Paragraph`]. + fn text_size(&self) -> Pixels; + + /// Returns the [`LineHeight`] of the [`Paragraph`]. + fn line_height(&self) -> LineHeight; + + /// Returns the [`Font`] of the [`Paragraph`]. + fn font(&self) -> Self::Font; + + /// Returns the [`Shaping`] strategy of the [`Paragraph`]. + fn shaping(&self) -> Shaping; + + /// Returns the horizontal alignment of the [`Paragraph`]. + fn horizontal_alignment(&self) -> alignment::Horizontal; + + /// Returns the vertical alignment of the [`Paragraph`]. + fn vertical_alignment(&self) -> alignment::Vertical; + + /// Returns the boundaries of the [`Paragraph`]. + fn bounds(&self) -> Size; + + /// Returns the minimum boundaries that can fit the contents of the + /// [`Paragraph`]. + fn min_bounds(&self) -> Size; + + /// Tests whether the provided point is within the boundaries of the + /// [`Paragraph`], returning information about the nearest character. + fn hit_test(&self, point: Point) -> Option; + + /// Returns the distance to the given grapheme index in the [`Paragraph`]. + fn grapheme_position(&self, line: usize, index: usize) -> Option; + + /// Returns the minimum width that can fit the contents of the [`Paragraph`]. + fn min_width(&self) -> f32 { + self.min_bounds().width + } + + /// Returns the minimum height that can fit the contents of the [`Paragraph`]. + fn min_height(&self) -> f32 { + self.min_bounds().height + } } diff --git a/core/src/widget.rs b/core/src/widget.rs index d6a99208..70328ff7 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -55,6 +55,7 @@ where /// user interface. fn layout( &self, + tree: &Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node; @@ -62,7 +63,7 @@ where /// Draws the [`Widget`] using the associated `Renderer`. fn draw( &self, - state: &Tree, + tree: &Tree, renderer: &mut Renderer, theme: &Renderer::Theme, style: &renderer::Style, diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 79df2b02..0405537b 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -3,11 +3,12 @@ use crate::alignment; use crate::layout; use crate::mouse; use crate::renderer; -use crate::text; -use crate::widget::Tree; -use crate::{Color, Element, Layout, Length, Pixels, Rectangle, Widget}; +use crate::text::{self, Paragraph as _}; +use crate::widget::tree::{self, Tree}; +use crate::{Color, Element, Layout, Length, Pixels, Point, Rectangle, Widget}; use std::borrow::Cow; +use std::cell::RefCell; pub use text::{LineHeight, Shaping}; @@ -19,7 +20,7 @@ where Renderer::Theme: StyleSheet, { content: Cow<'a, str>, - size: Option, + size: Option, line_height: LineHeight, width: Length, height: Length, @@ -53,7 +54,7 @@ where /// Sets the size of the [`Text`]. pub fn size(mut self, size: impl Into) -> Self { - self.size = Some(size.into().0); + self.size = Some(size.into()); self } @@ -117,11 +118,23 @@ where } } +/// The internal state of a [`Text`] widget. +#[derive(Debug, Default)] +pub struct State(RefCell); + impl<'a, Message, Renderer> Widget for Text<'a, Renderer> where Renderer: text::Renderer, Renderer::Theme: StyleSheet, { + fn tag(&self) -> tree::Tag { + tree::Tag::of::>() + } + + fn state(&self) -> tree::State { + tree::State::new(State(RefCell::new(Renderer::Paragraph::default()))) + } + fn width(&self) -> Length { self.width } @@ -132,30 +145,29 @@ where fn layout( &self, + tree: &Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - - let size = self.size.unwrap_or_else(|| renderer.default_size()); - - let bounds = renderer.measure( + layout( + tree.state.downcast_ref::>(), + renderer, + limits, + self.width, + self.height, &self.content, - size, self.line_height, - self.font.unwrap_or_else(|| renderer.default_font()), - limits.max(), + self.size, + self.font, + self.horizontal_alignment, + self.vertical_alignment, self.shaping, - ); - - let size = limits.resolve(bounds); - - layout::Node::new(size) + ) } fn draw( &self, - _state: &Tree, + tree: &Tree, renderer: &mut Renderer, theme: &Renderer::Theme, style: &renderer::Style, @@ -163,22 +175,63 @@ where _cursor_position: mouse::Cursor, _viewport: &Rectangle, ) { + let state = tree.state.downcast_ref::>(); + draw( renderer, style, layout, - &self.content, - self.size, - self.line_height, - self.font, + state, theme.appearance(self.style.clone()), - self.horizontal_alignment, - self.vertical_alignment, - self.shaping, ); } } +/// Produces the [`layout::Node`] of a [`Text`] widget. +pub fn layout( + state: &State, + renderer: &Renderer, + limits: &layout::Limits, + width: Length, + height: Length, + content: &str, + line_height: LineHeight, + size: Option, + font: Option, + horizontal_alignment: alignment::Horizontal, + vertical_alignment: alignment::Vertical, + shaping: Shaping, +) -> layout::Node +where + Renderer: text::Renderer, +{ + let limits = limits.width(width).height(height); + let bounds = limits.max(); + + let size = size.unwrap_or_else(|| renderer.default_size()); + let font = font.unwrap_or_else(|| renderer.default_font()); + + let mut paragraph = state.0.borrow_mut(); + + renderer.update_paragraph( + &mut paragraph, + text::Text { + content, + bounds, + size, + line_height, + font, + shaping, + horizontal_alignment, + vertical_alignment, + }, + ); + + let size = limits.resolve(paragraph.min_bounds()); + + layout::Node::new(size) +} + /// Draws text using the same logic as the [`Text`] widget. /// /// Specifically: @@ -193,44 +246,31 @@ pub fn draw( renderer: &mut Renderer, style: &renderer::Style, layout: Layout<'_>, - content: &str, - size: Option, - line_height: LineHeight, - font: Option, + state: &State, appearance: Appearance, - horizontal_alignment: alignment::Horizontal, - vertical_alignment: alignment::Vertical, - shaping: Shaping, ) where Renderer: text::Renderer, { + let paragraph = state.0.borrow(); let bounds = layout.bounds(); - let x = match horizontal_alignment { + let x = match paragraph.horizontal_alignment() { alignment::Horizontal::Left => bounds.x, alignment::Horizontal::Center => bounds.center_x(), alignment::Horizontal::Right => bounds.x + bounds.width, }; - let y = match vertical_alignment { + let y = match paragraph.vertical_alignment() { alignment::Vertical::Top => bounds.y, alignment::Vertical::Center => bounds.center_y(), alignment::Vertical::Bottom => bounds.y + bounds.height, }; - let size = size.unwrap_or_else(|| renderer.default_size()); - - renderer.fill_text(crate::Text { - content, - size, - line_height, - bounds: Rectangle { x, y, ..bounds }, - color: appearance.color.unwrap_or(style.text_color), - font: font.unwrap_or_else(|| renderer.default_font()), - horizontal_alignment, - vertical_alignment, - shaping, - }); + renderer.fill_paragraph( + ¶graph, + Point::new(x, y), + appearance.color.unwrap_or(style.text_color), + ); } impl<'a, Message, Renderer> From> -- cgit From c44611cc7d43b5d8336509030e1181d1ec04ff64 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 30 Aug 2023 05:33:39 +0200 Subject: Fix vertical alignment in `layout::next_to_each_other` --- core/src/layout.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/layout.rs b/core/src/layout.rs index 50ccf1f4..caf315b6 100644 --- a/core/src/layout.rs +++ b/core/src/layout.rs @@ -71,7 +71,7 @@ pub fn next_to_each_other( left: impl FnOnce(&Limits) -> Node, right: impl FnOnce(&Limits) -> Node, ) -> Node { - let left_node = left(limits); + let mut left_node = left(limits); let left_size = left_node.size(); let right_limits = limits.shrink(Size::new(left_size.width + spacing, 0.0)); @@ -79,7 +79,14 @@ pub fn next_to_each_other( let mut right_node = right(&right_limits); let right_size = right_node.size(); - right_node.move_to(Point::new(left_size.width + spacing, 0.0)); + let (left_y, right_y) = if left_size.height > right_size.height { + (0.0, (left_size.height - right_size.height) / 2.0) + } else { + ((right_size.height - left_size.height) / 2.0, 0.0) + }; + + left_node.move_to(Point::new(0.0, left_y)); + right_node.move_to(Point::new(left_size.width + spacing, right_y)); Node::with_children( Size::new( -- cgit From a026e917d3364e58fd827995261158d8cb356ce9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 30 Aug 2023 06:36:24 +0200 Subject: Make `widget::Tree` mutable in `Widget::layout` --- core/src/element.rs | 4 ++-- core/src/layout/flex.rs | 4 ++-- core/src/overlay.rs | 2 +- core/src/overlay/element.rs | 4 ++-- core/src/overlay/group.rs | 4 ++-- core/src/widget.rs | 2 +- core/src/widget/text.rs | 19 +++++++++---------- 7 files changed, 19 insertions(+), 20 deletions(-) (limited to 'core') diff --git a/core/src/element.rs b/core/src/element.rs index 0d23a9e7..02f16bcb 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -306,7 +306,7 @@ where fn layout( &self, - tree: &Tree, + tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { @@ -492,7 +492,7 @@ where fn layout( &self, - tree: &Tree, + tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index 86b1a45b..c02b63d8 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -67,7 +67,7 @@ pub fn resolve( spacing: f32, align_items: Alignment, items: &[Element<'_, Message, Renderer>], - trees: &[widget::Tree], + trees: &mut [widget::Tree], ) -> Node where Renderer: crate::Renderer, @@ -83,7 +83,7 @@ where let mut nodes: Vec = Vec::with_capacity(items.len()); nodes.resize(items.len(), Node::default()); - for (i, (child, tree)) in items.iter().zip(trees).enumerate() { + for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { let fill_factor = match axis { Axis::Horizontal => child.as_widget().width(), Axis::Vertical => child.as_widget().height(), diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 2e05db93..f71f25f7 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -25,7 +25,7 @@ where /// /// [`Node`]: layout::Node fn layout( - &self, + &mut self, renderer: &Renderer, bounds: Size, position: Point, diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index 29b404b8..689e69be 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -54,7 +54,7 @@ where /// Computes the layout of the [`Element`] in the given bounds. pub fn layout( - &self, + &mut self, renderer: &Renderer, bounds: Size, translation: Vector, @@ -150,7 +150,7 @@ where Renderer: crate::Renderer, { fn layout( - &self, + &mut self, renderer: &Renderer, bounds: Size, position: Point, diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index 691686cd..a0bae6bb 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -61,7 +61,7 @@ where Renderer: crate::Renderer, { fn layout( - &self, + &mut self, renderer: &Renderer, bounds: Size, position: Point, @@ -71,7 +71,7 @@ where layout::Node::with_children( bounds, self.children - .iter() + .iter_mut() .map(|child| child.layout(renderer, bounds, translation)) .collect(), ) diff --git a/core/src/widget.rs b/core/src/widget.rs index 70328ff7..294d5984 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -55,7 +55,7 @@ where /// user interface. fn layout( &self, - tree: &Tree, + tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node; diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 0405537b..b349399b 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -3,12 +3,11 @@ use crate::alignment; use crate::layout; use crate::mouse; use crate::renderer; -use crate::text::{self, Paragraph as _}; +use crate::text::{self, Paragraph}; use crate::widget::tree::{self, Tree}; use crate::{Color, Element, Layout, Length, Pixels, Point, Rectangle, Widget}; use std::borrow::Cow; -use std::cell::RefCell; pub use text::{LineHeight, Shaping}; @@ -120,7 +119,7 @@ where /// The internal state of a [`Text`] widget. #[derive(Debug, Default)] -pub struct State(RefCell); +pub struct State(P); impl<'a, Message, Renderer> Widget for Text<'a, Renderer> where @@ -132,7 +131,7 @@ where } fn state(&self) -> tree::State { - tree::State::new(State(RefCell::new(Renderer::Paragraph::default()))) + tree::State::new(State(Renderer::Paragraph::default())) } fn width(&self) -> Length { @@ -145,12 +144,12 @@ where fn layout( &self, - tree: &Tree, + tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { layout( - tree.state.downcast_ref::>(), + tree.state.downcast_mut::>(), renderer, limits, self.width, @@ -189,7 +188,7 @@ where /// Produces the [`layout::Node`] of a [`Text`] widget. pub fn layout( - state: &State, + state: &mut State, renderer: &Renderer, limits: &layout::Limits, width: Length, @@ -211,10 +210,10 @@ where let size = size.unwrap_or_else(|| renderer.default_size()); let font = font.unwrap_or_else(|| renderer.default_font()); - let mut paragraph = state.0.borrow_mut(); + let State(ref mut paragraph) = state; renderer.update_paragraph( - &mut paragraph, + paragraph, text::Text { content, bounds, @@ -251,7 +250,7 @@ pub fn draw( ) where Renderer: text::Renderer, { - let paragraph = state.0.borrow(); + let State(ref paragraph) = state; let bounds = layout.bounds(); let x = match paragraph.horizontal_alignment() { -- cgit From b51ffe53ed540d27022157dfb202364825806699 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 30 Aug 2023 06:44:40 +0200 Subject: Fix unnecessary dereference in `widget::text` --- core/src/widget/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index b349399b..53ed463e 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -266,7 +266,7 @@ pub fn draw( }; renderer.fill_paragraph( - ¶graph, + paragraph, Point::new(x, y), appearance.color.unwrap_or(style.text_color), ); -- cgit From 020fb3c37794fcfa2670c3c0ada949aee95855a0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 3 Sep 2023 01:01:33 +0200 Subject: Fix `iced_wgpu` device selection on Wasm --- core/src/window/redraw_request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/window/redraw_request.rs b/core/src/window/redraw_request.rs index 3b4f0fd3..8a59e83c 100644 --- a/core/src/window/redraw_request.rs +++ b/core/src/window/redraw_request.rs @@ -13,7 +13,7 @@ pub enum RedrawRequest { #[cfg(test)] mod tests { use super::*; - use std::time::{Duration, Instant}; + use crate::time::{Duration, Instant}; #[test] fn ordering() { -- cgit From 34495bba1c1ffaa4ea2bab46103b5d66e333c51e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 Sep 2023 02:55:09 +0200 Subject: Introduce `keyed::Column` widget --- core/src/widget/tree.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'core') diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index 0af40c33..202cca9a 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -107,6 +107,88 @@ impl Tree { } } +/// Reconciliates the `current_children` with the provided list of widgets using +/// custom logic both for diffing and creating new widget state. +/// +/// The algorithm will try to minimize the impact of diffing by querying the +/// `maybe_changed` closure. +pub fn diff_children_custom_with_search( + current_children: &mut Vec, + new_children: &[T], + diff: impl Fn(&mut Tree, &T), + maybe_changed: impl Fn(usize) -> bool, + new_state: impl Fn(&T) -> Tree, +) { + if new_children.is_empty() { + current_children.clear(); + return; + } + + if current_children.is_empty() { + current_children.extend(new_children.iter().map(new_state)); + return; + } + + let first_maybe_changed = maybe_changed(0); + let last_maybe_changed = maybe_changed(current_children.len() - 1); + + if current_children.len() > new_children.len() { + if !first_maybe_changed && last_maybe_changed { + current_children.truncate(new_children.len()); + } else { + let difference_index = if first_maybe_changed { + 0 + } else { + (1..current_children.len()) + .find(|&i| maybe_changed(i)) + .unwrap_or(0) + }; + + let _ = current_children.splice( + difference_index + ..difference_index + + (current_children.len() - new_children.len()), + std::iter::empty(), + ); + } + } + + if current_children.len() < new_children.len() { + let first_maybe_changed = maybe_changed(0); + let last_maybe_changed = maybe_changed(current_children.len() - 1); + + if !first_maybe_changed && last_maybe_changed { + current_children.extend( + new_children[current_children.len()..].iter().map(new_state), + ); + } else { + let difference_index = if first_maybe_changed { + 0 + } else { + (1..current_children.len()) + .find(|&i| maybe_changed(i)) + .unwrap_or(0) + }; + + let _ = current_children.splice( + difference_index..difference_index, + new_children[difference_index + ..difference_index + + (new_children.len() - current_children.len())] + .iter() + .map(new_state), + ); + } + } + + // TODO: Merge loop with extend logic (?) + for (child_state, new) in + current_children.iter_mut().zip(new_children.iter()) + { + diff(child_state, new); + } +} + /// The identifier of some widget state. #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub struct Tag(any::TypeId); -- cgit From f468e25d0c67a01ee79d892f6e8ba9be019f06c7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 4 Sep 2023 12:58:41 +0200 Subject: Use workspace dependencies and package inheritance We are also taking this as a chance to synchronize the versions of all the crates! Because of this, we will skip the `0.11` version. --- core/Cargo.toml | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 8bb37309..8859e91e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,24 +1,26 @@ [package] name = "iced_core" -version = "0.10.0" -authors = ["Héctor Ramón Jiménez "] -edition = "2021" -description = "The essential concepts of Iced" -license = "MIT" -repository = "https://github.com/iced-rs/iced" +description = "The essential ideas of iced" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +categories.workspace = true +keywords.workspace = true [dependencies] -bitflags = "1.2" -thiserror = "1" -log = "0.4.17" -twox-hash = { version = "1.5", default-features = false } +bitflags.workspace = true +log.workspace = true +thiserror.workspace = true +twox-hash.workspace = true -[dependencies.palette] -version = "0.7" -optional = true +palette.workspace = true +palette.optional = true [target.'cfg(target_arch = "wasm32")'.dependencies] -instant = "0.1" +instant.workspace = true [dev-dependencies] approx = "0.5" -- cgit From d2294737c2e28b3b3050d7bf820bbca09896b00e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 8 Sep 2023 01:58:52 +0200 Subject: Use `Radians` as a number directly in `gradient` example --- core/Cargo.toml | 1 + core/src/angle.rs | 45 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 8859e91e..7acb7511 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,6 +15,7 @@ bitflags.workspace = true log.workspace = true thiserror.workspace = true twox-hash.workspace = true +num-traits.workspace = true palette.workspace = true palette.optional = true diff --git a/core/src/angle.rs b/core/src/angle.rs index 75a57c76..91fc2ba7 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -1,17 +1,56 @@ use crate::{Point, Rectangle, Vector}; + use std::f32::consts::PI; +use std::ops::RangeInclusive; -#[derive(Debug, Copy, Clone, PartialEq)] /// Degrees +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub struct Degrees(pub f32); -#[derive(Debug, Copy, Clone, PartialEq)] /// Radians +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub struct Radians(pub f32); +impl Radians { + /// The range of radians of a circle. + pub const RANGE: RangeInclusive = Radians(0.0)..=Radians(2.0 * PI); +} + impl From for Radians { fn from(degrees: Degrees) -> Self { - Radians(degrees.0 * PI / 180.0) + Self(degrees.0 * PI / 180.0) + } +} + +impl From for Radians { + fn from(radians: f32) -> Self { + Self(radians) + } +} + +impl From for Radians { + fn from(radians: u8) -> Self { + Self(f32::from(radians)) + } +} + +impl From for f64 { + fn from(radians: Radians) -> Self { + Self::from(radians.0) + } +} + +impl num_traits::FromPrimitive for Radians { + fn from_i64(n: i64) -> Option { + Some(Self(n as f32)) + } + + fn from_u64(n: u64) -> Option { + Some(Self(n as f32)) + } + + fn from_f64(n: f64) -> Option { + Some(Self(n as f32)) } } -- cgit From 90cbab18b95a2a90a6a527280a6ca461203ea1b3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 8 Sep 2023 02:36:17 +0200 Subject: Fine-tune `Radians::to_distance` An angle of 0 radians will "point" to the top-center of a `Rectangle` and then increase clockwise. --- core/src/angle.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'core') diff --git a/core/src/angle.rs b/core/src/angle.rs index 91fc2ba7..c8f3f013 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -1,6 +1,6 @@ use crate::{Point, Rectangle, Vector}; -use std::f32::consts::PI; +use std::f32::consts::{FRAC_PI_2, PI}; use std::ops::RangeInclusive; /// Degrees @@ -57,15 +57,16 @@ impl num_traits::FromPrimitive for Radians { impl Radians { /// Calculates the line in which the [`Angle`] intercepts the `bounds`. pub fn to_distance(&self, bounds: &Rectangle) -> (Point, Point) { - let v1 = Vector::new(f32::cos(self.0), f32::sin(self.0)); + let angle = self.0 - FRAC_PI_2; + let r = Vector::new(f32::cos(angle), f32::sin(angle)); - let distance_to_rect = f32::min( - f32::abs((bounds.y - bounds.center().y) / v1.y), - f32::abs(((bounds.x + bounds.width) - bounds.center().x) / v1.x), + let distance_to_rect = f32::max( + f32::abs(r.x * bounds.width / 2.0), + f32::abs(r.y * bounds.height / 2.0), ); - let start = bounds.center() + v1 * distance_to_rect; - let end = bounds.center() - v1 * distance_to_rect; + let start = bounds.center() - r * distance_to_rect; + let end = bounds.center() + r * distance_to_rect; (start, end) } -- cgit From 3450987355be7fe029db112474d06613929b54c7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 9 Sep 2023 11:21:32 +0200 Subject: Invalidate existing paragraphs when new fonts are loaded --- core/src/text.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 12 deletions(-) (limited to 'core') diff --git a/core/src/text.rs b/core/src/text.rs index c59c683a..f5e9abc4 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -172,18 +172,14 @@ pub trait Renderer: crate::Renderer { paragraph: &mut Self::Paragraph, text: Text<'_, Self::Font>, ) { - if paragraph.content() != text.content - || paragraph.text_size() != text.size - || paragraph.line_height().to_absolute(text.size) - != text.line_height.to_absolute(text.size) - || paragraph.font() != text.font - || paragraph.shaping() != text.shaping - || paragraph.horizontal_alignment() != text.horizontal_alignment - || paragraph.vertical_alignment() != text.vertical_alignment - { - *paragraph = self.create_paragraph(text); - } else if paragraph.bounds() != text.bounds { - self.resize_paragraph(paragraph, text.bounds); + match compare(paragraph, text) { + Difference::None => {} + Difference::Bounds => { + self.resize_paragraph(paragraph, text.bounds); + } + Difference::Shape => { + *paragraph = self.create_paragraph(text); + } } } @@ -255,3 +251,51 @@ pub trait Paragraph: Default { self.min_bounds().height } } + +/// The difference detected in some text. +/// +/// You will obtain a [`Difference`] when you [`compare`] a [`Paragraph`] with some +/// [`Text`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Difference { + /// No difference. + /// + /// The text can be reused as it is! + None, + + /// A bounds difference. + /// + /// This normally means a relayout is necessary, but the shape of the text can + /// be reused. + Bounds, + + /// A shape difference. + /// + /// The contents, alignment, sizes, fonts, or any other essential attributes + /// of the shape of the text have changed. A complete reshape and relayout of + /// the text is necessary. + Shape, +} + +/// Compares a [`Paragraph`] with some desired [`Text`] and returns the +/// [`Difference`]. +pub fn compare( + paragraph: &impl Paragraph, + text: Text<'_, Font>, +) -> Difference { + if paragraph.content() != text.content + || paragraph.text_size() != text.size + || paragraph.line_height().to_absolute(text.size) + != text.line_height.to_absolute(text.size) + || paragraph.font() != text.font + || paragraph.shaping() != text.shaping + || paragraph.horizontal_alignment() != text.horizontal_alignment + || paragraph.vertical_alignment() != text.vertical_alignment + { + Difference::Shape + } else if paragraph.bounds() != text.bounds { + Difference::Bounds + } else { + Difference::None + } +} -- cgit From 89d9f1d7d2202029028a487df1dd11b0665a7517 Mon Sep 17 00:00:00 2001 From: Matthias Vogelgesang Date: Sat, 9 Sep 2023 12:24:47 +0200 Subject: Fix majority of unresolved documentation links --- core/src/angle.rs | 2 +- core/src/gradient.rs | 2 +- core/src/shell.rs | 4 ++-- core/src/window/icon.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/src/angle.rs b/core/src/angle.rs index c8f3f013..102b69cf 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -55,7 +55,7 @@ impl num_traits::FromPrimitive for Radians { } impl Radians { - /// Calculates the line in which the [`Angle`] intercepts the `bounds`. + /// Calculates the line in which the angle intercepts the `bounds`. pub fn to_distance(&self, bounds: &Rectangle) -> (Point, Point) { let angle = self.0 - FRAC_PI_2; let r = Vector::new(f32::cos(angle), f32::sin(angle)); diff --git a/core/src/gradient.rs b/core/src/gradient.rs index e19622fb..576c9e4d 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -9,7 +9,7 @@ use std::cmp::Ordering; /// /// For a gradient which can be used as a fill on a canvas, see [`iced_graphics::Gradient`]. pub enum Gradient { - /// A linear gradient interpolates colors along a direction at a specific [`Angle`]. + /// A linear gradient interpolates colors along a direction at a specific angle. Linear(Linear), } diff --git a/core/src/shell.rs b/core/src/shell.rs index 74a5c616..246c937a 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -35,7 +35,7 @@ impl<'a, Message> Shell<'a, Message> { self.messages.push(message); } - /// Requests a new frame to be drawn at the given [`Instant`]. + /// Requests a new frame to be drawn. pub fn request_redraw(&mut self, request: window::RedrawRequest) { match self.redraw_request { None => { @@ -48,7 +48,7 @@ impl<'a, Message> Shell<'a, Message> { } } - /// Returns the requested [`Instant`] a redraw should happen, if any. + /// Returns the request a redraw should happen, if any. pub fn redraw_request(&self) -> Option { self.redraw_request } diff --git a/core/src/window/icon.rs b/core/src/window/icon.rs index 31868ecf..2fc48e3b 100644 --- a/core/src/window/icon.rs +++ b/core/src/window/icon.rs @@ -49,7 +49,7 @@ impl Icon { } #[derive(Debug, thiserror::Error)] -/// An error produced when using [`Icon::from_rgba`] with invalid arguments. +/// An error produced when using [`from_rgba`] with invalid arguments. pub enum Error { /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be /// safely interpreted as 32bpp RGBA pixels. -- cgit From f60884f6f8639f75258c264bf4a15591351ef05b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 9 Sep 2023 20:58:45 +0200 Subject: Deny `broken_intradoc_links` and verify documentation in CI --- core/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/lib.rs b/core/src/lib.rs index c1c8424b..1bfba7bd 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,6 +9,7 @@ #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] +#![forbid(unsafe_code, rust_2018_idioms)] #![deny( missing_debug_implementations, missing_docs, @@ -17,9 +18,9 @@ clippy::from_over_into, clippy::needless_borrow, clippy::new_without_default, - clippy::useless_conversion + clippy::useless_conversion, + rustdoc::broken_intra_doc_links )] -#![forbid(unsafe_code, rust_2018_idioms)] #![allow(clippy::inherent_to_string, clippy::type_complexity)] pub mod alignment; pub mod clipboard; -- cgit From 419d9374b79b39293ba9a17967c2356d29377d8f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 9 Sep 2023 21:08:23 +0200 Subject: Fix outstanding broken intradoc links --- core/src/gradient.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'core') diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 576c9e4d..4a0d5ea0 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -6,8 +6,6 @@ use std::cmp::Ordering; #[derive(Debug, Clone, Copy, PartialEq)] /// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), /// or conically (TBD). -/// -/// For a gradient which can be used as a fill on a canvas, see [`iced_graphics::Gradient`]. pub enum Gradient { /// A linear gradient interpolates colors along a direction at a specific angle. Linear(Linear), -- cgit From b42b24b79a097aab10006490cd2ec8a2332fd5d0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 10 Sep 2023 00:55:46 +0200 Subject: Fix (more) broken intradoc links Good thing I just set up CI earlier for this :sweat_smile: --- core/src/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/text.rs b/core/src/text.rs index f5e9abc4..0e3617b1 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -215,7 +215,7 @@ pub trait Paragraph: Default { /// Returns the [`LineHeight`] of the [`Paragraph`]. fn line_height(&self) -> LineHeight; - /// Returns the [`Font`] of the [`Paragraph`]. + /// Returns the [`Self::Font`] of the [`Paragraph`]. fn font(&self) -> Self::Font; /// Returns the [`Shaping`] strategy of the [`Paragraph`]. -- cgit From 346af3f8b0baa418fd37b878bc2930ff0bd57cc0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 11 Sep 2023 02:47:24 +0200 Subject: Make `FontSystem` global and simplify `Paragraph` API --- core/src/renderer/null.rs | 34 +++--------------- core/src/text.rs | 90 ++++++++++++----------------------------------- core/src/widget/text.rs | 23 ++++++------ 3 files changed, 37 insertions(+), 110 deletions(-) (limited to 'core') diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 55d58a59..0ffd3649 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -58,16 +58,6 @@ impl text::Renderer for Null { fn load_font(&mut self, _font: Cow<'static, [u8]>) {} - fn create_paragraph(&self, _text: Text<'_, Self::Font>) -> Self::Paragraph { - } - - fn resize_paragraph( - &self, - _paragraph: &mut Self::Paragraph, - _new_bounds: Size, - ) { - } - fn fill_paragraph( &mut self, _paragraph: &Self::Paragraph, @@ -88,24 +78,12 @@ impl text::Renderer for Null { impl text::Paragraph for () { type Font = Font; - fn content(&self) -> &str { - "" - } - - fn text_size(&self) -> Pixels { - Pixels(16.0) - } - - fn font(&self) -> Self::Font { - Font::default() - } + fn with_text(_text: Text<'_, Self::Font>) -> Self {} - fn line_height(&self) -> text::LineHeight { - text::LineHeight::default() - } + fn resize(&mut self, _new_bounds: Size) {} - fn shaping(&self) -> text::Shaping { - text::Shaping::default() + fn compare(&self, _text: Text<'_, Self::Font>) -> text::Difference { + text::Difference::None } fn horizontal_alignment(&self) -> alignment::Horizontal { @@ -120,10 +98,6 @@ impl text::Paragraph for () { None } - fn bounds(&self) -> Size { - Size::ZERO - } - fn min_bounds(&self) -> Size { Size::ZERO } diff --git a/core/src/text.rs b/core/src/text.rs index 0e3617b1..ff85696e 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -156,33 +156,6 @@ pub trait Renderer: crate::Renderer { /// Loads a [`Self::Font`] from its bytes. fn load_font(&mut self, font: Cow<'static, [u8]>); - /// Creates a new [`Paragraph`] laid out with the given [`Text`]. - fn create_paragraph(&self, text: Text<'_, Self::Font>) -> Self::Paragraph; - - /// Lays out the given [`Paragraph`] with some new boundaries. - fn resize_paragraph( - &self, - paragraph: &mut Self::Paragraph, - new_bounds: Size, - ); - - /// Updates a [`Paragraph`] to match the given [`Text`], if needed. - fn update_paragraph( - &self, - paragraph: &mut Self::Paragraph, - text: Text<'_, Self::Font>, - ) { - match compare(paragraph, text) { - Difference::None => {} - Difference::Bounds => { - self.resize_paragraph(paragraph, text.bounds); - } - Difference::Shape => { - *paragraph = self.create_paragraph(text); - } - } - } - /// Draws the given [`Paragraph`] at the given position and with the given /// [`Color`]. fn fill_paragraph( @@ -201,25 +174,21 @@ pub trait Renderer: crate::Renderer { color: Color, ); } + /// A text paragraph. -pub trait Paragraph: Default { +pub trait Paragraph: Sized + Default { /// The font of this [`Paragraph`]. - type Font; - - /// Returns the content of the [`Paragraph`]. - fn content(&self) -> &str; - - /// Returns the text size of the [`Paragraph`]. - fn text_size(&self) -> Pixels; + type Font: Copy + PartialEq; - /// Returns the [`LineHeight`] of the [`Paragraph`]. - fn line_height(&self) -> LineHeight; + /// Creates a new [`Paragraph`] laid out with the given [`Text`]. + fn with_text(text: Text<'_, Self::Font>) -> Self; - /// Returns the [`Self::Font`] of the [`Paragraph`]. - fn font(&self) -> Self::Font; + /// Lays out the [`Paragraph`] with some new boundaries. + fn resize(&mut self, new_bounds: Size); - /// Returns the [`Shaping`] strategy of the [`Paragraph`]. - fn shaping(&self) -> Shaping; + /// Compares the [`Paragraph`] with some desired [`Text`] and returns the + /// [`Difference`]. + fn compare(&self, text: Text<'_, Self::Font>) -> Difference; /// Returns the horizontal alignment of the [`Paragraph`]. fn horizontal_alignment(&self) -> alignment::Horizontal; @@ -227,9 +196,6 @@ pub trait Paragraph: Default { /// Returns the vertical alignment of the [`Paragraph`]. fn vertical_alignment(&self) -> alignment::Vertical; - /// Returns the boundaries of the [`Paragraph`]. - fn bounds(&self) -> Size; - /// Returns the minimum boundaries that can fit the contents of the /// [`Paragraph`]. fn min_bounds(&self) -> Size; @@ -241,6 +207,19 @@ pub trait Paragraph: Default { /// Returns the distance to the given grapheme index in the [`Paragraph`]. fn grapheme_position(&self, line: usize, index: usize) -> Option; + /// Updates the [`Paragraph`] to match the given [`Text`], if needed. + fn update(&mut self, text: Text<'_, Self::Font>) { + match self.compare(text) { + Difference::None => {} + Difference::Bounds => { + self.resize(text.bounds); + } + Difference::Shape => { + *self = Self::with_text(text); + } + } + } + /// Returns the minimum width that can fit the contents of the [`Paragraph`]. fn min_width(&self) -> f32 { self.min_bounds().width @@ -276,26 +255,3 @@ pub enum Difference { /// the text is necessary. Shape, } - -/// Compares a [`Paragraph`] with some desired [`Text`] and returns the -/// [`Difference`]. -pub fn compare( - paragraph: &impl Paragraph, - text: Text<'_, Font>, -) -> Difference { - if paragraph.content() != text.content - || paragraph.text_size() != text.size - || paragraph.line_height().to_absolute(text.size) - != text.line_height.to_absolute(text.size) - || paragraph.font() != text.font - || paragraph.shaping() != text.shaping - || paragraph.horizontal_alignment() != text.horizontal_alignment - || paragraph.vertical_alignment() != text.vertical_alignment - { - Difference::Shape - } else if paragraph.bounds() != text.bounds { - Difference::Bounds - } else { - Difference::None - } -} diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 53ed463e..c7c9f539 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -212,19 +212,16 @@ where let State(ref mut paragraph) = state; - renderer.update_paragraph( - paragraph, - text::Text { - content, - bounds, - size, - line_height, - font, - shaping, - horizontal_alignment, - vertical_alignment, - }, - ); + paragraph.update(text::Text { + content, + bounds, + size, + line_height, + font, + shaping, + horizontal_alignment, + vertical_alignment, + }); let size = limits.resolve(paragraph.min_bounds()); -- cgit From 6448429103c9c82b90040ac5a5a097bdded23f82 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 12 Sep 2023 14:51:00 +0200 Subject: Draft `Editor` API and `TextEditor` widget --- core/src/layout/limits.rs | 2 +- core/src/lib.rs | 2 +- core/src/renderer/null.rs | 38 ++++++++++++++ core/src/text.rs | 123 ++++++++++++++++----------------------------- core/src/text/editor.rs | 68 +++++++++++++++++++++++++ core/src/text/paragraph.rs | 59 ++++++++++++++++++++++ 6 files changed, 209 insertions(+), 83 deletions(-) create mode 100644 core/src/text/editor.rs create mode 100644 core/src/text/paragraph.rs (limited to 'core') diff --git a/core/src/layout/limits.rs b/core/src/layout/limits.rs index 5d3c1556..39a3d98b 100644 --- a/core/src/layout/limits.rs +++ b/core/src/layout/limits.rs @@ -2,7 +2,7 @@ use crate::{Length, Padding, Size}; /// A set of size constraints for layouting. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Limits { min: Size, max: Size, diff --git a/core/src/lib.rs b/core/src/lib.rs index 1bfba7bd..13a9f06b 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -12,7 +12,7 @@ #![forbid(unsafe_code, rust_2018_idioms)] #![deny( missing_debug_implementations, - missing_docs, + // missing_docs, unused_results, clippy::extra_unused_lifetimes, clippy::from_over_into, diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 0ffd3649..adf75969 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -43,6 +43,7 @@ impl Renderer for Null { impl text::Renderer for Null { type Font = Font; type Paragraph = (); + type Editor = (); const ICON_FONT: Font = Font::DEFAULT; const CHECKMARK_ICON: char = '0'; @@ -66,6 +67,14 @@ impl text::Renderer for Null { ) { } + fn fill_editor( + &mut self, + _editor: &Self::Editor, + _position: Point, + _color: Color, + ) { + } + fn fill_text( &mut self, _paragraph: Text<'_, Self::Font>, @@ -106,3 +115,32 @@ impl text::Paragraph for () { None } } + +impl text::Editor for () { + type Font = Font; + + fn with_text(_text: &str) -> Self {} + + fn cursor(&self) -> text::editor::Cursor { + text::editor::Cursor::Caret(Point::ORIGIN) + } + + fn perform(&mut self, _action: text::editor::Action) {} + + fn bounds(&self) -> Size { + Size::ZERO + } + + fn min_bounds(&self) -> Size { + Size::ZERO + } + + fn update( + &mut self, + _new_bounds: Size, + _new_font: Self::Font, + _new_size: Pixels, + _new_line_height: text::LineHeight, + ) { + } +} diff --git a/core/src/text.rs b/core/src/text.rs index ff85696e..5aacbcc5 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -1,4 +1,11 @@ //! Draw and interact with text. +mod paragraph; + +pub mod editor; + +pub use editor::Editor; +pub use paragraph::Paragraph; + use crate::alignment; use crate::{Color, Pixels, Point, Size}; @@ -126,6 +133,31 @@ impl Hit { } } +/// The difference detected in some text. +/// +/// You will obtain a [`Difference`] when you [`compare`] a [`Paragraph`] with some +/// [`Text`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Difference { + /// No difference. + /// + /// The text can be reused as it is! + None, + + /// A bounds difference. + /// + /// This normally means a relayout is necessary, but the shape of the text can + /// be reused. + Bounds, + + /// A shape difference. + /// + /// The contents, alignment, sizes, fonts, or any other essential attributes + /// of the shape of the text have changed. A complete reshape and relayout of + /// the text is necessary. + Shape, +} + /// A renderer capable of measuring and drawing [`Text`]. pub trait Renderer: crate::Renderer { /// The font type used. @@ -134,6 +166,9 @@ pub trait Renderer: crate::Renderer { /// The [`Paragraph`] of this [`Renderer`]. type Paragraph: Paragraph + 'static; + /// The [`Editor`] of this [`Renderer`]. + type Editor: Editor + 'static; + /// The icon font of the backend. const ICON_FONT: Self::Font; @@ -165,6 +200,13 @@ pub trait Renderer: crate::Renderer { color: Color, ); + fn fill_editor( + &mut self, + editor: &Self::Editor, + position: Point, + color: Color, + ); + /// Draws the given [`Text`] at the given position and with the given /// [`Color`]. fn fill_text( @@ -174,84 +216,3 @@ pub trait Renderer: crate::Renderer { color: Color, ); } - -/// A text paragraph. -pub trait Paragraph: Sized + Default { - /// The font of this [`Paragraph`]. - type Font: Copy + PartialEq; - - /// Creates a new [`Paragraph`] laid out with the given [`Text`]. - fn with_text(text: Text<'_, Self::Font>) -> Self; - - /// Lays out the [`Paragraph`] with some new boundaries. - fn resize(&mut self, new_bounds: Size); - - /// Compares the [`Paragraph`] with some desired [`Text`] and returns the - /// [`Difference`]. - fn compare(&self, text: Text<'_, Self::Font>) -> Difference; - - /// Returns the horizontal alignment of the [`Paragraph`]. - fn horizontal_alignment(&self) -> alignment::Horizontal; - - /// Returns the vertical alignment of the [`Paragraph`]. - fn vertical_alignment(&self) -> alignment::Vertical; - - /// Returns the minimum boundaries that can fit the contents of the - /// [`Paragraph`]. - fn min_bounds(&self) -> Size; - - /// Tests whether the provided point is within the boundaries of the - /// [`Paragraph`], returning information about the nearest character. - fn hit_test(&self, point: Point) -> Option; - - /// Returns the distance to the given grapheme index in the [`Paragraph`]. - fn grapheme_position(&self, line: usize, index: usize) -> Option; - - /// Updates the [`Paragraph`] to match the given [`Text`], if needed. - fn update(&mut self, text: Text<'_, Self::Font>) { - match self.compare(text) { - Difference::None => {} - Difference::Bounds => { - self.resize(text.bounds); - } - Difference::Shape => { - *self = Self::with_text(text); - } - } - } - - /// Returns the minimum width that can fit the contents of the [`Paragraph`]. - fn min_width(&self) -> f32 { - self.min_bounds().width - } - - /// Returns the minimum height that can fit the contents of the [`Paragraph`]. - fn min_height(&self) -> f32 { - self.min_bounds().height - } -} - -/// The difference detected in some text. -/// -/// You will obtain a [`Difference`] when you [`compare`] a [`Paragraph`] with some -/// [`Text`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Difference { - /// No difference. - /// - /// The text can be reused as it is! - None, - - /// A bounds difference. - /// - /// This normally means a relayout is necessary, but the shape of the text can - /// be reused. - Bounds, - - /// A shape difference. - /// - /// The contents, alignment, sizes, fonts, or any other essential attributes - /// of the shape of the text have changed. A complete reshape and relayout of - /// the text is necessary. - Shape, -} diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs new file mode 100644 index 00000000..a4fd0ec1 --- /dev/null +++ b/core/src/text/editor.rs @@ -0,0 +1,68 @@ +use crate::text::LineHeight; +use crate::{Pixels, Point, Rectangle, Size}; + +pub trait Editor: Sized + Default { + type Font: Copy + PartialEq + Default; + + /// Creates a new [`Editor`] laid out with the given text. + fn with_text(text: &str) -> Self; + + fn cursor(&self) -> Cursor; + + fn perform(&mut self, action: Action); + + /// Returns the current boundaries of the [`Editor`]. + fn bounds(&self) -> Size; + + /// Returns the minimum boundaries that can fit the contents of the + /// [`Editor`]. + fn min_bounds(&self) -> Size; + + /// Updates the [`Editor`] with some new attributes. + fn update( + &mut self, + new_bounds: Size, + new_font: Self::Font, + new_size: Pixels, + new_line_height: LineHeight, + ); + + /// Returns the minimum width that can fit the contents of the [`Editor`]. + fn min_width(&self) -> f32 { + self.min_bounds().width + } + + /// Returns the minimum height that can fit the contents of the [`Editor`]. + fn min_height(&self) -> f32 { + self.min_bounds().height + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Action { + MoveLeft, + MoveRight, + MoveUp, + MoveDown, + MoveLeftWord, + MoveRightWord, + MoveHome, + MoveEnd, + SelectWord, + SelectLine, + Insert(char), + Backspace, + Delete, + Click(Point), + Drag(Point), +} + +/// The cursor of an [`Editor`]. +#[derive(Debug, Clone)] +pub enum Cursor { + /// Cursor without a selection + Caret(Point), + + /// Cursor selecting a range of text + Selection(Vec), +} diff --git a/core/src/text/paragraph.rs b/core/src/text/paragraph.rs new file mode 100644 index 00000000..de1fb74d --- /dev/null +++ b/core/src/text/paragraph.rs @@ -0,0 +1,59 @@ +use crate::alignment; +use crate::text::{Difference, Hit, Text}; +use crate::{Point, Size}; + +/// A text paragraph. +pub trait Paragraph: Sized + Default { + /// The font of this [`Paragraph`]. + type Font: Copy + PartialEq; + + /// Creates a new [`Paragraph`] laid out with the given [`Text`]. + fn with_text(text: Text<'_, Self::Font>) -> Self; + + /// Lays out the [`Paragraph`] with some new boundaries. + fn resize(&mut self, new_bounds: Size); + + /// Compares the [`Paragraph`] with some desired [`Text`] and returns the + /// [`Difference`]. + fn compare(&self, text: Text<'_, Self::Font>) -> Difference; + + /// Returns the horizontal alignment of the [`Paragraph`]. + fn horizontal_alignment(&self) -> alignment::Horizontal; + + /// Returns the vertical alignment of the [`Paragraph`]. + fn vertical_alignment(&self) -> alignment::Vertical; + + /// Returns the minimum boundaries that can fit the contents of the + /// [`Paragraph`]. + fn min_bounds(&self) -> Size; + + /// Tests whether the provided point is within the boundaries of the + /// [`Paragraph`], returning information about the nearest character. + fn hit_test(&self, point: Point) -> Option; + + /// Returns the distance to the given grapheme index in the [`Paragraph`]. + fn grapheme_position(&self, line: usize, index: usize) -> Option; + + /// Updates the [`Paragraph`] to match the given [`Text`], if needed. + fn update(&mut self, text: Text<'_, Self::Font>) { + match self.compare(text) { + Difference::None => {} + Difference::Bounds => { + self.resize(text.bounds); + } + Difference::Shape => { + *self = Self::with_text(text); + } + } + } + + /// Returns the minimum width that can fit the contents of the [`Paragraph`]. + fn min_width(&self) -> f32 { + self.min_bounds().width + } + + /// Returns the minimum height that can fit the contents of the [`Paragraph`]. + fn min_height(&self) -> f32 { + self.min_bounds().height + } +} -- cgit From 1455911b636f19810e12eeb12a6eed11c5244cfe Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 12 Sep 2023 15:03:23 +0200 Subject: Add `Enter` variant to `Action` in `text::Editor` --- core/src/text/editor.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index a4fd0ec1..09d4efde 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -51,6 +51,7 @@ pub enum Action { SelectWord, SelectLine, Insert(char), + Enter, Backspace, Delete, Click(Point), -- cgit From f4c51a96d50953d5fb6e9eb62194f226e2cbfd3c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 13 Sep 2023 16:11:43 +0200 Subject: Introduce `Motion` concept in `core::text::editor` --- core/src/text/editor.rs | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'core') diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 09d4efde..f87e18f3 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -40,14 +40,8 @@ pub trait Editor: Sized + Default { #[derive(Debug, Clone, Copy, PartialEq)] pub enum Action { - MoveLeft, - MoveRight, - MoveUp, - MoveDown, - MoveLeftWord, - MoveRightWord, - MoveHome, - MoveEnd, + Move(Motion), + Select(Motion), SelectWord, SelectLine, Insert(char), @@ -58,6 +52,34 @@ pub enum Action { Drag(Point), } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Motion { + Left, + Right, + Up, + Down, + WordLeft, + WordRight, + Home, + End, + PageUp, + PageDown, + DocumentStart, + DocumentEnd, +} + +impl Motion { + pub fn widen(self) -> Self { + match self { + Self::Left => Self::WordLeft, + Self::Right => Self::WordRight, + Self::Home => Self::DocumentStart, + Self::End => Self::DocumentEnd, + _ => self, + } + } +} + /// The cursor of an [`Editor`]. #[derive(Debug, Clone)] pub enum Cursor { -- cgit From b24b94d82778733ddae1b824d0d7690afcec3056 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 14 Sep 2023 14:18:49 +0200 Subject: Handle motions when a selection is present in `text::Editor` --- core/src/text/editor.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'core') diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index f87e18f3..3adfc61a 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -78,6 +78,29 @@ impl Motion { _ => self, } } + + pub fn direction(&self) -> Direction { + match self { + Self::Left + | Self::Up + | Self::WordLeft + | Self::Home + | Self::PageUp + | Self::DocumentStart => Direction::Left, + Self::Right + | Self::Down + | Self::WordRight + | Self::End + | Self::PageDown + | Self::DocumentEnd => Direction::Right, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Direction { + Left, + Right, } /// The cursor of an [`Editor`]. -- cgit From c7d02e24e6f8265c205a68bd97b2643d40ae30ee Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 14 Sep 2023 18:57:09 +0200 Subject: Remove `Editor::min_bounds` and use `bounds` instead --- core/src/renderer/null.rs | 4 ---- core/src/text/editor.rs | 14 -------------- 2 files changed, 18 deletions(-) (limited to 'core') diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index adf75969..e714e492 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -131,10 +131,6 @@ impl text::Editor for () { Size::ZERO } - fn min_bounds(&self) -> Size { - Size::ZERO - } - fn update( &mut self, _new_bounds: Size, diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 3adfc61a..56cda3ef 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -14,10 +14,6 @@ pub trait Editor: Sized + Default { /// Returns the current boundaries of the [`Editor`]. fn bounds(&self) -> Size; - /// Returns the minimum boundaries that can fit the contents of the - /// [`Editor`]. - fn min_bounds(&self) -> Size; - /// Updates the [`Editor`] with some new attributes. fn update( &mut self, @@ -26,16 +22,6 @@ pub trait Editor: Sized + Default { new_size: Pixels, new_line_height: LineHeight, ); - - /// Returns the minimum width that can fit the contents of the [`Editor`]. - fn min_width(&self) -> f32 { - self.min_bounds().width - } - - /// Returns the minimum height that can fit the contents of the [`Editor`]. - fn min_height(&self) -> f32 { - self.min_bounds().height - } } #[derive(Debug, Clone, Copy, PartialEq)] -- cgit From 8e6e37e0cee79a2f293abedd18a6a7249575bb63 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 14 Sep 2023 19:05:50 +0200 Subject: Fix broken intra-doc links --- core/src/text.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'core') diff --git a/core/src/text.rs b/core/src/text.rs index 5aacbcc5..90581fea 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -137,6 +137,8 @@ impl Hit { /// /// You will obtain a [`Difference`] when you [`compare`] a [`Paragraph`] with some /// [`Text`]. +/// +/// [`compare`]: Paragraph::compare #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Difference { /// No difference. -- cgit From c6d0443627c22dcf1576303e5a426aa3622f1b7d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 16 Sep 2023 15:27:25 +0200 Subject: Implement methods to query the contents of a `TextEditor` --- core/src/renderer/null.rs | 12 ++++++++++++ core/src/text/editor.rs | 6 ++++++ 2 files changed, 18 insertions(+) (limited to 'core') diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index e714e492..01a52c7a 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -125,6 +125,18 @@ impl text::Editor for () { text::editor::Cursor::Caret(Point::ORIGIN) } + fn selection(&self) -> Option { + None + } + + fn line(&self, _index: usize) -> Option<&str> { + None + } + + fn line_count(&self) -> usize { + 0 + } + fn perform(&mut self, _action: text::editor::Action) {} fn bounds(&self) -> Size { diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 56cda3ef..5532fac5 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -9,6 +9,12 @@ pub trait Editor: Sized + Default { fn cursor(&self) -> Cursor; + fn selection(&self) -> Option; + + fn line(&self, index: usize) -> Option<&str>; + + fn line_count(&self) -> usize; + fn perform(&mut self, action: Action); /// Returns the current boundaries of the [`Editor`]. -- cgit From d051f21597bb333ac10183aaa3214a292e9aa365 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 16 Sep 2023 15:40:16 +0200 Subject: Implement `Copy` and `Paste` actions for `text::Editor` --- core/src/text/editor.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 5532fac5..003557c1 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -1,6 +1,8 @@ use crate::text::LineHeight; use crate::{Pixels, Point, Rectangle, Size}; +use std::sync::Arc; + pub trait Editor: Sized + Default { type Font: Copy + PartialEq + Default; @@ -30,13 +32,14 @@ pub trait Editor: Sized + Default { ); } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Action { Move(Motion), Select(Motion), SelectWord, SelectLine, Insert(char), + Paste(Arc), Enter, Backspace, Delete, -- cgit From 45c5cfe5774ac99a6e1b1d1014418f68b21b41cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 16 Sep 2023 19:05:31 +0200 Subject: Avoid drag on double or triple click for now in `TextEditor` --- core/src/mouse/click.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'core') diff --git a/core/src/mouse/click.rs b/core/src/mouse/click.rs index 4a7d796c..e8e5fb56 100644 --- a/core/src/mouse/click.rs +++ b/core/src/mouse/click.rs @@ -61,6 +61,10 @@ impl Click { self.kind } + pub fn position(&self) -> Point { + self.position + } + fn is_consecutive(&self, new_position: Point, time: Instant) -> bool { let duration = if time > self.time { Some(time - self.time) -- cgit From 76dc82e8e8b5201ec10f8d00d851c1decf998583 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 17 Sep 2023 15:29:14 +0200 Subject: Draft `Highlighter` API --- core/src/renderer/null.rs | 11 +++++++++ core/src/text.rs | 2 ++ core/src/text/editor.rs | 9 +++++++ core/src/text/highlighter.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 core/src/text/highlighter.rs (limited to 'core') diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 01a52c7a..21597c8e 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -149,6 +149,17 @@ impl text::Editor for () { _new_font: Self::Font, _new_size: Pixels, _new_line_height: text::LineHeight, + _new_highlighter: &mut impl text::Highlighter, + ) { + } + + fn highlight( + &mut self, + _font: Self::Font, + _highlighter: &mut H, + _format_highlight: impl Fn( + &H::Highlight, + ) -> text::highlighter::Format, ) { } } diff --git a/core/src/text.rs b/core/src/text.rs index 90581fea..9b9c753c 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -2,8 +2,10 @@ mod paragraph; pub mod editor; +pub mod highlighter; pub use editor::Editor; +pub use highlighter::Highlighter; pub use paragraph::Paragraph; use crate::alignment; diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 003557c1..0f439c8d 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -1,3 +1,4 @@ +use crate::text::highlighter::{self, Highlighter}; use crate::text::LineHeight; use crate::{Pixels, Point, Rectangle, Size}; @@ -29,6 +30,14 @@ pub trait Editor: Sized + Default { new_font: Self::Font, new_size: Pixels, new_line_height: LineHeight, + new_highlighter: &mut impl Highlighter, + ); + + fn highlight( + &mut self, + font: Self::Font, + highlighter: &mut H, + format_highlight: impl Fn(&H::Highlight) -> highlighter::Format, ); } diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs new file mode 100644 index 00000000..1f9ac840 --- /dev/null +++ b/core/src/text/highlighter.rs @@ -0,0 +1,56 @@ +use crate::Color; + +use std::hash::Hash; +use std::ops::Range; + +pub trait Highlighter: Clone + 'static { + type Settings: Hash; + type Highlight; + + type Iterator<'a>: Iterator, Self::Highlight)> + where + Self: 'a; + + fn new(settings: &Self::Settings) -> Self; + + fn change_line(&mut self, line: usize); + + fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_>; + + fn current_line(&self) -> usize; +} + +#[derive(Debug, Clone, Copy)] +pub struct Style { + pub color: Color, +} + +#[derive(Debug, Clone, Copy)] +pub struct PlainText; + +impl Highlighter for PlainText { + type Settings = (); + type Highlight = (); + + type Iterator<'a> = std::iter::Empty<(Range, Self::Highlight)>; + + fn new(_settings: &Self::Settings) -> Self { + Self + } + + fn change_line(&mut self, _line: usize) {} + + fn highlight_line(&mut self, _line: &str) -> Self::Iterator<'_> { + std::iter::empty() + } + + fn current_line(&self) -> usize { + usize::MAX + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Format { + pub color: Option, + pub font: Option, +} -- cgit From d3011992a76e83e12f74402c2ade616cdc7f1497 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 17 Sep 2023 19:03:58 +0200 Subject: Implement basic syntax highlighting with `syntect` in `editor` example --- core/src/text/highlighter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs index 1f9ac840..a929826f 100644 --- a/core/src/text/highlighter.rs +++ b/core/src/text/highlighter.rs @@ -3,7 +3,7 @@ use crate::Color; use std::hash::Hash; use std::ops::Range; -pub trait Highlighter: Clone + 'static { +pub trait Highlighter: 'static { type Settings: Hash; type Highlight; -- cgit From 2897986f2ded7318894a52572bec3d62754ebfaa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 17 Sep 2023 19:27:51 +0200 Subject: Notify `Highlighter` of topmost line change --- core/src/text/editor.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 0f439c8d..2144715f 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -47,13 +47,18 @@ pub enum Action { Select(Motion), SelectWord, SelectLine, + Edit(Edit), + Click(Point), + Drag(Point), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Edit { Insert(char), Paste(Arc), Enter, Backspace, Delete, - Click(Point), - Drag(Point), } #[derive(Debug, Clone, Copy, PartialEq)] -- cgit From 8446fe6de52fa68077d23d39f728f79a29b52f00 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 18 Sep 2023 14:38:54 +0200 Subject: Implement theme selector in `editor` example --- core/src/text/highlighter.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs index a929826f..b462d083 100644 --- a/core/src/text/highlighter.rs +++ b/core/src/text/highlighter.rs @@ -1,10 +1,9 @@ use crate::Color; -use std::hash::Hash; use std::ops::Range; pub trait Highlighter: 'static { - type Settings: Hash; + type Settings: PartialEq + Clone; type Highlight; type Iterator<'a>: Iterator, Self::Highlight)> @@ -13,6 +12,8 @@ pub trait Highlighter: 'static { fn new(settings: &Self::Settings) -> Self; + fn update(&mut self, new_settings: &Self::Settings); + fn change_line(&mut self, line: usize); fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_>; @@ -38,6 +39,8 @@ impl Highlighter for PlainText { Self } + fn update(&mut self, _new_settings: &Self::Settings) {} + fn change_line(&mut self, _line: usize) {} fn highlight_line(&mut self, _line: &str) -> Self::Iterator<'_> { -- cgit From e7326f0af6f16cf2ff04fbac93bf296a044923f4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 18 Sep 2023 19:07:41 +0200 Subject: Flesh out the `editor` example a bit more --- core/src/renderer/null.rs | 4 ++++ core/src/text/editor.rs | 8 ++++++++ 2 files changed, 12 insertions(+) (limited to 'core') diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 21597c8e..da0f32de 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -125,6 +125,10 @@ impl text::Editor for () { text::editor::Cursor::Caret(Point::ORIGIN) } + fn cursor_position(&self) -> (usize, usize) { + (0, 0) + } + fn selection(&self) -> Option { None } diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 2144715f..13bafc3d 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -12,6 +12,8 @@ pub trait Editor: Sized + Default { fn cursor(&self) -> Cursor; + fn cursor_position(&self) -> (usize, usize); + fn selection(&self) -> Option; fn line(&self, index: usize) -> Option<&str>; @@ -52,6 +54,12 @@ pub enum Action { Drag(Point), } +impl Action { + pub fn is_edit(&self) -> bool { + matches!(self, Self::Edit(_)) + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Edit { Insert(char), -- cgit From 8eec0033dee816bfcc102fc4f511c8bfe08c14ee Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 18 Sep 2023 19:24:09 +0200 Subject: Remove unnecessary `monospaced` flag in `Font` --- core/src/font.rs | 4 ---- 1 file changed, 4 deletions(-) (limited to 'core') diff --git a/core/src/font.rs b/core/src/font.rs index 7f647847..2b68decf 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -12,8 +12,6 @@ pub struct Font { pub stretch: Stretch, /// The [`Style`] of the [`Font`]. pub style: Style, - /// Whether if the [`Font`] is monospaced or not. - pub monospaced: bool, } impl Font { @@ -23,13 +21,11 @@ impl Font { weight: Weight::Normal, stretch: Stretch::Normal, style: Style::Normal, - monospaced: false, }; /// A monospaced font with normal [`Weight`]. pub const MONOSPACE: Font = Font { family: Family::Monospace, - monospaced: true, ..Self::DEFAULT }; -- cgit From 4e757a26d0c1c58001f31cf0592131cd5ad886ad Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Sep 2023 01:18:06 +0200 Subject: Implement `Scroll` action in `text::editor` --- core/src/text/editor.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index 13bafc3d..e9d66ce9 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -52,6 +52,7 @@ pub enum Action { Edit(Edit), Click(Point), Drag(Point), + Scroll { lines: i32 }, } impl Action { -- cgit From c997aad85d7ee6e77085e50e5e599002549d228f Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Tue, 19 Sep 2023 01:46:46 -0400 Subject: Chore: Apply clippy map transformations Convert `.map().unwrap_or()` to `.map_or()` and similar transformations. --- core/src/mouse/click.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'core') diff --git a/core/src/mouse/click.rs b/core/src/mouse/click.rs index 4a7d796c..240e5c64 100644 --- a/core/src/mouse/click.rs +++ b/core/src/mouse/click.rs @@ -69,8 +69,6 @@ impl Click { }; self.position == new_position - && duration - .map(|duration| duration.as_millis() <= 300) - .unwrap_or(false) + && duration.is_some_and(|duration| duration.as_millis() <= 300) } } -- cgit From c6554d990770b941b5003d6ef40af3f9dedcd052 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Tue, 19 Sep 2023 01:50:05 -0400 Subject: Chore: Apply clippy docs keyword quoting Add quotes a number of doc strings like `sRGB` --- core/src/color.rs | 2 +- core/src/window/icon.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/color.rs b/core/src/color.rs index 1392f28b..0e8b7475 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -1,7 +1,7 @@ #[cfg(feature = "palette")] use palette::rgb::{Srgb, Srgba}; -/// A color in the sRGB color space. +/// A color in the `sRGB` color space. #[derive(Debug, Clone, Copy, PartialEq, Default)] pub struct Color { /// Red component, 0.0 - 1.0 diff --git a/core/src/window/icon.rs b/core/src/window/icon.rs index 2fc48e3b..5ef0eed7 100644 --- a/core/src/window/icon.rs +++ b/core/src/window/icon.rs @@ -3,7 +3,7 @@ use crate::Size; use std::mem; -/// Builds an [`Icon`] from its RGBA pixels in the sRGB color space. +/// Builds an [`Icon`] from its RGBA pixels in the `sRGB` color space. pub fn from_rgba( rgba: Vec, width: u32, -- cgit From efd0ff6ded4e647e5fad0964555dbed541a075d7 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Tue, 19 Sep 2023 01:52:25 -0400 Subject: Chore: Apply some minor clippy fixes * Use `.elapsed()` for duration * Use direct iteration without calling `.iter()` and the like * order fields in the `Text` struct creation as declared --- core/src/gradient.rs | 2 +- core/src/widget/text.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 4a0d5ea0..6a5533f8 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -94,7 +94,7 @@ impl Linear { mut self, stops: impl IntoIterator, ) -> Self { - for stop in stops.into_iter() { + for stop in stops { self = self.add_stop(stop.offset, stop.color) } diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 53ed463e..ba98f2d8 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -220,9 +220,9 @@ where size, line_height, font, - shaping, horizontal_alignment, vertical_alignment, + shaping, }, ); -- cgit From f806d001e6fb44b5a45029ca257261e6e0d4d4b2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Sep 2023 20:48:50 +0200 Subject: Introduce new `iced_highlighter` subcrate --- core/src/text/highlighter.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs index b462d083..9a9cff89 100644 --- a/core/src/text/highlighter.rs +++ b/core/src/text/highlighter.rs @@ -52,8 +52,17 @@ impl Highlighter for PlainText { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Format { pub color: Option, pub font: Option, } + +impl Default for Format { + fn default() -> Self { + Self { + color: None, + font: None, + } + } +} -- cgit From be340a8cd822be1ea0fe4c1b1f3a62ca66d705b4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Sep 2023 23:00:20 +0200 Subject: Fix gamma correction for colored glyphs in `iced_wgpu` --- core/src/color.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'core') diff --git a/core/src/color.rs b/core/src/color.rs index 1392f28b..cce8b340 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -89,6 +89,26 @@ impl Color { } } + /// Creates a [`Color`] from its linear RGBA components. + pub fn from_linear_rgba(r: f32, g: f32, b: f32, a: f32) -> Self { + // As described in: + // https://en.wikipedia.org/wiki/SRGB + fn gamma_component(u: f32) -> f32 { + if u < 0.0031308 { + 12.92 * u + } else { + 1.055 * u.powf(1.0 / 2.4) - 0.055 + } + } + + Self { + r: gamma_component(r), + g: gamma_component(g), + b: gamma_component(b), + a, + } + } + /// Converts the [`Color`] into its RGBA8 equivalent. #[must_use] pub fn into_rgba8(self) -> [u8; 4] { -- cgit From 34f07b60273d6cfe13834af54cd0e24d34569387 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 Sep 2023 04:11:52 +0200 Subject: Fix `clippy::semicolon_if_nothing_returned` --- core/src/element.rs | 6 +++--- core/src/gradient.rs | 2 +- core/src/hasher.rs | 2 +- core/src/overlay/element.rs | 6 +++--- core/src/overlay/group.rs | 2 +- core/src/shell.rs | 2 +- core/src/widget/operation/focusable.rs | 10 +++++----- core/src/widget/operation/scrollable.rs | 4 ++-- core/src/widget/operation/text_input.rs | 8 ++++---- core/src/widget/tree.rs | 4 ++-- 10 files changed, 23 insertions(+), 23 deletions(-) (limited to 'core') diff --git a/core/src/element.rs b/core/src/element.rs index 02f16bcb..dea111af 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -293,7 +293,7 @@ where } fn diff(&self, tree: &mut Tree) { - self.widget.diff(tree) + self.widget.diff(tree); } fn width(&self) -> Length { @@ -418,7 +418,7 @@ where viewport: &Rectangle, ) { self.widget - .draw(tree, renderer, theme, style, layout, cursor, viewport) + .draw(tree, renderer, theme, style, layout, cursor, viewport); } fn mouse_interaction( @@ -508,7 +508,7 @@ where ) { self.element .widget - .operate(state, layout, renderer, operation) + .operate(state, layout, renderer, operation); } fn on_event( diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 6a5533f8..4711b044 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -95,7 +95,7 @@ impl Linear { stops: impl IntoIterator, ) -> Self { for stop in stops { - self = self.add_stop(stop.offset, stop.color) + self = self.add_stop(stop.offset, stop.color); } self diff --git a/core/src/hasher.rs b/core/src/hasher.rs index fa52f16d..9d8f75b3 100644 --- a/core/src/hasher.rs +++ b/core/src/hasher.rs @@ -4,7 +4,7 @@ pub struct Hasher(twox_hash::XxHash64); impl core::hash::Hasher for Hasher { fn write(&mut self, bytes: &[u8]) { - self.0.write(bytes) + self.0.write(bytes); } fn finish(&self) -> u64 { diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index 689e69be..3dd58f9b 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -98,7 +98,7 @@ where layout: Layout<'_>, cursor: mouse::Cursor, ) { - self.overlay.draw(renderer, theme, style, layout, cursor) + self.overlay.draw(renderer, theme, style, layout, cursor); } /// Applies a [`widget::Operation`] to the [`Element`]. @@ -205,7 +205,7 @@ where state: &mut dyn widget::operation::TextInput, id: Option<&widget::Id>, ) { - self.operation.text_input(state, id) + self.operation.text_input(state, id); } fn custom(&mut self, state: &mut dyn Any, id: Option<&widget::Id>) { @@ -262,7 +262,7 @@ where layout: Layout<'_>, cursor: mouse::Cursor, ) { - self.content.draw(renderer, theme, style, layout, cursor) + self.content.draw(renderer, theme, style, layout, cursor); } fn is_over( diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index a0bae6bb..dccf6dba 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -143,7 +143,7 @@ where |(child, layout)| { child.operate(layout, renderer, operation); }, - ) + ); }); } diff --git a/core/src/shell.rs b/core/src/shell.rs index 246c937a..2952ceff 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -71,7 +71,7 @@ impl<'a, Message> Shell<'a, Message> { if self.is_layout_invalid { self.is_layout_invalid = false; - f() + f(); } } diff --git a/core/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs index ab1b677e..68c22faa 100644 --- a/core/src/widget/operation/focusable.rs +++ b/core/src/widget/operation/focusable.rs @@ -49,7 +49,7 @@ pub fn focus(target: Id) -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } } @@ -85,7 +85,7 @@ where _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } fn finish(&self) -> Outcome { @@ -132,7 +132,7 @@ pub fn focus_previous() -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } } @@ -166,7 +166,7 @@ pub fn focus_next() -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } } @@ -193,7 +193,7 @@ pub fn find_focused() -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } fn finish(&self) -> Outcome { diff --git a/core/src/widget/operation/scrollable.rs b/core/src/widget/operation/scrollable.rs index 4f8b2a98..12161255 100644 --- a/core/src/widget/operation/scrollable.rs +++ b/core/src/widget/operation/scrollable.rs @@ -26,7 +26,7 @@ pub fn snap_to(target: Id, offset: RelativeOffset) -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } fn scrollable( @@ -60,7 +60,7 @@ pub fn scroll_to(target: Id, offset: AbsoluteOffset) -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } fn scrollable( diff --git a/core/src/widget/operation/text_input.rs b/core/src/widget/operation/text_input.rs index a9ea2e81..41731d4c 100644 --- a/core/src/widget/operation/text_input.rs +++ b/core/src/widget/operation/text_input.rs @@ -38,7 +38,7 @@ pub fn move_cursor_to_front(target: Id) -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } } @@ -68,7 +68,7 @@ pub fn move_cursor_to_end(target: Id) -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } } @@ -99,7 +99,7 @@ pub fn move_cursor_to(target: Id, position: usize) -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } } @@ -128,7 +128,7 @@ pub fn select_all(target: Id) -> impl Operation { _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation), ) { - operate_on_children(self) + operate_on_children(self); } } diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index 202cca9a..d4b8828a 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -61,7 +61,7 @@ impl Tree { Renderer: crate::Renderer, { if self.tag == new.borrow().tag() { - new.borrow().diff(self) + new.borrow().diff(self); } else { *self = Self::new(new); } @@ -78,7 +78,7 @@ impl Tree { new_children, |tree, widget| tree.diff(widget.borrow()), |widget| Self::new(widget.borrow()), - ) + ); } /// Reconciliates the children of the tree with the provided list of widgets using custom -- cgit From 6c386e90a12fd26da12541da3f086dddb7211c0c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 Sep 2023 04:33:48 +0200 Subject: Fix `clippy::trivially-copy-pass-by-ref` --- core/src/mouse/click.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/mouse/click.rs b/core/src/mouse/click.rs index 240e5c64..53354098 100644 --- a/core/src/mouse/click.rs +++ b/core/src/mouse/click.rs @@ -24,7 +24,7 @@ pub enum Kind { } impl Kind { - fn next(&self) -> Kind { + fn next(self) -> Kind { match self { Kind::Single => Kind::Double, Kind::Double => Kind::Triple, -- cgit From b27762554627b8e89f2b840b1a8a512e22d4cd87 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 Sep 2023 16:26:43 +0200 Subject: Revert "Chore: Apply clippy map transformations" This reverts commit c997aad85d7ee6e77085e50e5e599002549d228f. --- core/src/mouse/click.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/mouse/click.rs b/core/src/mouse/click.rs index 53354098..9cc44a71 100644 --- a/core/src/mouse/click.rs +++ b/core/src/mouse/click.rs @@ -69,6 +69,8 @@ impl Click { }; self.position == new_position - && duration.is_some_and(|duration| duration.as_millis() <= 300) + && duration + .map(|duration| duration.as_millis() <= 300) + .unwrap_or(false) } } -- cgit From f137d71e8fb926e784680d56d1cfa6817c3710a1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 20 Sep 2023 16:40:03 +0200 Subject: Centralize `clippy` lints in `.cargo/config.toml` --- core/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'core') diff --git a/core/src/lib.rs b/core/src/lib.rs index 1bfba7bd..54ea5839 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,14 +14,8 @@ missing_debug_implementations, missing_docs, unused_results, - clippy::extra_unused_lifetimes, - clippy::from_over_into, - clippy::needless_borrow, - clippy::new_without_default, - clippy::useless_conversion, rustdoc::broken_intra_doc_links )] -#![allow(clippy::inherent_to_string, clippy::type_complexity)] pub mod alignment; pub mod clipboard; pub mod event; -- cgit From 625cd745f38215b1cb8f629cdc6d2fa41c9a739a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 27 Oct 2023 05:04:14 +0200 Subject: Write documentation for the new text APIs --- core/src/lib.rs | 2 +- core/src/mouse/click.rs | 1 + core/src/text.rs | 2 ++ core/src/text/editor.rs | 51 +++++++++++++++++++++++++++++++++++++++++++- core/src/text/highlighter.rs | 30 +++++++++++++++++++++----- 5 files changed, 79 insertions(+), 7 deletions(-) (limited to 'core') diff --git a/core/src/lib.rs b/core/src/lib.rs index 9eb3da34..54ea5839 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -12,7 +12,7 @@ #![forbid(unsafe_code, rust_2018_idioms)] #![deny( missing_debug_implementations, - // missing_docs, + missing_docs, unused_results, rustdoc::broken_intra_doc_links )] diff --git a/core/src/mouse/click.rs b/core/src/mouse/click.rs index b427da6c..6f3844be 100644 --- a/core/src/mouse/click.rs +++ b/core/src/mouse/click.rs @@ -61,6 +61,7 @@ impl Click { self.kind } + /// Returns the position of the [`Click`]. pub fn position(&self) -> Point { self.position } diff --git a/core/src/text.rs b/core/src/text.rs index 9b9c753c..546d0b5c 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -204,6 +204,8 @@ pub trait Renderer: crate::Renderer { color: Color, ); + /// Draws the given [`Editor`] at the given position and with the given + /// [`Color`]. fn fill_editor( &mut self, editor: &Self::Editor, diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index e9d66ce9..ebb0eee2 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -1,25 +1,36 @@ +//! Edit text. use crate::text::highlighter::{self, Highlighter}; use crate::text::LineHeight; use crate::{Pixels, Point, Rectangle, Size}; use std::sync::Arc; +/// A component that can be used by widgets to edit multi-line text. pub trait Editor: Sized + Default { + /// The [`Font`] of the [`Editor`]. type Font: Copy + PartialEq + Default; /// Creates a new [`Editor`] laid out with the given text. fn with_text(text: &str) -> Self; + /// Returns the current [`Cursor`] of the [`Editor`]. fn cursor(&self) -> Cursor; + /// Returns the current cursor position of the [`Editor`]. + /// + /// Line and column, respectively. fn cursor_position(&self) -> (usize, usize); + /// Returns the current selected text of the [`Editor`]. fn selection(&self) -> Option; + /// Returns the text of the given line in the [`Editor`], if it exists. fn line(&self, index: usize) -> Option<&str>; + /// Returns the amount of lines in the [`Editor`]. fn line_count(&self) -> usize; + /// Performs an [`Action`] on the [`Editor`]. fn perform(&mut self, action: Action); /// Returns the current boundaries of the [`Editor`]. @@ -35,6 +46,7 @@ pub trait Editor: Sized + Default { new_highlighter: &mut impl Highlighter, ); + /// Runs a text [`Highlighter`] in the [`Editor`]. fn highlight( &mut self, font: Self::Font, @@ -43,50 +55,83 @@ pub trait Editor: Sized + Default { ); } +/// An interaction with an [`Editor`]. #[derive(Debug, Clone, PartialEq)] pub enum Action { + /// Apply a [`Motion`]. Move(Motion), + /// Select text with a given [`Motion`]. Select(Motion), + /// Select the word at the current cursor. SelectWord, + /// Select the line at the current cursor. SelectLine, + /// Perform an [`Edit`]. Edit(Edit), + /// Click the [`Editor`] at the given [`Point`]. Click(Point), + /// Drag the mouse on the [`Editor`] to the given [`Point`]. Drag(Point), - Scroll { lines: i32 }, + /// Scroll the [`Editor`] a certain amount of lines. + Scroll { + /// The amount of lines to scroll. + lines: i32, + }, } impl Action { + /// Returns whether the [`Action`] is an editing action. pub fn is_edit(&self) -> bool { matches!(self, Self::Edit(_)) } } +/// An action that edits text. #[derive(Debug, Clone, PartialEq)] pub enum Edit { + /// Insert the given character. Insert(char), + /// Paste the given text. Paste(Arc), + /// Break the current line. Enter, + /// Delete the previous character. Backspace, + /// Delete the next character. Delete, } +/// A cursor movement. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Motion { + /// Move left. Left, + /// Move right. Right, + /// Move up. Up, + /// Move down. Down, + /// Move to the left boundary of a word. WordLeft, + /// Move to the right boundary of a word. WordRight, + /// Move to the start of the line. Home, + /// Move to the end of the line. End, + /// Move to the start of the previous window. PageUp, + /// Move to the start of the next window. PageDown, + /// Move to the start of the text. DocumentStart, + /// Move to the end of the text. DocumentEnd, } impl Motion { + /// Widens the [`Motion`], if possible. pub fn widen(self) -> Self { match self { Self::Left => Self::WordLeft, @@ -97,6 +142,7 @@ impl Motion { } } + /// Returns the [`Direction`] of the [`Motion`]. pub fn direction(&self) -> Direction { match self { Self::Left @@ -115,9 +161,12 @@ impl Motion { } } +/// A direction in some text. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Direction { + /// <- Left, + /// -> Right, } diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs index 9a9cff89..a0535228 100644 --- a/core/src/text/highlighter.rs +++ b/core/src/text/highlighter.rs @@ -1,31 +1,48 @@ +//! Highlight text. use crate::Color; use std::ops::Range; +/// A type capable of highlighting text. +/// +/// A [`Highlighter`] highlights lines in sequence. When a line changes, +/// it must be notified and the lines after the changed one must be fed +/// again to the [`Highlighter`]. pub trait Highlighter: 'static { + /// The settings to configure the [`Highlighter`]. type Settings: PartialEq + Clone; + + /// The output of the [`Highlighter`]. type Highlight; + /// The highlight iterator type. type Iterator<'a>: Iterator, Self::Highlight)> where Self: 'a; + /// Creates a new [`Highlighter`] from its [`Self::Settings`]. fn new(settings: &Self::Settings) -> Self; + /// Updates the [`Highlighter`] with some new [`Self::Settings`]. fn update(&mut self, new_settings: &Self::Settings); + /// Notifies the [`Highlighter`] that the line at the given index has changed. fn change_line(&mut self, line: usize); + /// Highlights the given line. + /// + /// If a line changed prior to this, the first line provided here will be the + /// line that changed. fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_>; + /// Returns the current line of the [`Highlighter`]. + /// + /// If `change_line` has been called, this will normally be the least index + /// that changed. fn current_line(&self) -> usize; } -#[derive(Debug, Clone, Copy)] -pub struct Style { - pub color: Color, -} - +/// A highlighter that highlights nothing. #[derive(Debug, Clone, Copy)] pub struct PlainText; @@ -52,9 +69,12 @@ impl Highlighter for PlainText { } } +/// The format of some text. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Format { + /// The [`Color`] of the text. pub color: Option, + /// The `Font` of the text. pub font: Option, } -- cgit From 57f9024e89256ad3f99a3ab19bdc8524c1defa54 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 27 Oct 2023 05:19:35 +0200 Subject: Fix intra-doc broken links --- core/src/text/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs index ebb0eee2..f3c6e342 100644 --- a/core/src/text/editor.rs +++ b/core/src/text/editor.rs @@ -7,7 +7,7 @@ use std::sync::Arc; /// A component that can be used by widgets to edit multi-line text. pub trait Editor: Sized + Default { - /// The [`Font`] of the [`Editor`]. + /// The font of the [`Editor`]. type Font: Copy + PartialEq + Default; /// Creates a new [`Editor`] laid out with the given text. -- cgit From 98e088e731e6fbd5b5035033ae61bda823ced988 Mon Sep 17 00:00:00 2001 From: dtzxporter Date: Tue, 12 Sep 2023 18:15:00 -0400 Subject: Migrate twox-hash -> xxhash_rust. Switch to Xxh3 for better performance. xxhash-rust is more maintained, built against `::core`, so no workaround for wasm is necessary. Switch to Xxh3 for better performance, which shows when loading/hashing image buffers. --- core/Cargo.toml | 2 +- core/src/hasher.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 7acb7511..82946847 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,7 +14,7 @@ keywords.workspace = true bitflags.workspace = true log.workspace = true thiserror.workspace = true -twox-hash.workspace = true +xxhash-rust.workspace = true num-traits.workspace = true palette.workspace = true diff --git a/core/src/hasher.rs b/core/src/hasher.rs index 9d8f75b3..a13d78af 100644 --- a/core/src/hasher.rs +++ b/core/src/hasher.rs @@ -1,6 +1,7 @@ /// The hasher used to compare layouts. -#[derive(Debug, Default)] -pub struct Hasher(twox_hash::XxHash64); +#[allow(missing_debug_implementations)] // Doesn't really make sense to have debug on the hasher state anyways. +#[derive(Default)] +pub struct Hasher(xxhash_rust::xxh3::Xxh3); impl core::hash::Hasher for Hasher { fn write(&mut self, bytes: &[u8]) { -- cgit From 5759096a4c33935fcdf5f96606143e4f21159186 Mon Sep 17 00:00:00 2001 From: Remmirad Date: Wed, 31 May 2023 15:46:21 +0200 Subject: Implement texture filtering options --- core/src/image.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index 85d9d475..9a6011a3 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -5,11 +5,31 @@ use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; use std::sync::Arc; +/// Image filter method +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum FilterMethod { + /// Bilinear interpolation + #[default] + Linear, + /// Nearest Neighbor + Nearest, +} + +/// Texture filter settings +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] +pub struct TextureFilter { + /// Filter for scaling the image down. + pub min: FilterMethod, + /// Filter for scaling the image up. + pub mag: FilterMethod, +} + /// A handle of some image data. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Handle { id: u64, data: Data, + filter: TextureFilter, } impl Handle { @@ -56,6 +76,7 @@ impl Handle { Handle { id: hasher.finish(), data, + filter: TextureFilter::default(), } } @@ -68,6 +89,17 @@ impl Handle { pub fn data(&self) -> &Data { &self.data } + + /// Returns a reference to the [`TextureFilter`]. + pub fn filter(&self) -> &TextureFilter { + &self.filter + } + + /// Sets the texture filtering methods. + pub fn set_filter(mut self, filter: TextureFilter) -> Self { + self.filter = filter; + self + } } impl From for Handle -- cgit From 4b32a488808e371313ce78e727c9d98ab2eb759e Mon Sep 17 00:00:00 2001 From: Remmirad Date: Fri, 4 Aug 2023 13:50:16 +0200 Subject: Fix clippy + fmt --- core/src/image.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index 9a6011a3..69f19436 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -11,7 +11,7 @@ pub enum FilterMethod { /// Bilinear interpolation #[default] Linear, - /// Nearest Neighbor + /// Nearest Neighbor Nearest, } -- cgit From a5125d6fea824df1191777fe3eb53a2f748208b9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 11 Nov 2023 07:02:01 +0100 Subject: Refactor texture image filtering - Support only `Linear` or `Nearest` - Simplify `Layer` groups - Move `FilterMethod` to `Image` and `image::Viewer` --- core/src/image.rs | 49 ++++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index 69f19436..e9675316 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -5,31 +5,11 @@ use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; use std::sync::Arc; -/// Image filter method -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum FilterMethod { - /// Bilinear interpolation - #[default] - Linear, - /// Nearest Neighbor - Nearest, -} - -/// Texture filter settings -#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] -pub struct TextureFilter { - /// Filter for scaling the image down. - pub min: FilterMethod, - /// Filter for scaling the image up. - pub mag: FilterMethod, -} - /// A handle of some image data. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Handle { id: u64, data: Data, - filter: TextureFilter, } impl Handle { @@ -76,7 +56,6 @@ impl Handle { Handle { id: hasher.finish(), data, - filter: TextureFilter::default(), } } @@ -89,17 +68,6 @@ impl Handle { pub fn data(&self) -> &Data { &self.data } - - /// Returns a reference to the [`TextureFilter`]. - pub fn filter(&self) -> &TextureFilter { - &self.filter - } - - /// Sets the texture filtering methods. - pub fn set_filter(mut self, filter: TextureFilter) -> Self { - self.filter = filter; - self - } } impl From for Handle @@ -196,6 +164,16 @@ impl std::fmt::Debug for Data { } } +/// Image filtering strategy. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum FilterMethod { + /// Bilinear interpolation. + #[default] + Linear, + /// Nearest neighbor. + Nearest, +} + /// A [`Renderer`] that can render raster graphics. /// /// [renderer]: crate::renderer @@ -210,5 +188,10 @@ pub trait Renderer: crate::Renderer { /// Draws an image with the given [`Handle`] and inside the provided /// `bounds`. - fn draw(&mut self, handle: Self::Handle, bounds: Rectangle); + fn draw( + &mut self, + handle: Self::Handle, + filter_method: FilterMethod, + bounds: Rectangle, + ); } -- cgit From 25006b9c6f2ae909d86871d3a13631d518c07158 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 21 Nov 2023 14:41:22 +0100 Subject: Fix `Overlay` composition Translations were not easily composable. --- core/src/overlay.rs | 3 ++- core/src/overlay/element.rs | 20 ++++++++++++++++---- core/src/overlay/group.rs | 9 +++++---- 3 files changed, 23 insertions(+), 9 deletions(-) (limited to 'core') diff --git a/core/src/overlay.rs b/core/src/overlay.rs index f71f25f7..af10afee 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -11,7 +11,7 @@ use crate::mouse; use crate::renderer; use crate::widget; use crate::widget::Tree; -use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size}; +use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector}; /// An interactive component that can be displayed on top of other widgets. pub trait Overlay @@ -29,6 +29,7 @@ where renderer: &Renderer, bounds: Size, position: Point, + translation: Vector, ) -> layout::Node; /// Draws the [`Overlay`] using the associated `Renderer`. diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index 3dd58f9b..a279fe28 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -13,6 +13,7 @@ use std::any::Any; #[allow(missing_debug_implementations)] pub struct Element<'a, Message, Renderer> { position: Point, + translation: Vector, overlay: Box + 'a>, } @@ -25,7 +26,11 @@ where position: Point, overlay: Box + 'a>, ) -> Self { - Self { position, overlay } + Self { + position, + overlay, + translation: Vector::ZERO, + } } /// Returns the position of the [`Element`]. @@ -36,6 +41,7 @@ where /// Translates the [`Element`]. pub fn translate(mut self, translation: Vector) -> Self { self.position = self.position + translation; + self.translation = self.translation + translation; self } @@ -48,6 +54,7 @@ where { Element { position: self.position, + translation: self.translation, overlay: Box::new(Map::new(self.overlay, f)), } } @@ -59,8 +66,12 @@ where bounds: Size, translation: Vector, ) -> layout::Node { - self.overlay - .layout(renderer, bounds, self.position + translation) + self.overlay.layout( + renderer, + bounds, + self.position + translation, + self.translation + translation, + ) } /// Processes a runtime [`Event`]. @@ -154,8 +165,9 @@ where renderer: &Renderer, bounds: Size, position: Point, + translation: Vector, ) -> layout::Node { - self.content.layout(renderer, bounds, position) + self.content.layout(renderer, bounds, position, translation) } fn operate( diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index dccf6dba..e1e9727a 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -4,7 +4,9 @@ use crate::mouse; use crate::overlay; use crate::renderer; use crate::widget; -use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size}; +use crate::{ + Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size, Vector, +}; /// An [`Overlay`] container that displays multiple overlay [`overlay::Element`] /// children. @@ -64,10 +66,9 @@ where &mut self, renderer: &Renderer, bounds: Size, - position: Point, + _position: Point, + translation: Vector, ) -> layout::Node { - let translation = position - Point::ORIGIN; - layout::Node::with_children( bounds, self.children -- cgit