Merge branch 'master' into paint-example

This commit is contained in:
Héctor Ramón Jiménez 2020-01-11 00:44:56 +01:00
commit de71776e02
93 changed files with 2376 additions and 657 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,
) )
@ -124,10 +127,7 @@ impl Sandbox for Example {
.max_width(500) .max_width(500)
.align_items(Align::Center) .align_items(Align::Center)
.push(Circle::new(self.radius)) .push(Circle::new(self.radius))
.push( .push(Text::new(format!("Radius: {}", self.radius.to_string())))
Text::new(format!("Radius: {}", self.radius.to_string()))
.width(Length::Shrink),
)
.push(Slider::new( .push(Slider::new(
&mut self.slider, &mut self.slider,
1.0..=100.0, 1.0..=100.0,

View File

@ -57,13 +57,9 @@ impl Application for Events {
fn view(&mut self) -> Element<Message> { fn view(&mut self) -> Element<Message> {
let events = self.last.iter().fold( let events = self.last.iter().fold(
Column::new().width(Length::Shrink).spacing(10), Column::new().spacing(10),
|column, event| { |column, event| {
column.push( column.push(Text::new(format!("{:?}", event)).size(40))
Text::new(format!("{:?}", event))
.size(40)
.width(Length::Shrink),
)
}, },
); );
@ -71,11 +67,9 @@ impl Application for Events {
self.enabled, self.enabled,
"Listen to runtime events", "Listen to runtime events",
Message::Toggled, Message::Toggled,
) );
.width(Length::Shrink);
let content = Column::new() let content = Column::new()
.width(Length::Shrink)
.align_items(Align::Center) .align_items(Align::Center)
.spacing(20) .spacing(20)
.push(events) .push(events)

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() {
@ -77,11 +77,8 @@ impl Application for Pokedex {
fn view(&mut self) -> Element<Message> { fn view(&mut self) -> Element<Message> {
let content = match self { let content = match self {
Pokedex::Loading => Column::new().width(Length::Shrink).push( Pokedex::Loading => Column::new()
Text::new("Searching for Pokémon...") .push(Text::new("Searching for Pokémon...").size(40)),
.width(Length::Shrink)
.size(40),
),
Pokedex::Loaded { pokemon, search } => Column::new() Pokedex::Loaded { pokemon, search } => Column::new()
.max_width(500) .max_width(500)
.spacing(20) .spacing(20)
@ -91,14 +88,9 @@ impl Application for Pokedex {
button(search, "Keep searching!").on_press(Message::Search), button(search, "Keep searching!").on_press(Message::Search),
), ),
Pokedex::Errored { try_again, .. } => Column::new() Pokedex::Errored { try_again, .. } => Column::new()
.width(Length::Shrink)
.spacing(20) .spacing(20)
.align_items(Align::End) .align_items(Align::End)
.push( .push(Text::new("Whoops! Something went wrong...").size(40))
Text::new("Whoops! Something went wrong...")
.width(Length::Shrink)
.size(40),
)
.push(button(try_again, "Try again").on_press(Message::Search)), .push(button(try_again, "Try again").on_press(Message::Search)),
}; };
@ -134,10 +126,13 @@ impl Pokemon {
Row::new() Row::new()
.align_items(Align::Center) .align_items(Align::Center)
.spacing(20) .spacing(20)
.push(Text::new(&self.name).size(30)) .push(
Text::new(&self.name)
.size(30)
.width(Length::Fill),
)
.push( .push(
Text::new(format!("#{}", self.number)) Text::new(format!("#{}", self.number))
.width(Length::Shrink)
.size(20) .size(20)
.color([0.5, 0.5, 0.5]), .color([0.5, 0.5, 0.5]),
), ),
@ -219,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};
@ -96,42 +95,38 @@ impl Application for Stopwatch {
seconds % MINUTE, seconds % MINUTE,
self.duration.subsec_millis() / 10, self.duration.subsec_millis() / 10,
)) ))
.width(Length::Shrink)
.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()
.width(Length::Shrink)
.spacing(20) .spacing(20)
.push(toggle_button) .push(toggle_button)
.push(reset_button); .push(reset_button);
let content = Column::new() let content = Column::new()
.width(Length::Shrink)
.align_items(Align::Center) .align_items(Align::Center)
.spacing(20) .spacing(20)
.push(duration) .push(duration)
@ -180,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

@ -25,13 +25,14 @@ impl Sandbox for Tiger {
let content = { let content = {
use iced::{Column, Svg}; use iced::{Column, Svg};
Column::new() Column::new().padding(20).push(
.width(Length::Shrink) Svg::new(format!(
.padding(20)
.push(Svg::new(format!(
"{}/examples/resources/tiger.svg", "{}/examples/resources/tiger.svg",
env!("CARGO_MANIFEST_DIR") env!("CARGO_MANIFEST_DIR")
))) ))
.width(Length::Fill)
.height(Length::Fill),
)
}; };
#[cfg(not(feature = "svg"))] #[cfg(not(feature = "svg"))]
@ -39,7 +40,6 @@ impl Sandbox for Tiger {
use iced::{HorizontalAlignment, Text}; use iced::{HorizontalAlignment, Text};
Text::new("You need to enable the `svg` feature!") Text::new("You need to enable the `svg` feature!")
.width(Length::Shrink)
.horizontal_alignment(HorizontalAlignment::Center) .horizontal_alignment(HorizontalAlignment::Center)
.size(30) .size(30)
}; };

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};
@ -146,6 +146,7 @@ impl Application for Todos {
.. ..
}) => { }) => {
let title = Text::new("todos") let title = Text::new("todos")
.width(Length::Fill)
.size(100) .size(100)
.color([0.5, 0.5, 0.5]) .color([0.5, 0.5, 0.5])
.horizontal_alignment(HorizontalAlignment::Center); .horizontal_alignment(HorizontalAlignment::Center);
@ -284,19 +285,18 @@ impl Task {
self.completed, self.completed,
&self.description, &self.description,
TaskMessage::Completed, TaskMessage::Completed,
); )
.width(Length::Fill);
Row::new() Row::new()
.spacing(20) .spacing(20)
.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()
} }
@ -322,17 +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( .push(Text::new("Delete")),
Text::new("Delete")
.width(Length::Shrink)
.color(Color::WHITE),
),
) )
.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()
} }
@ -358,18 +353,13 @@ impl Controls {
let tasks_left = tasks.iter().filter(|task| !task.completed).count(); let tasks_left = tasks.iter().filter(|task| !task.completed).count();
let filter_button = |state, label, filter, current_filter| { let filter_button = |state, label, filter, current_filter| {
let label = Text::new(label).size(16).width(Length::Shrink); 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()
@ -381,11 +371,11 @@ impl Controls {
tasks_left, tasks_left,
if tasks_left == 1 { "task" } else { "tasks" } if tasks_left == 1 { "task" } else { "tasks" }
)) ))
.width(Length::Fill)
.size(16), .size(16),
) )
.push( .push(
Row::new() Row::new()
.width(Length::Shrink)
.spacing(10) .spacing(10)
.push(filter_button( .push(filter_button(
all_button, all_button,
@ -562,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),
); );
} }
@ -401,6 +403,7 @@ impl<'a> Step {
)) ))
.push( .push(
Text::new(&value.to_string()) Text::new(&value.to_string())
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center), .horizontal_alignment(HorizontalAlignment::Center),
) )
} }
@ -447,6 +450,7 @@ impl<'a> Step {
)) ))
.push( .push(
Text::new(&format!("{} px", spacing)) Text::new(&format!("{} px", spacing))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center), .horizontal_alignment(HorizontalAlignment::Center),
); );
@ -561,6 +565,7 @@ impl<'a> Step {
)) ))
.push( .push(
Text::new(&format!("Width: {} px", width.to_string())) Text::new(&format!("Width: {} px", width.to_string()))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center), .horizontal_alignment(HorizontalAlignment::Center),
) )
} }
@ -580,6 +585,7 @@ impl<'a> Step {
.push(Column::new().height(Length::Units(4096))) .push(Column::new().height(Length::Units(4096)))
.push( .push(
Text::new("You are halfway there!") Text::new("You are halfway there!")
.width(Length::Fill)
.size(30) .size(30)
.horizontal_alignment(HorizontalAlignment::Center), .horizontal_alignment(HorizontalAlignment::Center),
) )
@ -587,6 +593,7 @@ impl<'a> Step {
.push(ferris(300)) .push(ferris(300))
.push( .push(
Text::new("You made it!") Text::new("You made it!")
.width(Length::Fill)
.size(50) .size(50)
.horizontal_alignment(HorizontalAlignment::Center), .horizontal_alignment(HorizontalAlignment::Center),
) )
@ -629,6 +636,7 @@ impl<'a> Step {
} else { } else {
value value
}) })
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center), .horizontal_alignment(HorizontalAlignment::Center),
) )
} }
@ -692,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,
@ -757,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

@ -1,4 +1,7 @@
use crate::input::{keyboard, mouse}; use crate::{
input::{keyboard, mouse},
window,
};
/// A user interface event. /// A user interface event.
/// ///
@ -13,4 +16,7 @@ pub enum Event {
/// A mouse event /// A mouse event
Mouse(mouse::Event), Mouse(mouse::Event),
/// A window event
Window(window::Event),
} }

View File

@ -77,7 +77,7 @@ where
let max_cross = axis.cross(limits.max()); let max_cross = axis.cross(limits.max());
let mut fill_sum = 0; let mut fill_sum = 0;
let mut cross = axis.cross(limits.min()); let mut cross = axis.cross(limits.min()).max(axis.cross(limits.fill()));
let mut available = axis.main(limits.max()) - total_spacing; let mut available = axis.main(limits.max()) - total_spacing;
let mut nodes: Vec<Node> = Vec::with_capacity(items.len()); let mut nodes: Vec<Node> = Vec::with_capacity(items.len());

View File

@ -44,6 +44,14 @@ impl Limits {
self.max self.max
} }
/// Returns the fill [`Size`] of the [`Limits`].
///
/// [`Limits`]: struct.Limits.html
/// [`Size`]: ../struct.Size.html
pub fn fill(&self) -> Size {
self.fill
}
/// Applies a width constraint to the current [`Limits`]. /// Applies a width constraint to the current [`Limits`].
/// ///
/// [`Limits`]: struct.Limits.html /// [`Limits`]: struct.Limits.html

View File

@ -44,6 +44,7 @@ pub mod layout;
pub mod renderer; pub mod renderer;
pub mod subscription; pub mod subscription;
pub mod widget; pub mod widget;
pub mod window;
mod clipboard; mod clipboard;
mod element; mod element;

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::Fill, 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

@ -33,7 +33,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
Column { Column {
spacing: 0, spacing: 0,
padding: 0, padding: 0,
width: Length::Fill, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
max_width: u32::MAX, max_width: u32::MAX,
max_height: u32::MAX, max_height: u32::MAX,
@ -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,
@ -95,6 +96,7 @@ where
let size = self::Renderer::default_size(renderer); let size = self::Renderer::default_size(renderer);
Row::<(), Renderer>::new() Row::<(), Renderer>::new()
.width(Length::Fill)
.spacing(15) .spacing(15)
.align_items(Align::Center) .align_items(Align::Center)
.push( .push(
@ -131,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 {
@ -143,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,
); );
@ -160,6 +164,7 @@ where
self.is_selected, self.is_selected,
is_mouse_over, is_mouse_over,
label, label,
&self.style,
) )
} }
@ -176,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
@ -196,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

@ -33,7 +33,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
Row { Row {
spacing: 0, spacing: 0,
padding: 0, padding: 0,
width: Length::Fill, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
max_width: u32::MAX, max_width: u32::MAX,
max_height: u32::MAX, max_height: u32::MAX,
@ -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

@ -30,7 +30,7 @@ impl Svg {
Svg { Svg {
handle: handle.into(), handle: handle.into(),
width: Length::Fill, width: Length::Fill,
height: Length::Fill, height: Length::Shrink,
} }
} }
@ -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

@ -41,7 +41,7 @@ impl Text {
size: None, size: None,
color: None, color: None,
font: Font::Default, font: Font::Default,
width: Length::Fill, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
horizontal_alignment: HorizontalAlignment::Left, horizontal_alignment: HorizontalAlignment::Left,
vertical_alignment: VerticalAlignment::Top, vertical_alignment: VerticalAlignment::Top,
@ -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:
@ -68,7 +70,7 @@ impl<'a, Message> TextInput<'a, Message> {
where where
F: 'static + Fn(String) -> Message, F: 'static + Fn(String) -> Message,
{ {
Self { TextInput {
state, state,
placeholder: String::from(placeholder), placeholder: String::from(placeholder),
value: Value::new(value), value: Value::new(value),
@ -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)
} }

4
native/src/window.rs Normal file
View File

@ -0,0 +1,4 @@
//! Build window-based GUI applications.
mod event;
pub use event::Event;

View File

@ -0,0 +1,12 @@
/// A window-related event.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Event {
/// A window was resized
Resized {
/// The new width of the window (in units)
width: u32,
/// The new height of the window (in units)
height: u32,
},
}

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

@ -39,7 +39,11 @@ cargo build --example tour --target wasm32-unknown-unknown
wasm-bindgen ../target/wasm32-unknown-unknown/debug/examples/tour.wasm --out-dir tour --web wasm-bindgen ../target/wasm32-unknown-unknown/debug/examples/tour.wasm --out-dir tour --web
``` ```
Then, we need to create an `.html` file to load our application: *__Note:__ Keep in mind that Iced is still in early exploration stages and most of the work needs to happen on the native side of the ecosystem. At this stage, it is important to be able to batch work without having to constantly jump back and forth. Because of this, there is currently no requirement for the `master` branch to contain a cross-platform API at all times. If you hit an issue when building an example and want to help, it may be a good way to [start contributing]!*
[start contributing]: ../CONTRIBUTING.md
Once the example is compiled, we need to create an `.html` file to load our application:
```html ```html
<!DOCTYPE html> <!DOCTYPE html>

View File

@ -28,7 +28,7 @@ impl<'a, Message> Column<'a, Message> {
Column { Column {
spacing: 0, spacing: 0,
padding: 0, padding: 0,
width: Length::Fill, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
max_width: u32::MAX, max_width: u32::MAX,
max_height: u32::MAX, max_height: u32::MAX,

View File

@ -28,7 +28,7 @@ impl<'a, Message> Row<'a, Message> {
Row { Row {
spacing: 0, spacing: 0,
padding: 0, padding: 0,
width: Length::Fill, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
max_width: u32::MAX, max_width: u32::MAX,
max_height: u32::MAX, max_height: u32::MAX,

View File

@ -36,7 +36,7 @@ impl Text {
size: None, size: None,
color: None, color: None,
font: Font::Default, font: Font::Default,
width: Length::Fill, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
horizontal_alignment: HorizontalAlignment::Left, horizontal_alignment: HorizontalAlignment::Left,
vertical_alignment: VerticalAlignment::Top, vertical_alignment: VerticalAlignment::Top,

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,9 +1,9 @@
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, window, Cache, Clipboard, Command, Container, Debug, Element,
Length, MouseCursor, Settings, Subscription, UserInterface, Event, Length, MouseCursor, Settings, Subscription, UserInterface,
}; };
/// An interactive, native cross-platform application. /// An interactive, native cross-platform application.
@ -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);
@ -371,10 +373,13 @@ pub trait Application: Sized {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
} }
WindowEvent::Resized(new_size) => { WindowEvent::Resized(new_size) => {
events.push(Event::Window(window::Event::Resized {
width: new_size.width.round() as u32,
height: new_size.height.round() as u32,
}));
size = new_size; size = new_size;
resized = true; resized = true;
log::debug!("Resized: {:?}", new_size);
} }
_ => {} _ => {}
}, },

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 {