diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/element.rs | 184 | ||||
-rw-r--r-- | src/hasher.rs | 19 | ||||
-rw-r--r-- | src/input.rs | 1 | ||||
-rw-r--r-- | src/input/button_state.rs | 14 | ||||
-rw-r--r-- | src/input/keyboard.rs | 1 | ||||
-rw-r--r-- | src/input/keyboard/event.rs | 7 | ||||
-rw-r--r-- | src/input/keyboard/key_code.rs | 200 | ||||
-rw-r--r-- | src/input/mouse.rs | 1 | ||||
-rw-r--r-- | src/input/mouse/button.rs | 20 | ||||
-rw-r--r-- | src/layout.rs | 12 | ||||
-rw-r--r-- | src/lib.rs | 206 | ||||
-rw-r--r-- | src/mouse_cursor.rs | 14 | ||||
-rw-r--r-- | src/node.rs | 28 | ||||
-rw-r--r-- | src/point.rs | 32 | ||||
-rw-r--r-- | src/rectangle.rs | 18 | ||||
-rw-r--r-- | src/renderer.rs | 34 | ||||
-rw-r--r-- | src/runtime.rs | 106 | ||||
-rw-r--r-- | src/user_interface.rs | 323 | ||||
-rw-r--r-- | src/vector.rs | 15 | ||||
-rw-r--r-- | src/widget.rs | 57 | ||||
-rw-r--r-- | src/widget/button.rs | 19 | ||||
-rw-r--r-- | src/widget/checkbox.rs | 52 | ||||
-rw-r--r-- | src/widget/column.rs | 13 | ||||
-rw-r--r-- | src/widget/panel.rs | 2 | ||||
-rw-r--r-- | src/widget/radio.rs | 48 | ||||
-rw-r--r-- | src/widget/row.rs | 13 | ||||
-rw-r--r-- | src/widget/slider.rs | 17 | ||||
-rw-r--r-- | src/widget/text.rs | 65 |
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, @@ -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); /// ``` +/// +///  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`]. /// -///  +///  /// /// [`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); /// ``` +/// +///  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); /// ``` /// -///  +///  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); /// ``` +/// +///  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, ); |