Merge branch 'master' into paint-example
This commit is contained in:
commit
de71776e02
@ -24,6 +24,7 @@ maintenance = { status = "actively-developed" }
|
||||
members = [
|
||||
"core",
|
||||
"native",
|
||||
"style",
|
||||
"web",
|
||||
"wgpu",
|
||||
"winit",
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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])
|
||||
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
514
examples/styling.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
};
|
||||
|
@ -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]),
|
||||
)
|
||||
Button::new(edit_button, edit_icon())
|
||||
.on_press(TaskMessage::Edit)
|
||||
.padding(10),
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -44,6 +44,7 @@ pub mod layout;
|
||||
pub mod renderer;
|
||||
pub mod subscription;
|
||||
pub mod widget;
|
||||
pub mod window;
|
||||
|
||||
mod clipboard;
|
||||
mod element;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -107,6 +107,7 @@ where
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
defaults: &Renderer::Defaults,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) -> Renderer::Output;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -95,6 +95,7 @@ where
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
_defaults: &Renderer::Defaults,
|
||||
layout: Layout<'_>,
|
||||
_cursor_position: Point,
|
||||
) -> Renderer::Output {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ where
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
_defaults: &Renderer::Defaults,
|
||||
layout: Layout<'_>,
|
||||
_cursor_position: Point,
|
||||
) -> Renderer::Output {
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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
4
native/src/window.rs
Normal file
@ -0,0 +1,4 @@
|
||||
//! Build window-based GUI applications.
|
||||
mod event;
|
||||
|
||||
pub use event::Event;
|
12
native/src/window/event.rs
Normal file
12
native/src/window/event.rs
Normal 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,
|
||||
},
|
||||
}
|
@ -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();
|
||||
|
@ -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)]
|
||||
|
@ -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
14
style/Cargo.toml
Normal 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
96
style/src/button.rs
Normal 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
55
style/src/checkbox.rs
Normal 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
59
style/src/container.rs
Normal 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
8
style/src/lib.rs
Normal 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
42
style/src/progress_bar.rs
Normal 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
53
style/src/radio.rs
Normal 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
76
style/src/scrollable.rs
Normal 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
95
style/src/slider.rs
Normal 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
83
style/src/text_input.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
32
wgpu/src/defaults.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
mod button;
|
||||
mod checkbox;
|
||||
mod column;
|
||||
mod container;
|
||||
mod image;
|
||||
mod progress_bar;
|
||||
mod radio;
|
||||
|
@ -1,54 +1,86 @@
|
||||
use crate::{Primitive, Renderer};
|
||||
use iced_native::{button, Background, MouseCursor, Point, Rectangle};
|
||||
use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer};
|
||||
use iced_native::{
|
||||
Background, Color, Element, Layout, MouseCursor, Point, Rectangle, Vector,
|
||||
};
|
||||
|
||||
impl button::Renderer for Renderer {
|
||||
fn draw(
|
||||
impl iced_native::button::Renderer for Renderer {
|
||||
type Style = Box<dyn StyleSheet>;
|
||||
|
||||
fn draw<Message>(
|
||||
&mut self,
|
||||
defaults: &Defaults,
|
||||
bounds: Rectangle,
|
||||
cursor_position: Point,
|
||||
is_disabled: bool,
|
||||
is_pressed: bool,
|
||||
background: Option<Background>,
|
||||
border_radius: u16,
|
||||
(content, _): Self::Output,
|
||||
style: &Box<dyn StyleSheet>,
|
||||
content: &Element<'_, Message, Self>,
|
||||
content_layout: Layout<'_>,
|
||||
) -> Self::Output {
|
||||
let is_mouse_over = bounds.contains(cursor_position);
|
||||
|
||||
// TODO: Render proper shadows
|
||||
// TODO: Make hovering and pressed styles configurable
|
||||
let shadow_offset = if is_mouse_over {
|
||||
let styling = if is_disabled {
|
||||
style.disabled()
|
||||
} else if is_mouse_over {
|
||||
if is_pressed {
|
||||
0.0
|
||||
style.pressed()
|
||||
} else {
|
||||
2.0
|
||||
style.hovered()
|
||||
}
|
||||
} else {
|
||||
1.0
|
||||
style.active()
|
||||
};
|
||||
|
||||
let (content, _) = content.draw(
|
||||
self,
|
||||
&Defaults {
|
||||
text: defaults::Text {
|
||||
color: styling.text_color,
|
||||
},
|
||||
..*defaults
|
||||
},
|
||||
content_layout,
|
||||
cursor_position,
|
||||
);
|
||||
|
||||
(
|
||||
match background {
|
||||
None => content,
|
||||
Some(background) => Primitive::Group {
|
||||
primitives: vec![
|
||||
Primitive::Quad {
|
||||
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 + 1.0,
|
||||
y: bounds.y + shadow_offset,
|
||||
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,
|
||||
},
|
||||
Primitive::Quad {
|
||||
bounds,
|
||||
background,
|
||||
border_radius,
|
||||
},
|
||||
content,
|
||||
],
|
||||
},
|
||||
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
|
||||
|
@ -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]
|
||||
let style = if is_mouse_over {
|
||||
style_sheet.hovered(is_checked)
|
||||
} else {
|
||||
[0.95, 0.95, 0.95]
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
border_radius: 5,
|
||||
},
|
||||
);
|
||||
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 {
|
||||
|
@ -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;
|
||||
|
49
wgpu/src/renderer/widget/container.rs
Normal file
49
wgpu/src/renderer/widget/container.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
(
|
||||
if active_progress_width > 0.0 {
|
||||
let bar = 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,
|
||||
background: style.bar,
|
||||
border_radius: style.border_radius,
|
||||
border_width: 0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
};
|
||||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: vec![background, active_progress],
|
||||
primitives: vec![background, bar],
|
||||
}
|
||||
} else {
|
||||
background
|
||||
},
|
||||
MouseCursor::OutOfBounds,
|
||||
)
|
||||
|
@ -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]
|
||||
let style = if is_mouse_over {
|
||||
style_sheet.hovered()
|
||||
} else {
|
||||
[0.95, 0.95, 0.95]
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
border_radius: (SIZE / 2.0 - 1.0) as u16,
|
||||
},
|
||||
);
|
||||
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 {
|
||||
|
@ -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;
|
||||
|
@ -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,41 +66,54 @@ impl scrollable::Renderer for Renderer {
|
||||
|
||||
(
|
||||
if let Some(scrollbar) = scrollbar {
|
||||
if is_mouse_over || state.is_scroller_grabbed() {
|
||||
let scroller = Primitive::Quad {
|
||||
bounds: scrollbar.scroller.bounds,
|
||||
background: Background::Color(
|
||||
[0.0, 0.0, 0.0, 0.7].into(),
|
||||
),
|
||||
border_radius: 5,
|
||||
let style = if state.is_scroller_grabbed() {
|
||||
style_sheet.dragging()
|
||||
} else if is_mouse_over_scrollbar {
|
||||
style_sheet.hovered()
|
||||
} else {
|
||||
style_sheet.active()
|
||||
};
|
||||
|
||||
if is_mouse_over_scrollbar || state.is_scroller_grabbed() {
|
||||
let scrollbar = Primitive::Quad {
|
||||
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(style.scroller.color),
|
||||
border_radius: style.scroller.border_radius,
|
||||
border_width: style.scroller.border_width,
|
||||
border_color: style.scroller.border_color,
|
||||
}
|
||||
} else {
|
||||
Primitive::None
|
||||
};
|
||||
|
||||
let scrollbar = if is_scrollbar_visible {
|
||||
Primitive::Quad {
|
||||
bounds: Rectangle {
|
||||
x: scrollbar.bounds.x
|
||||
+ f32::from(SCROLLBAR_MARGIN),
|
||||
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,
|
||||
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 {
|
||||
Primitive::Group {
|
||||
primitives: vec![clip, scroller],
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clip
|
||||
}
|
||||
} else {
|
||||
clip
|
||||
},
|
||||
|
@ -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,
|
||||
},
|
||||
Primitive::Quad {
|
||||
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,
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -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]
|
||||
let style = if state.is_focused() {
|
||||
style_sheet.focused()
|
||||
} else if is_mouse_over {
|
||||
style_sheet.hovered()
|
||||
} else {
|
||||
[0.7, 0.7, 0.7]
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
border_radius: 5,
|
||||
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
10
wgpu/src/settings.rs
Normal 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]>,
|
||||
}
|
@ -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.
@ -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.
@ -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
|
||||
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());
|
||||
.unwrap_or_else(|_| FALLBACK_FONT.to_vec())
|
||||
});
|
||||
|
||||
let load_glyph_brush = |font: Vec<u8>| {
|
||||
let builder =
|
||||
|
34
wgpu/src/widget.rs
Normal file
34
wgpu/src/widget.rs
Normal 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
15
wgpu/src/widget/button.rs
Normal 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>;
|
9
wgpu/src/widget/checkbox.rs
Normal file
9
wgpu/src/widget/checkbox.rs
Normal 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>;
|
10
wgpu/src/widget/container.rs
Normal file
10
wgpu/src/widget/container.rs
Normal 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>;
|
15
wgpu/src/widget/progress_bar.rs
Normal file
15
wgpu/src/widget/progress_bar.rs
Normal 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
10
wgpu/src/widget/radio.rs
Normal 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>;
|
13
wgpu/src/widget/scrollable.rs
Normal file
13
wgpu/src/widget/scrollable.rs
Normal 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
16
wgpu/src/widget/slider.rs
Normal 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>;
|
15
wgpu/src/widget/text_input.rs
Normal file
15
wgpu/src/widget/text_input.rs
Normal 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>;
|
@ -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 = []
|
||||
|
@ -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);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user