Make iced_core::Button customizable

Now it supports:
  - Any kind of content
  - Custom border radius
  - Custom background
This commit is contained in:
Héctor Ramón Jiménez 2019-10-08 03:13:41 +02:00
parent a0234d5bce
commit 10e10e5e06
35 changed files with 288 additions and 160 deletions

View file

@ -19,6 +19,8 @@ members = [
"core", "core",
"native", "native",
"web", "web",
"wgpu",
"winit",
] ]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]

7
core/src/background.rs Normal file
View file

@ -0,0 +1,7 @@
use crate::Color;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Background {
Color(Color),
// TODO: Add gradient and image variants
}

View file

@ -17,6 +17,14 @@ impl Color {
a: 1.0, a: 1.0,
}; };
/// The white color.
pub const WHITE: Color = Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
pub fn into_linear(self) -> [f32; 4] { pub fn into_linear(self) -> [f32; 4] {
// As described in: // As described in:
// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation // https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation

View file

@ -1,6 +1,7 @@
pub mod widget; pub mod widget;
mod align; mod align;
mod background;
mod color; mod color;
mod justify; mod justify;
mod length; mod length;
@ -9,6 +10,7 @@ mod rectangle;
mod vector; mod vector;
pub use align::Align; pub use align::Align;
pub use background::Background;
pub use color::Color; pub use color::Color;
pub use justify::Justify; pub use justify::Justify;
pub use length::Length; pub use length::Length;

View file

@ -5,68 +5,58 @@
//! [`Button`]: struct.Button.html //! [`Button`]: struct.Button.html
//! [`State`]: struct.State.html //! [`State`]: struct.State.html
use crate::{Align, Length}; use crate::{Align, Background, Length};
/// A generic widget that produces a message when clicked. /// A generic widget that produces a message when clicked.
/// pub struct Button<'a, Message, Element> {
/// # Example
///
/// ```
/// use iced_core::{button, Button};
///
/// pub enum Message {
/// ButtonClicked,
/// }
///
/// let state = &mut button::State::new();
///
/// Button::new(state, "Click me!")
/// .on_press(Message::ButtonClicked);
/// ```
///
/// ![Button drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/button.png?raw=true)
pub struct Button<'a, Message> {
/// The current state of the button /// The current state of the button
pub state: &'a mut State, pub state: &'a mut State,
/// The label of the button pub content: Element,
pub label: String,
/// The message to produce when the button is pressed /// The message to produce when the button is pressed
pub on_press: Option<Message>, pub on_press: Option<Message>,
pub class: Class,
pub width: Length, pub width: Length,
pub padding: u16,
pub background: Option<Background>,
pub border_radius: u16,
pub align_self: Option<Align>, pub align_self: Option<Align>,
} }
impl<'a, Message> std::fmt::Debug for Button<'a, Message> impl<'a, Message, Element> std::fmt::Debug for Button<'a, Message, Element>
where where
Message: std::fmt::Debug, Message: std::fmt::Debug,
{ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Button") f.debug_struct("Button")
.field("state", &self.state) .field("state", &self.state)
.field("label", &self.label)
.field("on_press", &self.on_press) .field("on_press", &self.on_press)
.finish() .finish()
} }
} }
impl<'a, Message> Button<'a, Message> { impl<'a, Message, Element> Button<'a, Message, Element> {
/// Creates a new [`Button`] with some local [`State`] and the given label. /// Creates a new [`Button`] with some local [`State`] and the given label.
/// ///
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
/// [`State`]: struct.State.html /// [`State`]: struct.State.html
pub fn new(state: &'a mut State, label: &str) -> Self { pub fn new<E>(state: &'a mut State, content: E) -> Self
where
E: Into<Element>,
{
Button { Button {
state, state,
label: String::from(label), content: content.into(),
on_press: None, on_press: None,
class: Class::Primary,
width: Length::Shrink, width: Length::Shrink,
padding: 0,
background: None,
border_radius: 0,
align_self: None, align_self: None,
} }
} }
@ -79,6 +69,21 @@ impl<'a, Message> Button<'a, Message> {
self self
} }
pub fn padding(mut self, padding: u16) -> Self {
self.padding = padding;
self
}
pub fn background(mut self, background: Background) -> Self {
self.background = Some(background);
self
}
pub fn border_radius(mut self, border_radius: u16) -> Self {
self.border_radius = border_radius;
self
}
/// Sets the alignment of the [`Button`] itself. /// Sets the alignment of the [`Button`] itself.
/// ///
/// This is useful if you want to override the default alignment given by /// This is useful if you want to override the default alignment given by
@ -90,16 +95,6 @@ impl<'a, Message> Button<'a, Message> {
self 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. /// Sets the message that will be produced when the [`Button`] is pressed.
/// ///
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
@ -133,26 +128,3 @@ impl State {
self.is_pressed 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,
}

View file

@ -1,5 +1,5 @@
//! Write some text for your users to read. //! Write some text for your users to read.
use crate::{Color, Length}; use crate::{Align, Color, Length};
/// A paragraph of text. /// A paragraph of text.
/// ///

View file

@ -1,7 +1,7 @@
use iced::{ use iced::{
button, slider, text::HorizontalAlignment, Align, Button, Checkbox, Color, button, slider, text::HorizontalAlignment, Align, Background, Button,
Column, Element, Image, Justify, Length, Radio, Row, Slider, Text, Checkbox, Color, Column, Element, Image, Justify, Length, Radio, Row,
UserInterface, Slider, Text, UserInterface,
}; };
pub fn main() { pub fn main() {
@ -59,9 +59,8 @@ impl UserInterface for Tour {
if steps.has_previous() { if steps.has_previous() {
controls = controls.push( controls = controls.push(
Button::new(back_button, "Back") secondary_button(back_button, "Back")
.on_press(Message::BackPressed) .on_press(Message::BackPressed),
.class(button::Class::Secondary),
); );
} }
@ -69,7 +68,8 @@ impl UserInterface for Tour {
if steps.can_continue() { if steps.can_continue() {
controls = controls.push( controls = controls.push(
Button::new(next_button, "Next").on_press(Message::NextPressed), primary_button(next_button, "Next")
.on_press(Message::NextPressed),
); );
} }
@ -546,6 +546,44 @@ impl<'a> Step {
} }
} }
fn button<'a, Message>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
Button::new(
state,
Text::new(label)
.color(Color::WHITE)
.horizontal_alignment(HorizontalAlignment::Center),
)
.padding(10)
.border_radius(10)
}
fn primary_button<'a, Message>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
button(state, label).background(Background::Color(Color {
r: 0.3,
g: 0.3,
b: 0.8,
a: 1.0,
}))
}
fn secondary_button<'a, Message>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
button(state, label).background(Background::Color(Color {
r: 0.8,
g: 0.8,
b: 0.8,
a: 1.0,
}))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language { pub enum Language {
Rust, Rust,

View file

@ -41,6 +41,10 @@ where
} }
} }
pub fn node(&self, renderer: &Renderer) -> Node {
self.widget.node(renderer)
}
pub fn draw( pub fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
@ -97,22 +101,22 @@ where
/// ///
/// ``` /// ```
/// # mod counter { /// # mod counter {
/// # use iced_native::{button, Button}; /// # use iced_native::{text, Text};
/// # /// #
/// # #[derive(Debug, Clone, Copy)] /// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {} /// # pub enum Message {}
/// # pub struct Counter(button::State); /// # pub struct Counter;
/// # /// #
/// # impl Counter { /// # impl Counter {
/// # pub fn view(&mut self) -> Button<Message> { /// # pub fn view(&mut self) -> Text {
/// # Button::new(&mut self.0, "_") /// # Text::new("")
/// # } /// # }
/// # } /// # }
/// # } /// # }
/// # /// #
/// # mod iced_wgpu { /// # mod iced_wgpu {
/// # use iced_native::{ /// # use iced_native::{
/// # button, row, Button, Node, Point, Rectangle, Style, Layout, Row /// # text, row, Text, Node, Point, Rectangle, Style, Layout, Row
/// # }; /// # };
/// # pub struct Renderer; /// # pub struct Renderer;
/// # /// #
@ -127,16 +131,15 @@ where
/// # ) {} /// # ) {}
/// # } /// # }
/// # /// #
/// # impl button::Renderer for Renderer { /// # impl text::Renderer for Renderer {
/// # fn node<Message>(&self, _button: &Button<'_, Message>) -> Node { /// # fn node(&self, _text: &Text) -> Node {
/// # Node::new(Style::default()) /// # Node::new(Style::default())
/// # } /// # }
/// # /// #
/// # fn draw<Message>( /// # fn draw(
/// # &mut self, /// # &mut self,
/// # _button: &Button<'_, Message>, /// # _text: &Text,
/// # _layout: Layout<'_>, /// # _layout: Layout<'_>,
/// # _cursor_position: Point,
/// # ) {} /// # ) {}
/// # } /// # }
/// # } /// # }
@ -289,7 +292,7 @@ where
A: Copy, A: Copy,
Renderer: crate::Renderer, Renderer: crate::Renderer,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
self.widget.node(renderer) self.widget.node(renderer)
} }
@ -359,7 +362,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
where where
Renderer: crate::Renderer + renderer::Debugger, Renderer: crate::Renderer + renderer::Debugger,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
self.element.widget.node(renderer) self.element.widget.node(renderer)
} }

View file

@ -89,14 +89,14 @@
//! # impl button::Renderer for Renderer { //! # impl button::Renderer for Renderer {
//! # fn node<Message>( //! # fn node<Message>(
//! # &self, //! # &self,
//! # _button: &Button<'_, Message> //! # _button: &Button<'_, Message, Self>
//! # ) -> Node { //! # ) -> Node {
//! # Node::new(Style::default()) //! # Node::new(Style::default())
//! # } //! # }
//! # //! #
//! # fn draw<Message>( //! # fn draw<Message>(
//! # &mut self, //! # &mut self,
//! # _button: &Button<'_, Message>, //! # _button: &Button<'_, Message, Self>,
//! # _layout: Layout<'_>, //! # _layout: Layout<'_>,
//! # _cursor_position: Point, //! # _cursor_position: Point,
//! # ) {} //! # ) {}
@ -125,7 +125,7 @@
//! .push( //! .push(
//! // The increment button. We tell it to produce an //! // The increment button. We tell it to produce an
//! // `IncrementPressed` message when pressed //! // `IncrementPressed` message when pressed
//! Button::new(&mut self.increment_button, "+") //! Button::new(&mut self.increment_button, Text::new("+"))
//! .on_press(Message::IncrementPressed), //! .on_press(Message::IncrementPressed),
//! ) //! )
//! .push( //! .push(
@ -135,7 +135,7 @@
//! .push( //! .push(
//! // The decrement button. We tell it to produce a //! // The decrement button. We tell it to produce a
//! // `DecrementPressed` message when pressed //! // `DecrementPressed` message when pressed
//! Button::new(&mut self.decrement_button, "-") //! Button::new(&mut self.decrement_button, Text::new("-"))
//! .on_press(Message::DecrementPressed), //! .on_press(Message::DecrementPressed),
//! ) //! )
//! } //! }
@ -212,7 +212,9 @@ mod user_interface;
pub(crate) use iced_core::Vector; pub(crate) use iced_core::Vector;
pub use iced_core::{Align, Color, Justify, Length, Point, Rectangle}; pub use iced_core::{
Align, Background, Color, Justify, Length, Point, Rectangle,
};
#[doc(no_inline)] #[doc(no_inline)]
pub use stretch::{geometry::Size, number::Number}; pub use stretch::{geometry::Size, number::Number};

View file

@ -74,12 +74,12 @@ impl Style {
self self
} }
pub(crate) fn align_items(mut self, align: Align) -> Self { pub fn align_items(mut self, align: Align) -> Self {
self.0.align_items = into_align_items(align); self.0.align_items = into_align_items(align);
self self
} }
pub(crate) fn justify_content(mut self, justify: Justify) -> Self { pub fn justify_content(mut self, justify: Justify) -> Self {
self.0.justify_content = into_justify_content(justify); self.0.justify_content = into_justify_content(justify);
self self
} }

View file

@ -67,7 +67,7 @@ where
/// [`Node`]: ../struct.Node.html /// [`Node`]: ../struct.Node.html
/// [`Widget`]: trait.Widget.html /// [`Widget`]: trait.Widget.html
/// [`Layout`]: ../struct.Layout.html /// [`Layout`]: ../struct.Layout.html
fn node(&self, renderer: &mut Renderer) -> Node; fn node(&self, renderer: &Renderer) -> Node;
/// Draws the [`Widget`] using the associated `Renderer`. /// Draws the [`Widget`] using the associated `Renderer`.
/// ///

View file

@ -10,14 +10,18 @@ use crate::input::{mouse, ButtonState};
use crate::{Element, Event, Hasher, Layout, Node, Point, Widget}; use crate::{Element, Event, Hasher, Layout, Node, Point, Widget};
use std::hash::Hash; use std::hash::Hash;
pub use iced_core::button::*; pub use iced_core::button::State;
impl<'a, Message, Renderer> Widget<Message, Renderer> for Button<'a, Message> pub type Button<'a, Message, Renderer> =
iced_core::Button<'a, Message, Element<'a, Message, Renderer>>;
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Button<'a, Message, Renderer>
where where
Renderer: self::Renderer, Renderer: self::Renderer,
Message: Copy + std::fmt::Debug, Message: Copy + std::fmt::Debug,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
renderer.node(&self) renderer.node(&self)
} }
@ -68,9 +72,9 @@ where
} }
fn hash_layout(&self, state: &mut Hasher) { fn hash_layout(&self, state: &mut Hasher) {
self.label.hash(state);
self.width.hash(state); self.width.hash(state);
self.align_self.hash(state); self.align_self.hash(state);
self.content.hash_layout(state);
} }
} }
@ -81,31 +85,33 @@ where
/// ///
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer + Sized {
/// Creates a [`Node`] for the provided [`Button`]. /// Creates a [`Node`] for the provided [`Button`].
/// ///
/// [`Node`]: ../../struct.Node.html /// [`Node`]: ../../struct.Node.html
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
fn node<Message>(&self, button: &Button<'_, Message>) -> Node; fn node<Message>(&self, button: &Button<'_, Message, Self>) -> Node;
/// Draws a [`Button`]. /// Draws a [`Button`].
/// ///
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
button: &Button<'_, Message>, button: &Button<'_, Message, Self>,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Self::Primitive; ) -> Self::Primitive;
} }
impl<'a, Message, Renderer> From<Button<'a, Message>> impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: self::Renderer, Renderer: 'static + self::Renderer,
Message: 'static + Copy + std::fmt::Debug, Message: 'static + Copy + std::fmt::Debug,
{ {
fn from(button: Button<'a, Message>) -> Element<'a, Message, Renderer> { fn from(
button: Button<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(button) Element::new(button)
} }
} }

View file

@ -10,7 +10,7 @@ impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>
where where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
renderer.node(&self) renderer.node(&self)
} }
@ -64,7 +64,7 @@ pub trait Renderer: crate::Renderer {
/// ///
/// [`Node`]: ../../struct.Node.html /// [`Node`]: ../../struct.Node.html
/// [`Checkbox`]: struct.Checkbox.html /// [`Checkbox`]: struct.Checkbox.html
fn node<Message>(&mut self, checkbox: &Checkbox<Message>) -> Node; fn node<Message>(&self, checkbox: &Checkbox<Message>) -> Node;
/// Draws a [`Checkbox`]. /// Draws a [`Checkbox`].
/// ///

View file

@ -11,7 +11,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
where where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
let mut children: Vec<Node> = self let mut children: Vec<Node> = self
.children .children
.iter() .iter()

View file

@ -11,7 +11,7 @@ where
Renderer: self::Renderer<I>, Renderer: self::Renderer<I>,
I: Clone, I: Clone,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
renderer.node(&self) renderer.node(&self)
} }
@ -45,7 +45,7 @@ pub trait Renderer<I>: crate::Renderer {
/// ///
/// [`Node`]: ../../struct.Node.html /// [`Node`]: ../../struct.Node.html
/// [`Image`]: struct.Image.html /// [`Image`]: struct.Image.html
fn node(&mut self, image: &Image<I>) -> Node; fn node(&self, image: &Image<I>) -> Node;
/// Draws an [`Image`]. /// Draws an [`Image`].
/// ///

View file

@ -11,7 +11,7 @@ where
Renderer: self::Renderer, Renderer: self::Renderer,
Message: Copy + std::fmt::Debug, Message: Copy + std::fmt::Debug,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
renderer.node(&self) renderer.node(&self)
} }
@ -61,7 +61,7 @@ pub trait Renderer: crate::Renderer {
/// ///
/// [`Node`]: ../../struct.Node.html /// [`Node`]: ../../struct.Node.html
/// [`Radio`]: struct.Radio.html /// [`Radio`]: struct.Radio.html
fn node<Message>(&mut self, radio: &Radio<Message>) -> Node; fn node<Message>(&self, radio: &Radio<Message>) -> Node;
/// Draws a [`Radio`] button. /// Draws a [`Radio`] button.
/// ///

View file

@ -11,7 +11,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
where where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
let mut children: Vec<Node> = self let mut children: Vec<Node> = self
.children .children
.iter() .iter()

View file

@ -15,7 +15,7 @@ impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message>
where where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
renderer.node(&self) renderer.node(&self)
} }

View file

@ -9,7 +9,7 @@ impl<Message, Renderer> Widget<Message, Renderer> for Text
where where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
fn node(&self, renderer: &mut Renderer) -> Node { fn node(&self, renderer: &Renderer) -> Node {
renderer.node(&self) renderer.node(&self)
} }

View file

@ -1,12 +1,13 @@
pub use iced_wgpu::{Primitive, Renderer}; pub use iced_wgpu::{Primitive, Renderer};
pub use iced_winit::{ pub use iced_winit::{
button, slider, text, winit, Align, Button, Checkbox, Color, Image, button, slider, text, winit, Align, Background, Checkbox, Color, Image,
Justify, Length, Radio, Slider, Text, Justify, Length, Radio, Slider, Text,
}; };
pub type Element<'a, Message> = iced_winit::Element<'a, Message, Renderer>; pub type Element<'a, Message> = iced_winit::Element<'a, Message, Renderer>;
pub type Row<'a, Message> = iced_winit::Row<'a, Message, Renderer>; pub type Row<'a, Message> = iced_winit::Row<'a, Message, Renderer>;
pub type Column<'a, Message> = iced_winit::Column<'a, Message, Renderer>; pub type Column<'a, Message> = iced_winit::Column<'a, Message, Renderer>;
pub type Button<'a, Message> = iced_winit::Button<'a, Message, Renderer>;
pub trait UserInterface { pub trait UserInterface {
type Message; type Message;

View file

@ -14,6 +14,14 @@ impl<'a, Message> Element<'a, Message> {
} }
} }
pub fn node<'b>(
&self,
bump: &'b bumpalo::Bump,
bus: &Bus<Message>,
) -> dodrio::Node<'b> {
self.widget.node(bump, bus)
}
pub fn explain(self, _color: Color) -> Element<'a, Message> { pub fn explain(self, _color: Color) -> Element<'a, Message> {
self self
} }

View file

@ -2,7 +2,10 @@ use crate::{Bus, Element, Widget};
use dodrio::bumpalo; use dodrio::bumpalo;
pub use iced_core::button::*; pub use iced_core::button::State;
pub type Button<'a, Message> =
iced_core::Button<'a, Message, Element<'a, Message>>;
impl<'a, Message> Widget<Message> for Button<'a, Message> impl<'a, Message> Widget<Message> for Button<'a, Message>
where where
@ -15,9 +18,8 @@ where
) -> dodrio::Node<'b> { ) -> dodrio::Node<'b> {
use dodrio::builder::*; use dodrio::builder::*;
let label = bumpalo::format!(in bump, "{}", self.label); let mut node =
button(bump).children(vec![self.content.node(bump, bus)]);
let mut node = button(bump).children(vec![text(label.into_bump_str())]);
if let Some(on_press) = self.on_press { if let Some(on_press) = self.on_press {
let event_bus = bus.clone(); let event_bus = bus.clone();

View file

@ -8,5 +8,5 @@ pub(crate) use quad::Quad;
pub(crate) use transformation::Transformation; pub(crate) use transformation::Transformation;
pub use mouse_cursor::MouseCursor; pub use mouse_cursor::MouseCursor;
pub use primitive::{Background, Primitive}; pub use primitive::Primitive;
pub use renderer::{Renderer, Target}; pub use renderer::{Renderer, Target};

View file

@ -1,4 +1,4 @@
use iced_native::{Color, Rectangle}; use iced_native::{text, Background, Color, Rectangle};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Primitive { pub enum Primitive {
@ -9,16 +9,14 @@ pub enum Primitive {
Text { Text {
content: String, content: String,
bounds: Rectangle, bounds: Rectangle,
color: Color,
size: f32, size: f32,
horizontal_alignment: text::HorizontalAlignment,
vertical_alignment: text::VerticalAlignment,
}, },
Quad { Quad {
bounds: Rectangle, bounds: Rectangle,
background: Background, background: Background,
border_radius: u16,
}, },
} }
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Background {
Color(Color),
// TODO: Add gradient and image variants
}

View file

@ -123,6 +123,11 @@ impl Pipeline {
format: wgpu::VertexFormat::Float4, format: wgpu::VertexFormat::Float4,
offset: 4 * (2 + 2), offset: 4 * (2 + 2),
}, },
wgpu::VertexAttributeDescriptor {
shader_location: 4,
format: wgpu::VertexFormat::Uint,
offset: 4 * (2 + 2 + 4),
},
], ],
}, },
], ],
@ -262,6 +267,7 @@ pub struct Quad {
pub position: [f32; 2], pub position: [f32; 2],
pub scale: [f32; 2], pub scale: [f32; 2],
pub color: [f32; 4], pub color: [f32; 4],
pub border_radius: u32,
} }
impl Quad { impl Quad {

View file

@ -1,5 +1,7 @@
use crate::{quad, Background, Primitive, Quad, Transformation}; use crate::{quad, Primitive, Quad, Transformation};
use iced_native::{renderer::Debugger, Color, Layout, Point, Widget}; use iced_native::{
renderer::Debugger, Background, Color, Layout, Point, Widget,
};
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use wgpu::{ use wgpu::{
@ -159,20 +161,74 @@ impl Renderer {
content, content,
bounds, bounds,
size, size,
} => self.glyph_brush.borrow_mut().queue(Section { color,
text: &content, horizontal_alignment,
screen_position: (bounds.x, bounds.y), vertical_alignment,
bounds: (bounds.width, bounds.height), } => {
scale: wgpu_glyph::Scale { x: *size, y: *size }, let x = match horizontal_alignment {
..Default::default() iced_native::text::HorizontalAlignment::Left => bounds.x,
}), iced_native::text::HorizontalAlignment::Center => {
Primitive::Quad { bounds, background } => { bounds.x + bounds.width / 2.0
}
iced_native::text::HorizontalAlignment::Right => {
bounds.x + bounds.width
}
};
let y = match vertical_alignment {
iced_native::text::VerticalAlignment::Top => bounds.y,
iced_native::text::VerticalAlignment::Center => {
bounds.y + bounds.height / 2.0
}
iced_native::text::VerticalAlignment::Bottom => {
bounds.y + bounds.height
}
};
self.glyph_brush.borrow_mut().queue(Section {
text: &content,
screen_position: (x, y),
bounds: (bounds.width, bounds.height),
scale: wgpu_glyph::Scale { x: *size, y: *size },
color: color.into_linear(),
layout: wgpu_glyph::Layout::default()
.h_align(match horizontal_alignment {
iced_native::text::HorizontalAlignment::Left => {
wgpu_glyph::HorizontalAlign::Left
}
iced_native::text::HorizontalAlignment::Center => {
wgpu_glyph::HorizontalAlign::Center
}
iced_native::text::HorizontalAlignment::Right => {
wgpu_glyph::HorizontalAlign::Right
}
})
.v_align(match vertical_alignment {
iced_native::text::VerticalAlignment::Top => {
wgpu_glyph::VerticalAlign::Top
}
iced_native::text::VerticalAlignment::Center => {
wgpu_glyph::VerticalAlign::Center
}
iced_native::text::VerticalAlignment::Bottom => {
wgpu_glyph::VerticalAlign::Bottom
}
}),
..Default::default()
})
}
Primitive::Quad {
bounds,
background,
border_radius,
} => {
self.quads.push(Quad { self.quads.push(Quad {
position: [bounds.x, bounds.y], position: [bounds.x, bounds.y],
scale: [bounds.width, bounds.height], scale: [bounds.width, bounds.height],
color: match background { color: match background {
Background::Color(color) => color.into_linear(), Background::Color(color) => color.into_linear(),
}, },
border_radius: u32::from(*border_radius),
}); });
} }
} }

View file

@ -1,22 +1,26 @@
use crate::{Background, Primitive, Renderer}; use crate::{Primitive, Renderer};
use iced_native::{button, Button, Color, Layout, Length, Node, Point, Style}; use iced_native::{
button, Align, Background, Button, Color, Layout, Length, Node, Point,
Style,
};
impl button::Renderer for Renderer { impl button::Renderer for Renderer {
fn node<Message>(&self, button: &Button<Message>) -> Node { fn node<Message>(&self, button: &Button<Message, Self>) -> Node {
let style = Style::default() let style = Style::default()
.width(button.width) .width(button.width)
.min_height(Length::Units(30)) .padding(button.padding)
.min_width(Length::Units(100)) .min_width(Length::Units(100))
.align_self(button.align_self); .align_self(button.align_self)
.align_items(Align::Stretch);
Node::new(style) Node::with_children(style, vec![button.content.node(self)])
} }
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
button: &Button<Message>, button: &Button<Message, Self>,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: Point, cursor_position: Point,
) -> Self::Primitive { ) -> Self::Primitive {
let bounds = layout.bounds(); let bounds = layout.bounds();
@ -24,18 +28,21 @@ impl button::Renderer for Renderer {
primitives: vec![ primitives: vec![
Primitive::Quad { Primitive::Quad {
bounds, bounds,
background: Background::Color(Color { background: button.background.unwrap_or(Background::Color(
r: 0.8, Color {
b: 0.8, r: 0.8,
g: 0.8, b: 0.8,
a: 1.0, g: 0.8,
}), a: 1.0,
}, },
Primitive::Text { )),
content: button.label.clone(), border_radius: button.border_radius,
size: 20.0,
bounds: layout.bounds(),
}, },
button.content.draw(
self,
layout.children().next().unwrap(),
cursor_position,
),
], ],
} }
} }

View file

@ -2,7 +2,7 @@ use crate::{Primitive, Renderer};
use iced_native::{checkbox, Checkbox, Layout, Node, Point, Style}; use iced_native::{checkbox, Checkbox, Layout, Node, Point, Style};
impl checkbox::Renderer for Renderer { impl checkbox::Renderer for Renderer {
fn node<Message>(&mut self, _checkbox: &Checkbox<Message>) -> Node { fn node<Message>(&self, _checkbox: &Checkbox<Message>) -> Node {
Node::new(Style::default()) Node::new(Style::default())
} }

View file

@ -2,7 +2,7 @@ use crate::{Primitive, Renderer};
use iced_native::{image, Image, Layout, Node, Style}; use iced_native::{image, Image, Layout, Node, Style};
impl image::Renderer<&str> for Renderer { impl image::Renderer<&str> for Renderer {
fn node(&mut self, _image: &Image<&str>) -> Node { fn node(&self, _image: &Image<&str>) -> Node {
Node::new(Style::default()) Node::new(Style::default())
} }

View file

@ -2,7 +2,7 @@ use crate::{Primitive, Renderer};
use iced_native::{radio, Layout, Node, Point, Radio, Style}; use iced_native::{radio, Layout, Node, Point, Radio, Style};
impl radio::Renderer for Renderer { impl radio::Renderer for Renderer {
fn node<Message>(&mut self, _checkbox: &Radio<Message>) -> Node { fn node<Message>(&self, _checkbox: &Radio<Message>) -> Node {
Node::new(Style::default()) Node::new(Style::default())
} }

View file

@ -1,5 +1,5 @@
use crate::{Primitive, Renderer}; use crate::{Primitive, Renderer};
use iced_native::{text, Layout, Node, Style, Text}; use iced_native::{text, Color, Layout, Node, Style, Text};
use wgpu_glyph::{GlyphCruncher, Section}; use wgpu_glyph::{GlyphCruncher, Section};
@ -72,6 +72,9 @@ impl text::Renderer for Renderer {
content: text.content.clone(), content: text.content.clone(),
size: f32::from(text.size.unwrap_or(20)), size: f32::from(text.size.unwrap_or(20)),
bounds: layout.bounds(), bounds: layout.bounds(),
color: text.color.unwrap_or(Color::BLACK),
horizontal_alignment: text.horizontal_alignment,
vertical_alignment: text.vertical_alignment,
} }
} }
} }

View file

@ -3,6 +3,7 @@
layout(location = 0) in vec4 v_Color; layout(location = 0) in vec4 v_Color;
layout(location = 1) in vec2 v_Pos; layout(location = 1) in vec2 v_Pos;
layout(location = 2) in vec2 v_Scale; layout(location = 2) in vec2 v_Scale;
layout(location = 3) in flat uint v_BorderRadius;
layout(location = 0) out vec4 o_Color; layout(location = 0) out vec4 o_Color;
@ -26,8 +27,11 @@ float rounded(in vec2 frag_coord, in vec2 position, in vec2 size, float radius,
} }
void main() { void main() {
o_Color = vec4( float radius_alpha = 1.0;
v_Color.xyz,
v_Color.w * rounded(gl_FragCoord.xy, v_Pos, v_Scale, 5.0, 1.0) if(v_BorderRadius > 0.0) {
); radius_alpha = rounded(gl_FragCoord.xy, v_Pos, v_Scale, v_BorderRadius, 1.0);
}
o_Color = vec4(v_Color.xyz, v_Color.w * radius_alpha);
} }

Binary file not shown.

View file

@ -4,6 +4,7 @@ layout(location = 0) in vec2 v_Pos;
layout(location = 1) in vec2 i_Pos; layout(location = 1) in vec2 i_Pos;
layout(location = 2) in vec2 i_Scale; layout(location = 2) in vec2 i_Scale;
layout(location = 3) in vec4 i_Color; layout(location = 3) in vec4 i_Color;
layout(location = 4) in uint i_BorderRadius;
layout (set = 0, binding = 0) uniform Globals { layout (set = 0, binding = 0) uniform Globals {
mat4 u_Transform; mat4 u_Transform;
@ -12,6 +13,7 @@ layout (set = 0, binding = 0) uniform Globals {
layout(location = 0) out vec4 o_Color; layout(location = 0) out vec4 o_Color;
layout(location = 1) out vec2 o_Pos; layout(location = 1) out vec2 o_Pos;
layout(location = 2) out vec2 o_Scale; layout(location = 2) out vec2 o_Scale;
layout(location = 3) out uint o_BorderRadius;
void main() { void main() {
mat4 i_Transform = mat4( mat4 i_Transform = mat4(
@ -24,6 +26,7 @@ void main() {
o_Color = i_Color; o_Color = i_Color;
o_Pos = i_Pos; o_Pos = i_Pos;
o_Scale = i_Scale; o_Scale = i_Scale;
o_BorderRadius = i_BorderRadius;
gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0); gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0);
} }

Binary file not shown.