Rename iced_virtual to iced_pure

`virtual` is a reserved keyword in Rust 😬
This commit is contained in:
Héctor Ramón Jiménez 2022-02-11 17:50:12 +07:00
parent e03de01988
commit 897188317b
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
15 changed files with 16 additions and 15 deletions

1
pure/src/application.rs Normal file
View file

@ -0,0 +1 @@

232
pure/src/flex.rs Normal file
View file

@ -0,0 +1,232 @@
//! Distribute elements using a flex-based layout.
// This code is heavily inspired by the [`druid`] codebase.
//
// [`druid`]: https://github.com/xi-editor/druid
//
// Copyright 2018 The xi-editor Authors, Héctor Ramón
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::Element;
use iced_native::layout::{Limits, Node};
use iced_native::{Alignment, Padding, Point, Size};
/// The main axis of a flex layout.
#[derive(Debug)]
pub enum Axis {
/// The horizontal axis
Horizontal,
/// The vertical axis
Vertical,
}
impl Axis {
fn main(&self, size: Size) -> f32 {
match self {
Axis::Horizontal => size.width,
Axis::Vertical => size.height,
}
}
fn cross(&self, size: Size) -> f32 {
match self {
Axis::Horizontal => size.height,
Axis::Vertical => size.width,
}
}
fn pack(&self, main: f32, cross: f32) -> (f32, f32) {
match self {
Axis::Horizontal => (main, cross),
Axis::Vertical => (cross, main),
}
}
}
/// Computes the flex layout with the given axis and limits, applying spacing,
/// padding and alignment to the items as needed.
///
/// It returns a new layout [`Node`].
pub fn resolve<Message, Renderer>(
axis: Axis,
renderer: &Renderer,
limits: &Limits,
padding: Padding,
spacing: f32,
align_items: Alignment,
items: &[Element<Message, Renderer>],
) -> Node
where
Renderer: iced_native::Renderer,
{
let limits = limits.pad(padding);
let total_spacing = spacing * items.len().saturating_sub(1) as f32;
let max_cross = axis.cross(limits.max());
let mut fill_sum = 0;
let mut cross = axis.cross(limits.min()).max(axis.cross(limits.fill()));
let mut available = axis.main(limits.max()) - total_spacing;
let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
nodes.resize(items.len(), Node::default());
if align_items == Alignment::Fill {
let mut fill_cross = axis.cross(limits.min());
items.iter().for_each(|child| {
let cross_fill_factor = match axis {
Axis::Horizontal => child.as_widget().height(),
Axis::Vertical => child.as_widget().width(),
}
.fill_factor();
if cross_fill_factor == 0 {
let (max_width, max_height) = axis.pack(available, max_cross);
let child_limits =
Limits::new(Size::ZERO, Size::new(max_width, max_height));
let layout = child.as_widget().layout(renderer, &child_limits);
let size = layout.size();
fill_cross = fill_cross.max(axis.cross(size));
}
});
cross = fill_cross;
}
for (i, child) in items.iter().enumerate() {
let fill_factor = match axis {
Axis::Horizontal => child.as_widget().width(),
Axis::Vertical => child.as_widget().height(),
}
.fill_factor();
if fill_factor == 0 {
let (min_width, min_height) = if align_items == Alignment::Fill {
axis.pack(0.0, cross)
} else {
axis.pack(0.0, 0.0)
};
let (max_width, max_height) = if align_items == Alignment::Fill {
axis.pack(available, cross)
} else {
axis.pack(available, max_cross)
};
let child_limits = Limits::new(
Size::new(min_width, min_height),
Size::new(max_width, max_height),
);
let layout = child.as_widget().layout(renderer, &child_limits);
let size = layout.size();
available -= axis.main(size);
if align_items != Alignment::Fill {
cross = cross.max(axis.cross(size));
}
nodes[i] = layout;
} else {
fill_sum += fill_factor;
}
}
let remaining = available.max(0.0);
for (i, child) in items.iter().enumerate() {
let fill_factor = match axis {
Axis::Horizontal => child.as_widget().width(),
Axis::Vertical => child.as_widget().height(),
}
.fill_factor();
if fill_factor != 0 {
let max_main = remaining * fill_factor as f32 / fill_sum as f32;
let min_main = if max_main.is_infinite() {
0.0
} else {
max_main
};
let (min_width, min_height) = if align_items == Alignment::Fill {
axis.pack(min_main, cross)
} else {
axis.pack(min_main, axis.cross(limits.min()))
};
let (max_width, max_height) = if align_items == Alignment::Fill {
axis.pack(max_main, cross)
} else {
axis.pack(max_main, max_cross)
};
let child_limits = Limits::new(
Size::new(min_width, min_height),
Size::new(max_width, max_height),
);
let layout = child.as_widget().layout(renderer, &child_limits);
if align_items != Alignment::Fill {
cross = cross.max(axis.cross(layout.size()));
}
nodes[i] = layout;
}
}
let pad = axis.pack(padding.left as f32, padding.top as f32);
let mut main = pad.0;
for (i, node) in nodes.iter_mut().enumerate() {
if i > 0 {
main += spacing;
}
let (x, y) = axis.pack(main, pad.1);
node.move_to(Point::new(x, y));
match axis {
Axis::Horizontal => {
node.align(
Alignment::Start,
align_items,
Size::new(0.0, cross),
);
}
Axis::Vertical => {
node.align(
align_items,
Alignment::Start,
Size::new(cross, 0.0),
);
}
}
let size = node.size();
main += axis.main(size);
}
let (width, height) = axis.pack(main - pad.0, cross);
let size = limits.resolve(Size::new(width, height));
Node::with_children(size.pad(padding), nodes)
}

146
pure/src/lib.rs Normal file
View file

@ -0,0 +1,146 @@
pub mod widget;
pub(crate) mod flex;
pub use widget::*;
use iced_native::event::{self, Event};
use iced_native::layout::{self, Layout};
use iced_native::mouse;
use iced_native::renderer;
use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell};
pub struct Pure<'a, Message, Renderer> {
state: &'a mut State<Message, Renderer>,
}
impl<'a, Message, Renderer> Pure<'a, Message, Renderer>
where
Message: 'static,
Renderer: iced_native::Renderer + 'static,
{
pub fn new(
state: &'a mut State<Message, Renderer>,
content: impl Into<Element<Message, Renderer>>,
) -> Self {
let _ = state.diff(content.into());
Self { state }
}
}
pub struct State<Message, Renderer> {
state_tree: widget::Tree<Message, Renderer>,
last_element: Element<Message, Renderer>,
}
impl<Message, Renderer> State<Message, Renderer>
where
Message: 'static,
Renderer: iced_native::Renderer + 'static,
{
pub fn new() -> Self {
let last_element = Element::new(widget::Column::new());
Self {
state_tree: widget::Tree::new(&last_element),
last_element,
}
}
fn diff(&mut self, new_element: Element<Message, Renderer>) {
self.state_tree.diff(&self.last_element, &new_element);
self.last_element = new_element;
}
}
impl<'a, Message, Renderer> iced_native::Widget<Message, Renderer>
for Pure<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
{
fn width(&self) -> Length {
self.state.last_element.as_widget().width()
}
fn height(&self) -> Length {
self.state.last_element.as_widget().height()
}
fn hash_layout(&self, state: &mut Hasher) {
self.state.last_element.as_widget().hash_layout(state)
}
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.state.last_element.as_widget().layout(renderer, limits)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.state.last_element.as_widget_mut().on_event(
&mut self.state.state_tree,
event,
layout,
cursor_position,
renderer,
clipboard,
shell,
)
}
fn draw(
&self,
renderer: &mut Renderer,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
) {
self.state.last_element.as_widget().draw(
&self.state.state_tree,
renderer,
style,
layout,
cursor_position,
viewport,
)
}
fn mouse_interaction(
&self,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
self.state.last_element.as_widget().mouse_interaction(
&self.state.state_tree,
layout,
cursor_position,
viewport,
renderer,
)
}
}
impl<'a, Message, Renderer> Into<iced_native::Element<'a, Message, Renderer>>
for Pure<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
{
fn into(self) -> iced_native::Element<'a, Message, Renderer> {
iced_native::Element::new(self)
}
}

73
pure/src/widget.rs Normal file
View file

@ -0,0 +1,73 @@
mod button;
mod column;
mod element;
mod text;
mod tree;
pub use button::Button;
pub use column::Column;
pub use element::Element;
pub use text::Text;
pub use tree::Tree;
use iced_native::event::{self, Event};
use iced_native::layout::{self, Layout};
use iced_native::mouse;
use iced_native::renderer;
use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell};
use std::any::{self, Any};
pub trait Widget<Message, Renderer> {
fn tag(&self) -> any::TypeId;
fn state(&self) -> Box<dyn Any>;
fn children(&self) -> &[Element<Message, Renderer>];
fn width(&self) -> Length;
fn height(&self) -> Length;
fn hash_layout(&self, state: &mut Hasher);
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node;
fn draw(
&self,
state: &Tree<Message, Renderer>,
renderer: &mut Renderer,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
);
fn mouse_interaction(
&self,
_state: &Tree<Message, Renderer>,
_layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
mouse::Interaction::Idle
}
fn on_event(
&mut self,
_state: &mut Tree<Message, Renderer>,
_event: Event,
_layout: Layout<'_>,
_cursor_position: Point,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_shell: &mut Shell<'_, Message>,
) -> event::Status {
event::Status::Ignored
}
}

272
pure/src/widget/button.rs Normal file
View file

@ -0,0 +1,272 @@
use crate::widget::{Element, Tree, Widget};
use iced_native::event::{self, Event};
use iced_native::layout;
use iced_native::mouse;
use iced_native::renderer;
use iced_native::touch;
use iced_native::{
Background, Clipboard, Color, Hasher, Layout, Length, Padding, Point,
Rectangle, Shell, Vector,
};
use iced_style::button::StyleSheet;
use std::any::Any;
pub struct Button<Message, Renderer> {
content: Element<Message, Renderer>,
on_press: Option<Message>,
style_sheet: Box<dyn StyleSheet>,
width: Length,
height: Length,
padding: Padding,
}
impl<Message, Renderer> Button<Message, Renderer> {
pub fn new(content: impl Into<Element<Message, Renderer>>) -> Self {
Button {
content: content.into(),
on_press: None,
style_sheet: Default::default(),
width: Length::Shrink,
height: Length::Shrink,
padding: Padding::new(5),
}
}
pub fn on_press(mut self, on_press: Message) -> Self {
self.on_press = Some(on_press);
self
}
}
impl<Message, Renderer> Widget<Message, Renderer> for Button<Message, Renderer>
where
Message: 'static + Clone,
Renderer: 'static + iced_native::Renderer,
{
fn tag(&self) -> std::any::TypeId {
std::any::TypeId::of::<State>()
}
fn state(&self) -> Box<dyn Any> {
Box::new(State { is_pressed: false })
}
fn children(&self) -> &[Element<Message, Renderer>] {
std::slice::from_ref(&self.content)
}
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
self.height
}
fn hash_layout(&self, state: &mut Hasher) {
use std::hash::Hash;
self.tag().hash(state);
self.width.hash(state);
self.content.as_widget().hash_layout(state);
}
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits
.width(self.width)
.height(self.height)
.pad(self.padding);
let mut content = self.content.as_widget().layout(renderer, &limits);
content.move_to(Point::new(
self.padding.left.into(),
self.padding.top.into(),
));
let size = limits.resolve(content.size()).pad(self.padding);
layout::Node::with_children(size, vec![content])
}
fn on_event(
&mut self,
tree: &mut Tree<Message, Renderer>,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
let state = if let Some(state) = tree.state.downcast_mut::<State>() {
state
} else {
return event::Status::Ignored;
};
if let event::Status::Captured = self.content.as_widget_mut().on_event(
&mut tree.children[0],
event.clone(),
layout.children().next().unwrap(),
cursor_position,
renderer,
clipboard,
shell,
) {
return event::Status::Captured;
}
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
| Event::Touch(touch::Event::FingerPressed { .. }) => {
if self.on_press.is_some() {
let bounds = layout.bounds();
if bounds.contains(cursor_position) {
state.is_pressed = true;
return event::Status::Captured;
}
}
}
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
| Event::Touch(touch::Event::FingerLifted { .. }) => {
if let Some(on_press) = self.on_press.clone() {
let bounds = layout.bounds();
if state.is_pressed {
state.is_pressed = false;
if bounds.contains(cursor_position) {
shell.publish(on_press);
}
return event::Status::Captured;
}
}
}
Event::Touch(touch::Event::FingerLost { .. }) => {
state.is_pressed = false;
}
_ => {}
}
event::Status::Ignored
}
fn draw(
&self,
tree: &Tree<Message, Renderer>,
renderer: &mut Renderer,
_style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
) {
let state = if let Some(state) = tree.state.downcast_ref::<State>() {
state
} else {
return;
};
let bounds = layout.bounds();
let content_layout = layout.children().next().unwrap();
let is_mouse_over = bounds.contains(cursor_position);
let is_disabled = self.on_press.is_none();
let styling = if is_disabled {
self.style_sheet.disabled()
} else if is_mouse_over {
if state.is_pressed {
self.style_sheet.pressed()
} else {
self.style_sheet.hovered()
}
} else {
self.style_sheet.active()
};
if styling.background.is_some() || styling.border_width > 0.0 {
if styling.shadow_offset != Vector::default() {
// TODO: Implement proper shadow support
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle {
x: bounds.x + styling.shadow_offset.x,
y: bounds.y + styling.shadow_offset.y,
..bounds
},
border_radius: styling.border_radius,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
Background::Color([0.0, 0.0, 0.0, 0.5].into()),
);
}
renderer.fill_quad(
renderer::Quad {
bounds,
border_radius: styling.border_radius,
border_width: styling.border_width,
border_color: styling.border_color,
},
styling
.background
.unwrap_or(Background::Color(Color::TRANSPARENT)),
);
}
self.content.as_widget().draw(
&tree.children[0],
renderer,
&renderer::Style {
text_color: styling.text_color,
},
content_layout,
cursor_position,
&bounds,
);
}
fn mouse_interaction(
&self,
_tree: &Tree<Message, Renderer>,
layout: Layout<'_>,
cursor_position: Point,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
let is_mouse_over = layout.bounds().contains(cursor_position);
let is_disabled = self.on_press.is_none();
if is_mouse_over && !is_disabled {
mouse::Interaction::Pointer
} else {
mouse::Interaction::default()
}
}
}
#[derive(Debug, Clone)]
struct State {
is_pressed: bool,
}
impl<Message, Renderer> Into<Element<Message, Renderer>>
for Button<Message, Renderer>
where
Message: Clone + 'static,
Renderer: iced_native::Renderer + 'static,
{
fn into(self) -> Element<Message, Renderer> {
Element::new(self)
}
}

220
pure/src/widget/column.rs Normal file
View file

@ -0,0 +1,220 @@
use crate::flex;
use crate::widget::{Element, Tree, Widget};
use iced_native::event::{self, Event};
use iced_native::layout::{self, Layout};
use iced_native::mouse;
use iced_native::renderer;
use iced_native::{
Alignment, Clipboard, Hasher, Length, Padding, Point, Rectangle, Shell,
};
use std::any::{self, Any};
pub struct Column<Message, Renderer> {
spacing: u16,
padding: Padding,
width: Length,
height: Length,
align_items: Alignment,
children: Vec<Element<Message, Renderer>>,
}
impl<'a, Message, Renderer> Column<Message, Renderer> {
pub fn new() -> Self {
Self::with_children(Vec::new())
}
pub fn with_children(children: Vec<Element<Message, Renderer>>) -> Self {
Column {
spacing: 0,
padding: Padding::ZERO,
width: Length::Shrink,
height: Length::Shrink,
align_items: Alignment::Start,
children,
}
}
pub fn spacing(mut self, units: u16) -> Self {
self.spacing = units;
self
}
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.padding = padding.into();
self
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn align_items(mut self, align: Alignment) -> Self {
self.align_items = align;
self
}
pub fn push(
mut self,
child: impl Into<Element<Message, Renderer>>,
) -> Self {
self.children.push(child.into());
self
}
}
impl<Message, Renderer> Widget<Message, Renderer> for Column<Message, Renderer>
where
Renderer: iced_native::Renderer,
{
fn tag(&self) -> any::TypeId {
struct Marker;
any::TypeId::of::<Marker>()
}
fn state(&self) -> Box<dyn Any> {
Box::new(())
}
fn children(&self) -> &[Element<Message, Renderer>] {
&self.children
}
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
self.height
}
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.width).height(self.height);
flex::resolve(
flex::Axis::Vertical,
renderer,
&limits,
self.padding,
self.spacing as f32,
self.align_items,
&self.children,
)
}
fn on_event(
&mut self,
tree: &mut Tree<Message, Renderer>,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.children
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.map(|((child, state), layout)| {
child.as_widget_mut().on_event(
state,
event.clone(),
layout,
cursor_position,
renderer,
clipboard,
shell,
)
})
.fold(event::Status::Ignored, event::Status::merge)
}
fn mouse_interaction(
&self,
tree: &Tree<Message, Renderer>,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
self.children
.iter()
.zip(&tree.children)
.zip(layout.children())
.map(|((child, state), layout)| {
child.as_widget().mouse_interaction(
state,
layout,
cursor_position,
viewport,
renderer,
)
})
.max()
.unwrap_or_default()
}
fn draw(
&self,
tree: &Tree<Message, Renderer>,
renderer: &mut Renderer,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
) {
for ((child, state), layout) in self
.children
.iter()
.zip(&tree.children)
.zip(layout.children())
{
child.as_widget().draw(
state,
renderer,
style,
layout,
cursor_position,
viewport,
);
}
}
fn hash_layout(&self, state: &mut Hasher) {
use std::hash::Hash;
self.tag().hash(state);
self.width.hash(state);
self.height.hash(state);
self.align_items.hash(state);
self.spacing.hash(state);
self.padding.hash(state);
for child in &self.children {
child.as_widget().hash_layout(state);
}
}
}
impl<Message, Renderer> Into<Element<Message, Renderer>>
for Column<Message, Renderer>
where
Message: 'static,
Renderer: iced_native::Renderer + 'static,
{
fn into(self) -> Element<Message, Renderer> {
Element::new(self)
}
}

View file

@ -0,0 +1,21 @@
use crate::Widget;
pub struct Element<Message, Renderer> {
widget: Box<dyn Widget<Message, Renderer>>,
}
impl<Message, Renderer> Element<Message, Renderer> {
pub fn new(widget: impl Widget<Message, Renderer> + 'static) -> Self {
Self {
widget: Box::new(widget),
}
}
pub fn as_widget(&self) -> &dyn Widget<Message, Renderer> {
self.widget.as_ref()
}
pub fn as_widget_mut(&mut self) -> &mut dyn Widget<Message, Renderer> {
self.widget.as_mut()
}
}

185
pure/src/widget/text.rs Normal file
View file

@ -0,0 +1,185 @@
use crate::{Element, Tree, Widget};
use iced_native::alignment;
use iced_native::layout::{self, Layout};
use iced_native::renderer;
use iced_native::text;
use iced_native::{Color, Hasher, Length, Point, Rectangle, Size};
use std::any::{self, Any};
pub struct Text<Renderer>
where
Renderer: text::Renderer,
{
content: String,
size: Option<u16>,
color: Option<Color>,
font: Renderer::Font,
width: Length,
height: Length,
horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical,
}
impl<Renderer: text::Renderer> Text<Renderer> {
/// Create a new fragment of [`Text`] with the given contents.
pub fn new<T: Into<String>>(label: T) -> Self {
Text {
content: label.into(),
size: None,
color: None,
font: Default::default(),
width: Length::Shrink,
height: Length::Shrink,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
}
}
/// Sets the size of the [`Text`].
pub fn size(mut self, size: u16) -> Self {
self.size = Some(size);
self
}
/// Sets the [`Color`] of the [`Text`].
pub fn color<C: Into<Color>>(mut self, color: C) -> Self {
self.color = Some(color.into());
self
}
/// Sets the [`Font`] of the [`Text`].
///
/// [`Font`]: Renderer::Font
pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.font = font.into();
self
}
/// Sets the width of the [`Text`] boundaries.
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`Text`] boundaries.
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
/// Sets the [`HorizontalAlignment`] of the [`Text`].
pub fn horizontal_alignment(
mut self,
alignment: alignment::Horizontal,
) -> Self {
self.horizontal_alignment = alignment;
self
}
/// Sets the [`VerticalAlignment`] of the [`Text`].
pub fn vertical_alignment(
mut self,
alignment: alignment::Vertical,
) -> Self {
self.vertical_alignment = alignment;
self
}
}
impl<Message, Renderer> Widget<Message, Renderer> for Text<Renderer>
where
Renderer: text::Renderer,
{
fn tag(&self) -> any::TypeId {
any::TypeId::of::<()>()
}
fn state(&self) -> Box<dyn Any> {
Box::new(())
}
fn children(&self) -> &[Element<Message, Renderer>] {
&[]
}
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
self.height
}
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.width).height(self.height);
let size = self.size.unwrap_or(renderer.default_size());
let bounds = limits.max();
let (width, height) =
renderer.measure(&self.content, size, self.font.clone(), bounds);
let size = limits.resolve(Size::new(width, height));
layout::Node::new(size)
}
fn draw(
&self,
_tree: &Tree<Message, Renderer>,
renderer: &mut Renderer,
style: &renderer::Style,
layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
) {
iced_native::widget::text::draw(
renderer,
style,
layout,
&self.content,
self.font.clone(),
self.size,
self.color,
self.horizontal_alignment,
self.vertical_alignment,
);
}
fn hash_layout(&self, state: &mut Hasher) {
use std::hash::Hash;
struct Marker;
std::any::TypeId::of::<Marker>().hash(state);
self.content.hash(state);
self.size.hash(state);
self.width.hash(state);
self.height.hash(state);
}
}
impl<Message, Renderer> Into<Element<Message, Renderer>> for Text<Renderer>
where
Renderer: text::Renderer + 'static,
{
fn into(self) -> Element<Message, Renderer> {
Element::new(self)
}
}
impl<Message, Renderer> Into<Element<Message, Renderer>> for &'static str
where
Renderer: text::Renderer + 'static,
{
fn into(self) -> Element<Message, Renderer> {
Text::new(self).into()
}
}

58
pure/src/widget/tree.rs Normal file
View file

@ -0,0 +1,58 @@
use crate::widget::Element;
use std::any::Any;
use std::marker::PhantomData;
pub struct Tree<Message, Renderer> {
pub state: Box<dyn Any>,
pub children: Vec<Tree<Message, Renderer>>,
types_: PhantomData<(Message, Renderer)>,
}
impl<Message, Renderer> Tree<Message, Renderer> {
pub fn new(element: &Element<Message, Renderer>) -> Self {
Self {
state: element.as_widget().state(),
children: element
.as_widget()
.children()
.iter()
.map(Self::new)
.collect(),
types_: PhantomData,
}
}
pub fn diff(
&mut self,
current: &Element<Message, Renderer>,
new: &Element<Message, Renderer>,
) {
if current.as_widget().tag() == new.as_widget().tag() {
let current_children = current.as_widget().children();
let new_children = new.as_widget().children();
if current_children.len() > new_children.len() {
self.children.truncate(new_children.len());
}
for (child_state, (current, new)) in self
.children
.iter_mut()
.zip(current_children.iter().zip(new_children.iter()))
{
child_state.diff(current, new);
}
if current_children.len() < new_children.len() {
self.children.extend(
new_children[current_children.len()..]
.iter()
.map(Self::new),
);
}
} else {
*self = Self::new(new);
}
}
}