Merge pull request #146 from hecrj/feature/custom-styling

Custom styling
This commit is contained in:
Héctor Ramón 2020-01-09 18:46:06 +01:00 committed by GitHub
commit 0a83024505
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 2293 additions and 600 deletions

View file

@ -24,6 +24,7 @@ maintenance = { status = "actively-developed" }
members = [ members = [
"core", "core",
"native", "native",
"style",
"web", "web",
"wgpu", "wgpu",
"winit", "winit",

View file

@ -25,6 +25,21 @@ impl Color {
a: 1.0, 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. /// Creates a [`Color`] from its RGB8 components.
/// ///
/// [`Color`]: struct.Color.html /// [`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. /// Converts the [`Color`] into its linear values.
/// ///
/// [`Color`]: struct.Color.html /// [`Color`]: struct.Color.html

View file

@ -31,3 +31,15 @@ where
Self::new(self.x + b.x, self.y + b.y) 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(),
}
}
}

View file

@ -13,7 +13,7 @@ mod circle {
layout, Background, Color, Element, Hasher, Layout, Length, layout, Background, Color, Element, Hasher, Layout, Length,
MouseCursor, Point, Size, Widget, MouseCursor, Point, Size, Widget,
}; };
use iced_wgpu::{Primitive, Renderer}; use iced_wgpu::{Defaults, Primitive, Renderer};
pub struct Circle { pub struct Circle {
radius: u16, radius: u16,
@ -54,6 +54,7 @@ mod circle {
fn draw( fn draw(
&self, &self,
_renderer: &mut Renderer, _renderer: &mut Renderer,
_defaults: &Defaults,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
) -> (Primitive, MouseCursor) { ) -> (Primitive, MouseCursor) {
@ -62,6 +63,8 @@ mod circle {
bounds: layout.bounds(), bounds: layout.bounds(),
background: Background::Color(Color::BLACK), background: Background::Color(Color::BLACK),
border_radius: self.radius, border_radius: self.radius,
border_width: 0,
border_color: Color::TRANSPARENT,
}, },
MouseCursor::OutOfBounds, MouseCursor::OutOfBounds,
) )

View file

@ -16,7 +16,7 @@ mod rainbow {
}; };
use iced_wgpu::{ use iced_wgpu::{
triangle::{Mesh2D, Vertex2D}, triangle::{Mesh2D, Vertex2D},
Primitive, Renderer, Defaults, Primitive, Renderer,
}; };
pub struct Rainbow; pub struct Rainbow;
@ -51,6 +51,7 @@ mod rainbow {
fn draw( fn draw(
&self, &self,
_renderer: &mut Renderer, _renderer: &mut Renderer,
_defaults: &Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> (Primitive, MouseCursor) { ) -> (Primitive, MouseCursor) {

View file

@ -1,6 +1,6 @@
use iced::{ use iced::{
button, image, Align, Application, Button, Color, Column, Command, button, image, Align, Application, Button, Column, Command, Container,
Container, Element, Image, Length, Row, Settings, Text, Element, Image, Length, Row, Settings, Text,
}; };
pub fn main() { 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> { fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> {
Button::new(state, Text::new(text).color(Color::WHITE)) Button::new(state, Text::new(text))
.background(Color::from_rgb(0.11, 0.42, 0.87))
.border_radius(10)
.padding(10) .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()
}
}
}
} }

View file

@ -1,16 +1,7 @@
use iced::{ use iced::{slider, Column, Element, ProgressBar, Sandbox, Settings, Slider};
settings::Window, slider, Background, Color, Column, Element, Length,
ProgressBar, Sandbox, Settings, Slider,
};
pub fn main() { pub fn main() {
Progress::run(Settings { Progress::run(Settings::default())
window: Window {
size: (700, 300),
resizable: true,
decorations: true,
},
})
} }
#[derive(Default)] #[derive(Default)]
@ -44,14 +35,7 @@ impl Sandbox for Progress {
fn view(&mut self) -> Element<Message> { fn view(&mut self) -> Element<Message> {
Column::new() Column::new()
.padding(20) .padding(20)
.push( .push(ProgressBar::new(0.0..=100.0, self.value))
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(Slider::new( .push(Slider::new(
&mut self.progress_bar_slider, &mut self.progress_bar_slider,
0.0..=100.0, 0.0..=100.0,

View file

@ -1,7 +1,6 @@
use iced::{ use iced::{
button, Align, Application, Background, Button, Color, Column, Command, button, Align, Application, Button, Column, Command, Container, Element,
Container, Element, HorizontalAlignment, Length, Row, Settings, HorizontalAlignment, Length, Row, Settings, Subscription, Text,
Subscription, Text,
}; };
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -98,30 +97,29 @@ impl Application for Stopwatch {
)) ))
.size(40); .size(40);
let button = |state, label, color: [f32; 3]| { let button = |state, label, style| {
Button::new( Button::new(
state, state,
Text::new(label) Text::new(label)
.color(Color::WHITE)
.horizontal_alignment(HorizontalAlignment::Center), .horizontal_alignment(HorizontalAlignment::Center),
) )
.min_width(80) .min_width(80)
.background(Background::Color(color.into()))
.border_radius(10)
.padding(10) .padding(10)
.style(style)
}; };
let toggle_button = { let toggle_button = {
let (label, color) = match self.state { let (label, color) = match self.state {
State::Idle => ("Start", [0.11, 0.42, 0.87]), State::Idle => ("Start", style::Button::Primary),
State::Ticking { .. } => ("Stop", [0.9, 0.4, 0.4]), State::Ticking { .. } => ("Stop", style::Button::Destructive),
}; };
button(&mut self.toggle, label, color).on_press(Message::Toggle) button(&mut self.toggle, label, color).on_press(Message::Toggle)
}; };
let reset_button = button(&mut self.reset, "Reset", [0.7, 0.7, 0.7]) let reset_button =
.on_press(Message::Reset); button(&mut self.reset, "Reset", style::Button::Secondary)
.on_press(Message::Reset);
let controls = Row::new() let controls = Row::new()
.spacing(20) .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()
}
}
}
}

514
examples/styling.rs Normal file
View file

@ -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)
}
}
}
}
}

View file

@ -1,7 +1,7 @@
use iced::{ use iced::{
button, scrollable, text_input, Align, Application, Button, Checkbox, button, scrollable, text_input, Align, Application, Button, Checkbox,
Color, Column, Command, Container, Element, Font, HorizontalAlignment, Column, Command, Container, Element, Font, HorizontalAlignment, Length,
Length, Row, Scrollable, Settings, Text, TextInput, Row, Scrollable, Settings, Text, TextInput,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -293,12 +293,10 @@ impl Task {
.align_items(Align::Center) .align_items(Align::Center)
.push(checkbox) .push(checkbox)
.push( .push(
Button::new( Button::new(edit_button, edit_icon())
edit_button, .on_press(TaskMessage::Edit)
edit_icon().color([0.5, 0.5, 0.5]), .padding(10)
) .style(style::Button::Icon),
.on_press(TaskMessage::Edit)
.padding(10),
) )
.into() .into()
} }
@ -324,13 +322,12 @@ impl Task {
delete_button, delete_button,
Row::new() Row::new()
.spacing(10) .spacing(10)
.push(delete_icon().color(Color::WHITE)) .push(delete_icon())
.push(Text::new("Delete").color(Color::WHITE)), .push(Text::new("Delete")),
) )
.on_press(TaskMessage::Delete) .on_press(TaskMessage::Delete)
.padding(10) .padding(10)
.border_radius(5) .style(style::Button::Destructive),
.background(Color::from_rgb(0.8, 0.2, 0.2)),
) )
.into() .into()
} }
@ -357,17 +354,12 @@ impl Controls {
let filter_button = |state, label, filter, current_filter| { let filter_button = |state, label, filter, current_filter| {
let label = Text::new(label).size(16); let label = Text::new(label).size(16);
let button = if filter == current_filter { let button =
Button::new(state, label.color(Color::WHITE)) Button::new(state, label).style(style::Button::Filter {
.background(Color::from_rgb(0.2, 0.2, 0.7)) selected: filter == current_filter,
} else { });
Button::new(state, label)
};
button button.on_press(Message::FilterChanged(filter)).padding(8)
.on_press(Message::FilterChanged(filter))
.padding(8)
.border_radius(10)
}; };
Row::new() Row::new()
@ -560,3 +552,63 @@ impl SavedState {
Ok(()) 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
}
}
}
}

View file

@ -62,8 +62,9 @@ impl Sandbox for Tour {
if steps.has_previous() { if steps.has_previous() {
controls = controls.push( controls = controls.push(
secondary_button(back_button, "Back") button(back_button, "Back")
.on_press(Message::BackPressed), .on_press(Message::BackPressed)
.style(style::Button::Secondary),
); );
} }
@ -71,8 +72,9 @@ impl Sandbox for Tour {
if steps.can_continue() { if steps.can_continue() {
controls = controls.push( controls = controls.push(
primary_button(next_button, "Next") button(next_button, "Next")
.on_press(Message::NextPressed), .on_press(Message::NextPressed)
.style(style::Button::Primary),
); );
} }
@ -698,29 +700,12 @@ fn button<'a, Message>(
) -> Button<'a, Message> { ) -> Button<'a, Message> {
Button::new( Button::new(
state, state,
Text::new(label) Text::new(label).horizontal_alignment(HorizontalAlignment::Center),
.color(Color::WHITE)
.horizontal_alignment(HorizontalAlignment::Center),
) )
.padding(12) .padding(12)
.border_radius(12)
.min_width(100) .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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language { pub enum Language {
Rust, Rust,
@ -763,6 +748,38 @@ pub enum Layout {
Column, 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 // 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 // own proc macro, or maybe the whole process is streamlined by `wasm-pack` at
// some point. // some point.

View file

@ -235,10 +235,12 @@ where
pub fn draw( pub fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> 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) { pub(crate) fn hash_layout(&self, state: &mut Hasher) {
@ -316,10 +318,12 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
self.widget.draw(renderer, layout, cursor_position) self.widget
.draw(renderer, defaults, layout, cursor_position)
} }
fn hash_layout(&self, state: &mut Hasher) { fn hash_layout(&self, state: &mut Hasher) {
@ -384,10 +388,12 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
renderer.explain( renderer.explain(
defaults,
self.element.widget.as_ref(), self.element.widget.as_ref(),
layout, layout,
cursor_position, cursor_position,

View file

@ -21,14 +21,15 @@
//! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html //! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html
mod debugger; mod debugger;
#[cfg(debug_assertions)]
mod null;
mod windowed; mod windowed;
pub use debugger::Debugger; pub use debugger::Debugger;
pub use windowed::{Target, Windowed};
#[cfg(debug_assertions)]
mod null;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub use null::Null; pub use null::Null;
pub use windowed::{Target, Windowed};
use crate::{layout, Element}; use crate::{layout, Element};
@ -43,6 +44,13 @@ pub trait Renderer: Sized {
/// [`Renderer`]: trait.Renderer.html /// [`Renderer`]: trait.Renderer.html
type Output; 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. /// Lays out the elements of a user interface.
/// ///
/// You should override this if you need to perform any operations before or /// You should override this if you need to perform any operations before or

View file

@ -17,6 +17,7 @@ pub trait Debugger: super::Renderer {
/// [`Element::explain`]: struct.Element.html#method.explain /// [`Element::explain`]: struct.Element.html#method.explain
fn explain<Message>( fn explain<Message>(
&mut self, &mut self,
defaults: &Self::Defaults,
widget: &dyn Widget<Message, Self>, widget: &dyn Widget<Message, Self>,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,

View file

@ -1,20 +1,33 @@
use crate::{ use crate::{
button, checkbox, column, radio, row, scrollable, text, text_input, button, checkbox, column, progress_bar, radio, row, scrollable, slider,
Background, Color, Element, Font, HorizontalAlignment, Layout, Point, text, text_input, Color, Element, Font, HorizontalAlignment, Layout, Point,
Rectangle, Renderer, Size, VerticalAlignment, Rectangle, Renderer, Size, VerticalAlignment,
}; };
/// A renderer that does nothing. /// A renderer that does nothing.
///
/// It can be useful if you are writing tests!
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Null; pub struct Null;
impl Null {
/// Creates a new [`Null`] renderer.
///
/// [`Null`]: struct.Null.html
pub fn new() -> Null {
Null
}
}
impl Renderer for Null { impl Renderer for Null {
type Output = (); type Output = ();
type Defaults = ();
} }
impl column::Renderer for Null { impl column::Renderer for Null {
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
_defaults: &Self::Defaults,
_content: &[Element<'_, Message, Self>], _content: &[Element<'_, Message, Self>],
_layout: Layout<'_>, _layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
@ -25,6 +38,7 @@ impl column::Renderer for Null {
impl row::Renderer for Null { impl row::Renderer for Null {
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
_defaults: &Self::Defaults,
_content: &[Element<'_, Message, Self>], _content: &[Element<'_, Message, Self>],
_layout: Layout<'_>, _layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
@ -49,6 +63,7 @@ impl text::Renderer for Null {
fn draw( fn draw(
&mut self, &mut self,
_defaults: &Self::Defaults,
_bounds: Rectangle, _bounds: Rectangle,
_content: &str, _content: &str,
_size: u16, _size: u16,
@ -61,6 +76,8 @@ impl text::Renderer for Null {
} }
impl scrollable::Renderer for Null { impl scrollable::Renderer for Null {
type Style = ();
fn scrollbar( fn scrollbar(
&self, &self,
_bounds: Rectangle, _bounds: Rectangle,
@ -79,12 +96,15 @@ impl scrollable::Renderer for Null {
_is_mouse_over_scrollbar: bool, _is_mouse_over_scrollbar: bool,
_scrollbar: Option<scrollable::Scrollbar>, _scrollbar: Option<scrollable::Scrollbar>,
_offset: u32, _offset: u32,
_style: &Self::Style,
_content: Self::Output, _content: Self::Output,
) { ) {
} }
} }
impl text_input::Renderer for Null { impl text_input::Renderer for Null {
type Style = ();
fn default_size(&self) -> u16 { fn default_size(&self) -> u16 {
20 20
} }
@ -112,24 +132,31 @@ impl text_input::Renderer for Null {
_placeholder: &str, _placeholder: &str,
_value: &text_input::Value, _value: &text_input::Value,
_state: &text_input::State, _state: &text_input::State,
_style: &Self::Style,
) -> Self::Output { ) -> Self::Output {
} }
} }
impl button::Renderer for Null { impl button::Renderer for Null {
fn draw( type Style = ();
fn draw<Message>(
&mut self, &mut self,
_defaults: &Self::Defaults,
_bounds: Rectangle, _bounds: Rectangle,
_cursor_position: Point, _cursor_position: Point,
_is_disabled: bool,
_is_pressed: bool, _is_pressed: bool,
_background: Option<Background>, _style: &Self::Style,
_border_radius: u16, _content: &Element<'_, Message, Self>,
_content: Self::Output, _content_layout: Layout<'_>,
) -> Self::Output { ) -> Self::Output {
} }
} }
impl radio::Renderer for Null { impl radio::Renderer for Null {
type Style = ();
fn default_size(&self) -> u32 { fn default_size(&self) -> u32 {
20 20
} }
@ -140,11 +167,14 @@ impl radio::Renderer for Null {
_is_selected: bool, _is_selected: bool,
_is_mouse_over: bool, _is_mouse_over: bool,
_label: Self::Output, _label: Self::Output,
_style: &Self::Style,
) { ) {
} }
} }
impl checkbox::Renderer for Null { impl checkbox::Renderer for Null {
type Style = ();
fn default_size(&self) -> u32 { fn default_size(&self) -> u32 {
20 20
} }
@ -155,6 +185,41 @@ impl checkbox::Renderer for Null {
_is_checked: bool, _is_checked: bool,
_is_mouse_over: bool, _is_mouse_over: bool,
_label: Self::Output, _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,
) { ) {
} }
} }

View file

@ -4,13 +4,16 @@ use raw_window_handle::HasRawWindowHandle;
/// A renderer that can target windows. /// A renderer that can target windows.
pub trait Windowed: super::Renderer + Sized { pub trait Windowed: super::Renderer + Sized {
/// The settings of the renderer.
type Settings: Default;
/// The type of target. /// The type of target.
type Target: Target<Renderer = Self>; type Target: Target<Renderer = Self>;
/// Creates a new [`Windowed`] renderer. /// Creates a new [`Windowed`] renderer.
/// ///
/// [`Windowed`]: trait.Windowed.html /// [`Windowed`]: trait.Windowed.html
fn new() -> Self; fn new(settings: Self::Settings) -> Self;
/// Performs the drawing operations described in the output on the given /// Performs the drawing operations described in the output on the given
/// target. /// target.

View file

@ -43,24 +43,7 @@ where
/// use iced_wgpu::Renderer; /// use iced_wgpu::Renderer;
/// ///
/// # mod iced_wgpu { /// # mod iced_wgpu {
/// # pub struct Renderer; /// # pub use iced_native::renderer::Null as 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 {
/// # ()
/// # }
/// # }
/// # } /// # }
/// # /// #
/// # use iced_native::Column; /// # use iced_native::Column;
@ -139,24 +122,7 @@ where
/// use iced_wgpu::Renderer; /// use iced_wgpu::Renderer;
/// ///
/// # mod iced_wgpu { /// # mod iced_wgpu {
/// # pub struct Renderer; /// # pub use iced_native::renderer::Null as 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 {
/// # ()
/// # }
/// # }
/// # } /// # }
/// # /// #
/// # use iced_native::Column; /// # use iced_native::Column;
@ -241,24 +207,7 @@ where
/// use iced_wgpu::Renderer; /// use iced_wgpu::Renderer;
/// ///
/// # mod iced_wgpu { /// # mod iced_wgpu {
/// # pub struct Renderer; /// # pub use iced_native::renderer::Null as 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 {
/// # ()
/// # }
/// # }
/// # } /// # }
/// # /// #
/// # use iced_native::Column; /// # use iced_native::Column;
@ -304,6 +253,7 @@ where
pub fn draw(&self, renderer: &mut Renderer) -> Renderer::Output { pub fn draw(&self, renderer: &mut Renderer) -> Renderer::Output {
self.root.widget.draw( self.root.widget.draw(
renderer, renderer,
&Renderer::Defaults::default(),
Layout::new(&self.layout), Layout::new(&self.layout),
self.cursor_position, self.cursor_position,
) )

View file

@ -107,6 +107,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output; ) -> Renderer::Output;

View file

@ -6,8 +6,8 @@
//! [`State`]: struct.State.html //! [`State`]: struct.State.html
use crate::{ use crate::{
input::{mouse, ButtonState}, input::{mouse, ButtonState},
layout, Background, Clipboard, Element, Event, Hasher, Layout, Length, layout, Clipboard, Element, Event, Hasher, Layout, Length, Point,
Point, Rectangle, Widget, Rectangle, Widget,
}; };
use std::hash::Hash; use std::hash::Hash;
@ -28,7 +28,7 @@ use std::hash::Hash;
/// .on_press(Message::ButtonPressed); /// .on_press(Message::ButtonPressed);
/// ``` /// ```
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Button<'a, Message, Renderer> { pub struct Button<'a, Message, Renderer: self::Renderer> {
state: &'a mut State, state: &'a mut State,
content: Element<'a, Message, Renderer>, content: Element<'a, Message, Renderer>,
on_press: Option<Message>, on_press: Option<Message>,
@ -37,11 +37,13 @@ pub struct Button<'a, Message, Renderer> {
min_width: u32, min_width: u32,
min_height: u32, min_height: u32,
padding: u16, padding: u16,
background: Option<Background>, style: Renderer::Style,
border_radius: u16,
} }
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 /// Creates a new [`Button`] with some local [`State`] and the given
/// content. /// content.
/// ///
@ -60,8 +62,7 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
min_width: 0, min_width: 0,
min_height: 0, min_height: 0,
padding: 0, padding: 0,
background: None, style: Renderer::Style::default(),
border_radius: 0,
} }
} }
@ -105,23 +106,6 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
self 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`].
///
/// [`Button`]: struct.Button.html
pub fn border_radius(mut self, border_radius: u16) -> Self {
self.border_radius = border_radius;
self
}
/// Sets the message that will be produced when the [`Button`] is pressed. /// Sets the message that will be produced when the [`Button`] is pressed.
/// ///
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
@ -129,6 +113,14 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> {
self.on_press = Some(msg); self.on_press = Some(msg);
self self
} }
/// Sets the style of the [`Button`].
///
/// [`Button`]: struct.Button.html
pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
self.style = style.into();
self
}
} }
/// The local state of a [`Button`]. /// The local state of a [`Button`].
@ -227,22 +219,19 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
let content = self.content.draw(
renderer,
layout.children().next().unwrap(),
cursor_position,
);
renderer.draw( renderer.draw(
defaults,
layout.bounds(), layout.bounds(),
cursor_position, cursor_position,
self.on_press.is_none(),
self.state.is_pressed, self.state.is_pressed,
self.background, &self.style,
self.border_radius, &self.content,
content, layout.children().next().unwrap(),
) )
} }
@ -260,17 +249,22 @@ where
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized { pub trait Renderer: crate::Renderer + Sized {
/// The style supported by this renderer.
type Style: Default;
/// Draws a [`Button`]. /// Draws a [`Button`].
/// ///
/// [`Button`]: struct.Button.html /// [`Button`]: struct.Button.html
fn draw( fn draw<Message>(
&mut self, &mut self,
defaults: &Self::Defaults,
bounds: Rectangle, bounds: Rectangle,
cursor_position: Point, cursor_position: Point,
is_disabled: bool,
is_pressed: bool, is_pressed: bool,
background: Option<Background>, style: &Self::Style,
border_radius: u16, content: &Element<'_, Message, Self>,
content: Self::Output, content_layout: Layout<'_>,
) -> Self::Output; ) -> Self::Output;
} }

View file

@ -3,7 +3,7 @@ use std::hash::Hash;
use crate::{ use crate::{
input::{mouse, ButtonState}, 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, HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
VerticalAlignment, Widget, VerticalAlignment, Widget,
}; };
@ -13,7 +13,7 @@ use crate::{
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use iced_native::Checkbox; /// # type Checkbox<Message> = iced_native::Checkbox<Message, iced_native::renderer::Null>;
/// # /// #
/// pub enum Message { /// pub enum Message {
/// CheckboxToggled(bool), /// 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) /// ![Checkbox drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true)
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Checkbox<Message> { pub struct Checkbox<Message, Renderer: self::Renderer> {
is_checked: bool, is_checked: bool,
on_toggle: Box<dyn Fn(bool) -> Message>, on_toggle: Box<dyn Fn(bool) -> Message>,
label: String, label: String,
label_color: Option<Color>,
width: Length, width: Length,
style: Renderer::Style,
} }
impl<Message> Checkbox<Message> { impl<Message, Renderer: self::Renderer> Checkbox<Message, Renderer> {
/// Creates a new [`Checkbox`]. /// Creates a new [`Checkbox`].
/// ///
/// It expects: /// It expects:
@ -53,19 +53,11 @@ impl<Message> Checkbox<Message> {
is_checked, is_checked,
on_toggle: Box::new(f), on_toggle: Box::new(f),
label: String::from(label), label: String::from(label),
label_color: None,
width: Length::Shrink, width: Length::Shrink,
style: Renderer::Style::default(),
} }
} }
/// Sets the color of the label 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());
self
}
/// Sets the width of the [`Checkbox`]. /// Sets the width of the [`Checkbox`].
/// ///
/// [`Checkbox`]: struct.Checkbox.html /// [`Checkbox`]: struct.Checkbox.html
@ -73,9 +65,18 @@ impl<Message> Checkbox<Message> {
self.width = width; self.width = width;
self self
} }
/// Sets the style of the [`Checkbox`].
///
/// [`Checkbox`]: struct.Checkbox.html
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 where
Renderer: self::Renderer + text::Renderer + row::Renderer, Renderer: self::Renderer + text::Renderer + row::Renderer,
{ {
@ -134,6 +135,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
@ -146,11 +148,12 @@ where
let label = text::Renderer::draw( let label = text::Renderer::draw(
renderer, renderer,
defaults,
label_layout.bounds(), label_layout.bounds(),
&self.label, &self.label,
text::Renderer::default_size(renderer), text::Renderer::default_size(renderer),
Font::Default, Font::Default,
self.label_color, None,
HorizontalAlignment::Left, HorizontalAlignment::Left,
VerticalAlignment::Center, VerticalAlignment::Center,
); );
@ -163,6 +166,7 @@ where
self.is_checked, self.is_checked,
is_mouse_over, is_mouse_over,
label, label,
&self.style,
) )
} }
@ -179,6 +183,9 @@ where
/// [`Checkbox`]: struct.Checkbox.html /// [`Checkbox`]: struct.Checkbox.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// The style supported by this renderer.
type Style: Default;
/// Returns the default size of a [`Checkbox`]. /// Returns the default size of a [`Checkbox`].
/// ///
/// [`Checkbox`]: struct.Checkbox.html /// [`Checkbox`]: struct.Checkbox.html
@ -199,16 +206,19 @@ pub trait Renderer: crate::Renderer {
is_checked: bool, is_checked: bool,
is_mouse_over: bool, is_mouse_over: bool,
label: Self::Output, label: Self::Output,
style: &Self::Style,
) -> Self::Output; ) -> Self::Output;
} }
impl<'a, Message, Renderer> From<Checkbox<Message>> impl<'a, Message, Renderer> From<Checkbox<Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: self::Renderer + text::Renderer + row::Renderer, Renderer: 'static + self::Renderer + text::Renderer + row::Renderer,
Message: 'static, Message: 'static,
{ {
fn from(checkbox: Checkbox<Message>) -> Element<'a, Message, Renderer> { fn from(
checkbox: Checkbox<Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(checkbox) Element::new(checkbox)
} }
} }

View file

@ -173,10 +173,11 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
renderer.draw(&self.children, layout, cursor_position) renderer.draw(defaults, &self.children, layout, cursor_position)
} }
fn hash_layout(&self, state: &mut Hasher) { fn hash_layout(&self, state: &mut Hasher) {
@ -213,6 +214,7 @@ pub trait Renderer: crate::Renderer + Sized {
/// [`Layout`]: ../layout/struct.Layout.html /// [`Layout`]: ../layout/struct.Layout.html
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
defaults: &Self::Defaults,
content: &[Element<'_, Message, Self>], content: &[Element<'_, Message, Self>],
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,

View file

@ -3,7 +3,7 @@ use std::hash::Hash;
use crate::{ use crate::{
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point,
Widget, Rectangle, Widget,
}; };
use std::u32; use std::u32;
@ -12,17 +12,21 @@ use std::u32;
/// ///
/// It is normally used for alignment purposes. /// It is normally used for alignment purposes.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Container<'a, Message, Renderer> { pub struct Container<'a, Message, Renderer: self::Renderer> {
width: Length, width: Length,
height: Length, height: Length,
max_width: u32, max_width: u32,
max_height: u32, max_height: u32,
horizontal_alignment: Align, horizontal_alignment: Align,
vertical_alignment: Align, vertical_alignment: Align,
style: Renderer::Style,
content: Element<'a, Message, Renderer>, 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`]. /// Creates an empty [`Container`].
/// ///
/// [`Container`]: struct.Container.html /// [`Container`]: struct.Container.html
@ -37,6 +41,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
max_height: u32::MAX, max_height: u32::MAX,
horizontal_alignment: Align::Start, horizontal_alignment: Align::Start,
vertical_alignment: Align::Start, vertical_alignment: Align::Start,
style: Renderer::Style::default(),
content: content.into(), content: content.into(),
} }
} }
@ -78,7 +83,6 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
/// [`Container`]: struct.Container.html /// [`Container`]: struct.Container.html
pub fn center_x(mut self) -> Self { pub fn center_x(mut self) -> Self {
self.horizontal_alignment = Align::Center; self.horizontal_alignment = Align::Center;
self self
} }
@ -87,7 +91,14 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
/// [`Container`]: struct.Container.html /// [`Container`]: struct.Container.html
pub fn center_y(mut self) -> Self { pub fn center_y(mut self) -> Self {
self.vertical_alignment = Align::Center; 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 self
} }
} }
@ -95,7 +106,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer> impl<'a, Message, Renderer> Widget<Message, Renderer>
for Container<'a, Message, Renderer> for Container<'a, Message, Renderer>
where where
Renderer: crate::Renderer, Renderer: self::Renderer,
{ {
fn width(&self) -> Length { fn width(&self) -> Length {
self.width self.width
@ -147,13 +158,17 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
self.content.draw( renderer.draw(
renderer, defaults,
layout.children().next().unwrap(), layout.bounds(),
cursor_position, 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>> impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: 'a + crate::Renderer, Renderer: 'a + self::Renderer,
Message: 'static, Message: 'static,
{ {
fn from( fn from(

View file

@ -95,6 +95,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {

View file

@ -1,7 +1,6 @@
//! Provide progress feedback to your users. //! Provide progress feedback to your users.
use crate::{ use crate::{
layout, Background, Color, Element, Hasher, Layout, Length, Point, layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
Rectangle, Size, Widget,
}; };
use std::{hash::Hash, ops::RangeInclusive}; use std::{hash::Hash, ops::RangeInclusive};
@ -10,8 +9,9 @@ use std::{hash::Hash, ops::RangeInclusive};
/// ///
/// # Example /// # Example
/// ``` /// ```
/// # use iced_native::ProgressBar; /// # use iced_native::renderer::Null;
/// # /// #
/// # pub type ProgressBar = iced_native::ProgressBar<Null>;
/// let value = 50.0; /// let value = 50.0;
/// ///
/// ProgressBar::new(0.0..=100.0, value); /// 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) /// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct ProgressBar { pub struct ProgressBar<Renderer: self::Renderer> {
range: RangeInclusive<f32>, range: RangeInclusive<f32>,
value: f32, value: f32,
width: Length, width: Length,
height: Option<Length>, height: Option<Length>,
background: Option<Background>, style: Renderer::Style,
active_color: Option<Color>,
} }
impl ProgressBar { impl<Renderer: self::Renderer> ProgressBar<Renderer> {
/// Creates a new [`ProgressBar`]. /// Creates a new [`ProgressBar`].
/// ///
/// It expects: /// It expects:
@ -42,8 +41,7 @@ impl ProgressBar {
range, range,
width: Length::Fill, width: Length::Fill,
height: None, height: None,
background: None, style: Renderer::Style::default(),
active_color: None,
} }
} }
@ -63,24 +61,16 @@ impl ProgressBar {
self self
} }
/// Sets the background of the [`ProgressBar`]. /// Sets the style of the [`ProgressBar`].
/// ///
/// [`ProgressBar`]: struct.ProgressBar.html /// [`ProgressBar`]: struct.ProgressBar.html
pub fn background(mut self, background: Background) -> Self { pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
self.background = Some(background); self.style = style.into();
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);
self self
} }
} }
impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar<Renderer>
where where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
@ -111,6 +101,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
@ -118,8 +109,7 @@ where
layout.bounds(), layout.bounds(),
self.range.clone(), self.range.clone(),
self.value, self.value,
self.background, &self.style,
self.active_color,
) )
} }
@ -137,6 +127,9 @@ where
/// [`ProgressBar`]: struct.ProgressBar.html /// [`ProgressBar`]: struct.ProgressBar.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// The style supported by this renderer.
type Style: Default;
/// The default height of a [`ProgressBar`]. /// The default height of a [`ProgressBar`].
/// ///
/// [`ProgressBar`]: struct.ProgressBar.html /// [`ProgressBar`]: struct.ProgressBar.html
@ -157,17 +150,19 @@ pub trait Renderer: crate::Renderer {
bounds: Rectangle, bounds: Rectangle,
range: RangeInclusive<f32>, range: RangeInclusive<f32>,
value: f32, value: f32,
background: Option<Background>, style: &Self::Style,
active_color: Option<Color>,
) -> Self::Output; ) -> 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 where
Renderer: self::Renderer, Renderer: 'static + self::Renderer,
Message: 'static, 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) Element::new(progress_bar)
} }
} }

View file

@ -1,7 +1,7 @@
//! Create choices using radio buttons. //! Create choices using radio buttons.
use crate::{ use crate::{
input::{mouse, ButtonState}, 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, HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
VerticalAlignment, Widget, VerticalAlignment, Widget,
}; };
@ -12,7 +12,8 @@ use std::hash::Hash;
/// ///
/// # Example /// # Example
/// ``` /// ```
/// # use iced_native::Radio; /// # type Radio<Message> =
/// # iced_native::Radio<Message, iced_native::renderer::Null>;
/// # /// #
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// pub enum Choice { /// 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) /// ![Radio buttons drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true)
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Radio<Message> { pub struct Radio<Message, Renderer: self::Renderer> {
is_selected: bool, is_selected: bool,
on_click: Message, on_click: Message,
label: String, 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. /// Creates a new [`Radio`] button.
/// ///
/// It expects: /// It expects:
@ -61,20 +62,20 @@ impl<Message> Radio<Message> {
is_selected: Some(value) == selected, is_selected: Some(value) == selected,
on_click: f(value), on_click: f(value),
label: String::from(label), 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 /// [`Radio`]: struct.Radio.html
pub fn label_color<C: Into<Color>>(mut self, color: C) -> Self { pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
self.label_color = Some(color.into()); self.style = style.into();
self self
} }
} }
impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message> impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message, Renderer>
where where
Renderer: self::Renderer + text::Renderer + row::Renderer, Renderer: self::Renderer + text::Renderer + row::Renderer,
Message: Clone, Message: Clone,
@ -132,6 +133,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
@ -144,11 +146,12 @@ where
let label = text::Renderer::draw( let label = text::Renderer::draw(
renderer, renderer,
defaults,
label_layout.bounds(), label_layout.bounds(),
&self.label, &self.label,
text::Renderer::default_size(renderer), text::Renderer::default_size(renderer),
Font::Default, Font::Default,
self.label_color, None,
HorizontalAlignment::Left, HorizontalAlignment::Left,
VerticalAlignment::Center, VerticalAlignment::Center,
); );
@ -161,6 +164,7 @@ where
self.is_selected, self.is_selected,
is_mouse_over, is_mouse_over,
label, label,
&self.style,
) )
} }
@ -177,6 +181,9 @@ where
/// [`Radio`]: struct.Radio.html /// [`Radio`]: struct.Radio.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// The style supported by this renderer.
type Style: Default;
/// Returns the default size of a [`Radio`] button. /// Returns the default size of a [`Radio`] button.
/// ///
/// [`Radio`]: struct.Radio.html /// [`Radio`]: struct.Radio.html
@ -197,16 +204,17 @@ pub trait Renderer: crate::Renderer {
is_selected: bool, is_selected: bool,
is_mouse_over: bool, is_mouse_over: bool,
label: Self::Output, label: Self::Output,
style: &Self::Style,
) -> Self::Output; ) -> Self::Output;
} }
impl<'a, Message, Renderer> From<Radio<Message>> impl<'a, Message, Renderer> From<Radio<Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: self::Renderer + row::Renderer + text::Renderer, Renderer: 'static + self::Renderer + row::Renderer + text::Renderer,
Message: 'static + Clone, 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) Element::new(radio)
} }
} }

View file

@ -174,10 +174,11 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
renderer.draw(&self.children, layout, cursor_position) renderer.draw(defaults, &self.children, layout, cursor_position)
} }
fn hash_layout(&self, state: &mut Hasher) { fn hash_layout(&self, state: &mut Hasher) {
@ -215,6 +216,7 @@ pub trait Renderer: crate::Renderer + Sized {
/// [`Layout`]: ../layout/struct.Layout.html /// [`Layout`]: ../layout/struct.Layout.html
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
defaults: &Self::Defaults,
children: &[Element<'_, Message, Self>], children: &[Element<'_, Message, Self>],
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,

View file

@ -11,14 +11,15 @@ use std::{f32, hash::Hash, u32};
/// A widget that can vertically display an infinite amount of content with a /// A widget that can vertically display an infinite amount of content with a
/// scrollbar. /// scrollbar.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Scrollable<'a, Message, Renderer> { pub struct Scrollable<'a, Message, Renderer: self::Renderer> {
state: &'a mut State, state: &'a mut State,
height: Length, height: Length,
max_height: u32, max_height: u32,
content: Column<'a, Message, Renderer>, 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`]. /// Creates a new [`Scrollable`] with the given [`State`].
/// ///
/// [`Scrollable`]: struct.Scrollable.html /// [`Scrollable`]: struct.Scrollable.html
@ -29,6 +30,7 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
height: Length::Shrink, height: Length::Shrink,
max_height: u32::MAX, max_height: u32::MAX,
content: Column::new(), content: Column::new(),
style: Renderer::Style::default(),
} }
} }
@ -90,6 +92,14 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
self 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`]. /// Adds an element to the [`Scrollable`].
/// ///
/// [`Scrollable`]: struct.Scrollable.html /// [`Scrollable`]: struct.Scrollable.html
@ -105,7 +115,7 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer> impl<'a, Message, Renderer> Widget<Message, Renderer>
for Scrollable<'a, Message, Renderer> for Scrollable<'a, Message, Renderer>
where where
Renderer: self::Renderer + column::Renderer, Renderer: 'static + self::Renderer + column::Renderer,
{ {
fn width(&self) -> Length { fn width(&self) -> Length {
Length::Fill Length::Fill
@ -255,6 +265,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
@ -277,7 +288,12 @@ where
Point::new(cursor_position.x, -1.0) 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( self::Renderer::draw(
@ -289,12 +305,13 @@ where
is_mouse_over_scrollbar, is_mouse_over_scrollbar,
scrollbar, scrollbar,
offset, offset,
&self.style,
content, content,
) )
} }
fn hash_layout(&self, state: &mut Hasher) { 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.height.hash(state);
self.max_height.hash(state); self.max_height.hash(state);
@ -441,6 +458,9 @@ pub struct Scroller {
/// [`Scrollable`]: struct.Scrollable.html /// [`Scrollable`]: struct.Scrollable.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized { 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 /// Returns the [`Scrollbar`] given the bounds and content bounds of a
/// [`Scrollable`]. /// [`Scrollable`].
/// ///
@ -477,6 +497,7 @@ pub trait Renderer: crate::Renderer + Sized {
is_mouse_over_scrollbar: bool, is_mouse_over_scrollbar: bool,
scrollbar: Option<Scrollbar>, scrollbar: Option<Scrollbar>,
offset: u32, offset: u32,
style: &Self::Style,
content: Self::Output, content: Self::Output,
) -> Self::Output; ) -> Self::Output;
} }
@ -484,7 +505,7 @@ pub trait Renderer: crate::Renderer + Sized {
impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>> impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: 'a + self::Renderer + column::Renderer, Renderer: 'static + self::Renderer + column::Renderer,
Message: 'static, Message: 'static,
{ {
fn from( fn from(

View file

@ -21,8 +21,9 @@ use std::{hash::Hash, ops::RangeInclusive};
/// ///
/// # Example /// # 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 { /// pub enum Message {
/// SliderChanged(f32), /// 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) /// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true)
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Slider<'a, Message> { pub struct Slider<'a, Message, Renderer: self::Renderer> {
state: &'a mut State, state: &'a mut State,
range: RangeInclusive<f32>, range: RangeInclusive<f32>,
value: f32, value: f32,
on_change: Box<dyn Fn(f32) -> Message>, on_change: Box<dyn Fn(f32) -> Message>,
width: Length, 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`]. /// Creates a new [`Slider`].
/// ///
/// It expects: /// It expects:
@ -71,6 +73,7 @@ impl<'a, Message> Slider<'a, Message> {
range, range,
on_change: Box::new(on_change), on_change: Box::new(on_change),
width: Length::Fill, width: Length::Fill,
style: Renderer::Style::default(),
} }
} }
@ -81,6 +84,14 @@ impl<'a, Message> Slider<'a, Message> {
self.width = width; self.width = width;
self 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`]. /// 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 where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
@ -178,6 +190,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
@ -187,6 +200,7 @@ where
self.range.clone(), self.range.clone(),
self.value, self.value,
self.state.is_dragging, self.state.is_dragging,
&self.style,
) )
} }
@ -203,6 +217,9 @@ where
/// [`Slider`]: struct.Slider.html /// [`Slider`]: struct.Slider.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// The style supported by this renderer.
type Style: Default;
/// Returns the height of the [`Slider`]. /// Returns the height of the [`Slider`].
/// ///
/// [`Slider`]: struct.Slider.html /// [`Slider`]: struct.Slider.html
@ -227,16 +244,19 @@ pub trait Renderer: crate::Renderer {
range: RangeInclusive<f32>, range: RangeInclusive<f32>,
value: f32, value: f32,
is_dragging: bool, is_dragging: bool,
style: &Self::Style,
) -> Self::Output; ) -> Self::Output;
} }
impl<'a, Message, Renderer> From<Slider<'a, Message>> impl<'a, Message, Renderer> From<Slider<'a, Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: self::Renderer, Renderer: 'static + self::Renderer,
Message: 'static, 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) Element::new(slider)
} }
} }

View file

@ -68,6 +68,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {

View file

@ -91,6 +91,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {

View file

@ -146,10 +146,12 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: Point, _cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
renderer.draw( renderer.draw(
defaults,
layout.bounds(), layout.bounds(),
&self.content, &self.content,
self.size.unwrap_or(renderer.default_size()), self.size.unwrap_or(renderer.default_size()),
@ -209,6 +211,7 @@ pub trait Renderer: crate::Renderer {
/// [`VerticalAlignment`]: enum.VerticalAlignment.html /// [`VerticalAlignment`]: enum.VerticalAlignment.html
fn draw( fn draw(
&mut self, &mut self,
defaults: &Self::Defaults,
bounds: Rectangle, bounds: Rectangle,
content: &str, content: &str,
size: u16, size: u16,

View file

@ -15,8 +15,9 @@ use unicode_segmentation::UnicodeSegmentation;
/// ///
/// # Example /// # 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)] /// #[derive(Debug, Clone)]
/// enum Message { /// enum Message {
/// TextInputChanged(String), /// 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) /// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true)
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct TextInput<'a, Message> { pub struct TextInput<'a, Message, Renderer: self::Renderer> {
state: &'a mut State, state: &'a mut State,
placeholder: String, placeholder: String,
value: Value, value: Value,
@ -46,9 +47,10 @@ pub struct TextInput<'a, Message> {
size: Option<u16>, size: Option<u16>,
on_change: Box<dyn Fn(String) -> Message>, on_change: Box<dyn Fn(String) -> Message>,
on_submit: Option<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`]. /// Creates a new [`TextInput`].
/// ///
/// It expects: /// It expects:
@ -64,7 +66,7 @@ impl<'a, Message> TextInput<'a, Message> {
placeholder: &str, placeholder: &str,
value: &str, value: &str,
on_change: F, on_change: F,
) -> TextInput<'a, Message> ) -> Self
where where
F: 'static + Fn(String) -> Message, F: 'static + Fn(String) -> Message,
{ {
@ -79,6 +81,7 @@ impl<'a, Message> TextInput<'a, Message> {
size: None, size: None,
on_change: Box::new(on_change), on_change: Box::new(on_change),
on_submit: None, on_submit: None,
style: Renderer::Style::default(),
} }
} }
@ -130,11 +133,20 @@ impl<'a, Message> TextInput<'a, Message> {
self.on_submit = Some(message); self.on_submit = Some(message);
self 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 where
Renderer: self::Renderer, Renderer: 'static + self::Renderer,
Message: Clone + std::fmt::Debug, Message: Clone + std::fmt::Debug,
{ {
fn width(&self) -> Length { fn width(&self) -> Length {
@ -343,6 +355,7 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
) -> Renderer::Output { ) -> Renderer::Output {
@ -358,6 +371,7 @@ where
&self.placeholder, &self.placeholder,
&self.value.secure(), &self.value.secure(),
&self.state, &self.state,
&self.style,
) )
} else { } else {
renderer.draw( renderer.draw(
@ -368,6 +382,7 @@ where
&self.placeholder, &self.placeholder,
&self.value, &self.value,
&self.state, &self.state,
&self.style,
) )
} }
} }
@ -375,7 +390,7 @@ where
fn hash_layout(&self, state: &mut Hasher) { fn hash_layout(&self, state: &mut Hasher) {
use std::{any::TypeId, hash::Hash}; use std::{any::TypeId, hash::Hash};
TypeId::of::<TextInput<'static, ()>>().hash(state); TypeId::of::<TextInput<'static, (), Renderer>>().hash(state);
self.width.hash(state); self.width.hash(state);
self.max_width.hash(state); self.max_width.hash(state);
@ -392,6 +407,9 @@ where
/// [`TextInput`]: struct.TextInput.html /// [`TextInput`]: struct.TextInput.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized { 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`]. /// Returns the default size of the text of the [`TextInput`].
/// ///
/// [`TextInput`]: struct.TextInput.html /// [`TextInput`]: struct.TextInput.html
@ -440,17 +458,18 @@ pub trait Renderer: crate::Renderer + Sized {
placeholder: &str, placeholder: &str,
value: &Value, value: &Value,
state: &State, state: &State,
style: &Self::Style,
) -> Self::Output; ) -> Self::Output;
} }
impl<'a, Message, Renderer> From<TextInput<'a, Message>> impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>>
for Element<'a, Message, Renderer> for Element<'a, Message, Renderer>
where where
Renderer: 'static + self::Renderer, Renderer: 'static + self::Renderer,
Message: 'static + Clone + std::fmt::Debug, Message: 'static + Clone + std::fmt::Debug,
{ {
fn from( fn from(
text_input: TextInput<'a, Message>, text_input: TextInput<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> { ) -> Element<'a, Message, Renderer> {
Element::new(text_input) Element::new(text_input)
} }

View file

@ -151,7 +151,12 @@ pub trait Application: Sized {
Self: 'static, Self: 'static,
{ {
#[cfg(not(target_arch = "wasm32"))] #[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")] #[cfg(target_arch = "wasm32")]
<Instance<Self> as iced_web::Application>::run(); <Instance<Self> as iced_web::Application>::run();

View file

@ -1,6 +1,6 @@
pub use iced_winit::{ pub use iced_winit::{
Align, Background, Color, Command, Font, HorizontalAlignment, Length, Align, Background, Color, Command, Font, HorizontalAlignment, Length,
Space, Subscription, VerticalAlignment, Space, Subscription, Vector, VerticalAlignment,
}; };
pub mod widget { pub mod widget {
@ -22,58 +22,7 @@ pub mod widget {
//! //!
//! [`TextInput`]: text_input/struct.TextInput.html //! [`TextInput`]: text_input/struct.TextInput.html
//! [`text_input::State`]: text_input/struct.State.html //! [`text_input::State`]: text_input/struct.State.html
pub mod button { pub use iced_wgpu::widget::*;
//! 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 mod image { pub mod image {
//! Display images in your user interface. //! Display images in your user interface.
@ -85,12 +34,13 @@ pub mod widget {
pub use iced_winit::svg::{Handle, Svg}; pub use iced_winit::svg::{Handle, Svg};
} }
pub use iced_winit::{Checkbox, ProgressBar, Radio, Text}; pub use iced_winit::Text;
#[doc(no_inline)] #[doc(no_inline)]
pub use { pub use {
button::Button, image::Image, scrollable::Scrollable, slider::Slider, button::Button, checkbox::Checkbox, container::Container, image::Image,
svg::Svg, text_input::TextInput, progress_bar::ProgressBar, radio::Radio, scrollable::Scrollable,
slider::Slider, svg::Svg, text_input::TextInput,
}; };
/// A container that distributes its contents vertically. /// 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`. /// This is an alias of an `iced_native` row with a default `Renderer`.
pub type Row<'a, Message> = pub type Row<'a, Message> =
iced_winit::Row<'a, Message, iced_wgpu::Renderer>; 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)] #[doc(no_inline)]

View file

@ -9,6 +9,12 @@ pub struct Settings {
/// ///
/// [`Window`]: struct.Window.html /// [`Window`]: struct.Window.html
pub window: Window, 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. /// The window settings of an application.

14
style/Cargo.toml Normal file
View file

@ -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" }

96
style/src/button.rs Normal file
View file

@ -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)
}
}

55
style/src/checkbox.rs Normal file
View file

@ -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)
}
}

59
style/src/container.rs Normal file
View file

@ -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)
}
}

8
style/src/lib.rs Normal file
View file

@ -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;

42
style/src/progress_bar.rs Normal file
View file

@ -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)
}
}

53
style/src/radio.rs Normal file
View file

@ -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)
}
}

76
style/src/scrollable.rs Normal file
View file

@ -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)
}
}

95
style/src/slider.rs Normal file
View file

@ -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)
}
}

83
style/src/text_input.rs Normal file
View file

@ -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)
}
}

View file

@ -12,6 +12,7 @@ svg = ["resvg"]
[dependencies] [dependencies]
iced_native = { version = "0.1.0", path = "../native" } iced_native = { version = "0.1.0", path = "../native" }
iced_style = { version = "0.1.0-alpha", path = "../style" }
wgpu = "0.4" wgpu = "0.4"
glyph_brush = "0.6" glyph_brush = "0.6"
wgpu_glyph = { version = "0.7", git = "https://github.com/hecrj/wgpu_glyph", branch = "fix/font-load-panic" } wgpu_glyph = { version = "0.7", git = "https://github.com/hecrj/wgpu_glyph", branch = "fix/font-load-panic" }

32
wgpu/src/defaults.rs Normal file
View file

@ -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,
}
}
}

View file

@ -24,18 +24,25 @@
#![deny(unused_results)] #![deny(unused_results)]
#![deny(unsafe_code)] #![deny(unsafe_code)]
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms)]
pub mod defaults;
pub mod triangle; pub mod triangle;
pub mod widget;
mod image; mod image;
mod primitive; mod primitive;
mod quad; mod quad;
mod renderer; mod renderer;
mod settings;
mod text; mod text;
mod transformation; mod transformation;
pub(crate) use crate::image::Image; pub use defaults::Defaults;
pub(crate) use quad::Quad;
pub(crate) use transformation::Transformation;
pub use primitive::Primitive; pub use primitive::Primitive;
pub use renderer::{Renderer, Target}; 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;

View file

@ -41,6 +41,10 @@ pub enum Primitive {
background: Background, background: Background,
/// The border radius of the quad /// The border radius of the quad
border_radius: u16, border_radius: u16,
/// The border width of the quad
border_width: u16,
/// The border color of the quad
border_color: Color,
}, },
/// An image primitive /// An image primitive
Image { Image {

View file

@ -125,9 +125,19 @@ impl Pipeline {
}, },
wgpu::VertexAttributeDescriptor { wgpu::VertexAttributeDescriptor {
shader_location: 4, shader_location: 4,
format: wgpu::VertexFormat::Float, format: wgpu::VertexFormat::Float4,
offset: 4 * (2 + 2 + 4), 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.x,
bounds.y, bounds.y,
bounds.width, bounds.width,
bounds.height, // TODO: Address anti-aliasing adjustments properly
bounds.height + 1,
); );
render_pass.draw_indexed( render_pass.draw_indexed(
@ -277,7 +288,9 @@ pub struct Quad {
pub position: [f32; 2], pub position: [f32; 2],
pub scale: [f32; 2], pub scale: [f32; 2],
pub color: [f32; 4], pub color: [f32; 4],
pub border_color: [f32; 4],
pub border_radius: f32, pub border_radius: f32,
pub border_width: f32,
} }
impl Quad { impl Quad {

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
image, quad, text, triangle, Image, Primitive, Quad, Transformation, image, quad, text, triangle, Defaults, Image, Primitive, Quad, Settings,
Transformation,
}; };
use iced_native::{ use iced_native::{
renderer::{Debugger, Windowed}, renderer::{Debugger, Windowed},
@ -24,7 +25,7 @@ pub struct Renderer {
device: Device, device: Device,
queue: Queue, queue: Queue,
quad_pipeline: quad::Pipeline, quad_pipeline: quad::Pipeline,
image_pipeline: crate::image::Pipeline, image_pipeline: image::Pipeline,
text_pipeline: text::Pipeline, text_pipeline: text::Pipeline,
triangle_pipeline: crate::triangle::Pipeline, triangle_pipeline: crate::triangle::Pipeline,
} }
@ -52,7 +53,7 @@ impl<'a> Layer<'a> {
} }
impl Renderer { impl Renderer {
fn new() -> Self { fn new(settings: Settings) -> Self {
let adapter = Adapter::request(&RequestAdapterOptions { let adapter = Adapter::request(&RequestAdapterOptions {
power_preference: PowerPreference::Default, power_preference: PowerPreference::Default,
backends: BackendBit::all(), backends: BackendBit::all(),
@ -66,7 +67,8 @@ impl Renderer {
limits: Limits { max_bind_groups: 2 }, 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 quad_pipeline = quad::Pipeline::new(&mut device);
let image_pipeline = crate::image::Pipeline::new(&mut device); let image_pipeline = crate::image::Pipeline::new(&mut device);
let triangle_pipeline = triangle::Pipeline::new(&mut device); let triangle_pipeline = triangle::Pipeline::new(&mut device);
@ -223,6 +225,8 @@ impl Renderer {
bounds, bounds,
background, background,
border_radius, border_radius,
border_width,
border_color,
} => { } => {
// TODO: Move some of this computations to the GPU (?) // TODO: Move some of this computations to the GPU (?)
layer.quads.push(Quad { layer.quads.push(Quad {
@ -235,6 +239,8 @@ impl Renderer {
Background::Color(color) => color.into_linear(), Background::Color(color) => color.into_linear(),
}, },
border_radius: *border_radius as f32, border_radius: *border_radius as f32,
border_width: *border_width as f32,
border_color: border_color.into_linear(),
}); });
} }
Primitive::Image { handle, bounds } => { Primitive::Image { handle, bounds } => {
@ -434,6 +440,7 @@ impl Renderer {
impl iced_native::Renderer for Renderer { impl iced_native::Renderer for Renderer {
type Output = (Primitive, MouseCursor); type Output = (Primitive, MouseCursor);
type Defaults = Defaults;
fn layout<'a, Message>( fn layout<'a, Message>(
&mut self, &mut self,
@ -448,10 +455,11 @@ impl iced_native::Renderer for Renderer {
} }
impl Windowed for Renderer { impl Windowed for Renderer {
type Settings = Settings;
type Target = Target; type Target = Target;
fn new() -> Self { fn new(settings: Settings) -> Self {
Self::new() Self::new(settings)
} }
fn draw<T: AsRef<str>>( fn draw<T: AsRef<str>>(
@ -467,13 +475,15 @@ impl Windowed for Renderer {
impl Debugger for Renderer { impl Debugger for Renderer {
fn explain<Message>( fn explain<Message>(
&mut self, &mut self,
defaults: &Defaults,
widget: &dyn Widget<Message, Self>, widget: &dyn Widget<Message, Self>,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
color: Color, color: Color,
) -> Self::Output { ) -> Self::Output {
let mut primitives = Vec::new(); 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); explain_layout(layout, color, &mut primitives);
primitives.push(primitive); primitives.push(primitive);
@ -487,11 +497,12 @@ fn explain_layout(
color: Color, color: Color,
primitives: &mut Vec<Primitive>, primitives: &mut Vec<Primitive>,
) { ) {
// TODO: Draw borders instead
primitives.push(Primitive::Quad { primitives.push(Primitive::Quad {
bounds: layout.bounds(), bounds: layout.bounds(),
background: Background::Color([0.0, 0.0, 0.0, 0.05].into()), background: Background::Color(Color::TRANSPARENT),
border_radius: 0, border_radius: 0,
border_width: 1,
border_color: [0.6, 0.6, 0.6, 0.5].into(),
}); });
for child in layout.children() { for child in layout.children() {

View file

@ -1,6 +1,7 @@
mod button; mod button;
mod checkbox; mod checkbox;
mod column; mod column;
mod container;
mod image; mod image;
mod progress_bar; mod progress_bar;
mod radio; mod radio;

View file

@ -1,54 +1,86 @@
use crate::{Primitive, Renderer}; use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer};
use iced_native::{button, Background, MouseCursor, Point, Rectangle}; use iced_native::{
Background, Color, Element, Layout, MouseCursor, Point, Rectangle, Vector,
};
impl button::Renderer for Renderer { impl iced_native::button::Renderer for Renderer {
fn draw( type Style = Box<dyn StyleSheet>;
fn draw<Message>(
&mut self, &mut self,
defaults: &Defaults,
bounds: Rectangle, bounds: Rectangle,
cursor_position: Point, cursor_position: Point,
is_disabled: bool,
is_pressed: bool, is_pressed: bool,
background: Option<Background>, style: &Box<dyn StyleSheet>,
border_radius: u16, content: &Element<'_, Message, Self>,
(content, _): Self::Output, content_layout: Layout<'_>,
) -> Self::Output { ) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position); let is_mouse_over = bounds.contains(cursor_position);
// TODO: Render proper shadows let styling = if is_disabled {
// TODO: Make hovering and pressed styles configurable style.disabled()
let shadow_offset = if is_mouse_over { } else if is_mouse_over {
if is_pressed { if is_pressed {
0.0 style.pressed()
} else { } else {
2.0 style.hovered()
} }
} else { } else {
1.0 style.active()
}; };
( let (content, _) = content.draw(
match background { self,
None => content, &Defaults {
Some(background) => Primitive::Group { text: defaults::Text {
primitives: vec![ color: styling.text_color,
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,
},
content,
],
}, },
..*defaults
},
content_layout,
cursor_position,
);
(
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
},
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 { if is_mouse_over {
MouseCursor::Pointer MouseCursor::Pointer

View file

@ -1,12 +1,13 @@
use crate::{Primitive, Renderer}; use crate::{checkbox::StyleSheet, Primitive, Renderer};
use iced_native::{ use iced_native::{
checkbox, Background, HorizontalAlignment, MouseCursor, Rectangle, checkbox, HorizontalAlignment, MouseCursor, Rectangle, VerticalAlignment,
VerticalAlignment,
}; };
const SIZE: f32 = 28.0; const SIZE: f32 = 28.0;
impl checkbox::Renderer for Renderer { impl checkbox::Renderer for Renderer {
type Style = Box<dyn StyleSheet>;
fn default_size(&self) -> u32 { fn default_size(&self) -> u32 {
SIZE as u32 SIZE as u32
} }
@ -17,31 +18,21 @@ impl checkbox::Renderer for Renderer {
is_checked: bool, is_checked: bool,
is_mouse_over: bool, is_mouse_over: bool,
(label, _): Self::Output, (label, _): Self::Output,
style_sheet: &Self::Style,
) -> Self::Output { ) -> Self::Output {
let (checkbox_border, checkbox_box) = ( let style = if is_mouse_over {
Primitive::Quad { style_sheet.hovered(is_checked)
bounds, } else {
background: Background::Color([0.6, 0.6, 0.6].into()), style_sheet.active(is_checked)
border_radius: 6, };
},
Primitive::Quad { let checkbox = Primitive::Quad {
bounds: Rectangle { bounds,
x: bounds.x + 1.0, background: style.background,
y: bounds.y + 1.0, border_radius: style.border_radius,
width: bounds.width - 2.0, border_width: style.border_width,
height: bounds.height - 2.0, border_color: style.border_color,
}, };
background: Background::Color(
if is_mouse_over {
[0.90, 0.90, 0.90]
} else {
[0.95, 0.95, 0.95]
}
.into(),
),
border_radius: 5,
},
);
( (
Primitive::Group { Primitive::Group {
@ -51,14 +42,14 @@ impl checkbox::Renderer for Renderer {
font: crate::text::BUILTIN_ICONS, font: crate::text::BUILTIN_ICONS,
size: bounds.height * 0.7, size: bounds.height * 0.7,
bounds: bounds, bounds: bounds,
color: [0.3, 0.3, 0.3].into(), color: style.checkmark_color,
horizontal_alignment: HorizontalAlignment::Center, horizontal_alignment: HorizontalAlignment::Center,
vertical_alignment: VerticalAlignment::Center, vertical_alignment: VerticalAlignment::Center,
}; };
vec![checkbox_border, checkbox_box, check, label] vec![checkbox, check, label]
} else { } else {
vec![checkbox_border, checkbox_box, label] vec![checkbox, label]
}, },
}, },
if is_mouse_over { if is_mouse_over {

View file

@ -4,6 +4,7 @@ use iced_native::{column, Element, Layout, MouseCursor, Point};
impl column::Renderer for Renderer { impl column::Renderer for Renderer {
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
defaults: &Self::Defaults,
content: &[Element<'_, Message, Self>], content: &[Element<'_, Message, Self>],
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
@ -17,7 +18,7 @@ impl column::Renderer for Renderer {
.zip(layout.children()) .zip(layout.children())
.map(|(child, layout)| { .map(|(child, layout)| {
let (primitive, new_mouse_cursor) = let (primitive, new_mouse_cursor) =
child.draw(self, layout, cursor_position); child.draw(self, defaults, layout, cursor_position);
if new_mouse_cursor > mouse_cursor { if new_mouse_cursor > mouse_cursor {
mouse_cursor = new_mouse_cursor; mouse_cursor = new_mouse_cursor;

View file

@ -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)
}
}
}

View file

@ -1,7 +1,9 @@
use crate::{Primitive, Renderer}; use crate::{progress_bar::StyleSheet, Primitive, Renderer};
use iced_native::{progress_bar, Background, Color, MouseCursor, Rectangle}; use iced_native::{progress_bar, Color, MouseCursor, Rectangle};
impl progress_bar::Renderer for Renderer { impl progress_bar::Renderer for Renderer {
type Style = Box<dyn StyleSheet>;
const DEFAULT_HEIGHT: u16 = 30; const DEFAULT_HEIGHT: u16 = 30;
fn draw( fn draw(
@ -9,9 +11,10 @@ impl progress_bar::Renderer for Renderer {
bounds: Rectangle, bounds: Rectangle,
range: std::ops::RangeInclusive<f32>, range: std::ops::RangeInclusive<f32>,
value: f32, value: f32,
background: Option<Background>, style_sheet: &Self::Style,
active_color: Option<Color>,
) -> Self::Output { ) -> Self::Output {
let style = style_sheet.style();
let (range_start, range_end) = range.into_inner(); let (range_start, range_end) = range.into_inner();
let active_progress_width = bounds.width let active_progress_width = bounds.width
* ((value - range_start) / (range_end - range_start).max(1.0)); * ((value - range_start) / (range_end - range_start).max(1.0));
@ -19,27 +22,31 @@ impl progress_bar::Renderer for Renderer {
let background = Primitive::Group { let background = Primitive::Group {
primitives: vec![Primitive::Quad { primitives: vec![Primitive::Quad {
bounds: Rectangle { ..bounds }, bounds: Rectangle { ..bounds },
background: background background: style.background,
.unwrap_or(Background::Color([0.6, 0.6, 0.6].into())) border_radius: style.border_radius,
.into(), border_width: 0,
border_radius: 5, 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 { if active_progress_width > 0.0 {
primitives: vec![background, active_progress], 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, MouseCursor::OutOfBounds,
) )

View file

@ -1,10 +1,12 @@
use crate::{Primitive, Renderer}; use crate::{radio::StyleSheet, Primitive, Renderer};
use iced_native::{radio, Background, MouseCursor, Rectangle}; use iced_native::{radio, Background, Color, MouseCursor, Rectangle};
const SIZE: f32 = 28.0; const SIZE: f32 = 28.0;
const DOT_SIZE: f32 = SIZE / 2.0; const DOT_SIZE: f32 = SIZE / 2.0;
impl radio::Renderer for Renderer { impl radio::Renderer for Renderer {
type Style = Box<dyn StyleSheet>;
fn default_size(&self) -> u32 { fn default_size(&self) -> u32 {
SIZE as u32 SIZE as u32
} }
@ -15,31 +17,21 @@ impl radio::Renderer for Renderer {
is_selected: bool, is_selected: bool,
is_mouse_over: bool, is_mouse_over: bool,
(label, _): Self::Output, (label, _): Self::Output,
style_sheet: &Self::Style,
) -> Self::Output { ) -> Self::Output {
let (radio_border, radio_box) = ( let style = if is_mouse_over {
Primitive::Quad { style_sheet.hovered()
bounds, } else {
background: Background::Color([0.6, 0.6, 0.6].into()), style_sheet.active()
border_radius: (SIZE / 2.0) as u16, };
},
Primitive::Quad { let radio = Primitive::Quad {
bounds: Rectangle { bounds,
x: bounds.x + 1.0, background: style.background,
y: bounds.y + 1.0, border_radius: (SIZE / 2.0) as u16,
width: bounds.width - 2.0, border_width: style.border_width,
height: bounds.height - 2.0, border_color: style.border_color,
}, };
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,
},
);
( (
Primitive::Group { Primitive::Group {
@ -51,13 +43,15 @@ impl radio::Renderer for Renderer {
width: bounds.width - DOT_SIZE, width: bounds.width - DOT_SIZE,
height: bounds.height - 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_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 { } else {
vec![radio_border, radio_box, label] vec![radio, label]
}, },
}, },
if is_mouse_over { if is_mouse_over {

View file

@ -4,6 +4,7 @@ use iced_native::{row, Element, Layout, MouseCursor, Point};
impl row::Renderer for Renderer { impl row::Renderer for Renderer {
fn draw<Message>( fn draw<Message>(
&mut self, &mut self,
defaults: &Self::Defaults,
children: &[Element<'_, Message, Self>], children: &[Element<'_, Message, Self>],
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: Point,
@ -17,7 +18,7 @@ impl row::Renderer for Renderer {
.zip(layout.children()) .zip(layout.children())
.map(|(child, layout)| { .map(|(child, layout)| {
let (primitive, new_mouse_cursor) = let (primitive, new_mouse_cursor) =
child.draw(self, layout, cursor_position); child.draw(self, defaults, layout, cursor_position);
if new_mouse_cursor > mouse_cursor { if new_mouse_cursor > mouse_cursor {
mouse_cursor = new_mouse_cursor; mouse_cursor = new_mouse_cursor;

View file

@ -1,10 +1,14 @@
use crate::{Primitive, Renderer}; 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_WIDTH: u16 = 10;
const SCROLLBAR_MARGIN: u16 = 2; const SCROLLBAR_MARGIN: u16 = 2;
impl scrollable::Renderer for Renderer { impl scrollable::Renderer for Renderer {
type Style = Box<dyn iced_style::scrollable::StyleSheet>;
fn scrollbar( fn scrollbar(
&self, &self,
bounds: Rectangle, bounds: Rectangle,
@ -51,6 +55,7 @@ impl scrollable::Renderer for Renderer {
is_mouse_over_scrollbar: bool, is_mouse_over_scrollbar: bool,
scrollbar: Option<scrollable::Scrollbar>, scrollbar: Option<scrollable::Scrollbar>,
offset: u32, offset: u32,
style_sheet: &Self::Style,
(content, mouse_cursor): Self::Output, (content, mouse_cursor): Self::Output,
) -> Self::Output { ) -> Self::Output {
let clip = Primitive::Clip { let clip = Primitive::Clip {
@ -61,40 +66,53 @@ impl scrollable::Renderer for Renderer {
( (
if let Some(scrollbar) = scrollbar { if let Some(scrollbar) = scrollbar {
if is_mouse_over || state.is_scroller_grabbed() { let style = if state.is_scroller_grabbed() {
let scroller = Primitive::Quad { 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;
let scroller = if is_mouse_over
|| state.is_scroller_grabbed()
|| is_scrollbar_visible
{
Primitive::Quad {
bounds: scrollbar.scroller.bounds, bounds: scrollbar.scroller.bounds,
background: Background::Color( background: Background::Color(style.scroller.color),
[0.0, 0.0, 0.0, 0.7].into(), border_radius: style.scroller.border_radius,
), border_width: style.scroller.border_width,
border_radius: 5, border_color: style.scroller.border_color,
};
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,
};
Primitive::Group {
primitives: vec![clip, scrollbar, scroller],
}
} else {
Primitive::Group {
primitives: vec![clip, scroller],
}
} }
} else { } else {
clip Primitive::None
};
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 {
Primitive::None
};
Primitive::Group {
primitives: vec![clip, scrollbar, scroller],
} }
} else { } else {
clip clip

View file

@ -1,10 +1,14 @@
use crate::{Primitive, Renderer}; use crate::{
slider::{HandleShape, StyleSheet},
Primitive, Renderer,
};
use iced_native::{slider, Background, Color, MouseCursor, Point, Rectangle}; use iced_native::{slider, Background, Color, MouseCursor, Point, Rectangle};
const HANDLE_WIDTH: f32 = 8.0;
const HANDLE_HEIGHT: f32 = 22.0; const HANDLE_HEIGHT: f32 = 22.0;
impl slider::Renderer for Renderer { impl slider::Renderer for Renderer {
type Style = Box<dyn StyleSheet>;
fn height(&self) -> u32 { fn height(&self) -> u32 {
30 30
} }
@ -16,9 +20,18 @@ impl slider::Renderer for Renderer {
range: std::ops::RangeInclusive<f32>, range: std::ops::RangeInclusive<f32>,
value: f32, value: f32,
is_dragging: bool, is_dragging: bool,
style_sheet: &Self::Style,
) -> Self::Output { ) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position); 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_y = bounds.y + (bounds.height / 2.0).round();
let (rail_top, rail_bottom) = ( let (rail_top, rail_bottom) = (
@ -29,8 +42,10 @@ impl slider::Renderer for Renderer {
width: bounds.width, width: bounds.width,
height: 2.0, 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_radius: 0,
border_width: 0,
border_color: Color::TRANSPARENT,
}, },
Primitive::Quad { Primitive::Quad {
bounds: Rectangle { bounds: Rectangle {
@ -39,51 +54,45 @@ impl slider::Renderer for Renderer {
width: bounds.width, width: bounds.width,
height: 2.0, height: 2.0,
}, },
background: Background::Color(Color::WHITE), background: Background::Color(style.rail_colors.1),
border_radius: 0, border_radius: 0,
border_width: 0,
border_color: Color::TRANSPARENT,
}, },
); );
let (range_start, range_end) = range.into_inner(); 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)); * ((value - range_start) / (range_end - range_start).max(1.0));
let (handle_border, handle) = ( let handle = Primitive::Quad {
Primitive::Quad { bounds: Rectangle {
bounds: Rectangle { x: bounds.x + handle_offset.round(),
x: bounds.x + handle_offset.round() - 1.0, y: rail_y - handle_height / 2.0,
y: rail_y - HANDLE_HEIGHT / 2.0 - 1.0, width: handle_width,
width: HANDLE_WIDTH + 2.0, height: handle_height,
height: HANDLE_HEIGHT + 2.0,
},
background: Color::from_rgb(0.6, 0.6, 0.6).into(),
border_radius: 5,
}, },
Primitive::Quad { background: Background::Color(style.handle.color),
bounds: Rectangle { border_radius: handle_border_radius,
x: bounds.x + handle_offset.round(), border_width: style.handle.border_width,
y: rail_y - HANDLE_HEIGHT / 2.0, border_color: style.handle.border_color,
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,
},
);
( (
Primitive::Group { Primitive::Group {
primitives: vec![rail_top, rail_bottom, handle_border, handle], primitives: vec![rail_top, rail_bottom, handle],
}, },
if is_dragging { if is_dragging {
MouseCursor::Grabbing MouseCursor::Grabbing

View file

@ -27,6 +27,7 @@ impl text::Renderer for Renderer {
fn draw( fn draw(
&mut self, &mut self,
defaults: &Self::Defaults,
bounds: Rectangle, bounds: Rectangle,
content: &str, content: &str,
size: u16, size: u16,
@ -40,7 +41,7 @@ impl text::Renderer for Renderer {
content: content.to_string(), content: content.to_string(),
size: f32::from(size), size: f32::from(size),
bounds, bounds,
color: color.unwrap_or(Color::BLACK), color: color.unwrap_or(defaults.text.color),
font, font,
horizontal_alignment, horizontal_alignment,
vertical_alignment, vertical_alignment,

View file

@ -1,4 +1,4 @@
use crate::{Primitive, Renderer}; use crate::{text_input::StyleSheet, Primitive, Renderer};
use iced_native::{ use iced_native::{
text_input, Background, Color, Font, HorizontalAlignment, MouseCursor, text_input, Background, Color, Font, HorizontalAlignment, MouseCursor,
@ -7,6 +7,8 @@ use iced_native::{
use std::f32; use std::f32;
impl text_input::Renderer for Renderer { impl text_input::Renderer for Renderer {
type Style = Box<dyn StyleSheet>;
fn default_size(&self) -> u16 { fn default_size(&self) -> u16 {
// TODO: Make this configurable // TODO: Make this configurable
20 20
@ -61,31 +63,24 @@ impl text_input::Renderer for Renderer {
placeholder: &str, placeholder: &str,
value: &text_input::Value, value: &text_input::Value,
state: &text_input::State, state: &text_input::State,
style_sheet: &Self::Style,
) -> Self::Output { ) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position); let is_mouse_over = bounds.contains(cursor_position);
let border = Primitive::Quad { let style = if state.is_focused() {
bounds, style_sheet.focused()
background: Background::Color( } else if is_mouse_over {
if is_mouse_over || state.is_focused() { style_sheet.hovered()
[0.5, 0.5, 0.5] } else {
} else { style_sheet.active()
[0.7, 0.7, 0.7]
}
.into(),
),
border_radius: 5,
}; };
let input = Primitive::Quad { let input = Primitive::Quad {
bounds: Rectangle { bounds,
x: bounds.x + 1.0, background: style.background,
y: bounds.y + 1.0, border_radius: style.border_radius,
width: bounds.width - 2.0, border_width: style.border_width,
height: bounds.height - 2.0, border_color: style.border_color,
},
background: Background::Color(Color::WHITE),
border_radius: 4,
}; };
let text = value.to_string(); let text = value.to_string();
@ -97,9 +92,9 @@ impl text_input::Renderer for Renderer {
text.clone() text.clone()
}, },
color: if text.is_empty() { color: if text.is_empty() {
[0.7, 0.7, 0.7] style_sheet.placeholder_color()
} else { } else {
[0.3, 0.3, 0.3] style_sheet.value_color()
} }
.into(), .into(),
font: Font::Default, font: Font::Default,
@ -128,8 +123,10 @@ impl text_input::Renderer for Renderer {
width: 1.0, width: 1.0,
height: text_bounds.height, height: text_bounds.height,
}, },
background: Background::Color(Color::BLACK), background: Background::Color(style_sheet.value_color()),
border_radius: 0, border_radius: 0,
border_width: 0,
border_color: Color::TRANSPARENT,
}; };
( (
@ -150,7 +147,7 @@ impl text_input::Renderer for Renderer {
( (
Primitive::Group { Primitive::Group {
primitives: vec![border, input, contents], primitives: vec![input, contents],
}, },
if is_mouse_over { if is_mouse_over {
MouseCursor::Text MouseCursor::Text

10
wgpu/src/settings.rs Normal file
View file

@ -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]>,
}

View file

@ -1,14 +1,17 @@
#version 450 #version 450
layout(location = 0) in vec4 v_Color; layout(location = 0) in vec4 v_Color;
layout(location = 1) in vec2 v_Pos; layout(location = 1) in vec4 v_BorderColor;
layout(location = 2) in vec2 v_Scale; layout(location = 2) in vec2 v_Pos;
layout(location = 3) in float v_BorderRadius; 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; 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 inner_size = size - vec2(radius, radius) * 2.0;
vec2 top_left = position + vec2(radius, radius); vec2 top_left = position + vec2(radius, radius);
vec2 bottom_right = top_left + inner_size; 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) max(max(top_left_distance.y, bottom_right_distance.y), 0)
); );
float d = sqrt(distance.x * distance.x + distance.y * distance.y); return sqrt(distance.x * distance.x + distance.y * distance.y);
return 1.0 - smoothstep(radius - s, radius + s, d);
} }
void main() { void main() {
float radius_alpha = rounded(gl_FragCoord.xy, v_Pos, v_Scale, v_BorderRadius, 0.5); vec4 mixed_color;
o_Color = vec4(v_Color.xyz, v_Color.w * radius_alpha); // 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(mixed_color.xyz, mixed_color.w * radius_alpha);
} }

Binary file not shown.

View file

@ -4,7 +4,9 @@ layout(location = 0) in vec2 v_Pos;
layout(location = 1) in vec2 i_Pos; layout(location = 1) in vec2 i_Pos;
layout(location = 2) in vec2 i_Scale; layout(location = 2) in vec2 i_Scale;
layout(location = 3) in vec4 i_Color; layout(location = 3) in vec4 i_Color;
layout(location = 4) in 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 { layout (set = 0, binding = 0) uniform Globals {
mat4 u_Transform; mat4 u_Transform;
@ -12,9 +14,11 @@ layout (set = 0, binding = 0) uniform Globals {
}; };
layout(location = 0) out vec4 o_Color; layout(location = 0) out vec4 o_Color;
layout(location = 1) out vec2 o_Pos; layout(location = 1) out vec4 o_BorderColor;
layout(location = 2) out vec2 o_Scale; layout(location = 2) out vec2 o_Pos;
layout(location = 3) out float o_BorderRadius; layout(location = 3) out vec2 o_Scale;
layout(location = 4) out float o_BorderRadius;
layout(location = 5) out float o_BorderWidth;
void main() { void main() {
vec2 p_Pos = i_Pos * u_Scale; vec2 p_Pos = i_Pos * u_Scale;
@ -28,9 +32,11 @@ void main() {
); );
o_Color = i_Color; o_Color = i_Color;
o_BorderColor = i_BorderColor;
o_Pos = p_Pos; o_Pos = p_Pos;
o_Scale = p_Scale; o_Scale = p_Scale;
o_BorderRadius = i_BorderRadius * u_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); gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0);
} }

Binary file not shown.

View file

@ -22,13 +22,16 @@ pub struct Pipeline {
} }
impl 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 // TODO: Font customization
let font_source = font::Source::new(); let font_source = font::Source::new();
let default_font = font_source let default_font =
.load(&[font::Family::SansSerif, font::Family::Serif]) default_font.map(|slice| slice.to_vec()).unwrap_or_else(|| {
.unwrap_or_else(|_| FALLBACK_FONT.to_vec()); font_source
.load(&[font::Family::SansSerif, font::Family::Serif])
.unwrap_or_else(|_| FALLBACK_FONT.to_vec())
});
let load_glyph_brush = |font: Vec<u8>| { let load_glyph_brush = |font: Vec<u8>| {
let builder = let builder =

34
wgpu/src/widget.rs Normal file
View file

@ -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;

15
wgpu/src/widget/button.rs Normal file
View file

@ -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>;

View file

@ -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>;

View file

@ -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>;

View file

@ -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>;

10
wgpu/src/widget/radio.rs Normal file
View file

@ -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>;

View file

@ -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>;

16
wgpu/src/widget/slider.rs Normal file
View file

@ -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>;

View file

@ -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>;

View file

@ -6,6 +6,9 @@ edition = "2018"
description = "A winit runtime for Iced" description = "A winit runtime for Iced"
license = "MIT" license = "MIT"
repository = "https://github.com/hecrj/iced" repository = "https://github.com/hecrj/iced"
documentation = "https://docs.rs/iced_winit"
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[features] [features]
debug = [] debug = []

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
conversion, container, conversion,
input::{keyboard, mouse}, input::{keyboard, mouse},
renderer::{Target, Windowed}, renderer::{Target, Windowed},
subscription, Cache, Clipboard, Command, Container, Debug, Element, Event, subscription, Cache, Clipboard, Command, Container, Debug, Element, Event,
@ -18,7 +18,7 @@ pub trait Application: Sized {
/// The renderer to use to draw the [`Application`]. /// The renderer to use to draw the [`Application`].
/// ///
/// [`Application`]: trait.Application.html /// [`Application`]: trait.Application.html
type Renderer: Windowed; type Renderer: Windowed + container::Renderer;
/// The type of __messages__ your [`Application`] will produce. /// 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. /// It should probably be that last thing you call in your `main` function.
/// ///
/// [`Application`]: trait.Application.html /// [`Application`]: trait.Application.html
fn run(settings: Settings) fn run(
where settings: Settings,
renderer_settings: <Self::Renderer as Windowed>::Settings,
) where
Self: 'static, Self: 'static,
{ {
use winit::{ use winit::{
@ -140,7 +142,7 @@ pub trait Application: Sized {
let mut resized = false; let mut resized = false;
let clipboard = Clipboard::new(&window); let clipboard = Clipboard::new(&window);
let mut renderer = Self::Renderer::new(); let mut renderer = Self::Renderer::new(renderer_settings);
let mut target = { let mut target = {
let (width, height) = to_physical(size, dpi); let (width, height) = to_physical(size, dpi);

View file

@ -1,5 +1,4 @@
//! Configure your application. //! Configure your application.
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
#[path = "windows.rs"] #[path = "windows.rs"]
mod platform; mod platform;
@ -10,7 +9,7 @@ mod platform;
pub use platform::PlatformSpecific; pub use platform::PlatformSpecific;
/// The settings of an application. /// The settings of an application.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Settings { pub struct Settings {
/// The [`Window`] settings /// The [`Window`] settings
/// ///
@ -18,6 +17,14 @@ pub struct Settings {
pub window: Window, pub window: Window,
} }
impl Default for Settings {
fn default() -> Settings {
Settings {
window: Window::default(),
}
}
}
/// The window settings of an application. /// The window settings of an application.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Window { pub struct Window {