summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/element.rs184
-rw-r--r--src/hasher.rs19
-rw-r--r--src/input.rs1
-rw-r--r--src/input/button_state.rs14
-rw-r--r--src/input/keyboard.rs1
-rw-r--r--src/input/keyboard/event.rs7
-rw-r--r--src/input/keyboard/key_code.rs200
-rw-r--r--src/input/mouse.rs1
-rw-r--r--src/input/mouse/button.rs20
-rw-r--r--src/layout.rs12
-rw-r--r--src/lib.rs206
-rw-r--r--src/mouse_cursor.rs14
-rw-r--r--src/node.rs28
-rw-r--r--src/point.rs32
-rw-r--r--src/rectangle.rs18
-rw-r--r--src/renderer.rs34
-rw-r--r--src/runtime.rs106
-rw-r--r--src/user_interface.rs323
-rw-r--r--src/vector.rs15
-rw-r--r--src/widget.rs57
-rw-r--r--src/widget/button.rs19
-rw-r--r--src/widget/checkbox.rs52
-rw-r--r--src/widget/column.rs13
-rw-r--r--src/widget/panel.rs2
-rw-r--r--src/widget/radio.rs48
-rw-r--r--src/widget/row.rs13
-rw-r--r--src/widget/slider.rs17
-rw-r--r--src/widget/text.rs65
28 files changed, 1192 insertions, 329 deletions
diff --git a/src/element.rs b/src/element.rs
index 6bc0ad74..70d06f42 100644
--- a/src/element.rs
+++ b/src/element.rs
@@ -1,13 +1,19 @@
use stretch::{geometry, result};
-use crate::{Event, Hasher, Layout, MouseCursor, Node, Point, Widget};
+use crate::{
+ renderer, Event, Hasher, Layout, MouseCursor, Node, Point, Widget,
+};
/// A generic [`Widget`].
///
-/// If you have a widget, you should be able to use `widget.into()` to turn it
-/// into an [`Element`].
+/// It is useful to build composable user interfaces that do not leak
+/// implementation details in their __view logic__.
///
-/// [`Widget`]: trait.Widget.html
+/// If you have a [built-in widget], you should be able to use `Into<Element>`
+/// to turn it into an [`Element`].
+///
+/// [built-in widget]: widget/index.html#built-in-widgets
+/// [`Widget`]: widget/trait.Widget.html
/// [`Element`]: struct.Element.html
pub struct Element<'a, Message, Renderer> {
pub(crate) widget: Box<dyn Widget<Message, Renderer> + 'a>,
@@ -25,7 +31,7 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// Create a new [`Element`] containing the given [`Widget`].
///
/// [`Element`]: struct.Element.html
- /// [`Widget`]: trait.Widget.html
+ /// [`Widget`]: widget/trait.Widget.html
pub fn new(
widget: impl Widget<Message, Renderer> + 'a,
) -> Element<'a, Message, Renderer> {
@@ -37,12 +43,154 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// Applies a transformation to the produced message of the [`Element`].
///
/// This method is useful when you want to decouple different parts of your
- /// UI.
+ /// UI and make them __composable__.
///
/// [`Element`]: struct.Element.html
///
/// # Example
- /// TODO
+ /// Imagine we want to use [our counter](index.html#usage). But instead of
+ /// showing a single counter, we want to display many of them. We can reuse
+ /// the `Counter` type as it is!
+ ///
+ /// We use composition to model the __state__ of our new application:
+ ///
+ /// ```
+ /// # mod counter {
+ /// # pub struct Counter;
+ /// # }
+ /// use counter::Counter;
+ ///
+ /// struct ManyCounters {
+ /// counters: Vec<Counter>,
+ /// }
+ /// ```
+ ///
+ /// We can store the state of multiple counters now. However, the
+ /// __messages__ we implemented before describe the user interactions
+ /// of a __single__ counter. Right now, we need to also identify which
+ /// counter is receiving user interactions. Can we use composition again?
+ /// Yes.
+ ///
+ /// ```
+ /// # mod counter {
+ /// # #[derive(Debug, Clone, Copy)]
+ /// # pub enum Message {}
+ /// # }
+ /// #[derive(Debug, Clone, Copy)]
+ /// pub enum Message {
+ /// Counter(usize, counter::Message)
+ /// }
+ /// ```
+ ///
+ /// We compose the previous __messages__ with the index of the counter
+ /// producing them. Let's implement our __view logic__ now:
+ ///
+ /// ```
+ /// # mod counter {
+ /// # use iced::{button, Button};
+ /// #
+ /// # #[derive(Debug, Clone, Copy)]
+ /// # pub enum Message {}
+ /// # pub struct Counter(button::State);
+ /// #
+ /// # impl Counter {
+ /// # pub fn view(&mut self) -> Button<Message> {
+ /// # Button::new(&mut self.0, "_")
+ /// # }
+ /// # }
+ /// # }
+ /// #
+ /// # mod iced_wgpu {
+ /// # use iced::{
+ /// # button, MouseCursor, Node, Point, Rectangle, Style,
+ /// # };
+ /// # pub struct Renderer;
+ /// #
+ /// # impl button::Renderer for Renderer {
+ /// # fn draw(
+ /// # &mut self,
+ /// # _cursor_position: Point,
+ /// # _bounds: Rectangle,
+ /// # _state: &button::State,
+ /// # _label: &str,
+ /// # _class: button::Class,
+ /// # ) -> MouseCursor {
+ /// # MouseCursor::OutOfBounds
+ /// # }
+ /// # }
+ /// # }
+ /// #
+ /// # use counter::Counter;
+ /// #
+ /// # struct ManyCounters {
+ /// # counters: Vec<Counter>,
+ /// # }
+ /// #
+ /// # #[derive(Debug, Clone, Copy)]
+ /// # pub enum Message {
+ /// # Counter(usize, counter::Message)
+ /// # }
+ /// use iced::{Element, Row};
+ /// use iced_wgpu::Renderer;
+ ///
+ /// impl ManyCounters {
+ /// pub fn view(&mut self) -> Row<Message, Renderer> {
+ /// // We can quickly populate a `Row` by folding over our counters
+ /// self.counters.iter_mut().enumerate().fold(
+ /// Row::new().spacing(20),
+ /// |row, (index, counter)| {
+ /// // We display the counter
+ /// let element: Element<counter::Message, Renderer> =
+ /// counter.view().into();
+ ///
+ /// row.push(
+ /// // Here we turn our `Element<counter::Message>` into
+ /// // an `Element<Message>` by combining the `index` and the
+ /// // message of the `element`.
+ /// element.map(move |message| Message::Counter(index, message))
+ /// )
+ /// }
+ /// )
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Finally, our __update logic__ is pretty straightforward: simple
+ /// delegation.
+ ///
+ /// ```
+ /// # mod counter {
+ /// # #[derive(Debug, Clone, Copy)]
+ /// # pub enum Message {}
+ /// # pub struct Counter;
+ /// #
+ /// # impl Counter {
+ /// # pub fn update(&mut self, _message: Message) {}
+ /// # }
+ /// # }
+ /// #
+ /// # use counter::Counter;
+ /// #
+ /// # struct ManyCounters {
+ /// # counters: Vec<Counter>,
+ /// # }
+ /// #
+ /// # #[derive(Debug, Clone, Copy)]
+ /// # pub enum Message {
+ /// # Counter(usize, counter::Message)
+ /// # }
+ /// impl ManyCounters {
+ /// pub fn update(&mut self, message: Message) {
+ /// match message {
+ /// Message::Counter(index, counter_msg) => {
+ /// if let Some(counter) = self.counters.get_mut(index) {
+ /// counter.update(counter_msg);
+ /// }
+ /// }
+ /// }
+ /// }
+ /// }
+ /// ```
pub fn map<F, B>(self, f: F) -> Element<'a, B, Renderer>
where
Message: 'static + Copy,
@@ -68,7 +216,7 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
) -> Element<'a, Message, Renderer>
where
Message: 'static,
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + renderer::Debugger,
{
Element {
widget: Box::new(Explain::new(self, color)),
@@ -81,8 +229,8 @@ impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
node.0.compute_layout(geometry::Size::undefined()).unwrap()
}
- pub(crate) fn hash(&self, state: &mut Hasher) {
- self.widget.hash(state);
+ pub(crate) fn hash_layout(&self, state: &mut Hasher) {
+ self.widget.hash_layout(state);
}
}
@@ -151,19 +299,19 @@ where
self.widget.draw(renderer, layout, cursor_position)
}
- fn hash(&self, state: &mut Hasher) {
- self.widget.hash(state);
+ fn hash_layout(&self, state: &mut Hasher) {
+ self.widget.hash_layout(state);
}
}
-struct Explain<'a, Message, Renderer: crate::Renderer> {
+struct Explain<'a, Message, Renderer: renderer::Debugger> {
element: Element<'a, Message, Renderer>,
color: Renderer::Color,
}
impl<'a, Message, Renderer> std::fmt::Debug for Explain<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: renderer::Debugger,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Explain")
@@ -174,7 +322,7 @@ where
impl<'a, Message, Renderer> Explain<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: renderer::Debugger,
{
fn new(
element: Element<'a, Message, Renderer>,
@@ -187,7 +335,7 @@ where
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Explain<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: renderer::Debugger,
{
fn node(&self, renderer: &Renderer) -> Node {
self.element.widget.node(renderer)
@@ -216,7 +364,7 @@ where
self.element.widget.draw(renderer, layout, cursor_position)
}
- fn hash(&self, state: &mut Hasher) {
- self.element.widget.hash(state);
+ fn hash_layout(&self, state: &mut Hasher) {
+ self.element.widget.hash_layout(state);
}
}
diff --git a/src/hasher.rs b/src/hasher.rs
index a930fa13..9f6aacce 100644
--- a/src/hasher.rs
+++ b/src/hasher.rs
@@ -1,2 +1,19 @@
/// The hasher used to compare layouts.
-pub type Hasher = twox_hash::XxHash;
+#[derive(Debug)]
+pub struct Hasher(twox_hash::XxHash64);
+
+impl Default for Hasher {
+ fn default() -> Self {
+ Hasher(twox_hash::XxHash64::default())
+ }
+}
+
+impl core::hash::Hasher for Hasher {
+ fn write(&mut self, bytes: &[u8]) {
+ self.0.write(bytes)
+ }
+
+ fn finish(&self) -> u64 {
+ self.0.finish()
+ }
+}
diff --git a/src/input.rs b/src/input.rs
index 282d5c00..097fa730 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -1,3 +1,4 @@
+//! Map your system events into input events that the runtime can understand.
pub mod keyboard;
pub mod mouse;
diff --git a/src/input/button_state.rs b/src/input/button_state.rs
index e8845cc7..3a3472fb 100644
--- a/src/input/button_state.rs
+++ b/src/input/button_state.rs
@@ -1,5 +1,19 @@
+/// The state of a button.
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
pub enum ButtonState {
+ /// The button is pressed.
Pressed,
+
+ /// The button is __not__ pressed.
Released,
}
+
+#[cfg(feature = "winit")]
+impl From<winit::event::ElementState> for ButtonState {
+ fn from(element_state: winit::event::ElementState) -> Self {
+ match element_state {
+ winit::event::ElementState::Pressed => ButtonState::Pressed,
+ winit::event::ElementState::Released => ButtonState::Released,
+ }
+ }
+}
diff --git a/src/input/keyboard.rs b/src/input/keyboard.rs
index 2b0188ff..57c24484 100644
--- a/src/input/keyboard.rs
+++ b/src/input/keyboard.rs
@@ -1,3 +1,4 @@
+//! Build keyboard events.
mod event;
mod key_code;
diff --git a/src/input/keyboard/event.rs b/src/input/keyboard/event.rs
index 3804c42d..5acd46c0 100644
--- a/src/input/keyboard/event.rs
+++ b/src/input/keyboard/event.rs
@@ -13,9 +13,6 @@ pub enum Event {
key_code: KeyCode,
},
- /// Text was entered.
- TextEntered {
- /// The character entered
- character: char,
- },
+ /// A unicode character was received.
+ CharacterReceived(char),
}
diff --git a/src/input/keyboard/key_code.rs b/src/input/keyboard/key_code.rs
index 5cf9301f..9b449735 100644
--- a/src/input/keyboard/key_code.rs
+++ b/src/input/keyboard/key_code.rs
@@ -1,6 +1,11 @@
-/// The symbolic name of a keyboard key
+/// The symbolic name of a keyboard key.
+///
+/// This is mostly the `KeyCode` type found in `winit`. If you are using
+/// `winit`, consider enabling the `winit` feature to get conversion
+/// implementations for free!
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
+#[allow(missing_docs)]
pub enum KeyCode {
/// The '1' key over the letters.
Key1,
@@ -50,7 +55,7 @@ pub enum KeyCode {
Y,
Z,
- /// The Escape key, next to F1.
+ /// The Escape key, next to F1
Escape,
F1,
@@ -78,14 +83,14 @@ pub enum KeyCode {
F23,
F24,
- /// Print Screen/SysRq.
+ /// Print Screen/SysRq
Snapshot,
- /// Scroll Lock.
+ /// Scroll Lock
Scroll,
- /// Pause/Break key, next to Scroll lock.
+ /// Pause/Break key, next to Scroll lock
Pause,
- /// `Insert`, next to Backspace.
+ /// `Insert`, next to Backspace
Insert,
Home,
Delete,
@@ -98,15 +103,11 @@ pub enum KeyCode {
Right,
Down,
- /// The Backspace key, right over Enter.
- // TODO: rename
- Back,
- /// The Enter key.
- Return,
- /// The space bar.
+ Backspace,
+ Enter,
Space,
- /// The "Compose" key on Linux.
+ /// The "Compose" key on Linux
Compose,
Caret,
@@ -195,3 +196,176 @@ pub enum KeyCode {
Paste,
Cut,
}
+
+#[cfg(feature = "winit")]
+impl From<winit::event::VirtualKeyCode> for KeyCode {
+ fn from(virtual_keycode: winit::event::VirtualKeyCode) -> Self {
+ match virtual_keycode {
+ winit::event::VirtualKeyCode::Key1 => KeyCode::Key1,
+ winit::event::VirtualKeyCode::Key2 => KeyCode::Key2,
+ winit::event::VirtualKeyCode::Key3 => KeyCode::Key3,
+ winit::event::VirtualKeyCode::Key4 => KeyCode::Key4,
+ winit::event::VirtualKeyCode::Key5 => KeyCode::Key5,
+ winit::event::VirtualKeyCode::Key6 => KeyCode::Key6,
+ winit::event::VirtualKeyCode::Key7 => KeyCode::Key7,
+ winit::event::VirtualKeyCode::Key8 => KeyCode::Key8,
+ winit::event::VirtualKeyCode::Key9 => KeyCode::Key9,
+ winit::event::VirtualKeyCode::Key0 => KeyCode::Key0,
+ winit::event::VirtualKeyCode::A => KeyCode::A,
+ winit::event::VirtualKeyCode::B => KeyCode::B,
+ winit::event::VirtualKeyCode::C => KeyCode::C,
+ winit::event::VirtualKeyCode::D => KeyCode::D,
+ winit::event::VirtualKeyCode::E => KeyCode::E,
+ winit::event::VirtualKeyCode::F => KeyCode::F,
+ winit::event::VirtualKeyCode::G => KeyCode::G,
+ winit::event::VirtualKeyCode::H => KeyCode::H,
+ winit::event::VirtualKeyCode::I => KeyCode::I,
+ winit::event::VirtualKeyCode::J => KeyCode::J,
+ winit::event::VirtualKeyCode::K => KeyCode::K,
+ winit::event::VirtualKeyCode::L => KeyCode::L,
+ winit::event::VirtualKeyCode::M => KeyCode::M,
+ winit::event::VirtualKeyCode::N => KeyCode::N,
+ winit::event::VirtualKeyCode::O => KeyCode::O,
+ winit::event::VirtualKeyCode::P => KeyCode::P,
+ winit::event::VirtualKeyCode::Q => KeyCode::Q,
+ winit::event::VirtualKeyCode::R => KeyCode::R,
+ winit::event::VirtualKeyCode::S => KeyCode::S,
+ winit::event::VirtualKeyCode::T => KeyCode::T,
+ winit::event::VirtualKeyCode::U => KeyCode::U,
+ winit::event::VirtualKeyCode::V => KeyCode::V,
+ winit::event::VirtualKeyCode::W => KeyCode::W,
+ winit::event::VirtualKeyCode::X => KeyCode::X,
+ winit::event::VirtualKeyCode::Y => KeyCode::Y,
+ winit::event::VirtualKeyCode::Z => KeyCode::Z,
+ winit::event::VirtualKeyCode::Escape => KeyCode::Escape,
+ winit::event::VirtualKeyCode::F1 => KeyCode::F1,
+ winit::event::VirtualKeyCode::F2 => KeyCode::F2,
+ winit::event::VirtualKeyCode::F3 => KeyCode::F3,
+ winit::event::VirtualKeyCode::F4 => KeyCode::F4,
+ winit::event::VirtualKeyCode::F5 => KeyCode::F5,
+ winit::event::VirtualKeyCode::F6 => KeyCode::F6,
+ winit::event::VirtualKeyCode::F7 => KeyCode::F7,
+ winit::event::VirtualKeyCode::F8 => KeyCode::F8,
+ winit::event::VirtualKeyCode::F9 => KeyCode::F9,
+ winit::event::VirtualKeyCode::F10 => KeyCode::F10,
+ winit::event::VirtualKeyCode::F11 => KeyCode::F11,
+ winit::event::VirtualKeyCode::F12 => KeyCode::F12,
+ winit::event::VirtualKeyCode::F13 => KeyCode::F13,
+ winit::event::VirtualKeyCode::F14 => KeyCode::F14,
+ winit::event::VirtualKeyCode::F15 => KeyCode::F15,
+ winit::event::VirtualKeyCode::F16 => KeyCode::F16,
+ winit::event::VirtualKeyCode::F17 => KeyCode::F17,
+ winit::event::VirtualKeyCode::F18 => KeyCode::F18,
+ winit::event::VirtualKeyCode::F19 => KeyCode::F19,
+ winit::event::VirtualKeyCode::F20 => KeyCode::F20,
+ winit::event::VirtualKeyCode::F21 => KeyCode::F21,
+ winit::event::VirtualKeyCode::F22 => KeyCode::F22,
+ winit::event::VirtualKeyCode::F23 => KeyCode::F23,
+ winit::event::VirtualKeyCode::F24 => KeyCode::F24,
+ winit::event::VirtualKeyCode::Snapshot => KeyCode::Snapshot,
+ winit::event::VirtualKeyCode::Scroll => KeyCode::Scroll,
+ winit::event::VirtualKeyCode::Pause => KeyCode::Pause,
+ winit::event::VirtualKeyCode::Insert => KeyCode::Insert,
+ winit::event::VirtualKeyCode::Home => KeyCode::Home,
+ winit::event::VirtualKeyCode::Delete => KeyCode::Delete,
+ winit::event::VirtualKeyCode::End => KeyCode::End,
+ winit::event::VirtualKeyCode::PageDown => KeyCode::PageDown,
+ winit::event::VirtualKeyCode::PageUp => KeyCode::PageUp,
+ winit::event::VirtualKeyCode::Left => KeyCode::Left,
+ winit::event::VirtualKeyCode::Up => KeyCode::Up,
+ winit::event::VirtualKeyCode::Right => KeyCode::Right,
+ winit::event::VirtualKeyCode::Down => KeyCode::Down,
+ winit::event::VirtualKeyCode::Back => KeyCode::Backspace,
+ winit::event::VirtualKeyCode::Return => KeyCode::Enter,
+ winit::event::VirtualKeyCode::Space => KeyCode::Space,
+ winit::event::VirtualKeyCode::Compose => KeyCode::Compose,
+ winit::event::VirtualKeyCode::Caret => KeyCode::Caret,
+ winit::event::VirtualKeyCode::Numlock => KeyCode::Numlock,
+ winit::event::VirtualKeyCode::Numpad0 => KeyCode::Numpad0,
+ winit::event::VirtualKeyCode::Numpad1 => KeyCode::Numpad1,
+ winit::event::VirtualKeyCode::Numpad2 => KeyCode::Numpad2,
+ winit::event::VirtualKeyCode::Numpad3 => KeyCode::Numpad3,
+ winit::event::VirtualKeyCode::Numpad4 => KeyCode::Numpad4,
+ winit::event::VirtualKeyCode::Numpad5 => KeyCode::Numpad5,
+ winit::event::VirtualKeyCode::Numpad6 => KeyCode::Numpad6,
+ winit::event::VirtualKeyCode::Numpad7 => KeyCode::Numpad7,
+ winit::event::VirtualKeyCode::Numpad8 => KeyCode::Numpad8,
+ winit::event::VirtualKeyCode::Numpad9 => KeyCode::Numpad9,
+ winit::event::VirtualKeyCode::AbntC1 => KeyCode::AbntC1,
+ winit::event::VirtualKeyCode::AbntC2 => KeyCode::AbntC2,
+ winit::event::VirtualKeyCode::Add => KeyCode::Add,
+ winit::event::VirtualKeyCode::Apostrophe => KeyCode::Apostrophe,
+ winit::event::VirtualKeyCode::Apps => KeyCode::Apps,
+ winit::event::VirtualKeyCode::At => KeyCode::At,
+ winit::event::VirtualKeyCode::Ax => KeyCode::Ax,
+ winit::event::VirtualKeyCode::Backslash => KeyCode::Backslash,
+ winit::event::VirtualKeyCode::Calculator => KeyCode::Calculator,
+ winit::event::VirtualKeyCode::Capital => KeyCode::Capital,
+ winit::event::VirtualKeyCode::Colon => KeyCode::Colon,
+ winit::event::VirtualKeyCode::Comma => KeyCode::Comma,
+ winit::event::VirtualKeyCode::Convert => KeyCode::Convert,
+ winit::event::VirtualKeyCode::Decimal => KeyCode::Decimal,
+ winit::event::VirtualKeyCode::Divide => KeyCode::Divide,
+ winit::event::VirtualKeyCode::Equals => KeyCode::Equals,
+ winit::event::VirtualKeyCode::Grave => KeyCode::Grave,
+ winit::event::VirtualKeyCode::Kana => KeyCode::Kana,
+ winit::event::VirtualKeyCode::Kanji => KeyCode::Kanji,
+ winit::event::VirtualKeyCode::LAlt => KeyCode::LAlt,
+ winit::event::VirtualKeyCode::LBracket => KeyCode::LBracket,
+ winit::event::VirtualKeyCode::LControl => KeyCode::LControl,
+ winit::event::VirtualKeyCode::LShift => KeyCode::LShift,
+ winit::event::VirtualKeyCode::LWin => KeyCode::LWin,
+ winit::event::VirtualKeyCode::Mail => KeyCode::Mail,
+ winit::event::VirtualKeyCode::MediaSelect => KeyCode::MediaSelect,
+ winit::event::VirtualKeyCode::MediaStop => KeyCode::MediaStop,
+ winit::event::VirtualKeyCode::Minus => KeyCode::Minus,
+ winit::event::VirtualKeyCode::Multiply => KeyCode::Multiply,
+ winit::event::VirtualKeyCode::Mute => KeyCode::Mute,
+ winit::event::VirtualKeyCode::MyComputer => KeyCode::MyComputer,
+ winit::event::VirtualKeyCode::NavigateForward => {
+ KeyCode::NavigateForward
+ }
+ winit::event::VirtualKeyCode::NavigateBackward => {
+ KeyCode::NavigateBackward
+ }
+ winit::event::VirtualKeyCode::NextTrack => KeyCode::NextTrack,
+ winit::event::VirtualKeyCode::NoConvert => KeyCode::NoConvert,
+ winit::event::VirtualKeyCode::NumpadComma => KeyCode::NumpadComma,
+ winit::event::VirtualKeyCode::NumpadEnter => KeyCode::NumpadEnter,
+ winit::event::VirtualKeyCode::NumpadEquals => KeyCode::NumpadEquals,
+ winit::event::VirtualKeyCode::OEM102 => KeyCode::OEM102,
+ winit::event::VirtualKeyCode::Period => KeyCode::Period,
+ winit::event::VirtualKeyCode::PlayPause => KeyCode::PlayPause,
+ winit::event::VirtualKeyCode::Power => KeyCode::Power,
+ winit::event::VirtualKeyCode::PrevTrack => KeyCode::PrevTrack,
+ winit::event::VirtualKeyCode::RAlt => KeyCode::RAlt,
+ winit::event::VirtualKeyCode::RBracket => KeyCode::RBracket,
+ winit::event::VirtualKeyCode::RControl => KeyCode::RControl,
+ winit::event::VirtualKeyCode::RShift => KeyCode::RShift,
+ winit::event::VirtualKeyCode::RWin => KeyCode::RWin,
+ winit::event::VirtualKeyCode::Semicolon => KeyCode::Semicolon,
+ winit::event::VirtualKeyCode::Slash => KeyCode::Slash,
+ winit::event::VirtualKeyCode::Sleep => KeyCode::Sleep,
+ winit::event::VirtualKeyCode::Stop => KeyCode::Stop,
+ winit::event::VirtualKeyCode::Subtract => KeyCode::Subtract,
+ winit::event::VirtualKeyCode::Sysrq => KeyCode::Sysrq,
+ winit::event::VirtualKeyCode::Tab => KeyCode::Tab,
+ winit::event::VirtualKeyCode::Underline => KeyCode::Underline,
+ winit::event::VirtualKeyCode::Unlabeled => KeyCode::Unlabeled,
+ winit::event::VirtualKeyCode::VolumeDown => KeyCode::VolumeDown,
+ winit::event::VirtualKeyCode::VolumeUp => KeyCode::VolumeUp,
+ winit::event::VirtualKeyCode::Wake => KeyCode::Wake,
+ winit::event::VirtualKeyCode::WebBack => KeyCode::WebBack,
+ winit::event::VirtualKeyCode::WebFavorites => KeyCode::WebFavorites,
+ winit::event::VirtualKeyCode::WebForward => KeyCode::WebForward,
+ winit::event::VirtualKeyCode::WebHome => KeyCode::WebHome,
+ winit::event::VirtualKeyCode::WebRefresh => KeyCode::WebRefresh,
+ winit::event::VirtualKeyCode::WebSearch => KeyCode::WebSearch,
+ winit::event::VirtualKeyCode::WebStop => KeyCode::WebStop,
+ winit::event::VirtualKeyCode::Yen => KeyCode::Yen,
+ winit::event::VirtualKeyCode::Copy => KeyCode::Copy,
+ winit::event::VirtualKeyCode::Paste => KeyCode::Paste,
+ winit::event::VirtualKeyCode::Cut => KeyCode::Cut,
+ }
+ }
+}
diff --git a/src/input/mouse.rs b/src/input/mouse.rs
index 49a62961..d37f5b96 100644
--- a/src/input/mouse.rs
+++ b/src/input/mouse.rs
@@ -1,3 +1,4 @@
+//! Build mouse events.
mod button;
mod event;
diff --git a/src/input/mouse/button.rs b/src/input/mouse/button.rs
index c51bedfc..e552af5a 100644
--- a/src/input/mouse/button.rs
+++ b/src/input/mouse/button.rs
@@ -1,7 +1,27 @@
+/// The button of a mouse.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum Button {
+ /// The left mouse button.
Left,
+
+ /// The right mouse button.
Right,
+
+ /// The middle (wheel) button.
Middle,
+
+ /// Some other button.
Other(u8),
}
+
+#[cfg(feature = "winit")]
+impl From<winit::event::MouseButton> for super::Button {
+ fn from(mouse_button: winit::event::MouseButton) -> Self {
+ match mouse_button {
+ winit::event::MouseButton::Left => Button::Left,
+ winit::event::MouseButton::Right => Button::Right,
+ winit::event::MouseButton::Middle => Button::Middle,
+ winit::event::MouseButton::Other(other) => Button::Other(other),
+ }
+ }
+}
diff --git a/src/layout.rs b/src/layout.rs
index 481b4166..de284a43 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -5,13 +5,13 @@ use crate::{Point, Rectangle, Vector};
/// The computed bounds of a [`Node`] and its children.
///
/// This type is provided by the GUI runtime to [`Widget::on_event`] and
-/// [`Widget::draw`], describing the layout of the produced [`Node`] by
+/// [`Widget::draw`], describing the layout of the [`Node`] produced by
/// [`Widget::node`].
///
/// [`Node`]: struct.Node.html
-/// [`Widget::on_event`]: trait.Widget.html#method.on_event
-/// [`Widget::draw`]: trait.Widget.html#tymethod.draw
-/// [`Widget::node`]: trait.Widget.html#tymethod.node
+/// [`Widget::on_event`]: widget/trait.Widget.html#method.on_event
+/// [`Widget::draw`]: widget/trait.Widget.html#tymethod.draw
+/// [`Widget::node`]: widget/trait.Widget.html#tymethod.node
#[derive(Debug)]
pub struct Layout<'a> {
layout: &'a result::Layout,
@@ -39,9 +39,9 @@ impl<'a> Layout<'a> {
/// [`Node`].
///
/// [`Layout`]: struct.Layout.html
- /// [`Rectangle`]: ../../graphics/struct.Rectangle.html
+ /// [`Rectangle`]: struct.Rectangle.html
/// [`Node`]: struct.Node.html
- pub fn bounds(&self) -> Rectangle<f32> {
+ pub fn bounds(&self) -> Rectangle {
Rectangle {
x: self.position.x,
y: self.position.y,
diff --git a/src/lib.rs b/src/lib.rs
index 6ea3e60d..88a5e81e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,9 +1,203 @@
-//#![deny(missing_docs)]
-//#![deny(missing_debug_implementations)]
+//! Iced is a renderer-agnostic GUI library focused on simplicity and
+//! type-safety. Inspired by [Elm].
+//!
+//! # Features
+//! * Simple, easy-to-use, renderer-agnostic API
+//! * Responsive, flexbox-based layouting
+//! * Type-safe, reactive programming model
+//! * Built-in widgets
+//! * Custom widget support
+//!
+//! Check out the [repository] and the [examples] for more details!
+//!
+//! [examples]: https://github.com/hecrj/iced/tree/0.1.0/examples
+//! [repository]: https://github.com/hecrj/iced
+//!
+//! # Usage
+//! 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
+//! about
+//! * __View logic__ — a way to display your __state__ as widgets that
+//! may produce __messages__ on user interaction
+//! * __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 start by modelling the __state__ of our application:
+//!
+//! ```
+//! use iced::button;
+//!
+//! struct Counter {
+//! // The counter value
+//! value: i32,
+//!
+//! // The local state of the two buttons
+//! increment_button: button::State,
+//! decrement_button: button::State,
+//! }
+//! ```
+//!
+//! Next, we need to define the possible user interactions of our counter:
+//! the button presses. These interactions are our __messages__:
+//!
+//! ```
+//! #[derive(Debug, Clone, Copy)]
+//! pub enum Message {
+//! IncrementPressed,
+//! DecrementPressed,
+//! }
+//! ```
+//!
+//! Now, let's show the actual counter by putting it all together in our
+//! __view logic__:
+//!
+//! ```
+//! # use iced::button;
+//! #
+//! # struct Counter {
+//! # // The counter value
+//! # value: i32,
+//! #
+//! # // The local state of the two buttons
+//! # increment_button: button::State,
+//! # decrement_button: button::State,
+//! # }
+//! #
+//! # #[derive(Debug, Clone, Copy)]
+//! # pub enum Message {
+//! # IncrementPressed,
+//! # DecrementPressed,
+//! # }
+//! #
+//! # mod iced_wgpu {
+//! # use iced::{
+//! # button, text, text::HorizontalAlignment, text::VerticalAlignment,
+//! # MouseCursor, Node, Point, Rectangle, Style,
+//! # };
+//! #
+//! # pub struct Renderer {}
+//! #
+//! # impl button::Renderer for Renderer {
+//! # fn draw(
+//! # &mut self,
+//! # _cursor_position: Point,
+//! # _bounds: Rectangle,
+//! # _state: &button::State,
+//! # _label: &str,
+//! # _class: button::Class,
+//! # ) -> MouseCursor {
+//! # MouseCursor::OutOfBounds
+//! # }
+//! # }
+//! #
+//! # impl text::Renderer<[f32; 4]> for Renderer {
+//! # fn node(&self, style: Style, _content: &str, _size: f32) -> Node {
+//! # Node::new(style)
+//! # }
+//! #
+//! # fn draw(
+//! # &mut self,
+//! # _bounds: Rectangle,
+//! # _content: &str,
+//! # _size: f32,
+//! # _color: Option<[f32; 4]>,
+//! # _horizontal_alignment: HorizontalAlignment,
+//! # _vertical_alignment: VerticalAlignment,
+//! # ) {
+//! # }
+//! # }
+//! # }
+//! use iced::{Button, Column, Text};
+//! use iced_wgpu::Renderer; // Iced does not include a renderer! We need to bring our own!
+//!
+//! impl Counter {
+//! pub fn view(&mut self) -> Column<Message, Renderer> {
+//! // We use a column: a simple vertical layout
+//! Column::new()
+//! .push(
+//! // The increment button. We tell it to produce an
+//! // `IncrementPressed` message when pressed
+//! Button::new(&mut self.increment_button, "+")
+//! .on_press(Message::IncrementPressed),
+//! )
+//! .push(
+//! // We show the value of the counter here
+//! Text::new(&self.value.to_string()).size(50),
+//! )
+//! .push(
+//! // The decrement button. We tell it to produce a
+//! // `DecrementPressed` message when pressed
+//! Button::new(&mut self.decrement_button, "-")
+//! .on_press(Message::DecrementPressed),
+//! )
+//! }
+//! }
+//! ```
+//!
+//! Finally, we need to be able to react to any produced __messages__ and change
+//! our __state__ accordingly in our __update logic__:
+//!
+//! ```
+//! # use iced::button;
+//! #
+//! # struct Counter {
+//! # // The counter value
+//! # value: i32,
+//! #
+//! # // The local state of the two buttons
+//! # increment_button: button::State,
+//! # decrement_button: button::State,
+//! # }
+//! #
+//! # #[derive(Debug, Clone, Copy)]
+//! # pub enum Message {
+//! # IncrementPressed,
+//! # DecrementPressed,
+//! # }
+//! impl Counter {
+//! // ...
+//!
+//! pub fn update(&mut self, message: Message) {
+//! match message {
+//! Message::IncrementPressed => {
+//! self.value += 1;
+//! }
+//! Message::DecrementPressed => {
+//! self.value -= 1;
+//! }
+//! }
+//! }
+//! }
+//! ```
+//!
+//! 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
+//! __update logic__.
+//! 1. Draw the resulting user interface using our chosen __renderer__.
+//!
+//! Check out the [`UserInterface`] type to learn how to wire everything up!
+//!
+//! [Elm]: https://elm-lang.org/
+//! [The Elm Architecture]: https://guide.elm-lang.org/architecture/
+//! [documentation]: https://docs.rs/iced
+//! [examples]: https://github.com/hecrj/iced/tree/master/examples
+//! [`UserInterface`]: struct.UserInterface.html
+#![deny(missing_docs)]
+#![deny(missing_debug_implementations)]
#![deny(unused_results)]
#![deny(unsafe_code)]
#![deny(rust_2018_idioms)]
pub mod input;
+pub mod renderer;
pub mod widget;
mod element;
@@ -14,9 +208,8 @@ mod mouse_cursor;
mod node;
mod point;
mod rectangle;
-mod renderer;
-mod runtime;
mod style;
+mod user_interface;
mod vector;
#[doc(no_inline)]
@@ -30,8 +223,7 @@ pub use mouse_cursor::MouseCursor;
pub use node::Node;
pub use point::Point;
pub use rectangle::Rectangle;
-pub use renderer::Renderer;
-pub use runtime::{Interface, Runtime};
pub use style::{Align, Justify, Style};
-pub use vector::Vector;
+pub use user_interface::{Cache, UserInterface};
+pub(crate) use vector::Vector;
pub use widget::*;
diff --git a/src/mouse_cursor.rs b/src/mouse_cursor.rs
index f6a68c54..4ef6361a 100644
--- a/src/mouse_cursor.rs
+++ b/src/mouse_cursor.rs
@@ -19,3 +19,17 @@ pub enum MouseCursor {
/// The cursor is grabbing a widget.
Grabbing,
}
+
+#[cfg(feature = "winit")]
+impl From<MouseCursor> for winit::window::CursorIcon {
+ fn from(mouse_cursor: MouseCursor) -> winit::window::CursorIcon {
+ match mouse_cursor {
+ MouseCursor::OutOfBounds => winit::window::CursorIcon::Default,
+ MouseCursor::Idle => winit::window::CursorIcon::Default,
+ MouseCursor::Pointer => winit::window::CursorIcon::Hand,
+ MouseCursor::Working => winit::window::CursorIcon::Progress,
+ MouseCursor::Grab => winit::window::CursorIcon::Grab,
+ MouseCursor::Grabbing => winit::window::CursorIcon::Grabbing,
+ }
+ }
+}
diff --git a/src/node.rs b/src/node.rs
index 3e368ecb..1db10d7f 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -8,9 +8,9 @@ use crate::{Number, Size, Style};
/// runtime obtains a [`Node`] by calling [`Widget::node`].
///
/// [`Style`]: struct.Style.html
-/// [`Widget`]: trait.Widget.html
+/// [`Widget`]: widget/trait.Widget.html
/// [`Node`]: struct.Node.html
-/// [`Widget::node`]: trait.Widget.html#tymethod.node
+/// [`Widget::node`]: widget/trait.Widget.html#tymethod.node
/// [`Layout`]: struct.Layout.html
#[derive(Debug)]
pub struct Node(pub(crate) node::Node);
@@ -24,17 +24,6 @@ impl Node {
Self::with_children(style, Vec::new())
}
- /// Creates a new [`Node`] with the given [`Style`] and children.
- ///
- /// [`Node`]: struct.Node.html
- /// [`Style`]: struct.Style.html
- pub(crate) fn with_children(style: Style, children: Vec<Node>) -> Node {
- Node(node::Node::new(
- style.0,
- children.iter().map(|c| &c.0).collect(),
- ))
- }
-
/// Creates a new [`Node`] with the given [`Style`] and a measure function.
///
/// This type of node cannot have any children.
@@ -47,7 +36,7 @@ impl Node {
///
/// [`Node`]: struct.Node.html
/// [`Style`]: struct.Style.html
- /// [`Widget`]: trait.Widget.html
+ /// [`Widget`]: widget/trait.Widget.html
pub fn with_measure<F>(style: Style, measure: F) -> Node
where
F: 'static + Fn(Size<Number>) -> Size<f32>,
@@ -57,4 +46,15 @@ impl Node {
Box::new(move |size| Ok(measure(size))),
))
}
+
+ /// Creates a new [`Node`] with the given [`Style`] and children.
+ ///
+ /// [`Node`]: struct.Node.html
+ /// [`Style`]: struct.Style.html
+ pub fn with_children(style: Style, children: Vec<Node>) -> Node {
+ Node(node::Node::new(
+ style.0,
+ children.iter().map(|c| &c.0).collect(),
+ ))
+ }
}
diff --git a/src/point.rs b/src/point.rs
index dbb6027e..183998dd 100644
--- a/src/point.rs
+++ b/src/point.rs
@@ -1 +1,31 @@
-pub type Point = nalgebra::Point2<f32>;
+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/src/rectangle.rs b/src/rectangle.rs
index ca224dad..9f2a1350 100644
--- a/src/rectangle.rs
+++ b/src/rectangle.rs
@@ -1,25 +1,25 @@
use crate::Point;
-/// A generic rectangle.
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub struct Rectangle<T> {
+/// A rectangle.
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub struct Rectangle {
/// X coordinate of the top-left corner.
- pub x: T,
+ pub x: f32,
/// Y coordinate of the top-left corner.
- pub y: T,
+ pub y: f32,
/// Width of the rectangle.
- pub width: T,
+ pub width: f32,
/// Height of the rectangle.
- pub height: T,
+ pub height: f32,
}
-impl Rectangle<f32> {
+impl Rectangle {
/// Returns true if the given [`Point`] is contained in the [`Rectangle`].
///
- /// [`Point`]: type.Point.html
+ /// [`Point`]: struct.Point.html
/// [`Rectangle`]: struct.Rectangle.html
pub fn contains(&self, point: Point) -> bool {
self.x <= point.x
diff --git a/src/renderer.rs b/src/renderer.rs
index 86e93a06..b445190b 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -1,12 +1,42 @@
+//! Write your own renderer.
+//!
+//! There is not a common entrypoint or trait for a __renderer__ in Iced.
+//! Instead, every [`Widget`] constrains its generic `Renderer` type as
+//! necessary.
+//!
+//! This approach is flexible and composable. For instance, the
+//! [`Text`] widget only needs a [`text::Renderer`] while a [`Checkbox`] widget
+//! needs both a [`text::Renderer`] and a [`checkbox::Renderer`], reusing logic.
+//!
+//! In the end, a __renderer__ satisfying all the constraints is
+//! needed to build a [`UserInterface`].
+//!
+//! [`Widget`]: ../widget/trait.Widget.html
+//! [`UserInterface`]: ../struct.UserInterface.html
+//! [`Text`]: ../widget/text/struct.Text.html
+//! [`text::Renderer`]: ../widget/text/trait.Renderer.html
+//! [`Checkbox`]: ../widget/checkbox/struct.Checkbox.html
+//! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html
use crate::Layout;
-pub trait Renderer {
+/// A renderer able to graphically explain a [`Layout`].
+///
+/// [`Layout`]: ../struct.Layout.html
+pub trait Debugger {
+ /// The color type that will be used to configure the _explanation_.
+ ///
+ /// This is the type that will be asked in [`Element::explain`].
+ ///
+ /// [`Element::explain`]: ../struct.Element.html#method.explain
type Color: Copy;
/// Explains the [`Layout`] of an [`Element`] for debugging purposes.
///
/// This will be called when [`Element::explain`] has been used. It should
- /// _explain_ the [`Layout`] graphically.
+ /// _explain_ the given [`Layout`] graphically.
+ ///
+ /// A common approach consists in recursively rendering the bounds of the
+ /// [`Layout`] and its children.
///
/// [`Layout`]: struct.Layout.html
/// [`Element`]: struct.Element.html
diff --git a/src/runtime.rs b/src/runtime.rs
deleted file mode 100644
index 926e29ea..00000000
--- a/src/runtime.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-use crate::{input::mouse, Column, Element, Event, Layout, MouseCursor, Point};
-
-use std::hash::Hasher;
-use stretch::result;
-
-pub struct Runtime {
- cache: Cache,
- events: Vec<Event>,
- cursor_position: Point,
-}
-
-impl Runtime {
- pub fn new() -> Runtime {
- // We use this as a placeholder to initialize the cache.
- // This way, we can avoid the overhead of using an `Option`
- // in `compute`.
- let root: Element<'_, (), ()> = Column::new().into();
-
- let hasher = &mut crate::Hasher::default();
- root.hash(hasher);
-
- Runtime {
- cache: Cache {
- hash: hasher.finish(),
- layout: root.compute_layout(&()),
- },
- events: Vec::new(),
- cursor_position: Point::new(0.0, 0.0),
- }
- }
-
- pub fn on_event(&mut self, event: Event) {
- match event {
- Event::Mouse(mouse::Event::CursorMoved { x, y }) => {
- self.cursor_position = Point::new(x, y);
- }
- _ => {}
- }
-
- self.events.push(event);
- }
-
- pub fn compute<'a, Message, Renderer>(
- &'a mut self,
- root: Element<'a, Message, Renderer>,
- renderer: &Renderer,
- ) -> Interface<'a, Message, Renderer> {
- let hasher = &mut crate::Hasher::default();
- root.hash(hasher);
-
- let hash = hasher.finish();
-
- if hash != self.cache.hash {
- self.cache = Cache {
- hash,
- layout: root.compute_layout(renderer),
- };
- }
-
- Interface {
- root,
- layout: &self.cache.layout,
- events: &mut self.events,
- cursor_position: self.cursor_position,
- }
- }
-}
-
-struct Cache {
- hash: u64,
- layout: result::Layout,
-}
-
-pub struct Interface<'a, Message, Renderer> {
- root: Element<'a, Message, Renderer>,
- layout: &'a result::Layout,
- events: &'a mut Vec<Event>,
- cursor_position: Point,
-}
-
-impl<'a, Message, Renderer> Interface<'a, Message, Renderer> {
- pub fn update(&mut self) -> Vec<Message> {
- let mut messages = Vec::new();
-
- for event in self.events.drain(..) {
- self.root.widget.on_event(
- event,
- Layout::new(&self.layout),
- self.cursor_position,
- &mut messages,
- );
- }
-
- messages
- }
-
- pub fn draw(&self, renderer: &mut Renderer) -> MouseCursor {
- let cursor = self.root.widget.draw(
- renderer,
- Layout::new(self.layout),
- self.cursor_position,
- );
-
- cursor
- }
-}
diff --git a/src/user_interface.rs b/src/user_interface.rs
new file mode 100644
index 00000000..2c7cbf82
--- /dev/null
+++ b/src/user_interface.rs
@@ -0,0 +1,323 @@
+use crate::{input::mouse, Column, Element, Event, Layout, MouseCursor, Point};
+
+use std::hash::Hasher;
+use stretch::result;
+
+/// A set of interactive graphical elements with a specific [`Layout`].
+///
+/// It can be updated and drawn.
+///
+/// Iced tries to avoid dictating how to write your event loop. You are in
+/// 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>,
+ layout: result::Layout,
+ cursor_position: Point,
+}
+
+impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer> {
+ /// Builds a user interface for an [`Element`].
+ ///
+ /// It is able to avoid expensive computations when using a [`Cache`]
+ /// obtained from a previous instance of a [`UserInterface`].
+ ///
+ /// [`Element`]: struct.Element.html
+ /// [`Cache`]: struct.Cache.html
+ /// [`UserInterface`]: struct.UserInterface.html
+ ///
+ /// # Example
+ /// Imagine we want to build a [`UserInterface`] for
+ /// [the counter example that we previously wrote](index.html#usage). Here
+ /// is naive way to set up our application loop:
+ ///
+ /// ```no_run
+ /// use iced::{UserInterface, Cache};
+ /// use iced_wgpu::Renderer;
+ ///
+ /// # mod iced_wgpu {
+ /// # pub struct Renderer;
+ /// #
+ /// # impl Renderer {
+ /// # pub fn new() -> Self { Renderer }
+ /// # }
+ /// # }
+ /// #
+ /// # use iced::Column;
+ /// #
+ /// # pub struct Counter;
+ /// #
+ /// # impl Counter {
+ /// # pub fn new() -> Self { Counter }
+ /// # pub fn view(&self) -> Column<(), Renderer> {
+ /// # Column::new()
+ /// # }
+ /// # }
+ /// // Initialization
+ /// let mut counter = Counter::new();
+ /// let mut cache = Cache::new();
+ /// let mut renderer = Renderer::new();
+ ///
+ /// // Application loop
+ /// loop {
+ /// // Process system events here...
+ ///
+ /// // Build the user interface
+ /// let user_interface = UserInterface::build(
+ /// counter.view(),
+ /// cache,
+ /// &renderer,
+ /// );
+ ///
+ /// // Update and draw the user interface here...
+ /// // ...
+ ///
+ /// // Obtain the cache for the next iteration
+ /// cache = user_interface.into_cache();
+ /// }
+ /// ```
+ pub fn build<E: Into<Element<'a, Message, Renderer>>>(
+ root: E,
+ cache: Cache,
+ renderer: &Renderer,
+ ) -> Self {
+ let root = root.into();
+
+ let hasher = &mut crate::Hasher::default();
+ root.hash_layout(hasher);
+
+ let hash = hasher.finish();
+
+ let layout = if hash == cache.hash {
+ cache.layout
+ } else {
+ root.compute_layout(renderer)
+ };
+
+ UserInterface {
+ hash,
+ root,
+ layout,
+ cursor_position: cache.cursor_position,
+ }
+ }
+
+ /// Updates the [`UserInterface`] by processing each provided [`Event`].
+ ///
+ /// It returns __messages__ that may have been produced as a result of user
+ /// interactions. You should feed these to your __update logic__.
+ ///
+ /// [`UserInterface`]: struct.UserInterface.html
+ /// [`Event`]: enum.Event.html
+ ///
+ /// # Example
+ /// Let's allow our [counter](index.html#usage) to change state by completing
+ /// [the previous example](#example):
+ ///
+ /// ```no_run
+ /// use iced::{UserInterface, Cache};
+ /// use iced_wgpu::Renderer;
+ ///
+ /// # mod iced_wgpu {
+ /// # pub struct Renderer;
+ /// #
+ /// # impl Renderer {
+ /// # pub fn new() -> Self { Renderer }
+ /// # }
+ /// # }
+ /// #
+ /// # use iced::Column;
+ /// #
+ /// # pub struct Counter;
+ /// #
+ /// # impl Counter {
+ /// # pub fn new() -> Self { Counter }
+ /// # pub fn view(&self) -> Column<(), Renderer> {
+ /// # Column::new()
+ /// # }
+ /// # pub fn update(&mut self, message: ()) {}
+ /// # }
+ /// let mut counter = Counter::new();
+ /// let mut cache = Cache::new();
+ /// let mut renderer = Renderer::new();
+ ///
+ /// // Initialize our event storage
+ /// let mut events = Vec::new();
+ ///
+ /// loop {
+ /// // Process system events...
+ ///
+ /// let mut user_interface = UserInterface::build(
+ /// counter.view(),
+ /// cache,
+ /// &renderer,
+ /// );
+ ///
+ /// // Update the user interface
+ /// let messages = user_interface.update(events.drain(..));
+ ///
+ /// cache = user_interface.into_cache();
+ ///
+ /// // Process the produced messages
+ /// for message in messages {
+ /// counter.update(message);
+ /// }
+ /// }
+ /// ```
+ pub fn update(
+ &mut self,
+ events: impl Iterator<Item = Event>,
+ ) -> Vec<Message> {
+ let mut messages = Vec::new();
+
+ for event in events {
+ if let Event::Mouse(mouse::Event::CursorMoved { x, y }) = event {
+ self.cursor_position = Point::new(x, y);
+ }
+
+ self.root.widget.on_event(
+ event,
+ Layout::new(&self.layout),
+ self.cursor_position,
+ &mut messages,
+ );
+ }
+
+ messages
+ }
+
+ /// Draws the [`UserInterface`] with the provided [`Renderer`].
+ ///
+ /// It returns the current state of the [`MouseCursor`]. You should update
+ /// the icon of the mouse cursor accordingly in your system.
+ ///
+ /// [`UserInterface`]: struct.UserInterface.html
+ /// [`Renderer`]: trait.Renderer.html
+ /// [`MouseCursor`]: enum.MouseCursor.html
+ ///
+ /// # Example
+ /// We can finally draw our [counter](index.html#usage) by
+ /// [completing the last example](#example-1):
+ ///
+ /// ```no_run
+ /// use iced::{UserInterface, Cache};
+ /// use iced_wgpu::Renderer;
+ ///
+ /// # mod iced_wgpu {
+ /// # pub struct Renderer;
+ /// #
+ /// # impl Renderer {
+ /// # pub fn new() -> Self { Renderer }
+ /// # }
+ /// # }
+ /// #
+ /// # use iced::Column;
+ /// #
+ /// # pub struct Counter;
+ /// #
+ /// # impl Counter {
+ /// # pub fn new() -> Self { Counter }
+ /// # pub fn view(&self) -> Column<(), Renderer> {
+ /// # Column::new()
+ /// # }
+ /// # pub fn update(&mut self, message: ()) {}
+ /// # }
+ /// let mut counter = Counter::new();
+ /// let mut cache = Cache::new();
+ /// let mut renderer = Renderer::new();
+ /// let mut events = Vec::new();
+ ///
+ /// loop {
+ /// // Process system events...
+ ///
+ /// let mut user_interface = UserInterface::build(
+ /// counter.view(),
+ /// cache,
+ /// &renderer,
+ /// );
+ ///
+ /// let messages = user_interface.update(events.drain(..));
+ ///
+ /// // Draw the user interface
+ /// let mouse_cursor = user_interface.draw(&mut renderer);
+ ///
+ /// cache = user_interface.into_cache();
+ ///
+ /// for message in messages {
+ /// counter.update(message);
+ /// }
+ ///
+ /// // Update mouse cursor icon...
+ /// // Flush rendering operations...
+ /// }
+ /// ```
+ pub fn draw(&self, renderer: &mut Renderer) -> MouseCursor {
+ self.root.widget.draw(
+ renderer,
+ Layout::new(&self.layout),
+ self.cursor_position,
+ )
+ }
+
+ /// Extract the [`Cache`] of the [`UserInterface`], consuming it in the
+ /// process.
+ ///
+ /// [`Cache`]: struct.Cache.html
+ /// [`UserInterface`]: struct.UserInterface.html
+ pub fn into_cache(self) -> Cache {
+ Cache {
+ hash: self.hash,
+ layout: self.layout,
+ cursor_position: self.cursor_position,
+ }
+ }
+}
+
+/// Reusable data of a specific [`UserInterface`].
+///
+/// [`UserInterface`]: struct.UserInterface.html
+#[derive(Debug, Clone)]
+pub struct Cache {
+ hash: u64,
+ layout: result::Layout,
+ cursor_position: Point,
+}
+
+impl Cache {
+ /// Creates an empty [`Cache`].
+ ///
+ /// You should use this to initialize a [`Cache`] before building your first
+ /// [`UserInterface`].
+ ///
+ /// [`Cache`]: struct.Cache.html
+ /// [`UserInterface`]: struct.UserInterface.html
+ pub fn new() -> Cache {
+ let root: Element<'_, (), ()> = Column::new().into();
+
+ let hasher = &mut crate::Hasher::default();
+ root.hash_layout(hasher);
+
+ Cache {
+ hash: hasher.finish(),
+ layout: root.compute_layout(&()),
+ cursor_position: Point::new(0.0, 0.0),
+ }
+ }
+}
+
+impl Default for Cache {
+ fn default() -> Cache {
+ Cache::new()
+ }
+}
+
+impl PartialEq for Cache {
+ fn eq(&self, other: &Cache) -> bool {
+ self.hash == other.hash && self.cursor_position == other.cursor_position
+ }
+}
+
+impl Eq for Cache {}
diff --git a/src/vector.rs b/src/vector.rs
index 12f1f082..f45daab9 100644
--- a/src/vector.rs
+++ b/src/vector.rs
@@ -1,2 +1,15 @@
/// A 2D vector.
-pub type Vector = nalgebra::Vector2<f32>;
+#[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/src/widget.rs b/src/widget.rs
index 17ff50b6..b8ecb409 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -1,16 +1,25 @@
-//! Use the built-in widgets in your user interface.
+//! Use the built-in widgets or create your own.
//!
-//! # Customization
-//! Every drawable widget has its own module with a `Renderer` trait that must
-//! be implemented by a custom renderer before being able to use the
-//! widget.
+//! # Built-in widgets
+//! Every built-in drawable widget has its own module with a `Renderer` trait
+//! that must be implemented by a [renderer] before being able to use it as
+//! a [`Widget`].
//!
-//! The built-in [`Renderer`] supports all the widgets in this module!
+//! # Custom widgets
+//! If you want to implement a custom widget, you simply need to implement the
+//! [`Widget`] trait. You can use the API of the built-in widgets as a guide or
+//! source of inspiration.
//!
-//! [`ui` module]: ../index.html
-//! [`Row`]: struct.Row.html
-//! [`Column`]: struct.Column.html
-//! [`Renderer`]: ../struct.Renderer.html
+//! # Re-exports
+//! For convenience, the contents of this module are available at the root
+//! module. Therefore, you can directly type:
+//!
+//! ```
+//! use iced::{button, Button, Widget};
+//! ```
+//!
+//! [`Widget`]: trait.Widget.html
+//! [renderer]: ../renderer/index.html
mod column;
mod row;
@@ -34,24 +43,22 @@ pub use text::Text;
use crate::{Event, Hasher, Layout, MouseCursor, Node, Point};
-/// A component that displays information or allows interaction.
+/// A component that displays information and allows interaction.
///
-/// If you want to build a custom widget, you will need to implement this trait.
-/// Additionally, remember to also provide [`Into<Element>`] so your users can
-/// easily turn your [`Widget`] into a generic [`Element`]
+/// If you want to build your own widgets, you will need to implement this
+/// trait.
///
-/// [`Into<Element>`]: struct.Element.html
/// [`Widget`]: trait.Widget.html
-/// [`Element`]: struct.Element.html
+/// [`Element`]: ../struct.Element.html
pub trait Widget<Message, Renderer>: std::fmt::Debug {
/// Returns the [`Node`] of the [`Widget`].
///
/// This [`Node`] is used by the runtime to compute the [`Layout`] of the
/// user interface.
///
- /// [`Node`]: struct.Node.html
+ /// [`Node`]: ../struct.Node.html
/// [`Widget`]: trait.Widget.html
- /// [`Layout`]: struct.Layout.html
+ /// [`Layout`]: ../struct.Layout.html
fn node(&self, renderer: &Renderer) -> Node;
/// Draws the [`Widget`] using the associated `Renderer`.
@@ -59,7 +66,7 @@ pub trait Widget<Message, Renderer>: std::fmt::Debug {
/// It must return the [`MouseCursor`] state for the [`Widget`].
///
/// [`Widget`]: trait.Widget.html
- /// [`MouseCursor`]: enum.MouseCursor.html
+ /// [`MouseCursor`]: ../enum.MouseCursor.html
fn draw(
&self,
renderer: &mut Renderer,
@@ -78,9 +85,9 @@ pub trait Widget<Message, Renderer>: std::fmt::Debug {
/// its value cannot affect the overall [`Layout`] of the user interface.
///
/// [`Widget`]: trait.Widget.html
- /// [`Layout`]: struct.Layout.html
- /// [`Text`]: ../widget/text/struct.Text.html
- fn hash(&self, state: &mut Hasher);
+ /// [`Layout`]: ../struct.Layout.html
+ /// [`Text`]: text/struct.Text.html
+ fn hash_layout(&self, state: &mut Hasher);
/// Processes a runtime [`Event`].
///
@@ -88,14 +95,14 @@ pub trait Widget<Message, Renderer>: std::fmt::Debug {
/// * an [`Event`] describing user interaction
/// * the computed [`Layout`] of the [`Widget`]
/// * the current cursor position
- /// * a mutable `Message` vector, allowing the [`Widget`] to produce
+ /// * a mutable `Message` list, allowing the [`Widget`] to produce
/// new messages based on user interaction.
///
/// By default, it does nothing.
///
- /// [`Event`]: enum.Event.html
+ /// [`Event`]: ../enum.Event.html
/// [`Widget`]: trait.Widget.html
- /// [`Layout`]: struct.Layout.html
+ /// [`Layout`]: ../struct.Layout.html
fn on_event(
&mut self,
_event: Event,
diff --git a/src/widget/button.rs b/src/widget/button.rs
index 35fa6ca8..14fd3852 100644
--- a/src/widget/button.rs
+++ b/src/widget/button.rs
@@ -16,11 +16,10 @@ use std::hash::Hash;
/// A generic widget that produces a message when clicked.
///
-/// It implements [`Widget`] when the associated [`core::Renderer`] implements
-/// the [`button::Renderer`] trait.
+/// It implements [`Widget`] when the associated `Renderer` implements the
+/// [`button::Renderer`] trait.
///
-/// [`Widget`]: ../../core/trait.Widget.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [`Widget`]: ../trait.Widget.html
/// [`button::Renderer`]: trait.Renderer.html
///
/// # Example
@@ -37,6 +36,8 @@ use std::hash::Hash;
/// Button::new(state, "Click me!")
/// .on_press(Message::ButtonClicked);
/// ```
+///
+/// ![Button drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/button.png?raw=true)
pub struct Button<'a, Message> {
state: &'a mut State,
label: String,
@@ -186,7 +187,7 @@ where
)
}
- fn hash(&self, state: &mut Hasher) {
+ fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
}
}
@@ -218,7 +219,7 @@ impl State {
/// The type of a [`Button`].
///
-/// ![Different buttons drawn by the built-in renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/button_classes.png?raw=true)
+/// ![Different buttons drawn by the built-in renderer in Coffee](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/button_classes.png?raw=true)
///
/// [`Button`]: struct.Button.html
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -241,11 +242,11 @@ pub enum Class {
/// The renderer of a [`Button`].
///
-/// Your [`core::Renderer`] will need to implement this trait before being
+/// Your [renderer] will need to implement this trait before being
/// able to use a [`Button`] in your user interface.
///
/// [`Button`]: struct.Button.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Button`].
///
@@ -262,7 +263,7 @@ pub trait Renderer {
fn draw(
&mut self,
cursor_position: Point,
- bounds: Rectangle<f32>,
+ bounds: Rectangle,
state: &State,
label: &str,
class: Class,
diff --git a/src/widget/checkbox.rs b/src/widget/checkbox.rs
index c30b8308..b9a40a37 100644
--- a/src/widget/checkbox.rs
+++ b/src/widget/checkbox.rs
@@ -8,13 +8,12 @@ use crate::{
Widget,
};
-/// A box that can be checked.
+/// A box that can be checked, with a generic text `Color`.
///
-/// It implements [`Widget`] when the [`core::Renderer`] implements the
+/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`checkbox::Renderer`] trait.
///
-/// [`Widget`]: ../../core/trait.Widget.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [`Widget`]: ../trait.Widget.html
/// [`checkbox::Renderer`]: trait.Renderer.html
///
/// # Example
@@ -25,29 +24,24 @@ use crate::{
/// #[derive(Debug, Clone, Copy)]
/// pub enum Color {
/// Black,
-/// White,
-/// }
-///
-/// impl Default for Color {
-/// fn default() -> Color {
-/// Color::Black
-/// }
/// }
///
/// pub enum Message {
/// CheckboxToggled(bool),
/// }
///
-/// fn some_checkbox(is_checked: bool) -> Checkbox<Color, Message> {
-/// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled)
-/// .label_color(Color::White)
-/// }
+/// let is_checked = true;
+///
+/// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled)
+/// .label_color(Color::Black);
/// ```
+///
+/// ![Checkbox drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/checkbox.png?raw=true)
pub struct Checkbox<Color, Message> {
is_checked: bool,
on_toggle: Box<dyn Fn(bool) -> Message>,
label: String,
- label_color: Color,
+ label_color: Option<Color>,
}
impl<Color, Message> std::fmt::Debug for Checkbox<Color, Message>
@@ -63,10 +57,7 @@ where
}
}
-impl<Color, Message> Checkbox<Color, Message>
-where
- Color: Default,
-{
+impl<Color, Message> Checkbox<Color, Message> {
/// Creates a new [`Checkbox`].
///
/// It expects:
@@ -85,16 +76,15 @@ where
is_checked,
on_toggle: Box::new(f),
label: String::from(label),
- label_color: Color::default(),
+ label_color: None,
}
}
- /// Sets the [`Color`] of the label of the [`Checkbox`].
+ /// Sets the `Color` of the label of the [`Checkbox`].
///
- /// [`Color`]: ../../../../graphics/struct.Color.html
/// [`Checkbox`]: struct.Checkbox.html
pub fn label_color(mut self, color: Color) -> Self {
- self.label_color = color;
+ self.label_color = Some(color);
self
}
}
@@ -102,7 +92,7 @@ where
impl<Color, Message, Renderer> Widget<Message, Renderer>
for Checkbox<Color, Message>
where
- Color: 'static + Copy + Default + std::fmt::Debug,
+ Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
{
fn node(&self, renderer: &Renderer) -> Node {
@@ -167,18 +157,18 @@ where
)
}
- fn hash(&self, state: &mut Hasher) {
+ fn hash_layout(&self, state: &mut Hasher) {
self.label.hash(state);
}
}
/// The renderer of a [`Checkbox`].
///
-/// Your [`core::Renderer`] will need to implement this trait before being
+/// Your [renderer] will need to implement this trait before being
/// able to use a [`Checkbox`] in your user interface.
///
/// [`Checkbox`]: struct.Checkbox.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Checkbox`].
///
@@ -192,8 +182,8 @@ pub trait Renderer {
fn draw(
&mut self,
cursor_position: Point,
- bounds: Rectangle<f32>,
- label_bounds: Rectangle<f32>,
+ bounds: Rectangle,
+ label_bounds: Rectangle,
is_checked: bool,
) -> MouseCursor;
}
@@ -201,7 +191,7 @@ pub trait Renderer {
impl<'a, Color, Message, Renderer> From<Checkbox<Color, Message>>
for Element<'a, Message, Renderer>
where
- Color: 'static + Copy + Default + std::fmt::Debug,
+ Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
Message: 'static,
{
diff --git a/src/widget/column.rs b/src/widget/column.rs
index 18a68f60..903de897 100644
--- a/src/widget/column.rs
+++ b/src/widget/column.rs
@@ -5,11 +5,12 @@ use crate::{
Style, Widget,
};
-/// A container that places its contents vertically.
+/// A container that distributes its contents vertically.
///
/// A [`Column`] will try to fill the horizontal space of its container.
///
/// [`Column`]: struct.Column.html
+#[derive(Default)]
pub struct Column<'a, Message, Renderer> {
style: Style,
spacing: u16,
@@ -43,7 +44,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
/// Sets the vertical spacing _between_ elements in pixels.
///
- /// Custom margins per element do not exist in Coffee. You should use this
+ /// 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, px: u16) -> Self {
@@ -121,7 +122,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
/// Adds an [`Element`] to the [`Column`].
///
- /// [`Element`]: ../core/struct.Element.html
+ /// [`Element`]: ../struct.Element.html
/// [`Column`]: struct.Column.html
pub fn push<E>(mut self, child: E) -> Column<'a, Message, Renderer>
where
@@ -144,7 +145,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
let mut style = node.0.style();
style.margin.bottom =
- stretch::style::Dimension::Points(self.spacing as f32);
+ stretch::style::Dimension::Points(f32::from(self.spacing));
node.0.set_style(style);
node
@@ -199,12 +200,12 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
cursor
}
- fn hash(&self, state: &mut Hasher) {
+ fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
self.spacing.hash(state);
for child in &self.children {
- child.widget.hash(state);
+ child.widget.hash_layout(state);
}
}
}
diff --git a/src/widget/panel.rs b/src/widget/panel.rs
index 28e5989c..d43d6fb6 100644
--- a/src/widget/panel.rs
+++ b/src/widget/panel.rs
@@ -90,5 +90,5 @@ where
}
pub trait Renderer {
- fn draw(&mut self, bounds: Rectangle<f32>);
+ fn draw(&mut self, bounds: Rectangle);
}
diff --git a/src/widget/radio.rs b/src/widget/radio.rs
index 8a678ec8..a59d52aa 100644
--- a/src/widget/radio.rs
+++ b/src/widget/radio.rs
@@ -8,13 +8,12 @@ use crate::{
use std::hash::Hash;
-/// A circular button representing a choice.
+/// A circular button representing a choice, with a generic text `Color`.
///
-/// It implements [`Widget`] when the [`core::Renderer`] implements the
+/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`radio::Renderer`] trait.
///
-/// [`Widget`]: ../../core/trait.Widget.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [`Widget`]: ../trait.Widget.html
/// [`radio::Renderer`]: trait.Renderer.html
///
/// # Example
@@ -26,12 +25,6 @@ use std::hash::Hash;
/// Black,
/// }
///
-/// impl Default for Color {
-/// fn default() -> Color {
-/// Color::Black
-/// }
-/// }
-///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// pub enum Choice {
/// A,
@@ -45,16 +38,19 @@ use std::hash::Hash;
///
/// let selected_choice = Some(Choice::A);
///
-/// Radio::<Color, Message>::new(Choice::A, "This is A", selected_choice, Message::RadioSelected)
+/// Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected)
+/// .label_color(Color::Black);
+///
+/// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected)
/// .label_color(Color::Black);
/// ```
///
-/// ![Checkbox drawn by the built-in renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/radio.png?raw=true)
+/// ![Radio buttons drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/radio.png?raw=true)
pub struct Radio<Color, Message> {
is_selected: bool,
on_click: Message,
label: String,
- label_color: Color,
+ label_color: Option<Color>,
}
impl<Color, Message> std::fmt::Debug for Radio<Color, Message>
@@ -72,10 +68,7 @@ where
}
}
-impl<Color, Message> Radio<Color, Message>
-where
- Color: Default,
-{
+impl<Color, Message> Radio<Color, Message> {
/// Creates a new [`Radio`] button.
///
/// It expects:
@@ -95,16 +88,15 @@ where
is_selected: Some(value) == selected,
on_click: f(value),
label: String::from(label),
- label_color: Color::default(),
+ label_color: None,
}
}
- /// Sets the [`Color`] of the label of the [`Radio`].
+ /// Sets the `Color` of the label of the [`Radio`].
///
- /// [`Color`]: ../../../../graphics/struct.Color.html
/// [`Radio`]: struct.Radio.html
pub fn label_color(mut self, color: Color) -> Self {
- self.label_color = color;
+ self.label_color = Some(color);
self
}
}
@@ -112,7 +104,7 @@ where
impl<Color, Message, Renderer> Widget<Message, Renderer>
for Radio<Color, Message>
where
- Color: 'static + Copy + Default + std::fmt::Debug,
+ Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
Message: Copy + std::fmt::Debug,
{
@@ -175,18 +167,18 @@ where
)
}
- fn hash(&self, state: &mut Hasher) {
+ fn hash_layout(&self, state: &mut Hasher) {
self.label.hash(state);
}
}
/// The renderer of a [`Radio`] button.
///
-/// Your [`core::Renderer`] will need to implement this trait before being
+/// Your [renderer] will need to implement this trait before being
/// able to use a [`Radio`] button in your user interface.
///
/// [`Radio`]: struct.Radio.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Radio`] button.
///
@@ -200,8 +192,8 @@ pub trait Renderer {
fn draw(
&mut self,
cursor_position: Point,
- bounds: Rectangle<f32>,
- label_bounds: Rectangle<f32>,
+ bounds: Rectangle,
+ label_bounds: Rectangle,
is_selected: bool,
) -> MouseCursor;
}
@@ -209,7 +201,7 @@ pub trait Renderer {
impl<'a, Color, Message, Renderer> From<Radio<Color, Message>>
for Element<'a, Message, Renderer>
where
- Color: 'static + Copy + Default + std::fmt::Debug,
+ Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
Message: 'static + Copy + std::fmt::Debug,
{
diff --git a/src/widget/row.rs b/src/widget/row.rs
index 9e0f9d4c..7b7033a1 100644
--- a/src/widget/row.rs
+++ b/src/widget/row.rs
@@ -5,11 +5,12 @@ use crate::{
Style, Widget,
};
-/// A container that places its contents horizontally.
+/// A container that distributes its contents horizontally.
///
/// A [`Row`] will try to fill the horizontal space of its container.
///
/// [`Row`]: struct.Row.html
+#[derive(Default)]
pub struct Row<'a, Message, Renderer> {
style: Style,
spacing: u16,
@@ -40,7 +41,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
/// Sets the horizontal spacing _between_ elements in pixels.
///
- /// Custom margins per element do not exist in Coffee. You should use this
+ /// 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, px: u16) -> Self {
@@ -118,7 +119,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
/// Adds an [`Element`] to the [`Row`].
///
- /// [`Element`]: ../core/struct.Element.html
+ /// [`Element`]: ../struct.Element.html
/// [`Row`]: struct.Row.html
pub fn push<E>(mut self, child: E) -> Row<'a, Message, Renderer>
where
@@ -141,7 +142,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
let mut style = node.0.style();
style.margin.end =
- stretch::style::Dimension::Points(self.spacing as f32);
+ stretch::style::Dimension::Points(f32::from(self.spacing));
node.0.set_style(style);
node
@@ -196,12 +197,12 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
cursor
}
- fn hash(&self, state: &mut Hasher) {
+ fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
self.spacing.hash(state);
for child in &self.children {
- child.widget.hash(state);
+ child.widget.hash_layout(state);
}
}
}
diff --git a/src/widget/slider.rs b/src/widget/slider.rs
index 75a0fdaf..c7adbb51 100644
--- a/src/widget/slider.rs
+++ b/src/widget/slider.rs
@@ -18,12 +18,11 @@ use crate::{
///
/// A [`Slider`] will try to fill the horizontal space of its container.
///
-/// It implements [`Widget`] when the associated [`core::Renderer`] implements
-/// the [`slider::Renderer`] trait.
+/// It implements [`Widget`] when the associated `Renderer` implements the
+/// [`slider::Renderer`] trait.
///
/// [`Slider`]: struct.Slider.html
-/// [`Widget`]: ../../core/trait.Widget.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [`Widget`]: ../trait.Widget.html
/// [`slider::Renderer`]: trait.Renderer.html
///
/// # Example
@@ -39,6 +38,8 @@ use crate::{
///
/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged);
/// ```
+///
+/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)
pub struct Slider<'a, Message> {
state: &'a mut State,
range: RangeInclusive<f32>,
@@ -168,7 +169,7 @@ where
)
}
- fn hash(&self, state: &mut Hasher) {
+ fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
}
}
@@ -200,11 +201,11 @@ impl State {
/// The renderer of a [`Slider`].
///
-/// Your [`core::Renderer`] will need to implement this trait before being
+/// Your [renderer] will need to implement this trait before being
/// able to use a [`Slider`] in your user interface.
///
/// [`Slider`]: struct.Slider.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Slider`].
///
@@ -221,7 +222,7 @@ pub trait Renderer {
fn draw(
&mut self,
cursor_position: Point,
- bounds: Rectangle<f32>,
+ bounds: Rectangle,
state: &State,
range: RangeInclusive<f32>,
value: f32,
diff --git a/src/widget/text.rs b/src/widget/text.rs
index e1ce8b16..7b62e5cb 100644
--- a/src/widget/text.rs
+++ b/src/widget/text.rs
@@ -5,13 +5,12 @@ use crate::{
use std::hash::Hash;
-/// A fragment of text.
+/// A fragment of text with a generic `Color`.
///
-/// It implements [`Widget`] when the associated [`core::Renderer`] implements
-/// the [`text::Renderer`] trait.
+/// It implements [`Widget`] when the associated `Renderer` implements the
+/// [`text::Renderer`] trait.
///
-/// [`Widget`]: ../../core/trait.Widget.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [`Widget`]: ../trait.Widget.html
/// [`text::Renderer`]: trait.Renderer.html
///
/// # Example
@@ -19,24 +18,26 @@ use std::hash::Hash;
/// ```
/// use iced::Text;
///
-/// Text::<(f32, f32, f32)>::new("I <3 iced!")
+/// #[derive(Debug, Clone, Copy)]
+/// pub enum Color {
+/// Black,
+/// }
+///
+/// Text::new("I <3 iced!")
/// .size(40)
-/// .color((0.0, 0.0, 1.0));
+/// .color(Color::Black);
/// ```
#[derive(Debug, Clone)]
pub struct Text<Color> {
content: String,
size: u16,
- color: Color,
+ color: Option<Color>,
style: Style,
horizontal_alignment: HorizontalAlignment,
vertical_alignment: VerticalAlignment,
}
-impl<Color> Text<Color>
-where
- Color: Default,
-{
+impl<Color> Text<Color> {
/// Create a new fragment of [`Text`] with the given contents.
///
/// [`Text`]: struct.Text.html
@@ -44,7 +45,7 @@ where
Text {
content: String::from(label),
size: 20,
- color: Color::default(),
+ color: None,
style: Style::default().fill_width(),
horizontal_alignment: HorizontalAlignment::Left,
vertical_alignment: VerticalAlignment::Top,
@@ -59,12 +60,11 @@ where
self
}
- /// Sets the [`Color`] of the [`Text`].
+ /// Sets the `Color` of the [`Text`].
///
/// [`Text`]: struct.Text.html
- /// [`Color`]: ../../../graphics/struct.Color.html
pub fn color(mut self, color: Color) -> Self {
- self.color = color;
+ self.color = Some(color);
self
}
@@ -87,7 +87,7 @@ where
/// Sets the [`HorizontalAlignment`] of the [`Text`].
///
/// [`Text`]: struct.Text.html
- /// [`HorizontalAlignment`]: ../../../graphics/enum.HorizontalAlignment.html
+ /// [`HorizontalAlignment`]: enum.HorizontalAlignment.html
pub fn horizontal_alignment(
mut self,
alignment: HorizontalAlignment,
@@ -99,7 +99,7 @@ where
/// Sets the [`VerticalAlignment`] of the [`Text`].
///
/// [`Text`]: struct.Text.html
- /// [`VerticalAlignment`]: ../../../graphics/enum.VerticalAlignment.html
+ /// [`VerticalAlignment`]: enum.VerticalAlignment.html
pub fn vertical_alignment(mut self, alignment: VerticalAlignment) -> Self {
self.vertical_alignment = alignment;
self
@@ -112,7 +112,7 @@ where
Renderer: self::Renderer<Color>,
{
fn node(&self, renderer: &Renderer) -> Node {
- renderer.node(self.style, &self.content, self.size as f32)
+ renderer.node(self.style, &self.content, f32::from(self.size))
}
fn draw(
@@ -124,7 +124,7 @@ where
renderer.draw(
layout.bounds(),
&self.content,
- self.size as f32,
+ f32::from(self.size),
self.color,
self.horizontal_alignment,
self.vertical_alignment,
@@ -133,7 +133,7 @@ where
MouseCursor::OutOfBounds
}
- fn hash(&self, state: &mut Hasher) {
+ fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
self.content.hash(state);
@@ -141,13 +141,14 @@ where
}
}
-/// The renderer of a [`Text`] fragment.
+/// The renderer of a [`Text`] fragment with a generic `Color`.
///
-/// Your [`core::Renderer`] will need to implement this trait before being
-/// able to use a [`Text`] in your user interface.
+/// Your [renderer] will need to implement this trait before being
+/// able to use [`Text`] in your [`UserInterface`].
///
/// [`Text`]: struct.Text.html
-/// [`core::Renderer`]: ../../core/trait.Renderer.html
+/// [renderer]: ../../renderer/index.html
+/// [`UserInterface`]: ../../struct.UserInterface.html
pub trait Renderer<Color> {
/// Creates a [`Node`] with the given [`Style`] for the provided [`Text`]
/// contents and size.
@@ -155,10 +156,10 @@ pub trait Renderer<Color> {
/// You should probably use [`Node::with_measure`] to allow [`Text`] to
/// adapt to the dimensions of its container.
///
- /// [`Node`]: ../../core/struct.Node.html
- /// [`Style`]: ../../core/struct.Style.html
+ /// [`Node`]: ../../struct.Node.html
+ /// [`Style`]: ../../struct.Style.html
/// [`Text`]: struct.Text.html
- /// [`Node::with_measure`]: ../../core/struct.Node.html#method.with_measure
+ /// [`Node::with_measure`]: ../../struct.Node.html#method.with_measure
fn node(&self, style: Style, content: &str, size: f32) -> Node;
/// Draws a [`Text`] fragment.
@@ -172,14 +173,14 @@ pub trait Renderer<Color> {
/// * the [`VerticalAlignment`] of the [`Text`]
///
/// [`Text`]: struct.Text.html
- /// [`HorizontalAlignment`]: ../../../graphics/enum.HorizontalAlignment.html
- /// [`VerticalAlignment`]: ../../../graphics/enum.VerticalAlignment.html
+ /// [`HorizontalAlignment`]: enum.HorizontalAlignment.html
+ /// [`VerticalAlignment`]: enum.VerticalAlignment.html
fn draw(
&mut self,
- bounds: Rectangle<f32>,
+ bounds: Rectangle,
content: &str,
size: f32,
- color: Color,
+ color: Option<Color>,
horizontal_alignment: HorizontalAlignment,
vertical_alignment: VerticalAlignment,
);