summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2020-01-09 18:46:06 +0100
committerLibravatar GitHub <noreply@github.com>2020-01-09 18:46:06 +0100
commit0a8302450557877cb667b51fc84383aaf0a11b02 (patch)
treefe3a8a6b0ae82f7fd1fa0c0de34b4b09d0b9edda
parent6699329d3f91c5b9d8e8e55ad88de24bd3894955 (diff)
parent7b278755fc7929633b5771824beac4d39b16e82e (diff)
downloadiced-0a8302450557877cb667b51fc84383aaf0a11b02.tar.gz
iced-0a8302450557877cb667b51fc84383aaf0a11b02.tar.bz2
iced-0a8302450557877cb667b51fc84383aaf0a11b02.zip
Merge pull request #146 from hecrj/feature/custom-styling
Custom styling
-rw-r--r--Cargo.toml1
-rw-r--r--core/src/color.rs22
-rw-r--r--core/src/vector.rs12
-rw-r--r--examples/custom_widget.rs5
-rw-r--r--examples/geometry.rs3
-rw-r--r--examples/pokedex.rs31
-rw-r--r--examples/progress_bar.rs22
-rw-r--r--examples/stopwatch.rs46
-rw-r--r--examples/styling.rs514
-rw-r--r--examples/todos.rs98
-rw-r--r--examples/tour.rs61
-rw-r--r--native/src/element.rs10
-rw-r--r--native/src/renderer.rs14
-rw-r--r--native/src/renderer/debugger.rs1
-rw-r--r--native/src/renderer/null.rs77
-rw-r--r--native/src/renderer/windowed.rs5
-rw-r--r--native/src/user_interface.rs58
-rw-r--r--native/src/widget.rs1
-rw-r--r--native/src/widget/button.rs66
-rw-r--r--native/src/widget/checkbox.rs44
-rw-r--r--native/src/widget/column.rs4
-rw-r--r--native/src/widget/container.rs58
-rw-r--r--native/src/widget/image.rs1
-rw-r--r--native/src/widget/progress_bar.rs51
-rw-r--r--native/src/widget/radio.rs36
-rw-r--r--native/src/widget/row.rs4
-rw-r--r--native/src/widget/scrollable.rs33
-rw-r--r--native/src/widget/slider.rs34
-rw-r--r--native/src/widget/space.rs1
-rw-r--r--native/src/widget/svg.rs1
-rw-r--r--native/src/widget/text.rs3
-rw-r--r--native/src/widget/text_input.rs37
-rw-r--r--src/application.rs7
-rw-r--r--src/native.rs69
-rw-r--r--src/settings.rs6
-rw-r--r--style/Cargo.toml14
-rw-r--r--style/src/button.rs96
-rw-r--r--style/src/checkbox.rs55
-rw-r--r--style/src/container.rs59
-rw-r--r--style/src/lib.rs8
-rw-r--r--style/src/progress_bar.rs42
-rw-r--r--style/src/radio.rs53
-rw-r--r--style/src/scrollable.rs76
-rw-r--r--style/src/slider.rs95
-rw-r--r--style/src/text_input.rs83
-rw-r--r--wgpu/Cargo.toml1
-rw-r--r--wgpu/src/defaults.rs32
-rw-r--r--wgpu/src/lib.rs15
-rw-r--r--wgpu/src/primitive.rs4
-rw-r--r--wgpu/src/quad.rs17
-rw-r--r--wgpu/src/renderer.rs29
-rw-r--r--wgpu/src/renderer/widget.rs1
-rw-r--r--wgpu/src/renderer/widget/button.rs102
-rw-r--r--wgpu/src/renderer/widget/checkbox.rs51
-rw-r--r--wgpu/src/renderer/widget/column.rs3
-rw-r--r--wgpu/src/renderer/widget/container.rs49
-rw-r--r--wgpu/src/renderer/widget/progress_bar.rs49
-rw-r--r--wgpu/src/renderer/widget/radio.rs52
-rw-r--r--wgpu/src/renderer/widget/row.rs3
-rw-r--r--wgpu/src/renderer/widget/scrollable.rs80
-rw-r--r--wgpu/src/renderer/widget/slider.rs81
-rw-r--r--wgpu/src/renderer/widget/text.rs3
-rw-r--r--wgpu/src/renderer/widget/text_input.rs45
-rw-r--r--wgpu/src/settings.rs10
-rw-r--r--wgpu/src/shader/quad.frag51
-rw-r--r--wgpu/src/shader/quad.frag.spvbin3044 -> 4212 bytes
-rw-r--r--wgpu/src/shader/quad.vert14
-rw-r--r--wgpu/src/shader/quad.vert.spvbin3020 -> 3372 bytes
-rw-r--r--wgpu/src/text.rs11
-rw-r--r--wgpu/src/widget.rs34
-rw-r--r--wgpu/src/widget/button.rs15
-rw-r--r--wgpu/src/widget/checkbox.rs9
-rw-r--r--wgpu/src/widget/container.rs10
-rw-r--r--wgpu/src/widget/progress_bar.rs15
-rw-r--r--wgpu/src/widget/radio.rs10
-rw-r--r--wgpu/src/widget/scrollable.rs13
-rw-r--r--wgpu/src/widget/slider.rs16
-rw-r--r--wgpu/src/widget/text_input.rs15
-rw-r--r--winit/Cargo.toml3
-rw-r--r--winit/src/application.rs12
-rw-r--r--winit/src/settings/mod.rs11
81 files changed, 2288 insertions, 595 deletions
diff --git a/Cargo.toml b/Cargo.toml
index ebd6412e..79a9007a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,6 +24,7 @@ maintenance = { status = "actively-developed" }
members = [
"core",
"native",
+ "style",
"web",
"wgpu",
"winit",
diff --git a/core/src/color.rs b/core/src/color.rs
index c28e784f..d6bdd365 100644
--- a/core/src/color.rs
+++ b/core/src/color.rs
@@ -25,6 +25,21 @@ impl Color {
a: 1.0,
};
+ /// A color with no opacity.
+ pub const TRANSPARENT: Color = Color {
+ r: 0.0,
+ g: 0.0,
+ b: 0.0,
+ a: 0.0,
+ };
+
+ /// Creates a [`Color`] from its RGB components.
+ ///
+ /// [`Color`]: struct.Color.html
+ pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color {
+ Color { r, g, b, a: 1.0 }
+ }
+
/// Creates a [`Color`] from its RGB8 components.
///
/// [`Color`]: struct.Color.html
@@ -37,13 +52,6 @@ impl Color {
}
}
- /// Creates a [`Color`] from its RGB components.
- ///
- /// [`Color`]: struct.Color.html
- pub fn from_rgb(r: f32, g: f32, b: f32) -> Color {
- Color { r, g, b, a: 1.0 }
- }
-
/// Converts the [`Color`] into its linear values.
///
/// [`Color`]: struct.Color.html
diff --git a/core/src/vector.rs b/core/src/vector.rs
index 7d87343a..1c09ee3e 100644
--- a/core/src/vector.rs
+++ b/core/src/vector.rs
@@ -31,3 +31,15 @@ where
Self::new(self.x + b.x, self.y + b.y)
}
}
+
+impl<T> Default for Vector<T>
+where
+ T: Default,
+{
+ fn default() -> Self {
+ Self {
+ x: T::default(),
+ y: T::default(),
+ }
+ }
+}
diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs
index c0038cd9..0a570745 100644
--- a/examples/custom_widget.rs
+++ b/examples/custom_widget.rs
@@ -13,7 +13,7 @@ mod circle {
layout, Background, Color, Element, Hasher, Layout, Length,
MouseCursor, Point, Size, Widget,
};
- use iced_wgpu::{Primitive, Renderer};
+ use iced_wgpu::{Defaults, Primitive, Renderer};
pub struct Circle {
radius: u16,
@@ -54,6 +54,7 @@ mod circle {
fn draw(
&self,
_renderer: &mut Renderer,
+ _defaults: &Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> (Primitive, MouseCursor) {
@@ -62,6 +63,8 @@ mod circle {
bounds: layout.bounds(),
background: Background::Color(Color::BLACK),
border_radius: self.radius,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
},
MouseCursor::OutOfBounds,
)
diff --git a/examples/geometry.rs b/examples/geometry.rs
index ae6c9ca0..9d5fd611 100644
--- a/examples/geometry.rs
+++ b/examples/geometry.rs
@@ -16,7 +16,7 @@ mod rainbow {
};
use iced_wgpu::{
triangle::{Mesh2D, Vertex2D},
- Primitive, Renderer,
+ Defaults, Primitive, Renderer,
};
pub struct Rainbow;
@@ -51,6 +51,7 @@ mod rainbow {
fn draw(
&self,
_renderer: &mut Renderer,
+ _defaults: &Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> (Primitive, MouseCursor) {
diff --git a/examples/pokedex.rs b/examples/pokedex.rs
index f44b6163..7326f94f 100644
--- a/examples/pokedex.rs
+++ b/examples/pokedex.rs
@@ -1,6 +1,6 @@
use iced::{
- button, image, Align, Application, Button, Color, Column, Command,
- Container, Element, Image, Length, Row, Settings, Text,
+ button, image, Align, Application, Button, Column, Command, Container,
+ Element, Image, Length, Row, Settings, Text,
};
pub fn main() {
@@ -214,8 +214,29 @@ impl From<surf::Exception> for Error {
}
fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> {
- Button::new(state, Text::new(text).color(Color::WHITE))
- .background(Color::from_rgb(0.11, 0.42, 0.87))
- .border_radius(10)
+ Button::new(state, Text::new(text))
.padding(10)
+ .style(style::Button::Primary)
+}
+
+mod style {
+ use iced::{button, Background, Color, Vector};
+
+ pub enum Button {
+ Primary,
+ }
+
+ impl button::StyleSheet for Button {
+ fn active(&self) -> button::Style {
+ button::Style {
+ background: Some(Background::Color(match self {
+ Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
+ })),
+ border_radius: 12,
+ shadow_offset: Vector::new(1.0, 1.0),
+ text_color: Color::WHITE,
+ ..button::Style::default()
+ }
+ }
+ }
}
diff --git a/examples/progress_bar.rs b/examples/progress_bar.rs
index 525019b4..43b09928 100644
--- a/examples/progress_bar.rs
+++ b/examples/progress_bar.rs
@@ -1,16 +1,7 @@
-use iced::{
- settings::Window, slider, Background, Color, Column, Element, Length,
- ProgressBar, Sandbox, Settings, Slider,
-};
+use iced::{slider, Column, Element, ProgressBar, Sandbox, Settings, Slider};
pub fn main() {
- Progress::run(Settings {
- window: Window {
- size: (700, 300),
- resizable: true,
- decorations: true,
- },
- })
+ Progress::run(Settings::default())
}
#[derive(Default)]
@@ -44,14 +35,7 @@ impl Sandbox for Progress {
fn view(&mut self) -> Element<Message> {
Column::new()
.padding(20)
- .push(
- ProgressBar::new(0.0..=100.0, self.value)
- .background(Background::Color(Color::from_rgb(
- 0.6, 0.6, 0.6,
- )))
- .active_color(Color::from_rgb(0.0, 0.95, 0.0))
- .height(Length::Units(30)),
- )
+ .push(ProgressBar::new(0.0..=100.0, self.value))
.push(Slider::new(
&mut self.progress_bar_slider,
0.0..=100.0,
diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs
index f4d485e2..c9a61ee9 100644
--- a/examples/stopwatch.rs
+++ b/examples/stopwatch.rs
@@ -1,7 +1,6 @@
use iced::{
- button, Align, Application, Background, Button, Color, Column, Command,
- Container, Element, HorizontalAlignment, Length, Row, Settings,
- Subscription, Text,
+ button, Align, Application, Button, Column, Command, Container, Element,
+ HorizontalAlignment, Length, Row, Settings, Subscription, Text,
};
use std::time::{Duration, Instant};
@@ -98,30 +97,29 @@ impl Application for Stopwatch {
))
.size(40);
- let button = |state, label, color: [f32; 3]| {
+ let button = |state, label, style| {
Button::new(
state,
Text::new(label)
- .color(Color::WHITE)
.horizontal_alignment(HorizontalAlignment::Center),
)
.min_width(80)
- .background(Background::Color(color.into()))
- .border_radius(10)
.padding(10)
+ .style(style)
};
let toggle_button = {
let (label, color) = match self.state {
- State::Idle => ("Start", [0.11, 0.42, 0.87]),
- State::Ticking { .. } => ("Stop", [0.9, 0.4, 0.4]),
+ State::Idle => ("Start", style::Button::Primary),
+ State::Ticking { .. } => ("Stop", style::Button::Destructive),
};
button(&mut self.toggle, label, color).on_press(Message::Toggle)
};
- let reset_button = button(&mut self.reset, "Reset", [0.7, 0.7, 0.7])
- .on_press(Message::Reset);
+ let reset_button =
+ button(&mut self.reset, "Reset", style::Button::Secondary)
+ .on_press(Message::Reset);
let controls = Row::new()
.spacing(20)
@@ -177,3 +175,29 @@ mod time {
}
}
}
+
+mod style {
+ use iced::{button, Background, Color, Vector};
+
+ pub enum Button {
+ Primary,
+ Secondary,
+ Destructive,
+ }
+
+ impl button::StyleSheet for Button {
+ fn active(&self) -> button::Style {
+ button::Style {
+ background: Some(Background::Color(match self {
+ Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
+ Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5),
+ Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2),
+ })),
+ border_radius: 12,
+ shadow_offset: Vector::new(1.0, 1.0),
+ text_color: Color::WHITE,
+ ..button::Style::default()
+ }
+ }
+ }
+}
diff --git a/examples/styling.rs b/examples/styling.rs
new file mode 100644
index 00000000..50095ec7
--- /dev/null
+++ b/examples/styling.rs
@@ -0,0 +1,514 @@
+use iced::{
+ button, scrollable, slider, text_input, Align, Button, Checkbox, Column,
+ Container, Element, Length, ProgressBar, Radio, Row, Sandbox, Scrollable,
+ Settings, Slider, Space, Text, TextInput,
+};
+
+pub fn main() {
+ Styling::run(Settings::default())
+}
+
+#[derive(Default)]
+struct Styling {
+ theme: style::Theme,
+ scroll: scrollable::State,
+ input: text_input::State,
+ input_value: String,
+ button: button::State,
+ slider: slider::State,
+ slider_value: f32,
+ toggle_value: bool,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ ThemeChanged(style::Theme),
+ InputChanged(String),
+ ButtonPressed,
+ SliderChanged(f32),
+ CheckboxToggled(bool),
+}
+
+impl Sandbox for Styling {
+ type Message = Message;
+
+ fn new() -> Self {
+ Styling::default()
+ }
+
+ fn title(&self) -> String {
+ String::from("Styling - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::ThemeChanged(theme) => self.theme = theme,
+ Message::InputChanged(value) => self.input_value = value,
+ Message::ButtonPressed => (),
+ Message::SliderChanged(value) => self.slider_value = value,
+ Message::CheckboxToggled(value) => self.toggle_value = value,
+ }
+ }
+
+ fn view(&mut self) -> Element<Message> {
+ let choose_theme = style::Theme::ALL.iter().fold(
+ Column::new().spacing(10).push(Text::new("Choose a theme:")),
+ |column, theme| {
+ column.push(
+ Radio::new(
+ *theme,
+ &format!("{:?}", theme),
+ Some(self.theme),
+ Message::ThemeChanged,
+ )
+ .style(self.theme),
+ )
+ },
+ );
+
+ let text_input = TextInput::new(
+ &mut self.input,
+ "Type something...",
+ &self.input_value,
+ Message::InputChanged,
+ )
+ .padding(10)
+ .size(20)
+ .style(self.theme);
+
+ let button = Button::new(&mut self.button, Text::new("Submit"))
+ .padding(10)
+ .on_press(Message::ButtonPressed)
+ .style(self.theme);
+
+ let slider = Slider::new(
+ &mut self.slider,
+ 0.0..=100.0,
+ self.slider_value,
+ Message::SliderChanged,
+ )
+ .style(self.theme);
+
+ let progress_bar =
+ ProgressBar::new(0.0..=100.0, self.slider_value).style(self.theme);
+
+ let scrollable = Scrollable::new(&mut self.scroll)
+ .height(Length::Units(100))
+ .style(self.theme)
+ .push(Text::new("Scroll me!"))
+ .push(Space::with_height(Length::Units(800)))
+ .push(Text::new("You did it!"));
+
+ let checkbox = Checkbox::new(
+ self.toggle_value,
+ "Toggle me!",
+ Message::CheckboxToggled,
+ )
+ .style(self.theme);
+
+ let content = Column::new()
+ .spacing(20)
+ .padding(20)
+ .max_width(600)
+ .push(choose_theme)
+ .push(Row::new().spacing(10).push(text_input).push(button))
+ .push(slider)
+ .push(progress_bar)
+ .push(
+ Row::new()
+ .spacing(10)
+ .align_items(Align::Center)
+ .push(scrollable)
+ .push(checkbox),
+ );
+
+ Container::new(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y()
+ .style(self.theme)
+ .into()
+ }
+}
+
+mod style {
+ use iced::{
+ button, checkbox, container, progress_bar, radio, scrollable, slider,
+ text_input,
+ };
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum Theme {
+ Light,
+ Dark,
+ }
+
+ impl Theme {
+ pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark];
+ }
+
+ impl Default for Theme {
+ fn default() -> Theme {
+ Theme::Light
+ }
+ }
+
+ impl From<Theme> for Box<dyn container::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Container.into(),
+ }
+ }
+ }
+
+ impl From<Theme> for Box<dyn radio::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Radio.into(),
+ }
+ }
+ }
+
+ impl From<Theme> for Box<dyn text_input::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::TextInput.into(),
+ }
+ }
+ }
+
+ impl From<Theme> for Box<dyn button::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => light::Button.into(),
+ Theme::Dark => dark::Button.into(),
+ }
+ }
+ }
+
+ impl From<Theme> for Box<dyn scrollable::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Scrollable.into(),
+ }
+ }
+ }
+
+ impl From<Theme> for Box<dyn slider::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Slider.into(),
+ }
+ }
+ }
+
+ impl From<Theme> for Box<dyn progress_bar::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::ProgressBar.into(),
+ }
+ }
+ }
+
+ impl From<Theme> for Box<dyn checkbox::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Checkbox.into(),
+ }
+ }
+ }
+
+ mod light {
+ use iced::{button, Background, Color, Vector};
+
+ pub struct Button;
+
+ impl button::StyleSheet for Button {
+ fn active(&self) -> button::Style {
+ button::Style {
+ background: Some(Background::Color(Color::from_rgb(
+ 0.11, 0.42, 0.87,
+ ))),
+ border_radius: 12,
+ shadow_offset: Vector::new(1.0, 1.0),
+ text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
+ ..button::Style::default()
+ }
+ }
+
+ fn hovered(&self) -> button::Style {
+ button::Style {
+ text_color: Color::WHITE,
+ shadow_offset: Vector::new(1.0, 2.0),
+ ..self.active()
+ }
+ }
+ }
+ }
+
+ mod dark {
+ use iced::{
+ button, checkbox, container, progress_bar, radio, scrollable,
+ slider, text_input, Background, Color,
+ };
+
+ const SURFACE: Color = Color::from_rgb(
+ 0x40 as f32 / 255.0,
+ 0x44 as f32 / 255.0,
+ 0x4B as f32 / 255.0,
+ );
+
+ const ACCENT: Color = Color::from_rgb(
+ 0x6F as f32 / 255.0,
+ 0xFF as f32 / 255.0,
+ 0xE9 as f32 / 255.0,
+ );
+
+ const ACTIVE: Color = Color::from_rgb(
+ 0x72 as f32 / 255.0,
+ 0x89 as f32 / 255.0,
+ 0xDA as f32 / 255.0,
+ );
+
+ const HOVERED: Color = Color::from_rgb(
+ 0x67 as f32 / 255.0,
+ 0x7B as f32 / 255.0,
+ 0xC4 as f32 / 255.0,
+ );
+
+ pub struct Container;
+
+ impl container::StyleSheet for Container {
+ fn style(&self) -> container::Style {
+ container::Style {
+ background: Some(Background::Color(Color::from_rgb8(
+ 0x36, 0x39, 0x3F,
+ ))),
+ text_color: Some(Color::WHITE),
+ ..container::Style::default()
+ }
+ }
+ }
+
+ pub struct Radio;
+
+ impl radio::StyleSheet for Radio {
+ fn active(&self) -> radio::Style {
+ radio::Style {
+ background: Background::Color(SURFACE),
+ dot_color: ACTIVE,
+ border_width: 1,
+ border_color: ACTIVE,
+ }
+ }
+
+ fn hovered(&self) -> radio::Style {
+ radio::Style {
+ background: Background::Color(Color { a: 0.5, ..SURFACE }),
+ ..self.active()
+ }
+ }
+ }
+
+ pub struct TextInput;
+
+ impl text_input::StyleSheet for TextInput {
+ fn active(&self) -> text_input::Style {
+ text_input::Style {
+ background: Background::Color(SURFACE),
+ border_radius: 2,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ }
+ }
+
+ fn focused(&self) -> text_input::Style {
+ text_input::Style {
+ border_width: 1,
+ border_color: ACCENT,
+ ..self.active()
+ }
+ }
+
+ fn hovered(&self) -> text_input::Style {
+ text_input::Style {
+ border_width: 1,
+ border_color: Color { a: 0.3, ..ACCENT },
+ ..self.focused()
+ }
+ }
+
+ fn placeholder_color(&self) -> Color {
+ Color::from_rgb(0.4, 0.4, 0.4)
+ }
+
+ fn value_color(&self) -> Color {
+ Color::WHITE
+ }
+ }
+
+ pub struct Button;
+
+ impl button::StyleSheet for Button {
+ fn active(&self) -> button::Style {
+ button::Style {
+ background: Some(Background::Color(ACTIVE)),
+ border_radius: 3,
+ text_color: Color::WHITE,
+ ..button::Style::default()
+ }
+ }
+
+ fn hovered(&self) -> button::Style {
+ button::Style {
+ background: Some(Background::Color(HOVERED)),
+ text_color: Color::WHITE,
+ ..self.active()
+ }
+ }
+
+ fn pressed(&self) -> button::Style {
+ button::Style {
+ border_width: 1,
+ border_color: Color::WHITE,
+ ..self.hovered()
+ }
+ }
+ }
+
+ pub struct Scrollable;
+
+ impl scrollable::StyleSheet for Scrollable {
+ fn active(&self) -> scrollable::Scrollbar {
+ scrollable::Scrollbar {
+ background: Some(Background::Color(SURFACE)),
+ border_radius: 2,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ scroller: scrollable::Scroller {
+ color: ACTIVE,
+ border_radius: 2,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ },
+ }
+ }
+
+ fn hovered(&self) -> scrollable::Scrollbar {
+ let active = self.active();
+
+ scrollable::Scrollbar {
+ background: Some(Background::Color(Color {
+ a: 0.5,
+ ..SURFACE
+ })),
+ scroller: scrollable::Scroller {
+ color: HOVERED,
+ ..active.scroller
+ },
+ ..active
+ }
+ }
+
+ fn dragging(&self) -> scrollable::Scrollbar {
+ let hovered = self.hovered();
+
+ scrollable::Scrollbar {
+ scroller: scrollable::Scroller {
+ color: Color::from_rgb(0.85, 0.85, 0.85),
+ ..hovered.scroller
+ },
+ ..hovered
+ }
+ }
+ }
+
+ pub struct Slider;
+
+ impl slider::StyleSheet for Slider {
+ fn active(&self) -> slider::Style {
+ slider::Style {
+ rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }),
+ handle: slider::Handle {
+ shape: slider::HandleShape::Circle { radius: 9 },
+ color: ACTIVE,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ },
+ }
+ }
+
+ fn hovered(&self) -> slider::Style {
+ let active = self.active();
+
+ slider::Style {
+ handle: slider::Handle {
+ color: HOVERED,
+ ..active.handle
+ },
+ ..active
+ }
+ }
+
+ fn dragging(&self) -> slider::Style {
+ let active = self.active();
+
+ slider::Style {
+ handle: slider::Handle {
+ color: Color::from_rgb(0.85, 0.85, 0.85),
+ ..active.handle
+ },
+ ..active
+ }
+ }
+ }
+
+ pub struct ProgressBar;
+
+ impl progress_bar::StyleSheet for ProgressBar {
+ fn style(&self) -> progress_bar::Style {
+ progress_bar::Style {
+ background: Background::Color(SURFACE),
+ bar: Background::Color(ACTIVE),
+ border_radius: 10,
+ }
+ }
+ }
+
+ pub struct Checkbox;
+
+ impl checkbox::StyleSheet for Checkbox {
+ fn active(&self, is_checked: bool) -> checkbox::Style {
+ checkbox::Style {
+ background: Background::Color(if is_checked {
+ ACTIVE
+ } else {
+ SURFACE
+ }),
+ checkmark_color: Color::WHITE,
+ border_radius: 2,
+ border_width: 1,
+ border_color: ACTIVE,
+ }
+ }
+
+ fn hovered(&self, is_checked: bool) -> checkbox::Style {
+ checkbox::Style {
+ background: Background::Color(Color {
+ a: 0.8,
+ ..if is_checked { ACTIVE } else { SURFACE }
+ }),
+ ..self.active(is_checked)
+ }
+ }
+ }
+ }
+}
diff --git a/examples/todos.rs b/examples/todos.rs
index f5f2f459..4166f75a 100644
--- a/examples/todos.rs
+++ b/examples/todos.rs
@@ -1,7 +1,7 @@
use iced::{
button, scrollable, text_input, Align, Application, Button, Checkbox,
- Color, Column, Command, Container, Element, Font, HorizontalAlignment,
- Length, Row, Scrollable, Settings, Text, TextInput,
+ Column, Command, Container, Element, Font, HorizontalAlignment, Length,
+ Row, Scrollable, Settings, Text, TextInput,
};
use serde::{Deserialize, Serialize};
@@ -293,12 +293,10 @@ impl Task {
.align_items(Align::Center)
.push(checkbox)
.push(
- Button::new(
- edit_button,
- edit_icon().color([0.5, 0.5, 0.5]),
- )
- .on_press(TaskMessage::Edit)
- .padding(10),
+ Button::new(edit_button, edit_icon())
+ .on_press(TaskMessage::Edit)
+ .padding(10)
+ .style(style::Button::Icon),
)
.into()
}
@@ -324,13 +322,12 @@ impl Task {
delete_button,
Row::new()
.spacing(10)
- .push(delete_icon().color(Color::WHITE))
- .push(Text::new("Delete").color(Color::WHITE)),
+ .push(delete_icon())
+ .push(Text::new("Delete")),
)
.on_press(TaskMessage::Delete)
.padding(10)
- .border_radius(5)
- .background(Color::from_rgb(0.8, 0.2, 0.2)),
+ .style(style::Button::Destructive),
)
.into()
}
@@ -357,17 +354,12 @@ impl Controls {
let filter_button = |state, label, filter, current_filter| {
let label = Text::new(label).size(16);
- let button = if filter == current_filter {
- Button::new(state, label.color(Color::WHITE))
- .background(Color::from_rgb(0.2, 0.2, 0.7))
- } else {
- Button::new(state, label)
- };
-
- button
- .on_press(Message::FilterChanged(filter))
- .padding(8)
- .border_radius(10)
+ let button =
+ Button::new(state, label).style(style::Button::Filter {
+ selected: filter == current_filter,
+ });
+
+ button.on_press(Message::FilterChanged(filter)).padding(8)
};
Row::new()
@@ -560,3 +552,63 @@ impl SavedState {
Ok(())
}
}
+
+mod style {
+ use iced::{button, Background, Color, Vector};
+
+ pub enum Button {
+ Filter { selected: bool },
+ Icon,
+ Destructive,
+ }
+
+ impl button::StyleSheet for Button {
+ fn active(&self) -> button::Style {
+ match self {
+ Button::Filter { selected } => {
+ if *selected {
+ button::Style {
+ background: Some(Background::Color(
+ Color::from_rgb(0.2, 0.2, 0.7),
+ )),
+ border_radius: 10,
+ text_color: Color::WHITE,
+ ..button::Style::default()
+ }
+ } else {
+ button::Style::default()
+ }
+ }
+ Button::Icon => button::Style {
+ text_color: Color::from_rgb(0.5, 0.5, 0.5),
+ ..button::Style::default()
+ },
+ Button::Destructive => button::Style {
+ background: Some(Background::Color(Color::from_rgb(
+ 0.8, 0.2, 0.2,
+ ))),
+ border_radius: 5,
+ text_color: Color::WHITE,
+ shadow_offset: Vector::new(1.0, 1.0),
+ ..button::Style::default()
+ },
+ }
+ }
+
+ fn hovered(&self) -> button::Style {
+ let active = self.active();
+
+ button::Style {
+ text_color: match self {
+ Button::Icon => Color::from_rgb(0.2, 0.2, 0.7),
+ Button::Filter { selected } if !selected => {
+ Color::from_rgb(0.2, 0.2, 0.7)
+ }
+ _ => active.text_color,
+ },
+ shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0),
+ ..active
+ }
+ }
+ }
+}
diff --git a/examples/tour.rs b/examples/tour.rs
index 7fe8f4da..b0ee4d96 100644
--- a/examples/tour.rs
+++ b/examples/tour.rs
@@ -62,8 +62,9 @@ impl Sandbox for Tour {
if steps.has_previous() {
controls = controls.push(
- secondary_button(back_button, "Back")
- .on_press(Message::BackPressed),
+ button(back_button, "Back")
+ .on_press(Message::BackPressed)
+ .style(style::Button::Secondary),
);
}
@@ -71,8 +72,9 @@ impl Sandbox for Tour {
if steps.can_continue() {
controls = controls.push(
- primary_button(next_button, "Next")
- .on_press(Message::NextPressed),
+ button(next_button, "Next")
+ .on_press(Message::NextPressed)
+ .style(style::Button::Primary),
);
}
@@ -698,29 +700,12 @@ fn button<'a, Message>(
) -> Button<'a, Message> {
Button::new(
state,
- Text::new(label)
- .color(Color::WHITE)
- .horizontal_alignment(HorizontalAlignment::Center),
+ Text::new(label).horizontal_alignment(HorizontalAlignment::Center),
)
.padding(12)
- .border_radius(12)
.min_width(100)
}
-fn primary_button<'a, Message>(
- state: &'a mut button::State,
- label: &str,
-) -> Button<'a, Message> {
- button(state, label).background(Color::from_rgb(0.11, 0.42, 0.87))
-}
-
-fn secondary_button<'a, Message>(
- state: &'a mut button::State,
- label: &str,
-) -> Button<'a, Message> {
- button(state, label).background(Color::from_rgb(0.4, 0.4, 0.4))
-}
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language {
Rust,
@@ -763,6 +748,38 @@ pub enum Layout {
Column,
}
+mod style {
+ use iced::{button, Background, Color, Vector};
+
+ pub enum Button {
+ Primary,
+ Secondary,
+ }
+
+ impl button::StyleSheet for Button {
+ fn active(&self) -> button::Style {
+ button::Style {
+ background: Some(Background::Color(match self {
+ Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
+ Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5),
+ })),
+ border_radius: 12,
+ shadow_offset: Vector::new(1.0, 1.0),
+ text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
+ ..button::Style::default()
+ }
+ }
+
+ fn hovered(&self) -> button::Style {
+ button::Style {
+ text_color: Color::WHITE,
+ shadow_offset: Vector::new(1.0, 2.0),
+ ..self.active()
+ }
+ }
+ }
+}
+
// This should be gracefully handled by Iced in the future. Probably using our
// own proc macro, or maybe the whole process is streamlined by `wasm-pack` at
// some point.
diff --git a/native/src/element.rs b/native/src/element.rs
index 63d2de0c..9b5adb9c 100644
--- a/native/src/element.rs
+++ b/native/src/element.rs
@@ -235,10 +235,12 @@ where
pub fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- self.widget.draw(renderer, layout, cursor_position)
+ self.widget
+ .draw(renderer, defaults, layout, cursor_position)
}
pub(crate) fn hash_layout(&self, state: &mut Hasher) {
@@ -316,10 +318,12 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- self.widget.draw(renderer, layout, cursor_position)
+ self.widget
+ .draw(renderer, defaults, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -384,10 +388,12 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
renderer.explain(
+ defaults,
self.element.widget.as_ref(),
layout,
cursor_position,
diff --git a/native/src/renderer.rs b/native/src/renderer.rs
index 7a68ada4..284c95f6 100644
--- a/native/src/renderer.rs
+++ b/native/src/renderer.rs
@@ -21,14 +21,15 @@
//! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html
mod debugger;
-#[cfg(debug_assertions)]
-mod null;
mod windowed;
pub use debugger::Debugger;
+pub use windowed::{Target, Windowed};
+
+#[cfg(debug_assertions)]
+mod null;
#[cfg(debug_assertions)]
pub use null::Null;
-pub use windowed::{Target, Windowed};
use crate::{layout, Element};
@@ -43,6 +44,13 @@ pub trait Renderer: Sized {
/// [`Renderer`]: trait.Renderer.html
type Output;
+ /// The default styling attributes of the [`Renderer`].
+ ///
+ /// This type can be leveraged to implement style inheritance.
+ ///
+ /// [`Renderer`]: trait.Renderer.html
+ type Defaults: Default;
+
/// Lays out the elements of a user interface.
///
/// You should override this if you need to perform any operations before or
diff --git a/native/src/renderer/debugger.rs b/native/src/renderer/debugger.rs
index 4cc50661..30f3d9a0 100644
--- a/native/src/renderer/debugger.rs
+++ b/native/src/renderer/debugger.rs
@@ -17,6 +17,7 @@ pub trait Debugger: super::Renderer {
/// [`Element::explain`]: struct.Element.html#method.explain
fn explain<Message>(
&mut self,
+ defaults: &Self::Defaults,
widget: &dyn Widget<Message, Self>,
layout: Layout<'_>,
cursor_position: Point,
diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs
index 43076d61..df261cdc 100644
--- a/native/src/renderer/null.rs
+++ b/native/src/renderer/null.rs
@@ -1,20 +1,33 @@
use crate::{
- button, checkbox, column, radio, row, scrollable, text, text_input,
- Background, Color, Element, Font, HorizontalAlignment, Layout, Point,
+ button, checkbox, column, progress_bar, radio, row, scrollable, slider,
+ text, text_input, Color, Element, Font, HorizontalAlignment, Layout, Point,
Rectangle, Renderer, Size, VerticalAlignment,
};
/// A renderer that does nothing.
+///
+/// It can be useful if you are writing tests!
#[derive(Debug, Clone, Copy)]
pub struct Null;
+impl Null {
+ /// Creates a new [`Null`] renderer.
+ ///
+ /// [`Null`]: struct.Null.html
+ pub fn new() -> Null {
+ Null
+ }
+}
+
impl Renderer for Null {
type Output = ();
+ type Defaults = ();
}
impl column::Renderer for Null {
fn draw<Message>(
&mut self,
+ _defaults: &Self::Defaults,
_content: &[Element<'_, Message, Self>],
_layout: Layout<'_>,
_cursor_position: Point,
@@ -25,6 +38,7 @@ impl column::Renderer for Null {
impl row::Renderer for Null {
fn draw<Message>(
&mut self,
+ _defaults: &Self::Defaults,
_content: &[Element<'_, Message, Self>],
_layout: Layout<'_>,
_cursor_position: Point,
@@ -49,6 +63,7 @@ impl text::Renderer for Null {
fn draw(
&mut self,
+ _defaults: &Self::Defaults,
_bounds: Rectangle,
_content: &str,
_size: u16,
@@ -61,6 +76,8 @@ impl text::Renderer for Null {
}
impl scrollable::Renderer for Null {
+ type Style = ();
+
fn scrollbar(
&self,
_bounds: Rectangle,
@@ -79,12 +96,15 @@ impl scrollable::Renderer for Null {
_is_mouse_over_scrollbar: bool,
_scrollbar: Option<scrollable::Scrollbar>,
_offset: u32,
+ _style: &Self::Style,
_content: Self::Output,
) {
}
}
impl text_input::Renderer for Null {
+ type Style = ();
+
fn default_size(&self) -> u16 {
20
}
@@ -112,24 +132,31 @@ impl text_input::Renderer for Null {
_placeholder: &str,
_value: &text_input::Value,
_state: &text_input::State,
+ _style: &Self::Style,
) -> Self::Output {
}
}
impl button::Renderer for Null {
- fn draw(
+ type Style = ();
+
+ fn draw<Message>(
&mut self,
+ _defaults: &Self::Defaults,
_bounds: Rectangle,
_cursor_position: Point,
+ _is_disabled: bool,
_is_pressed: bool,
- _background: Option<Background>,
- _border_radius: u16,
- _content: Self::Output,
+ _style: &Self::Style,
+ _content: &Element<'_, Message, Self>,
+ _content_layout: Layout<'_>,
) -> Self::Output {
}
}
impl radio::Renderer for Null {
+ type Style = ();
+
fn default_size(&self) -> u32 {
20
}
@@ -140,11 +167,14 @@ impl radio::Renderer for Null {
_is_selected: bool,
_is_mouse_over: bool,
_label: Self::Output,
+ _style: &Self::Style,
) {
}
}
impl checkbox::Renderer for Null {
+ type Style = ();
+
fn default_size(&self) -> u32 {
20
}
@@ -155,6 +185,41 @@ impl checkbox::Renderer for Null {
_is_checked: bool,
_is_mouse_over: bool,
_label: Self::Output,
+ _style: &Self::Style,
+ ) {
+ }
+}
+
+impl slider::Renderer for Null {
+ type Style = ();
+
+ fn height(&self) -> u32 {
+ 30
+ }
+
+ fn draw(
+ &mut self,
+ _bounds: Rectangle,
+ _cursor_position: Point,
+ _range: std::ops::RangeInclusive<f32>,
+ _value: f32,
+ _is_dragging: bool,
+ _style_sheet: &Self::Style,
+ ) {
+ }
+}
+
+impl progress_bar::Renderer for Null {
+ type Style = ();
+
+ const DEFAULT_HEIGHT: u16 = 30;
+
+ fn draw(
+ &self,
+ _bounds: Rectangle,
+ _range: std::ops::RangeInclusive<f32>,
+ _value: f32,
+ _style: &Self::Style,
) {
}
}
diff --git a/native/src/renderer/windowed.rs b/native/src/renderer/windowed.rs
index 813a03f2..ee020ab1 100644
--- a/native/src/renderer/windowed.rs
+++ b/native/src/renderer/windowed.rs
@@ -4,13 +4,16 @@ use raw_window_handle::HasRawWindowHandle;
/// A renderer that can target windows.
pub trait Windowed: super::Renderer + Sized {
+ /// The settings of the renderer.
+ type Settings: Default;
+
/// The type of target.
type Target: Target<Renderer = Self>;
/// Creates a new [`Windowed`] renderer.
///
/// [`Windowed`]: trait.Windowed.html
- fn new() -> Self;
+ fn new(settings: Self::Settings) -> Self;
/// Performs the drawing operations described in the output on the given
/// target.
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 07b936a9..970bf0c1 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -43,24 +43,7 @@ where
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
- /// # pub struct Renderer;
- /// #
- /// # impl Renderer {
- /// # pub fn new() -> Self { Renderer }
- /// # }
- /// #
- /// # impl iced_native::Renderer for Renderer { type Output = (); }
- /// #
- /// # impl iced_native::column::Renderer for Renderer {
- /// # fn draw<Message>(
- /// # &mut self,
- /// # _children: &[iced_native::Element<'_, Message, Self>],
- /// # _layout: iced_native::Layout<'_>,
- /// # _cursor_position: iced_native::Point,
- /// # ) -> Self::Output {
- /// # ()
- /// # }
- /// # }
+ /// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use iced_native::Column;
@@ -139,24 +122,7 @@ where
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
- /// # pub struct Renderer;
- /// #
- /// # impl Renderer {
- /// # pub fn new() -> Self { Renderer }
- /// # }
- /// #
- /// # impl iced_native::Renderer for Renderer { type Output = (); }
- /// #
- /// # impl iced_native::column::Renderer for Renderer {
- /// # fn draw<Message>(
- /// # &mut self,
- /// # _children: &[iced_native::Element<'_, Message, Self>],
- /// # _layout: iced_native::Layout<'_>,
- /// # _cursor_position: iced_native::Point,
- /// # ) -> Self::Output {
- /// # ()
- /// # }
- /// # }
+ /// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use iced_native::Column;
@@ -241,24 +207,7 @@ where
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
- /// # pub struct Renderer;
- /// #
- /// # impl Renderer {
- /// # pub fn new() -> Self { Renderer }
- /// # }
- /// #
- /// # impl iced_native::Renderer for Renderer { type Output = (); }
- /// #
- /// # impl iced_native::column::Renderer for Renderer {
- /// # fn draw<Message>(
- /// # &mut self,
- /// # _children: &[iced_native::Element<'_, Message, Self>],
- /// # _layout: iced_native::Layout<'_>,
- /// # _cursor_position: iced_native::Point,
- /// # ) -> Self::Output {
- /// # ()
- /// # }
- /// # }
+ /// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use iced_native::Column;
@@ -304,6 +253,7 @@ where
pub fn draw(&self, renderer: &mut Renderer) -> Renderer::Output {
self.root.widget.draw(
renderer,
+ &Renderer::Defaults::default(),
Layout::new(&self.layout),
self.cursor_position,
)
diff --git a/native/src/widget.rs b/native/src/widget.rs
index ccc9b47e..f9424b02 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -107,6 +107,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output;
diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs
index 2881105f..51b02172 100644
--- a/native/src/widget/button.rs
+++ b/native/src/widget/button.rs
@@ -6,8 +6,8 @@
//! [`State`]: struct.State.html
use crate::{
input::{mouse, ButtonState},
- layout, Background, Clipboard, Element, Event, Hasher, Layout, Length,
- Point, Rectangle, Widget,
+ layout, Clipboard, Element, Event, Hasher, Layout, Length, Point,
+ Rectangle, Widget,
};
use std::hash::Hash;
@@ -28,7 +28,7 @@ use std::hash::Hash;
/// .on_press(Message::ButtonPressed);
/// ```
#[allow(missing_debug_implementations)]
-pub struct Button<'a, Message, Renderer> {
+pub struct Button<'a, Message, Renderer: self::Renderer> {
state: &'a mut State,
content: Element<'a, Message, Renderer>,
on_press: Option<Message>,
@@ -37,11 +37,13 @@ pub struct Button<'a, Message, Renderer> {
min_width: u32,
min_height: u32,
padding: u16,
- background: Option<Background>,
- border_radius: u16,
+ style: Renderer::Style,
}
-impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
+impl<'a, Message, Renderer> Button<'a, Message, Renderer>
+where
+ Renderer: self::Renderer,
+{
/// Creates a new [`Button`] with some local [`State`] and the given
/// content.
///
@@ -60,8 +62,7 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
min_width: 0,
min_height: 0,
padding: 0,
- background: None,
- border_radius: 0,
+ style: Renderer::Style::default(),
}
}
@@ -105,28 +106,19 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
self
}
- /// Sets the [`Background`] of the [`Button`].
- ///
- /// [`Button`]: struct.Button.html
- /// [`Background`]: ../../struct.Background.html
- pub fn background<T: Into<Background>>(mut self, background: T) -> Self {
- self.background = Some(background.into());
- self
- }
-
- /// Sets the border radius of the [`Button`].
+ /// Sets the message that will be produced when the [`Button`] is pressed.
///
/// [`Button`]: struct.Button.html
- pub fn border_radius(mut self, border_radius: u16) -> Self {
- self.border_radius = border_radius;
+ pub fn on_press(mut self, msg: Message) -> Self {
+ self.on_press = Some(msg);
self
}
- /// Sets the message that will be produced when the [`Button`] is pressed.
+ /// Sets the style of the [`Button`].
///
/// [`Button`]: struct.Button.html
- pub fn on_press(mut self, msg: Message) -> Self {
- self.on_press = Some(msg);
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
self
}
}
@@ -227,22 +219,19 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- let content = self.content.draw(
- renderer,
- layout.children().next().unwrap(),
- cursor_position,
- );
-
renderer.draw(
+ defaults,
layout.bounds(),
cursor_position,
+ self.on_press.is_none(),
self.state.is_pressed,
- self.background,
- self.border_radius,
- content,
+ &self.style,
+ &self.content,
+ layout.children().next().unwrap(),
)
}
@@ -260,17 +249,22 @@ where
/// [`Button`]: struct.Button.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized {
+ /// The style supported by this renderer.
+ type Style: Default;
+
/// Draws a [`Button`].
///
/// [`Button`]: struct.Button.html
- fn draw(
+ fn draw<Message>(
&mut self,
+ defaults: &Self::Defaults,
bounds: Rectangle,
cursor_position: Point,
+ is_disabled: bool,
is_pressed: bool,
- background: Option<Background>,
- border_radius: u16,
- content: Self::Output,
+ style: &Self::Style,
+ content: &Element<'_, Message, Self>,
+ content_layout: Layout<'_>,
) -> Self::Output;
}
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index 06a484c7..95165997 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -3,7 +3,7 @@ use std::hash::Hash;
use crate::{
input::{mouse, ButtonState},
- layout, row, text, Align, Clipboard, Color, Element, Event, Font, Hasher,
+ layout, row, text, Align, Clipboard, Element, Event, Font, Hasher,
HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
VerticalAlignment, Widget,
};
@@ -13,7 +13,7 @@ use crate::{
/// # Example
///
/// ```
-/// # use iced_native::Checkbox;
+/// # type Checkbox<Message> = iced_native::Checkbox<Message, iced_native::renderer::Null>;
/// #
/// pub enum Message {
/// CheckboxToggled(bool),
@@ -26,15 +26,15 @@ use crate::{
///
/// ![Checkbox drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct Checkbox<Message> {
+pub struct Checkbox<Message, Renderer: self::Renderer> {
is_checked: bool,
on_toggle: Box<dyn Fn(bool) -> Message>,
label: String,
- label_color: Option<Color>,
width: Length,
+ style: Renderer::Style,
}
-impl<Message> Checkbox<Message> {
+impl<Message, Renderer: self::Renderer> Checkbox<Message, Renderer> {
/// Creates a new [`Checkbox`].
///
/// It expects:
@@ -53,29 +53,30 @@ impl<Message> Checkbox<Message> {
is_checked,
on_toggle: Box::new(f),
label: String::from(label),
- label_color: None,
width: Length::Shrink,
+ style: Renderer::Style::default(),
}
}
- /// Sets the color of the label of the [`Checkbox`].
+ /// Sets the width of the [`Checkbox`].
///
/// [`Checkbox`]: struct.Checkbox.html
- pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self {
- self.label_color = Some(color.into());
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
self
}
- /// Sets the width of the [`Checkbox`].
+ /// Sets the style of the [`Checkbox`].
///
/// [`Checkbox`]: struct.Checkbox.html
- pub fn width(mut self, width: Length) -> Self {
- self.width = width;
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
self
}
}
-impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>
+impl<Message, Renderer> Widget<Message, Renderer>
+ for Checkbox<Message, Renderer>
where
Renderer: self::Renderer + text::Renderer + row::Renderer,
{
@@ -134,6 +135,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
@@ -146,11 +148,12 @@ where
let label = text::Renderer::draw(
renderer,
+ defaults,
label_layout.bounds(),
&self.label,
text::Renderer::default_size(renderer),
Font::Default,
- self.label_color,
+ None,
HorizontalAlignment::Left,
VerticalAlignment::Center,
);
@@ -163,6 +166,7 @@ where
self.is_checked,
is_mouse_over,
label,
+ &self.style,
)
}
@@ -179,6 +183,9 @@ where
/// [`Checkbox`]: struct.Checkbox.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
+ /// The style supported by this renderer.
+ type Style: Default;
+
/// Returns the default size of a [`Checkbox`].
///
/// [`Checkbox`]: struct.Checkbox.html
@@ -199,16 +206,19 @@ pub trait Renderer: crate::Renderer {
is_checked: bool,
is_mouse_over: bool,
label: Self::Output,
+ style: &Self::Style,
) -> Self::Output;
}
-impl<'a, Message, Renderer> From<Checkbox<Message>>
+impl<'a, Message, Renderer> From<Checkbox<Message, Renderer>>
for Element<'a, Message, Renderer>
where
- Renderer: self::Renderer + text::Renderer + row::Renderer,
+ Renderer: 'static + self::Renderer + text::Renderer + row::Renderer,
Message: 'static,
{
- fn from(checkbox: Checkbox<Message>) -> Element<'a, Message, Renderer> {
+ fn from(
+ checkbox: Checkbox<Message, Renderer>,
+ ) -> Element<'a, Message, Renderer> {
Element::new(checkbox)
}
}
diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs
index 0901bff4..79ec5ab4 100644
--- a/native/src/widget/column.rs
+++ b/native/src/widget/column.rs
@@ -173,10 +173,11 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self.children, layout, cursor_position)
+ renderer.draw(defaults, &self.children, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -213,6 +214,7 @@ pub trait Renderer: crate::Renderer + Sized {
/// [`Layout`]: ../layout/struct.Layout.html
fn draw<Message>(
&mut self,
+ defaults: &Self::Defaults,
content: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs
index 74f0e0ef..5682fc87 100644
--- a/native/src/widget/container.rs
+++ b/native/src/widget/container.rs
@@ -3,7 +3,7 @@ use std::hash::Hash;
use crate::{
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point,
- Widget,
+ Rectangle, Widget,
};
use std::u32;
@@ -12,17 +12,21 @@ use std::u32;
///
/// It is normally used for alignment purposes.
#[allow(missing_debug_implementations)]
-pub struct Container<'a, Message, Renderer> {
+pub struct Container<'a, Message, Renderer: self::Renderer> {
width: Length,
height: Length,
max_width: u32,
max_height: u32,
horizontal_alignment: Align,
vertical_alignment: Align,
+ style: Renderer::Style,
content: Element<'a, Message, Renderer>,
}
-impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
+impl<'a, Message, Renderer> Container<'a, Message, Renderer>
+where
+ Renderer: self::Renderer,
+{
/// Creates an empty [`Container`].
///
/// [`Container`]: struct.Container.html
@@ -37,6 +41,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
max_height: u32::MAX,
horizontal_alignment: Align::Start,
vertical_alignment: Align::Start,
+ style: Renderer::Style::default(),
content: content.into(),
}
}
@@ -78,7 +83,6 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
/// [`Container`]: struct.Container.html
pub fn center_x(mut self) -> Self {
self.horizontal_alignment = Align::Center;
-
self
}
@@ -87,7 +91,14 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
/// [`Container`]: struct.Container.html
pub fn center_y(mut self) -> Self {
self.vertical_alignment = Align::Center;
+ self
+ }
+ /// Sets the style of the [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
self
}
}
@@ -95,7 +106,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Container<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: self::Renderer,
{
fn width(&self) -> Length {
self.width
@@ -147,13 +158,17 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- self.content.draw(
- renderer,
- layout.children().next().unwrap(),
+ renderer.draw(
+ defaults,
+ layout.bounds(),
cursor_position,
+ &self.style,
+ &self.content,
+ layout.children().next().unwrap(),
)
}
@@ -168,10 +183,35 @@ where
}
}
+/// The renderer of a [`Container`].
+///
+/// Your [renderer] will need to implement this trait before being
+/// able to use a [`Container`] in your user interface.
+///
+/// [`Container`]: struct.Container.html
+/// [renderer]: ../../renderer/index.html
+pub trait Renderer: crate::Renderer {
+ /// The style supported by this renderer.
+ type Style: Default;
+
+ /// Draws a [`Container`].
+ ///
+ /// [`Container`]: struct.Container.html
+ fn draw<Message>(
+ &mut self,
+ defaults: &Self::Defaults,
+ bounds: Rectangle,
+ cursor_position: Point,
+ style: &Self::Style,
+ content: &Element<'_, Message, Self>,
+ content_layout: Layout<'_>,
+ ) -> Self::Output;
+}
+
impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
- Renderer: 'a + crate::Renderer,
+ Renderer: 'a + self::Renderer,
Message: 'static,
{
fn from(
diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs
index 20375822..1efe4570 100644
--- a/native/src/widget/image.rs
+++ b/native/src/widget/image.rs
@@ -95,6 +95,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ _defaults: &Renderer::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs
index b1d4fd92..67d1ab83 100644
--- a/native/src/widget/progress_bar.rs
+++ b/native/src/widget/progress_bar.rs
@@ -1,7 +1,6 @@
//! Provide progress feedback to your users.
use crate::{
- layout, Background, Color, Element, Hasher, Layout, Length, Point,
- Rectangle, Size, Widget,
+ layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
};
use std::{hash::Hash, ops::RangeInclusive};
@@ -10,8 +9,9 @@ use std::{hash::Hash, ops::RangeInclusive};
///
/// # Example
/// ```
-/// # use iced_native::ProgressBar;
+/// # use iced_native::renderer::Null;
/// #
+/// # pub type ProgressBar = iced_native::ProgressBar<Null>;
/// let value = 50.0;
///
/// ProgressBar::new(0.0..=100.0, value);
@@ -19,16 +19,15 @@ use std::{hash::Hash, ops::RangeInclusive};
///
/// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
#[allow(missing_debug_implementations)]
-pub struct ProgressBar {
+pub struct ProgressBar<Renderer: self::Renderer> {
range: RangeInclusive<f32>,
value: f32,
width: Length,
height: Option<Length>,
- background: Option<Background>,
- active_color: Option<Color>,
+ style: Renderer::Style,
}
-impl ProgressBar {
+impl<Renderer: self::Renderer> ProgressBar<Renderer> {
/// Creates a new [`ProgressBar`].
///
/// It expects:
@@ -42,8 +41,7 @@ impl ProgressBar {
range,
width: Length::Fill,
height: None,
- background: None,
- active_color: None,
+ style: Renderer::Style::default(),
}
}
@@ -63,24 +61,16 @@ impl ProgressBar {
self
}
- /// Sets the background of the [`ProgressBar`].
+ /// Sets the style of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
- pub fn background(mut self, background: Background) -> Self {
- self.background = Some(background);
- self
- }
-
- /// Sets the active color of the [`ProgressBar`].
- ///
- /// [`ProgressBar`]: struct.ProgressBar.html
- pub fn active_color(mut self, active_color: Color) -> Self {
- self.active_color = Some(active_color);
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
self
}
}
-impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar
+impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar<Renderer>
where
Renderer: self::Renderer,
{
@@ -111,6 +101,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ _defaults: &Renderer::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
@@ -118,8 +109,7 @@ where
layout.bounds(),
self.range.clone(),
self.value,
- self.background,
- self.active_color,
+ &self.style,
)
}
@@ -137,6 +127,9 @@ where
/// [`ProgressBar`]: struct.ProgressBar.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
+ /// The style supported by this renderer.
+ type Style: Default;
+
/// The default height of a [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
@@ -157,17 +150,19 @@ pub trait Renderer: crate::Renderer {
bounds: Rectangle,
range: RangeInclusive<f32>,
value: f32,
- background: Option<Background>,
- active_color: Option<Color>,
+ style: &Self::Style,
) -> Self::Output;
}
-impl<'a, Message, Renderer> From<ProgressBar> for Element<'a, Message, Renderer>
+impl<'a, Message, Renderer> From<ProgressBar<Renderer>>
+ for Element<'a, Message, Renderer>
where
- Renderer: self::Renderer,
+ Renderer: 'static + self::Renderer,
Message: 'static,
{
- fn from(progress_bar: ProgressBar) -> Element<'a, Message, Renderer> {
+ fn from(
+ progress_bar: ProgressBar<Renderer>,
+ ) -> Element<'a, Message, Renderer> {
Element::new(progress_bar)
}
}
diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs
index 876f4f48..99743ec3 100644
--- a/native/src/widget/radio.rs
+++ b/native/src/widget/radio.rs
@@ -1,7 +1,7 @@
//! Create choices using radio buttons.
use crate::{
input::{mouse, ButtonState},
- layout, row, text, Align, Clipboard, Color, Element, Event, Font, Hasher,
+ layout, row, text, Align, Clipboard, Element, Event, Font, Hasher,
HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
VerticalAlignment, Widget,
};
@@ -12,7 +12,8 @@ use std::hash::Hash;
///
/// # Example
/// ```
-/// # use iced_native::Radio;
+/// # type Radio<Message> =
+/// # iced_native::Radio<Message, iced_native::renderer::Null>;
/// #
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// pub enum Choice {
@@ -34,14 +35,14 @@ use std::hash::Hash;
///
/// ![Radio buttons drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct Radio<Message> {
+pub struct Radio<Message, Renderer: self::Renderer> {
is_selected: bool,
on_click: Message,
label: String,
- label_color: Option<Color>,
+ style: Renderer::Style,
}
-impl<Message> Radio<Message> {
+impl<Message, Renderer: self::Renderer> Radio<Message, Renderer> {
/// Creates a new [`Radio`] button.
///
/// It expects:
@@ -61,20 +62,20 @@ impl<Message> Radio<Message> {
is_selected: Some(value) == selected,
on_click: f(value),
label: String::from(label),
- label_color: None,
+ style: Renderer::Style::default(),
}
}
- /// Sets the `Color` of the label of the [`Radio`].
+ /// Sets the style of the [`Radio`] button.
///
/// [`Radio`]: struct.Radio.html
- pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self {
- self.label_color = Some(color.into());
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
self
}
}
-impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message>
+impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message, Renderer>
where
Renderer: self::Renderer + text::Renderer + row::Renderer,
Message: Clone,
@@ -132,6 +133,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
@@ -144,11 +146,12 @@ where
let label = text::Renderer::draw(
renderer,
+ defaults,
label_layout.bounds(),
&self.label,
text::Renderer::default_size(renderer),
Font::Default,
- self.label_color,
+ None,
HorizontalAlignment::Left,
VerticalAlignment::Center,
);
@@ -161,6 +164,7 @@ where
self.is_selected,
is_mouse_over,
label,
+ &self.style,
)
}
@@ -177,6 +181,9 @@ where
/// [`Radio`]: struct.Radio.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
+ /// The style supported by this renderer.
+ type Style: Default;
+
/// Returns the default size of a [`Radio`] button.
///
/// [`Radio`]: struct.Radio.html
@@ -197,16 +204,17 @@ pub trait Renderer: crate::Renderer {
is_selected: bool,
is_mouse_over: bool,
label: Self::Output,
+ style: &Self::Style,
) -> Self::Output;
}
-impl<'a, Message, Renderer> From<Radio<Message>>
+impl<'a, Message, Renderer> From<Radio<Message, Renderer>>
for Element<'a, Message, Renderer>
where
- Renderer: self::Renderer + row::Renderer + text::Renderer,
+ Renderer: 'static + self::Renderer + row::Renderer + text::Renderer,
Message: 'static + Clone,
{
- fn from(radio: Radio<Message>) -> Element<'a, Message, Renderer> {
+ fn from(radio: Radio<Message, Renderer>) -> Element<'a, Message, Renderer> {
Element::new(radio)
}
}
diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs
index 7d951968..b3dc90ba 100644
--- a/native/src/widget/row.rs
+++ b/native/src/widget/row.rs
@@ -174,10 +174,11 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
- renderer.draw(&self.children, layout, cursor_position)
+ renderer.draw(defaults, &self.children, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
@@ -215,6 +216,7 @@ pub trait Renderer: crate::Renderer + Sized {
/// [`Layout`]: ../layout/struct.Layout.html
fn draw<Message>(
&mut self,
+ defaults: &Self::Defaults,
children: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 9fa602d5..e83f25af 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -11,14 +11,15 @@ use std::{f32, hash::Hash, u32};
/// A widget that can vertically display an infinite amount of content with a
/// scrollbar.
#[allow(missing_debug_implementations)]
-pub struct Scrollable<'a, Message, Renderer> {
+pub struct Scrollable<'a, Message, Renderer: self::Renderer> {
state: &'a mut State,
height: Length,
max_height: u32,
content: Column<'a, Message, Renderer>,
+ style: Renderer::Style,
}
-impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
+impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
/// Creates a new [`Scrollable`] with the given [`State`].
///
/// [`Scrollable`]: struct.Scrollable.html
@@ -29,6 +30,7 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
height: Length::Shrink,
max_height: u32::MAX,
content: Column::new(),
+ style: Renderer::Style::default(),
}
}
@@ -90,6 +92,14 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
self
}
+ /// Sets the style of the [`Scrollable`] .
+ ///
+ /// [`Scrollable`]: struct.Scrollable.html
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
+ self
+ }
+
/// Adds an element to the [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
@@ -105,7 +115,7 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Scrollable<'a, Message, Renderer>
where
- Renderer: self::Renderer + column::Renderer,
+ Renderer: 'static + self::Renderer + column::Renderer,
{
fn width(&self) -> Length {
Length::Fill
@@ -255,6 +265,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
@@ -277,7 +288,12 @@ where
Point::new(cursor_position.x, -1.0)
};
- self.content.draw(renderer, content_layout, cursor_position)
+ self.content.draw(
+ renderer,
+ defaults,
+ content_layout,
+ cursor_position,
+ )
};
self::Renderer::draw(
@@ -289,12 +305,13 @@ where
is_mouse_over_scrollbar,
scrollbar,
offset,
+ &self.style,
content,
)
}
fn hash_layout(&self, state: &mut Hasher) {
- std::any::TypeId::of::<Scrollable<'static, (), ()>>().hash(state);
+ std::any::TypeId::of::<Scrollable<'static, (), Renderer>>().hash(state);
self.height.hash(state);
self.max_height.hash(state);
@@ -441,6 +458,9 @@ pub struct Scroller {
/// [`Scrollable`]: struct.Scrollable.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized {
+ /// The style supported by this renderer.
+ type Style: Default;
+
/// Returns the [`Scrollbar`] given the bounds and content bounds of a
/// [`Scrollable`].
///
@@ -477,6 +497,7 @@ pub trait Renderer: crate::Renderer + Sized {
is_mouse_over_scrollbar: bool,
scrollbar: Option<Scrollbar>,
offset: u32,
+ style: &Self::Style,
content: Self::Output,
) -> Self::Output;
}
@@ -484,7 +505,7 @@ pub trait Renderer: crate::Renderer + Sized {
impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
- Renderer: 'a + self::Renderer + column::Renderer,
+ Renderer: 'static + self::Renderer + column::Renderer,
Message: 'static,
{
fn from(
diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs
index f446f7e8..008203fe 100644
--- a/native/src/widget/slider.rs
+++ b/native/src/widget/slider.rs
@@ -21,8 +21,9 @@ use std::{hash::Hash, ops::RangeInclusive};
///
/// # Example
/// ```
-/// # use iced_native::{slider, Slider};
+/// # use iced_native::{slider, renderer::Null};
/// #
+/// # pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Null>;
/// pub enum Message {
/// SliderChanged(f32),
/// }
@@ -35,15 +36,16 @@ use std::{hash::Hash, ops::RangeInclusive};
///
/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct Slider<'a, Message> {
+pub struct Slider<'a, Message, Renderer: self::Renderer> {
state: &'a mut State,
range: RangeInclusive<f32>,
value: f32,
on_change: Box<dyn Fn(f32) -> Message>,
width: Length,
+ style: Renderer::Style,
}
-impl<'a, Message> Slider<'a, Message> {
+impl<'a, Message, Renderer: self::Renderer> Slider<'a, Message, Renderer> {
/// Creates a new [`Slider`].
///
/// It expects:
@@ -71,6 +73,7 @@ impl<'a, Message> Slider<'a, Message> {
range,
on_change: Box::new(on_change),
width: Length::Fill,
+ style: Renderer::Style::default(),
}
}
@@ -81,6 +84,14 @@ impl<'a, Message> Slider<'a, Message> {
self.width = width;
self
}
+
+ /// Sets the style of the [`Slider`].
+ ///
+ /// [`Slider`]: struct.Slider.html
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
+ self
+ }
}
/// The local state of a [`Slider`].
@@ -100,7 +111,8 @@ impl State {
}
}
-impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message>
+impl<'a, Message, Renderer> Widget<Message, Renderer>
+ for Slider<'a, Message, Renderer>
where
Renderer: self::Renderer,
{
@@ -178,6 +190,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ _defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
@@ -187,6 +200,7 @@ where
self.range.clone(),
self.value,
self.state.is_dragging,
+ &self.style,
)
}
@@ -203,6 +217,9 @@ where
/// [`Slider`]: struct.Slider.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
+ /// The style supported by this renderer.
+ type Style: Default;
+
/// Returns the height of the [`Slider`].
///
/// [`Slider`]: struct.Slider.html
@@ -227,16 +244,19 @@ pub trait Renderer: crate::Renderer {
range: RangeInclusive<f32>,
value: f32,
is_dragging: bool,
+ style: &Self::Style,
) -> Self::Output;
}
-impl<'a, Message, Renderer> From<Slider<'a, Message>>
+impl<'a, Message, Renderer> From<Slider<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
- Renderer: self::Renderer,
+ Renderer: 'static + self::Renderer,
Message: 'static,
{
- fn from(slider: Slider<'a, Message>) -> Element<'a, Message, Renderer> {
+ fn from(
+ slider: Slider<'a, Message, Renderer>,
+ ) -> Element<'a, Message, Renderer> {
Element::new(slider)
}
}
diff --git a/native/src/widget/space.rs b/native/src/widget/space.rs
index 2029c52f..24c94bf6 100644
--- a/native/src/widget/space.rs
+++ b/native/src/widget/space.rs
@@ -68,6 +68,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ _defaults: &Renderer::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs
index 25587ffa..063730bb 100644
--- a/native/src/widget/svg.rs
+++ b/native/src/widget/svg.rs
@@ -91,6 +91,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ _defaults: &Renderer::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs
index caa81db0..e4490fb6 100644
--- a/native/src/widget/text.rs
+++ b/native/src/widget/text.rs
@@ -146,10 +146,12 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ defaults: &Renderer::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
renderer.draw(
+ defaults,
layout.bounds(),
&self.content,
self.size.unwrap_or(renderer.default_size()),
@@ -209,6 +211,7 @@ pub trait Renderer: crate::Renderer {
/// [`VerticalAlignment`]: enum.VerticalAlignment.html
fn draw(
&mut self,
+ defaults: &Self::Defaults,
bounds: Rectangle,
content: &str,
size: u16,
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index f744da27..25032559 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -15,8 +15,9 @@ use unicode_segmentation::UnicodeSegmentation;
///
/// # Example
/// ```
-/// # use iced_native::{text_input, TextInput};
+/// # use iced_native::{text_input, renderer::Null};
/// #
+/// # pub type TextInput<'a, Message> = iced_native::TextInput<'a, Message, Null>;
/// #[derive(Debug, Clone)]
/// enum Message {
/// TextInputChanged(String),
@@ -35,7 +36,7 @@ use unicode_segmentation::UnicodeSegmentation;
/// ```
/// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true)
#[allow(missing_debug_implementations)]
-pub struct TextInput<'a, Message> {
+pub struct TextInput<'a, Message, Renderer: self::Renderer> {
state: &'a mut State,
placeholder: String,
value: Value,
@@ -46,9 +47,10 @@ pub struct TextInput<'a, Message> {
size: Option<u16>,
on_change: Box<dyn Fn(String) -> Message>,
on_submit: Option<Message>,
+ style: Renderer::Style,
}
-impl<'a, Message> TextInput<'a, Message> {
+impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> {
/// Creates a new [`TextInput`].
///
/// It expects:
@@ -64,7 +66,7 @@ impl<'a, Message> TextInput<'a, Message> {
placeholder: &str,
value: &str,
on_change: F,
- ) -> TextInput<'a, Message>
+ ) -> Self
where
F: 'static + Fn(String) -> Message,
{
@@ -79,6 +81,7 @@ impl<'a, Message> TextInput<'a, Message> {
size: None,
on_change: Box::new(on_change),
on_submit: None,
+ style: Renderer::Style::default(),
}
}
@@ -130,11 +133,20 @@ impl<'a, Message> TextInput<'a, Message> {
self.on_submit = Some(message);
self
}
+
+ /// Sets the style of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
+ self.style = style.into();
+ self
+ }
}
-impl<'a, Message, Renderer> Widget<Message, Renderer> for TextInput<'a, Message>
+impl<'a, Message, Renderer> Widget<Message, Renderer>
+ for TextInput<'a, Message, Renderer>
where
- Renderer: self::Renderer,
+ Renderer: 'static + self::Renderer,
Message: Clone + std::fmt::Debug,
{
fn width(&self) -> Length {
@@ -343,6 +355,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
+ _defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
@@ -358,6 +371,7 @@ where
&self.placeholder,
&self.value.secure(),
&self.state,
+ &self.style,
)
} else {
renderer.draw(
@@ -368,6 +382,7 @@ where
&self.placeholder,
&self.value,
&self.state,
+ &self.style,
)
}
}
@@ -375,7 +390,7 @@ where
fn hash_layout(&self, state: &mut Hasher) {
use std::{any::TypeId, hash::Hash};
- TypeId::of::<TextInput<'static, ()>>().hash(state);
+ TypeId::of::<TextInput<'static, (), Renderer>>().hash(state);
self.width.hash(state);
self.max_width.hash(state);
@@ -392,6 +407,9 @@ where
/// [`TextInput`]: struct.TextInput.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized {
+ /// The style supported by this renderer.
+ type Style: Default;
+
/// Returns the default size of the text of the [`TextInput`].
///
/// [`TextInput`]: struct.TextInput.html
@@ -440,17 +458,18 @@ pub trait Renderer: crate::Renderer + Sized {
placeholder: &str,
value: &Value,
state: &State,
+ style: &Self::Style,
) -> Self::Output;
}
-impl<'a, Message, Renderer> From<TextInput<'a, Message>>
+impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'static + self::Renderer,
Message: 'static + Clone + std::fmt::Debug,
{
fn from(
- text_input: TextInput<'a, Message>,
+ text_input: TextInput<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(text_input)
}
diff --git a/src/application.rs b/src/application.rs
index a7e826fb..7dd76774 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -151,7 +151,12 @@ pub trait Application: Sized {
Self: 'static,
{
#[cfg(not(target_arch = "wasm32"))]
- <Instance<Self> as iced_winit::Application>::run(_settings.into());
+ <Instance<Self> as iced_winit::Application>::run(
+ _settings.into(),
+ iced_wgpu::Settings {
+ default_font: _settings.default_font,
+ },
+ );
#[cfg(target_arch = "wasm32")]
<Instance<Self> as iced_web::Application>::run();
diff --git a/src/native.rs b/src/native.rs
index 022cf337..35441a3e 100644
--- a/src/native.rs
+++ b/src/native.rs
@@ -1,6 +1,6 @@
pub use iced_winit::{
Align, Background, Color, Command, Font, HorizontalAlignment, Length,
- Space, Subscription, VerticalAlignment,
+ Space, Subscription, Vector, VerticalAlignment,
};
pub mod widget {
@@ -22,58 +22,7 @@ pub mod widget {
//!
//! [`TextInput`]: text_input/struct.TextInput.html
//! [`text_input::State`]: text_input/struct.State.html
- pub mod button {
- //! Allow your users to perform actions by pressing a button.
- //!
- //! A [`Button`] has some local [`State`].
- //!
- //! [`Button`]: type.Button.html
- //! [`State`]: struct.State.html
-
- /// A widget that produces a message when clicked.
- ///
- /// This is an alias of an `iced_native` button with a default
- /// `Renderer`.
- pub type Button<'a, Message> =
- iced_winit::Button<'a, Message, iced_wgpu::Renderer>;
-
- pub use iced_winit::button::State;
- }
-
- pub mod scrollable {
- //! Navigate an endless amount of content with a scrollbar.
-
- /// A widget that can vertically display an infinite amount of content
- /// with a scrollbar.
- ///
- /// This is an alias of an `iced_native` scrollable with a default
- /// `Renderer`.
- pub type Scrollable<'a, Message> =
- iced_winit::Scrollable<'a, Message, iced_wgpu::Renderer>;
-
- pub use iced_winit::scrollable::State;
- }
-
- pub mod text_input {
- //! Ask for information using text fields.
- //!
- //! A [`TextInput`] has some local [`State`].
- //!
- //! [`TextInput`]: struct.TextInput.html
- //! [`State`]: struct.State.html
- pub use iced_winit::text_input::{State, TextInput};
- }
-
- pub mod slider {
- //! 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
- pub use iced_winit::slider::{Slider, State};
- }
+ pub use iced_wgpu::widget::*;
pub mod image {
//! Display images in your user interface.
@@ -85,12 +34,13 @@ pub mod widget {
pub use iced_winit::svg::{Handle, Svg};
}
- pub use iced_winit::{Checkbox, ProgressBar, Radio, Text};
+ pub use iced_winit::Text;
#[doc(no_inline)]
pub use {
- button::Button, image::Image, scrollable::Scrollable, slider::Slider,
- svg::Svg, text_input::TextInput,
+ button::Button, checkbox::Checkbox, container::Container, image::Image,
+ progress_bar::ProgressBar, radio::Radio, scrollable::Scrollable,
+ slider::Slider, svg::Svg, text_input::TextInput,
};
/// A container that distributes its contents vertically.
@@ -104,13 +54,6 @@ pub mod widget {
/// This is an alias of an `iced_native` row with a default `Renderer`.
pub type Row<'a, Message> =
iced_winit::Row<'a, Message, iced_wgpu::Renderer>;
-
- /// An element decorating some content.
- ///
- /// This is an alias of an `iced_native` container with a default
- /// `Renderer`.
- pub type Container<'a, Message> =
- iced_winit::Container<'a, Message, iced_wgpu::Renderer>;
}
#[doc(no_inline)]
diff --git a/src/settings.rs b/src/settings.rs
index 62a1a614..e20edc97 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -9,6 +9,12 @@ pub struct Settings {
///
/// [`Window`]: struct.Window.html
pub window: Window,
+
+ /// The bytes of the font that will be used by default.
+ ///
+ /// If `None` is provided, a default system font will be chosen.
+ // TODO: Add `name` for web compatibility
+ pub default_font: Option<&'static [u8]>,
}
/// The window settings of an application.
diff --git a/style/Cargo.toml b/style/Cargo.toml
new file mode 100644
index 00000000..5928c60d
--- /dev/null
+++ b/style/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "iced_style"
+version = "0.1.0-alpha"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2018"
+description = "The default set of styles of Iced"
+license = "MIT"
+repository = "https://github.com/hecrj/iced"
+documentation = "https://docs.rs/iced_style"
+keywords = ["gui", "ui", "graphics", "interface", "widgets"]
+categories = ["gui"]
+
+[dependencies]
+iced_core = { version = "0.1.0", path = "../core" }
diff --git a/style/src/button.rs b/style/src/button.rs
new file mode 100644
index 00000000..93c27860
--- /dev/null
+++ b/style/src/button.rs
@@ -0,0 +1,96 @@
+//! Allow your users to perform actions by pressing a button.
+use iced_core::{Background, Color, Vector};
+
+/// The appearance of a button.
+#[derive(Debug)]
+pub struct Style {
+ pub shadow_offset: Vector,
+ pub background: Option<Background>,
+ pub border_radius: u16,
+ pub border_width: u16,
+ pub border_color: Color,
+ pub text_color: Color,
+}
+
+impl std::default::Default for Style {
+ fn default() -> Self {
+ Self {
+ shadow_offset: Vector::default(),
+ background: None,
+ border_radius: 0,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ text_color: Color::BLACK,
+ }
+ }
+}
+
+/// A set of rules that dictate the style of a button.
+pub trait StyleSheet {
+ fn active(&self) -> Style;
+
+ fn hovered(&self) -> Style {
+ let active = self.active();
+
+ Style {
+ shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0),
+ ..active
+ }
+ }
+
+ fn pressed(&self) -> Style {
+ Style {
+ shadow_offset: Vector::default(),
+ ..self.active()
+ }
+ }
+
+ fn disabled(&self) -> Style {
+ let active = self.active();
+
+ Style {
+ shadow_offset: Vector::default(),
+ background: active.background.map(|background| match background {
+ Background::Color(color) => Background::Color(Color {
+ a: color.a * 0.5,
+ ..color
+ }),
+ }),
+ text_color: Color {
+ a: active.text_color.a * 0.5,
+ ..active.text_color
+ },
+ ..active
+ }
+ }
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn active(&self) -> Style {
+ Style {
+ shadow_offset: Vector::new(0.0, 1.0),
+ background: Some(Background::Color([0.5, 0.5, 0.5].into())),
+ border_radius: 5,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ text_color: Color::WHITE,
+ }
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/style/src/checkbox.rs b/style/src/checkbox.rs
new file mode 100644
index 00000000..3c645f15
--- /dev/null
+++ b/style/src/checkbox.rs
@@ -0,0 +1,55 @@
+//! Show toggle controls using checkboxes.
+use iced_core::{Background, Color};
+
+/// The appearance of a checkbox.
+#[derive(Debug)]
+pub struct Style {
+ pub background: Background,
+ pub checkmark_color: Color,
+ pub border_radius: u16,
+ pub border_width: u16,
+ pub border_color: Color,
+}
+
+/// A set of rules that dictate the style of a checkbox.
+pub trait StyleSheet {
+ fn active(&self, is_checked: bool) -> Style;
+
+ fn hovered(&self, is_checked: bool) -> Style;
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn active(&self, _is_checked: bool) -> Style {
+ Style {
+ background: Background::Color(Color::from_rgb(0.95, 0.95, 0.95)),
+ checkmark_color: Color::from_rgb(0.3, 0.3, 0.3),
+ border_radius: 5,
+ border_width: 1,
+ border_color: Color::from_rgb(0.6, 0.6, 0.6),
+ }
+ }
+
+ fn hovered(&self, is_checked: bool) -> Style {
+ Style {
+ background: Background::Color(Color::from_rgb(0.90, 0.90, 0.90)),
+ ..self.active(is_checked)
+ }
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/style/src/container.rs b/style/src/container.rs
new file mode 100644
index 00000000..d2247342
--- /dev/null
+++ b/style/src/container.rs
@@ -0,0 +1,59 @@
+//! Decorate content and apply alignment.
+use iced_core::{Background, Color};
+
+/// The appearance of a container.
+#[derive(Debug, Clone, Copy)]
+pub struct Style {
+ pub text_color: Option<Color>,
+ pub background: Option<Background>,
+ pub border_radius: u16,
+ pub border_width: u16,
+ pub border_color: Color,
+}
+
+impl std::default::Default for Style {
+ fn default() -> Self {
+ Self {
+ text_color: None,
+ background: None,
+ border_radius: 0,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ }
+ }
+}
+
+/// A set of rules that dictate the style of a container.
+pub trait StyleSheet {
+ /// Produces the style of a container.
+ fn style(&self) -> Style;
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn style(&self) -> Style {
+ Style {
+ text_color: None,
+ background: None,
+ border_radius: 0,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ }
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/style/src/lib.rs b/style/src/lib.rs
new file mode 100644
index 00000000..e0f56594
--- /dev/null
+++ b/style/src/lib.rs
@@ -0,0 +1,8 @@
+pub mod button;
+pub mod checkbox;
+pub mod container;
+pub mod progress_bar;
+pub mod radio;
+pub mod scrollable;
+pub mod slider;
+pub mod text_input;
diff --git a/style/src/progress_bar.rs b/style/src/progress_bar.rs
new file mode 100644
index 00000000..73503fa8
--- /dev/null
+++ b/style/src/progress_bar.rs
@@ -0,0 +1,42 @@
+//! Provide progress feedback to your users.
+use iced_core::{Background, Color};
+
+/// The appearance of a progress bar.
+#[derive(Debug)]
+pub struct Style {
+ pub background: Background,
+ pub bar: Background,
+ pub border_radius: u16,
+}
+
+/// A set of rules that dictate the style of a progress bar.
+pub trait StyleSheet {
+ fn style(&self) -> Style;
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn style(&self) -> Style {
+ Style {
+ background: Background::Color(Color::from_rgb(0.6, 0.6, 0.6)),
+ bar: Background::Color(Color::from_rgb(0.3, 0.9, 0.3)),
+ border_radius: 5,
+ }
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/style/src/radio.rs b/style/src/radio.rs
new file mode 100644
index 00000000..1f0689b9
--- /dev/null
+++ b/style/src/radio.rs
@@ -0,0 +1,53 @@
+//! Create choices using radio buttons.
+use iced_core::{Background, Color};
+
+/// The appearance of a radio button.
+#[derive(Debug)]
+pub struct Style {
+ pub background: Background,
+ pub dot_color: Color,
+ pub border_width: u16,
+ pub border_color: Color,
+}
+
+/// A set of rules that dictate the style of a radio button.
+pub trait StyleSheet {
+ fn active(&self) -> Style;
+
+ fn hovered(&self) -> Style;
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn active(&self) -> Style {
+ Style {
+ background: Background::Color(Color::from_rgb(0.95, 0.95, 0.95)),
+ dot_color: Color::from_rgb(0.3, 0.3, 0.3),
+ border_width: 1,
+ border_color: Color::from_rgb(0.6, 0.6, 0.6),
+ }
+ }
+
+ fn hovered(&self) -> Style {
+ Style {
+ background: Background::Color(Color::from_rgb(0.90, 0.90, 0.90)),
+ ..self.active()
+ }
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs
new file mode 100644
index 00000000..690c14a2
--- /dev/null
+++ b/style/src/scrollable.rs
@@ -0,0 +1,76 @@
+//! Navigate an endless amount of content with a scrollbar.
+use iced_core::{Background, Color};
+
+/// The appearance of a scrollable.
+#[derive(Debug, Clone, Copy)]
+pub struct Scrollbar {
+ pub background: Option<Background>,
+ pub border_radius: u16,
+ pub border_width: u16,
+ pub border_color: Color,
+ pub scroller: Scroller,
+}
+
+/// The appearance of the scroller of a scrollable.
+#[derive(Debug, Clone, Copy)]
+pub struct Scroller {
+ pub color: Color,
+ pub border_radius: u16,
+ pub border_width: u16,
+ pub border_color: Color,
+}
+
+/// A set of rules that dictate the style of a scrollable.
+pub trait StyleSheet {
+ /// Produces the style of an active scrollbar.
+ fn active(&self) -> Scrollbar;
+
+ /// Produces the style of an hovered scrollbar.
+ fn hovered(&self) -> Scrollbar;
+
+ /// Produces the style of a scrollbar that is being dragged.
+ fn dragging(&self) -> Scrollbar {
+ self.hovered()
+ }
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn active(&self) -> Scrollbar {
+ Scrollbar {
+ background: None,
+ border_radius: 5,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ scroller: Scroller {
+ color: [0.0, 0.0, 0.0, 0.7].into(),
+ border_radius: 5,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ },
+ }
+ }
+
+ fn hovered(&self) -> Scrollbar {
+ Scrollbar {
+ background: Some(Background::Color([0.0, 0.0, 0.0, 0.3].into())),
+ ..self.active()
+ }
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/style/src/slider.rs b/style/src/slider.rs
new file mode 100644
index 00000000..776e180c
--- /dev/null
+++ b/style/src/slider.rs
@@ -0,0 +1,95 @@
+//! Display an interactive selector of a single value from a range of values.
+use iced_core::Color;
+
+/// The appearance of a slider.
+#[derive(Debug, Clone, Copy)]
+pub struct Style {
+ pub rail_colors: (Color, Color),
+ pub handle: Handle,
+}
+
+/// The appearance of the handle of a slider.
+#[derive(Debug, Clone, Copy)]
+pub struct Handle {
+ pub shape: HandleShape,
+ pub color: Color,
+ pub border_width: u16,
+ pub border_color: Color,
+}
+
+/// The shape of the handle of a slider.
+#[derive(Debug, Clone, Copy)]
+pub enum HandleShape {
+ Circle { radius: u16 },
+ Rectangle { width: u16, border_radius: u16 },
+}
+
+/// A set of rules that dictate the style of a slider.
+pub trait StyleSheet {
+ /// Produces the style of an active slider.
+ fn active(&self) -> Style;
+
+ /// Produces the style of an hovered slider.
+ fn hovered(&self) -> Style;
+
+ /// Produces the style of a slider that is being dragged.
+ fn dragging(&self) -> Style;
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn active(&self) -> Style {
+ Style {
+ rail_colors: ([0.6, 0.6, 0.6, 0.5].into(), Color::WHITE),
+ handle: Handle {
+ shape: HandleShape::Rectangle {
+ width: 8,
+ border_radius: 4,
+ },
+ color: Color::from_rgb(0.95, 0.95, 0.95),
+ border_color: Color::from_rgb(0.6, 0.6, 0.6),
+ border_width: 1,
+ },
+ }
+ }
+
+ fn hovered(&self) -> Style {
+ let active = self.active();
+
+ Style {
+ handle: Handle {
+ color: Color::from_rgb(0.90, 0.90, 0.90),
+ ..active.handle
+ },
+ ..active
+ }
+ }
+
+ fn dragging(&self) -> Style {
+ let active = self.active();
+
+ Style {
+ handle: Handle {
+ color: Color::from_rgb(0.85, 0.85, 0.85),
+ ..active.handle
+ },
+ ..active
+ }
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/style/src/text_input.rs b/style/src/text_input.rs
new file mode 100644
index 00000000..c5123b20
--- /dev/null
+++ b/style/src/text_input.rs
@@ -0,0 +1,83 @@
+//! Display fields that can be filled with text.
+use iced_core::{Background, Color};
+
+/// The appearance of a text input.
+#[derive(Debug, Clone, Copy)]
+pub struct Style {
+ pub background: Background,
+ pub border_radius: u16,
+ pub border_width: u16,
+ pub border_color: Color,
+}
+
+impl std::default::Default for Style {
+ fn default() -> Self {
+ Self {
+ background: Background::Color(Color::WHITE),
+ border_radius: 0,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ }
+ }
+}
+
+/// A set of rules that dictate the style of a text input.
+pub trait StyleSheet {
+ /// Produces the style of an active text input.
+ fn active(&self) -> Style;
+
+ /// Produces the style of a focused text input.
+ fn focused(&self) -> Style;
+
+ fn placeholder_color(&self) -> Color;
+
+ fn value_color(&self) -> Color;
+
+ /// Produces the style of an hovered text input.
+ fn hovered(&self) -> Style {
+ self.focused()
+ }
+}
+
+struct Default;
+
+impl StyleSheet for Default {
+ fn active(&self) -> Style {
+ Style {
+ background: Background::Color(Color::WHITE),
+ border_radius: 5,
+ border_width: 1,
+ border_color: Color::from_rgb(0.7, 0.7, 0.7),
+ }
+ }
+
+ fn focused(&self) -> Style {
+ Style {
+ border_color: Color::from_rgb(0.5, 0.5, 0.5),
+ ..self.active()
+ }
+ }
+
+ fn placeholder_color(&self) -> Color {
+ Color::from_rgb(0.7, 0.7, 0.7)
+ }
+
+ fn value_color(&self) -> Color {
+ Color::from_rgb(0.3, 0.3, 0.3)
+ }
+}
+
+impl std::default::Default for Box<dyn StyleSheet> {
+ fn default() -> Self {
+ Box::new(Default)
+ }
+}
+
+impl<T> From<T> for Box<dyn StyleSheet>
+where
+ T: 'static + StyleSheet,
+{
+ fn from(style: T) -> Self {
+ Box::new(style)
+ }
+}
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index bb241914..19d41bba 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -12,6 +12,7 @@ svg = ["resvg"]
[dependencies]
iced_native = { version = "0.1.0", path = "../native" }
+iced_style = { version = "0.1.0-alpha", path = "../style" }
wgpu = "0.4"
glyph_brush = "0.6"
wgpu_glyph = { version = "0.7", git = "https://github.com/hecrj/wgpu_glyph", branch = "fix/font-load-panic" }
diff --git a/wgpu/src/defaults.rs b/wgpu/src/defaults.rs
new file mode 100644
index 00000000..11718a87
--- /dev/null
+++ b/wgpu/src/defaults.rs
@@ -0,0 +1,32 @@
+//! Use default styling attributes to inherit styles.
+use iced_native::Color;
+
+/// Some default styling attributes.
+#[derive(Debug, Clone, Copy)]
+pub struct Defaults {
+ /// Text styling
+ pub text: Text,
+}
+
+impl Default for Defaults {
+ fn default() -> Defaults {
+ Defaults {
+ text: Text::default(),
+ }
+ }
+}
+
+/// Some default text styling attributes.
+#[derive(Debug, Clone, Copy)]
+pub struct Text {
+ /// The default color of text
+ pub color: Color,
+}
+
+impl Default for Text {
+ fn default() -> Text {
+ Text {
+ color: Color::BLACK,
+ }
+ }
+}
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 972f56af..ab14987c 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -24,18 +24,25 @@
#![deny(unused_results)]
#![deny(unsafe_code)]
#![deny(rust_2018_idioms)]
+pub mod defaults;
pub mod triangle;
+pub mod widget;
mod image;
mod primitive;
mod quad;
mod renderer;
+mod settings;
mod text;
mod transformation;
-pub(crate) use crate::image::Image;
-pub(crate) use quad::Quad;
-pub(crate) use transformation::Transformation;
-
+pub use defaults::Defaults;
pub use primitive::Primitive;
pub use renderer::{Renderer, Target};
+pub use settings::Settings;
+#[doc(no_inline)]
+pub use widget::*;
+
+pub(crate) use self::image::Image;
+pub(crate) use quad::Quad;
+pub(crate) use transformation::Transformation;
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
index 815ba3b0..481252ef 100644
--- a/wgpu/src/primitive.rs
+++ b/wgpu/src/primitive.rs
@@ -41,6 +41,10 @@ pub enum Primitive {
background: Background,
/// The border radius of the quad
border_radius: u16,
+ /// The border width of the quad
+ border_width: u16,
+ /// The border color of the quad
+ border_color: Color,
},
/// An image primitive
Image {
diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs
index c292dec3..fe3276a3 100644
--- a/wgpu/src/quad.rs
+++ b/wgpu/src/quad.rs
@@ -125,9 +125,19 @@ impl Pipeline {
},
wgpu::VertexAttributeDescriptor {
shader_location: 4,
- format: wgpu::VertexFormat::Float,
+ format: wgpu::VertexFormat::Float4,
offset: 4 * (2 + 2 + 4),
},
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 5,
+ format: wgpu::VertexFormat::Float,
+ offset: 4 * (2 + 2 + 4 + 4),
+ },
+ wgpu::VertexAttributeDescriptor {
+ shader_location: 6,
+ format: wgpu::VertexFormat::Float,
+ offset: 4 * (2 + 2 + 4 + 4 + 1),
+ },
],
},
],
@@ -233,7 +243,8 @@ impl Pipeline {
bounds.x,
bounds.y,
bounds.width,
- bounds.height,
+ // TODO: Address anti-aliasing adjustments properly
+ bounds.height + 1,
);
render_pass.draw_indexed(
@@ -277,7 +288,9 @@ pub struct Quad {
pub position: [f32; 2],
pub scale: [f32; 2],
pub color: [f32; 4],
+ pub border_color: [f32; 4],
pub border_radius: f32,
+ pub border_width: f32,
}
impl Quad {
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs
index 050daca9..9757904c 100644
--- a/wgpu/src/renderer.rs
+++ b/wgpu/src/renderer.rs
@@ -1,5 +1,6 @@
use crate::{
- image, quad, text, triangle, Image, Primitive, Quad, Transformation,
+ image, quad, text, triangle, Defaults, Image, Primitive, Quad, Settings,
+ Transformation,
};
use iced_native::{
renderer::{Debugger, Windowed},
@@ -24,7 +25,7 @@ pub struct Renderer {
device: Device,
queue: Queue,
quad_pipeline: quad::Pipeline,
- image_pipeline: crate::image::Pipeline,
+ image_pipeline: image::Pipeline,
text_pipeline: text::Pipeline,
triangle_pipeline: crate::triangle::Pipeline,
}
@@ -52,7 +53,7 @@ impl<'a> Layer<'a> {
}
impl Renderer {
- fn new() -> Self {
+ fn new(settings: Settings) -> Self {
let adapter = Adapter::request(&RequestAdapterOptions {
power_preference: PowerPreference::Default,
backends: BackendBit::all(),
@@ -66,7 +67,8 @@ impl Renderer {
limits: Limits { max_bind_groups: 2 },
});
- let text_pipeline = text::Pipeline::new(&mut device);
+ let text_pipeline =
+ text::Pipeline::new(&mut device, settings.default_font);
let quad_pipeline = quad::Pipeline::new(&mut device);
let image_pipeline = crate::image::Pipeline::new(&mut device);
let triangle_pipeline = triangle::Pipeline::new(&mut device);
@@ -223,6 +225,8 @@ impl Renderer {
bounds,
background,
border_radius,
+ border_width,
+ border_color,
} => {
// TODO: Move some of this computations to the GPU (?)
layer.quads.push(Quad {
@@ -235,6 +239,8 @@ impl Renderer {
Background::Color(color) => color.into_linear(),
},
border_radius: *border_radius as f32,
+ border_width: *border_width as f32,
+ border_color: border_color.into_linear(),
});
}
Primitive::Image { handle, bounds } => {
@@ -434,6 +440,7 @@ impl Renderer {
impl iced_native::Renderer for Renderer {
type Output = (Primitive, MouseCursor);
+ type Defaults = Defaults;
fn layout<'a, Message>(
&mut self,
@@ -448,10 +455,11 @@ impl iced_native::Renderer for Renderer {
}
impl Windowed for Renderer {
+ type Settings = Settings;
type Target = Target;
- fn new() -> Self {
- Self::new()
+ fn new(settings: Settings) -> Self {
+ Self::new(settings)
}
fn draw<T: AsRef<str>>(
@@ -467,13 +475,15 @@ impl Windowed for Renderer {
impl Debugger for Renderer {
fn explain<Message>(
&mut self,
+ defaults: &Defaults,
widget: &dyn Widget<Message, Self>,
layout: Layout<'_>,
cursor_position: Point,
color: Color,
) -> Self::Output {
let mut primitives = Vec::new();
- let (primitive, cursor) = widget.draw(self, layout, cursor_position);
+ let (primitive, cursor) =
+ widget.draw(self, defaults, layout, cursor_position);
explain_layout(layout, color, &mut primitives);
primitives.push(primitive);
@@ -487,11 +497,12 @@ fn explain_layout(
color: Color,
primitives: &mut Vec<Primitive>,
) {
- // TODO: Draw borders instead
primitives.push(Primitive::Quad {
bounds: layout.bounds(),
- background: Background::Color([0.0, 0.0, 0.0, 0.05].into()),
+ background: Background::Color(Color::TRANSPARENT),
border_radius: 0,
+ border_width: 1,
+ border_color: [0.6, 0.6, 0.6, 0.5].into(),
});
for child in layout.children() {
diff --git a/wgpu/src/renderer/widget.rs b/wgpu/src/renderer/widget.rs
index 32187c10..2c75413f 100644
--- a/wgpu/src/renderer/widget.rs
+++ b/wgpu/src/renderer/widget.rs
@@ -1,6 +1,7 @@
mod button;
mod checkbox;
mod column;
+mod container;
mod image;
mod progress_bar;
mod radio;
diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs
index 86963053..a9209f64 100644
--- a/wgpu/src/renderer/widget/button.rs
+++ b/wgpu/src/renderer/widget/button.rs
@@ -1,54 +1,86 @@
-use crate::{Primitive, Renderer};
-use iced_native::{button, Background, MouseCursor, Point, Rectangle};
+use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer};
+use iced_native::{
+ Background, Color, Element, Layout, MouseCursor, Point, Rectangle, Vector,
+};
-impl button::Renderer for Renderer {
- fn draw(
+impl iced_native::button::Renderer for Renderer {
+ type Style = Box<dyn StyleSheet>;
+
+ fn draw<Message>(
&mut self,
+ defaults: &Defaults,
bounds: Rectangle,
cursor_position: Point,
+ is_disabled: bool,
is_pressed: bool,
- background: Option<Background>,
- border_radius: u16,
- (content, _): Self::Output,
+ style: &Box<dyn StyleSheet>,
+ content: &Element<'_, Message, Self>,
+ content_layout: Layout<'_>,
) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position);
- // TODO: Render proper shadows
- // TODO: Make hovering and pressed styles configurable
- let shadow_offset = if is_mouse_over {
+ let styling = if is_disabled {
+ style.disabled()
+ } else if is_mouse_over {
if is_pressed {
- 0.0
+ style.pressed()
} else {
- 2.0
+ style.hovered()
}
} else {
- 1.0
+ style.active()
};
+ let (content, _) = content.draw(
+ self,
+ &Defaults {
+ text: defaults::Text {
+ color: styling.text_color,
+ },
+ ..*defaults
+ },
+ content_layout,
+ cursor_position,
+ );
+
(
- match background {
- None => content,
- Some(background) => Primitive::Group {
- primitives: vec![
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + 1.0,
- y: bounds.y + shadow_offset,
- ..bounds
- },
- background: Background::Color(
- [0.0, 0.0, 0.0, 0.5].into(),
- ),
- border_radius,
- },
- Primitive::Quad {
- bounds,
- background,
- border_radius,
+ if styling.background.is_some() || styling.border_width > 0 {
+ let background = Primitive::Quad {
+ bounds,
+ background: styling
+ .background
+ .unwrap_or(Background::Color(Color::TRANSPARENT)),
+ border_radius: styling.border_radius,
+ border_width: styling.border_width,
+ border_color: styling.border_color,
+ };
+
+ if styling.shadow_offset == Vector::default() {
+ Primitive::Group {
+ primitives: vec![background, content],
+ }
+ } else {
+ // TODO: Implement proper shadow support
+ let shadow = Primitive::Quad {
+ bounds: Rectangle {
+ x: bounds.x + styling.shadow_offset.x,
+ y: bounds.y + styling.shadow_offset.y,
+ ..bounds
},
- content,
- ],
- },
+ background: Background::Color(
+ [0.0, 0.0, 0.0, 0.5].into(),
+ ),
+ border_radius: styling.border_radius,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ };
+
+ Primitive::Group {
+ primitives: vec![shadow, background, content],
+ }
+ }
+ } else {
+ content
},
if is_mouse_over {
MouseCursor::Pointer
diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs
index 54b4b1cc..17121eea 100644
--- a/wgpu/src/renderer/widget/checkbox.rs
+++ b/wgpu/src/renderer/widget/checkbox.rs
@@ -1,12 +1,13 @@
-use crate::{Primitive, Renderer};
+use crate::{checkbox::StyleSheet, Primitive, Renderer};
use iced_native::{
- checkbox, Background, HorizontalAlignment, MouseCursor, Rectangle,
- VerticalAlignment,
+ checkbox, HorizontalAlignment, MouseCursor, Rectangle, VerticalAlignment,
};
const SIZE: f32 = 28.0;
impl checkbox::Renderer for Renderer {
+ type Style = Box<dyn StyleSheet>;
+
fn default_size(&self) -> u32 {
SIZE as u32
}
@@ -17,31 +18,21 @@ impl checkbox::Renderer for Renderer {
is_checked: bool,
is_mouse_over: bool,
(label, _): Self::Output,
+ style_sheet: &Self::Style,
) -> Self::Output {
- let (checkbox_border, checkbox_box) = (
- Primitive::Quad {
- bounds,
- background: Background::Color([0.6, 0.6, 0.6].into()),
- border_radius: 6,
- },
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + 1.0,
- y: bounds.y + 1.0,
- width: bounds.width - 2.0,
- height: bounds.height - 2.0,
- },
- background: Background::Color(
- if is_mouse_over {
- [0.90, 0.90, 0.90]
- } else {
- [0.95, 0.95, 0.95]
- }
- .into(),
- ),
- border_radius: 5,
- },
- );
+ let style = if is_mouse_over {
+ style_sheet.hovered(is_checked)
+ } else {
+ style_sheet.active(is_checked)
+ };
+
+ let checkbox = Primitive::Quad {
+ bounds,
+ background: style.background,
+ border_radius: style.border_radius,
+ border_width: style.border_width,
+ border_color: style.border_color,
+ };
(
Primitive::Group {
@@ -51,14 +42,14 @@ impl checkbox::Renderer for Renderer {
font: crate::text::BUILTIN_ICONS,
size: bounds.height * 0.7,
bounds: bounds,
- color: [0.3, 0.3, 0.3].into(),
+ color: style.checkmark_color,
horizontal_alignment: HorizontalAlignment::Center,
vertical_alignment: VerticalAlignment::Center,
};
- vec![checkbox_border, checkbox_box, check, label]
+ vec![checkbox, check, label]
} else {
- vec![checkbox_border, checkbox_box, label]
+ vec![checkbox, label]
},
},
if is_mouse_over {
diff --git a/wgpu/src/renderer/widget/column.rs b/wgpu/src/renderer/widget/column.rs
index 6c31af90..95a7463a 100644
--- a/wgpu/src/renderer/widget/column.rs
+++ b/wgpu/src/renderer/widget/column.rs
@@ -4,6 +4,7 @@ use iced_native::{column, Element, Layout, MouseCursor, Point};
impl column::Renderer for Renderer {
fn draw<Message>(
&mut self,
+ defaults: &Self::Defaults,
content: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
@@ -17,7 +18,7 @@ impl column::Renderer for Renderer {
.zip(layout.children())
.map(|(child, layout)| {
let (primitive, new_mouse_cursor) =
- child.draw(self, layout, cursor_position);
+ child.draw(self, defaults, layout, cursor_position);
if new_mouse_cursor > mouse_cursor {
mouse_cursor = new_mouse_cursor;
diff --git a/wgpu/src/renderer/widget/container.rs b/wgpu/src/renderer/widget/container.rs
new file mode 100644
index 00000000..2d4d1db8
--- /dev/null
+++ b/wgpu/src/renderer/widget/container.rs
@@ -0,0 +1,49 @@
+use crate::{container, defaults, Defaults, Primitive, Renderer};
+use iced_native::{Background, Color, Element, Layout, Point, Rectangle};
+
+impl iced_native::container::Renderer for Renderer {
+ type Style = Box<dyn container::StyleSheet>;
+
+ fn draw<Message>(
+ &mut self,
+ defaults: &Defaults,
+ bounds: Rectangle,
+ cursor_position: Point,
+ style_sheet: &Self::Style,
+ content: &Element<'_, Message, Self>,
+ content_layout: Layout<'_>,
+ ) -> Self::Output {
+ let style = style_sheet.style();
+
+ let defaults = Defaults {
+ text: defaults::Text {
+ color: style.text_color.unwrap_or(defaults.text.color),
+ },
+ ..*defaults
+ };
+
+ let (content, mouse_cursor) =
+ content.draw(self, &defaults, content_layout, cursor_position);
+
+ if style.background.is_some() || style.border_width > 0 {
+ let quad = Primitive::Quad {
+ bounds,
+ background: style
+ .background
+ .unwrap_or(Background::Color(Color::TRANSPARENT)),
+ border_radius: style.border_radius,
+ border_width: style.border_width,
+ border_color: style.border_color,
+ };
+
+ (
+ Primitive::Group {
+ primitives: vec![quad, content],
+ },
+ mouse_cursor,
+ )
+ } else {
+ (content, mouse_cursor)
+ }
+ }
+}
diff --git a/wgpu/src/renderer/widget/progress_bar.rs b/wgpu/src/renderer/widget/progress_bar.rs
index 8ed4bab7..34e33276 100644
--- a/wgpu/src/renderer/widget/progress_bar.rs
+++ b/wgpu/src/renderer/widget/progress_bar.rs
@@ -1,7 +1,9 @@
-use crate::{Primitive, Renderer};
-use iced_native::{progress_bar, Background, Color, MouseCursor, Rectangle};
+use crate::{progress_bar::StyleSheet, Primitive, Renderer};
+use iced_native::{progress_bar, Color, MouseCursor, Rectangle};
impl progress_bar::Renderer for Renderer {
+ type Style = Box<dyn StyleSheet>;
+
const DEFAULT_HEIGHT: u16 = 30;
fn draw(
@@ -9,9 +11,10 @@ impl progress_bar::Renderer for Renderer {
bounds: Rectangle,
range: std::ops::RangeInclusive<f32>,
value: f32,
- background: Option<Background>,
- active_color: Option<Color>,
+ style_sheet: &Self::Style,
) -> Self::Output {
+ let style = style_sheet.style();
+
let (range_start, range_end) = range.into_inner();
let active_progress_width = bounds.width
* ((value - range_start) / (range_end - range_start).max(1.0));
@@ -19,27 +22,31 @@ impl progress_bar::Renderer for Renderer {
let background = Primitive::Group {
primitives: vec![Primitive::Quad {
bounds: Rectangle { ..bounds },
- background: background
- .unwrap_or(Background::Color([0.6, 0.6, 0.6].into()))
- .into(),
- border_radius: 5,
+ background: style.background,
+ border_radius: style.border_radius,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
}],
};
- let active_progress = Primitive::Quad {
- bounds: Rectangle {
- width: active_progress_width,
- ..bounds
- },
- background: Background::Color(
- active_color.unwrap_or([0.0, 0.95, 0.0].into()),
- ),
- border_radius: 5,
- };
-
(
- Primitive::Group {
- primitives: vec![background, active_progress],
+ if active_progress_width > 0.0 {
+ let bar = Primitive::Quad {
+ bounds: Rectangle {
+ width: active_progress_width,
+ ..bounds
+ },
+ background: style.bar,
+ border_radius: style.border_radius,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
+ };
+
+ Primitive::Group {
+ primitives: vec![background, bar],
+ }
+ } else {
+ background
},
MouseCursor::OutOfBounds,
)
diff --git a/wgpu/src/renderer/widget/radio.rs b/wgpu/src/renderer/widget/radio.rs
index 3c00a4c2..564f066b 100644
--- a/wgpu/src/renderer/widget/radio.rs
+++ b/wgpu/src/renderer/widget/radio.rs
@@ -1,10 +1,12 @@
-use crate::{Primitive, Renderer};
-use iced_native::{radio, Background, MouseCursor, Rectangle};
+use crate::{radio::StyleSheet, Primitive, Renderer};
+use iced_native::{radio, Background, Color, MouseCursor, Rectangle};
const SIZE: f32 = 28.0;
const DOT_SIZE: f32 = SIZE / 2.0;
impl radio::Renderer for Renderer {
+ type Style = Box<dyn StyleSheet>;
+
fn default_size(&self) -> u32 {
SIZE as u32
}
@@ -15,31 +17,21 @@ impl radio::Renderer for Renderer {
is_selected: bool,
is_mouse_over: bool,
(label, _): Self::Output,
+ style_sheet: &Self::Style,
) -> Self::Output {
- let (radio_border, radio_box) = (
- Primitive::Quad {
- bounds,
- background: Background::Color([0.6, 0.6, 0.6].into()),
- border_radius: (SIZE / 2.0) as u16,
- },
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + 1.0,
- y: bounds.y + 1.0,
- width: bounds.width - 2.0,
- height: bounds.height - 2.0,
- },
- background: Background::Color(
- if is_mouse_over {
- [0.90, 0.90, 0.90]
- } else {
- [0.95, 0.95, 0.95]
- }
- .into(),
- ),
- border_radius: (SIZE / 2.0 - 1.0) as u16,
- },
- );
+ let style = if is_mouse_over {
+ style_sheet.hovered()
+ } else {
+ style_sheet.active()
+ };
+
+ let radio = Primitive::Quad {
+ bounds,
+ background: style.background,
+ border_radius: (SIZE / 2.0) as u16,
+ border_width: style.border_width,
+ border_color: style.border_color,
+ };
(
Primitive::Group {
@@ -51,13 +43,15 @@ impl radio::Renderer for Renderer {
width: bounds.width - DOT_SIZE,
height: bounds.height - DOT_SIZE,
},
- background: Background::Color([0.3, 0.3, 0.3].into()),
+ background: Background::Color(style.dot_color),
border_radius: (DOT_SIZE / 2.0) as u16,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
};
- vec![radio_border, radio_box, radio_circle, label]
+ vec![radio, radio_circle, label]
} else {
- vec![radio_border, radio_box, label]
+ vec![radio, label]
},
},
if is_mouse_over {
diff --git a/wgpu/src/renderer/widget/row.rs b/wgpu/src/renderer/widget/row.rs
index f082dc61..bd9f1a04 100644
--- a/wgpu/src/renderer/widget/row.rs
+++ b/wgpu/src/renderer/widget/row.rs
@@ -4,6 +4,7 @@ use iced_native::{row, Element, Layout, MouseCursor, Point};
impl row::Renderer for Renderer {
fn draw<Message>(
&mut self,
+ defaults: &Self::Defaults,
children: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,
@@ -17,7 +18,7 @@ impl row::Renderer for Renderer {
.zip(layout.children())
.map(|(child, layout)| {
let (primitive, new_mouse_cursor) =
- child.draw(self, layout, cursor_position);
+ child.draw(self, defaults, layout, cursor_position);
if new_mouse_cursor > mouse_cursor {
mouse_cursor = new_mouse_cursor;
diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs
index 6ef57185..bfee7411 100644
--- a/wgpu/src/renderer/widget/scrollable.rs
+++ b/wgpu/src/renderer/widget/scrollable.rs
@@ -1,10 +1,14 @@
use crate::{Primitive, Renderer};
-use iced_native::{scrollable, Background, MouseCursor, Rectangle, Vector};
+use iced_native::{
+ scrollable, Background, Color, MouseCursor, Rectangle, Vector,
+};
const SCROLLBAR_WIDTH: u16 = 10;
const SCROLLBAR_MARGIN: u16 = 2;
impl scrollable::Renderer for Renderer {
+ type Style = Box<dyn iced_style::scrollable::StyleSheet>;
+
fn scrollbar(
&self,
bounds: Rectangle,
@@ -51,6 +55,7 @@ impl scrollable::Renderer for Renderer {
is_mouse_over_scrollbar: bool,
scrollbar: Option<scrollable::Scrollbar>,
offset: u32,
+ style_sheet: &Self::Style,
(content, mouse_cursor): Self::Output,
) -> Self::Output {
let clip = Primitive::Clip {
@@ -61,40 +66,53 @@ impl scrollable::Renderer for Renderer {
(
if let Some(scrollbar) = scrollbar {
- if is_mouse_over || state.is_scroller_grabbed() {
- let scroller = Primitive::Quad {
- bounds: scrollbar.scroller.bounds,
- background: Background::Color(
- [0.0, 0.0, 0.0, 0.7].into(),
- ),
- border_radius: 5,
- };
+ let style = if state.is_scroller_grabbed() {
+ style_sheet.dragging()
+ } else if is_mouse_over_scrollbar {
+ style_sheet.hovered()
+ } else {
+ style_sheet.active()
+ };
+
+ let is_scrollbar_visible =
+ style.background.is_some() || style.border_width > 0;
- if is_mouse_over_scrollbar || state.is_scroller_grabbed() {
- let scrollbar = Primitive::Quad {
- bounds: Rectangle {
- x: scrollbar.bounds.x
- + f32::from(SCROLLBAR_MARGIN),
- width: scrollbar.bounds.width
- - f32::from(2 * SCROLLBAR_MARGIN),
- ..scrollbar.bounds
- },
- background: Background::Color(
- [0.0, 0.0, 0.0, 0.3].into(),
- ),
- border_radius: 5,
- };
+ let scroller = if is_mouse_over
+ || state.is_scroller_grabbed()
+ || is_scrollbar_visible
+ {
+ Primitive::Quad {
+ bounds: scrollbar.scroller.bounds,
+ background: Background::Color(style.scroller.color),
+ border_radius: style.scroller.border_radius,
+ border_width: style.scroller.border_width,
+ border_color: style.scroller.border_color,
+ }
+ } else {
+ Primitive::None
+ };
- Primitive::Group {
- primitives: vec![clip, scrollbar, scroller],
- }
- } else {
- Primitive::Group {
- primitives: vec![clip, scroller],
- }
+ let scrollbar = if is_scrollbar_visible {
+ Primitive::Quad {
+ bounds: Rectangle {
+ x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN),
+ width: scrollbar.bounds.width
+ - f32::from(2 * SCROLLBAR_MARGIN),
+ ..scrollbar.bounds
+ },
+ background: style
+ .background
+ .unwrap_or(Background::Color(Color::TRANSPARENT)),
+ border_radius: style.border_radius,
+ border_width: style.border_width,
+ border_color: style.border_color,
}
} else {
- clip
+ Primitive::None
+ };
+
+ Primitive::Group {
+ primitives: vec![clip, scrollbar, scroller],
}
} else {
clip
diff --git a/wgpu/src/renderer/widget/slider.rs b/wgpu/src/renderer/widget/slider.rs
index c73a4e56..c8ebd0da 100644
--- a/wgpu/src/renderer/widget/slider.rs
+++ b/wgpu/src/renderer/widget/slider.rs
@@ -1,10 +1,14 @@
-use crate::{Primitive, Renderer};
+use crate::{
+ slider::{HandleShape, StyleSheet},
+ Primitive, Renderer,
+};
use iced_native::{slider, Background, Color, MouseCursor, Point, Rectangle};
-const HANDLE_WIDTH: f32 = 8.0;
const HANDLE_HEIGHT: f32 = 22.0;
impl slider::Renderer for Renderer {
+ type Style = Box<dyn StyleSheet>;
+
fn height(&self) -> u32 {
30
}
@@ -16,9 +20,18 @@ impl slider::Renderer for Renderer {
range: std::ops::RangeInclusive<f32>,
value: f32,
is_dragging: bool,
+ style_sheet: &Self::Style,
) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position);
+ let style = if is_dragging {
+ style_sheet.dragging()
+ } else if is_mouse_over {
+ style_sheet.hovered()
+ } else {
+ style_sheet.active()
+ };
+
let rail_y = bounds.y + (bounds.height / 2.0).round();
let (rail_top, rail_bottom) = (
@@ -29,8 +42,10 @@ impl slider::Renderer for Renderer {
width: bounds.width,
height: 2.0,
},
- background: Color::from_rgb(0.6, 0.6, 0.6).into(),
+ background: Background::Color(style.rail_colors.0),
border_radius: 0,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
},
Primitive::Quad {
bounds: Rectangle {
@@ -39,51 +54,45 @@ impl slider::Renderer for Renderer {
width: bounds.width,
height: 2.0,
},
- background: Background::Color(Color::WHITE),
+ background: Background::Color(style.rail_colors.1),
border_radius: 0,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
},
);
let (range_start, range_end) = range.into_inner();
- let handle_offset = (bounds.width - HANDLE_WIDTH)
+ let (handle_width, handle_height, handle_border_radius) =
+ match style.handle.shape {
+ HandleShape::Circle { radius } => {
+ (f32::from(radius * 2), f32::from(radius * 2), radius)
+ }
+ HandleShape::Rectangle {
+ width,
+ border_radius,
+ } => (f32::from(width), HANDLE_HEIGHT, border_radius),
+ };
+
+ let handle_offset = (bounds.width - handle_width)
* ((value - range_start) / (range_end - range_start).max(1.0));
- let (handle_border, handle) = (
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + handle_offset.round() - 1.0,
- y: rail_y - HANDLE_HEIGHT / 2.0 - 1.0,
- width: HANDLE_WIDTH + 2.0,
- height: HANDLE_HEIGHT + 2.0,
- },
- background: Color::from_rgb(0.6, 0.6, 0.6).into(),
- border_radius: 5,
+ let handle = Primitive::Quad {
+ bounds: Rectangle {
+ x: bounds.x + handle_offset.round(),
+ y: rail_y - handle_height / 2.0,
+ width: handle_width,
+ height: handle_height,
},
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + handle_offset.round(),
- y: rail_y - HANDLE_HEIGHT / 2.0,
- width: HANDLE_WIDTH,
- height: HANDLE_HEIGHT,
- },
- background: Background::Color(
- if is_dragging {
- [0.85, 0.85, 0.85]
- } else if is_mouse_over {
- [0.90, 0.90, 0.90]
- } else {
- [0.95, 0.95, 0.95]
- }
- .into(),
- ),
- border_radius: 4,
- },
- );
+ background: Background::Color(style.handle.color),
+ border_radius: handle_border_radius,
+ border_width: style.handle.border_width,
+ border_color: style.handle.border_color,
+ };
(
Primitive::Group {
- primitives: vec![rail_top, rail_bottom, handle_border, handle],
+ primitives: vec![rail_top, rail_bottom, handle],
},
if is_dragging {
MouseCursor::Grabbing
diff --git a/wgpu/src/renderer/widget/text.rs b/wgpu/src/renderer/widget/text.rs
index 08a162ba..d61c5523 100644
--- a/wgpu/src/renderer/widget/text.rs
+++ b/wgpu/src/renderer/widget/text.rs
@@ -27,6 +27,7 @@ impl text::Renderer for Renderer {
fn draw(
&mut self,
+ defaults: &Self::Defaults,
bounds: Rectangle,
content: &str,
size: u16,
@@ -40,7 +41,7 @@ impl text::Renderer for Renderer {
content: content.to_string(),
size: f32::from(size),
bounds,
- color: color.unwrap_or(Color::BLACK),
+ color: color.unwrap_or(defaults.text.color),
font,
horizontal_alignment,
vertical_alignment,
diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs
index 929f94db..8b774a48 100644
--- a/wgpu/src/renderer/widget/text_input.rs
+++ b/wgpu/src/renderer/widget/text_input.rs
@@ -1,4 +1,4 @@
-use crate::{Primitive, Renderer};
+use crate::{text_input::StyleSheet, Primitive, Renderer};
use iced_native::{
text_input, Background, Color, Font, HorizontalAlignment, MouseCursor,
@@ -7,6 +7,8 @@ use iced_native::{
use std::f32;
impl text_input::Renderer for Renderer {
+ type Style = Box<dyn StyleSheet>;
+
fn default_size(&self) -> u16 {
// TODO: Make this configurable
20
@@ -61,31 +63,24 @@ impl text_input::Renderer for Renderer {
placeholder: &str,
value: &text_input::Value,
state: &text_input::State,
+ style_sheet: &Self::Style,
) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position);
- let border = Primitive::Quad {
- bounds,
- background: Background::Color(
- if is_mouse_over || state.is_focused() {
- [0.5, 0.5, 0.5]
- } else {
- [0.7, 0.7, 0.7]
- }
- .into(),
- ),
- border_radius: 5,
+ let style = if state.is_focused() {
+ style_sheet.focused()
+ } else if is_mouse_over {
+ style_sheet.hovered()
+ } else {
+ style_sheet.active()
};
let input = Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + 1.0,
- y: bounds.y + 1.0,
- width: bounds.width - 2.0,
- height: bounds.height - 2.0,
- },
- background: Background::Color(Color::WHITE),
- border_radius: 4,
+ bounds,
+ background: style.background,
+ border_radius: style.border_radius,
+ border_width: style.border_width,
+ border_color: style.border_color,
};
let text = value.to_string();
@@ -97,9 +92,9 @@ impl text_input::Renderer for Renderer {
text.clone()
},
color: if text.is_empty() {
- [0.7, 0.7, 0.7]
+ style_sheet.placeholder_color()
} else {
- [0.3, 0.3, 0.3]
+ style_sheet.value_color()
}
.into(),
font: Font::Default,
@@ -128,8 +123,10 @@ impl text_input::Renderer for Renderer {
width: 1.0,
height: text_bounds.height,
},
- background: Background::Color(Color::BLACK),
+ background: Background::Color(style_sheet.value_color()),
border_radius: 0,
+ border_width: 0,
+ border_color: Color::TRANSPARENT,
};
(
@@ -150,7 +147,7 @@ impl text_input::Renderer for Renderer {
(
Primitive::Group {
- primitives: vec![border, input, contents],
+ primitives: vec![input, contents],
},
if is_mouse_over {
MouseCursor::Text
diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs
new file mode 100644
index 00000000..dbe81830
--- /dev/null
+++ b/wgpu/src/settings.rs
@@ -0,0 +1,10 @@
+/// The settings of a [`Renderer`].
+///
+/// [`Renderer`]: struct.Renderer.html
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub struct Settings {
+ /// The bytes of the font that will be used by default.
+ ///
+ /// If `None` is provided, a default system font will be chosen.
+ pub default_font: Option<&'static [u8]>,
+}
diff --git a/wgpu/src/shader/quad.frag b/wgpu/src/shader/quad.frag
index 2ee77e71..ad1af1ad 100644
--- a/wgpu/src/shader/quad.frag
+++ b/wgpu/src/shader/quad.frag
@@ -1,14 +1,17 @@
#version 450
layout(location = 0) in vec4 v_Color;
-layout(location = 1) in vec2 v_Pos;
-layout(location = 2) in vec2 v_Scale;
-layout(location = 3) in float v_BorderRadius;
+layout(location = 1) in vec4 v_BorderColor;
+layout(location = 2) in vec2 v_Pos;
+layout(location = 3) in vec2 v_Scale;
+layout(location = 4) in float v_BorderRadius;
+layout(location = 5) in float v_BorderWidth;
layout(location = 0) out vec4 o_Color;
-float rounded(in vec2 frag_coord, in vec2 position, in vec2 size, float radius, float s)
+float distance(in vec2 frag_coord, in vec2 position, in vec2 size, float radius)
{
+ // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN
vec2 inner_size = size - vec2(radius, radius) * 2.0;
vec2 top_left = position + vec2(radius, radius);
vec2 bottom_right = top_left + inner_size;
@@ -21,13 +24,43 @@ float rounded(in vec2 frag_coord, in vec2 position, in vec2 size, float radius,
max(max(top_left_distance.y, bottom_right_distance.y), 0)
);
- float d = sqrt(distance.x * distance.x + distance.y * distance.y);
-
- return 1.0 - smoothstep(radius - s, radius + s, d);
+ return sqrt(distance.x * distance.x + distance.y * distance.y);
}
void main() {
- float radius_alpha = rounded(gl_FragCoord.xy, v_Pos, v_Scale, v_BorderRadius, 0.5);
+ vec4 mixed_color;
+
+ // TODO: Remove branching (?)
+ if(v_BorderWidth > 0) {
+ float internal_border = max(v_BorderRadius - v_BorderWidth, 0);
+
+ float internal_distance = distance(
+ gl_FragCoord.xy,
+ v_Pos + vec2(v_BorderWidth),
+ v_Scale - vec2(v_BorderWidth * 2.0),
+ internal_border
+ );
+
+ float border_mix = smoothstep(
+ max(internal_border - 0.5, 0.0),
+ internal_border + 0.5,
+ internal_distance
+ );
+
+ mixed_color = mix(v_Color, v_BorderColor, border_mix);
+ } else {
+ mixed_color = v_Color;
+ }
+
+ float d = distance(
+ gl_FragCoord.xy,
+ v_Pos,
+ v_Scale,
+ v_BorderRadius
+ );
+
+ float radius_alpha =
+ 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0), v_BorderRadius + 0.5, d);
- o_Color = vec4(v_Color.xyz, v_Color.w * radius_alpha);
+ o_Color = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
}
diff --git a/wgpu/src/shader/quad.frag.spv b/wgpu/src/shader/quad.frag.spv
index 17bd8f46..519f5f01 100644
--- a/wgpu/src/shader/quad.frag.spv
+++ b/wgpu/src/shader/quad.frag.spv
Binary files differ
diff --git a/wgpu/src/shader/quad.vert b/wgpu/src/shader/quad.vert
index 539755cb..1d9a4fd2 100644
--- a/wgpu/src/shader/quad.vert
+++ b/wgpu/src/shader/quad.vert
@@ -4,7 +4,9 @@ layout(location = 0) in vec2 v_Pos;
layout(location = 1) in vec2 i_Pos;
layout(location = 2) in vec2 i_Scale;
layout(location = 3) in vec4 i_Color;
-layout(location = 4) in float i_BorderRadius;
+layout(location = 4) in vec4 i_BorderColor;
+layout(location = 5) in float i_BorderRadius;
+layout(location = 6) in float i_BorderWidth;
layout (set = 0, binding = 0) uniform Globals {
mat4 u_Transform;
@@ -12,9 +14,11 @@ layout (set = 0, binding = 0) uniform Globals {
};
layout(location = 0) out vec4 o_Color;
-layout(location = 1) out vec2 o_Pos;
-layout(location = 2) out vec2 o_Scale;
-layout(location = 3) out float o_BorderRadius;
+layout(location = 1) out vec4 o_BorderColor;
+layout(location = 2) out vec2 o_Pos;
+layout(location = 3) out vec2 o_Scale;
+layout(location = 4) out float o_BorderRadius;
+layout(location = 5) out float o_BorderWidth;
void main() {
vec2 p_Pos = i_Pos * u_Scale;
@@ -28,9 +32,11 @@ void main() {
);
o_Color = i_Color;
+ o_BorderColor = i_BorderColor;
o_Pos = p_Pos;
o_Scale = p_Scale;
o_BorderRadius = i_BorderRadius * u_Scale;
+ o_BorderWidth = i_BorderWidth * u_Scale;
gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0);
}
diff --git a/wgpu/src/shader/quad.vert.spv b/wgpu/src/shader/quad.vert.spv
index 9050adfb..7059b51b 100644
--- a/wgpu/src/shader/quad.vert.spv
+++ b/wgpu/src/shader/quad.vert.spv
Binary files differ
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 880ad1a6..ab9a2f71 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -22,13 +22,16 @@ pub struct Pipeline {
}
impl Pipeline {
- pub fn new(device: &mut wgpu::Device) -> Self {
+ pub fn new(device: &mut wgpu::Device, default_font: Option<&[u8]>) -> Self {
// TODO: Font customization
let font_source = font::Source::new();
- let default_font = font_source
- .load(&[font::Family::SansSerif, font::Family::Serif])
- .unwrap_or_else(|_| FALLBACK_FONT.to_vec());
+ let default_font =
+ default_font.map(|slice| slice.to_vec()).unwrap_or_else(|| {
+ font_source
+ .load(&[font::Family::SansSerif, font::Family::Serif])
+ .unwrap_or_else(|_| FALLBACK_FONT.to_vec())
+ });
let load_glyph_brush = |font: Vec<u8>| {
let builder =
diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs
new file mode 100644
index 00000000..e3edda0b
--- /dev/null
+++ b/wgpu/src/widget.rs
@@ -0,0 +1,34 @@
+//! Use the widgets supported out-of-the-box.
+//!
+//! # Re-exports
+//! For convenience, the contents of this module are available at the root
+//! module. Therefore, you can directly type:
+//!
+//! ```
+//! use iced_wgpu::{button, Button};
+//! ```
+pub mod button;
+pub mod checkbox;
+pub mod container;
+pub mod progress_bar;
+pub mod radio;
+pub mod scrollable;
+pub mod slider;
+pub mod text_input;
+
+#[doc(no_inline)]
+pub use button::Button;
+#[doc(no_inline)]
+pub use checkbox::Checkbox;
+#[doc(no_inline)]
+pub use container::Container;
+#[doc(no_inline)]
+pub use progress_bar::ProgressBar;
+#[doc(no_inline)]
+pub use radio::Radio;
+#[doc(no_inline)]
+pub use scrollable::Scrollable;
+#[doc(no_inline)]
+pub use slider::Slider;
+#[doc(no_inline)]
+pub use text_input::TextInput;
diff --git a/wgpu/src/widget/button.rs b/wgpu/src/widget/button.rs
new file mode 100644
index 00000000..b738c55e
--- /dev/null
+++ b/wgpu/src/widget/button.rs
@@ -0,0 +1,15 @@
+//! Allow your users to perform actions by pressing a button.
+//!
+//! A [`Button`] has some local [`State`].
+//!
+//! [`Button`]: type.Button.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_native::button::State;
+pub use iced_style::button::{Style, StyleSheet};
+
+/// A widget that produces a message when clicked.
+///
+/// This is an alias of an `iced_native` button with an `iced_wgpu::Renderer`.
+pub type Button<'a, Message> = iced_native::Button<'a, Message, Renderer>;
diff --git a/wgpu/src/widget/checkbox.rs b/wgpu/src/widget/checkbox.rs
new file mode 100644
index 00000000..da0d7a84
--- /dev/null
+++ b/wgpu/src/widget/checkbox.rs
@@ -0,0 +1,9 @@
+//! Show toggle controls using checkboxes.
+use crate::Renderer;
+
+pub use iced_style::checkbox::{Style, StyleSheet};
+
+/// A box that can be checked.
+///
+/// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`.
+pub type Checkbox<Message> = iced_native::Checkbox<Message, Renderer>;
diff --git a/wgpu/src/widget/container.rs b/wgpu/src/widget/container.rs
new file mode 100644
index 00000000..9a93a246
--- /dev/null
+++ b/wgpu/src/widget/container.rs
@@ -0,0 +1,10 @@
+//! Decorate content and apply alignment.
+use crate::Renderer;
+
+pub use iced_style::container::{Style, StyleSheet};
+
+/// An element decorating some content.
+///
+/// This is an alias of an `iced_native` container with a default
+/// `Renderer`.
+pub type Container<'a, Message> = iced_native::Container<'a, Message, Renderer>;
diff --git a/wgpu/src/widget/progress_bar.rs b/wgpu/src/widget/progress_bar.rs
new file mode 100644
index 00000000..34450b5e
--- /dev/null
+++ b/wgpu/src/widget/progress_bar.rs
@@ -0,0 +1,15 @@
+//! Allow your users to perform actions by pressing a button.
+//!
+//! A [`Button`] has some local [`State`].
+//!
+//! [`Button`]: type.Button.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_style::progress_bar::{Style, StyleSheet};
+
+/// A bar that displays progress.
+///
+/// This is an alias of an `iced_native` progress bar with an
+/// `iced_wgpu::Renderer`.
+pub type ProgressBar = iced_native::ProgressBar<Renderer>;
diff --git a/wgpu/src/widget/radio.rs b/wgpu/src/widget/radio.rs
new file mode 100644
index 00000000..6e5cf042
--- /dev/null
+++ b/wgpu/src/widget/radio.rs
@@ -0,0 +1,10 @@
+//! Create choices using radio buttons.
+use crate::Renderer;
+
+pub use iced_style::radio::{Style, StyleSheet};
+
+/// A circular button representing a choice.
+///
+/// This is an alias of an `iced_native` radio button with an
+/// `iced_wgpu::Renderer`.
+pub type Radio<Message> = iced_native::Radio<Message, Renderer>;
diff --git a/wgpu/src/widget/scrollable.rs b/wgpu/src/widget/scrollable.rs
new file mode 100644
index 00000000..1d236105
--- /dev/null
+++ b/wgpu/src/widget/scrollable.rs
@@ -0,0 +1,13 @@
+//! Navigate an endless amount of content with a scrollbar.
+use crate::Renderer;
+
+pub use iced_native::scrollable::State;
+pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet};
+
+/// A widget that can vertically display an infinite amount of content
+/// with a scrollbar.
+///
+/// This is an alias of an `iced_native` scrollable with a default
+/// `Renderer`.
+pub type Scrollable<'a, Message> =
+ iced_native::Scrollable<'a, Message, Renderer>;
diff --git a/wgpu/src/widget/slider.rs b/wgpu/src/widget/slider.rs
new file mode 100644
index 00000000..4e47978f
--- /dev/null
+++ b/wgpu/src/widget/slider.rs
@@ -0,0 +1,16 @@
+//! Display an interactive selector of a single value from a range of values.
+//!
+//! A [`Slider`] has some local [`State`].
+//!
+//! [`Slider`]: struct.Slider.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_native::slider::State;
+pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet};
+
+/// An horizontal bar and a handle that selects a single value from a range of
+/// values.
+///
+/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`.
+pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Renderer>;
diff --git a/wgpu/src/widget/text_input.rs b/wgpu/src/widget/text_input.rs
new file mode 100644
index 00000000..260fe3a6
--- /dev/null
+++ b/wgpu/src/widget/text_input.rs
@@ -0,0 +1,15 @@
+//! Display fields that can be filled with text.
+//!
+//! A [`TextInput`] has some local [`State`].
+//!
+//! [`TextInput`]: struct.TextInput.html
+//! [`State`]: struct.State.html
+use crate::Renderer;
+
+pub use iced_native::text_input::State;
+pub use iced_style::text_input::{Style, StyleSheet};
+
+/// A field that can be filled with text.
+///
+/// This is an alias of an `iced_native` text input with an `iced_wgpu::Renderer`.
+pub type TextInput<'a, Message> = iced_native::TextInput<'a, Message, Renderer>;
diff --git a/winit/Cargo.toml b/winit/Cargo.toml
index 60e3f2d0..5727f8cf 100644
--- a/winit/Cargo.toml
+++ b/winit/Cargo.toml
@@ -6,6 +6,9 @@ edition = "2018"
description = "A winit runtime for Iced"
license = "MIT"
repository = "https://github.com/hecrj/iced"
+documentation = "https://docs.rs/iced_winit"
+keywords = ["gui", "ui", "graphics", "interface", "widgets"]
+categories = ["gui"]
[features]
debug = []
diff --git a/winit/src/application.rs b/winit/src/application.rs
index a8612b1a..da943660 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -1,5 +1,5 @@
use crate::{
- conversion,
+ container, conversion,
input::{keyboard, mouse},
renderer::{Target, Windowed},
subscription, Cache, Clipboard, Command, Container, Debug, Element, Event,
@@ -18,7 +18,7 @@ pub trait Application: Sized {
/// The renderer to use to draw the [`Application`].
///
/// [`Application`]: trait.Application.html
- type Renderer: Windowed;
+ type Renderer: Windowed + container::Renderer;
/// The type of __messages__ your [`Application`] will produce.
///
@@ -81,8 +81,10 @@ pub trait Application: Sized {
/// It should probably be that last thing you call in your `main` function.
///
/// [`Application`]: trait.Application.html
- fn run(settings: Settings)
- where
+ fn run(
+ settings: Settings,
+ renderer_settings: <Self::Renderer as Windowed>::Settings,
+ ) where
Self: 'static,
{
use winit::{
@@ -140,7 +142,7 @@ pub trait Application: Sized {
let mut resized = false;
let clipboard = Clipboard::new(&window);
- let mut renderer = Self::Renderer::new();
+ let mut renderer = Self::Renderer::new(renderer_settings);
let mut target = {
let (width, height) = to_physical(size, dpi);
diff --git a/winit/src/settings/mod.rs b/winit/src/settings/mod.rs
index 58e3d879..b2290b46 100644
--- a/winit/src/settings/mod.rs
+++ b/winit/src/settings/mod.rs
@@ -1,5 +1,4 @@
//! Configure your application.
-
#[cfg(target_os = "windows")]
#[path = "windows.rs"]
mod platform;
@@ -10,7 +9,7 @@ mod platform;
pub use platform::PlatformSpecific;
/// The settings of an application.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Settings {
/// The [`Window`] settings
///
@@ -18,6 +17,14 @@ pub struct Settings {
pub window: Window,
}
+impl Default for Settings {
+ fn default() -> Settings {
+ Settings {
+ window: Window::default(),
+ }
+ }
+}
+
/// The window settings of an application.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Window {