diff options
author | 2019-09-20 19:15:31 +0200 | |
---|---|---|
committer | 2019-09-20 19:15:31 +0200 | |
commit | b9e0f7494881ad7cdfbcbc16878ecc6ef717753f (patch) | |
tree | c8a7419b5cb4c0161306c479b93038f2f86498c2 /core | |
parent | b83a4b42dd912b5f59d40e7d4f7f7ccdabc43019 (diff) | |
download | iced-b9e0f7494881ad7cdfbcbc16878ecc6ef717753f.tar.gz iced-b9e0f7494881ad7cdfbcbc16878ecc6ef717753f.tar.bz2 iced-b9e0f7494881ad7cdfbcbc16878ecc6ef717753f.zip |
Create `iced_core` and `iced_native`
Diffstat (limited to 'core')
-rw-r--r-- | core/Cargo.toml | 13 | ||||
-rw-r--r-- | core/src/align.rs | 47 | ||||
-rw-r--r-- | core/src/color.rs | 19 | ||||
-rw-r--r-- | core/src/justify.rs | 44 | ||||
-rw-r--r-- | core/src/length.rs | 6 | ||||
-rw-r--r-- | core/src/lib.rs | 18 | ||||
-rw-r--r-- | core/src/point.rs | 31 | ||||
-rw-r--r-- | core/src/rectangle.rs | 30 | ||||
-rw-r--r-- | core/src/vector.rs | 15 | ||||
-rw-r--r-- | core/src/widget.rs | 27 | ||||
-rw-r--r-- | core/src/widget/button.rs | 158 | ||||
-rw-r--r-- | core/src/widget/checkbox.rs | 78 | ||||
-rw-r--r-- | core/src/widget/column.rs | 147 | ||||
-rw-r--r-- | core/src/widget/image.rs | 89 | ||||
-rw-r--r-- | core/src/widget/radio.rs | 88 | ||||
-rw-r--r-- | core/src/widget/row.rs | 142 | ||||
-rw-r--r-- | core/src/widget/slider.rs | 123 | ||||
-rw-r--r-- | core/src/widget/text.rs | 119 |
18 files changed, 1194 insertions, 0 deletions
diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 00000000..40a8bcfb --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "iced_core" +version = "0.1.0-alpha" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2018" +description = "The essential concepts of Iced" +license = "MIT" +repository = "https://github.com/hecrj/iced" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +stretch = { version = "0.2", optional = true } diff --git a/core/src/align.rs b/core/src/align.rs new file mode 100644 index 00000000..5876e0f8 --- /dev/null +++ b/core/src/align.rs @@ -0,0 +1,47 @@ +/// Alignment on the cross axis of a container. +/// +/// * On a [`Column`], it describes __horizontal__ alignment. +/// * On a [`Row`], it describes __vertical__ alignment. +/// +/// [`Column`]: widget/struct.Column.html +/// [`Row`]: widget/struct.Row.html +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Align { + /// Align at the start of the cross axis. + Start, + + /// Align at the center of the cross axis. + Center, + + /// Align at the end of the cross axis. + End, + + /// Stretch over the cross axis. + Stretch, +} + +#[cfg(feature = "stretch")] +#[doc(hidden)] +impl From<Align> for stretch::style::AlignItems { + fn from(align: Align) -> Self { + match align { + Align::Start => stretch::style::AlignItems::FlexStart, + Align::Center => stretch::style::AlignItems::Center, + Align::End => stretch::style::AlignItems::FlexEnd, + Align::Stretch => stretch::style::AlignItems::Stretch, + } + } +} + +#[cfg(feature = "stretch")] +#[doc(hidden)] +impl From<Align> for stretch::style::AlignSelf { + fn from(align: Align) -> Self { + match align { + Align::Start => stretch::style::AlignSelf::FlexStart, + Align::Center => stretch::style::AlignSelf::Center, + Align::End => stretch::style::AlignSelf::FlexEnd, + Align::Stretch => stretch::style::AlignSelf::Stretch, + } + } +} diff --git a/core/src/color.rs b/core/src/color.rs new file mode 100644 index 00000000..5cc3a084 --- /dev/null +++ b/core/src/color.rs @@ -0,0 +1,19 @@ +/// A color in the sRGB color space. +#[derive(Debug, Clone, Copy, PartialEq)] +#[allow(missing_docs)] +pub struct Color { + pub r: f32, + pub g: f32, + pub b: f32, + pub a: f32, +} + +impl Color { + /// The black color. + pub const BLACK: Color = Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }; +} diff --git a/core/src/justify.rs b/core/src/justify.rs new file mode 100644 index 00000000..878573b0 --- /dev/null +++ b/core/src/justify.rs @@ -0,0 +1,44 @@ +/// Distribution on the main axis of a container. +/// +/// * On a [`Column`], it describes __vertical__ distribution. +/// * On a [`Row`], it describes __horizontal__ distribution. +/// +/// [`Column`]: widget/struct.Column.html +/// [`Row`]: widget/struct.Row.html +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Justify { + /// Place items at the start of the main axis. + Start, + + /// Place items at the center of the main axis. + Center, + + /// Place items at the end of the main axis. + End, + + /// Place items with space between. + SpaceBetween, + + /// Place items with space around. + SpaceAround, + + /// Place items with evenly distributed space. + SpaceEvenly, +} + +#[cfg(feature = "stretch")] +#[doc(hidden)] +impl From<Justify> for stretch::style::JustifyContent { + fn from(justify: Justify) -> Self { + match justify { + Justify::Start => stretch::style::JustifyContent::FlexStart, + Justify::Center => stretch::style::JustifyContent::Center, + Justify::End => stretch::style::JustifyContent::FlexEnd, + Justify::SpaceBetween => { + stretch::style::JustifyContent::SpaceBetween + } + Justify::SpaceAround => stretch::style::JustifyContent::SpaceAround, + Justify::SpaceEvenly => stretch::style::JustifyContent::SpaceEvenly, + } + } +} diff --git a/core/src/length.rs b/core/src/length.rs new file mode 100644 index 00000000..3547b246 --- /dev/null +++ b/core/src/length.rs @@ -0,0 +1,6 @@ +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum Length { + Fill, + Shrink, + Units(u16), +} diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 00000000..1f43b2b7 --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,18 @@ +pub mod widget; + +mod align; +mod color; +mod justify; +mod length; +mod point; +mod rectangle; +mod vector; + +pub use align::Align; +pub use color::Color; +pub use justify::Justify; +pub use length::Length; +pub use point::Point; +pub use rectangle::Rectangle; +pub use vector::Vector; +pub use widget::*; diff --git a/core/src/point.rs b/core/src/point.rs new file mode 100644 index 00000000..183998dd --- /dev/null +++ b/core/src/point.rs @@ -0,0 +1,31 @@ +use crate::Vector; + +/// A 2D point. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Point { + /// The X coordinate. + pub x: f32, + + /// The Y coordinate. + pub y: f32, +} + +impl Point { + /// Creates a new [`Point`] with the given coordinates. + /// + /// [`Point`]: struct.Point.html + pub fn new(x: f32, y: f32) -> Self { + Self { x, y } + } +} + +impl std::ops::Add<Vector> for Point { + type Output = Self; + + fn add(self, vector: Vector) -> Self { + Self { + x: self.x + vector.x, + y: self.y + vector.y, + } + } +} diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs new file mode 100644 index 00000000..95c2570c --- /dev/null +++ b/core/src/rectangle.rs @@ -0,0 +1,30 @@ +use crate::Point; + +/// A rectangle. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rectangle<T = f32> { + /// X coordinate of the top-left corner. + pub x: T, + + /// Y coordinate of the top-left corner. + pub y: T, + + /// Width of the rectangle. + pub width: T, + + /// Height of the rectangle. + pub height: T, +} + +impl Rectangle<f32> { + /// Returns true if the given [`Point`] is contained in the [`Rectangle`]. + /// + /// [`Point`]: struct.Point.html + /// [`Rectangle`]: struct.Rectangle.html + pub fn contains(&self, point: Point) -> bool { + self.x <= point.x + && point.x <= self.x + self.width + && self.y <= point.y + && point.y <= self.y + self.height + } +} diff --git a/core/src/vector.rs b/core/src/vector.rs new file mode 100644 index 00000000..f45daab9 --- /dev/null +++ b/core/src/vector.rs @@ -0,0 +1,15 @@ +/// A 2D vector. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Vector { + pub x: f32, + pub y: f32, +} + +impl Vector { + /// Creates a new [`Vector`] with the given components. + /// + /// [`Vector`]: struct.Vector.html + pub fn new(x: f32, y: f32) -> Self { + Self { x, y } + } +} diff --git a/core/src/widget.rs b/core/src/widget.rs new file mode 100644 index 00000000..4700be0e --- /dev/null +++ b/core/src/widget.rs @@ -0,0 +1,27 @@ +//! Use the essential widgets. +//! +//! # Re-exports +//! For convenience, the contents of this module are available at the root +//! module. Therefore, you can directly type: +//! +//! ``` +//! use iced_core::{button, Button}; +//! ``` +mod column; +mod row; + +pub mod button; +pub mod checkbox; +pub mod image; +pub mod radio; +pub mod slider; +pub mod text; + +pub use button::Button; +pub use checkbox::Checkbox; +pub use column::Column; +pub use image::Image; +pub use radio::Radio; +pub use row::Row; +pub use slider::Slider; +pub use text::Text; diff --git a/core/src/widget/button.rs b/core/src/widget/button.rs new file mode 100644 index 00000000..b98bb443 --- /dev/null +++ b/core/src/widget/button.rs @@ -0,0 +1,158 @@ +//! Allow your users to perform actions by pressing a button. +//! +//! A [`Button`] has some local [`State`]. +//! +//! [`Button`]: struct.Button.html +//! [`State`]: struct.State.html + +use crate::{Align, Length}; + +/// A generic widget that produces a message when clicked. +/// +/// # Example +/// +/// ``` +/// use iced_core::{button, Button}; +/// +/// pub enum Message { +/// ButtonClicked, +/// } +/// +/// let state = &mut button::State::new(); +/// +/// Button::new(state, "Click me!") +/// .on_press(Message::ButtonClicked); +/// ``` +/// +///  +pub struct Button<'a, Message> { + /// The current state of the button + pub state: &'a mut State, + + /// The label of the button + pub label: String, + + /// The message to produce when the button is pressed + pub on_press: Option<Message>, + + pub class: Class, + + pub width: Length, + + pub align_self: Option<Align>, +} + +impl<'a, Message> std::fmt::Debug for Button<'a, Message> +where + Message: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Button") + .field("state", &self.state) + .field("label", &self.label) + .field("on_press", &self.on_press) + .finish() + } +} + +impl<'a, Message> Button<'a, Message> { + /// Creates a new [`Button`] with some local [`State`] and the given label. + /// + /// [`Button`]: struct.Button.html + /// [`State`]: struct.State.html + pub fn new(state: &'a mut State, label: &str) -> Self { + Button { + state, + label: String::from(label), + on_press: None, + class: Class::Primary, + width: Length::Shrink, + align_self: None, + } + } + + /// Sets the width of the [`Button`]. + /// + /// [`Button`]: struct.Button.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the alignment of the [`Button`] itself. + /// + /// This is useful if you want to override the default alignment given by + /// the parent container. + /// + /// [`Button`]: struct.Button.html + pub fn align_self(mut self, align: Align) -> Self { + self.align_self = Some(align); + self + } + + /// Sets the [`Class`] of the [`Button`]. + /// + /// + /// [`Button`]: struct.Button.html + /// [`Class`]: enum.Class.html + pub fn class(mut self, class: Class) -> Self { + self.class = class; + self + } + + /// 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 { + pub is_pressed: bool, +} + +impl State { + /// Creates a new [`State`]. + /// + /// [`State`]: struct.State.html + pub fn new() -> State { + State::default() + } + + /// Returns whether the associated [`Button`] is currently being pressed or + /// not. + /// + /// [`Button`]: struct.Button.html + pub fn is_pressed(&self) -> bool { + self.is_pressed + } +} + +/// The type of a [`Button`]. +/// +///  +/// +/// [`Button`]: struct.Button.html +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Class { + /// The [`Button`] performs the main action. + /// + /// [`Button`]: struct.Button.html + Primary, + + /// The [`Button`] performs an alternative action. + /// + /// [`Button`]: struct.Button.html + Secondary, + + /// The [`Button`] performs a productive action. + /// + /// [`Button`]: struct.Button.html + Positive, +} diff --git a/core/src/widget/checkbox.rs b/core/src/widget/checkbox.rs new file mode 100644 index 00000000..1f0a0c04 --- /dev/null +++ b/core/src/widget/checkbox.rs @@ -0,0 +1,78 @@ +//! Show toggle controls using checkboxes. +use crate::Color; + +/// A box that can be checked. +/// +/// # Example +/// +/// ``` +/// use iced_core::Checkbox; +/// +/// pub enum Message { +/// CheckboxToggled(bool), +/// } +/// +/// let is_checked = true; +/// +/// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled); +/// ``` +/// +///  +pub struct Checkbox<Message> { + /// Whether the checkbox is checked or not + pub is_checked: bool, + + /// Function to call when checkbox is toggled to produce a __message__. + /// + /// The function should be provided `true` when the checkbox is checked + /// and `false` otherwise. + pub on_toggle: Box<dyn Fn(bool) -> Message>, + + /// The label of the checkbox + pub label: String, + + /// The color of the label + pub label_color: Option<Color>, +} + +impl<Message> std::fmt::Debug for Checkbox<Message> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Checkbox") + .field("is_checked", &self.is_checked) + .field("label", &self.label) + .field("label_color", &self.label_color) + .finish() + } +} + +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 + } +} diff --git a/core/src/widget/column.rs b/core/src/widget/column.rs new file mode 100644 index 00000000..2df327a0 --- /dev/null +++ b/core/src/widget/column.rs @@ -0,0 +1,147 @@ +use crate::{Align, Justify, Length}; + +/// A container that distributes its contents vertically. +/// +/// A [`Column`] will try to fill the horizontal space of its container. +/// +/// [`Column`]: struct.Column.html +pub struct Column<Element> { + pub spacing: u16, + pub padding: u16, + pub width: Length, + pub height: Length, + pub max_width: Length, + pub max_height: Length, + pub align_self: Option<Align>, + pub align_items: Align, + pub justify_content: Justify, + pub children: Vec<Element>, +} + +impl<Element> Column<Element> { + /// 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: Length::Shrink, + max_height: Length::Shrink, + align_self: None, + align_items: Align::Start, + justify_content: Justify::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: Length) -> 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: Length) -> Self { + self.max_height = max_height; + self + } + + /// Sets the alignment of the [`Column`] itself. + /// + /// This is useful if you want to override the default alignment given by + /// the parent container. + /// + /// [`Column`]: struct.Column.html + pub fn align_self(mut self, align: Align) -> Self { + self.align_self = Some(align); + 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 + } + + /// Sets the vertical distribution strategy for the contents of the + /// [`Column`] . + /// + /// [`Column`]: struct.Column.html + pub fn justify_content(mut self, justify: Justify) -> Self { + self.justify_content = justify; + self + } + + /// Adds an element to the [`Column`]. + /// + /// [`Column`]: struct.Column.html + pub fn push<E>(mut self, child: E) -> Column<Element> + where + E: Into<Element>, + { + self.children.push(child.into()); + self + } +} + +impl<Element> Default for Column<Element> { + fn default() -> Self { + Self::new() + } +} + +impl<Element> std::fmt::Debug for Column<Element> +where + Element: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // TODO: Complete once stabilized + f.debug_struct("Column") + .field("spacing", &self.spacing) + .field("children", &self.children) + .finish() + } +} diff --git a/core/src/widget/image.rs b/core/src/widget/image.rs new file mode 100644 index 00000000..110ba99a --- /dev/null +++ b/core/src/widget/image.rs @@ -0,0 +1,89 @@ +//! Display images in your user interface. + +use crate::{Align, Length, Rectangle}; + +/// A frame that displays an image while keeping aspect ratio. +/// +/// # Example +/// +/// ``` +/// use iced_core::Image; +/// +/// # let my_handle = String::from("some_handle"); +/// let image = Image::new(my_handle); +/// ``` +pub struct Image<I> { + /// The image handle + pub handle: I, + + /// The part of the image to show + pub clip: Option<Rectangle<u16>>, + + /// The width of the image + pub width: Length, + + /// The height of the image + pub height: Length, + + pub align_self: Option<Align>, +} + +impl<I> std::fmt::Debug for Image<I> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Image") + .field("clip", &self.clip) + .field("width", &self.width) + .field("height", &self.height) + .finish() + } +} + +impl<I> Image<I> { + /// Creates a new [`Image`] with given image handle. + /// + /// [`Image`]: struct.Image.html + pub fn new(handle: I) -> Self { + Image { + handle, + clip: None, + width: Length::Shrink, + height: Length::Shrink, + align_self: None, + } + } + + /// Sets the portion of the [`Image`] to draw. + /// + /// [`Image`]: struct.Image.html + pub fn clip(mut self, clip: Rectangle<u16>) -> Self { + self.clip = Some(clip); + self + } + + /// 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 + } + + /// Sets the alignment of the [`Image`] itself. + /// + /// This is useful if you want to override the default alignment given by + /// the parent container. + /// + /// [`Image`]: struct.Image.html + pub fn align_self(mut self, align: Align) -> Self { + self.align_self = Some(align); + self + } +} diff --git a/core/src/widget/radio.rs b/core/src/widget/radio.rs new file mode 100644 index 00000000..9765e928 --- /dev/null +++ b/core/src/widget/radio.rs @@ -0,0 +1,88 @@ +//! Create choices using radio buttons. +use crate::Color; + +/// A circular button representing a choice. +/// +/// # Example +/// ``` +/// use iced_core::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); +/// ``` +/// +///  +pub struct Radio<Message> { + /// Whether the radio button is selected or not + pub is_selected: bool, + + /// The message to produce when the radio button is clicked + pub on_click: Message, + + /// The label of the radio button + pub label: String, + + /// The color of the label + pub label_color: Option<Color>, +} + +impl<Message> std::fmt::Debug for Radio<Message> +where + Message: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Radio") + .field("is_selected", &self.is_selected) + .field("on_click", &self.on_click) + .field("label", &self.label) + .field("label_color", &self.label_color) + .finish() + } +} + +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 + } +} diff --git a/core/src/widget/row.rs b/core/src/widget/row.rs new file mode 100644 index 00000000..6bdb4ed2 --- /dev/null +++ b/core/src/widget/row.rs @@ -0,0 +1,142 @@ +use crate::{Align, Justify, Length}; + +/// A container that distributes its contents horizontally. +/// +/// A [`Row`] will try to fill the horizontal space of its container. +/// +/// [`Row`]: struct.Row.html +pub struct Row<Element> { + pub spacing: u16, + pub padding: u16, + pub width: Length, + pub height: Length, + pub max_width: Length, + pub max_height: Length, + pub align_self: Option<Align>, + pub align_items: Align, + pub justify_content: Justify, + pub children: Vec<Element>, +} + +impl<Element> Row<Element> { + /// 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: Length::Shrink, + max_height: Length::Shrink, + align_self: None, + align_items: Align::Start, + justify_content: Justify::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: Length) -> 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: Length) -> Self { + self.max_height = max_height; + self + } + + /// Sets the alignment of the [`Row`] itself. + /// + /// This is useful if you want to override the default alignment given by + /// the parent container. + /// + /// [`Row`]: struct.Row.html + pub fn align_self(mut self, align: Align) -> Self { + self.align_self = Some(align); + 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 + } + + /// Sets the horizontal distribution strategy for the contents of the + /// [`Row`] . + /// + /// [`Row`]: struct.Row.html + pub fn justify_content(mut self, justify: Justify) -> Self { + self.justify_content = justify; + self + } + + /// Adds an [`Element`] to the [`Row`]. + /// + /// [`Element`]: ../struct.Element.html + /// [`Row`]: struct.Row.html + pub fn push<E>(mut self, child: E) -> Row<Element> + where + E: Into<Element>, + { + self.children.push(child.into()); + self + } +} + +impl<Element> std::fmt::Debug for Row<Element> +where + Element: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // TODO: Complete once stabilized + f.debug_struct("Row") + .field("spacing", &self.spacing) + .field("children", &self.children) + .finish() + } +} diff --git a/core/src/widget/slider.rs b/core/src/widget/slider.rs new file mode 100644 index 00000000..b65e3991 --- /dev/null +++ b/core/src/widget/slider.rs @@ -0,0 +1,123 @@ +//! Display an interactive selector of a single value from a range of values. +//! +//! A [`Slider`] has some local [`State`]. +//! +//! [`Slider`]: struct.Slider.html +//! [`State`]: struct.State.html +use crate::Length; + +use std::ops::RangeInclusive; +use std::rc::Rc; + +/// An horizontal bar and a handle that selects a single value from a range of +/// values. +/// +/// A [`Slider`] will try to fill the horizontal space of its container. +/// +/// [`Slider`]: struct.Slider.html +/// +/// # Example +/// ``` +/// use iced_core::{slider, Slider}; +/// +/// pub enum Message { +/// SliderChanged(f32), +/// } +/// +/// let state = &mut slider::State::new(); +/// let value = 50.0; +/// +/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged); +/// ``` +/// +///  +pub struct Slider<'a, Message> { + /// The state of the slider + pub state: &'a mut State, + + /// The range of the slider + pub range: RangeInclusive<f32>, + + /// The current value of the slider + pub value: f32, + + /// The function to produce messages on change + pub on_change: Rc<Box<dyn Fn(f32) -> Message>>, + + pub width: Length, +} + +impl<'a, Message> std::fmt::Debug for Slider<'a, Message> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Slider") + .field("state", &self.state) + .field("range", &self.range) + .field("value", &self.value) + .finish() + } +} + +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: Rc::new(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 { + pub is_dragging: bool, +} + +impl State { + /// Creates a new [`State`]. + /// + /// [`State`]: struct.State.html + pub fn new() -> State { + State::default() + } + + /// Returns whether the associated [`Slider`] is currently being dragged or + /// not. + /// + /// [`Slider`]: struct.Slider.html + pub fn is_dragging(&self) -> bool { + self.is_dragging + } +} diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs new file mode 100644 index 00000000..427d9471 --- /dev/null +++ b/core/src/widget/text.rs @@ -0,0 +1,119 @@ +//! Write some text for your users to read. +use crate::{Color, Length}; + +/// A paragraph of text. +/// +/// # Example +/// +/// ``` +/// use iced_core::Text; +/// +/// Text::new("I <3 iced!") +/// .size(40); +/// ``` +#[derive(Debug, Clone)] +pub struct Text { + pub content: String, + pub size: Option<u16>, + pub color: Option<Color>, + pub width: Length, + pub height: Length, + pub horizontal_alignment: HorizontalAlignment, + pub 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, + 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 + pub fn color<C: Into<Color>>(mut self, color: C) -> Self { + self.color = Some(color.into()); + 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 + } +} + +/// The horizontal alignment of some resource. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HorizontalAlignment { + /// Align left + Left, + + /// Horizontally centered + Center, + + /// Align right + Right, +} + +/// The vertical alignment of some resource. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VerticalAlignment { + /// Align top + Top, + + /// Vertically centered + Center, + + /// Align bottom + Bottom, +} |