summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2023-11-29 22:28:31 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2023-11-29 22:28:31 +0100
commite09b4e24dda51b8212d8ece52431dacaa3922a7b (patch)
tree7005e181528134ebdde5bbbe5909273db9f30174 /core
parent83c7870c569a2976923ee6243a19813094d44673 (diff)
parent7f8b17604a31e00becc43130ec516c1a53552c88 (diff)
downloadiced-e09b4e24dda51b8212d8ece52431dacaa3922a7b.tar.gz
iced-e09b4e24dda51b8212d8ece52431dacaa3922a7b.tar.bz2
iced-e09b4e24dda51b8212d8ece52431dacaa3922a7b.zip
Merge branch 'master' into feat/multi-window-support
Diffstat (limited to 'core')
-rw-r--r--core/Cargo.toml31
-rw-r--r--core/src/angle.rs62
-rw-r--r--core/src/color.rs22
-rw-r--r--core/src/element.rs32
-rw-r--r--core/src/font.rs17
-rw-r--r--core/src/gradient.rs8
-rw-r--r--core/src/hasher.rs7
-rw-r--r--core/src/image.rs17
-rw-r--r--core/src/layout.rs35
-rw-r--r--core/src/layout/flex.rs12
-rw-r--r--core/src/layout/limits.rs2
-rw-r--r--core/src/lib.rs14
-rw-r--r--core/src/mouse/click.rs7
-rw-r--r--core/src/overlay.rs5
-rw-r--r--core/src/overlay/element.rs37
-rw-r--r--core/src/overlay/group.rs17
-rw-r--r--core/src/rectangle.rs19
-rw-r--r--core/src/renderer.rs15
-rw-r--r--core/src/renderer/null.rs137
-rw-r--r--core/src/shell.rs6
-rw-r--r--core/src/text.rs134
-rw-r--r--core/src/text/editor.rs181
-rw-r--r--core/src/text/highlighter.rs88
-rw-r--r--core/src/text/paragraph.rs59
-rw-r--r--core/src/widget.rs14
-rw-r--r--core/src/widget/operation.rs32
-rw-r--r--core/src/widget/operation/focusable.rs16
-rw-r--r--core/src/widget/operation/scrollable.rs23
-rw-r--r--core/src/widget/operation/text_input.rs13
-rw-r--r--core/src/widget/text.rs132
-rw-r--r--core/src/widget/tree.rs86
-rw-r--r--core/src/window.rs2
-rw-r--r--core/src/window/icon.rs4
-rw-r--r--core/src/window/redraw_request.rs2
-rw-r--r--core/src/window/settings.rs17
-rw-r--r--core/src/window/settings/linux.rs11
36 files changed, 1035 insertions, 281 deletions
diff --git a/core/Cargo.toml b/core/Cargo.toml
index edf9e7c8..7db4fa53 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -1,24 +1,27 @@
[package]
name = "iced_core"
-version = "0.9.0"
-authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
-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
+xxhash-rust.workspace = true
+num-traits.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
[target.'cfg(windows)'.dependencies.raw-window-handle]
version = "0.5.2"
diff --git a/core/src/angle.rs b/core/src/angle.rs
index 75a57c76..102b69cf 100644
--- a/core/src/angle.rs
+++ b/core/src/angle.rs
@@ -1,32 +1,72 @@
use crate::{Point, Rectangle, Vector};
-use std::f32::consts::PI;
-#[derive(Debug, Copy, Clone, PartialEq)]
+use std::f32::consts::{FRAC_PI_2, PI};
+use std::ops::RangeInclusive;
+
/// 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> = Radians(0.0)..=Radians(2.0 * PI);
+}
+
impl From<Degrees> for Radians {
fn from(degrees: Degrees) -> Self {
- Radians(degrees.0 * PI / 180.0)
+ Self(degrees.0 * PI / 180.0)
+ }
+}
+
+impl From<f32> for Radians {
+ fn from(radians: f32) -> Self {
+ Self(radians)
+ }
+}
+
+impl From<u8> for Radians {
+ fn from(radians: u8) -> Self {
+ Self(f32::from(radians))
+ }
+}
+
+impl From<Radians> for f64 {
+ fn from(radians: Radians) -> Self {
+ Self::from(radians.0)
+ }
+}
+
+impl num_traits::FromPrimitive for Radians {
+ fn from_i64(n: i64) -> Option<Self> {
+ Some(Self(n as f32))
+ }
+
+ fn from_u64(n: u64) -> Option<Self> {
+ Some(Self(n as f32))
+ }
+
+ fn from_f64(n: f64) -> Option<Self> {
+ Some(Self(n as f32))
}
}
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 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)
}
diff --git a/core/src/color.rs b/core/src/color.rs
index 1392f28b..13077628 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
@@ -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] {
diff --git a/core/src/element.rs b/core/src/element.rs
index 3268f14b..dea111af 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;
@@ -291,7 +293,7 @@ where
}
fn diff(&self, tree: &mut Tree) {
- self.widget.diff(tree)
+ self.widget.diff(tree);
}
fn width(&self) -> Length {
@@ -304,10 +306,11 @@ where
fn layout(
&self,
+ tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- self.widget.layout(renderer, limits)
+ self.widget.layout(tree, renderer, limits)
}
fn operate(
@@ -325,11 +328,12 @@ where
fn container(
&mut self,
id: Option<&widget::Id>,
+ bounds: Rectangle,
operate_on_children: &mut dyn FnMut(
&mut dyn widget::Operation<T>,
),
) {
- self.operation.container(id, &mut |operation| {
+ self.operation.container(id, bounds, &mut |operation| {
operate_on_children(&mut MapOperation { operation });
});
}
@@ -346,8 +350,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(
@@ -380,6 +386,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 +399,7 @@ where
renderer,
clipboard,
&mut local_shell,
+ viewport,
);
shell.merge(local_shell, &self.mapper);
@@ -410,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(
@@ -484,10 +492,11 @@ where
fn layout(
&self,
+ tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- self.element.widget.layout(renderer, limits)
+ self.element.widget.layout(tree, renderer, limits)
}
fn operate(
@@ -499,7 +508,7 @@ where
) {
self.element
.widget
- .operate(state, layout, renderer, operation)
+ .operate(state, layout, renderer, operation);
}
fn on_event(
@@ -511,10 +520,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/font.rs b/core/src/font.rs
index bb425fd6..2b68decf 100644
--- a/core/src/font.rs
+++ b/core/src/font.rs
@@ -10,8 +10,8 @@ pub struct Font {
pub weight: Weight,
/// The [`Stretch`] of the [`Font`].
pub stretch: Stretch,
- /// Whether if the [`Font`] is monospaced or not.
- pub monospaced: bool,
+ /// The [`Style`] of the [`Font`].
+ pub style: Style,
}
impl Font {
@@ -20,13 +20,12 @@ impl Font {
family: Family::SansSerif,
weight: Weight::Normal,
stretch: Stretch::Normal,
- monospaced: false,
+ style: Style::Normal,
};
/// A monospaced font with normal [`Weight`].
pub const MONOSPACE: Font = Font {
family: Family::Monospace,
- monospaced: true,
..Self::DEFAULT
};
@@ -100,3 +99,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,
+}
diff --git a/core/src/gradient.rs b/core/src/gradient.rs
index e19622fb..4711b044 100644
--- a/core/src/gradient.rs
+++ b/core/src/gradient.rs
@@ -6,10 +6,8 @@ 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`].
+ /// A linear gradient interpolates colors along a direction at a specific angle.
Linear(Linear),
}
@@ -96,8 +94,8 @@ impl Linear {
mut self,
stops: impl IntoIterator<Item = ColorStop>,
) -> Self {
- for stop in stops.into_iter() {
- self = self.add_stop(stop.offset, stop.color)
+ for stop in stops {
+ self = self.add_stop(stop.offset, stop.color);
}
self
diff --git a/core/src/hasher.rs b/core/src/hasher.rs
index fa52f16d..a13d78af 100644
--- a/core/src/hasher.rs
+++ b/core/src/hasher.rs
@@ -1,10 +1,11 @@
/// 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]) {
- self.0.write(bytes)
+ self.0.write(bytes);
}
fn finish(&self) -> u64 {
diff --git a/core/src/image.rs b/core/src/image.rs
index 85d9d475..e9675316 100644
--- a/core/src/image.rs
+++ b/core/src/image.rs
@@ -164,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
@@ -178,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,
+ );
}
diff --git a/core/src/layout.rs b/core/src/layout.rs
index 04954fb9..caf315b6 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,36 @@ 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 mut 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();
+
+ 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(
+ 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..c02b63d8 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<Message, Renderer>(
spacing: f32,
align_items: Alignment,
items: &[Element<'_, Message, Renderer>],
+ trees: &mut [widget::Tree],
) -> Node
where
Renderer: crate::Renderer,
@@ -81,7 +83,7 @@ where
let mut nodes: Vec<Node> = 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.iter_mut()).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/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 76d775e7..54ea5839 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -1,29 +1,21 @@
//! 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"
)]
+#![forbid(unsafe_code, rust_2018_idioms)]
#![deny(
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
)]
-#![forbid(unsafe_code, rust_2018_idioms)]
-#![allow(clippy::inherent_to_string, clippy::type_complexity)]
pub mod alignment;
pub mod clipboard;
pub mod event;
diff --git a/core/src/mouse/click.rs b/core/src/mouse/click.rs
index 4a7d796c..6f3844be 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,
@@ -61,6 +61,11 @@ impl Click {
self.kind
}
+ /// Returns the position of the [`Click`].
+ 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)
diff --git a/core/src/overlay.rs b/core/src/overlay.rs
index 2e05db93..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<Message, Renderer>
@@ -25,10 +25,11 @@ where
///
/// [`Node`]: layout::Node
fn layout(
- &self,
+ &mut self,
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 c2134343..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<dyn Overlay<Message, Renderer> + 'a>,
}
@@ -25,7 +26,11 @@ where
position: Point,
overlay: Box<dyn Overlay<Message, Renderer> + '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,19 +54,24 @@ where
{
Element {
position: self.position,
+ translation: self.translation,
overlay: Box::new(Map::new(self.overlay, f)),
}
}
/// Computes the layout of the [`Element`] in the given bounds.
pub fn layout(
- &self,
+ &mut self,
renderer: &Renderer,
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`].
@@ -98,7 +109,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`].
@@ -150,12 +161,13 @@ where
Renderer: crate::Renderer,
{
fn layout(
- &self,
+ &mut self,
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(
@@ -172,11 +184,12 @@ where
fn container(
&mut self,
id: Option<&widget::Id>,
+ bounds: Rectangle,
operate_on_children: &mut dyn FnMut(
&mut dyn widget::Operation<T>,
),
) {
- self.operation.container(id, &mut |operation| {
+ self.operation.container(id, bounds, &mut |operation| {
operate_on_children(&mut MapOperation { operation });
});
}
@@ -193,8 +206,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(
@@ -202,7 +217,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>) {
@@ -259,7 +274,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 deffaad0..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.
@@ -61,17 +63,16 @@ where
Renderer: crate::Renderer,
{
fn layout(
- &self,
+ &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
- .iter()
+ .iter_mut()
.map(|child| child.layout(renderer, bounds, translation))
.collect(),
)
@@ -138,12 +139,12 @@ where
renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
- 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..c1c2eeac 100644
--- a/core/src/rectangle.rs
+++ b/core/src/rectangle.rs
@@ -74,9 +74,9 @@ impl Rectangle<f32> {
/// 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
@@ -197,3 +197,18 @@ where
}
}
}
+
+impl<T> std::ops::Sub<Vector<T>> for Rectangle<T>
+where
+ T: std::ops::Sub<Output = T>,
+{
+ type Output = Rectangle<T>;
+
+ fn sub(self, translation: Vector<T>) -> Self {
+ Rectangle {
+ x: self.x - translation.x,
+ y: self.y - translation.y,
+ ..self
+ }
+ }
+}
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<Message>(
- &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..da0f32de 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,8 @@ 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';
@@ -50,37 +53,117 @@ 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 hit_test(
- &self,
- _contents: &str,
- _size: f32,
- _line_height: text::LineHeight,
- _font: Self::Font,
- _bounds: Size,
- _shaping: text::Shaping,
- _point: Point,
- _nearest_only: bool,
- ) -> Option<text::Hit> {
+ fn fill_paragraph(
+ &mut self,
+ _paragraph: &Self::Paragraph,
+ _position: Point,
+ _color: Color,
+ ) {
+ }
+
+ fn fill_editor(
+ &mut self,
+ _editor: &Self::Editor,
+ _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 with_text(_text: Text<'_, Self::Font>) -> Self {}
+
+ fn resize(&mut self, _new_bounds: Size) {}
+
+ fn compare(&self, _text: Text<'_, Self::Font>) -> text::Difference {
+ text::Difference::None
+ }
+
+ 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<Point> {
+ None
+ }
+
+ fn min_bounds(&self) -> Size {
+ Size::ZERO
+ }
+
+ fn hit_test(&self, _point: Point) -> Option<text::Hit> {
+ 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 cursor_position(&self) -> (usize, usize) {
+ (0, 0)
+ }
+
+ fn selection(&self) -> Option<String> {
+ None
+ }
+
+ fn line(&self, _index: usize) -> Option<&str> {
None
}
- fn fill_text(&mut self, _text: Text<'_, Self::Font>) {}
+ fn line_count(&self) -> usize {
+ 0
+ }
+
+ fn perform(&mut self, _action: text::editor::Action) {}
+
+ fn bounds(&self) -> Size {
+ Size::ZERO
+ }
+
+ fn update(
+ &mut self,
+ _new_bounds: Size,
+ _new_font: Self::Font,
+ _new_size: Pixels,
+ _new_line_height: text::LineHeight,
+ _new_highlighter: &mut impl text::Highlighter,
+ ) {
+ }
+
+ fn highlight<H: text::Highlighter>(
+ &mut self,
+ _font: Self::Font,
+ _highlighter: &mut H,
+ _format_highlight: impl Fn(
+ &H::Highlight,
+ ) -> text::highlighter::Format<Self::Font>,
+ ) {
+ }
}
diff --git a/core/src/shell.rs b/core/src/shell.rs
index 74a5c616..2952ceff 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<window::RedrawRequest> {
self.redraw_request
}
@@ -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/text.rs b/core/src/text.rs
index fc8aa20e..546d0b5c 100644
--- a/core/src/text.rs
+++ b/core/src/text.rs
@@ -1,6 +1,15 @@
//! Draw and interact with text.
+mod paragraph;
+
+pub mod editor;
+pub mod highlighter;
+
+pub use editor::Editor;
+pub use highlighter::Highlighter;
+pub use paragraph::Paragraph;
+
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 +21,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,
@@ -129,10 +135,43 @@ impl Hit {
}
}
+/// The difference detected in some text.
+///
+/// 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.
+ ///
+ /// 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.
- type Font: Copy;
+ type Font: Copy + PartialEq;
+
+ /// The [`Paragraph`] of this [`Renderer`].
+ type Paragraph: Paragraph<Font = Self::Font> + 'static;
+
+ /// The [`Editor`] of this [`Renderer`].
+ type Editor: Editor<Font = Self::Font> + 'static;
/// The icon font of the backend.
const ICON_FONT: Self::Font;
@@ -151,62 +190,35 @@ pub trait Renderer: crate::Renderer {
fn default_font(&self) -> Self::Font;
/// Returns the default size of [`Text`].
- fn default_size(&self) -> f32;
-
- /// Measures the text in the given bounds and returns the minimum boundaries
- /// that can fit the contents.
- fn measure(
- &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(
- &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
- }
-
- /// 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<Hit>;
+ fn default_size(&self) -> Pixels;
/// Loads a [`Self::Font`] from its bytes.
fn load_font(&mut self, font: Cow<'static, [u8]>);
- /// Draws the given [`Text`].
- fn fill_text(&mut self, text: Text<'_, Self::Font>);
+ /// 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 [`Editor`] at the given position and with the given
+ /// [`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(
+ &mut self,
+ text: Text<'_, Self::Font>,
+ position: Point,
+ color: Color,
+ );
}
diff --git a/core/src/text/editor.rs b/core/src/text/editor.rs
new file mode 100644
index 00000000..f3c6e342
--- /dev/null
+++ b/core/src/text/editor.rs
@@ -0,0 +1,181 @@
+//! 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<String>;
+
+ /// 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`].
+ fn 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,
+ new_highlighter: &mut impl Highlighter,
+ );
+
+ /// Runs a text [`Highlighter`] in the [`Editor`].
+ fn highlight<H: Highlighter>(
+ &mut self,
+ font: Self::Font,
+ highlighter: &mut H,
+ format_highlight: impl Fn(&H::Highlight) -> highlighter::Format<Self::Font>,
+ );
+}
+
+/// 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 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<String>),
+ /// 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,
+ Self::Right => Self::WordRight,
+ Self::Home => Self::DocumentStart,
+ Self::End => Self::DocumentEnd,
+ _ => self,
+ }
+ }
+
+ /// Returns the [`Direction`] of the [`Motion`].
+ 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,
+ }
+ }
+}
+
+/// A direction in some text.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Direction {
+ /// <-
+ Left,
+ /// ->
+ Right,
+}
+
+/// 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<Rectangle>),
+}
diff --git a/core/src/text/highlighter.rs b/core/src/text/highlighter.rs
new file mode 100644
index 00000000..a0535228
--- /dev/null
+++ b/core/src/text/highlighter.rs
@@ -0,0 +1,88 @@
+//! 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<Item = (Range<usize>, 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;
+}
+
+/// A highlighter that highlights nothing.
+#[derive(Debug, Clone, Copy)]
+pub struct PlainText;
+
+impl Highlighter for PlainText {
+ type Settings = ();
+ type Highlight = ();
+
+ type Iterator<'a> = std::iter::Empty<(Range<usize>, Self::Highlight)>;
+
+ fn new(_settings: &Self::Settings) -> Self {
+ 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<'_> {
+ std::iter::empty()
+ }
+
+ fn current_line(&self) -> usize {
+ usize::MAX
+ }
+}
+
+/// The format of some text.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Format<Font> {
+ /// The [`Color`] of the text.
+ pub color: Option<Color>,
+ /// The `Font` of the text.
+ pub font: Option<Font>,
+}
+
+impl<Font> Default for Format<Font> {
+ fn default() -> Self {
+ Self {
+ color: None,
+ font: None,
+ }
+ }
+}
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<Hit>;
+
+ /// Returns the distance to the given grapheme index in the [`Paragraph`].
+ fn grapheme_position(&self, line: usize, index: usize) -> Option<Point>;
+
+ /// 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
+ }
+}
diff --git a/core/src/widget.rs b/core/src/widget.rs
index 79d86444..294d5984 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<Message, Renderer>
where
Renderer: crate::Renderer,
@@ -55,6 +55,7 @@ where
/// user interface.
fn layout(
&self,
+ tree: &mut 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,
@@ -115,6 +116,7 @@ where
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_shell: &mut Shell<'_, Message>,
+ _viewport: &Rectangle,
) -> event::Status {
event::Status::Ignored
}
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<T> {
fn container(
&mut self,
id: Option<&Id>,
+ bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
);
@@ -30,7 +32,14 @@ pub trait Operation<T> {
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<B>),
) {
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<B>),
) {
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<T: 'static>(
fn container(
&mut self,
id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>),
) {
if id == Some(&self.target) {
diff --git a/core/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs
index 312e4894..68c22faa 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,9 +46,10 @@ pub fn focus<T>(target: Id) -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
}
@@ -80,9 +82,10 @@ where
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
fn finish(&self) -> Outcome<T> {
@@ -126,9 +129,10 @@ pub fn focus_previous<T>() -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
}
@@ -159,9 +163,10 @@ pub fn focus_next<T>() -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
}
@@ -185,9 +190,10 @@ pub fn find_focused() -> impl Operation<Id> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<Id>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
fn finish(&self) -> Outcome<Id> {
diff --git a/core/src/widget/operation/scrollable.rs b/core/src/widget/operation/scrollable.rs
index f947344d..12161255 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<T>(target: Id, offset: RelativeOffset) -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ 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<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ 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..41731d4c 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,9 +35,10 @@ pub fn move_cursor_to_front<T>(target: Id) -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
}
@@ -63,9 +65,10 @@ pub fn move_cursor_to_end<T>(target: Id) -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
}
@@ -93,9 +96,10 @@ pub fn move_cursor_to<T>(target: Id, position: usize) -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
}
@@ -121,9 +125,10 @@ pub fn select_all<T>(target: Id) -> impl Operation<T> {
fn container(
&mut self,
_id: Option<&Id>,
+ _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) {
- operate_on_children(self)
+ operate_on_children(self);
}
}
diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs
index 79df2b02..97e0acac 100644
--- a/core/src/widget/text.rs
+++ b/core/src/widget/text.rs
@@ -3,9 +3,9 @@ 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};
+use crate::widget::tree::{self, Tree};
+use crate::{Color, Element, Layout, Length, Pixels, Point, Rectangle, Widget};
use std::borrow::Cow;
@@ -19,7 +19,7 @@ where
Renderer::Theme: StyleSheet,
{
content: Cow<'a, str>,
- size: Option<f32>,
+ size: Option<Pixels>,
line_height: LineHeight,
width: Length,
height: Length,
@@ -53,7 +53,7 @@ where
/// Sets the size of the [`Text`].
pub fn size(mut self, size: impl Into<Pixels>) -> Self {
- self.size = Some(size.into().0);
+ self.size = Some(size.into());
self
}
@@ -117,11 +117,23 @@ where
}
}
+/// The internal state of a [`Text`] widget.
+#[derive(Debug, Default)]
+pub struct State<P: Paragraph>(P);
+
impl<'a, Message, Renderer> Widget<Message, Renderer> for Text<'a, Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
{
+ fn tag(&self) -> tree::Tag {
+ tree::Tag::of::<State<Renderer::Paragraph>>()
+ }
+
+ fn state(&self) -> tree::State {
+ tree::State::new(State(Renderer::Paragraph::default()))
+ }
+
fn width(&self) -> Length {
self.width
}
@@ -132,30 +144,29 @@ where
fn layout(
&self,
+ tree: &mut 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_mut::<State<Renderer::Paragraph>>(),
+ 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 +174,60 @@ where
_cursor_position: mouse::Cursor,
_viewport: &Rectangle,
) {
+ let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
+
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<Renderer>(
+ state: &mut State<Renderer::Paragraph>,
+ renderer: &Renderer,
+ limits: &layout::Limits,
+ width: Length,
+ height: Length,
+ content: &str,
+ line_height: LineHeight,
+ size: Option<Pixels>,
+ font: Option<Renderer::Font>,
+ 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 State(ref mut paragraph) = state;
+
+ paragraph.update(text::Text {
+ content,
+ bounds,
+ size,
+ line_height,
+ font,
+ horizontal_alignment,
+ vertical_alignment,
+ shaping,
+ });
+
+ let size = limits.resolve(paragraph.min_bounds());
+
+ layout::Node::new(size)
+}
+
/// Draws text using the same logic as the [`Text`] widget.
///
/// Specifically:
@@ -193,44 +242,31 @@ pub fn draw<Renderer>(
renderer: &mut Renderer,
style: &renderer::Style,
layout: Layout<'_>,
- content: &str,
- size: Option<f32>,
- line_height: LineHeight,
- font: Option<Renderer::Font>,
+ state: &State<Renderer::Paragraph>,
appearance: Appearance,
- horizontal_alignment: alignment::Horizontal,
- vertical_alignment: alignment::Vertical,
- shaping: Shaping,
) where
Renderer: text::Renderer,
{
+ let State(ref paragraph) = state;
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(
+ paragraph,
+ Point::new(x, y),
+ appearance.color.unwrap_or(style.text_color),
+ );
}
impl<'a, Message, Renderer> From<Text<'a, Renderer>>
diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs
index da269632..ff52b1ce 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
@@ -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<T>(
+ current_children: &mut Vec<Tree>,
+ 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);
diff --git a/core/src/window.rs b/core/src/window.rs
index 10db31b6..448ffc45 100644
--- a/core/src/window.rs
+++ b/core/src/window.rs
@@ -1,5 +1,6 @@
//! Build window-based GUI applications.
pub mod icon;
+pub mod settings;
mod event;
mod id;
@@ -7,7 +8,6 @@ mod level;
mod mode;
mod position;
mod redraw_request;
-mod settings;
mod user_attention;
pub use event::Event;
diff --git a/core/src/window/icon.rs b/core/src/window/icon.rs
index 31868ecf..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<u8>,
width: u32,
@@ -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.
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() {
diff --git a/core/src/window/settings.rs b/core/src/window/settings.rs
index eba27914..25df8159 100644
--- a/core/src/window/settings.rs
+++ b/core/src/window/settings.rs
@@ -1,5 +1,4 @@
-use crate::window::{Icon, Level, Position};
-
+//! Configure your windows.
#[cfg(target_os = "windows")]
#[path = "settings/windows.rs"]
mod platform;
@@ -8,6 +7,10 @@ mod platform;
#[path = "settings/macos.rs"]
mod platform;
+#[cfg(target_os = "linux")]
+#[path = "settings/linux.rs"]
+mod platform;
+
#[cfg(target_arch = "wasm32")]
#[path = "settings/wasm.rs"]
mod platform;
@@ -15,13 +18,15 @@ mod platform;
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
+ target_os = "linux",
target_arch = "wasm32"
)))]
#[path = "settings/other.rs"]
mod platform;
-pub use platform::PlatformSpecific;
+use crate::window::{Icon, Level, Position};
+pub use platform::PlatformSpecific;
/// The window settings of an application.
#[derive(Debug, Clone)]
pub struct Settings {
@@ -70,8 +75,8 @@ pub struct Settings {
}
impl Default for Settings {
- fn default() -> Settings {
- Settings {
+ fn default() -> Self {
+ Self {
size: (1024, 768),
position: Position::default(),
min_size: None,
@@ -82,8 +87,8 @@ impl Default for Settings {
transparent: false,
level: Level::default(),
icon: None,
- platform_specific: Default::default(),
exit_on_close_request: true,
+ platform_specific: PlatformSpecific::default(),
}
}
}
diff --git a/core/src/window/settings/linux.rs b/core/src/window/settings/linux.rs
new file mode 100644
index 00000000..009b9d9e
--- /dev/null
+++ b/core/src/window/settings/linux.rs
@@ -0,0 +1,11 @@
+//! Platform specific settings for Linux.
+
+/// The platform specific window settings of an application.
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
+pub struct PlatformSpecific {
+ /// Sets the application id of the window.
+ ///
+ /// As a best practice, it is suggested to select an application id that match
+ /// the basename of the application’s .desktop file.
+ pub application_id: String,
+}