summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/README.md6
-rw-r--r--examples/bezier_tool/src/main.rs78
-rw-r--r--examples/clock/src/main.rs2
-rw-r--r--examples/color_palette/src/main.rs34
-rw-r--r--examples/counter/src/main.rs2
-rw-r--r--examples/custom_widget/Cargo.toml2
-rw-r--r--examples/custom_widget/src/main.rs59
-rw-r--r--examples/download_progress/Cargo.toml2
-rw-r--r--examples/download_progress/src/main.rs2
-rw-r--r--examples/events/src/main.rs2
-rw-r--r--examples/game_of_life/Cargo.toml2
-rw-r--r--examples/game_of_life/README.md4
-rw-r--r--examples/game_of_life/src/main.rs192
-rw-r--r--examples/game_of_life/src/preset.rs142
-rw-r--r--examples/game_of_life/src/style.rs68
-rw-r--r--examples/geometry/Cargo.toml2
-rw-r--r--examples/geometry/src/main.rs31
-rw-r--r--examples/integration/Cargo.toml2
-rw-r--r--examples/integration/src/controls.rs64
-rw-r--r--examples/integration/src/main.rs233
-rw-r--r--examples/integration/src/scene.rs109
-rw-r--r--examples/pane_grid/Cargo.toml3
-rw-r--r--examples/pane_grid/README.md4
-rw-r--r--examples/pane_grid/src/main.rs152
-rw-r--r--examples/pick_list/Cargo.toml9
-rw-r--r--examples/pick_list/README.md18
-rw-r--r--examples/pick_list/src/main.rs113
-rw-r--r--examples/pokedex/Cargo.toml2
-rw-r--r--examples/pokedex/src/main.rs4
-rw-r--r--examples/progress_bar/src/main.rs17
-rw-r--r--examples/qr_code/Cargo.toml9
-rw-r--r--examples/qr_code/README.md18
-rw-r--r--examples/qr_code/src/main.rs81
-rw-r--r--examples/scrollable/Cargo.toml9
-rw-r--r--examples/scrollable/README.md15
-rw-r--r--examples/scrollable/screenshot.pngbin0 -> 148253 bytes
-rw-r--r--examples/scrollable/src/main.rs184
-rw-r--r--examples/scrollable/src/style.rs190
-rw-r--r--examples/solar_system/src/main.rs2
-rw-r--r--examples/stopwatch/src/main.rs4
-rw-r--r--examples/styling/src/main.rs118
-rw-r--r--examples/svg/src/main.rs2
-rw-r--r--examples/todos/Cargo.toml4
-rw-r--r--examples/todos/src/main.rs12
-rw-r--r--examples/tour/Cargo.toml2
-rw-r--r--examples/tour/src/main.rs74
46 files changed, 1564 insertions, 520 deletions
diff --git a/examples/README.md b/examples/README.md
index 8e1b781f..10c28cf5 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -100,8 +100,10 @@ A bunch of simpler examples exist:
- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu).
- [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application.
- [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized.
+- [`pick_list`](pick_list), a dropdown list of selectable options.
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].
- [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider.
+- [`scrollable`](scrollable), a showcase of the various scrollbar width options.
- [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms.
- [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time.
- [`svg`](svg), an application that renders the [Ghostscript Tiger] by leveraging the `Svg` widget.
@@ -116,7 +118,7 @@ cargo run --package <example>
[Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
## [Coffee]
-Since [Iced was born in May], it has been powering the user interfaces in
+Since [Iced was born in May 2019], it has been powering the user interfaces in
[Coffee], an experimental 2D game engine.
@@ -126,6 +128,6 @@ Since [Iced was born in May], it has been powering the user interfaces in
</a>
</div>
-[Iced was born in May]: https://github.com/hecrj/coffee/pull/35
+[Iced was born in May 2019]: https://github.com/hecrj/coffee/pull/35
[`ui` module]: https://docs.rs/coffee/0.3.2/coffee/ui/index.html
[Coffee]: https://github.com/hecrj/coffee
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs
index fe41e1b2..97832e01 100644
--- a/examples/bezier_tool/src/main.rs
+++ b/examples/bezier_tool/src/main.rs
@@ -3,11 +3,11 @@ use iced::{
button, Align, Button, Column, Element, Length, Sandbox, Settings, Text,
};
-pub fn main() {
+pub fn main() -> iced::Result {
Example::run(Settings {
antialiasing: true,
..Settings::default()
- });
+ })
}
#[derive(Default)]
@@ -69,7 +69,8 @@ impl Sandbox for Example {
mod bezier {
use iced::{
- canvas::{self, Canvas, Cursor, Event, Frame, Geometry, Path, Stroke},
+ canvas::event::{self, Event},
+ canvas::{self, Canvas, Cursor, Frame, Geometry, Path, Stroke},
mouse, Element, Length, Point, Rectangle,
};
@@ -109,40 +110,51 @@ mod bezier {
event: Event,
bounds: Rectangle,
cursor: Cursor,
- ) -> Option<Curve> {
- let cursor_position = cursor.position_in(&bounds)?;
+ ) -> (event::Status, Option<Curve>) {
+ let cursor_position =
+ if let Some(position) = cursor.position_in(&bounds) {
+ position
+ } else {
+ return (event::Status::Ignored, None);
+ };
match event {
- Event::Mouse(mouse_event) => match mouse_event {
- mouse::Event::ButtonPressed(mouse::Button::Left) => {
- match self.state.pending {
- None => {
- self.state.pending = Some(Pending::One {
- from: cursor_position,
- });
- None
- }
- Some(Pending::One { from }) => {
- self.state.pending = Some(Pending::Two {
- from,
- to: cursor_position,
- });
-
- None
- }
- Some(Pending::Two { from, to }) => {
- self.state.pending = None;
-
- Some(Curve {
- from,
- to,
- control: cursor_position,
- })
+ Event::Mouse(mouse_event) => {
+ let message = match mouse_event {
+ mouse::Event::ButtonPressed(mouse::Button::Left) => {
+ match self.state.pending {
+ None => {
+ self.state.pending = Some(Pending::One {
+ from: cursor_position,
+ });
+
+ None
+ }
+ Some(Pending::One { from }) => {
+ self.state.pending = Some(Pending::Two {
+ from,
+ to: cursor_position,
+ });
+
+ None
+ }
+ Some(Pending::Two { from, to }) => {
+ self.state.pending = None;
+
+ Some(Curve {
+ from,
+ to,
+ control: cursor_position,
+ })
+ }
}
}
- }
- _ => None,
- },
+ _ => None,
+ };
+
+ (event::Status::Captured, message)
+ }
+ _ => (event::Status::Ignored, None),
}
}
diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs
index 9c583c78..b317ac00 100644
--- a/examples/clock/src/main.rs
+++ b/examples/clock/src/main.rs
@@ -4,7 +4,7 @@ use iced::{
Point, Rectangle, Settings, Subscription, Vector,
};
-pub fn main() {
+pub fn main() -> iced::Result {
Clock::run(Settings {
antialiasing: true,
..Settings::default()
diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs
index cec6ac79..bb2c61cb 100644
--- a/examples/color_palette/src/main.rs
+++ b/examples/color_palette/src/main.rs
@@ -8,7 +8,7 @@ use palette::{self, Hsl, Limited, Srgb};
use std::marker::PhantomData;
use std::ops::RangeInclusive;
-pub fn main() {
+pub fn main() -> iced::Result {
ColorPalette::run(Settings {
antialiasing: true,
..Settings::default()
@@ -269,7 +269,7 @@ struct ColorPicker<C: ColorSpace> {
trait ColorSpace: Sized {
const LABEL: &'static str;
- const COMPONENT_RANGES: [RangeInclusive<f32>; 3];
+ const COMPONENT_RANGES: [RangeInclusive<f64>; 3];
fn new(a: f32, b: f32, c: f32) -> Self;
@@ -284,13 +284,25 @@ impl<C: 'static + ColorSpace + Copy> ColorPicker<C> {
let [s1, s2, s3] = &mut self.sliders;
let [cr1, cr2, cr3] = C::COMPONENT_RANGES;
+ fn slider<C: Clone>(
+ state: &mut slider::State,
+ range: RangeInclusive<f64>,
+ component: f32,
+ update: impl Fn(f32) -> C + 'static,
+ ) -> Slider<f64, C> {
+ Slider::new(state, range, f64::from(component), move |v| {
+ update(v as f32)
+ })
+ .step(0.01)
+ }
+
Row::new()
.spacing(10)
.align_items(Align::Center)
.push(Text::new(C::LABEL).width(Length::Units(50)))
- .push(Slider::new(s1, cr1, c1, move |v| C::new(v, c2, c3)))
- .push(Slider::new(s2, cr2, c2, move |v| C::new(c1, v, c3)))
- .push(Slider::new(s3, cr3, c3, move |v| C::new(c1, c2, v)))
+ .push(slider(s1, cr1, c1, move |v| C::new(v, c2, c3)))
+ .push(slider(s2, cr2, c2, move |v| C::new(c1, v, c3)))
+ .push(slider(s3, cr3, c3, move |v| C::new(c1, c2, v)))
.push(
Text::new(color.to_string())
.width(Length::Units(185))
@@ -302,7 +314,7 @@ impl<C: 'static + ColorSpace + Copy> ColorPicker<C> {
impl ColorSpace for Color {
const LABEL: &'static str = "RGB";
- const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
+ const COMPONENT_RANGES: [RangeInclusive<f64>; 3] =
[0.0..=1.0, 0.0..=1.0, 0.0..=1.0];
fn new(r: f32, g: f32, b: f32) -> Self {
@@ -325,7 +337,7 @@ impl ColorSpace for Color {
impl ColorSpace for palette::Hsl {
const LABEL: &'static str = "HSL";
- const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
+ const COMPONENT_RANGES: [RangeInclusive<f64>; 3] =
[0.0..=360.0, 0.0..=1.0, 0.0..=1.0];
fn new(hue: f32, saturation: f32, lightness: f32) -> Self {
@@ -356,7 +368,7 @@ impl ColorSpace for palette::Hsl {
impl ColorSpace for palette::Hsv {
const LABEL: &'static str = "HSV";
- const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
+ const COMPONENT_RANGES: [RangeInclusive<f64>; 3] =
[0.0..=360.0, 0.0..=1.0, 0.0..=1.0];
fn new(hue: f32, saturation: f32, value: f32) -> Self {
@@ -379,7 +391,7 @@ impl ColorSpace for palette::Hsv {
impl ColorSpace for palette::Hwb {
const LABEL: &'static str = "HWB";
- const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
+ const COMPONENT_RANGES: [RangeInclusive<f64>; 3] =
[0.0..=360.0, 0.0..=1.0, 0.0..=1.0];
fn new(hue: f32, whiteness: f32, blackness: f32) -> Self {
@@ -410,7 +422,7 @@ impl ColorSpace for palette::Hwb {
impl ColorSpace for palette::Lab {
const LABEL: &'static str = "Lab";
- const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
+ const COMPONENT_RANGES: [RangeInclusive<f64>; 3] =
[0.0..=100.0, -128.0..=127.0, -128.0..=127.0];
fn new(l: f32, a: f32, b: f32) -> Self {
@@ -428,7 +440,7 @@ impl ColorSpace for palette::Lab {
impl ColorSpace for palette::Lch {
const LABEL: &'static str = "Lch";
- const COMPONENT_RANGES: [RangeInclusive<f32>; 3] =
+ const COMPONENT_RANGES: [RangeInclusive<f64>; 3] =
[0.0..=100.0, 0.0..=128.0, 0.0..=360.0];
fn new(l: f32, chroma: f32, hue: f32) -> Self {
diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs
index bde0ea94..e0b2ebd6 100644
--- a/examples/counter/src/main.rs
+++ b/examples/counter/src/main.rs
@@ -1,6 +1,6 @@
use iced::{button, Align, Button, Column, Element, Sandbox, Settings, Text};
-pub fn main() {
+pub fn main() -> iced::Result {
Counter::run(Settings::default())
}
diff --git a/examples/custom_widget/Cargo.toml b/examples/custom_widget/Cargo.toml
index 30747dc0..3942538d 100644
--- a/examples/custom_widget/Cargo.toml
+++ b/examples/custom_widget/Cargo.toml
@@ -8,4 +8,4 @@ publish = false
[dependencies]
iced = { path = "../.." }
iced_native = { path = "../../native" }
-iced_wgpu = { path = "../../wgpu" }
+iced_graphics = { path = "../../graphics" }
diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs
index f096fb54..36f468c7 100644
--- a/examples/custom_widget/src/main.rs
+++ b/examples/custom_widget/src/main.rs
@@ -9,23 +9,26 @@ mod circle {
// Of course, you can choose to make the implementation renderer-agnostic,
// if you wish to, by creating your own `Renderer` trait, which could be
// implemented by `iced_wgpu` and other renderers.
+ use iced_graphics::{Backend, Defaults, Primitive, Renderer};
use iced_native::{
layout, mouse, Background, Color, Element, Hasher, Layout, Length,
- Point, Size, Widget,
+ Point, Rectangle, Size, Widget,
};
- use iced_wgpu::{Defaults, Primitive, Renderer};
pub struct Circle {
- radius: u16,
+ radius: f32,
}
impl Circle {
- pub fn new(radius: u16) -> Self {
+ pub fn new(radius: f32) -> Self {
Self { radius }
}
}
- impl<Message> Widget<Message, Renderer> for Circle {
+ impl<Message, B> Widget<Message, Renderer<B>> for Circle
+ where
+ B: Backend,
+ {
fn width(&self) -> Length {
Length::Shrink
}
@@ -36,34 +39,32 @@ mod circle {
fn layout(
&self,
- _renderer: &Renderer,
+ _renderer: &Renderer<B>,
_limits: &layout::Limits,
) -> layout::Node {
- layout::Node::new(Size::new(
- f32::from(self.radius) * 2.0,
- f32::from(self.radius) * 2.0,
- ))
+ layout::Node::new(Size::new(self.radius * 2.0, self.radius * 2.0))
}
fn hash_layout(&self, state: &mut Hasher) {
use std::hash::Hash;
- self.radius.hash(state);
+ self.radius.to_bits().hash(state);
}
fn draw(
&self,
- _renderer: &mut Renderer,
+ _renderer: &mut Renderer<B>,
_defaults: &Defaults,
layout: Layout<'_>,
_cursor_position: Point,
+ _viewport: &Rectangle,
) -> (Primitive, mouse::Interaction) {
(
Primitive::Quad {
bounds: layout.bounds(),
background: Background::Color(Color::BLACK),
border_radius: self.radius,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
mouse::Interaction::default(),
@@ -71,8 +72,11 @@ mod circle {
}
}
- impl<'a, Message> Into<Element<'a, Message, Renderer>> for Circle {
- fn into(self) -> Element<'a, Message, Renderer> {
+ impl<'a, Message, B> Into<Element<'a, Message, Renderer<B>>> for Circle
+ where
+ B: Backend,
+ {
+ fn into(self) -> Element<'a, Message, Renderer<B>> {
Element::new(self)
}
}
@@ -84,12 +88,12 @@ use iced::{
Slider, Text,
};
-pub fn main() {
+pub fn main() -> iced::Result {
Example::run(Settings::default())
}
struct Example {
- radius: u16,
+ radius: f32,
slider: slider::State,
}
@@ -103,7 +107,7 @@ impl Sandbox for Example {
fn new() -> Self {
Example {
- radius: 50,
+ radius: 50.0,
slider: slider::State::new(),
}
}
@@ -115,7 +119,7 @@ impl Sandbox for Example {
fn update(&mut self, message: Message) {
match message {
Message::RadiusChanged(radius) => {
- self.radius = radius.round() as u16;
+ self.radius = radius;
}
}
}
@@ -127,13 +131,16 @@ 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())))
- .push(Slider::new(
- &mut self.slider,
- 1.0..=100.0,
- f32::from(self.radius),
- Message::RadiusChanged,
- ));
+ .push(Text::new(format!("Radius: {:.2}", self.radius)))
+ .push(
+ Slider::new(
+ &mut self.slider,
+ 1.0..=100.0,
+ self.radius,
+ Message::RadiusChanged,
+ )
+ .step(0.01),
+ );
Container::new(content)
.width(Length::Fill)
diff --git a/examples/download_progress/Cargo.toml b/examples/download_progress/Cargo.toml
index 34e6a132..4b05e7dc 100644
--- a/examples/download_progress/Cargo.toml
+++ b/examples/download_progress/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
publish = false
[dependencies]
-iced = { path = "../..", features = ["tokio"] }
+iced = { path = "../..", features = ["tokio_old"] }
iced_native = { path = "../../native" }
iced_futures = { path = "../../futures" }
reqwest = "0.10"
diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs
index c37ae678..77b01354 100644
--- a/examples/download_progress/src/main.rs
+++ b/examples/download_progress/src/main.rs
@@ -5,7 +5,7 @@ use iced::{
mod download;
-pub fn main() {
+pub fn main() -> iced::Result {
Example::run(Settings::default())
}
diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs
index 066fc230..6eba6aad 100644
--- a/examples/events/src/main.rs
+++ b/examples/events/src/main.rs
@@ -3,7 +3,7 @@ use iced::{
Element, Length, Settings, Subscription, Text,
};
-pub fn main() {
+pub fn main() -> iced::Result {
Events::run(Settings::default())
}
diff --git a/examples/game_of_life/Cargo.toml b/examples/game_of_life/Cargo.toml
index b9bb7f2a..9c4172c4 100644
--- a/examples/game_of_life/Cargo.toml
+++ b/examples/game_of_life/Cargo.toml
@@ -7,6 +7,6 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
-tokio = { version = "0.2", features = ["blocking"] }
+tokio = { version = "0.3", features = ["sync"] }
itertools = "0.9"
rustc-hash = "1.1"
diff --git a/examples/game_of_life/README.md b/examples/game_of_life/README.md
index 1aeb1455..aa39201c 100644
--- a/examples/game_of_life/README.md
+++ b/examples/game_of_life/README.md
@@ -7,8 +7,8 @@ It runs a simulation in a background thread while allowing interaction with a `C
The __[`main`]__ file contains the relevant code of the example.
<div align="center">
- <a href="https://gfycat.com/briefaccurateaardvark">
- <img src="https://thumbs.gfycat.com/BriefAccurateAardvark-size_restricted.gif">
+ <a href="https://gfycat.com/WhichPaltryChick">
+ <img src="https://thumbs.gfycat.com/WhichPaltryChick-size_restricted.gif">
</a>
</div>
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 080d55c0..e18bd6e0 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -1,18 +1,22 @@
//! This example showcases an interactive version of the Game of Life, invented
//! by John Conway. It leverages a `Canvas` together with other widgets.
+mod preset;
mod style;
use grid::Grid;
+use iced::button::{self, Button};
+use iced::executor;
+use iced::pick_list::{self, PickList};
+use iced::slider::{self, Slider};
+use iced::time;
use iced::{
- button::{self, Button},
- executor,
- slider::{self, Slider},
- time, Align, Application, Checkbox, Column, Command, Container, Element,
- Length, Row, Settings, Subscription, Text,
+ Align, Application, Checkbox, Column, Command, Container, Element, Length,
+ Row, Settings, Subscription, Text,
};
+use preset::Preset;
use std::time::{Duration, Instant};
-pub fn main() {
+pub fn main() -> iced::Result {
GameOfLife::run(Settings {
antialiasing: true,
..Settings::default()
@@ -27,17 +31,19 @@ struct GameOfLife {
queued_ticks: usize,
speed: usize,
next_speed: Option<usize>,
+ version: usize,
}
#[derive(Debug, Clone)]
enum Message {
- Grid(grid::Message),
+ Grid(grid::Message, usize),
Tick(Instant),
TogglePlayback,
ToggleGrid(bool),
Next,
Clear,
SpeedChanged(f32),
+ PresetPicked(Preset),
}
impl Application for GameOfLife {
@@ -48,7 +54,7 @@ impl Application for GameOfLife {
fn new(_flags: ()) -> (Self, Command<Message>) {
(
Self {
- speed: 1,
+ speed: 5,
..Self::default()
},
Command::none(),
@@ -61,8 +67,10 @@ impl Application for GameOfLife {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
- Message::Grid(message) => {
- self.grid.update(message);
+ Message::Grid(message, version) => {
+ if version == self.version {
+ self.grid.update(message);
+ }
}
Message::Tick(_) | Message::Next => {
self.queued_ticks = (self.queued_ticks + 1).min(self.speed);
@@ -74,7 +82,11 @@ impl Application for GameOfLife {
self.queued_ticks = 0;
- return Command::perform(task, Message::Grid);
+ let version = self.version;
+
+ return Command::perform(task, move |message| {
+ Message::Grid(message, version)
+ });
}
}
Message::TogglePlayback => {
@@ -85,6 +97,7 @@ impl Application for GameOfLife {
}
Message::Clear => {
self.grid.clear();
+ self.version += 1;
}
Message::SpeedChanged(speed) => {
if self.is_playing {
@@ -93,6 +106,10 @@ impl Application for GameOfLife {
self.speed = speed.round() as usize;
}
}
+ Message::PresetPicked(new_preset) => {
+ self.grid = Grid::from_preset(new_preset);
+ self.version += 1;
+ }
}
Command::none()
@@ -108,15 +125,21 @@ impl Application for GameOfLife {
}
fn view(&mut self) -> Element<Message> {
+ let version = self.version;
let selected_speed = self.next_speed.unwrap_or(self.speed);
let controls = self.controls.view(
self.is_playing,
self.grid.are_lines_visible(),
selected_speed,
+ self.grid.preset(),
);
let content = Column::new()
- .push(self.grid.view().map(Message::Grid))
+ .push(
+ self.grid
+ .view()
+ .map(move |message| Message::Grid(message, version)),
+ )
.push(controls);
Container::new(content)
@@ -128,10 +151,10 @@ impl Application for GameOfLife {
}
mod grid {
+ use crate::Preset;
use iced::{
- canvas::{
- self, Cache, Canvas, Cursor, Event, Frame, Geometry, Path, Text,
- },
+ canvas::event::{self, Event},
+ canvas::{self, Cache, Canvas, Cursor, Frame, Geometry, Path, Text},
mouse, Color, Element, HorizontalAlignment, Length, Point, Rectangle,
Size, Vector, VerticalAlignment,
};
@@ -142,6 +165,7 @@ mod grid {
pub struct Grid {
state: State,
+ preset: Preset,
interaction: Interaction,
life_cache: Cache,
grid_cache: Cache,
@@ -150,7 +174,6 @@ mod grid {
show_lines: bool,
last_tick_duration: Duration,
last_queued_ticks: usize,
- version: usize,
}
#[derive(Debug, Clone)]
@@ -160,7 +183,6 @@ mod grid {
Ticked {
result: Result<Life, TickError>,
tick_duration: Duration,
- version: usize,
},
}
@@ -171,8 +193,24 @@ mod grid {
impl Default for Grid {
fn default() -> Self {
+ Self::from_preset(Preset::default())
+ }
+ }
+
+ impl Grid {
+ const MIN_SCALING: f32 = 0.1;
+ const MAX_SCALING: f32 = 2.0;
+
+ pub fn from_preset(preset: Preset) -> Self {
Self {
- state: State::default(),
+ state: State::with_life(
+ preset
+ .life()
+ .into_iter()
+ .map(|(i, j)| Cell { i, j })
+ .collect(),
+ ),
+ preset,
interaction: Interaction::None,
life_cache: Cache::default(),
grid_cache: Cache::default(),
@@ -181,20 +219,13 @@ mod grid {
show_lines: true,
last_tick_duration: Duration::default(),
last_queued_ticks: 0,
- version: 0,
}
}
- }
-
- impl Grid {
- const MIN_SCALING: f32 = 0.1;
- const MAX_SCALING: f32 = 2.0;
pub fn tick(
&mut self,
amount: usize,
) -> Option<impl Future<Output = Message>> {
- let version = self.version;
let tick = self.state.tick(amount)?;
self.last_queued_ticks = amount;
@@ -206,7 +237,6 @@ mod grid {
Message::Ticked {
result,
- version,
tick_duration,
}
})
@@ -217,16 +247,19 @@ mod grid {
Message::Populate(cell) => {
self.state.populate(cell);
self.life_cache.clear();
+
+ self.preset = Preset::Custom;
}
Message::Unpopulate(cell) => {
self.state.unpopulate(&cell);
self.life_cache.clear();
+
+ self.preset = Preset::Custom;
}
Message::Ticked {
result: Ok(life),
- version,
tick_duration,
- } if version == self.version => {
+ } => {
self.state.update(life);
self.life_cache.clear();
@@ -237,7 +270,6 @@ mod grid {
} => {
dbg!(error);
}
- Message::Ticked { .. } => {}
}
}
@@ -250,11 +282,15 @@ mod grid {
pub fn clear(&mut self) {
self.state = State::default();
- self.version += 1;
+ self.preset = Preset::Custom;
self.life_cache.clear();
}
+ pub fn preset(&self) -> Preset {
+ self.preset
+ }
+
pub fn toggle_lines(&mut self, enabled: bool) {
self.show_lines = enabled;
}
@@ -291,12 +327,18 @@ mod grid {
event: Event,
bounds: Rectangle,
cursor: Cursor,
- ) -> Option<Message> {
+ ) -> (event::Status, Option<Message>) {
if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event {
self.interaction = Interaction::None;
}
- let cursor_position = cursor.position_in(&bounds)?;
+ let cursor_position =
+ if let Some(position) = cursor.position_in(&bounds) {
+ position
+ } else {
+ return (event::Status::Ignored, None);
+ };
+
let cell = Cell::at(self.project(cursor_position, bounds.size()));
let is_populated = self.state.contains(&cell);
@@ -308,28 +350,32 @@ mod grid {
match event {
Event::Mouse(mouse_event) => match mouse_event {
- mouse::Event::ButtonPressed(button) => match button {
- mouse::Button::Left => {
- self.interaction = if is_populated {
- Interaction::Erasing
- } else {
- Interaction::Drawing
- };
-
- populate.or(unpopulate)
- }
- mouse::Button::Right => {
- self.interaction = Interaction::Panning {
- translation: self.translation,
- start: cursor_position,
- };
+ mouse::Event::ButtonPressed(button) => {
+ let message = match button {
+ mouse::Button::Left => {
+ self.interaction = if is_populated {
+ Interaction::Erasing
+ } else {
+ Interaction::Drawing
+ };
+
+ populate.or(unpopulate)
+ }
+ mouse::Button::Right => {
+ self.interaction = Interaction::Panning {
+ translation: self.translation,
+ start: cursor_position,
+ };
- None
- }
- _ => None,
- },
+ None
+ }
+ _ => None,
+ };
+
+ (event::Status::Captured, message)
+ }
mouse::Event::CursorMoved { .. } => {
- match self.interaction {
+ let message = match self.interaction {
Interaction::Drawing => populate,
Interaction::Erasing => unpopulate,
Interaction::Panning { translation, start } => {
@@ -343,7 +389,14 @@ mod grid {
None
}
_ => None,
- }
+ };
+
+ let event_status = match self.interaction {
+ Interaction::None => event::Status::Ignored,
+ _ => event::Status::Captured,
+ };
+
+ (event_status, message)
}
mouse::Event::WheelScrolled { delta } => match delta {
mouse::ScrollDelta::Lines { y, .. }
@@ -376,11 +429,12 @@ mod grid {
self.grid_cache.clear();
}
- None
+ (event::Status::Captured, None)
}
},
- _ => None,
+ _ => (event::Status::Ignored, None),
},
+ _ => (event::Status::Ignored, None),
}
}
@@ -533,6 +587,13 @@ mod grid {
}
impl State {
+ pub fn with_life(life: Life) -> Self {
+ Self {
+ life,
+ ..Self::default()
+ }
+ }
+
fn cell_count(&self) -> usize {
self.life.len() + self.births.len()
}
@@ -647,6 +708,14 @@ mod grid {
}
}
+ impl std::iter::FromIterator<Cell> for Life {
+ fn from_iter<I: IntoIterator<Item = Cell>>(iter: I) -> Self {
+ Life {
+ cells: iter.into_iter().collect(),
+ }
+ }
+ }
+
impl std::fmt::Debug for Life {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Life")
@@ -741,6 +810,7 @@ struct Controls {
next_button: button::State,
clear_button: button::State,
speed_slider: slider::State,
+ preset_list: pick_list::State<Preset>,
}
impl Controls {
@@ -749,6 +819,7 @@ impl Controls {
is_playing: bool,
is_grid_enabled: bool,
speed: usize,
+ preset: Preset,
) -> Element<'a, Message> {
let playback_controls = Row::new()
.spacing(10)
@@ -794,6 +865,17 @@ impl Controls {
.text_size(16),
)
.push(
+ PickList::new(
+ &mut self.preset_list,
+ preset::ALL,
+ Some(preset),
+ Message::PresetPicked,
+ )
+ .padding(8)
+ .text_size(16)
+ .style(style::PickList),
+ )
+ .push(
Button::new(&mut self.clear_button, Text::new("Clear"))
.on_press(Message::Clear)
.style(style::Clear),
diff --git a/examples/game_of_life/src/preset.rs b/examples/game_of_life/src/preset.rs
new file mode 100644
index 00000000..05157b6a
--- /dev/null
+++ b/examples/game_of_life/src/preset.rs
@@ -0,0 +1,142 @@
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Preset {
+ Custom,
+ XKCD,
+ Glider,
+ SmallExploder,
+ Exploder,
+ TenCellRow,
+ LightweightSpaceship,
+ Tumbler,
+ GliderGun,
+ Acorn,
+}
+
+pub static ALL: &[Preset] = &[
+ Preset::Custom,
+ Preset::XKCD,
+ Preset::Glider,
+ Preset::SmallExploder,
+ Preset::Exploder,
+ Preset::TenCellRow,
+ Preset::LightweightSpaceship,
+ Preset::Tumbler,
+ Preset::GliderGun,
+ Preset::Acorn,
+];
+
+impl Preset {
+ pub fn life(self) -> Vec<(isize, isize)> {
+ #[rustfmt::skip]
+ let cells = match self {
+ Preset::Custom => vec![],
+ Preset::XKCD => vec![
+ " xxx ",
+ " x x ",
+ " x x ",
+ " x ",
+ "x xxx ",
+ " x x x ",
+ " x x",
+ " x x ",
+ " x x ",
+ ],
+ Preset::Glider => vec![
+ " x ",
+ " x",
+ "xxx"
+ ],
+ Preset::SmallExploder => vec![
+ " x ",
+ "xxx",
+ "x x",
+ " x ",
+ ],
+ Preset::Exploder => vec![
+ "x x x",
+ "x x",
+ "x x",
+ "x x",
+ "x x x",
+ ],
+ Preset::TenCellRow => vec![
+ "xxxxxxxxxx",
+ ],
+ Preset::LightweightSpaceship => vec![
+ " xxxxx",
+ "x x",
+ " x",
+ "x x ",
+ ],
+ Preset::Tumbler => vec![
+ " xx xx ",
+ " xx xx ",
+ " x x ",
+ "x x x x",
+ "x x x x",
+ "xx xx",
+ ],
+ Preset::GliderGun => vec![
+ " x ",
+ " x x ",
+ " xx xx xx",
+ " x x xx xx",
+ "xx x x xx ",
+ "xx x x xx x x ",
+ " x x x ",
+ " x x ",
+ " xx ",
+ ],
+ Preset::Acorn => vec![
+ " x ",
+ " x ",
+ "xx xxx",
+ ],
+ };
+
+ let start_row = -(cells.len() as isize / 2);
+
+ cells
+ .into_iter()
+ .enumerate()
+ .flat_map(|(i, cells)| {
+ let start_column = -(cells.len() as isize / 2);
+
+ cells
+ .chars()
+ .enumerate()
+ .filter(|(_, c)| !c.is_whitespace())
+ .map(move |(j, _)| {
+ (start_row + i as isize, start_column + j as isize)
+ })
+ })
+ .collect()
+ }
+}
+
+impl Default for Preset {
+ fn default() -> Preset {
+ Preset::XKCD
+ }
+}
+
+impl std::fmt::Display for Preset {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Preset::Custom => "Custom",
+ Preset::XKCD => "xkcd #2293",
+ Preset::Glider => "Glider",
+ Preset::SmallExploder => "Small Exploder",
+ Preset::Exploder => "Exploder",
+ Preset::TenCellRow => "10 Cell Row",
+ Preset::LightweightSpaceship => "Lightweight spaceship",
+ Preset::Tumbler => "Tumbler",
+ Preset::GliderGun => "Gosper Glider Gun",
+ Preset::Acorn => "Acorn",
+ }
+ )
+ }
+}
diff --git a/examples/game_of_life/src/style.rs b/examples/game_of_life/src/style.rs
index d59569f2..6605826f 100644
--- a/examples/game_of_life/src/style.rs
+++ b/examples/game_of_life/src/style.rs
@@ -1,4 +1,4 @@
-use iced::{button, container, slider, Background, Color};
+use iced::{button, container, pick_list, slider, Background, Color};
const ACTIVE: Color = Color::from_rgb(
0x72 as f32 / 255.0,
@@ -18,6 +18,12 @@ const HOVERED: Color = Color::from_rgb(
0xC4 as f32 / 255.0,
);
+const BACKGROUND: Color = Color::from_rgb(
+ 0x2F as f32 / 255.0,
+ 0x31 as f32 / 255.0,
+ 0x36 as f32 / 255.0,
+);
+
pub struct Container;
impl container::StyleSheet for Container {
@@ -38,7 +44,7 @@ impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(ACTIVE)),
- border_radius: 3,
+ border_radius: 3.0,
text_color: Color::WHITE,
..button::Style::default()
}
@@ -54,7 +60,7 @@ impl button::StyleSheet for Button {
fn pressed(&self) -> button::Style {
button::Style {
- border_width: 1,
+ border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
@@ -67,7 +73,7 @@ impl button::StyleSheet for Clear {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(DESTRUCTIVE)),
- border_radius: 3,
+ border_radius: 3.0,
text_color: Color::WHITE,
..button::Style::default()
}
@@ -86,7 +92,7 @@ impl button::StyleSheet for Clear {
fn pressed(&self) -> button::Style {
button::Style {
- border_width: 1,
+ border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
@@ -100,9 +106,9 @@ impl slider::StyleSheet for Slider {
slider::Style {
rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }),
handle: slider::Handle {
- shape: slider::HandleShape::Circle { radius: 9 },
+ shape: slider::HandleShape::Circle { radius: 9.0 },
color: ACTIVE,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
@@ -132,3 +138,51 @@ impl slider::StyleSheet for Slider {
}
}
}
+
+pub struct PickList;
+
+impl pick_list::StyleSheet for PickList {
+ fn menu(&self) -> pick_list::Menu {
+ pick_list::Menu {
+ text_color: Color::WHITE,
+ background: BACKGROUND.into(),
+ border_width: 1.0,
+ border_color: Color {
+ a: 0.7,
+ ..Color::BLACK
+ },
+ selected_background: Color {
+ a: 0.5,
+ ..Color::BLACK
+ }
+ .into(),
+ selected_text_color: Color::WHITE,
+ }
+ }
+
+ fn active(&self) -> pick_list::Style {
+ pick_list::Style {
+ text_color: Color::WHITE,
+ background: BACKGROUND.into(),
+ border_width: 1.0,
+ border_color: Color {
+ a: 0.6,
+ ..Color::BLACK
+ },
+ border_radius: 2.0,
+ icon_size: 0.5,
+ }
+ }
+
+ fn hovered(&self) -> pick_list::Style {
+ let active = self.active();
+
+ pick_list::Style {
+ border_color: Color {
+ a: 0.9,
+ ..Color::BLACK
+ },
+ ..active
+ }
+ }
+}
diff --git a/examples/geometry/Cargo.toml b/examples/geometry/Cargo.toml
index 9df52454..34eec4fb 100644
--- a/examples/geometry/Cargo.toml
+++ b/examples/geometry/Cargo.toml
@@ -8,4 +8,4 @@ publish = false
[dependencies]
iced = { path = "../.." }
iced_native = { path = "../../native" }
-iced_wgpu = { path = "../../wgpu" }
+iced_graphics = { path = "../../graphics" }
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index aabe6b21..f650b2c1 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -10,13 +10,13 @@ mod rainbow {
// Of course, you can choose to make the implementation renderer-agnostic,
// if you wish to, by creating your own `Renderer` trait, which could be
// implemented by `iced_wgpu` and other renderers.
- use iced_native::{
- layout, mouse, Element, Hasher, Layout, Length, Point, Size, Vector,
- Widget,
- };
- use iced_wgpu::{
+ use iced_graphics::{
triangle::{Mesh2D, Vertex2D},
- Defaults, Primitive, Renderer,
+ Backend, Defaults, Primitive, Renderer,
+ };
+ use iced_native::{
+ layout, mouse, Element, Hasher, Layout, Length, Point, Rectangle, Size,
+ Vector, Widget,
};
pub struct Rainbow;
@@ -27,7 +27,10 @@ mod rainbow {
}
}
- impl<Message> Widget<Message, Renderer> for Rainbow {
+ impl<Message, B> Widget<Message, Renderer<B>> for Rainbow
+ where
+ B: Backend,
+ {
fn width(&self) -> Length {
Length::Fill
}
@@ -38,7 +41,7 @@ mod rainbow {
fn layout(
&self,
- _renderer: &Renderer,
+ _renderer: &Renderer<B>,
limits: &layout::Limits,
) -> layout::Node {
let size = limits.width(Length::Fill).resolve(Size::ZERO);
@@ -50,10 +53,11 @@ mod rainbow {
fn draw(
&self,
- _renderer: &mut Renderer,
+ _renderer: &mut Renderer<B>,
_defaults: &Defaults,
layout: Layout<'_>,
cursor_position: Point,
+ _viewport: &Rectangle,
) -> (Primitive, mouse::Interaction) {
let b = layout.bounds();
@@ -146,8 +150,11 @@ mod rainbow {
}
}
- impl<'a, Message> Into<Element<'a, Message, Renderer>> for Rainbow {
- fn into(self) -> Element<'a, Message, Renderer> {
+ impl<'a, Message, B> Into<Element<'a, Message, Renderer<B>>> for Rainbow
+ where
+ B: Backend,
+ {
+ fn into(self) -> Element<'a, Message, Renderer<B>> {
Element::new(self)
}
}
@@ -159,7 +166,7 @@ use iced::{
};
use rainbow::Rainbow;
-pub fn main() {
+pub fn main() -> iced::Result {
Example::run(Settings::default())
}
diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml
index afc2c791..4515502f 100644
--- a/examples/integration/Cargo.toml
+++ b/examples/integration/Cargo.toml
@@ -8,4 +8,4 @@ publish = false
[dependencies]
iced_winit = { path = "../../winit" }
iced_wgpu = { path = "../../wgpu" }
-env_logger = "0.7"
+env_logger = "0.8"
diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs
index 0999336b..824f9f53 100644
--- a/examples/integration/src/controls.rs
+++ b/examples/integration/src/controls.rs
@@ -1,15 +1,15 @@
-use crate::Scene;
-
use iced_wgpu::Renderer;
use iced_winit::{
- slider, Align, Color, Column, Element, Length, Row, Slider, Text,
+ slider, Align, Color, Column, Command, Element, Length, Program, Row,
+ Slider, Text,
};
pub struct Controls {
+ background_color: Color,
sliders: [slider::State; 3],
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub enum Message {
BackgroundColorChanged(Color),
}
@@ -17,58 +17,64 @@ pub enum Message {
impl Controls {
pub fn new() -> Controls {
Controls {
+ background_color: Color::BLACK,
sliders: Default::default(),
}
}
- pub fn update(&self, message: Message, scene: &mut Scene) {
+ pub fn background_color(&self) -> Color {
+ self.background_color
+ }
+}
+
+impl Program for Controls {
+ type Renderer = Renderer;
+ type Message = Message;
+
+ fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::BackgroundColorChanged(color) => {
- scene.background_color = color;
+ self.background_color = color;
}
}
+
+ Command::none()
}
- pub fn view(&mut self, scene: &Scene) -> Element<Message, Renderer> {
+ fn view(&mut self) -> Element<Message, Renderer> {
let [r, g, b] = &mut self.sliders;
- let background_color = scene.background_color;
+ let background_color = self.background_color;
let sliders = Row::new()
.width(Length::Units(500))
.spacing(20)
- .push(Slider::new(
- r,
- 0.0..=1.0,
- scene.background_color.r,
- move |r| {
+ .push(
+ Slider::new(r, 0.0..=1.0, background_color.r, move |r| {
Message::BackgroundColorChanged(Color {
r,
..background_color
})
- },
- ))
- .push(Slider::new(
- g,
- 0.0..=1.0,
- scene.background_color.g,
- move |g| {
+ })
+ .step(0.01),
+ )
+ .push(
+ Slider::new(g, 0.0..=1.0, background_color.g, move |g| {
Message::BackgroundColorChanged(Color {
g,
..background_color
})
- },
- ))
- .push(Slider::new(
- b,
- 0.0..=1.0,
- scene.background_color.b,
- move |b| {
+ })
+ .step(0.01),
+ )
+ .push(
+ Slider::new(b, 0.0..=1.0, background_color.b, move |b| {
Message::BackgroundColorChanged(Color {
b,
..background_color
})
- },
- ));
+ })
+ .step(0.01),
+ );
Row::new()
.width(Length::Fill)
diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs
index 92d2fa8d..9b52f3a5 100644
--- a/examples/integration/src/main.rs
+++ b/examples/integration/src/main.rs
@@ -4,14 +4,12 @@ mod scene;
use controls::Controls;
use scene::Scene;
-use iced_wgpu::{
- wgpu, window::SwapChain, Primitive, Renderer, Settings, Target,
-};
-use iced_winit::{
- futures, mouse, winit, Cache, Clipboard, Size, UserInterface,
-};
+use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport};
+use iced_winit::{conversion, futures, program, winit, Debug, Size};
+use futures::task::SpawnExt;
use winit::{
+ dpi::PhysicalPosition,
event::{Event, ModifiersState, WindowEvent},
event_loop::{ControlFlow, EventLoop},
};
@@ -22,32 +20,39 @@ pub fn main() {
// Initialize winit
let event_loop = EventLoop::new();
let window = winit::window::Window::new(&event_loop).unwrap();
- let mut logical_size =
- window.inner_size().to_logical(window.scale_factor());
+
+ let physical_size = window.inner_size();
+ let mut viewport = Viewport::with_physical_size(
+ Size::new(physical_size.width, physical_size.height),
+ window.scale_factor(),
+ );
+ let mut cursor_position = PhysicalPosition::new(-1.0, -1.0);
let mut modifiers = ModifiersState::default();
- // Initialize WGPU
+ // Initialize wgpu
+ let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
+ let surface = unsafe { instance.create_surface(&window) };
- let surface = wgpu::Surface::create(&window);
let (mut device, queue) = futures::executor::block_on(async {
- let adapter = wgpu::Adapter::request(
- &wgpu::RequestAdapterOptions {
+ let adapter = instance
+ .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface),
- },
- wgpu::BackendBit::PRIMARY,
- )
- .await
- .expect("Request adapter");
+ })
+ .await
+ .expect("Request adapter");
adapter
- .request_device(&wgpu::DeviceDescriptor {
- extensions: wgpu::Extensions {
- anisotropic_filtering: false,
+ .request_device(
+ &wgpu::DeviceDescriptor {
+ features: wgpu::Features::empty(),
+ limits: wgpu::Limits::default(),
+ shader_validation: false,
},
- limits: wgpu::Limits::default(),
- })
+ None,
+ )
.await
+ .expect("Request device")
});
let format = wgpu::TextureFormat::Bgra8UnormSrgb;
@@ -55,20 +60,39 @@ pub fn main() {
let mut swap_chain = {
let size = window.inner_size();
- SwapChain::new(&device, &surface, format, size.width, size.height)
+ device.create_swap_chain(
+ &surface,
+ &wgpu::SwapChainDescriptor {
+ usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
+ format: format,
+ width: size.width,
+ height: size.height,
+ present_mode: wgpu::PresentMode::Mailbox,
+ },
+ )
};
let mut resized = false;
- // Initialize iced
- let mut events = Vec::new();
- let mut cache = Some(Cache::default());
- let mut renderer = Renderer::new(&mut device, Settings::default());
- let mut output = (Primitive::None, mouse::Interaction::default());
- let clipboard = Clipboard::new(&window);
+ // Initialize staging belt and local pool
+ let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024);
+ let mut local_pool = futures::executor::LocalPool::new();
// Initialize scene and GUI controls
- let mut scene = Scene::new(&device);
- let mut controls = Controls::new();
+ let scene = Scene::new(&mut device);
+ let controls = Controls::new();
+
+ // Initialize iced
+ let mut debug = Debug::new();
+ let mut renderer =
+ Renderer::new(Backend::new(&mut device, Settings::default()));
+
+ let mut state = program::State::new(
+ controls,
+ viewport.logical_size(),
+ conversion::cursor_position(cursor_position, viewport.scale_factor()),
+ &mut renderer,
+ &mut debug,
+ );
// Run event loop
event_loop.run(move |event, _, control_flow| {
@@ -78,18 +102,23 @@ pub fn main() {
match event {
Event::WindowEvent { event, .. } => {
match event {
+ WindowEvent::CursorMoved { position, .. } => {
+ cursor_position = position;
+ }
WindowEvent::ModifiersChanged(new_modifiers) => {
modifiers = new_modifiers;
}
WindowEvent::Resized(new_size) => {
- logical_size =
- new_size.to_logical(window.scale_factor());
+ viewport = Viewport::with_physical_size(
+ Size::new(new_size.width, new_size.height),
+ window.scale_factor(),
+ );
+
resized = true;
}
WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
-
_ => {}
}
@@ -99,117 +128,95 @@ pub fn main() {
window.scale_factor(),
modifiers,
) {
- events.push(event);
+ state.queue_event(event);
}
}
Event::MainEventsCleared => {
- // If no relevant events happened, we can simply skip this
- if events.is_empty() {
- return;
- }
-
- // We need to:
- // 1. Process events of our user interface.
- // 2. Update state as a result of any interaction.
- // 3. Generate a new output for our renderer.
-
- // First, we build our user interface.
- let mut user_interface = UserInterface::build(
- controls.view(&scene),
- Size::new(logical_size.width, logical_size.height),
- cache.take().unwrap(),
- &mut renderer,
- );
-
- // Then, we process the events, obtaining messages in return.
- let messages = user_interface.update(
- events.drain(..),
- clipboard.as_ref().map(|c| c as _),
- &renderer,
- );
-
- let user_interface = if messages.is_empty() {
- // If there are no messages, no interactions we care about have
- // happened. We can simply leave our user interface as it is.
- user_interface
- } else {
- // If there are messages, we need to update our state
- // accordingly and rebuild our user interface.
- // We can only do this if we drop our user interface first
- // by turning it into its cache.
- cache = Some(user_interface.into_cache());
-
- // In this example, `Controls` is the only part that cares
- // about messages, so updating our state is pretty
- // straightforward.
- for message in messages {
- controls.update(message, &mut scene);
- }
-
- // Once the state has been changed, we rebuild our updated
- // user interface.
- UserInterface::build(
- controls.view(&scene),
- Size::new(logical_size.width, logical_size.height),
- cache.take().unwrap(),
+ // If there are events pending
+ if !state.is_queue_empty() {
+ // We update iced
+ let _ = state.update(
+ viewport.logical_size(),
+ conversion::cursor_position(
+ cursor_position,
+ viewport.scale_factor(),
+ ),
+ None,
&mut renderer,
- )
- };
-
- // Finally, we just need to draw a new output for our renderer,
- output = user_interface.draw(&mut renderer);
-
- // update our cache,
- cache = Some(user_interface.into_cache());
+ &mut debug,
+ );
- // and request a redraw
- window.request_redraw();
+ // and request a redraw
+ window.request_redraw();
+ }
}
Event::RedrawRequested(_) => {
if resized {
let size = window.inner_size();
- swap_chain = SwapChain::new(
- &device,
+ swap_chain = device.create_swap_chain(
&surface,
- format,
- size.width,
- size.height,
+ &wgpu::SwapChainDescriptor {
+ usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
+ format: format,
+ width: size.width,
+ height: size.height,
+ present_mode: wgpu::PresentMode::Mailbox,
+ },
);
+
+ resized = false;
}
- let (frame, viewport) =
- swap_chain.next_frame().expect("Next frame");
+ let frame = swap_chain.get_current_frame().expect("Next frame");
let mut encoder = device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { label: None },
);
- // We draw the scene first
- scene.draw(&mut encoder, &frame.view);
+ let program = state.program();
+
+ {
+ // We clear the frame
+ let mut render_pass = scene.clear(
+ &frame.output.view,
+ &mut encoder,
+ program.background_color(),
+ );
+
+ // Draw the scene
+ scene.draw(&mut render_pass);
+ }
// And then iced on top
- let mouse_interaction = renderer.draw(
+ let mouse_interaction = renderer.backend_mut().draw(
&mut device,
+ &mut staging_belt,
&mut encoder,
- Target {
- texture: &frame.view,
- viewport,
- },
- &output,
- window.scale_factor(),
- &["Some debug information!"],
+ &frame.output.view,
+ &viewport,
+ state.primitive(),
+ &debug.overlay(),
);
// Then we submit the work
- queue.submit(&[encoder.finish()]);
+ staging_belt.finish();
+ queue.submit(Some(encoder.finish()));
- // And update the mouse cursor
+ // Update the mouse cursor
window.set_cursor_icon(
iced_winit::conversion::mouse_interaction(
mouse_interaction,
),
);
+
+ // And recall staging buffers
+ local_pool
+ .spawner()
+ .spawn(staging_belt.recall())
+ .expect("Recall staging buffers");
+
+ local_pool.run_until_stalled();
}
_ => {}
}
diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs
index 22c6812a..03a338c6 100644
--- a/examples/integration/src/scene.rs
+++ b/examples/integration/src/scene.rs
@@ -2,91 +2,68 @@ use iced_wgpu::wgpu;
use iced_winit::Color;
pub struct Scene {
- pub background_color: Color,
pipeline: wgpu::RenderPipeline,
- bind_group: wgpu::BindGroup,
}
impl Scene {
pub fn new(device: &wgpu::Device) -> Scene {
- let (pipeline, bind_group) = build_pipeline(device);
+ let pipeline = build_pipeline(device);
- Scene {
- background_color: Color::BLACK,
- pipeline,
- bind_group,
- }
+ Scene { pipeline }
}
- pub fn draw(
+ pub fn clear<'a>(
&self,
- encoder: &mut wgpu::CommandEncoder,
- target: &wgpu::TextureView,
- ) {
- let mut rpass =
- encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
- color_attachments: &[
- wgpu::RenderPassColorAttachmentDescriptor {
- attachment: target,
- resolve_target: None,
- load_op: wgpu::LoadOp::Clear,
- store_op: wgpu::StoreOp::Store,
- clear_color: {
- let [r, g, b, a] =
- self.background_color.into_linear();
+ target: &'a wgpu::TextureView,
+ encoder: &'a mut wgpu::CommandEncoder,
+ background_color: Color,
+ ) -> wgpu::RenderPass<'a> {
+ encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
+ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
+ attachment: target,
+ resolve_target: None,
+ ops: wgpu::Operations {
+ load: wgpu::LoadOp::Clear({
+ let [r, g, b, a] = background_color.into_linear();
- wgpu::Color {
- r: r as f64,
- g: g as f64,
- b: b as f64,
- a: a as f64,
- }
- },
- },
- ],
- depth_stencil_attachment: None,
- });
+ wgpu::Color {
+ r: r as f64,
+ g: g as f64,
+ b: b as f64,
+ a: a as f64,
+ }
+ }),
+ store: true,
+ },
+ }],
+ depth_stencil_attachment: None,
+ })
+ }
- rpass.set_pipeline(&self.pipeline);
- rpass.set_bind_group(0, &self.bind_group, &[]);
- rpass.draw(0..3, 0..1);
+ pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
+ render_pass.set_pipeline(&self.pipeline);
+ render_pass.draw(0..3, 0..1);
}
}
-fn build_pipeline(
- device: &wgpu::Device,
-) -> (wgpu::RenderPipeline, wgpu::BindGroup) {
- let vs = include_bytes!("shader/vert.spv");
- let fs = include_bytes!("shader/frag.spv");
-
- let vs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&vs[..])).unwrap(),
- );
+fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
+ let vs_module =
+ device.create_shader_module(wgpu::include_spirv!("shader/vert.spv"));
- let fs_module = device.create_shader_module(
- &wgpu::read_spirv(std::io::Cursor::new(&fs[..])).unwrap(),
- );
-
- let bind_group_layout =
- device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
- label: None,
- bindings: &[],
- });
-
- let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
- label: None,
- layout: &bind_group_layout,
- bindings: &[],
- });
+ let fs_module =
+ device.create_shader_module(wgpu::include_spirv!("shader/frag.spv"));
let pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
- bind_group_layouts: &[&bind_group_layout],
+ label: None,
+ push_constant_ranges: &[],
+ bind_group_layouts: &[],
});
let pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
- layout: &pipeline_layout,
+ label: None,
+ layout: Some(&pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
@@ -98,9 +75,7 @@ fn build_pipeline(
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::None,
- depth_bias: 0,
- depth_bias_slope_scale: 0.0,
- depth_bias_clamp: 0.0,
+ ..Default::default()
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
@@ -119,5 +94,5 @@ fn build_pipeline(
alpha_to_coverage_enabled: false,
});
- (pipeline, bind_group)
+ pipeline
}
diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml
index 3ed912ac..e489f210 100644
--- a/examples/pane_grid/Cargo.toml
+++ b/examples/pane_grid/Cargo.toml
@@ -6,4 +6,5 @@ edition = "2018"
publish = false
[dependencies]
-iced = { path = "../.." }
+iced = { path = "../..", features = ["debug"] }
+iced_native = { path = "../../native" }
diff --git a/examples/pane_grid/README.md b/examples/pane_grid/README.md
index 3653fc5b..a4cfcb7d 100644
--- a/examples/pane_grid/README.md
+++ b/examples/pane_grid/README.md
@@ -15,8 +15,8 @@ This example showcases the `PaneGrid` widget, which features:
The __[`main`]__ file contains all the code of the example.
<div align="center">
- <a href="https://gfycat.com/mixedflatjellyfish">
- <img src="https://thumbs.gfycat.com/MixedFlatJellyfish-small.gif">
+ <a href="https://gfycat.com/frailfreshairedaleterrier">
+ <img src="https://thumbs.gfycat.com/FrailFreshAiredaleterrier-small.gif">
</a>
</div>
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index b4bbd68f..3c3256cf 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -1,16 +1,18 @@
use iced::{
- button, keyboard, pane_grid, scrollable, Align, Button, Column, Container,
- Element, HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable,
- Settings, Text,
+ button, executor, keyboard, pane_grid, scrollable, Align, Application,
+ Button, Column, Command, Container, Element, HorizontalAlignment, Length,
+ PaneGrid, Scrollable, Settings, Subscription, Text,
};
+use iced_native::{event, subscription, Event};
-pub fn main() {
+pub fn main() -> iced::Result {
Example::run(Settings::default())
}
struct Example {
panes: pane_grid::State<Content>,
panes_created: usize,
+ focus: Option<pane_grid::Pane>,
}
#[derive(Debug, Clone, Copy)]
@@ -18,59 +20,77 @@ enum Message {
Split(pane_grid::Axis, pane_grid::Pane),
SplitFocused(pane_grid::Axis),
FocusAdjacent(pane_grid::Direction),
+ Clicked(pane_grid::Pane),
Dragged(pane_grid::DragEvent),
Resized(pane_grid::ResizeEvent),
Close(pane_grid::Pane),
CloseFocused,
}
-impl Sandbox for Example {
+impl Application for Example {
type Message = Message;
+ type Executor = executor::Default;
+ type Flags = ();
- fn new() -> Self {
+ fn new(_flags: ()) -> (Self, Command<Message>) {
let (panes, _) = pane_grid::State::new(Content::new(0));
- Example {
- panes,
- panes_created: 1,
- }
+ (
+ Example {
+ panes,
+ panes_created: 1,
+ focus: None,
+ },
+ Command::none(),
+ )
}
fn title(&self) -> String {
String::from("Pane grid - Iced")
}
- fn update(&mut self, message: Message) {
+ fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Split(axis, pane) => {
- let _ = self.panes.split(
+ let result = self.panes.split(
axis,
&pane,
Content::new(self.panes_created),
);
+ if let Some((pane, _)) = result {
+ self.focus = Some(pane);
+ }
+
self.panes_created += 1;
}
Message::SplitFocused(axis) => {
- if let Some(pane) = self.panes.active() {
- let _ = self.panes.split(
+ if let Some(pane) = self.focus {
+ let result = self.panes.split(
axis,
&pane,
Content::new(self.panes_created),
);
+ if let Some((pane, _)) = result {
+ self.focus = Some(pane);
+ }
+
self.panes_created += 1;
}
}
Message::FocusAdjacent(direction) => {
- if let Some(pane) = self.panes.active() {
+ if let Some(pane) = self.focus {
if let Some(adjacent) =
self.panes.adjacent(&pane, direction)
{
- self.panes.focus(&adjacent);
+ self.focus = Some(adjacent);
}
}
}
+ Message::Clicked(pane) => {
+ self.focus = Some(pane);
+ }
Message::Resized(pane_grid::ResizeEvent { split, ratio }) => {
self.panes.resize(&split, ratio);
}
@@ -82,29 +102,60 @@ impl Sandbox for Example {
}
Message::Dragged(_) => {}
Message::Close(pane) => {
- let _ = self.panes.close(&pane);
+ if let Some((_, sibling)) = self.panes.close(&pane) {
+ self.focus = Some(sibling);
+ }
}
Message::CloseFocused => {
- if let Some(pane) = self.panes.active() {
- let _ = self.panes.close(&pane);
+ if let Some(pane) = self.focus {
+ if let Some((_, sibling)) = self.panes.close(&pane) {
+ self.focus = Some(sibling);
+ }
}
}
}
+
+ Command::none()
+ }
+
+ fn subscription(&self) -> Subscription<Message> {
+ subscription::events_with(|event, status| {
+ if let event::Status::Captured = status {
+ return None;
+ }
+
+ match event {
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ modifiers,
+ key_code,
+ }) if modifiers.is_command_pressed() => handle_hotkey(key_code),
+ _ => None,
+ }
+ })
}
fn view(&mut self) -> Element<Message> {
+ let focus = self.focus;
let total_panes = self.panes.len();
- let pane_grid =
- PaneGrid::new(&mut self.panes, |pane, content, focus| {
- content.view(pane, focus, total_panes)
- })
- .width(Length::Fill)
- .height(Length::Fill)
- .spacing(10)
- .on_drag(Message::Dragged)
- .on_resize(Message::Resized)
- .on_key_press(handle_hotkey);
+ let pane_grid = PaneGrid::new(&mut self.panes, |pane, content| {
+ let is_focused = focus == Some(pane);
+
+ let title_bar =
+ pane_grid::TitleBar::new(format!("Pane {}", content.id))
+ .padding(10)
+ .style(style::TitleBar { is_focused });
+
+ pane_grid::Content::new(content.view(pane, total_panes))
+ .title_bar(title_bar)
+ .style(style::Pane { is_focused })
+ })
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .spacing(10)
+ .on_click(Message::Clicked)
+ .on_drag(Message::Dragged)
+ .on_resize(10, Message::Resized);
Container::new(pane_grid)
.width(Length::Fill)
@@ -114,11 +165,11 @@ impl Sandbox for Example {
}
}
-fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> {
+fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> {
use keyboard::KeyCode;
use pane_grid::{Axis, Direction};
- let direction = match event.key_code {
+ let direction = match key_code {
KeyCode::Up => Some(Direction::Up),
KeyCode::Down => Some(Direction::Down),
KeyCode::Left => Some(Direction::Left),
@@ -126,7 +177,7 @@ fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> {
_ => None,
};
- match event.key_code {
+ match key_code {
KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)),
KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)),
KeyCode::W => Some(Message::CloseFocused),
@@ -155,15 +206,14 @@ impl Content {
fn view(
&mut self,
pane: pane_grid::Pane,
- focus: Option<pane_grid::Focus>,
total_panes: usize,
) -> Element<Message> {
let Content {
- id,
scroll,
split_horizontally,
split_vertically,
close,
+ ..
} = self;
let button = |state, label, message, style| {
@@ -209,7 +259,6 @@ impl Content {
.width(Length::Fill)
.spacing(10)
.align_items(Align::Center)
- .push(Text::new(format!("Pane {}", id)).size(30))
.push(controls);
Container::new(content)
@@ -217,9 +266,6 @@ impl Content {
.height(Length::Fill)
.padding(5)
.center_y()
- .style(style::Pane {
- is_focused: focus.is_some(),
- })
.into()
}
}
@@ -245,6 +291,25 @@ mod style {
0xC4 as f32 / 255.0,
);
+ pub struct TitleBar {
+ pub is_focused: bool,
+ }
+
+ impl container::StyleSheet for TitleBar {
+ fn style(&self) -> container::Style {
+ let pane = Pane {
+ is_focused: self.is_focused,
+ }
+ .style();
+
+ container::Style {
+ text_color: Some(Color::WHITE),
+ background: Some(pane.border_color.into()),
+ ..Default::default()
+ }
+ }
+ }
+
pub struct Pane {
pub is_focused: bool,
}
@@ -253,10 +318,11 @@ mod style {
fn style(&self) -> container::Style {
container::Style {
background: Some(Background::Color(SURFACE)),
- border_width: 2,
- border_color: Color {
- a: if self.is_focused { 1.0 } else { 0.3 },
- ..Color::BLACK
+ border_width: 2.0,
+ border_color: if self.is_focused {
+ Color::BLACK
+ } else {
+ Color::from_rgb(0.7, 0.7, 0.7)
},
..Default::default()
}
@@ -280,7 +346,7 @@ mod style {
button::Style {
text_color,
background: background.map(Background::Color),
- border_radius: 5,
+ border_radius: 5.0,
shadow_offset: Vector::new(0.0, 0.0),
..button::Style::default()
}
diff --git a/examples/pick_list/Cargo.toml b/examples/pick_list/Cargo.toml
new file mode 100644
index 00000000..a87d7217
--- /dev/null
+++ b/examples/pick_list/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "pick_list"
+version = "0.1.0"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["debug"] }
diff --git a/examples/pick_list/README.md b/examples/pick_list/README.md
new file mode 100644
index 00000000..6dc80bf4
--- /dev/null
+++ b/examples/pick_list/README.md
@@ -0,0 +1,18 @@
+## Pick-list
+
+A dropdown list of selectable options.
+
+It displays and positions an overlay based on the window position of the widget.
+
+The __[`main`]__ file contains all the code of the example.
+
+<div align="center">
+ <img src="https://user-images.githubusercontent.com/518289/87125075-2c232e80-c28a-11ea-95c2-769c610b8843.gif">
+</div>
+
+You can run it with `cargo run`:
+```
+cargo run --package pick_list
+```
+
+[`main`]: src/main.rs
diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs
new file mode 100644
index 00000000..68662602
--- /dev/null
+++ b/examples/pick_list/src/main.rs
@@ -0,0 +1,113 @@
+use iced::{
+ pick_list, scrollable, Align, Container, Element, Length, PickList,
+ Sandbox, Scrollable, Settings, Space, Text,
+};
+
+pub fn main() -> iced::Result {
+ Example::run(Settings::default())
+}
+
+#[derive(Default)]
+struct Example {
+ scroll: scrollable::State,
+ pick_list: pick_list::State<Language>,
+ selected_language: Language,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Message {
+ LanguageSelected(Language),
+}
+
+impl Sandbox for Example {
+ type Message = Message;
+
+ fn new() -> Self {
+ Self::default()
+ }
+
+ fn title(&self) -> String {
+ String::from("Pick list - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::LanguageSelected(language) => {
+ self.selected_language = language;
+ }
+ }
+ }
+
+ fn view(&mut self) -> Element<Message> {
+ let pick_list = PickList::new(
+ &mut self.pick_list,
+ &Language::ALL[..],
+ Some(self.selected_language),
+ Message::LanguageSelected,
+ );
+
+ let mut content = Scrollable::new(&mut self.scroll)
+ .width(Length::Fill)
+ .align_items(Align::Center)
+ .spacing(10)
+ .push(Space::with_height(Length::Units(600)))
+ .push(Text::new("Which is your favorite language?"))
+ .push(pick_list);
+
+ content = content.push(Space::with_height(Length::Units(600)));
+
+ Container::new(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y()
+ .into()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Language {
+ Rust,
+ Elm,
+ Ruby,
+ Haskell,
+ C,
+ Javascript,
+ Other,
+}
+
+impl Language {
+ const ALL: [Language; 7] = [
+ Language::C,
+ Language::Elm,
+ Language::Ruby,
+ Language::Haskell,
+ Language::Rust,
+ Language::Javascript,
+ Language::Other,
+ ];
+}
+
+impl Default for Language {
+ fn default() -> Language {
+ Language::Rust
+ }
+}
+
+impl std::fmt::Display for Language {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Language::Rust => "Rust",
+ Language::Elm => "Elm",
+ Language::Ruby => "Ruby",
+ Language::Haskell => "Haskell",
+ Language::C => "C",
+ Language::Javascript => "Javascript",
+ Language::Other => "Some other language",
+ }
+ )
+ }
+}
diff --git a/examples/pokedex/Cargo.toml b/examples/pokedex/Cargo.toml
index 94320086..05e73992 100644
--- a/examples/pokedex/Cargo.toml
+++ b/examples/pokedex/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
publish = false
[dependencies]
-iced = { path = "../..", features = ["image", "debug", "tokio"] }
+iced = { path = "../..", features = ["image", "debug", "tokio_old"] }
serde_json = "1.0"
[dependencies.serde]
diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs
index e7afa8f5..187e5dee 100644
--- a/examples/pokedex/src/main.rs
+++ b/examples/pokedex/src/main.rs
@@ -3,7 +3,7 @@ use iced::{
Container, Element, Image, Length, Row, Settings, Text,
};
-pub fn main() {
+pub fn main() -> iced::Result {
Pokedex::run(Settings::default())
}
@@ -251,7 +251,7 @@ mod style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
})),
- border_radius: 12,
+ border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::WHITE,
..button::Style::default()
diff --git a/examples/progress_bar/src/main.rs b/examples/progress_bar/src/main.rs
index 43b09928..c9a8e798 100644
--- a/examples/progress_bar/src/main.rs
+++ b/examples/progress_bar/src/main.rs
@@ -1,6 +1,6 @@
use iced::{slider, Column, Element, ProgressBar, Sandbox, Settings, Slider};
-pub fn main() {
+pub fn main() -> iced::Result {
Progress::run(Settings::default())
}
@@ -36,12 +36,15 @@ impl Sandbox for Progress {
Column::new()
.padding(20)
.push(ProgressBar::new(0.0..=100.0, self.value))
- .push(Slider::new(
- &mut self.progress_bar_slider,
- 0.0..=100.0,
- self.value,
- Message::SliderChanged,
- ))
+ .push(
+ Slider::new(
+ &mut self.progress_bar_slider,
+ 0.0..=100.0,
+ self.value,
+ Message::SliderChanged,
+ )
+ .step(0.01),
+ )
.into()
}
}
diff --git a/examples/qr_code/Cargo.toml b/examples/qr_code/Cargo.toml
new file mode 100644
index 00000000..7f2d4e42
--- /dev/null
+++ b/examples/qr_code/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "qr_code"
+version = "0.1.0"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["qr_code"] }
diff --git a/examples/qr_code/README.md b/examples/qr_code/README.md
new file mode 100644
index 00000000..2dd89c26
--- /dev/null
+++ b/examples/qr_code/README.md
@@ -0,0 +1,18 @@
+## QR Code Generator
+
+A basic QR code generator that showcases the `QRCode` widget.
+
+The __[`main`]__ file contains all the code of the example.
+
+<div align="center">
+ <a href="https://gfycat.com/heavyexhaustedaracari">
+ <img src="https://thumbs.gfycat.com/HeavyExhaustedAracari-size_restricted.gif">
+ </a>
+</div>
+
+You can run it with `cargo run`:
+```
+cargo run --package qr_code
+```
+
+[`main`]: src/main.rs
diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs
new file mode 100644
index 00000000..37b4855d
--- /dev/null
+++ b/examples/qr_code/src/main.rs
@@ -0,0 +1,81 @@
+use iced::qr_code::{self, QRCode};
+use iced::text_input::{self, TextInput};
+use iced::{
+ Align, Column, Container, Element, Length, Sandbox, Settings, Text,
+};
+
+pub fn main() -> iced::Result {
+ QRGenerator::run(Settings::default())
+}
+
+#[derive(Default)]
+struct QRGenerator {
+ data: String,
+ input: text_input::State,
+ qr_code: Option<qr_code::State>,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ DataChanged(String),
+}
+
+impl Sandbox for QRGenerator {
+ type Message = Message;
+
+ fn new() -> Self {
+ QRGenerator {
+ qr_code: qr_code::State::new("").ok(),
+ ..Self::default()
+ }
+ }
+
+ fn title(&self) -> String {
+ String::from("QR Code Generator - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::DataChanged(mut data) => {
+ data.truncate(100);
+
+ self.qr_code = qr_code::State::new(&data).ok();
+ self.data = data;
+ }
+ }
+ }
+
+ fn view(&mut self) -> Element<Message> {
+ let title = Text::new("QR Code Generator")
+ .size(70)
+ .color([0.5, 0.5, 0.5]);
+
+ let input = TextInput::new(
+ &mut self.input,
+ "Type the data of your QR code here...",
+ &self.data,
+ Message::DataChanged,
+ )
+ .size(30)
+ .padding(15);
+
+ let mut content = Column::new()
+ .width(Length::Units(700))
+ .spacing(20)
+ .align_items(Align::Center)
+ .push(title)
+ .push(input);
+
+ if let Some(qr_code) = self.qr_code.as_mut() {
+ content = content.push(QRCode::new(qr_code).cell_size(10));
+ }
+
+ Container::new(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .padding(20)
+ .center_x()
+ .center_y()
+ .into()
+ }
+}
diff --git a/examples/scrollable/Cargo.toml b/examples/scrollable/Cargo.toml
new file mode 100644
index 00000000..12753fb6
--- /dev/null
+++ b/examples/scrollable/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "scrollable"
+version = "0.1.0"
+authors = ["Clark Moody <clark@clarkmoody.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+iced = { path = "../.." }
diff --git a/examples/scrollable/README.md b/examples/scrollable/README.md
new file mode 100644
index 00000000..ed0e31b5
--- /dev/null
+++ b/examples/scrollable/README.md
@@ -0,0 +1,15 @@
+# Scrollable
+An example showcasing the various size and style options for the Scrollable.
+
+All the example code is located in the __[`main`](src/main.rs)__ file.
+
+<div align="center">
+ <a href="./screenshot.png">
+ <img src="./screenshot.png" height="640px">
+ </a>
+</div>
+
+You can run it with `cargo run`:
+```
+cargo run --package scrollable
+```
diff --git a/examples/scrollable/screenshot.png b/examples/scrollable/screenshot.png
new file mode 100644
index 00000000..2d800251
--- /dev/null
+++ b/examples/scrollable/screenshot.png
Binary files differ
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
new file mode 100644
index 00000000..8dd2e20c
--- /dev/null
+++ b/examples/scrollable/src/main.rs
@@ -0,0 +1,184 @@
+mod style;
+
+use iced::{
+ scrollable, Column, Container, Element, Length, Radio, Row, Rule, Sandbox,
+ Scrollable, Settings, Space, Text,
+};
+
+pub fn main() -> iced::Result {
+ ScrollableDemo::run(Settings::default())
+}
+
+struct ScrollableDemo {
+ theme: style::Theme,
+ variants: Vec<Variant>,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ ThemeChanged(style::Theme),
+}
+
+impl Sandbox for ScrollableDemo {
+ type Message = Message;
+
+ fn new() -> Self {
+ ScrollableDemo {
+ theme: Default::default(),
+ variants: Variant::all(),
+ }
+ }
+
+ fn title(&self) -> String {
+ String::from("Scrollable - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::ThemeChanged(theme) => self.theme = theme,
+ }
+ }
+
+ fn view(&mut self) -> Element<Message> {
+ let ScrollableDemo {
+ theme, variants, ..
+ } = self;
+
+ let choose_theme = style::Theme::ALL.iter().fold(
+ Column::new().spacing(10).push(Text::new("Choose a theme:")),
+ |column, option| {
+ column.push(
+ Radio::new(
+ *option,
+ &format!("{:?}", option),
+ Some(*theme),
+ Message::ThemeChanged,
+ )
+ .style(*theme),
+ )
+ },
+ );
+
+ let scrollable_row = Row::with_children(
+ variants
+ .iter_mut()
+ .map(|variant| {
+ let mut scrollable = Scrollable::new(&mut variant.state)
+ .padding(10)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .style(*theme)
+ .push(Text::new(variant.title));
+
+ if let Some(scrollbar_width) = variant.scrollbar_width {
+ scrollable = scrollable
+ .scrollbar_width(scrollbar_width)
+ .push(Text::new(format!(
+ "scrollbar_width: {:?}",
+ scrollbar_width
+ )));
+ }
+
+ if let Some(scrollbar_margin) = variant.scrollbar_margin {
+ scrollable = scrollable
+ .scrollbar_margin(scrollbar_margin)
+ .push(Text::new(format!(
+ "scrollbar_margin: {:?}",
+ scrollbar_margin
+ )));
+ }
+
+ if let Some(scroller_width) = variant.scroller_width {
+ scrollable = scrollable
+ .scroller_width(scroller_width)
+ .push(Text::new(format!(
+ "scroller_width: {:?}",
+ scroller_width
+ )));
+ }
+
+ scrollable = scrollable
+ .push(Space::with_height(Length::Units(100)))
+ .push(Text::new(
+ "Some content that should wrap within the \
+ scrollable. Let's output a lot of short words, so \
+ that we'll make sure to see how wrapping works \
+ with these scrollbars.",
+ ))
+ .push(Space::with_height(Length::Units(1200)))
+ .push(Text::new("Middle"))
+ .push(Space::with_height(Length::Units(1200)))
+ .push(Text::new("The End."));
+
+ Container::new(scrollable)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .style(*theme)
+ .into()
+ })
+ .collect(),
+ )
+ .spacing(20)
+ .width(Length::Fill)
+ .height(Length::Fill);
+
+ let content = Column::new()
+ .spacing(20)
+ .padding(20)
+ .push(choose_theme)
+ .push(Rule::horizontal(20).style(self.theme))
+ .push(scrollable_row);
+
+ Container::new(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y()
+ .style(self.theme)
+ .into()
+ }
+}
+
+/// A version of a scrollable
+struct Variant {
+ title: &'static str,
+ state: scrollable::State,
+ scrollbar_width: Option<u16>,
+ scrollbar_margin: Option<u16>,
+ scroller_width: Option<u16>,
+}
+
+impl Variant {
+ pub fn all() -> Vec<Self> {
+ vec![
+ Self {
+ title: "Default Scrollbar",
+ state: scrollable::State::new(),
+ scrollbar_width: None,
+ scrollbar_margin: None,
+ scroller_width: None,
+ },
+ Self {
+ title: "Slimmed & Margin",
+ state: scrollable::State::new(),
+ scrollbar_width: Some(4),
+ scrollbar_margin: Some(3),
+ scroller_width: Some(4),
+ },
+ Self {
+ title: "Wide Scroller",
+ state: scrollable::State::new(),
+ scrollbar_width: Some(4),
+ scrollbar_margin: None,
+ scroller_width: Some(10),
+ },
+ Self {
+ title: "Narrow Scroller",
+ state: scrollable::State::new(),
+ scrollbar_width: Some(10),
+ scrollbar_margin: None,
+ scroller_width: Some(4),
+ },
+ ]
+ }
+}
diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs
new file mode 100644
index 00000000..ae449141
--- /dev/null
+++ b/examples/scrollable/src/style.rs
@@ -0,0 +1,190 @@
+use iced::{container, radio, rule, scrollable};
+
+#[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 scrollable::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Scrollable.into(),
+ }
+ }
+}
+
+impl From<Theme> for Box<dyn rule::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Rule.into(),
+ }
+ }
+}
+
+mod dark {
+ use iced::{container, radio, rule, scrollable, Color};
+
+ const BACKGROUND: Color = Color::from_rgb(
+ 0x36 as f32 / 255.0,
+ 0x39 as f32 / 255.0,
+ 0x3F as f32 / 255.0,
+ );
+
+ 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 SCROLLBAR: Color = Color::from_rgb(
+ 0x2E as f32 / 255.0,
+ 0x33 as f32 / 255.0,
+ 0x38 as f32 / 255.0,
+ );
+
+ const SCROLLER: Color = Color::from_rgb(
+ 0x20 as f32 / 255.0,
+ 0x22 as f32 / 255.0,
+ 0x25 as f32 / 255.0,
+ );
+
+ pub struct Container;
+
+ impl container::StyleSheet for Container {
+ fn style(&self) -> container::Style {
+ container::Style {
+ background: Color {
+ a: 0.99,
+ ..BACKGROUND
+ }
+ .into(),
+ text_color: Color::WHITE.into(),
+ ..container::Style::default()
+ }
+ }
+ }
+
+ pub struct Radio;
+
+ impl radio::StyleSheet for Radio {
+ fn active(&self) -> radio::Style {
+ radio::Style {
+ background: SURFACE.into(),
+ dot_color: ACTIVE,
+ border_width: 1.0,
+ border_color: ACTIVE,
+ }
+ }
+
+ fn hovered(&self) -> radio::Style {
+ radio::Style {
+ background: Color { a: 0.5, ..SURFACE }.into(),
+ ..self.active()
+ }
+ }
+ }
+
+ pub struct Scrollable;
+
+ impl scrollable::StyleSheet for Scrollable {
+ fn active(&self) -> scrollable::Scrollbar {
+ scrollable::Scrollbar {
+ background: Color {
+ a: 0.8,
+ ..SCROLLBAR
+ }
+ .into(),
+ border_radius: 2.0,
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ scroller: scrollable::Scroller {
+ color: Color { a: 0.7, ..SCROLLER },
+ border_radius: 2.0,
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ }
+ }
+
+ fn hovered(&self) -> scrollable::Scrollbar {
+ let active = self.active();
+
+ scrollable::Scrollbar {
+ background: SCROLLBAR.into(),
+ scroller: scrollable::Scroller {
+ color: SCROLLER,
+ ..active.scroller
+ },
+ ..active
+ }
+ }
+
+ fn dragging(&self) -> scrollable::Scrollbar {
+ let hovered = self.hovered();
+
+ scrollable::Scrollbar {
+ scroller: scrollable::Scroller {
+ color: ACCENT,
+ ..hovered.scroller
+ },
+ ..hovered
+ }
+ }
+ }
+
+ pub struct Rule;
+
+ impl rule::StyleSheet for Rule {
+ fn style(&self) -> rule::Style {
+ rule::Style {
+ color: SURFACE,
+ width: 2,
+ radius: 1.0,
+ fill_mode: rule::FillMode::Percent(30.0),
+ }
+ }
+ }
+}
diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs
index 98bd3b21..6a2de736 100644
--- a/examples/solar_system/src/main.rs
+++ b/examples/solar_system/src/main.rs
@@ -14,7 +14,7 @@ use iced::{
use std::time::Instant;
-pub fn main() {
+pub fn main() -> iced::Result {
SolarSystem::run(Settings {
antialiasing: true,
..Settings::default()
diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs
index 9de6d39e..983cf3e6 100644
--- a/examples/stopwatch/src/main.rs
+++ b/examples/stopwatch/src/main.rs
@@ -5,7 +5,7 @@ use iced::{
};
use std::time::{Duration, Instant};
-pub fn main() {
+pub fn main() -> iced::Result {
Stopwatch::run(Settings::default())
}
@@ -161,7 +161,7 @@ mod style {
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,
+ border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::WHITE,
..button::Style::default()
diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs
index 63ab9d62..8975fd9a 100644
--- a/examples/styling/src/main.rs
+++ b/examples/styling/src/main.rs
@@ -1,10 +1,10 @@
use iced::{
button, scrollable, slider, text_input, Align, Button, Checkbox, Column,
- Container, Element, Length, ProgressBar, Radio, Row, Sandbox, Scrollable,
- Settings, Slider, Space, Text, TextInput,
+ Container, Element, Length, ProgressBar, Radio, Row, Rule, Sandbox,
+ Scrollable, Settings, Slider, Space, Text, TextInput,
};
-pub fn main() {
+pub fn main() -> iced::Result {
Styling::run(Settings::default())
}
@@ -113,14 +113,17 @@ impl Sandbox for Styling {
.padding(20)
.max_width(600)
.push(choose_theme)
+ .push(Rule::horizontal(38).style(self.theme))
.push(Row::new().spacing(10).push(text_input).push(button))
.push(slider)
.push(progress_bar)
.push(
Row::new()
.spacing(10)
+ .height(Length::Units(100))
.align_items(Align::Center)
.push(scrollable)
+ .push(Rule::vertical(38).style(self.theme))
.push(checkbox),
);
@@ -136,8 +139,8 @@ impl Sandbox for Styling {
mod style {
use iced::{
- button, checkbox, container, progress_bar, radio, scrollable, slider,
- text_input,
+ button, checkbox, container, progress_bar, radio, rule, scrollable,
+ slider, text_input,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -228,18 +231,25 @@ mod style {
}
}
+ impl From<Theme> for Box<dyn rule::StyleSheet> {
+ fn from(theme: Theme) -> Self {
+ match theme {
+ Theme::Light => Default::default(),
+ Theme::Dark => dark::Rule.into(),
+ }
+ }
+ }
+
mod light {
- use iced::{button, Background, Color, Vector};
+ use iced::{button, 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,
+ background: Color::from_rgb(0.11, 0.42, 0.87).into(),
+ border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
..button::Style::default()
@@ -258,8 +268,8 @@ mod style {
mod dark {
use iced::{
- button, checkbox, container, progress_bar, radio, scrollable,
- slider, text_input, Background, Color,
+ button, checkbox, container, progress_bar, radio, rule, scrollable,
+ slider, text_input, Color,
};
const SURFACE: Color = Color::from_rgb(
@@ -291,10 +301,8 @@ mod style {
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),
+ background: Color::from_rgb8(0x36, 0x39, 0x3F).into(),
+ text_color: Color::WHITE.into(),
..container::Style::default()
}
}
@@ -305,16 +313,16 @@ mod style {
impl radio::StyleSheet for Radio {
fn active(&self) -> radio::Style {
radio::Style {
- background: Background::Color(SURFACE),
+ background: SURFACE.into(),
dot_color: ACTIVE,
- border_width: 1,
+ border_width: 1.0,
border_color: ACTIVE,
}
}
fn hovered(&self) -> radio::Style {
radio::Style {
- background: Background::Color(Color { a: 0.5, ..SURFACE }),
+ background: Color { a: 0.5, ..SURFACE }.into(),
..self.active()
}
}
@@ -325,16 +333,16 @@ mod style {
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,
+ background: SURFACE.into(),
+ border_radius: 2.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
}
}
fn focused(&self) -> text_input::Style {
text_input::Style {
- border_width: 1,
+ border_width: 1.0,
border_color: ACCENT,
..self.active()
}
@@ -342,7 +350,7 @@ mod style {
fn hovered(&self) -> text_input::Style {
text_input::Style {
- border_width: 1,
+ border_width: 1.0,
border_color: Color { a: 0.3, ..ACCENT },
..self.focused()
}
@@ -366,8 +374,8 @@ mod style {
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
- background: Some(Background::Color(ACTIVE)),
- border_radius: 3,
+ background: ACTIVE.into(),
+ border_radius: 3.0,
text_color: Color::WHITE,
..button::Style::default()
}
@@ -375,7 +383,7 @@ mod style {
fn hovered(&self) -> button::Style {
button::Style {
- background: Some(Background::Color(HOVERED)),
+ background: HOVERED.into(),
text_color: Color::WHITE,
..self.active()
}
@@ -383,7 +391,7 @@ mod style {
fn pressed(&self) -> button::Style {
button::Style {
- border_width: 1,
+ border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
@@ -395,14 +403,14 @@ mod style {
impl scrollable::StyleSheet for Scrollable {
fn active(&self) -> scrollable::Scrollbar {
scrollable::Scrollbar {
- background: Some(Background::Color(SURFACE)),
- border_radius: 2,
- border_width: 0,
+ background: SURFACE.into(),
+ border_radius: 2.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
scroller: scrollable::Scroller {
color: ACTIVE,
- border_radius: 2,
- border_width: 0,
+ border_radius: 2.0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
@@ -412,10 +420,7 @@ mod style {
let active = self.active();
scrollable::Scrollbar {
- background: Some(Background::Color(Color {
- a: 0.5,
- ..SURFACE
- })),
+ background: Color { a: 0.5, ..SURFACE }.into(),
scroller: scrollable::Scroller {
color: HOVERED,
..active.scroller
@@ -444,9 +449,9 @@ mod style {
slider::Style {
rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }),
handle: slider::Handle {
- shape: slider::HandleShape::Circle { radius: 9 },
+ shape: slider::HandleShape::Circle { radius: 9.0 },
color: ACTIVE,
- border_width: 0,
+ border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
@@ -482,9 +487,9 @@ mod style {
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,
+ background: SURFACE.into(),
+ bar: ACTIVE.into(),
+ border_radius: 10.0,
}
}
}
@@ -494,27 +499,38 @@ mod style {
impl checkbox::StyleSheet for Checkbox {
fn active(&self, is_checked: bool) -> checkbox::Style {
checkbox::Style {
- background: Background::Color(if is_checked {
- ACTIVE
- } else {
- SURFACE
- }),
+ background: if is_checked { ACTIVE } else { SURFACE }
+ .into(),
checkmark_color: Color::WHITE,
- border_radius: 2,
- border_width: 1,
+ border_radius: 2.0,
+ border_width: 1.0,
border_color: ACTIVE,
}
}
fn hovered(&self, is_checked: bool) -> checkbox::Style {
checkbox::Style {
- background: Background::Color(Color {
+ background: Color {
a: 0.8,
..if is_checked { ACTIVE } else { SURFACE }
- }),
+ }
+ .into(),
..self.active(is_checked)
}
}
}
+
+ pub struct Rule;
+
+ impl rule::StyleSheet for Rule {
+ fn style(&self) -> rule::Style {
+ rule::Style {
+ color: SURFACE,
+ width: 2,
+ radius: 1.0,
+ fill_mode: rule::FillMode::Padded(15),
+ }
+ }
+ }
}
}
diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs
index e19eeca2..8707fa3b 100644
--- a/examples/svg/src/main.rs
+++ b/examples/svg/src/main.rs
@@ -1,6 +1,6 @@
use iced::{Container, Element, Length, Sandbox, Settings, Svg};
-pub fn main() {
+pub fn main() -> iced::Result {
Tiger::run(Settings::default())
}
diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml
index f945cde5..c8926c33 100644
--- a/examples/todos/Cargo.toml
+++ b/examples/todos/Cargo.toml
@@ -6,13 +6,13 @@ edition = "2018"
publish = false
[dependencies]
-iced = { path = "../..", features = ["async-std"] }
+iced = { path = "../..", features = ["async-std", "debug"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
async-std = "1.0"
-directories = "2.0"
+directories-next = "2.0"
[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3", features = ["Window", "Storage"] }
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index c9cbcc69..ccee2703 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -5,7 +5,7 @@ use iced::{
};
use serde::{Deserialize, Serialize};
-pub fn main() {
+pub fn main() -> iced::Result {
Todos::run(Settings::default())
}
@@ -425,7 +425,7 @@ impl Filter {
}
}
-fn loading_message() -> Element<'static, Message> {
+fn loading_message<'a>() -> Element<'a, Message> {
Container::new(
Text::new("Loading...")
.horizontal_alignment(HorizontalAlignment::Center)
@@ -437,7 +437,7 @@ fn loading_message() -> Element<'static, Message> {
.into()
}
-fn empty_message(message: &str) -> Element<'static, Message> {
+fn empty_message<'a>(message: &str) -> Element<'a, Message> {
Container::new(
Text::new(message)
.width(Length::Fill)
@@ -499,7 +499,7 @@ enum SaveError {
impl SavedState {
fn path() -> std::path::PathBuf {
let mut path = if let Some(project_dirs) =
- directories::ProjectDirs::from("rs", "Iced", "Todos")
+ directories_next::ProjectDirs::from("rs", "Iced", "Todos")
{
project_dirs.data_dir().into()
} else {
@@ -611,7 +611,7 @@ mod style {
background: Some(Background::Color(
Color::from_rgb(0.2, 0.2, 0.7),
)),
- border_radius: 10,
+ border_radius: 10.0,
text_color: Color::WHITE,
..button::Style::default()
}
@@ -627,7 +627,7 @@ mod style {
background: Some(Background::Color(Color::from_rgb(
0.8, 0.2, 0.2,
))),
- border_radius: 5,
+ border_radius: 5.0,
text_color: Color::WHITE,
shadow_offset: Vector::new(1.0, 1.0),
..button::Style::default()
diff --git a/examples/tour/Cargo.toml b/examples/tour/Cargo.toml
index 96749e90..bc7fac11 100644
--- a/examples/tour/Cargo.toml
+++ b/examples/tour/Cargo.toml
@@ -7,4 +7,4 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["image", "debug"] }
-env_logger = "0.7"
+env_logger = "0.8"
diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs
index c9678b9d..e8755d39 100644
--- a/examples/tour/src/main.rs
+++ b/examples/tour/src/main.rs
@@ -4,7 +4,7 @@ use iced::{
Sandbox, Scrollable, Settings, Slider, Space, Text, TextInput,
};
-pub fn main() {
+pub fn main() -> iced::Result {
env_logger::init();
Tour::run(Settings::default())
@@ -190,7 +190,7 @@ enum Step {
Welcome,
Slider {
state: slider::State,
- value: u16,
+ value: u8,
},
RowsAndColumns {
layout: Layout,
@@ -222,13 +222,13 @@ enum Step {
#[derive(Debug, Clone)]
pub enum StepMessage {
- SliderChanged(f32),
+ SliderChanged(u8),
LayoutChanged(Layout),
- SpacingChanged(f32),
- TextSizeChanged(f32),
+ SpacingChanged(u16),
+ TextSizeChanged(u16),
TextColorChanged(Color),
LanguageSelected(Language),
- ImageWidthChanged(f32),
+ ImageWidthChanged(u16),
InputChanged(String),
ToggleSecureInput(bool),
DebugToggled(bool),
@@ -249,12 +249,12 @@ impl<'a> Step {
}
StepMessage::SliderChanged(new_value) => {
if let Step::Slider { value, .. } = self {
- *value = new_value.round() as u16;
+ *value = new_value;
}
}
StepMessage::TextSizeChanged(new_size) => {
if let Step::Text { size, .. } = self {
- *size = new_size.round() as u16;
+ *size = new_size;
}
}
StepMessage::TextColorChanged(new_color) => {
@@ -269,12 +269,12 @@ impl<'a> Step {
}
StepMessage::SpacingChanged(new_spacing) => {
if let Step::RowsAndColumns { spacing, .. } = self {
- *spacing = new_spacing.round() as u16;
+ *spacing = new_spacing;
}
}
StepMessage::ImageWidthChanged(new_width) => {
if let Step::Image { width, .. } = self {
- *width = new_width.round() as u16;
+ *width = new_width;
}
}
StepMessage::InputChanged(new_value) => {
@@ -384,7 +384,7 @@ impl<'a> Step {
fn slider(
state: &'a mut slider::State,
- value: u16,
+ value: u8,
) -> Column<'a, StepMessage> {
Self::container("Slider")
.push(Text::new(
@@ -397,8 +397,8 @@ impl<'a> Step {
))
.push(Slider::new(
state,
- 0.0..=100.0,
- value as f32,
+ 0..=100,
+ value,
StepMessage::SliderChanged,
))
.push(
@@ -444,8 +444,8 @@ impl<'a> Step {
.spacing(10)
.push(Slider::new(
spacing_slider,
- 0.0..=80.0,
- spacing as f32,
+ 0..=80,
+ spacing,
StepMessage::SpacingChanged,
))
.push(
@@ -486,30 +486,25 @@ impl<'a> Step {
)
.push(Slider::new(
size_slider,
- 10.0..=70.0,
- size as f32,
+ 10..=70,
+ size,
StepMessage::TextSizeChanged,
));
let [red, green, blue] = color_sliders;
+
+ let color_sliders = Row::new()
+ .spacing(10)
+ .push(color_slider(red, color.r, move |r| Color { r, ..color }))
+ .push(color_slider(green, color.g, move |g| Color { g, ..color }))
+ .push(color_slider(blue, color.b, move |b| Color { b, ..color }));
+
let color_section = Column::new()
.padding(20)
.spacing(20)
.push(Text::new("And its color:"))
.push(Text::new(&format!("{:?}", color)).color(color))
- .push(
- Row::new()
- .spacing(10)
- .push(Slider::new(red, 0.0..=1.0, color.r, move |r| {
- StepMessage::TextColorChanged(Color { r, ..color })
- }))
- .push(Slider::new(green, 0.0..=1.0, color.g, move |g| {
- StepMessage::TextColorChanged(Color { g, ..color })
- }))
- .push(Slider::new(blue, 0.0..=1.0, color.b, move |b| {
- StepMessage::TextColorChanged(Color { b, ..color })
- })),
- );
+ .push(color_sliders);
Self::container("Text")
.push(Text::new(
@@ -559,8 +554,8 @@ impl<'a> Step {
.push(ferris(width))
.push(Slider::new(
slider,
- 100.0..=500.0,
- width as f32,
+ 100..=500,
+ width,
StepMessage::ImageWidthChanged,
))
.push(
@@ -694,7 +689,7 @@ fn ferris<'a>(width: u16) -> Container<'a, StepMessage> {
.center_x()
}
-fn button<'a, Message>(
+fn button<'a, Message: Clone>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
@@ -706,6 +701,17 @@ fn button<'a, Message>(
.min_width(100)
}
+fn color_slider(
+ state: &mut slider::State,
+ component: f32,
+ update: impl Fn(f32) -> Color + 'static,
+) -> Slider<f64, StepMessage> {
+ Slider::new(state, 0.0..=1.0, f64::from(component), move |c| {
+ StepMessage::TextColorChanged(update(c as f32))
+ })
+ .step(0.01)
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language {
Rust,
@@ -763,7 +769,7 @@ mod style {
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,
+ border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
..button::Style::default()