Rethink workspace structure

This commit is contained in:
Héctor Ramón Jiménez 2019-09-14 19:16:06 +02:00
parent 8b8f7563ad
commit a97401aed2
44 changed files with 684 additions and 51 deletions

View file

@ -1,25 +0,0 @@
[package]
name = "iced"
version = "0.1.0-alpha"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
description = "A GUI runtime, heavily inspired by Elm."
license = "MIT"
repository = "https://github.com/hecrj/iced"
documentation = "https://docs.rs/iced"
readme = "README.md"
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[badges]
maintenance = { status = "actively-developed" }
[package.metadata.docs.rs]
features = ["winit"]
[dependencies]
stretch = "0.2"
twox-hash = "1.5"
# Enable to obtain conversion traits
winit = { version = "0.20.0-alpha3", optional = true }

View file

@ -1,370 +0,0 @@
use stretch::{geometry, result};
use crate::{
renderer, Event, Hasher, Layout, MouseCursor, Node, Point, Widget,
};
/// A generic [`Widget`].
///
/// It is useful to build composable user interfaces that do not leak
/// implementation details in their __view logic__.
///
/// 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>,
}
impl<'a, Message, Renderer> std::fmt::Debug for Element<'a, Message, Renderer> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Element")
.field("widget", &self.widget)
.finish()
}
}
impl<'a, Message, Renderer> Element<'a, Message, Renderer> {
/// Create a new [`Element`] containing the given [`Widget`].
///
/// [`Element`]: struct.Element.html
/// [`Widget`]: widget/trait.Widget.html
pub fn new(
widget: impl Widget<Message, Renderer> + 'a,
) -> Element<'a, Message, Renderer> {
Element {
widget: Box::new(widget),
}
}
/// Applies a transformation to the produced message of the [`Element`].
///
/// This method is useful when you want to decouple different parts of your
/// UI and make them __composable__.
///
/// [`Element`]: struct.Element.html
///
/// # Example
/// 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,
Renderer: 'a,
B: 'static,
F: 'static + Fn(Message) -> B,
{
Element {
widget: Box::new(Map::new(self.widget, f)),
}
}
/// Marks the [`Element`] as _to-be-explained_.
///
/// The [`Renderer`] will explain the layout of the [`Element`] graphically.
/// This can be very useful for debugging your layout!
///
/// [`Element`]: struct.Element.html
/// [`Renderer`]: trait.Renderer.html
pub fn explain(
self,
color: Renderer::Color,
) -> Element<'a, Message, Renderer>
where
Message: 'static,
Renderer: 'a + renderer::Debugger,
{
Element {
widget: Box::new(Explain::new(self, color)),
}
}
pub(crate) fn compute_layout(&self, renderer: &Renderer) -> result::Layout {
let node = self.widget.node(renderer);
node.0.compute_layout(geometry::Size::undefined()).unwrap()
}
pub(crate) fn hash_layout(&self, state: &mut Hasher) {
self.widget.hash_layout(state);
}
}
struct Map<'a, A, B, Renderer> {
widget: Box<dyn Widget<A, Renderer> + 'a>,
mapper: Box<dyn Fn(A) -> B>,
}
impl<'a, A, B, Renderer> std::fmt::Debug for Map<'a, A, B, Renderer> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Map").field("widget", &self.widget).finish()
}
}
impl<'a, A, B, Renderer> Map<'a, A, B, Renderer> {
pub fn new<F>(
widget: Box<dyn Widget<A, Renderer> + 'a>,
mapper: F,
) -> Map<'a, A, B, Renderer>
where
F: 'static + Fn(A) -> B,
{
Map {
widget,
mapper: Box::new(mapper),
}
}
}
impl<'a, A, B, Renderer> Widget<B, Renderer> for Map<'a, A, B, Renderer>
where
A: Copy,
{
fn node(&self, renderer: &Renderer) -> Node {
self.widget.node(renderer)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<B>,
) {
let mut original_messages = Vec::new();
self.widget.on_event(
event,
layout,
cursor_position,
&mut original_messages,
);
original_messages
.iter()
.cloned()
.for_each(|message| messages.push((self.mapper)(message)));
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
self.widget.draw(renderer, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
self.widget.hash_layout(state);
}
}
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: renderer::Debugger,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Explain")
.field("element", &self.element)
.finish()
}
}
impl<'a, Message, Renderer> Explain<'a, Message, Renderer>
where
Renderer: renderer::Debugger,
{
fn new(
element: Element<'a, Message, Renderer>,
color: Renderer::Color,
) -> Self {
Explain { element, color }
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Explain<'a, Message, Renderer>
where
Renderer: renderer::Debugger,
{
fn node(&self, renderer: &Renderer) -> Node {
self.element.widget.node(renderer)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
self.element
.widget
.on_event(event, layout, cursor_position, messages)
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
renderer.explain(&layout, self.color);
self.element.widget.draw(renderer, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
self.element.widget.hash_layout(state);
}
}

View file

@ -1,16 +0,0 @@
use crate::input::{keyboard, mouse};
/// A user interface event.
///
/// _**Note:** This type is largely incomplete! If you need to track
/// additional events, feel free to [open an issue] and share your use case!_
///
/// [open an issue]: https://github.com/hecrj/iced/issues
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Event {
/// A keyboard event
Keyboard(keyboard::Event),
/// A mouse event
Mouse(mouse::Event),
}

View file

@ -1,19 +0,0 @@
/// The hasher used to compare layouts.
#[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()
}
}

View file

@ -1,7 +0,0 @@
//! Map your system events into input events that the runtime can understand.
pub mod keyboard;
pub mod mouse;
mod button_state;
pub use button_state::ButtonState;

View file

@ -1,24 +0,0 @@
/// The state of a button.
///
/// If you are using [`winit`], consider enabling the `winit` feature to get
/// conversion implementations for free!
///
/// [`winit`]: https://docs.rs/winit/0.20.0-alpha3/winit/
#[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,
}
}
}

View file

@ -1,6 +0,0 @@
//! Build keyboard events.
mod event;
mod key_code;
pub use event::Event;
pub use key_code::KeyCode;

View file

@ -1,23 +0,0 @@
use super::KeyCode;
use crate::input::ButtonState;
#[derive(Debug, Clone, Copy, PartialEq)]
/// A keyboard event.
///
/// _**Note:** This type is largely incomplete! If you need to track
/// additional events, feel free to [open an issue] and share your use case!_
///
/// [open an issue]: https://github.com/hecrj/iced/issues
pub enum Event {
/// A keyboard key was pressed or released.
Input {
/// The state of the key
state: ButtonState,
/// The key identifier
key_code: KeyCode,
},
/// A unicode character was received.
CharacterReceived(char),
}

View file

@ -1,374 +0,0 @@
/// 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!
///
/// [`winit`]: https://docs.rs/winit/0.20.0-alpha3/winit/
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
#[allow(missing_docs)]
pub enum KeyCode {
/// The '1' key over the letters.
Key1,
/// The '2' key over the letters.
Key2,
/// The '3' key over the letters.
Key3,
/// The '4' key over the letters.
Key4,
/// The '5' key over the letters.
Key5,
/// The '6' key over the letters.
Key6,
/// The '7' key over the letters.
Key7,
/// The '8' key over the letters.
Key8,
/// The '9' key over the letters.
Key9,
/// The '0' key over the 'O' and 'P' keys.
Key0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
/// The Escape key, next to F1
Escape,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
/// Print Screen/SysRq
Snapshot,
/// Scroll Lock
Scroll,
/// Pause/Break key, next to Scroll lock
Pause,
/// `Insert`, next to Backspace
Insert,
Home,
Delete,
End,
PageDown,
PageUp,
Left,
Up,
Right,
Down,
Backspace,
Enter,
Space,
/// The "Compose" key on Linux
Compose,
Caret,
Numlock,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
AbntC1,
AbntC2,
Add,
Apostrophe,
Apps,
At,
Ax,
Backslash,
Calculator,
Capital,
Colon,
Comma,
Convert,
Decimal,
Divide,
Equals,
Grave,
Kana,
Kanji,
LAlt,
LBracket,
LControl,
LShift,
LWin,
Mail,
MediaSelect,
MediaStop,
Minus,
Multiply,
Mute,
MyComputer,
NavigateForward, // also called "Prior"
NavigateBackward, // also called "Next"
NextTrack,
NoConvert,
NumpadComma,
NumpadEnter,
NumpadEquals,
OEM102,
Period,
PlayPause,
Power,
PrevTrack,
RAlt,
RBracket,
RControl,
RShift,
RWin,
Semicolon,
Slash,
Sleep,
Stop,
Subtract,
Sysrq,
Tab,
Underline,
Unlabeled,
VolumeDown,
VolumeUp,
Wake,
WebBack,
WebFavorites,
WebForward,
WebHome,
WebRefresh,
WebSearch,
WebStop,
Yen,
Copy,
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,
}
}
}

View file

@ -1,6 +0,0 @@
//! Build mouse events.
mod button;
mod event;
pub use button::Button;
pub use event::Event;

View file

@ -1,32 +0,0 @@
/// The button of a mouse.
///
/// If you are using [`winit`], consider enabling the `winit` feature to get
/// conversion implementations for free!
///
/// [`winit`]: https://docs.rs/winit/0.20.0-alpha3/winit/
#[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),
}
}
}

View file

@ -1,44 +0,0 @@
use super::Button;
use crate::input::ButtonState;
/// A mouse event.
///
/// _**Note:** This type is largely incomplete! If you need to track
/// additional events, feel free to [open an issue] and share your use case!_
///
/// [open an issue]: https://github.com/hecrj/iced/issues
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Event {
/// The mouse cursor entered the window.
CursorEntered,
/// The mouse cursor left the window.
CursorLeft,
/// The mouse cursor was moved
CursorMoved {
/// The X coordinate of the mouse position
x: f32,
/// The Y coordinate of the mouse position
y: f32,
},
/// A mouse button was pressed or released.
Input {
/// The state of the button
state: ButtonState,
/// The button identifier
button: Button,
},
/// The mouse wheel was scrolled.
WheelScrolled {
/// The number of horizontal lines scrolled
delta_x: f32,
/// The number of vertical lines scrolled
delta_y: f32,
},
}

View file

@ -1,62 +0,0 @@
use stretch::result;
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 [`Node`] produced by
/// [`Widget::node`].
///
/// [`Node`]: struct.Node.html
/// [`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,
position: Point,
}
impl<'a> Layout<'a> {
pub(crate) fn new(layout: &'a result::Layout) -> Self {
Self::with_parent_position(layout, Point::new(0.0, 0.0))
}
fn with_parent_position(
layout: &'a result::Layout,
parent_position: Point,
) -> Self {
let position =
parent_position + Vector::new(layout.location.x, layout.location.y);
Layout { layout, position }
}
/// Gets the bounds of the [`Layout`].
///
/// The returned [`Rectangle`] describes the position and size of a
/// [`Node`].
///
/// [`Layout`]: struct.Layout.html
/// [`Rectangle`]: struct.Rectangle.html
/// [`Node`]: struct.Node.html
pub fn bounds(&self) -> Rectangle {
Rectangle {
x: self.position.x,
y: self.position.y,
width: self.layout.size.width,
height: self.layout.size.height,
}
}
/// Returns an iterator over the [`Layout`] of the children of a [`Node`].
///
/// [`Layout`]: struct.Layout.html
/// [`Node`]: struct.Node.html
pub fn children(&'a self) -> impl Iterator<Item = Layout<'a>> {
self.layout.children.iter().map(move |layout| {
Layout::with_parent_position(layout, self.position)
})
}
}

View file

@ -1,229 +0,0 @@
//! 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: Option<u16>) -> Node {
//! # Node::new(style)
//! # }
//! #
//! # fn draw(
//! # &mut self,
//! # _bounds: Rectangle,
//! # _content: &str,
//! # _size: Option<u16>,
//! # _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;
mod event;
mod hasher;
mod layout;
mod mouse_cursor;
mod node;
mod point;
mod rectangle;
mod style;
mod user_interface;
mod vector;
#[doc(no_inline)]
pub use stretch::{geometry::Size, number::Number};
pub use element::Element;
pub use event::Event;
pub use hasher::Hasher;
pub use layout::Layout;
pub use mouse_cursor::MouseCursor;
pub use node::Node;
pub use point::Point;
pub use rectangle::Rectangle;
pub use style::{Align, Justify, Style};
pub use user_interface::{Cache, UserInterface};
pub(crate) use vector::Vector;
pub use widget::*;

View file

@ -1,35 +0,0 @@
/// The state of the mouse cursor.
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum MouseCursor {
/// The cursor is out of the bounds of the user interface.
OutOfBounds,
/// The cursor is over a non-interactive widget.
Idle,
/// The cursor is over a clickable widget.
Pointer,
/// The cursor is over a busy widget.
Working,
/// The cursor is over a grabbable widget.
Grab,
/// 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,
}
}
}

View file

@ -1,60 +0,0 @@
use stretch::node;
use crate::{Number, Size, Style};
/// The visual requirements of a [`Widget`] and its children.
///
/// When there have been changes and the [`Layout`] needs to be recomputed, the
/// runtime obtains a [`Node`] by calling [`Widget::node`].
///
/// [`Style`]: struct.Style.html
/// [`Widget`]: widget/trait.Widget.html
/// [`Node`]: struct.Node.html
/// [`Widget::node`]: widget/trait.Widget.html#tymethod.node
/// [`Layout`]: struct.Layout.html
#[derive(Debug)]
pub struct Node(pub(crate) node::Node);
impl Node {
/// Creates a new [`Node`] with the given [`Style`].
///
/// [`Node`]: struct.Node.html
/// [`Style`]: struct.Style.html
pub fn new(style: Style) -> Node {
Self::with_children(style, Vec::new())
}
/// Creates a new [`Node`] with the given [`Style`] and a measure function.
///
/// This type of node cannot have any children.
///
/// You should use this when your [`Widget`] can adapt its contents to the
/// size of its container. The measure function will receive the container
/// size as a parameter and must compute the size of the [`Node`] inside
/// the given bounds (if the `Number` for a dimension is `Undefined` it
/// means that it has no boundary).
///
/// [`Node`]: struct.Node.html
/// [`Style`]: struct.Style.html
/// [`Widget`]: widget/trait.Widget.html
pub fn with_measure<F>(style: Style, measure: F) -> Node
where
F: 'static + Fn(Size<Number>) -> Size<f32>,
{
Node(node::Node::new_leaf(
style.0,
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(),
))
}
}

View file

@ -1,31 +0,0 @@
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,
}
}
}

View file

@ -1,30 +0,0 @@
use crate::Point;
/// A rectangle.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rectangle<T = f32> {
/// X coordinate of the top-left corner.
pub x: T,
/// Y coordinate of the top-left corner.
pub y: T,
/// Width of the rectangle.
pub width: T,
/// Height of the rectangle.
pub height: T,
}
impl Rectangle<f32> {
/// Returns true if the given [`Point`] is contained in the [`Rectangle`].
///
/// [`Point`]: struct.Point.html
/// [`Rectangle`]: struct.Rectangle.html
pub fn contains(&self, point: Point) -> bool {
self.x <= point.x
&& point.x <= self.x + self.width
&& self.y <= point.y
&& point.y <= self.y + self.height
}
}

View file

@ -1,45 +0,0 @@
//! 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;
/// 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 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
/// [`Element::explain`]: struct.Element.html#method.explain
fn explain(&mut self, layout: &Layout<'_>, color: Self::Color);
}

View file

@ -1,262 +0,0 @@
use std::hash::{Hash, Hasher};
use stretch::{geometry, style};
/// The appearance of a [`Node`].
///
/// [`Node`]: struct.Node.html
#[derive(Debug, Clone, Copy)]
pub struct Style(pub(crate) style::Style);
impl Style {
/// Defines the width of a [`Node`] in pixels.
///
/// [`Node`]: struct.Node.html
pub fn width(mut self, width: u16) -> Self {
self.0.size.width = style::Dimension::Points(width as f32);
self
}
/// Defines the height of a [`Node`] in pixels.
///
/// [`Node`]: struct.Node.html
pub fn height(mut self, height: u16) -> Self {
self.0.size.height = style::Dimension::Points(height as f32);
self
}
/// Defines the minimum width of a [`Node`] in pixels.
///
/// [`Node`]: struct.Node.html
pub fn min_width(mut self, min_width: u16) -> Self {
self.0.min_size.width = style::Dimension::Points(min_width as f32);
self
}
/// Defines the maximum width of a [`Node`] in pixels.
///
/// [`Node`]: struct.Node.html
pub fn max_width(mut self, max_width: u16) -> Self {
self.0.max_size.width = style::Dimension::Points(max_width as f32);
self.fill_width()
}
/// Defines the minimum height of a [`Node`] in pixels.
///
/// [`Node`]: struct.Node.html
pub fn min_height(mut self, min_height: u16) -> Self {
self.0.min_size.height =
style::Dimension::Points(f32::from(min_height));
self
}
/// Defines the maximum height of a [`Node`] in pixels.
///
/// [`Node`]: struct.Node.html
pub fn max_height(mut self, max_height: u16) -> Self {
self.0.max_size.height =
style::Dimension::Points(f32::from(max_height));
self.fill_height()
}
/// Makes a [`Node`] fill all the horizontal available space.
///
/// [`Node`]: struct.Node.html
pub fn fill_width(mut self) -> Self {
self.0.size.width = stretch::style::Dimension::Percent(1.0);
self
}
/// Makes a [`Node`] fill all the vertical available space.
///
/// [`Node`]: struct.Node.html
pub fn fill_height(mut self) -> Self {
self.0.size.height = stretch::style::Dimension::Percent(1.0);
self
}
pub(crate) fn align_items(mut self, align: Align) -> Self {
self.0.align_items = align.into();
self
}
pub(crate) fn justify_content(mut self, justify: Justify) -> Self {
self.0.justify_content = justify.into();
self
}
/// Sets the alignment of a [`Node`].
///
/// If the [`Node`] is inside a...
///
/// * [`Column`], this setting will affect its __horizontal__ alignment.
/// * [`Row`], this setting will affect its __vertical__ alignment.
///
/// [`Node`]: struct.Node.html
/// [`Column`]: widget/struct.Column.html
/// [`Row`]: widget/struct.Row.html
pub fn align_self(mut self, align: Align) -> Self {
self.0.align_self = align.into();
self
}
/// Sets the padding of a [`Node`] in pixels.
///
/// [`Node`]: struct.Node.html
pub fn padding(mut self, px: u16) -> Self {
self.0.padding = stretch::geometry::Rect {
start: style::Dimension::Points(px as f32),
end: style::Dimension::Points(px as f32),
top: style::Dimension::Points(px as f32),
bottom: style::Dimension::Points(px as f32),
};
self
}
}
impl Default for Style {
fn default() -> Style {
Style(style::Style {
align_items: style::AlignItems::FlexStart,
justify_content: style::JustifyContent::FlexStart,
..style::Style::default()
})
}
}
impl Hash for Style {
fn hash<H: Hasher>(&self, state: &mut H) {
hash_size(&self.0.size, state);
hash_size(&self.0.min_size, state);
hash_size(&self.0.max_size, state);
hash_rect(&self.0.margin, state);
(self.0.flex_direction as u8).hash(state);
(self.0.align_items as u8).hash(state);
(self.0.justify_content as u8).hash(state);
(self.0.align_self as u8).hash(state);
(self.0.flex_grow as u32).hash(state);
}
}
fn hash_size<H: Hasher>(
size: &geometry::Size<style::Dimension>,
state: &mut H,
) {
hash_dimension(size.width, state);
hash_dimension(size.height, state);
}
fn hash_rect<H: Hasher>(
rect: &geometry::Rect<style::Dimension>,
state: &mut H,
) {
hash_dimension(rect.start, state);
hash_dimension(rect.end, state);
hash_dimension(rect.top, state);
hash_dimension(rect.bottom, state);
}
fn hash_dimension<H: Hasher>(dimension: style::Dimension, state: &mut H) {
match dimension {
style::Dimension::Undefined => state.write_u8(0),
style::Dimension::Auto => state.write_u8(1),
style::Dimension::Points(points) => {
state.write_u8(2);
(points as u32).hash(state);
}
style::Dimension::Percent(percent) => {
state.write_u8(3);
(percent as u32).hash(state);
}
}
}
/// Alignment on the cross axis of a container.
///
/// * On a [`Column`], it describes __horizontal__ alignment.
/// * On a [`Row`], it describes __vertical__ alignment.
///
/// [`Column`]: widget/struct.Column.html
/// [`Row`]: widget/struct.Row.html
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Align {
/// Align at the start of the cross axis.
Start,
/// Align at the center of the cross axis.
Center,
/// Align at the end of the cross axis.
End,
/// Stretch over the cross axis.
Stretch,
}
#[doc(hidden)]
impl From<Align> for style::AlignItems {
fn from(align: Align) -> Self {
match align {
Align::Start => style::AlignItems::FlexStart,
Align::Center => style::AlignItems::Center,
Align::End => style::AlignItems::FlexEnd,
Align::Stretch => style::AlignItems::Stretch,
}
}
}
#[doc(hidden)]
impl From<Align> for style::AlignSelf {
fn from(align: Align) -> Self {
match align {
Align::Start => style::AlignSelf::FlexStart,
Align::Center => style::AlignSelf::Center,
Align::End => style::AlignSelf::FlexEnd,
Align::Stretch => style::AlignSelf::Stretch,
}
}
}
/// Distribution on the main axis of a container.
///
/// * On a [`Column`], it describes __vertical__ distribution.
/// * On a [`Row`], it describes __horizontal__ distribution.
///
/// [`Column`]: widget/struct.Column.html
/// [`Row`]: widget/struct.Row.html
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Justify {
/// Place items at the start of the main axis.
Start,
/// Place items at the center of the main axis.
Center,
/// Place items at the end of the main axis.
End,
/// Place items with space between.
SpaceBetween,
/// Place items with space around.
SpaceAround,
/// Place items with evenly distributed space.
SpaceEvenly,
}
#[doc(hidden)]
impl From<Justify> for style::JustifyContent {
fn from(justify: Justify) -> Self {
match justify {
Justify::Start => style::JustifyContent::FlexStart,
Justify::Center => style::JustifyContent::Center,
Justify::End => style::JustifyContent::FlexEnd,
Justify::SpaceBetween => style::JustifyContent::SpaceBetween,
Justify::SpaceAround => style::JustifyContent::SpaceAround,
Justify::SpaceEvenly => style::JustifyContent::SpaceEvenly,
}
}
}

View file

@ -1,323 +0,0 @@
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 {}

View file

@ -1,15 +0,0 @@
/// A 2D vector.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector {
pub x: f32,
pub y: f32,
}
impl Vector {
/// Creates a new [`Vector`] with the given components.
///
/// [`Vector`]: struct.Vector.html
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}

View file

@ -1,114 +0,0 @@
//! Use the built-in widgets or create your own.
//!
//! # 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`].
//!
//! # 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.
//!
//! # 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;
pub mod button;
pub mod checkbox;
pub mod image;
//pub mod progress_bar;
pub mod radio;
pub mod slider;
pub mod text;
pub use button::Button;
pub use checkbox::Checkbox;
pub use column::Column;
pub use image::Image;
//pub use progress_bar::ProgressBar;
pub use radio::Radio;
pub use row::Row;
pub use slider::Slider;
pub use text::Text;
use crate::{Event, Hasher, Layout, MouseCursor, Node, Point};
/// A component that displays information and allows interaction.
///
/// If you want to build your own widgets, you will need to implement this
/// trait.
///
/// [`Widget`]: trait.Widget.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
/// [`Widget`]: trait.Widget.html
/// [`Layout`]: ../struct.Layout.html
fn node(&self, renderer: &Renderer) -> Node;
/// Draws the [`Widget`] using the associated `Renderer`.
///
/// It must return the [`MouseCursor`] state for the [`Widget`].
///
/// [`Widget`]: trait.Widget.html
/// [`MouseCursor`]: ../enum.MouseCursor.html
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor;
/// Computes the _layout_ hash of the [`Widget`].
///
/// The produced hash is used by the runtime to decide if the [`Layout`]
/// needs to be recomputed between frames. Therefore, to ensure maximum
/// efficiency, the hash should only be affected by the properties of the
/// [`Widget`] that can affect layouting.
///
/// For example, the [`Text`] widget does not hash its color property, as
/// its value cannot affect the overall [`Layout`] of the user interface.
///
/// [`Widget`]: trait.Widget.html
/// [`Layout`]: ../struct.Layout.html
/// [`Text`]: text/struct.Text.html
fn hash_layout(&self, state: &mut Hasher);
/// Processes a runtime [`Event`].
///
/// It receives:
/// * an [`Event`] describing user interaction
/// * the computed [`Layout`] of the [`Widget`]
/// * the current cursor position
/// * a mutable `Message` list, allowing the [`Widget`] to produce
/// new messages based on user interaction.
///
/// By default, it does nothing.
///
/// [`Event`]: ../enum.Event.html
/// [`Widget`]: trait.Widget.html
/// [`Layout`]: ../struct.Layout.html
fn on_event(
&mut self,
_event: Event,
_layout: Layout<'_>,
_cursor_position: Point,
_messages: &mut Vec<Message>,
) {
}
}

View file

@ -1,282 +0,0 @@
//! Allow your users to perform actions by pressing a button.
//!
//! A [`Button`] has some local [`State`] and a [`Class`].
//!
//! [`Button`]: struct.Button.html
//! [`State`]: struct.State.html
//! [`Class`]: enum.Class.html
use crate::input::{mouse, ButtonState};
use crate::{
Align, Element, Event, Hasher, Layout, MouseCursor, Node, Point, Rectangle,
Style, Widget,
};
use std::hash::Hash;
/// A generic widget that produces a message when clicked.
///
/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`button::Renderer`] trait.
///
/// [`Widget`]: ../trait.Widget.html
/// [`button::Renderer`]: trait.Renderer.html
///
/// # Example
///
/// ```
/// use iced::{button, Button};
///
/// pub enum Message {
/// ButtonClicked,
/// }
///
/// let state = &mut button::State::new();
///
/// 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,
class: Class,
on_press: Option<Message>,
style: Style,
}
impl<'a, Message> std::fmt::Debug for Button<'a, Message>
where
Message: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Button")
.field("state", &self.state)
.field("label", &self.label)
.field("class", &self.class)
.field("on_press", &self.on_press)
.field("style", &self.style)
.finish()
}
}
impl<'a, Message> Button<'a, Message> {
/// Creates a new [`Button`] with some local [`State`] and the given label.
///
/// The default [`Class`] of a new [`Button`] is [`Class::Primary`].
///
/// [`Button`]: struct.Button.html
/// [`State`]: struct.State.html
/// [`Class`]: enum.Class.html
/// [`Class::Primary`]: enum.Class.html#variant.Primary
pub fn new(state: &'a mut State, label: &str) -> Self {
Button {
state,
label: String::from(label),
class: Class::Primary,
on_press: None,
style: Style::default().min_width(100),
}
}
/// Sets the width of the [`Button`] in pixels.
///
/// [`Button`]: struct.Button.html
pub fn width(mut self, width: u16) -> Self {
self.style = self.style.width(width);
self
}
/// Makes the [`Button`] fill the horizontal space of its container.
///
/// [`Button`]: struct.Button.html
pub fn fill_width(mut self) -> Self {
self.style = self.style.fill_width();
self
}
/// Sets the alignment of the [`Button`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Button`]: struct.Button.html
pub fn align_self(mut self, align: Align) -> Self {
self.style = self.style.align_self(align);
self
}
/// Sets the [`Class`] of the [`Button`].
///
///
/// [`Button`]: struct.Button.html
/// [`Class`]: enum.Class.html
pub fn class(mut self, class: Class) -> Self {
self.class = class;
self
}
/// Sets the message that will be produced when the [`Button`] is pressed.
///
/// [`Button`]: struct.Button.html
pub fn on_press(mut self, msg: Message) -> Self {
self.on_press = Some(msg);
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer> for Button<'a, Message>
where
Renderer: self::Renderer,
Message: Copy + std::fmt::Debug,
{
fn node(&self, _renderer: &Renderer) -> Node {
Node::new(self.style.height(50))
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
match event {
Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left,
state,
}) => {
if let Some(on_press) = self.on_press {
let bounds = layout.bounds();
match state {
ButtonState::Pressed => {
self.state.is_pressed =
bounds.contains(cursor_position);
}
ButtonState::Released => {
let is_clicked = self.state.is_pressed
&& bounds.contains(cursor_position);
self.state.is_pressed = false;
if is_clicked {
messages.push(on_press);
}
}
}
}
}
_ => {}
}
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
renderer.draw(
cursor_position,
layout.bounds(),
self.state,
&self.label,
self.class,
)
}
fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
}
}
/// The local state of a [`Button`].
///
/// [`Button`]: struct.Button.html
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct State {
is_pressed: bool,
}
impl State {
/// Creates a new [`State`].
///
/// [`State`]: struct.State.html
pub fn new() -> State {
State::default()
}
/// Returns whether the associated [`Button`] is currently being pressed or
/// not.
///
/// [`Button`]: struct.Button.html
pub fn is_pressed(&self) -> bool {
self.is_pressed
}
}
/// The type of a [`Button`].
///
/// ![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)]
pub enum Class {
/// The [`Button`] performs the main action.
///
/// [`Button`]: struct.Button.html
Primary,
/// The [`Button`] performs an alternative action.
///
/// [`Button`]: struct.Button.html
Secondary,
/// The [`Button`] performs a productive action.
///
/// [`Button`]: struct.Button.html
Positive,
}
/// The renderer of a [`Button`].
///
/// Your [renderer] will need to implement this trait before being
/// able to use a [`Button`] in your user interface.
///
/// [`Button`]: struct.Button.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Button`].
///
/// It receives:
/// * the current cursor position
/// * the bounds of the [`Button`]
/// * the local state of the [`Button`]
/// * the label of the [`Button`]
/// * the [`Class`] of the [`Button`]
///
/// [`Button`]: struct.Button.html
/// [`State`]: struct.State.html
/// [`Class`]: enum.Class.html
fn draw(
&mut self,
cursor_position: Point,
bounds: Rectangle,
state: &State,
label: &str,
class: Class,
) -> MouseCursor;
}
impl<'a, Message, Renderer> From<Button<'a, Message>>
for Element<'a, Message, Renderer>
where
Renderer: self::Renderer,
Message: 'static + Copy + std::fmt::Debug,
{
fn from(button: Button<'a, Message>) -> Element<'a, Message, Renderer> {
Element::new(button)
}
}

View file

@ -1,203 +0,0 @@
//! Show toggle controls using checkboxes.
use std::hash::Hash;
use crate::input::{mouse, ButtonState};
use crate::widget::{text, Column, Row, Text};
use crate::{
Align, Element, Event, Hasher, Layout, MouseCursor, Node, Point, Rectangle,
Widget,
};
/// A box that can be checked, with a generic text `Color`.
///
/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`checkbox::Renderer`] trait.
///
/// [`Widget`]: ../trait.Widget.html
/// [`checkbox::Renderer`]: trait.Renderer.html
///
/// # Example
///
/// ```
/// use iced::Checkbox;
///
/// #[derive(Debug, Clone, Copy)]
/// pub enum Color {
/// Black,
/// }
///
/// pub enum Message {
/// CheckboxToggled(bool),
/// }
///
/// 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: Option<Color>,
}
impl<Color, Message> std::fmt::Debug for Checkbox<Color, Message>
where
Color: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Checkbox")
.field("is_checked", &self.is_checked)
.field("label", &self.label)
.field("label_color", &self.label_color)
.finish()
}
}
impl<Color, Message> Checkbox<Color, Message> {
/// Creates a new [`Checkbox`].
///
/// It expects:
/// * a boolean describing whether the [`Checkbox`] is checked or not
/// * the label of the [`Checkbox`]
/// * a function that will be called when the [`Checkbox`] is toggled.
/// It will receive the new state of the [`Checkbox`] and must produce
/// a `Message`.
///
/// [`Checkbox`]: struct.Checkbox.html
pub fn new<F>(is_checked: bool, label: &str, f: F) -> Self
where
F: 'static + Fn(bool) -> Message,
{
Checkbox {
is_checked,
on_toggle: Box::new(f),
label: String::from(label),
label_color: None,
}
}
/// Sets the `Color` of the label of the [`Checkbox`].
///
/// [`Checkbox`]: struct.Checkbox.html
pub fn label_color(mut self, color: Color) -> Self {
self.label_color = Some(color);
self
}
}
impl<Color, Message, Renderer> Widget<Message, Renderer>
for Checkbox<Color, Message>
where
Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
{
fn node(&self, renderer: &Renderer) -> Node {
Row::<(), Renderer>::new()
.spacing(15)
.align_items(Align::Center)
.push(Column::new().width(28).height(28))
.push(Text::new(&self.label))
.node(renderer)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
match event {
Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left,
state: ButtonState::Pressed,
}) => {
let mouse_over = layout
.children()
.any(|child| child.bounds().contains(cursor_position));
if mouse_over {
messages.push((self.on_toggle)(!self.is_checked));
}
}
_ => {}
}
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
let children: Vec<_> = layout.children().collect();
let text_bounds = children[1].bounds();
text::Renderer::draw(
renderer,
text_bounds,
&self.label,
None,
self.label_color,
text::HorizontalAlignment::Left,
text::VerticalAlignment::Top,
);
self::Renderer::draw(
renderer,
cursor_position,
children[0].bounds(),
text_bounds,
self.is_checked,
)
}
fn hash_layout(&self, state: &mut Hasher) {
self.label.hash(state);
}
}
/// The renderer of a [`Checkbox`].
///
/// Your [renderer] will need to implement this trait before being
/// able to use a [`Checkbox`] in your user interface.
///
/// [`Checkbox`]: struct.Checkbox.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Checkbox`].
///
/// It receives:
/// * the current cursor position
/// * the bounds of the [`Checkbox`]
/// * the bounds of the label of the [`Checkbox`]
/// * whether the [`Checkbox`] is checked or not
///
/// [`Checkbox`]: struct.Checkbox.html
fn draw(
&mut self,
cursor_position: Point,
bounds: Rectangle,
label_bounds: Rectangle,
is_checked: bool,
) -> MouseCursor;
}
impl<'a, Color, Message, Renderer> From<Checkbox<Color, Message>>
for Element<'a, Message, Renderer>
where
Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
Message: 'static,
{
fn from(
checkbox: Checkbox<Color, Message>,
) -> Element<'a, Message, Renderer> {
Element::new(checkbox)
}
}

View file

@ -1,224 +0,0 @@
use std::hash::Hash;
use crate::{
Align, Element, Event, Hasher, Justify, Layout, MouseCursor, Node, Point,
Style, Widget,
};
/// 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,
children: Vec<Element<'a, Message, Renderer>>,
}
impl<'a, Message, Renderer> std::fmt::Debug for Column<'a, Message, Renderer> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Column")
.field("style", &self.style)
.field("spacing", &self.spacing)
.field("children", &self.children)
.finish()
}
}
impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
/// Creates an empty [`Column`].
///
/// [`Column`]: struct.Column.html
pub fn new() -> Self {
let mut style = Style::default().fill_width();
style.0.flex_direction = stretch::style::FlexDirection::Column;
Column {
style,
spacing: 0,
children: Vec::new(),
}
}
/// Sets the vertical spacing _between_ elements in pixels.
///
/// 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 {
self.spacing = px;
self
}
/// Sets the padding of the [`Column`] in pixels.
///
/// [`Column`]: struct.Column.html
pub fn padding(mut self, px: u16) -> Self {
self.style = self.style.padding(px);
self
}
/// Sets the width of the [`Column`] in pixels.
///
/// [`Column`]: struct.Column.html
pub fn width(mut self, width: u16) -> Self {
self.style = self.style.width(width);
self
}
/// Sets the height of the [`Column`] in pixels.
///
/// [`Column`]: struct.Column.html
pub fn height(mut self, height: u16) -> Self {
self.style = self.style.height(height);
self
}
/// Sets the maximum width of the [`Column`] in pixels.
///
/// [`Column`]: struct.Column.html
pub fn max_width(mut self, max_width: u16) -> Self {
self.style = self.style.max_width(max_width);
self
}
/// Sets the maximum height of the [`Column`] in pixels.
///
/// [`Column`]: struct.Column.html
pub fn max_height(mut self, max_height: u16) -> Self {
self.style = self.style.max_height(max_height);
self
}
/// Sets the alignment of the [`Column`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Column`]: struct.Column.html
pub fn align_self(mut self, align: Align) -> Self {
self.style = self.style.align_self(align);
self
}
/// Sets the horizontal alignment of the contents of the [`Column`] .
///
/// [`Column`]: struct.Column.html
pub fn align_items(mut self, align: Align) -> Self {
self.style = self.style.align_items(align);
self
}
/// Sets the vertical distribution strategy for the contents of the
/// [`Column`] .
///
/// [`Column`]: struct.Column.html
pub fn justify_content(mut self, justify: Justify) -> Self {
self.style = self.style.justify_content(justify);
self
}
/// Adds an [`Element`] to the [`Column`].
///
/// [`Element`]: ../struct.Element.html
/// [`Column`]: struct.Column.html
pub fn push<E>(mut self, child: E) -> Column<'a, Message, Renderer>
where
E: Into<Element<'a, Message, Renderer>>,
{
self.children.push(child.into());
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Column<'a, Message, Renderer>
{
fn node(&self, renderer: &Renderer) -> Node {
let mut children: Vec<Node> = self
.children
.iter()
.map(|child| {
let mut node = child.widget.node(renderer);
let mut style = node.0.style();
style.margin.bottom =
stretch::style::Dimension::Points(f32::from(self.spacing));
node.0.set_style(style);
node
})
.collect();
if let Some(node) = children.last_mut() {
let mut style = node.0.style();
style.margin.bottom = stretch::style::Dimension::Undefined;
node.0.set_style(style);
}
Node::with_children(self.style, children)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
self.children.iter_mut().zip(layout.children()).for_each(
|(child, layout)| {
child
.widget
.on_event(event, layout, cursor_position, messages)
},
);
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
let mut cursor = MouseCursor::OutOfBounds;
self.children.iter().zip(layout.children()).for_each(
|(child, layout)| {
let new_cursor =
child.widget.draw(renderer, layout, cursor_position);
if new_cursor != MouseCursor::OutOfBounds {
cursor = new_cursor;
}
},
);
cursor
}
fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
self.spacing.hash(state);
for child in &self.children {
child.widget.hash_layout(state);
}
}
}
impl<'a, Message, Renderer> From<Column<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a,
Message: 'static,
{
fn from(
column: Column<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(column)
}
}

View file

@ -1,178 +0,0 @@
//! Display images in your user interface.
use crate::{
Align, Element, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Style,
Widget,
};
use std::hash::Hash;
/// A frame that displays an image while keeping aspect ratio.
///
/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`image::Renderer`] trait.
///
/// [`Widget`]: ../../core/trait.Widget.html
/// [`image::Renderer`]: trait.Renderer.html
///
/// # Example
///
/// ```
/// use iced::Image;
///
/// # let my_handle = String::from("some_handle");
/// let image = Image::new(my_handle);
/// ```
pub struct Image<I> {
image: I,
source: Option<Rectangle<u16>>,
width: Option<u16>,
height: Option<u16>,
style: Style,
}
impl<I> std::fmt::Debug for Image<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Image")
.field("source", &self.source)
.field("width", &self.width)
.field("height", &self.height)
.field("style", &self.style)
.finish()
}
}
impl<I> Image<I> {
/// Creates a new [`Image`] with given image handle.
///
/// [`Image`]: struct.Image.html
pub fn new(image: I) -> Self {
Image {
image,
source: None,
width: None,
height: None,
style: Style::default(),
}
}
/// Sets the portion of the [`Image`] to draw.
///
/// [`Image`]: struct.Image.html
pub fn clip(mut self, source: Rectangle<u16>) -> Self {
self.source = Some(source);
self
}
/// Sets the width of the [`Image`] boundaries in pixels.
///
/// [`Image`]: struct.Image.html
pub fn width(mut self, width: u16) -> Self {
self.width = Some(width);
self
}
/// Sets the height of the [`Image`] boundaries in pixels.
///
/// [`Image`]: struct.Image.html
pub fn height(mut self, height: u16) -> Self {
self.height = Some(height);
self
}
/// Sets the alignment of the [`Image`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Image`]: struct.Image.html
pub fn align_self(mut self, align: Align) -> Self {
self.style = self.style.align_self(align);
self
}
}
impl<I, Message, Renderer> Widget<Message, Renderer> for Image<I>
where
Renderer: self::Renderer<I>,
I: Clone,
{
fn node(&self, renderer: &Renderer) -> Node {
renderer.node(
self.style,
&self.image,
self.width,
self.height,
self.source,
)
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
_cursor_position: Point,
) -> MouseCursor {
renderer.draw(&self.image, layout.bounds(), self.source);
MouseCursor::OutOfBounds
}
fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
self.width.hash(state);
self.height.hash(state);
}
}
/// The renderer of an [`Image`].
///
/// Your [renderer] will need to implement this trait before being able to use
/// an [`Image`] in your user interface.
///
/// [`Image`]: struct.Image.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer<I> {
/// Creates a [`Node`] with the given [`Style`] for the provided [`Image`]
/// and its size.
///
/// You should probably keep the original aspect ratio, if possible.
///
/// [`Node`]: ../../struct.Node.html
/// [`Style`]: ../../struct.Style.html
/// [`Image`]: struct.Image.html
fn node(
&self,
style: Style,
image: &I,
width: Option<u16>,
height: Option<u16>,
source: Option<Rectangle<u16>>,
) -> Node;
/// Draws an [`Image`].
///
/// It receives:
/// * the bounds of the [`Image`]
/// * the handle of the loaded [`Image`]
/// * the portion of the image to draw. If not specified, the entire image
/// should be drawn.
///
/// [`Image`]: struct.Image.html
fn draw(
&mut self,
image: &I,
bounds: Rectangle<f32>,
source: Option<Rectangle<u16>>,
);
}
impl<'a, I, Message, Renderer> From<Image<I>> for Element<'a, Message, Renderer>
where
Renderer: self::Renderer<I>,
I: Clone + 'a,
{
fn from(image: Image<I>) -> Element<'a, Message, Renderer> {
Element::new(image)
}
}

View file

@ -1,94 +0,0 @@
use std::hash::Hash;
use crate::graphics::{Point, Rectangle};
use crate::ui::core::{
Event, Hasher, Layout, MouseCursor, Node, Style, Widget,
};
pub struct Panel<'a, Message, Renderer> {
style: Style,
content: Box<Widget<Message, Renderer> + 'a>,
}
impl<'a, Message, Renderer> Panel<'a, Message, Renderer> {
pub fn new(content: impl Widget<Message, Renderer> + 'a) -> Self {
Panel {
style: Style::default().padding(20),
content: Box::new(content),
}
}
pub fn width(mut self, width: u32) -> Self {
self.style = self.style.width(width);
self
}
pub fn max_width(mut self, max_width: u32) -> Self {
self.style = self.style.max_width(max_width);
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Panel<'a, Message, Renderer>
where
Renderer: self::Renderer,
{
fn node(&self, renderer: &Renderer) -> Node {
Node::with_children(self.style, vec![self.content.node(renderer)])
}
fn on_event(
&mut self,
event: Event,
layout: Layout,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
[&mut self.content]
.iter_mut()
.zip(layout.children())
.for_each(|(child, layout)| {
child.on_event(event, layout, cursor_position, messages)
});
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout,
cursor_position: Point,
) -> MouseCursor {
let bounds = layout.bounds();
let mut cursor = MouseCursor::OutOfBounds;
renderer.draw(bounds);
[&self.content].iter().zip(layout.children()).for_each(
|(child, layout)| {
let new_cursor = child.draw(renderer, layout, cursor_position);
if new_cursor != MouseCursor::OutOfBounds {
cursor = new_cursor;
}
},
);
if cursor == MouseCursor::OutOfBounds {
if bounds.contains(cursor_position) {
MouseCursor::Idle
} else {
MouseCursor::OutOfBounds
}
} else {
cursor
}
}
fn hash(&self, state: &mut Hasher) {
self.style.hash(state);
}
}
pub trait Renderer {
fn draw(&mut self, bounds: Rectangle);
}

View file

@ -1,106 +0,0 @@
//! Provide visual feedback to your users when performing a slow task.
use crate::{
Element, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Style, Widget,
};
use std::hash::Hash;
/// A bar that is filled based on an amount of progress.
///
/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`progress_bar::Renderer`] trait.
///
/// [`Widget`]: ../trait.Widget.html
/// [`progress_bar::Renderer`]: trait.Renderer.html
///
/// # Example
///
/// ```
/// use iced::ProgressBar;
///
/// let progress = 0.75;
///
/// ProgressBar::new(progress);
/// ```
#[derive(Debug)]
pub struct ProgressBar {
progress: f32,
style: Style,
}
impl ProgressBar {
/// Creates a new [`ProgressBar`] filled based on the given amount of
/// progress.
///
/// The progress should be in the `0.0..=1.0` range. `0` meaning no work
/// done, and `1` meaning work finished.
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn new(progress: f32) -> Self {
ProgressBar {
progress,
style: Style::default().fill_width(),
}
}
/// Sets the width of the [`ProgressBar`] in pixels.
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn width(mut self, width: u16) -> Self {
self.style = self.style.width(width);
self
}
}
impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar
where
Renderer: self::Renderer,
{
fn node(&self, _renderer: &Renderer) -> Node {
Node::new(self.style.height(50))
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
_cursor_position: Point,
) -> MouseCursor {
renderer.draw(layout.bounds(), self.progress);
MouseCursor::OutOfBounds
}
fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
}
}
/// The renderer of a [`ProgressBar`].
///
/// Your [renderer] will need to implement this trait before being able to use
/// a [`ProgressBar`] in your user interface.
///
/// [`ProgressBar`]: struct.ProgressBar.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`ProgressBar`].
///
/// It receives:
/// * the bounds of the [`ProgressBar`]
/// * the current progress of the [`ProgressBar`], in the `0.0..=1.0`
/// range.
///
/// [`ProgressBar`]: struct.ProgressBar.html
fn draw(&mut self, bounds: Rectangle, progress: f32);
}
impl<'a, Message, Renderer> From<ProgressBar> for Element<'a, Message, Renderer>
where
Renderer: self::Renderer,
{
fn from(progress_bar: ProgressBar) -> Element<'a, Message, Renderer> {
Element::new(progress_bar)
}
}

View file

@ -1,211 +0,0 @@
//! Create choices using radio buttons.
use crate::input::{mouse, ButtonState};
use crate::widget::{text, Column, Row, Text};
use crate::{
Align, Element, Event, Hasher, Layout, MouseCursor, Node, Point, Rectangle,
Widget,
};
use std::hash::Hash;
/// A circular button representing a choice, with a generic text `Color`.
///
/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`radio::Renderer`] trait.
///
/// [`Widget`]: ../trait.Widget.html
/// [`radio::Renderer`]: trait.Renderer.html
///
/// # Example
/// ```
/// use iced::{Column, Radio};
///
/// #[derive(Debug, Clone, Copy)]
/// pub enum Color {
/// Black,
/// }
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// pub enum Choice {
/// A,
/// B,
/// }
///
/// #[derive(Debug, Clone, Copy)]
/// pub enum Message {
/// RadioSelected(Choice),
/// }
///
/// let selected_choice = Some(Choice::A);
///
/// Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected)
/// .label_color(Color::Black);
///
/// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected)
/// .label_color(Color::Black);
/// ```
///
/// ![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: Option<Color>,
}
impl<Color, Message> std::fmt::Debug for Radio<Color, Message>
where
Color: std::fmt::Debug,
Message: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Radio")
.field("is_selected", &self.is_selected)
.field("on_click", &self.on_click)
.field("label", &self.label)
.field("label_color", &self.label_color)
.finish()
}
}
impl<Color, Message> Radio<Color, Message> {
/// Creates a new [`Radio`] button.
///
/// It expects:
/// * the value related to the [`Radio`] button
/// * the label of the [`Radio`] button
/// * the current selected value
/// * a function that will be called when the [`Radio`] is selected. It
/// receives the value of the radio and must produce a `Message`.
///
/// [`Radio`]: struct.Radio.html
pub fn new<F, V>(value: V, label: &str, selected: Option<V>, f: F) -> Self
where
V: Eq + Copy,
F: 'static + Fn(V) -> Message,
{
Radio {
is_selected: Some(value) == selected,
on_click: f(value),
label: String::from(label),
label_color: None,
}
}
/// Sets the `Color` of the label of the [`Radio`].
///
/// [`Radio`]: struct.Radio.html
pub fn label_color(mut self, color: Color) -> Self {
self.label_color = Some(color);
self
}
}
impl<Color, Message, Renderer> Widget<Message, Renderer>
for Radio<Color, Message>
where
Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
Message: Copy + std::fmt::Debug,
{
fn node(&self, renderer: &Renderer) -> Node {
Row::<(), Renderer>::new()
.spacing(15)
.align_items(Align::Center)
.push(Column::new().width(28).height(28))
.push(Text::new(&self.label))
.node(renderer)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
match event {
Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left,
state: ButtonState::Pressed,
}) => {
if layout.bounds().contains(cursor_position) {
messages.push(self.on_click);
}
}
_ => {}
}
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
let children: Vec<_> = layout.children().collect();
let mut text_bounds = children[1].bounds();
text_bounds.y -= 2.0;
text::Renderer::draw(
renderer,
text_bounds,
&self.label,
None,
self.label_color,
text::HorizontalAlignment::Left,
text::VerticalAlignment::Top,
);
self::Renderer::draw(
renderer,
cursor_position,
children[0].bounds(),
layout.bounds(),
self.is_selected,
)
}
fn hash_layout(&self, state: &mut Hasher) {
self.label.hash(state);
}
}
/// The renderer of a [`Radio`] button.
///
/// Your [renderer] will need to implement this trait before being
/// able to use a [`Radio`] button in your user interface.
///
/// [`Radio`]: struct.Radio.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Radio`] button.
///
/// It receives:
/// * the current cursor position
/// * the bounds of the [`Radio`]
/// * the bounds of the label of the [`Radio`]
/// * whether the [`Radio`] is selected or not
///
/// [`Radio`]: struct.Radio.html
fn draw(
&mut self,
cursor_position: Point,
bounds: Rectangle,
label_bounds: Rectangle,
is_selected: bool,
) -> MouseCursor;
}
impl<'a, Color, Message, Renderer> From<Radio<Color, Message>>
for Element<'a, Message, Renderer>
where
Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer + text::Renderer<Color>,
Message: 'static + Copy + std::fmt::Debug,
{
fn from(checkbox: Radio<Color, Message>) -> Element<'a, Message, Renderer> {
Element::new(checkbox)
}
}

View file

@ -1,219 +0,0 @@
use std::hash::Hash;
use crate::{
Align, Element, Event, Hasher, Justify, Layout, MouseCursor, Node, Point,
Style, Widget,
};
/// 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,
children: Vec<Element<'a, Message, Renderer>>,
}
impl<'a, Message, Renderer> std::fmt::Debug for Row<'a, Message, Renderer> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Row")
.field("style", &self.style)
.field("spacing", &self.spacing)
.field("children", &self.children)
.finish()
}
}
impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
/// Creates an empty [`Row`].
///
/// [`Row`]: struct.Row.html
pub fn new() -> Self {
Row {
style: Style::default().fill_width(),
spacing: 0,
children: Vec::new(),
}
}
/// Sets the horizontal spacing _between_ elements in pixels.
///
/// 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 {
self.spacing = px;
self
}
/// Sets the padding of the [`Row`] in pixels.
///
/// [`Row`]: struct.Row.html
pub fn padding(mut self, px: u16) -> Self {
self.style = self.style.padding(px);
self
}
/// Sets the width of the [`Row`] in pixels.
///
/// [`Row`]: struct.Row.html
pub fn width(mut self, width: u16) -> Self {
self.style = self.style.width(width);
self
}
/// Sets the height of the [`Row`] in pixels.
///
/// [`Row`]: struct.Row.html
pub fn height(mut self, height: u16) -> Self {
self.style = self.style.height(height);
self
}
/// Sets the maximum width of the [`Row`] in pixels.
///
/// [`Row`]: struct.Row.html
pub fn max_width(mut self, max_width: u16) -> Self {
self.style = self.style.max_width(max_width);
self
}
/// Sets the maximum height of the [`Row`] in pixels.
///
/// [`Row`]: struct.Row.html
pub fn max_height(mut self, max_height: u16) -> Self {
self.style = self.style.max_height(max_height);
self
}
/// Sets the alignment of the [`Row`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Row`]: struct.Row.html
pub fn align_self(mut self, align: Align) -> Self {
self.style = self.style.align_self(align);
self
}
/// Sets the vertical alignment of the contents of the [`Row`] .
///
/// [`Row`]: struct.Row.html
pub fn align_items(mut self, align: Align) -> Self {
self.style = self.style.align_items(align);
self
}
/// Sets the horizontal distribution strategy for the contents of the
/// [`Row`] .
///
/// [`Row`]: struct.Row.html
pub fn justify_content(mut self, justify: Justify) -> Self {
self.style = self.style.justify_content(justify);
self
}
/// Adds an [`Element`] to the [`Row`].
///
/// [`Element`]: ../struct.Element.html
/// [`Row`]: struct.Row.html
pub fn push<E>(mut self, child: E) -> Row<'a, Message, Renderer>
where
E: Into<Element<'a, Message, Renderer>>,
{
self.children.push(child.into());
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Row<'a, Message, Renderer>
{
fn node(&self, renderer: &Renderer) -> Node {
let mut children: Vec<Node> = self
.children
.iter()
.map(|child| {
let mut node = child.widget.node(renderer);
let mut style = node.0.style();
style.margin.end =
stretch::style::Dimension::Points(f32::from(self.spacing));
node.0.set_style(style);
node
})
.collect();
if let Some(node) = children.last_mut() {
let mut style = node.0.style();
style.margin.end = stretch::style::Dimension::Undefined;
node.0.set_style(style);
}
Node::with_children(self.style, children)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
self.children.iter_mut().zip(layout.children()).for_each(
|(child, layout)| {
child
.widget
.on_event(event, layout, cursor_position, messages)
},
);
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
let mut cursor = MouseCursor::OutOfBounds;
self.children.iter().zip(layout.children()).for_each(
|(child, layout)| {
let new_cursor =
child.widget.draw(renderer, layout, cursor_position);
if new_cursor != MouseCursor::OutOfBounds {
cursor = new_cursor;
}
},
);
cursor
}
fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
self.spacing.hash(state);
for child in &self.children {
child.widget.hash_layout(state);
}
}
}
impl<'a, Message, Renderer> From<Row<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a,
Message: 'static,
{
fn from(row: Row<'a, Message, Renderer>) -> Element<'a, Message, Renderer> {
Element::new(row)
}
}

View file

@ -1,241 +0,0 @@
//! Display an interactive selector of a single value from a range of values.
//!
//! A [`Slider`] has some local [`State`].
//!
//! [`Slider`]: struct.Slider.html
//! [`State`]: struct.State.html
use std::hash::Hash;
use std::ops::RangeInclusive;
use crate::input::{mouse, ButtonState};
use crate::{
Element, Event, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Style,
Widget,
};
/// An horizontal bar and a handle that selects a single value from a range of
/// values.
///
/// A [`Slider`] will try to fill the horizontal space of its container.
///
/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`slider::Renderer`] trait.
///
/// [`Slider`]: struct.Slider.html
/// [`Widget`]: ../trait.Widget.html
/// [`slider::Renderer`]: trait.Renderer.html
///
/// # Example
/// ```
/// use iced::{slider, Slider};
///
/// pub enum Message {
/// SliderChanged(f32),
/// }
///
/// let state = &mut slider::State::new();
/// let value = 50.0;
///
/// Slider::new(state, 0.0..=100.0, value, Message::SliderChanged);
/// ```
///
/// ![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>,
value: f32,
on_change: Box<dyn Fn(f32) -> Message>,
style: Style,
}
impl<'a, Message> std::fmt::Debug for Slider<'a, Message> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Slider")
.field("state", &self.state)
.field("range", &self.range)
.field("value", &self.value)
.field("style", &self.style)
.finish()
}
}
impl<'a, Message> Slider<'a, Message> {
/// Creates a new [`Slider`].
///
/// It expects:
/// * the local [`State`] of the [`Slider`]
/// * an inclusive range of possible values
/// * the current value of the [`Slider`]
/// * a function that will be called when the [`Slider`] is dragged.
/// It receives the new value of the [`Slider`] and must produce a
/// `Message`.
///
/// [`Slider`]: struct.Slider.html
/// [`State`]: struct.State.html
pub fn new<F>(
state: &'a mut State,
range: RangeInclusive<f32>,
value: f32,
on_change: F,
) -> Self
where
F: 'static + Fn(f32) -> Message,
{
Slider {
state,
value: value.max(*range.start()).min(*range.end()),
range,
on_change: Box::new(on_change),
style: Style::default().min_width(100).fill_width(),
}
}
/// Sets the width of the [`Slider`] in pixels.
///
/// [`Slider`]: struct.Slider.html
pub fn width(mut self, width: u16) -> Self {
self.style = self.style.width(width);
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message>
where
Renderer: self::Renderer,
{
fn node(&self, _renderer: &Renderer) -> Node {
Node::new(self.style.height(25))
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
) {
let mut change = || {
let bounds = layout.bounds();
if cursor_position.x <= bounds.x {
messages.push((self.on_change)(*self.range.start()));
} else if cursor_position.x >= bounds.x + bounds.width {
messages.push((self.on_change)(*self.range.end()));
} else {
let percent = (cursor_position.x - bounds.x) / bounds.width;
let value = (self.range.end() - self.range.start()) * percent
+ self.range.start();
messages.push((self.on_change)(value));
}
};
match event {
Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left,
state,
}) => match state {
ButtonState::Pressed => {
if layout.bounds().contains(cursor_position) {
change();
self.state.is_dragging = true;
}
}
ButtonState::Released => {
self.state.is_dragging = false;
}
},
Event::Mouse(mouse::Event::CursorMoved { .. }) => {
if self.state.is_dragging {
change();
}
}
_ => {}
}
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> MouseCursor {
renderer.draw(
cursor_position,
layout.bounds(),
self.state,
self.range.clone(),
self.value,
)
}
fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
}
}
/// The local state of a [`Slider`].
///
/// [`Slider`]: struct.Slider.html
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct State {
is_dragging: bool,
}
impl State {
/// Creates a new [`State`].
///
/// [`State`]: struct.State.html
pub fn new() -> State {
State::default()
}
/// Returns whether the associated [`Slider`] is currently being dragged or
/// not.
///
/// [`Slider`]: struct.Slider.html
pub fn is_dragging(&self) -> bool {
self.is_dragging
}
}
/// The renderer of a [`Slider`].
///
/// Your [renderer] will need to implement this trait before being
/// able to use a [`Slider`] in your user interface.
///
/// [`Slider`]: struct.Slider.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer {
/// Draws a [`Slider`].
///
/// It receives:
/// * the current cursor position
/// * the bounds of the [`Slider`]
/// * the local state of the [`Slider`]
/// * the range of values of the [`Slider`]
/// * the current value of the [`Slider`]
///
/// [`Slider`]: struct.Slider.html
/// [`State`]: struct.State.html
/// [`Class`]: enum.Class.html
fn draw(
&mut self,
cursor_position: Point,
bounds: Rectangle,
state: &State,
range: RangeInclusive<f32>,
value: f32,
) -> MouseCursor;
}
impl<'a, Message, Renderer> From<Slider<'a, Message>>
for Element<'a, Message, Renderer>
where
Renderer: self::Renderer,
Message: 'static,
{
fn from(slider: Slider<'a, Message>) -> Element<'a, Message, Renderer> {
Element::new(slider)
}
}

View file

@ -1,224 +0,0 @@
//! Write some text for your users to read.
use crate::{
Element, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Style, Widget,
};
use std::hash::Hash;
/// A fragment of text with a generic `Color`.
///
/// It implements [`Widget`] when the associated `Renderer` implements the
/// [`text::Renderer`] trait.
///
/// [`Widget`]: ../trait.Widget.html
/// [`text::Renderer`]: trait.Renderer.html
///
/// # Example
///
/// ```
/// use iced::Text;
///
/// #[derive(Debug, Clone, Copy)]
/// pub enum Color {
/// Black,
/// }
///
/// Text::new("I <3 iced!")
/// .size(40)
/// .color(Color::Black);
/// ```
#[derive(Debug, Clone)]
pub struct Text<Color> {
content: String,
size: Option<u16>,
color: Option<Color>,
style: Style,
horizontal_alignment: HorizontalAlignment,
vertical_alignment: VerticalAlignment,
}
impl<Color> Text<Color> {
/// Create a new fragment of [`Text`] with the given contents.
///
/// [`Text`]: struct.Text.html
pub fn new(label: &str) -> Self {
Text {
content: String::from(label),
size: None,
color: None,
style: Style::default().fill_width(),
horizontal_alignment: HorizontalAlignment::Left,
vertical_alignment: VerticalAlignment::Top,
}
}
/// Sets the size of the [`Text`] in pixels.
///
/// [`Text`]: struct.Text.html
pub fn size(mut self, size: u16) -> Self {
self.size = Some(size);
self
}
/// Sets the `Color` of the [`Text`].
///
/// [`Text`]: struct.Text.html
pub fn color(mut self, color: Color) -> Self {
self.color = Some(color);
self
}
/// Sets the width of the [`Text`] boundaries in pixels.
///
/// [`Text`]: struct.Text.html
pub fn width(mut self, width: u16) -> Self {
self.style = self.style.width(width);
self
}
/// Sets the height of the [`Text`] boundaries in pixels.
///
/// [`Text`]: struct.Text.html
pub fn height(mut self, height: u16) -> Self {
self.style = self.style.height(height);
self
}
/// Sets the [`HorizontalAlignment`] of the [`Text`].
///
/// [`Text`]: struct.Text.html
/// [`HorizontalAlignment`]: enum.HorizontalAlignment.html
pub fn horizontal_alignment(
mut self,
alignment: HorizontalAlignment,
) -> Self {
self.horizontal_alignment = alignment;
self
}
/// Sets the [`VerticalAlignment`] of the [`Text`].
///
/// [`Text`]: struct.Text.html
/// [`VerticalAlignment`]: enum.VerticalAlignment.html
pub fn vertical_alignment(mut self, alignment: VerticalAlignment) -> Self {
self.vertical_alignment = alignment;
self
}
}
impl<Message, Renderer, Color> Widget<Message, Renderer> for Text<Color>
where
Color: Copy + std::fmt::Debug,
Renderer: self::Renderer<Color>,
{
fn node(&self, renderer: &Renderer) -> Node {
renderer.node(self.style, &self.content, self.size)
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
_cursor_position: Point,
) -> MouseCursor {
renderer.draw(
layout.bounds(),
&self.content,
self.size,
self.color,
self.horizontal_alignment,
self.vertical_alignment,
);
MouseCursor::OutOfBounds
}
fn hash_layout(&self, state: &mut Hasher) {
self.style.hash(state);
self.content.hash(state);
self.size.hash(state);
}
}
/// The renderer of a [`Text`] fragment with a generic `Color`.
///
/// Your [renderer] will need to implement this trait before being
/// able to use [`Text`] in your [`UserInterface`].
///
/// [`Text`]: struct.Text.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.
///
/// You should probably use [`Node::with_measure`] to allow [`Text`] to
/// adapt to the dimensions of its container.
///
/// [`Node`]: ../../struct.Node.html
/// [`Style`]: ../../struct.Style.html
/// [`Text`]: struct.Text.html
/// [`Node::with_measure`]: ../../struct.Node.html#method.with_measure
fn node(&self, style: Style, content: &str, size: Option<u16>) -> Node;
/// Draws a [`Text`] fragment.
///
/// It receives:
/// * the bounds of the [`Text`]
/// * the contents of the [`Text`]
/// * the size of the [`Text`]
/// * the color of the [`Text`]
/// * the [`HorizontalAlignment`] of the [`Text`]
/// * the [`VerticalAlignment`] of the [`Text`]
///
/// [`Text`]: struct.Text.html
/// [`HorizontalAlignment`]: enum.HorizontalAlignment.html
/// [`VerticalAlignment`]: enum.VerticalAlignment.html
fn draw(
&mut self,
bounds: Rectangle,
content: &str,
size: Option<u16>,
color: Option<Color>,
horizontal_alignment: HorizontalAlignment,
vertical_alignment: VerticalAlignment,
);
}
impl<'a, Message, Renderer, Color> From<Text<Color>>
for Element<'a, Message, Renderer>
where
Color: 'static + Copy + std::fmt::Debug,
Renderer: self::Renderer<Color>,
{
fn from(text: Text<Color>) -> Element<'a, Message, Renderer> {
Element::new(text)
}
}
/// The horizontal alignment of some resource.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HorizontalAlignment {
/// Align left
Left,
/// Horizontally centered
Center,
/// Align right
Right,
}
/// The vertical alignment of some resource.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerticalAlignment {
/// Align top
Top,
/// Vertically centered
Center,
/// Align bottom
Bottom,
}