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 = [
"core",
"native",
"style",
"web",
"wgpu",
"winit",

View File

@ -25,6 +25,21 @@ impl Color {
a: 1.0,
};
/// A color with no opacity.
pub const TRANSPARENT: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
};
/// Creates a [`Color`] from its RGB components.
///
/// [`Color`]: struct.Color.html
pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color {
Color { r, g, b, a: 1.0 }
}
/// Creates a [`Color`] from its RGB8 components.
///
/// [`Color`]: struct.Color.html
@ -37,13 +52,6 @@ impl Color {
}
}
/// Creates a [`Color`] from its RGB components.
///
/// [`Color`]: struct.Color.html
pub fn from_rgb(r: f32, g: f32, b: f32) -> Color {
Color { r, g, b, a: 1.0 }
}
/// Converts the [`Color`] into its linear values.
///
/// [`Color`]: struct.Color.html

View File

@ -31,3 +31,15 @@ where
Self::new(self.x + b.x, self.y + b.y)
}
}
impl<T> Default for Vector<T>
where
T: Default,
{
fn default() -> Self {
Self {
x: T::default(),
y: T::default(),
}
}
}

View File

@ -13,7 +13,7 @@ mod circle {
layout, Background, Color, Element, Hasher, Layout, Length,
MouseCursor, Point, Size, Widget,
};
use iced_wgpu::{Primitive, Renderer};
use iced_wgpu::{Defaults, Primitive, Renderer};
pub struct Circle {
radius: u16,
@ -54,6 +54,7 @@ mod circle {
fn draw(
&self,
_renderer: &mut Renderer,
_defaults: &Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> (Primitive, MouseCursor) {
@ -62,6 +63,8 @@ mod circle {
bounds: layout.bounds(),
background: Background::Color(Color::BLACK),
border_radius: self.radius,
border_width: 0,
border_color: Color::TRANSPARENT,
},
MouseCursor::OutOfBounds,
)
@ -124,10 +127,7 @@ impl Sandbox for Example {
.max_width(500)
.align_items(Align::Center)
.push(Circle::new(self.radius))
.push(
Text::new(format!("Radius: {}", self.radius.to_string()))
.width(Length::Shrink),
)
.push(Text::new(format!("Radius: {}", self.radius.to_string())))
.push(Slider::new(
&mut self.slider,
1.0..=100.0,

View File

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

View File

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

View File

@ -1,6 +1,6 @@
use iced::{
button, image, Align, Application, Button, Color, Column, Command,
Container, Element, Image, Length, Row, Settings, Text,
button, image, Align, Application, Button, Column, Command, Container,
Element, Image, Length, Row, Settings, Text,
};
pub fn main() {
@ -77,11 +77,8 @@ impl Application for Pokedex {
fn view(&mut self) -> Element<Message> {
let content = match self {
Pokedex::Loading => Column::new().width(Length::Shrink).push(
Text::new("Searching for Pokémon...")
.width(Length::Shrink)
.size(40),
),
Pokedex::Loading => Column::new()
.push(Text::new("Searching for Pokémon...").size(40)),
Pokedex::Loaded { pokemon, search } => Column::new()
.max_width(500)
.spacing(20)
@ -91,14 +88,9 @@ impl Application for Pokedex {
button(search, "Keep searching!").on_press(Message::Search),
),
Pokedex::Errored { try_again, .. } => Column::new()
.width(Length::Shrink)
.spacing(20)
.align_items(Align::End)
.push(
Text::new("Whoops! Something went wrong...")
.width(Length::Shrink)
.size(40),
)
.push(Text::new("Whoops! Something went wrong...").size(40))
.push(button(try_again, "Try again").on_press(Message::Search)),
};
@ -134,10 +126,13 @@ impl Pokemon {
Row::new()
.align_items(Align::Center)
.spacing(20)
.push(Text::new(&self.name).size(30))
.push(
Text::new(&self.name)
.size(30)
.width(Length::Fill),
)
.push(
Text::new(format!("#{}", self.number))
.width(Length::Shrink)
.size(20)
.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> {
Button::new(state, Text::new(text).color(Color::WHITE))
.background(Color::from_rgb(0.11, 0.42, 0.87))
.border_radius(10)
Button::new(state, Text::new(text))
.padding(10)
.style(style::Button::Primary)
}
mod style {
use iced::{button, Background, Color, Vector};
pub enum Button {
Primary,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
})),
border_radius: 12,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::WHITE,
..button::Style::default()
}
}
}
}

View File

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

View File

@ -1,7 +1,6 @@
use iced::{
button, Align, Application, Background, Button, Color, Column, Command,
Container, Element, HorizontalAlignment, Length, Row, Settings,
Subscription, Text,
button, Align, Application, Button, Column, Command, Container, Element,
HorizontalAlignment, Length, Row, Settings, Subscription, Text,
};
use std::time::{Duration, Instant};
@ -96,42 +95,38 @@ impl Application for Stopwatch {
seconds % MINUTE,
self.duration.subsec_millis() / 10,
))
.width(Length::Shrink)
.size(40);
let button = |state, label, color: [f32; 3]| {
let button = |state, label, style| {
Button::new(
state,
Text::new(label)
.color(Color::WHITE)
.horizontal_alignment(HorizontalAlignment::Center),
)
.min_width(80)
.background(Background::Color(color.into()))
.border_radius(10)
.padding(10)
.style(style)
};
let toggle_button = {
let (label, color) = match self.state {
State::Idle => ("Start", [0.11, 0.42, 0.87]),
State::Ticking { .. } => ("Stop", [0.9, 0.4, 0.4]),
State::Idle => ("Start", style::Button::Primary),
State::Ticking { .. } => ("Stop", style::Button::Destructive),
};
button(&mut self.toggle, label, color).on_press(Message::Toggle)
};
let reset_button = button(&mut self.reset, "Reset", [0.7, 0.7, 0.7])
.on_press(Message::Reset);
let reset_button =
button(&mut self.reset, "Reset", style::Button::Secondary)
.on_press(Message::Reset);
let controls = Row::new()
.width(Length::Shrink)
.spacing(20)
.push(toggle_button)
.push(reset_button);
let content = Column::new()
.width(Length::Shrink)
.align_items(Align::Center)
.spacing(20)
.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 = {
use iced::{Column, Svg};
Column::new()
.width(Length::Shrink)
.padding(20)
.push(Svg::new(format!(
Column::new().padding(20).push(
Svg::new(format!(
"{}/examples/resources/tiger.svg",
env!("CARGO_MANIFEST_DIR")
)))
))
.width(Length::Fill)
.height(Length::Fill),
)
};
#[cfg(not(feature = "svg"))]
@ -39,7 +40,6 @@ impl Sandbox for Tiger {
use iced::{HorizontalAlignment, Text};
Text::new("You need to enable the `svg` feature!")
.width(Length::Shrink)
.horizontal_alignment(HorizontalAlignment::Center)
.size(30)
};

View File

@ -1,7 +1,7 @@
use iced::{
button, scrollable, text_input, Align, Application, Button, Checkbox,
Color, Column, Command, Container, Element, Font, HorizontalAlignment,
Length, Row, Scrollable, Settings, Text, TextInput,
Column, Command, Container, Element, Font, HorizontalAlignment, Length,
Row, Scrollable, Settings, Text, TextInput,
};
use serde::{Deserialize, Serialize};
@ -146,6 +146,7 @@ impl Application for Todos {
..
}) => {
let title = Text::new("todos")
.width(Length::Fill)
.size(100)
.color([0.5, 0.5, 0.5])
.horizontal_alignment(HorizontalAlignment::Center);
@ -284,19 +285,18 @@ impl Task {
self.completed,
&self.description,
TaskMessage::Completed,
);
)
.width(Length::Fill);
Row::new()
.spacing(20)
.align_items(Align::Center)
.push(checkbox)
.push(
Button::new(
edit_button,
edit_icon().color([0.5, 0.5, 0.5]),
)
.on_press(TaskMessage::Edit)
.padding(10),
Button::new(edit_button, edit_icon())
.on_press(TaskMessage::Edit)
.padding(10)
.style(style::Button::Icon),
)
.into()
}
@ -322,17 +322,12 @@ impl Task {
delete_button,
Row::new()
.spacing(10)
.push(delete_icon().color(Color::WHITE))
.push(
Text::new("Delete")
.width(Length::Shrink)
.color(Color::WHITE),
),
.push(delete_icon())
.push(Text::new("Delete")),
)
.on_press(TaskMessage::Delete)
.padding(10)
.border_radius(5)
.background(Color::from_rgb(0.8, 0.2, 0.2)),
.style(style::Button::Destructive),
)
.into()
}
@ -358,18 +353,13 @@ impl Controls {
let tasks_left = tasks.iter().filter(|task| !task.completed).count();
let filter_button = |state, label, filter, current_filter| {
let label = Text::new(label).size(16).width(Length::Shrink);
let button = if filter == current_filter {
Button::new(state, label.color(Color::WHITE))
.background(Color::from_rgb(0.2, 0.2, 0.7))
} else {
Button::new(state, label)
};
let label = Text::new(label).size(16);
let button =
Button::new(state, label).style(style::Button::Filter {
selected: filter == current_filter,
});
button
.on_press(Message::FilterChanged(filter))
.padding(8)
.border_radius(10)
button.on_press(Message::FilterChanged(filter)).padding(8)
};
Row::new()
@ -381,11 +371,11 @@ impl Controls {
tasks_left,
if tasks_left == 1 { "task" } else { "tasks" }
))
.width(Length::Fill)
.size(16),
)
.push(
Row::new()
.width(Length::Shrink)
.spacing(10)
.push(filter_button(
all_button,
@ -562,3 +552,63 @@ impl SavedState {
Ok(())
}
}
mod style {
use iced::{button, Background, Color, Vector};
pub enum Button {
Filter { selected: bool },
Icon,
Destructive,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
match self {
Button::Filter { selected } => {
if *selected {
button::Style {
background: Some(Background::Color(
Color::from_rgb(0.2, 0.2, 0.7),
)),
border_radius: 10,
text_color: Color::WHITE,
..button::Style::default()
}
} else {
button::Style::default()
}
}
Button::Icon => button::Style {
text_color: Color::from_rgb(0.5, 0.5, 0.5),
..button::Style::default()
},
Button::Destructive => button::Style {
background: Some(Background::Color(Color::from_rgb(
0.8, 0.2, 0.2,
))),
border_radius: 5,
text_color: Color::WHITE,
shadow_offset: Vector::new(1.0, 1.0),
..button::Style::default()
},
}
}
fn hovered(&self) -> button::Style {
let active = self.active();
button::Style {
text_color: match self {
Button::Icon => Color::from_rgb(0.2, 0.2, 0.7),
Button::Filter { selected } if !selected => {
Color::from_rgb(0.2, 0.2, 0.7)
}
_ => active.text_color,
},
shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0),
..active
}
}
}
}

View File

@ -62,8 +62,9 @@ impl Sandbox for Tour {
if steps.has_previous() {
controls = controls.push(
secondary_button(back_button, "Back")
.on_press(Message::BackPressed),
button(back_button, "Back")
.on_press(Message::BackPressed)
.style(style::Button::Secondary),
);
}
@ -71,8 +72,9 @@ impl Sandbox for Tour {
if steps.can_continue() {
controls = controls.push(
primary_button(next_button, "Next")
.on_press(Message::NextPressed),
button(next_button, "Next")
.on_press(Message::NextPressed)
.style(style::Button::Primary),
);
}
@ -401,6 +403,7 @@ impl<'a> Step {
))
.push(
Text::new(&value.to_string())
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
)
}
@ -447,6 +450,7 @@ impl<'a> Step {
))
.push(
Text::new(&format!("{} px", spacing))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
);
@ -561,6 +565,7 @@ impl<'a> Step {
))
.push(
Text::new(&format!("Width: {} px", width.to_string()))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
)
}
@ -580,6 +585,7 @@ impl<'a> Step {
.push(Column::new().height(Length::Units(4096)))
.push(
Text::new("You are halfway there!")
.width(Length::Fill)
.size(30)
.horizontal_alignment(HorizontalAlignment::Center),
)
@ -587,6 +593,7 @@ impl<'a> Step {
.push(ferris(300))
.push(
Text::new("You made it!")
.width(Length::Fill)
.size(50)
.horizontal_alignment(HorizontalAlignment::Center),
)
@ -629,6 +636,7 @@ impl<'a> Step {
} else {
value
})
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
)
}
@ -692,29 +700,12 @@ fn button<'a, Message>(
) -> Button<'a, Message> {
Button::new(
state,
Text::new(label)
.color(Color::WHITE)
.horizontal_alignment(HorizontalAlignment::Center),
Text::new(label).horizontal_alignment(HorizontalAlignment::Center),
)
.padding(12)
.border_radius(12)
.min_width(100)
}
fn primary_button<'a, Message>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
button(state, label).background(Color::from_rgb(0.11, 0.42, 0.87))
}
fn secondary_button<'a, Message>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
button(state, label).background(Color::from_rgb(0.4, 0.4, 0.4))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language {
Rust,
@ -757,6 +748,38 @@ pub enum Layout {
Column,
}
mod style {
use iced::{button, Background, Color, Vector};
pub enum Button {
Primary,
Secondary,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5),
})),
border_radius: 12,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
..button::Style::default()
}
}
fn hovered(&self) -> button::Style {
button::Style {
text_color: Color::WHITE,
shadow_offset: Vector::new(1.0, 2.0),
..self.active()
}
}
}
}
// This should be gracefully handled by Iced in the future. Probably using our
// own proc macro, or maybe the whole process is streamlined by `wasm-pack` at
// some point.

View File

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

View File

@ -1,4 +1,7 @@
use crate::input::{keyboard, mouse};
use crate::{
input::{keyboard, mouse},
window,
};
/// A user interface event.
///
@ -13,4 +16,7 @@ pub enum Event {
/// A 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 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 nodes: Vec<Node> = Vec::with_capacity(items.len());

View File

@ -44,6 +44,14 @@ impl Limits {
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`].
///
/// [`Limits`]: struct.Limits.html

View File

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

View File

@ -21,14 +21,15 @@
//! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html
mod debugger;
#[cfg(debug_assertions)]
mod null;
mod windowed;
pub use debugger::Debugger;
pub use windowed::{Target, Windowed};
#[cfg(debug_assertions)]
mod null;
#[cfg(debug_assertions)]
pub use null::Null;
pub use windowed::{Target, Windowed};
use crate::{layout, Element};
@ -43,6 +44,13 @@ pub trait Renderer: Sized {
/// [`Renderer`]: trait.Renderer.html
type Output;
/// The default styling attributes of the [`Renderer`].
///
/// This type can be leveraged to implement style inheritance.
///
/// [`Renderer`]: trait.Renderer.html
type Defaults: Default;
/// Lays out the elements of a user interface.
///
/// You should override this if you need to perform any operations before or

View File

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

View File

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

View File

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

View File

@ -43,24 +43,7 @@ where
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
/// # pub struct Renderer;
/// #
/// # impl Renderer {
/// # pub fn new() -> Self { Renderer }
/// # }
/// #
/// # impl iced_native::Renderer for Renderer { type Output = (); }
/// #
/// # impl iced_native::column::Renderer for Renderer {
/// # fn draw<Message>(
/// # &mut self,
/// # _children: &[iced_native::Element<'_, Message, Self>],
/// # _layout: iced_native::Layout<'_>,
/// # _cursor_position: iced_native::Point,
/// # ) -> Self::Output {
/// # ()
/// # }
/// # }
/// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use iced_native::Column;
@ -139,24 +122,7 @@ where
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
/// # pub struct Renderer;
/// #
/// # impl Renderer {
/// # pub fn new() -> Self { Renderer }
/// # }
/// #
/// # impl iced_native::Renderer for Renderer { type Output = (); }
/// #
/// # impl iced_native::column::Renderer for Renderer {
/// # fn draw<Message>(
/// # &mut self,
/// # _children: &[iced_native::Element<'_, Message, Self>],
/// # _layout: iced_native::Layout<'_>,
/// # _cursor_position: iced_native::Point,
/// # ) -> Self::Output {
/// # ()
/// # }
/// # }
/// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use iced_native::Column;
@ -241,24 +207,7 @@ where
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
/// # pub struct Renderer;
/// #
/// # impl Renderer {
/// # pub fn new() -> Self { Renderer }
/// # }
/// #
/// # impl iced_native::Renderer for Renderer { type Output = (); }
/// #
/// # impl iced_native::column::Renderer for Renderer {
/// # fn draw<Message>(
/// # &mut self,
/// # _children: &[iced_native::Element<'_, Message, Self>],
/// # _layout: iced_native::Layout<'_>,
/// # _cursor_position: iced_native::Point,
/// # ) -> Self::Output {
/// # ()
/// # }
/// # }
/// # pub use iced_native::renderer::Null as Renderer;
/// # }
/// #
/// # use iced_native::Column;
@ -304,6 +253,7 @@ where
pub fn draw(&self, renderer: &mut Renderer) -> Renderer::Output {
self.root.widget.draw(
renderer,
&Renderer::Defaults::default(),
Layout::new(&self.layout),
self.cursor_position,
)

View File

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

View File

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

View File

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

View File

@ -33,7 +33,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
Column {
spacing: 0,
padding: 0,
width: Length::Fill,
width: Length::Shrink,
height: Length::Shrink,
max_width: u32::MAX,
max_height: u32::MAX,
@ -173,10 +173,11 @@ where
fn draw(
&self,
renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
renderer.draw(&self.children, layout, cursor_position)
renderer.draw(defaults, &self.children, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
@ -213,6 +214,7 @@ pub trait Renderer: crate::Renderer + Sized {
/// [`Layout`]: ../layout/struct.Layout.html
fn draw<Message>(
&mut self,
defaults: &Self::Defaults,
content: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,

View File

@ -3,7 +3,7 @@ use std::hash::Hash;
use crate::{
layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point,
Widget,
Rectangle, Widget,
};
use std::u32;
@ -12,17 +12,21 @@ use std::u32;
///
/// It is normally used for alignment purposes.
#[allow(missing_debug_implementations)]
pub struct Container<'a, Message, Renderer> {
pub struct Container<'a, Message, Renderer: self::Renderer> {
width: Length,
height: Length,
max_width: u32,
max_height: u32,
horizontal_alignment: Align,
vertical_alignment: Align,
style: Renderer::Style,
content: Element<'a, Message, Renderer>,
}
impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
impl<'a, Message, Renderer> Container<'a, Message, Renderer>
where
Renderer: self::Renderer,
{
/// Creates an empty [`Container`].
///
/// [`Container`]: struct.Container.html
@ -37,6 +41,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
max_height: u32::MAX,
horizontal_alignment: Align::Start,
vertical_alignment: Align::Start,
style: Renderer::Style::default(),
content: content.into(),
}
}
@ -78,7 +83,6 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
/// [`Container`]: struct.Container.html
pub fn center_x(mut self) -> Self {
self.horizontal_alignment = Align::Center;
self
}
@ -87,7 +91,14 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
/// [`Container`]: struct.Container.html
pub fn center_y(mut self) -> Self {
self.vertical_alignment = Align::Center;
self
}
/// Sets the style of the [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
self.style = style.into();
self
}
}
@ -95,7 +106,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Container<'a, Message, Renderer>
where
Renderer: crate::Renderer,
Renderer: self::Renderer,
{
fn width(&self) -> Length {
self.width
@ -147,13 +158,17 @@ where
fn draw(
&self,
renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
self.content.draw(
renderer,
layout.children().next().unwrap(),
renderer.draw(
defaults,
layout.bounds(),
cursor_position,
&self.style,
&self.content,
layout.children().next().unwrap(),
)
}
@ -168,10 +183,35 @@ where
}
}
/// The renderer of a [`Container`].
///
/// Your [renderer] will need to implement this trait before being
/// able to use a [`Container`] in your user interface.
///
/// [`Container`]: struct.Container.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
/// The style supported by this renderer.
type Style: Default;
/// Draws a [`Container`].
///
/// [`Container`]: struct.Container.html
fn draw<Message>(
&mut self,
defaults: &Self::Defaults,
bounds: Rectangle,
cursor_position: Point,
style: &Self::Style,
content: &Element<'_, Message, Self>,
content_layout: Layout<'_>,
) -> Self::Output;
}
impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a + crate::Renderer,
Renderer: 'a + self::Renderer,
Message: 'static,
{
fn from(

View File

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

View File

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

View File

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

View File

@ -33,7 +33,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
Row {
spacing: 0,
padding: 0,
width: Length::Fill,
width: Length::Shrink,
height: Length::Shrink,
max_width: u32::MAX,
max_height: u32::MAX,
@ -174,10 +174,11 @@ where
fn draw(
&self,
renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
renderer.draw(&self.children, layout, cursor_position)
renderer.draw(defaults, &self.children, layout, cursor_position)
}
fn hash_layout(&self, state: &mut Hasher) {
@ -215,6 +216,7 @@ pub trait Renderer: crate::Renderer + Sized {
/// [`Layout`]: ../layout/struct.Layout.html
fn draw<Message>(
&mut self,
defaults: &Self::Defaults,
children: &[Element<'_, Message, Self>],
layout: Layout<'_>,
cursor_position: Point,

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ impl Svg {
Svg {
handle: handle.into(),
width: Length::Fill,
height: Length::Fill,
height: Length::Shrink,
}
}
@ -91,6 +91,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
_defaults: &Renderer::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {

View File

@ -41,7 +41,7 @@ impl Text {
size: None,
color: None,
font: Font::Default,
width: Length::Fill,
width: Length::Shrink,
height: Length::Shrink,
horizontal_alignment: HorizontalAlignment::Left,
vertical_alignment: VerticalAlignment::Top,
@ -146,10 +146,12 @@ where
fn draw(
&self,
renderer: &mut Renderer,
defaults: &Renderer::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
renderer.draw(
defaults,
layout.bounds(),
&self.content,
self.size.unwrap_or(renderer.default_size()),
@ -209,6 +211,7 @@ pub trait Renderer: crate::Renderer {
/// [`VerticalAlignment`]: enum.VerticalAlignment.html
fn draw(
&mut self,
defaults: &Self::Defaults,
bounds: Rectangle,
content: &str,
size: u16,

View File

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

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,
{
#[cfg(not(target_arch = "wasm32"))]
<Instance<Self> as iced_winit::Application>::run(_settings.into());
<Instance<Self> as iced_winit::Application>::run(
_settings.into(),
iced_wgpu::Settings {
default_font: _settings.default_font,
},
);
#[cfg(target_arch = "wasm32")]
<Instance<Self> as iced_web::Application>::run();

View File

@ -1,6 +1,6 @@
pub use iced_winit::{
Align, Background, Color, Command, Font, HorizontalAlignment, Length,
Space, Subscription, VerticalAlignment,
Space, Subscription, Vector, VerticalAlignment,
};
pub mod widget {
@ -22,58 +22,7 @@ pub mod widget {
//!
//! [`TextInput`]: text_input/struct.TextInput.html
//! [`text_input::State`]: text_input/struct.State.html
pub mod button {
//! Allow your users to perform actions by pressing a button.
//!
//! A [`Button`] has some local [`State`].
//!
//! [`Button`]: type.Button.html
//! [`State`]: struct.State.html
/// A widget that produces a message when clicked.
///
/// This is an alias of an `iced_native` button with a default
/// `Renderer`.
pub type Button<'a, Message> =
iced_winit::Button<'a, Message, iced_wgpu::Renderer>;
pub use iced_winit::button::State;
}
pub mod scrollable {
//! Navigate an endless amount of content with a scrollbar.
/// A widget that can vertically display an infinite amount of content
/// with a scrollbar.
///
/// This is an alias of an `iced_native` scrollable with a default
/// `Renderer`.
pub type Scrollable<'a, Message> =
iced_winit::Scrollable<'a, Message, iced_wgpu::Renderer>;
pub use iced_winit::scrollable::State;
}
pub mod text_input {
//! Ask for information using text fields.
//!
//! A [`TextInput`] has some local [`State`].
//!
//! [`TextInput`]: struct.TextInput.html
//! [`State`]: struct.State.html
pub use iced_winit::text_input::{State, TextInput};
}
pub mod slider {
//! Display an interactive selector of a single value from a range of
//! values.
//!
//! A [`Slider`] has some local [`State`].
//!
//! [`Slider`]: struct.Slider.html
//! [`State`]: struct.State.html
pub use iced_winit::slider::{Slider, State};
}
pub use iced_wgpu::widget::*;
pub mod image {
//! Display images in your user interface.
@ -85,12 +34,13 @@ pub mod widget {
pub use iced_winit::svg::{Handle, Svg};
}
pub use iced_winit::{Checkbox, ProgressBar, Radio, Text};
pub use iced_winit::Text;
#[doc(no_inline)]
pub use {
button::Button, image::Image, scrollable::Scrollable, slider::Slider,
svg::Svg, text_input::TextInput,
button::Button, checkbox::Checkbox, container::Container, image::Image,
progress_bar::ProgressBar, radio::Radio, scrollable::Scrollable,
slider::Slider, svg::Svg, text_input::TextInput,
};
/// A container that distributes its contents vertically.
@ -104,13 +54,6 @@ pub mod widget {
/// This is an alias of an `iced_native` row with a default `Renderer`.
pub type Row<'a, Message> =
iced_winit::Row<'a, Message, iced_wgpu::Renderer>;
/// An element decorating some content.
///
/// This is an alias of an `iced_native` container with a default
/// `Renderer`.
pub type Container<'a, Message> =
iced_winit::Container<'a, Message, iced_wgpu::Renderer>;
}
#[doc(no_inline)]

View File

@ -9,6 +9,12 @@ pub struct Settings {
///
/// [`Window`]: struct.Window.html
pub window: Window,
/// The bytes of the font that will be used by default.
///
/// If `None` is provided, a default system font will be chosen.
// TODO: Add `name` for web compatibility
pub default_font: Option<&'static [u8]>,
}
/// The window settings of an application.

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
```
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
<!DOCTYPE html>

View File

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

View File

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

View File

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

View File

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

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(unsafe_code)]
#![deny(rust_2018_idioms)]
pub mod defaults;
pub mod triangle;
pub mod widget;
mod image;
mod primitive;
mod quad;
mod renderer;
mod settings;
mod text;
mod transformation;
pub(crate) use crate::image::Image;
pub(crate) use quad::Quad;
pub(crate) use transformation::Transformation;
pub use defaults::Defaults;
pub use primitive::Primitive;
pub use renderer::{Renderer, Target};
pub use settings::Settings;
#[doc(no_inline)]
pub use widget::*;
pub(crate) use self::image::Image;
pub(crate) use quad::Quad;
pub(crate) use transformation::Transformation;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -1,10 +1,14 @@
use crate::{Primitive, Renderer};
use iced_native::{scrollable, Background, MouseCursor, Rectangle, Vector};
use iced_native::{
scrollable, Background, Color, MouseCursor, Rectangle, Vector,
};
const SCROLLBAR_WIDTH: u16 = 10;
const SCROLLBAR_MARGIN: u16 = 2;
impl scrollable::Renderer for Renderer {
type Style = Box<dyn iced_style::scrollable::StyleSheet>;
fn scrollbar(
&self,
bounds: Rectangle,
@ -51,6 +55,7 @@ impl scrollable::Renderer for Renderer {
is_mouse_over_scrollbar: bool,
scrollbar: Option<scrollable::Scrollbar>,
offset: u32,
style_sheet: &Self::Style,
(content, mouse_cursor): Self::Output,
) -> Self::Output {
let clip = Primitive::Clip {
@ -61,40 +66,53 @@ impl scrollable::Renderer for Renderer {
(
if let Some(scrollbar) = scrollbar {
if is_mouse_over || state.is_scroller_grabbed() {
let scroller = Primitive::Quad {
let style = if state.is_scroller_grabbed() {
style_sheet.dragging()
} else if is_mouse_over_scrollbar {
style_sheet.hovered()
} else {
style_sheet.active()
};
let is_scrollbar_visible =
style.background.is_some() || style.border_width > 0;
let scroller = if is_mouse_over
|| state.is_scroller_grabbed()
|| is_scrollbar_visible
{
Primitive::Quad {
bounds: scrollbar.scroller.bounds,
background: Background::Color(
[0.0, 0.0, 0.0, 0.7].into(),
),
border_radius: 5,
};
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],
}
background: Background::Color(style.scroller.color),
border_radius: style.scroller.border_radius,
border_width: style.scroller.border_width,
border_color: style.scroller.border_color,
}
} else {
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 {
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};
const HANDLE_WIDTH: f32 = 8.0;
const HANDLE_HEIGHT: f32 = 22.0;
impl slider::Renderer for Renderer {
type Style = Box<dyn StyleSheet>;
fn height(&self) -> u32 {
30
}
@ -16,9 +20,18 @@ impl slider::Renderer for Renderer {
range: std::ops::RangeInclusive<f32>,
value: f32,
is_dragging: bool,
style_sheet: &Self::Style,
) -> Self::Output {
let is_mouse_over = bounds.contains(cursor_position);
let style = if is_dragging {
style_sheet.dragging()
} else if is_mouse_over {
style_sheet.hovered()
} else {
style_sheet.active()
};
let rail_y = bounds.y + (bounds.height / 2.0).round();
let (rail_top, rail_bottom) = (
@ -29,8 +42,10 @@ impl slider::Renderer for Renderer {
width: bounds.width,
height: 2.0,
},
background: Color::from_rgb(0.6, 0.6, 0.6).into(),
background: Background::Color(style.rail_colors.0),
border_radius: 0,
border_width: 0,
border_color: Color::TRANSPARENT,
},
Primitive::Quad {
bounds: Rectangle {
@ -39,51 +54,45 @@ impl slider::Renderer for Renderer {
width: bounds.width,
height: 2.0,
},
background: Background::Color(Color::WHITE),
background: Background::Color(style.rail_colors.1),
border_radius: 0,
border_width: 0,
border_color: Color::TRANSPARENT,
},
);
let (range_start, range_end) = range.into_inner();
let handle_offset = (bounds.width - HANDLE_WIDTH)
let (handle_width, handle_height, handle_border_radius) =
match style.handle.shape {
HandleShape::Circle { radius } => {
(f32::from(radius * 2), f32::from(radius * 2), radius)
}
HandleShape::Rectangle {
width,
border_radius,
} => (f32::from(width), HANDLE_HEIGHT, border_radius),
};
let handle_offset = (bounds.width - handle_width)
* ((value - range_start) / (range_end - range_start).max(1.0));
let (handle_border, handle) = (
Primitive::Quad {
bounds: Rectangle {
x: bounds.x + handle_offset.round() - 1.0,
y: rail_y - HANDLE_HEIGHT / 2.0 - 1.0,
width: HANDLE_WIDTH + 2.0,
height: HANDLE_HEIGHT + 2.0,
},
background: Color::from_rgb(0.6, 0.6, 0.6).into(),
border_radius: 5,
let handle = Primitive::Quad {
bounds: Rectangle {
x: bounds.x + handle_offset.round(),
y: rail_y - handle_height / 2.0,
width: handle_width,
height: handle_height,
},
Primitive::Quad {
bounds: Rectangle {
x: bounds.x + handle_offset.round(),
y: rail_y - HANDLE_HEIGHT / 2.0,
width: HANDLE_WIDTH,
height: HANDLE_HEIGHT,
},
background: Background::Color(
if is_dragging {
[0.85, 0.85, 0.85]
} else if is_mouse_over {
[0.90, 0.90, 0.90]
} else {
[0.95, 0.95, 0.95]
}
.into(),
),
border_radius: 4,
},
);
background: Background::Color(style.handle.color),
border_radius: handle_border_radius,
border_width: style.handle.border_width,
border_color: style.handle.border_color,
};
(
Primitive::Group {
primitives: vec![rail_top, rail_bottom, handle_border, handle],
primitives: vec![rail_top, rail_bottom, handle],
},
if is_dragging {
MouseCursor::Grabbing

View File

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

View File

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

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
layout(location = 0) in vec4 v_Color;
layout(location = 1) in vec2 v_Pos;
layout(location = 2) in vec2 v_Scale;
layout(location = 3) in float v_BorderRadius;
layout(location = 1) in vec4 v_BorderColor;
layout(location = 2) in vec2 v_Pos;
layout(location = 3) in vec2 v_Scale;
layout(location = 4) in float v_BorderRadius;
layout(location = 5) in float v_BorderWidth;
layout(location = 0) out vec4 o_Color;
float rounded(in vec2 frag_coord, in vec2 position, in vec2 size, float radius, float s)
float distance(in vec2 frag_coord, in vec2 position, in vec2 size, float radius)
{
// TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN
vec2 inner_size = size - vec2(radius, radius) * 2.0;
vec2 top_left = position + vec2(radius, radius);
vec2 bottom_right = top_left + inner_size;
@ -21,13 +24,43 @@ float rounded(in vec2 frag_coord, in vec2 position, in vec2 size, float radius,
max(max(top_left_distance.y, bottom_right_distance.y), 0)
);
float d = sqrt(distance.x * distance.x + distance.y * distance.y);
return 1.0 - smoothstep(radius - s, radius + s, d);
return sqrt(distance.x * distance.x + distance.y * distance.y);
}
void main() {
float radius_alpha = rounded(gl_FragCoord.xy, v_Pos, v_Scale, v_BorderRadius, 0.5);
vec4 mixed_color;
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 = 2) in vec2 i_Scale;
layout(location = 3) in vec4 i_Color;
layout(location = 4) in float i_BorderRadius;
layout(location = 4) in vec4 i_BorderColor;
layout(location = 5) in float i_BorderRadius;
layout(location = 6) in float i_BorderWidth;
layout (set = 0, binding = 0) uniform Globals {
mat4 u_Transform;
@ -12,9 +14,11 @@ layout (set = 0, binding = 0) uniform Globals {
};
layout(location = 0) out vec4 o_Color;
layout(location = 1) out vec2 o_Pos;
layout(location = 2) out vec2 o_Scale;
layout(location = 3) out float o_BorderRadius;
layout(location = 1) out vec4 o_BorderColor;
layout(location = 2) out vec2 o_Pos;
layout(location = 3) out vec2 o_Scale;
layout(location = 4) out float o_BorderRadius;
layout(location = 5) out float o_BorderWidth;
void main() {
vec2 p_Pos = i_Pos * u_Scale;
@ -28,9 +32,11 @@ void main() {
);
o_Color = i_Color;
o_BorderColor = i_BorderColor;
o_Pos = p_Pos;
o_Scale = p_Scale;
o_BorderRadius = i_BorderRadius * u_Scale;
o_BorderWidth = i_BorderWidth * u_Scale;
gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0);
}

Binary file not shown.

View File

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

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"
license = "MIT"
repository = "https://github.com/hecrj/iced"
documentation = "https://docs.rs/iced_winit"
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
categories = ["gui"]
[features]
debug = []

View File

@ -1,9 +1,9 @@
use crate::{
conversion,
container, conversion,
input::{keyboard, mouse},
renderer::{Target, Windowed},
subscription, Cache, Clipboard, Command, Container, Debug, Element, Event,
Length, MouseCursor, Settings, Subscription, UserInterface,
subscription, window, Cache, Clipboard, Command, Container, Debug, Element,
Event, Length, MouseCursor, Settings, Subscription, UserInterface,
};
/// An interactive, native cross-platform application.
@ -18,7 +18,7 @@ pub trait Application: Sized {
/// The renderer to use to draw the [`Application`].
///
/// [`Application`]: trait.Application.html
type Renderer: Windowed;
type Renderer: Windowed + container::Renderer;
/// The type of __messages__ your [`Application`] will produce.
///
@ -81,8 +81,10 @@ pub trait Application: Sized {
/// It should probably be that last thing you call in your `main` function.
///
/// [`Application`]: trait.Application.html
fn run(settings: Settings)
where
fn run(
settings: Settings,
renderer_settings: <Self::Renderer as Windowed>::Settings,
) where
Self: 'static,
{
use winit::{
@ -140,7 +142,7 @@ pub trait Application: Sized {
let mut resized = false;
let clipboard = Clipboard::new(&window);
let mut renderer = Self::Renderer::new();
let mut renderer = Self::Renderer::new(renderer_settings);
let mut target = {
let (width, height) = to_physical(size, dpi);
@ -371,10 +373,13 @@ pub trait Application: Sized {
*control_flow = ControlFlow::Exit;
}
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;
resized = true;
log::debug!("Resized: {:?}", new_size);
}
_ => {}
},

View File

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