summaryrefslogtreecommitdiffstats
path: root/native/src
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-11-21 13:47:20 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-11-21 13:47:20 +0100
commit65eb218d3d7ba52b2869a586a1480eeb3c8f84e4 (patch)
tree644f27f40f2f4b8ee1abe7743aac426297503eea /native/src
parentd3553adf278e5b616fbd885f321faa83a4d24b56 (diff)
downloadiced-65eb218d3d7ba52b2869a586a1480eeb3c8f84e4.tar.gz
iced-65eb218d3d7ba52b2869a586a1480eeb3c8f84e4.tar.bz2
iced-65eb218d3d7ba52b2869a586a1480eeb3c8f84e4.zip
Move widgets from `core` to `native` and `web`
Also made fields private and improved `Renderer` traits.
Diffstat (limited to 'native/src')
-rw-r--r--native/src/element.rs57
-rw-r--r--native/src/lib.rs61
-rw-r--r--native/src/renderer.rs4
-rw-r--r--native/src/renderer/null.rs143
-rw-r--r--native/src/user_interface.rs11
-rw-r--r--native/src/widget.rs2
-rw-r--r--native/src/widget/button.rs166
-rw-r--r--native/src/widget/checkbox.rs128
-rw-r--r--native/src/widget/column.rs113
-rw-r--r--native/src/widget/container.rs92
-rw-r--r--native/src/widget/image.rs81
-rw-r--r--native/src/widget/radio.rs145
-rw-r--r--native/src/widget/row.rs115
-rw-r--r--native/src/widget/scrollable.rs217
-rw-r--r--native/src/widget/slider.rs109
-rw-r--r--native/src/widget/text.rs162
-rw-r--r--native/src/widget/text_input.rs229
17 files changed, 1572 insertions, 263 deletions
diff --git a/native/src/element.rs b/native/src/element.rs
index 23f069f1..cb8aaf54 100644
--- a/native/src/element.rs
+++ b/native/src/element.rs
@@ -17,14 +17,6 @@ pub struct Element<'a, Message, Renderer> {
pub(crate) widget: Box<dyn Widget<Message, Renderer> + 'a>,
}
-impl<'a, Message, Renderer> std::fmt::Debug for Element<'a, Message, Renderer> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("Element")
- .field("widget", &self.widget)
- .finish()
- }
-}
-
impl<'a, Message, Renderer> Element<'a, Message, Renderer>
where
Renderer: crate::Renderer,
@@ -127,37 +119,7 @@ where
/// # }
/// #
/// # mod iced_wgpu {
- /// # use iced_native::{
- /// # text, row, layout, Text, Size, Point, Rectangle, Layout, Row
- /// # };
- /// # pub struct Renderer;
- /// #
- /// # impl iced_native::Renderer for Renderer { type Output = (); }
- /// #
- /// # impl iced_native::row::Renderer for Renderer {
- /// # fn draw<Message>(
- /// # &mut self,
- /// # _column: &Row<'_, Message, Self>,
- /// # _layout: Layout<'_>,
- /// # _cursor_position: Point,
- /// # ) {}
- /// # }
- /// #
- /// # impl text::Renderer for Renderer {
- /// # fn layout(
- /// # &self,
- /// # _text: &Text,
- /// # _limits: &layout::Limits,
- /// # ) -> layout::Node {
- /// # layout::Node::new(Size::ZERO)
- /// # }
- /// #
- /// # fn draw(
- /// # &mut self,
- /// # _text: &Text,
- /// # _layout: Layout<'_>,
- /// # ) {}
- /// # }
+ /// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use counter::Counter;
@@ -273,12 +235,6 @@ struct Map<'a, A, B, Renderer> {
mapper: Box<dyn Fn(A) -> B>,
}
-impl<'a, A, B, Renderer> std::fmt::Debug for Map<'a, A, B, Renderer> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("Map").field("widget", &self.widget).finish()
- }
-}
-
impl<'a, A, B, Renderer> Map<'a, A, B, Renderer> {
pub fn new<F>(
widget: Box<dyn Widget<A, Renderer> + 'a>,
@@ -350,17 +306,6 @@ struct Explain<'a, Message, Renderer: crate::Renderer> {
color: Color,
}
-impl<'a, Message, Renderer> std::fmt::Debug for Explain<'a, Message, Renderer>
-where
- Renderer: crate::Renderer,
-{
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("Explain")
- .field("element", &self.element)
- .finish()
- }
-}
-
impl<'a, Message, Renderer> Explain<'a, Message, Renderer>
where
Renderer: crate::Renderer,
diff --git a/native/src/lib.rs b/native/src/lib.rs
index bd03ddcd..55331ba5 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -14,8 +14,8 @@
//! [repository]: https://github.com/hecrj/iced
//!
//! # Usage
-//! Inspired by [The Elm Architecture], Iced expects you to split user interfaces
-//! into four different concepts:
+//! Inspired by [The Elm Architecture], Iced expects you to split user
+//! interfaces into four different concepts:
//!
//! * __State__ — the state of your application
//! * __Messages__ — user interactions or meaningful events that you care
@@ -25,8 +25,8 @@
//! * __Update logic__ — a way to react to __messages__ and update your
//! __state__
//!
-//! We can build something to see how this works! Let's say we want a simple counter
-//! that can be incremented and decremented using two buttons.
+//! We can build something to see how this works! Let's say we want a simple
+//! counter that can be incremented and decremented using two buttons.
//!
//! We start by modelling the __state__ of our application:
//!
@@ -76,49 +76,7 @@
//! # }
//! #
//! # mod iced_wgpu {
-//! # use iced_native::{
-//! # button, text, layout, Button, Text, Point, Rectangle, Color, Layout, Size
-//! # };
-//! #
-//! # pub struct Renderer {}
-//! #
-//! # impl iced_native::Renderer for Renderer {
-//! # type Output = ();
-//! # }
-//! #
-//! # impl button::Renderer for Renderer {
-//! # fn layout<Message>(
-//! # &self,
-//! # _button: &Button<'_, Message, Self>,
-//! # _limits: &layout::Limits,
-//! # ) -> layout::Node {
-//! # layout::Node::new(Size::ZERO)
-//! # }
-//! #
-//! # fn draw<Message>(
-//! # &mut self,
-//! # _button: &Button<'_, Message, Self>,
-//! # _layout: Layout<'_>,
-//! # _cursor_position: Point,
-//! # ) {}
-//! # }
-//! #
-//! # impl text::Renderer for Renderer {
-//! # fn layout(
-//! # &self,
-//! # _text: &Text,
-//! # _limits: &layout::Limits,
-//! # ) -> layout::Node {
-//! # layout::Node::new(Size::ZERO)
-//! # }
-//! #
-//! # fn draw(
-//! # &mut self,
-//! # _text: &Text,
-//! # _layout: Layout<'_>,
-//! # ) {
-//! # }
-//! # }
+//! # pub use iced_native::renderer::Null as Renderer;
//! # }
//! use iced_native::{Button, Column, Text};
//! use iced_wgpu::Renderer; // Iced does not include a renderer! We need to bring our own!
@@ -183,8 +141,8 @@
//! }
//! ```
//!
-//! And that's everything! We just wrote a whole user interface. Iced is now able
-//! to:
+//! And that's everything! We just wrote a whole user interface. Iced is now
+//! able to:
//!
//! 1. Take the result of our __view logic__ and layout its widgets.
//! 1. Process events from our system and produce __messages__ for our
@@ -199,7 +157,7 @@
//! [examples]: https://github.com/hecrj/iced/tree/master/examples
//! [`UserInterface`]: struct.UserInterface.html
//#![deny(missing_docs)]
-#![deny(missing_debug_implementations)]
+//#![deny(missing_debug_implementations)]
#![deny(unused_results)]
#![deny(unsafe_code)]
#![deny(rust_2018_idioms)]
@@ -216,7 +174,8 @@ mod size;
mod user_interface;
pub use iced_core::{
- Align, Background, Color, Command, Font, Length, Point, Rectangle, Vector,
+ Align, Background, Color, Command, Font, HorizontalAlignment, Length,
+ Point, Rectangle, Vector, VerticalAlignment,
};
pub use element::Element;
diff --git a/native/src/renderer.rs b/native/src/renderer.rs
index 833de571..3e19be33 100644
--- a/native/src/renderer.rs
+++ b/native/src/renderer.rs
@@ -21,9 +21,13 @@
//! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html
mod debugger;
+#[cfg(debug_assertions)]
+mod null;
mod windowed;
pub use debugger::Debugger;
+#[cfg(debug_assertions)]
+pub use null::Null;
pub use windowed::{Target, Windowed};
use crate::{layout, Element};
diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs
new file mode 100644
index 00000000..8933b09b
--- /dev/null
+++ b/native/src/renderer/null.rs
@@ -0,0 +1,143 @@
+use crate::{
+ button, checkbox, column, radio, row, scrollable, text, text_input,
+ Background, Color, Element, Font, HorizontalAlignment, Layout, Point,
+ Rectangle, Renderer, Size, VerticalAlignment,
+};
+
+pub struct Null;
+
+impl Renderer for Null {
+ type Output = ();
+}
+
+impl column::Renderer for Null {
+ fn draw<Message>(
+ &mut self,
+ _content: &[Element<'_, Message, Self>],
+ _layout: Layout<'_>,
+ _cursor_position: Point,
+ ) {
+ }
+}
+
+impl row::Renderer for Null {
+ fn draw<Message>(
+ &mut self,
+ _content: &[Element<'_, Message, Self>],
+ _layout: Layout<'_>,
+ _cursor_position: Point,
+ ) {
+ }
+}
+
+impl text::Renderer for Null {
+ fn default_size(&self) -> u16 {
+ 20
+ }
+
+ fn measure(
+ &self,
+ _content: &str,
+ _size: u16,
+ _font: Font,
+ _bounds: Size,
+ ) -> (f32, f32) {
+ (0.0, 20.0)
+ }
+
+ fn draw(
+ &mut self,
+ _bounds: Rectangle,
+ _content: &str,
+ _size: u16,
+ _font: Font,
+ _color: Option<Color>,
+ _horizontal_alignment: HorizontalAlignment,
+ _vertical_alignment: VerticalAlignment,
+ ) {
+ }
+}
+
+impl scrollable::Renderer for Null {
+ fn is_mouse_over_scrollbar(
+ &self,
+ _bounds: Rectangle,
+ _content_bounds: Rectangle,
+ _cursor_position: Point,
+ ) -> bool {
+ false
+ }
+
+ fn draw(
+ &mut self,
+ _scrollable: &scrollable::State,
+ _bounds: Rectangle,
+ _content_bounds: Rectangle,
+ _is_mouse_over: bool,
+ _is_mouse_over_scrollbar: bool,
+ _offset: u32,
+ _content: Self::Output,
+ ) {
+ }
+}
+
+impl text_input::Renderer for Null {
+ fn default_size(&self) -> u16 {
+ 20
+ }
+
+ fn draw(
+ &mut self,
+ _bounds: Rectangle,
+ _text_bounds: Rectangle,
+ _cursor_position: Point,
+ _size: u16,
+ _placeholder: &str,
+ _value: &text_input::Value,
+ _state: &text_input::State,
+ ) -> Self::Output {
+ }
+}
+
+impl button::Renderer for Null {
+ fn draw(
+ &mut self,
+ _bounds: Rectangle,
+ _cursor_position: Point,
+ _is_pressed: bool,
+ _background: Option<Background>,
+ _border_radius: u16,
+ _content: Self::Output,
+ ) -> Self::Output {
+ }
+}
+
+impl radio::Renderer for Null {
+ fn default_size(&self) -> u32 {
+ 20
+ }
+
+ fn draw(
+ &mut self,
+ _bounds: Rectangle,
+ _is_selected: bool,
+ _is_mouse_over: bool,
+ _label: Self::Output,
+ ) {
+ }
+}
+
+impl checkbox::Renderer for Null {
+ fn default_size(&self) -> u32 {
+ 20
+ }
+
+ fn draw(
+ &mut self,
+ _bounds: Rectangle,
+ _is_checked: bool,
+ _is_mouse_over: bool,
+ _label: Self::Output,
+ ) {
+ }
+}
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index f031b090..f29cbcc5 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -10,7 +10,6 @@ use std::hash::Hasher;
/// charge of using this type in your system in any way you want.
///
/// [`Layout`]: struct.Layout.html
-#[derive(Debug)]
pub struct UserInterface<'a, Message, Renderer> {
hash: u64,
root: Element<'a, Message, Renderer>,
@@ -52,7 +51,7 @@ where
/// # impl iced_native::column::Renderer for Renderer {
/// # fn draw<Message>(
/// # &mut self,
- /// # _column: &iced_native::Column<'_, Message, Self>,
+ /// # _children: &[iced_native::Element<'_, Message, Self>],
/// # _layout: iced_native::Layout<'_>,
/// # _cursor_position: iced_native::Point,
/// # ) -> Self::Output {
@@ -133,8 +132,8 @@ where
/// [`Event`]: enum.Event.html
///
/// # Example
- /// Let's allow our [counter](index.html#usage) to change state by completing
- /// [the previous example](#example):
+ /// Let's allow our [counter](index.html#usage) to change state by
+ /// completing [the previous example](#example):
///
/// ```no_run
/// use iced_native::{UserInterface, Cache};
@@ -152,7 +151,7 @@ where
/// # impl iced_native::column::Renderer for Renderer {
/// # fn draw<Message>(
/// # &mut self,
- /// # _column: &iced_native::Column<'_, Message, Self>,
+ /// # _children: &[iced_native::Element<'_, Message, Self>],
/// # _layout: iced_native::Layout<'_>,
/// # _cursor_position: iced_native::Point,
/// # ) -> Self::Output {
@@ -252,7 +251,7 @@ where
/// # impl iced_native::column::Renderer for Renderer {
/// # fn draw<Message>(
/// # &mut self,
- /// # _column: &iced_native::Column<'_, Message, Self>,
+ /// # _children: &[iced_native::Element<'_, Message, Self>],
/// # _layout: iced_native::Layout<'_>,
/// # _cursor_position: iced_native::Point,
/// # ) -> Self::Output {
diff --git a/native/src/widget.rs b/native/src/widget.rs
index 9010b06f..9dd48697 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -65,7 +65,7 @@ use crate::{layout, Event, Hasher, Layout, Length, Point};
///
/// [`Widget`]: trait.Widget.html
/// [`Element`]: ../struct.Element.html
-pub trait Widget<Message, Renderer>: std::fmt::Debug
+pub trait Widget<Message, Renderer>
where
Renderer: crate::Renderer,
{
diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs
index 15beaeba..f2cce3d2 100644
--- a/native/src/widget/button.rs
+++ b/native/src/widget/button.rs
@@ -1,32 +1,144 @@
//! Allow your users to perform actions by pressing a button.
//!
-//! A [`Button`] has some local [`State`] and a [`Class`].
+//! A [`Button`] has some local [`State`].
//!
//! [`Button`]: struct.Button.html
//! [`State`]: struct.State.html
-//! [`Class`]: enum.Class.html
-use crate::input::{mouse, ButtonState};
-use crate::{layout, Element, Event, Hasher, Layout, Point, Widget};
+use crate::{
+ input::{mouse, ButtonState},
+ layout, Background, Element, Event, Hasher, Layout, Length, Point,
+ Rectangle, Widget,
+};
use std::hash::Hash;
-pub use iced_core::button::State;
+/// A generic widget that produces a message when clicked.
+pub struct Button<'a, Message, Renderer> {
+ state: &'a mut State,
+ content: Element<'a, Message, Renderer>,
+ on_press: Option<Message>,
+ width: Length,
+ min_width: u32,
+ padding: u16,
+ background: Option<Background>,
+ border_radius: u16,
+}
+
+impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
+ /// Creates a new [`Button`] with some local [`State`] and the given
+ /// content.
+ ///
+ /// [`Button`]: struct.Button.html
+ /// [`State`]: struct.State.html
+ pub fn new<E>(state: &'a mut State, content: E) -> Self
+ where
+ E: Into<Element<'a, Message, Renderer>>,
+ {
+ Button {
+ state,
+ content: content.into(),
+ on_press: None,
+ width: Length::Shrink,
+ min_width: 0,
+ padding: 0,
+ background: None,
+ border_radius: 0,
+ }
+ }
+
+ /// Sets the width of the [`Button`].
+ ///
+ /// [`Button`]: struct.Button.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the minimum width of the [`Button`].
+ ///
+ /// [`Button`]: struct.Button.html
+ pub fn min_width(mut self, min_width: u32) -> Self {
+ self.min_width = min_width;
+ self
+ }
+
+ /// Sets the padding of the [`Button`].
+ ///
+ /// [`Button`]: struct.Button.html
+ pub fn padding(mut self, padding: u16) -> Self {
+ self.padding = padding;
+ self
+ }
+
+ /// Sets the [`Background`] of the [`Button`].
+ ///
+ /// [`Button`]: struct.Button.html
+ /// [`Background`]: ../../struct.Background.html
+ pub fn background(mut self, background: Background) -> Self {
+ self.background = Some(background);
+ self
+ }
+
+ /// Sets the border radius of the [`Button`].
+ ///
+ /// [`Button`]: struct.Button.html
+ pub fn border_radius(mut self, border_radius: u16) -> Self {
+ self.border_radius = border_radius;
+ self
+ }
-pub type Button<'a, Message, Renderer> =
- iced_core::Button<'a, Message, Element<'a, Message, Renderer>>;
+ /// Sets the message that will be produced when the [`Button`] is pressed.
+ ///
+ /// [`Button`]: struct.Button.html
+ pub fn on_press(mut self, msg: Message) -> Self {
+ self.on_press = Some(msg);
+ self
+ }
+}
+
+/// The local state of a [`Button`].
+///
+/// [`Button`]: struct.Button.html
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub struct State {
+ is_pressed: bool,
+}
+
+impl State {
+ /// Creates a new [`State`].
+ ///
+ /// [`State`]: struct.State.html
+ pub fn new() -> State {
+ State::default()
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Button<'a, Message, Renderer>
where
Renderer: self::Renderer,
- Message: Clone + std::fmt::Debug,
+ Message: Clone,
{
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- renderer.layout(&self, limits)
+ let padding = f32::from(self.padding);
+ let limits = limits
+ .min_width(self.min_width)
+ .width(self.width)
+ .height(Length::Shrink)
+ .pad(padding);
+
+ let mut content = self.content.layout(renderer, &limits);
+
+ content.bounds.x = padding;
+ content.bounds.y = padding;
+
+ let size = limits.resolve(content.size()).pad(padding);
+
+ layout::Node::with_children(size, vec![content])
}
fn on_event(
@@ -73,7 +185,20 @@ where
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self, layout, cursor_position)
+ let content = self.content.draw(
+ renderer,
+ layout.children().next().unwrap(),
+ cursor_position,
+ );
+
+ renderer.draw(
+ layout.bounds(),
+ cursor_position,
+ self.state.is_pressed,
+ self.background,
+ self.border_radius,
+ content,
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -90,24 +215,17 @@ where
/// [`Button`]: struct.Button.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized {
- /// Creates a [`Node`] for the provided [`Button`].
- ///
- /// [`Node`]: ../../struct.Node.html
- /// [`Button`]: struct.Button.html
- fn layout<Message>(
- &self,
- button: &Button<'_, Message, Self>,
- limits: &layout::Limits,
- ) -> layout::Node;
-
/// Draws a [`Button`].
///
/// [`Button`]: struct.Button.html
- fn draw<Message>(
+ fn draw(
&mut self,
- button: &Button<'_, Message, Self>,
- layout: Layout<'_>,
+ bounds: Rectangle,
cursor_position: Point,
+ is_pressed: bool,
+ background: Option<Background>,
+ border_radius: u16,
+ content: Self::Output,
) -> Self::Output;
}
@@ -115,7 +233,7 @@ impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'static + self::Renderer,
- Message: 'static + Clone + std::fmt::Debug,
+ Message: 'static + Clone,
{
fn from(
button: Button<'a, Message, Renderer>,
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index 3f0f8dda..47512089 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -1,14 +1,72 @@
//! Show toggle controls using checkboxes.
use std::hash::Hash;
-use crate::input::{mouse, ButtonState};
-use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
+use crate::{
+ input::{mouse, ButtonState},
+ layout, row, text, Align, Color, Element, Event, Font, Hasher,
+ HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
+ VerticalAlignment, Widget,
+};
-pub use iced_core::Checkbox;
+/// A box that can be checked.
+///
+/// # Example
+///
+/// ```
+/// # use iced_native::Checkbox;
+///
+/// pub enum Message {
+/// CheckboxToggled(bool),
+/// }
+///
+/// let is_checked = true;
+///
+/// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled);
+/// ```
+///
+/// ![Checkbox drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/checkbox.png?raw=true)
+pub struct Checkbox<Message> {
+ is_checked: bool,
+ on_toggle: Box<dyn Fn(bool) -> Message>,
+ label: String,
+ label_color: Option<Color>,
+}
+
+impl<Message> Checkbox<Message> {
+ /// Creates a new [`Checkbox`].
+ ///
+ /// It expects:
+ /// * a boolean describing whether the [`Checkbox`] is checked or not
+ /// * the label of the [`Checkbox`]
+ /// * a function that will be called when the [`Checkbox`] is toggled. It
+ /// will receive the new state of the [`Checkbox`] and must produce a
+ /// `Message`.
+ ///
+ /// [`Checkbox`]: struct.Checkbox.html
+ pub fn new<F>(is_checked: bool, label: &str, f: F) -> Self
+ where
+ F: 'static + Fn(bool) -> Message,
+ {
+ Checkbox {
+ is_checked,
+ on_toggle: Box::new(f),
+ label: String::from(label),
+ label_color: None,
+ }
+ }
+
+ /// Sets the color of the label of the [`Checkbox`].
+ ///
+ /// [`Checkbox`]: struct.Checkbox.html
+ pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self {
+ self.label_color = Some(color.into());
+ self
+ }
+}
impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>
where
- Renderer: self::Renderer,
+ Renderer: self::Renderer + text::Renderer + row::Renderer,
{
fn width(&self) -> Length {
Length::Fill
@@ -19,7 +77,18 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- renderer.layout(&self, limits)
+ let size = self::Renderer::default_size(renderer);
+
+ Row::<(), Renderer>::new()
+ .spacing(15)
+ .align_items(Align::Center)
+ .push(
+ Row::new()
+ .width(Length::Units(size as u16))
+ .height(Length::Units(size as u16)),
+ )
+ .push(Text::new(&self.label))
+ .layout(renderer, limits)
}
fn on_event(
@@ -51,7 +120,33 @@ where
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self, layout, cursor_position)
+ let bounds = layout.bounds();
+ let mut children = layout.children();
+
+ let checkbox_layout = children.next().unwrap();
+ let label_layout = children.next().unwrap();
+ let checkbox_bounds = checkbox_layout.bounds();
+
+ let label = text::Renderer::draw(
+ renderer,
+ label_layout.bounds(),
+ &self.label,
+ text::Renderer::default_size(renderer),
+ Font::Default,
+ self.label_color,
+ HorizontalAlignment::Left,
+ VerticalAlignment::Center,
+ );
+
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ self::Renderer::draw(
+ renderer,
+ checkbox_bounds,
+ self.is_checked,
+ is_mouse_over,
+ label,
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -67,15 +162,7 @@ where
/// [`Checkbox`]: struct.Checkbox.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
- /// Creates a [`Node`] for the provided [`Checkbox`].
- ///
- /// [`Node`]: ../../struct.Node.html
- /// [`Checkbox`]: struct.Checkbox.html
- fn layout<Message>(
- &self,
- checkbox: &Checkbox<Message>,
- limits: &layout::Limits,
- ) -> layout::Node;
+ fn default_size(&self) -> u32;
/// Draws a [`Checkbox`].
///
@@ -86,18 +173,19 @@ pub trait Renderer: crate::Renderer {
/// * whether the [`Checkbox`] is checked or not
///
/// [`Checkbox`]: struct.Checkbox.html
- fn draw<Message>(
+ fn draw(
&mut self,
- checkbox: &Checkbox<Message>,
- layout: Layout<'_>,
- cursor_position: Point,
+ bounds: Rectangle,
+ is_checked: bool,
+ is_mouse_over: bool,
+ label: Self::Output,
) -> Self::Output;
}
impl<'a, Message, Renderer> From<Checkbox<Message>>
for Element<'a, Message, Renderer>
where
- Renderer: self::Renderer,
+ Renderer: self::Renderer + text::Renderer + row::Renderer,
Message: 'static,
{
fn from(checkbox: Checkbox<Message>) -> Element<'a, Message, Renderer> {
diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs
index 7e7156a0..8d98795c 100644
--- a/native/src/widget/column.rs
+++ b/native/src/widget/column.rs
@@ -1,10 +1,113 @@
use std::hash::Hash;
-use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
+use crate::{
+ layout, Align, Element, Event, Hasher, Layout, Length, Point, Widget,
+};
+
+use std::u32;
/// A container that distributes its contents vertically.
-pub type Column<'a, Message, Renderer> =
- iced_core::Column<Element<'a, Message, Renderer>>;
+///
+/// A [`Column`] will try to fill the horizontal space of its container.
+///
+/// [`Column`]: struct.Column.html
+pub struct Column<'a, Message, Renderer> {
+ spacing: u16,
+ padding: u16,
+ width: Length,
+ height: Length,
+ max_width: u32,
+ max_height: u32,
+ align_items: Align,
+ children: Vec<Element<'a, Message, Renderer>>,
+}
+
+impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
+ /// Creates an empty [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn new() -> Self {
+ Column {
+ spacing: 0,
+ padding: 0,
+ width: Length::Fill,
+ height: Length::Shrink,
+ max_width: u32::MAX,
+ max_height: u32::MAX,
+ align_items: Align::Start,
+ children: Vec::new(),
+ }
+ }
+
+ /// Sets the vertical spacing _between_ elements.
+ ///
+ /// Custom margins per element do not exist in Iced. You should use this
+ /// method instead! While less flexible, it helps you keep spacing between
+ /// elements consistent.
+ pub fn spacing(mut self, units: u16) -> Self {
+ self.spacing = units;
+ self
+ }
+
+ /// Sets the padding of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn padding(mut self, units: u16) -> Self {
+ self.padding = units;
+ self
+ }
+
+ /// Sets the width of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the height of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the maximum width of the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn max_width(mut self, max_width: u32) -> Self {
+ self.max_width = max_width;
+ self
+ }
+
+ /// Sets the maximum height of the [`Column`] in pixels.
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn max_height(mut self, max_height: u32) -> Self {
+ self.max_height = max_height;
+ self
+ }
+
+ /// Sets the horizontal alignment of the contents of the [`Column`] .
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn align_items(mut self, align: Align) -> Self {
+ self.align_items = align;
+ self
+ }
+
+ /// Adds an element to the [`Column`].
+ ///
+ /// [`Column`]: struct.Column.html
+ pub fn push<E>(mut self, child: E) -> Self
+ where
+ E: Into<Element<'a, Message, Renderer>>,
+ {
+ self.children.push(child.into());
+ self
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Column<'a, Message, Renderer>
@@ -64,7 +167,7 @@ where
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self, layout, cursor_position)
+ renderer.draw(&self.children, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -85,7 +188,7 @@ where
pub trait Renderer: crate::Renderer + Sized {
fn draw<Message>(
&mut self,
- row: &Column<'_, Message, Self>,
+ content: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
) -> Self::Output;
diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs
index fd7a5ca5..54117eb1 100644
--- a/native/src/widget/container.rs
+++ b/native/src/widget/container.rs
@@ -1,10 +1,94 @@
use std::hash::Hash;
-use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
+use crate::{
+ layout, Align, Element, Event, Hasher, Layout, Length, Point, Widget,
+};
-/// A container that distributes its contents vertically.
-pub type Container<'a, Message, Renderer> =
- iced_core::Container<Element<'a, Message, Renderer>>;
+use std::u32;
+
+/// An element decorating some content.
+///
+/// It is normally used for alignment purposes.
+#[allow(missing_docs)]
+pub struct Container<'a, Message, Renderer> {
+ width: Length,
+ height: Length,
+ max_width: u32,
+ max_height: u32,
+ horizontal_alignment: Align,
+ vertical_alignment: Align,
+ content: Element<'a, Message, Renderer>,
+}
+
+impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
+ /// Creates an empty [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn new<T>(content: T) -> Self
+ where
+ T: Into<Element<'a, Message, Renderer>>,
+ {
+ Container {
+ width: Length::Shrink,
+ height: Length::Shrink,
+ max_width: u32::MAX,
+ max_height: u32::MAX,
+ horizontal_alignment: Align::Start,
+ vertical_alignment: Align::Start,
+ content: content.into(),
+ }
+ }
+
+ /// Sets the width of the [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the height of the [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the maximum width of the [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn max_width(mut self, max_width: u32) -> Self {
+ self.max_width = max_width;
+ self
+ }
+
+ /// Sets the maximum height of the [`Container`] in pixels.
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn max_height(mut self, max_height: u32) -> Self {
+ self.max_height = max_height;
+ self
+ }
+
+ /// Centers the contents in the horizontal axis of the [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn center_x(mut self) -> Self {
+ self.horizontal_alignment = Align::Center;
+
+ self
+ }
+
+ /// Centers the contents in the vertical axis of the [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn center_y(mut self) -> Self {
+ self.vertical_alignment = Align::Center;
+
+ self
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Container<'a, Message, Renderer>
diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs
index b2541b87..8420ca76 100644
--- a/native/src/widget/image.rs
+++ b/native/src/widget/image.rs
@@ -1,10 +1,58 @@
//! Display images in your user interface.
-use crate::{layout, Element, Hasher, Layout, Point, Widget};
+use crate::{layout, Element, Hasher, Layout, Length, Point, Size, Widget};
use std::hash::Hash;
-pub use iced_core::Image;
+/// A frame that displays an image while keeping aspect ratio.
+///
+/// # Example
+///
+/// ```
+/// # use iced_native::Image;
+///
+/// let image = Image::new("resources/ferris.png");
+/// ```
+#[derive(Debug)]
+pub struct Image {
+ /// The image path
+ pub path: String,
+
+ /// The width of the image
+ pub width: Length,
+
+ /// The height of the image
+ pub height: Length,
+}
+
+impl Image {
+ /// Creates a new [`Image`] with the given path.
+ ///
+ /// [`Image`]: struct.Image.html
+ pub fn new<T: Into<String>>(path: T) -> Self {
+ Image {
+ path: path.into(),
+ width: Length::Shrink,
+ height: Length::Shrink,
+ }
+ }
+
+ /// Sets the width of the [`Image`] boundaries.
+ ///
+ /// [`Image`]: struct.Image.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the height of the [`Image`] boundaries.
+ ///
+ /// [`Image`]: struct.Image.html
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+}
impl<Message, Renderer> Widget<Message, Renderer> for Image
where
@@ -15,7 +63,26 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- renderer.layout(&self, limits)
+ let (width, height) = renderer.dimensions(&self.path);
+
+ let aspect_ratio = width as f32 / height as f32;
+
+ // TODO: Deal with additional cases
+ let (width, height) = match (self.width, self.height) {
+ (Length::Units(width), _) => (
+ self.width,
+ Length::Units((width as f32 / aspect_ratio).round() as u16),
+ ),
+ (_, _) => {
+ (Length::Units(width as u16), Length::Units(height as u16))
+ }
+ };
+
+ let mut size = limits.width(width).height(height).resolve(Size::ZERO);
+
+ size.height = size.width / aspect_ratio;
+
+ layout::Node::new(size)
}
fn draw(
@@ -41,13 +108,7 @@ where
/// [`Image`]: struct.Image.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
- /// Creates a [`Node`] for the provided [`Image`].
- ///
- /// You should probably keep the original aspect ratio, if possible.
- ///
- /// [`Node`]: ../../struct.Node.html
- /// [`Image`]: struct.Image.html
- fn layout(&self, image: &Image, limits: &layout::Limits) -> layout::Node;
+ fn dimensions(&self, path: &str) -> (u32, u32);
/// Draws an [`Image`].
///
diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs
index b68919e5..a3a091b1 100644
--- a/native/src/widget/radio.rs
+++ b/native/src/widget/radio.rs
@@ -1,15 +1,82 @@
//! Create choices using radio buttons.
-use crate::input::{mouse, ButtonState};
-use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
+use crate::{
+ input::{mouse, ButtonState},
+ layout, row, text, Align, Color, Element, Event, Font, Hasher,
+ HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
+ VerticalAlignment, Widget,
+};
use std::hash::Hash;
-pub use iced_core::Radio;
+/// A circular button representing a choice.
+///
+/// # Example
+/// ```
+/// # use iced_native::Radio;
+///
+/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+/// pub enum Choice {
+/// A,
+/// B,
+/// }
+///
+/// #[derive(Debug, Clone, Copy)]
+/// pub enum Message {
+/// RadioSelected(Choice),
+/// }
+///
+/// let selected_choice = Some(Choice::A);
+///
+/// Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected);
+///
+/// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected);
+/// ```
+///
+/// ![Radio buttons drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/radio.png?raw=true)
+pub struct Radio<Message> {
+ is_selected: bool,
+ on_click: Message,
+ label: String,
+ label_color: Option<Color>,
+}
+
+impl<Message> Radio<Message> {
+ /// Creates a new [`Radio`] button.
+ ///
+ /// It expects:
+ /// * the value related to the [`Radio`] button
+ /// * the label of the [`Radio`] button
+ /// * the current selected value
+ /// * a function that will be called when the [`Radio`] is selected. It
+ /// receives the value of the radio and must produce a `Message`.
+ ///
+ /// [`Radio`]: struct.Radio.html
+ pub fn new<F, V>(value: V, label: &str, selected: Option<V>, f: F) -> Self
+ where
+ V: Eq + Copy,
+ F: 'static + Fn(V) -> Message,
+ {
+ Radio {
+ is_selected: Some(value) == selected,
+ on_click: f(value),
+ label: String::from(label),
+ label_color: None,
+ }
+ }
+
+ /// Sets the `Color` of the label of the [`Radio`].
+ ///
+ /// [`Radio`]: struct.Radio.html
+ pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self {
+ self.label_color = Some(color.into());
+ self
+ }
+}
impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message>
where
- Renderer: self::Renderer,
- Message: Clone + std::fmt::Debug,
+ Renderer: self::Renderer + text::Renderer + row::Renderer,
+ Message: Clone,
{
fn width(&self) -> Length {
Length::Fill
@@ -20,7 +87,18 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- renderer.layout(&self, limits)
+ let size = self::Renderer::default_size(renderer);
+
+ Row::<(), Renderer>::new()
+ .spacing(15)
+ .align_items(Align::Center)
+ .push(
+ Row::new()
+ .width(Length::Units(size as u16))
+ .height(Length::Units(size as u16)),
+ )
+ .push(Text::new(&self.label))
+ .layout(renderer, limits)
}
fn on_event(
@@ -50,7 +128,33 @@ where
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self, layout, cursor_position)
+ let bounds = layout.bounds();
+ let mut children = layout.children();
+
+ let radio_layout = children.next().unwrap();
+ let label_layout = children.next().unwrap();
+ let radio_bounds = radio_layout.bounds();
+
+ let label = text::Renderer::draw(
+ renderer,
+ label_layout.bounds(),
+ &self.label,
+ text::Renderer::default_size(renderer),
+ Font::Default,
+ self.label_color,
+ HorizontalAlignment::Left,
+ VerticalAlignment::Center,
+ );
+
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ self::Renderer::draw(
+ renderer,
+ radio_bounds,
+ self.is_selected,
+ is_mouse_over,
+ label,
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -66,15 +170,7 @@ where
/// [`Radio`]: struct.Radio.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
- /// Creates a [`Node`] for the provided [`Radio`].
- ///
- /// [`Node`]: ../../struct.Node.html
- /// [`Radio`]: struct.Radio.html
- fn layout<Message>(
- &self,
- radio: &Radio<Message>,
- limits: &layout::Limits,
- ) -> layout::Node;
+ fn default_size(&self) -> u32;
/// Draws a [`Radio`] button.
///
@@ -85,21 +181,22 @@ pub trait Renderer: crate::Renderer {
/// * whether the [`Radio`] is selected or not
///
/// [`Radio`]: struct.Radio.html
- fn draw<Message>(
+ fn draw(
&mut self,
- radio: &Radio<Message>,
- layout: Layout<'_>,
- cursor_position: Point,
+ bounds: Rectangle,
+ is_selected: bool,
+ is_mouse_over: bool,
+ label: Self::Output,
) -> Self::Output;
}
impl<'a, Message, Renderer> From<Radio<Message>>
for Element<'a, Message, Renderer>
where
- Renderer: self::Renderer,
- Message: 'static + Clone + std::fmt::Debug,
+ Renderer: self::Renderer + row::Renderer + text::Renderer,
+ Message: 'static + Clone,
{
- fn from(checkbox: Radio<Message>) -> Element<'a, Message, Renderer> {
- Element::new(checkbox)
+ fn from(radio: Radio<Message>) -> Element<'a, Message, Renderer> {
+ Element::new(radio)
}
}
diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs
index 132479fd..e63d8d20 100644
--- a/native/src/widget/row.rs
+++ b/native/src/widget/row.rs
@@ -1,10 +1,115 @@
use std::hash::Hash;
-use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
+use crate::{
+ layout, Align, Element, Event, Hasher, Layout, Length, Point, Widget,
+};
+
+use std::u32;
/// A container that distributes its contents horizontally.
-pub type Row<'a, Message, Renderer> =
- iced_core::Row<Element<'a, Message, Renderer>>;
+///
+/// A [`Row`] will try to fill the horizontal space of its container.
+///
+/// [`Row`]: struct.Row.html
+#[allow(missing_docs)]
+pub struct Row<'a, Message, Renderer> {
+ spacing: u16,
+ padding: u16,
+ width: Length,
+ height: Length,
+ max_width: u32,
+ max_height: u32,
+ align_items: Align,
+ children: Vec<Element<'a, Message, Renderer>>,
+}
+
+impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
+ /// Creates an empty [`Row`].
+ ///
+ /// [`Row`]: struct.Row.html
+ pub fn new() -> Self {
+ Row {
+ spacing: 0,
+ padding: 0,
+ width: Length::Fill,
+ height: Length::Shrink,
+ max_width: u32::MAX,
+ max_height: u32::MAX,
+ align_items: Align::Start,
+ children: Vec::new(),
+ }
+ }
+
+ /// Sets the horizontal spacing _between_ elements.
+ ///
+ /// Custom margins per element do not exist in Iced. You should use this
+ /// method instead! While less flexible, it helps you keep spacing between
+ /// elements consistent.
+ pub fn spacing(mut self, units: u16) -> Self {
+ self.spacing = units;
+ self
+ }
+
+ /// Sets the padding of the [`Row`].
+ ///
+ /// [`Row`]: struct.Row.html
+ pub fn padding(mut self, units: u16) -> Self {
+ self.padding = units;
+ self
+ }
+
+ /// Sets the width of the [`Row`].
+ ///
+ /// [`Row`]: struct.Row.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the height of the [`Row`].
+ ///
+ /// [`Row`]: struct.Row.html
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the maximum width of the [`Row`].
+ ///
+ /// [`Row`]: struct.Row.html
+ pub fn max_width(mut self, max_width: u32) -> Self {
+ self.max_width = max_width;
+ self
+ }
+
+ /// Sets the maximum height of the [`Row`].
+ ///
+ /// [`Row`]: struct.Row.html
+ pub fn max_height(mut self, max_height: u32) -> Self {
+ self.max_height = max_height;
+ self
+ }
+
+ /// Sets the vertical alignment of the contents of the [`Row`] .
+ ///
+ /// [`Row`]: struct.Row.html
+ pub fn align_items(mut self, align: Align) -> Self {
+ self.align_items = align;
+ self
+ }
+
+ /// Adds an [`Element`] to the [`Row`].
+ ///
+ /// [`Element`]: ../struct.Element.html
+ /// [`Row`]: struct.Row.html
+ pub fn push<E>(mut self, child: E) -> Self
+ where
+ E: Into<Element<'a, Message, Renderer>>,
+ {
+ self.children.push(child.into());
+ self
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Row<'a, Message, Renderer>
@@ -64,7 +169,7 @@ where
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self, layout, cursor_position)
+ renderer.draw(&self.children, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -86,7 +191,7 @@ where
pub trait Renderer: crate::Renderer + Sized {
fn draw<Message>(
&mut self,
- row: &Row<'_, Message, Self>,
+ children: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
) -> Self::Output;
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 091dac47..d4568412 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -1,20 +1,104 @@
use crate::{
column,
input::{mouse, ButtonState},
- layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
- Widget,
+ layout, Align, Column, Element, Event, Hasher, Layout, Length, Point,
+ Rectangle, Size, Widget,
};
-pub use iced_core::scrollable::State;
+use std::{f32, hash::Hash, u32};
-use std::f32;
-use std::hash::Hash;
+/// A widget that can vertically display an infinite amount of content with a
+/// scrollbar.
+pub struct Scrollable<'a, Message, Renderer> {
+ state: &'a mut State,
+ height: Length,
+ max_height: u32,
+ content: Column<'a, Message, Renderer>,
+}
-/// A scrollable [`Column`].
-///
-/// [`Column`]: ../column/struct.Column.html
-pub type Scrollable<'a, Message, Renderer> =
- iced_core::Scrollable<'a, Element<'a, Message, Renderer>>;
+impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
+ /// Creates a new [`Scrollable`] with the given [`State`].
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ /// [`State`]: struct.State.html
+ pub fn new(state: &'a mut State) -> Self {
+ Scrollable {
+ state,
+ height: Length::Shrink,
+ max_height: u32::MAX,
+ content: Column::new(),
+ }
+ }
+
+ /// Sets the vertical spacing _between_ elements.
+ ///
+ /// Custom margins per element do not exist in Iced. You should use this
+ /// method instead! While less flexible, it helps you keep spacing between
+ /// elements consistent.
+ pub fn spacing(mut self, units: u16) -> Self {
+ self.content = self.content.spacing(units);
+ self
+ }
+
+ /// Sets the padding of the [`Scrollable`].
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn padding(mut self, units: u16) -> Self {
+ self.content = self.content.padding(units);
+ self
+ }
+
+ /// Sets the width of the [`Scrollable`].
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.content = self.content.width(width);
+ self
+ }
+
+ /// Sets the height of the [`Scrollable`].
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the maximum width of the [`Scrollable`].
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn max_width(mut self, max_width: u32) -> Self {
+ self.content = self.content.max_width(max_width);
+ self
+ }
+
+ /// Sets the maximum height of the [`Scrollable`] in pixels.
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn max_height(mut self, max_height: u32) -> Self {
+ self.max_height = max_height;
+ self
+ }
+
+ /// Sets the horizontal alignment of the contents of the [`Scrollable`] .
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn align_items(mut self, align_items: Align) -> Self {
+ self.content = self.content.align_items(align_items);
+ self
+ }
+
+ /// Adds an element to the [`Scrollable`].
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn push<E>(mut self, child: E) -> Self
+ where
+ E: Into<Element<'a, Message, Renderer>>,
+ {
+ self.content = self.content.push(child);
+ self
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Scrollable<'a, Message, Renderer>
@@ -153,13 +237,35 @@ where
) -> Renderer::Output {
let bounds = layout.bounds();
let content_layout = layout.children().next().unwrap();
+ let content_bounds = content_layout.bounds();
+ let offset = self.state.offset(bounds, content_bounds);
+
+ let is_mouse_over = bounds.contains(cursor_position);
+ let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar(
+ bounds,
+ content_bounds,
+ cursor_position,
+ );
+
+ let content = {
+ let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
+ Point::new(cursor_position.x, cursor_position.y + offset as f32)
+ } else {
+ Point::new(cursor_position.x, -1.0)
+ };
+
+ self.content.draw(renderer, content_layout, cursor_position)
+ };
self::Renderer::draw(
renderer,
- &self,
+ &self.state,
bounds,
- content_layout,
- cursor_position,
+ content_layout.bounds(),
+ is_mouse_over,
+ is_mouse_over_scrollbar,
+ offset,
+ content,
)
}
@@ -173,6 +279,80 @@ where
}
}
+/// The local state of a [`Scrollable`].
+///
+/// [`Scrollable`]: struct.Scrollable.html
+#[derive(Debug, Clone, Copy, Default)]
+pub struct State {
+ scrollbar_grabbed_at: Option<Point>,
+ offset: u32,
+}
+
+impl State {
+ /// Creates a new [`State`] with the scrollbar located at the top.
+ ///
+ /// [`State`]: struct.State.html
+ pub fn new() -> Self {
+ State::default()
+ }
+
+ /// Apply a scrolling offset to the current [`State`], given the bounds of
+ /// the [`Scrollable`] and its contents.
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ /// [`State`]: struct.State.html
+ pub fn scroll(
+ &mut self,
+ delta_y: f32,
+ bounds: Rectangle,
+ content_bounds: Rectangle,
+ ) {
+ if bounds.height >= content_bounds.height {
+ return;
+ }
+
+ self.offset = (self.offset as i32 - delta_y.round() as i32)
+ .max(0)
+ .min((content_bounds.height - bounds.height) as i32)
+ as u32;
+ }
+
+ /// Moves the scroll position to a relative amount, given the bounds of
+ /// the [`Scrollable`] and its contents.
+ ///
+ /// `0` represents scrollbar at the top, while `1` represents scrollbar at
+ /// the bottom.
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ /// [`State`]: struct.State.html
+ pub fn scroll_to(
+ &mut self,
+ percentage: f32,
+ bounds: Rectangle,
+ content_bounds: Rectangle,
+ ) {
+ self.offset = ((content_bounds.height - bounds.height) * percentage)
+ .max(0.0) as u32;
+ }
+
+ /// Returns the current scrolling offset of the [`State`], given the bounds
+ /// of the [`Scrollable`] and its contents.
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ /// [`State`]: struct.State.html
+ pub fn offset(&self, bounds: Rectangle, content_bounds: Rectangle) -> u32 {
+ let hidden_content =
+ (content_bounds.height - bounds.height).max(0.0).round() as u32;
+
+ self.offset.min(hidden_content)
+ }
+
+ /// Returns whether the scrollbar is currently grabbed or not.
+ pub fn is_scrollbar_grabbed(&self) -> bool {
+ self.scrollbar_grabbed_at.is_some()
+ }
+}
+
pub trait Renderer: crate::Renderer + Sized {
fn is_mouse_over_scrollbar(
&self,
@@ -181,12 +361,15 @@ pub trait Renderer: crate::Renderer + Sized {
cursor_position: Point,
) -> bool;
- fn draw<Message>(
+ fn draw(
&mut self,
- scrollable: &Scrollable<'_, Message, Self>,
+ scrollable: &State,
bounds: Rectangle,
- content_layout: Layout<'_>,
- cursor_position: Point,
+ content_bounds: Rectangle,
+ is_mouse_over: bool,
+ is_mouse_over_scrollbar: bool,
+ offset: u32,
+ content: Self::Output,
) -> Self::Output;
}
diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs
index 3a998c40..f91d3ac5 100644
--- a/native/src/widget/slider.rs
+++ b/native/src/widget/slider.rs
@@ -4,12 +4,79 @@
//!
//! [`Slider`]: struct.Slider.html
//! [`State`]: struct.State.html
-use std::hash::Hash;
-use crate::input::{mouse, ButtonState};
-use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
+use crate::{
+ input::{mouse, ButtonState},
+ layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
+ Widget,
+};
-pub use iced_core::slider::*;
+use std::{hash::Hash, ops::RangeInclusive};
+
+pub struct Slider<'a, Message> {
+ state: &'a mut State,
+ range: RangeInclusive<f32>,
+ value: f32,
+ on_change: Box<dyn Fn(f32) -> Message>,
+ width: Length,
+}
+
+impl<'a, Message> Slider<'a, Message> {
+ /// Creates a new [`Slider`].
+ ///
+ /// It expects:
+ /// * the local [`State`] of the [`Slider`]
+ /// * an inclusive range of possible values
+ /// * the current value of the [`Slider`]
+ /// * a function that will be called when the [`Slider`] is dragged.
+ /// It receives the new value of the [`Slider`] and must produce a
+ /// `Message`.
+ ///
+ /// [`Slider`]: struct.Slider.html
+ /// [`State`]: struct.State.html
+ pub fn new<F>(
+ state: &'a mut State,
+ range: RangeInclusive<f32>,
+ value: f32,
+ on_change: F,
+ ) -> Self
+ where
+ F: 'static + Fn(f32) -> Message,
+ {
+ Slider {
+ state,
+ value: value.max(*range.start()).min(*range.end()),
+ range,
+ on_change: Box::new(on_change),
+ width: Length::Fill,
+ }
+ }
+
+ /// Sets the width of the [`Slider`].
+ ///
+ /// [`Slider`]: struct.Slider.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+}
+
+/// The local state of a [`Slider`].
+///
+/// [`Slider`]: struct.Slider.html
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub struct State {
+ is_dragging: bool,
+}
+
+impl State {
+ /// Creates a new [`State`].
+ ///
+ /// [`State`]: struct.State.html
+ pub fn new() -> State {
+ State::default()
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message>
where
@@ -24,7 +91,13 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- renderer.layout(&self, limits)
+ let limits = limits
+ .width(self.width)
+ .height(Length::Units(renderer.height() as u16));
+
+ let size = limits.resolve(Size::ZERO);
+
+ layout::Node::new(size)
}
fn on_event(
@@ -81,7 +154,13 @@ where
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self, layout, cursor_position)
+ renderer.draw(
+ layout.bounds(),
+ cursor_position,
+ self.range.clone(),
+ self.value,
+ self.state.is_dragging,
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -97,15 +176,7 @@ where
/// [`Slider`]: struct.Slider.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
- /// Creates a [`Node`] for the provided [`Radio`].
- ///
- /// [`Node`]: ../../struct.Node.html
- /// [`Radio`]: struct.Radio.html
- fn layout<Message>(
- &self,
- slider: &Slider<'_, Message>,
- limits: &layout::Limits,
- ) -> layout::Node;
+ fn height(&self) -> u32;
/// Draws a [`Slider`].
///
@@ -119,11 +190,13 @@ pub trait Renderer: crate::Renderer {
/// [`Slider`]: struct.Slider.html
/// [`State`]: struct.State.html
/// [`Class`]: enum.Class.html
- fn draw<Message>(
+ fn draw(
&mut self,
- slider: &Slider<'_, Message>,
- layout: Layout<'_>,
+ bounds: Rectangle,
cursor_position: Point,
+ range: RangeInclusive<f32>,
+ value: f32,
+ is_dragging: bool,
) -> Self::Output;
}
diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs
index 10d892a3..f949b607 100644
--- a/native/src/widget/text.rs
+++ b/native/src/widget/text.rs
@@ -1,9 +1,113 @@
//! Write some text for your users to read.
-use crate::{layout, Element, Hasher, Layout, Length, Point, Widget};
+use crate::{
+ layout, Color, Element, Font, Hasher, HorizontalAlignment, Layout, Length,
+ Point, Rectangle, Size, VerticalAlignment, Widget,
+};
use std::hash::Hash;
-pub use iced_core::text::*;
+/// A paragraph of text.
+///
+/// # Example
+///
+/// ```
+/// # use iced_native::Text;
+///
+/// Text::new("I <3 iced!")
+/// .size(40);
+/// ```
+#[derive(Debug, Clone)]
+pub struct Text {
+ content: String,
+ size: Option<u16>,
+ color: Option<Color>,
+ font: Font,
+ width: Length,
+ height: Length,
+ horizontal_alignment: HorizontalAlignment,
+ vertical_alignment: VerticalAlignment,
+}
+
+impl Text {
+ /// Create a new fragment of [`Text`] with the given contents.
+ ///
+ /// [`Text`]: struct.Text.html
+ pub fn new(label: &str) -> Self {
+ Text {
+ content: String::from(label),
+ size: None,
+ color: None,
+ font: Font::Default,
+ width: Length::Fill,
+ height: Length::Shrink,
+ horizontal_alignment: HorizontalAlignment::Left,
+ vertical_alignment: VerticalAlignment::Top,
+ }
+ }
+
+ /// Sets the size of the [`Text`].
+ ///
+ /// [`Text`]: struct.Text.html
+ pub fn size(mut self, size: u16) -> Self {
+ self.size = Some(size);
+ self
+ }
+
+ /// Sets the [`Color`] of the [`Text`].
+ ///
+ /// [`Text`]: struct.Text.html
+ /// [`Color`]: ../../struct.Color.html
+ pub fn color<C: Into<Color>>(mut self, color: C) -> Self {
+ self.color = Some(color.into());
+ self
+ }
+
+ /// Sets the [`Font`] of the [`Text`].
+ ///
+ /// [`Text`]: struct.Text.html
+ /// [`Font`]: ../../struct.Font.html
+ pub fn font(mut self, font: Font) -> Self {
+ self.font = font;
+ self
+ }
+
+ /// Sets the width of the [`Text`] boundaries.
+ ///
+ /// [`Text`]: struct.Text.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the height of the [`Text`] boundaries.
+ ///
+ /// [`Text`]: struct.Text.html
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the [`HorizontalAlignment`] of the [`Text`].
+ ///
+ /// [`Text`]: struct.Text.html
+ /// [`HorizontalAlignment`]: enum.HorizontalAlignment.html
+ pub fn horizontal_alignment(
+ mut self,
+ alignment: HorizontalAlignment,
+ ) -> Self {
+ self.horizontal_alignment = alignment;
+ self
+ }
+
+ /// Sets the [`VerticalAlignment`] of the [`Text`].
+ ///
+ /// [`Text`]: struct.Text.html
+ /// [`VerticalAlignment`]: enum.VerticalAlignment.html
+ pub fn vertical_alignment(mut self, alignment: VerticalAlignment) -> Self {
+ self.vertical_alignment = alignment;
+ self
+ }
+}
impl<Message, Renderer> Widget<Message, Renderer> for Text
where
@@ -18,7 +122,18 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- renderer.layout(&self, limits)
+ 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, bounds);
+
+ let size = limits.resolve(Size::new(width, height));
+
+ layout::Node::new(size)
}
fn draw(
@@ -27,7 +142,15 @@ where
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self, layout)
+ renderer.draw(
+ layout.bounds(),
+ &self.content,
+ self.size.unwrap_or(renderer.default_size()),
+ self.font,
+ self.color,
+ self.horizontal_alignment,
+ self.vertical_alignment,
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -47,17 +170,15 @@ where
/// [renderer]: ../../renderer/index.html
/// [`UserInterface`]: ../../struct.UserInterface.html
pub trait Renderer: crate::Renderer {
- /// Creates a [`Node`] with the given [`Style`] for the provided [`Text`]
- /// contents and size.
- ///
- /// You should probably use [`Node::with_measure`] to allow [`Text`] to
- /// adapt to the dimensions of its container.
- ///
- /// [`Node`]: ../../struct.Node.html
- /// [`Style`]: ../../struct.Style.html
- /// [`Text`]: struct.Text.html
- /// [`Node::with_measure`]: ../../struct.Node.html#method.with_measure
- fn layout(&self, text: &Text, limits: &layout::Limits) -> layout::Node;
+ fn default_size(&self) -> u16;
+
+ fn measure(
+ &self,
+ content: &str,
+ size: u16,
+ font: Font,
+ bounds: Size,
+ ) -> (f32, f32);
/// Draws a [`Text`] fragment.
///
@@ -72,7 +193,16 @@ pub trait Renderer: crate::Renderer {
/// [`Text`]: struct.Text.html
/// [`HorizontalAlignment`]: enum.HorizontalAlignment.html
/// [`VerticalAlignment`]: enum.VerticalAlignment.html
- fn draw(&mut self, text: &Text, layout: Layout<'_>) -> Self::Output;
+ fn draw(
+ &mut self,
+ bounds: Rectangle,
+ content: &str,
+ size: u16,
+ font: Font,
+ color: Option<Color>,
+ horizontal_alignment: HorizontalAlignment,
+ vertical_alignment: VerticalAlignment,
+ ) -> Self::Output;
}
impl<'a, Message, Renderer> From<Text> for Element<'a, Message, Renderer>
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 35e10000..bb5bb523 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -1,10 +1,95 @@
+//! Display fields that can be filled with text.
+//!
+//! A [`TextInput`] has some local [`State`].
+//!
+//! [`TextInput`]: struct.TextInput.html
+//! [`State`]: struct.State.html
use crate::{
input::{keyboard, mouse, ButtonState},
layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
Widget,
};
-pub use iced_core::{text_input::State, TextInput};
+/// A widget that can be filled with text by using a keyboard.
+pub struct TextInput<'a, Message> {
+ state: &'a mut State,
+ placeholder: String,
+ value: Value,
+ width: Length,
+ max_width: Length,
+ padding: u16,
+ size: Option<u16>,
+ on_change: Box<dyn Fn(String) -> Message>,
+ on_submit: Option<Message>,
+}
+
+impl<'a, Message> TextInput<'a, Message> {
+ /// Creates a new [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn new<F>(
+ state: &'a mut State,
+ placeholder: &str,
+ value: &str,
+ on_change: F,
+ ) -> Self
+ where
+ F: 'static + Fn(String) -> Message,
+ {
+ Self {
+ state,
+ placeholder: String::from(placeholder),
+ value: Value::new(value),
+ width: Length::Fill,
+ max_width: Length::Shrink,
+ padding: 0,
+ size: None,
+ on_change: Box::new(on_change),
+ on_submit: None,
+ }
+ }
+
+ /// Sets the width of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the maximum width of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn max_width(mut self, max_width: Length) -> Self {
+ self.max_width = max_width;
+ self
+ }
+
+ /// Sets the padding of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn padding(mut self, units: u16) -> Self {
+ self.padding = units;
+ self
+ }
+
+ /// Sets the text size of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn size(mut self, size: u16) -> Self {
+ self.size = Some(size);
+ self
+ }
+
+ /// Sets the message that should be produced when the [`TextInput`] is
+ /// focused and the enter key is pressed.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn on_submit(mut self, message: Message) -> Self {
+ self.on_submit = Some(message);
+ self
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer> for TextInput<'a, Message>
where
@@ -120,12 +205,19 @@ where
let bounds = layout.bounds();
let text_bounds = layout.children().next().unwrap().bounds();
- renderer.draw(&self, bounds, text_bounds, cursor_position)
+ renderer.draw(
+ bounds,
+ text_bounds,
+ cursor_position,
+ self.size.unwrap_or(renderer.default_size()),
+ &self.placeholder,
+ &self.value,
+ &self.state,
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
- use std::any::TypeId;
- use std::hash::Hash;
+ use std::{any::TypeId, hash::Hash};
TypeId::of::<TextInput<'static, ()>>().hash(state);
@@ -139,12 +231,15 @@ where
pub trait Renderer: crate::Renderer + Sized {
fn default_size(&self) -> u16;
- fn draw<Message>(
+ fn draw(
&mut self,
- text_input: &TextInput<'_, Message>,
bounds: Rectangle,
text_bounds: Rectangle,
cursor_position: Point,
+ size: u16,
+ placeholder: &str,
+ value: &Value,
+ state: &State,
) -> Self::Output;
}
@@ -160,3 +255,125 @@ where
Element::new(text_input)
}
}
+
+/// The state of a [`TextInput`].
+///
+/// [`TextInput`]: struct.TextInput.html
+#[derive(Debug, Default, Clone)]
+pub struct State {
+ is_focused: bool,
+ cursor_position: usize,
+}
+
+impl State {
+ /// Creates a new [`State`], representing an unfocused [`TextInput`].
+ ///
+ /// [`State`]: struct.State.html
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Creates a new [`State`], representing a focused [`TextInput`].
+ ///
+ /// [`State`]: struct.State.html
+ pub fn focused() -> Self {
+ use std::usize;
+
+ Self {
+ is_focused: true,
+ cursor_position: usize::MAX,
+ }
+ }
+
+ pub fn is_focused(&self) -> bool {
+ self.is_focused
+ }
+
+ /// Returns the cursor position of a [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn cursor_position(&self, value: &Value) -> usize {
+ self.cursor_position.min(value.len())
+ }
+
+ /// Moves the cursor of a [`TextInput`] to the right.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn move_cursor_right(&mut self, value: &Value) {
+ let current = self.cursor_position(value);
+
+ if current < value.len() {
+ self.cursor_position = current + 1;
+ }
+ }
+
+ /// Moves the cursor of a [`TextInput`] to the left.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn move_cursor_left(&mut self, value: &Value) {
+ let current = self.cursor_position(value);
+
+ if current > 0 {
+ self.cursor_position = current - 1;
+ }
+ }
+
+ /// Moves the cursor of a [`TextInput`] to the end.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn move_cursor_to_end(&mut self, value: &Value) {
+ self.cursor_position = value.len();
+ }
+}
+
+/// The value of a [`TextInput`].
+///
+/// [`TextInput`]: struct.TextInput.html
+// TODO: Use `unicode-segmentation`
+#[derive(Debug)]
+pub struct Value(Vec<char>);
+
+impl Value {
+ /// Creates a new [`Value`] from a string slice.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn new(string: &str) -> Self {
+ Self(string.chars().collect())
+ }
+
+ /// Returns the total amount of `char` in the [`Value`].
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ /// Returns a new [`Value`] containing the `char` until the given `index`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn until(&self, index: usize) -> Self {
+ Self(self.0[..index.min(self.len())].to_vec())
+ }
+
+ /// Converts the [`Value`] into a `String`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn to_string(&self) -> String {
+ use std::iter::FromIterator;
+ String::from_iter(self.0.iter())
+ }
+
+ /// Inserts a new `char` at the given `index`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn insert(&mut self, index: usize, c: char) {
+ self.0.insert(index, c);
+ }
+
+ /// Removes the `char` at the given `index`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn remove(&mut self, index: usize) {
+ let _ = self.0.remove(index);
+ }
+}