summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorLibravatar Casper Storm <casper.storm@lich.io>2022-12-13 09:31:57 +0100
committerLibravatar Casper Storm <casper.storm@lich.io>2022-12-13 09:31:57 +0100
commit2e6d90f141217bad83eacd392562c13d7485881f (patch)
treebaa2c507076073aed4fd24abc9c7a7949d85c039 /examples
parentba95042fff378213f5029b2b164d79e768482a47 (diff)
parent02182eea45537c9eb5b2bddfdff822bb8a3d143d (diff)
downloadiced-2e6d90f141217bad83eacd392562c13d7485881f.tar.gz
iced-2e6d90f141217bad83eacd392562c13d7485881f.tar.bz2
iced-2e6d90f141217bad83eacd392562c13d7485881f.zip
Merge branch 'master' into feat/slider-orientation
Diffstat (limited to 'examples')
-rw-r--r--examples/arc/src/main.rs14
-rw-r--r--examples/cached/Cargo.toml10
-rw-r--r--examples/cached/src/main.rs139
-rw-r--r--examples/clock/src/main.rs54
-rw-r--r--examples/custom_quad/Cargo.toml10
-rw-r--r--examples/custom_quad/src/main.rs160
-rw-r--r--examples/custom_widget/src/main.rs2
-rw-r--r--examples/geometry/src/main.rs41
-rw-r--r--examples/lazy/Cargo.toml10
-rw-r--r--examples/lazy/src/main.rs139
-rw-r--r--examples/modal/Cargo.toml10
-rw-r--r--examples/modal/src/main.rs475
-rw-r--r--examples/modern_art/Cargo.toml11
-rw-r--r--examples/modern_art/src/main.rs142
-rw-r--r--examples/multitouch/src/main.rs5
-rw-r--r--examples/pane_grid/src/main.rs40
-rw-r--r--examples/scrollable/src/main.rs23
-rw-r--r--examples/solar_system/src/main.rs57
-rw-r--r--examples/styling/src/main.rs57
-rw-r--r--examples/svg/src/main.rs71
-rw-r--r--examples/todos/src/main.rs6
21 files changed, 1348 insertions, 128 deletions
diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs
index 0c619dc9..7b6ea0e1 100644
--- a/examples/arc/src/main.rs
+++ b/examples/arc/src/main.rs
@@ -2,7 +2,7 @@ use std::{f32::consts::PI, time::Instant};
use iced::executor;
use iced::widget::canvas::{
- self, Cache, Canvas, Cursor, Geometry, Path, Stroke,
+ self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke,
};
use iced::{
Application, Command, Element, Length, Point, Rectangle, Settings,
@@ -52,11 +52,6 @@ impl Application for Arc {
Command::none()
}
- fn subscription(&self) -> Subscription<Message> {
- iced::time::every(std::time::Duration::from_millis(10))
- .map(|_| Message::Tick)
- }
-
fn view(&self) -> Element<Message> {
Canvas::new(self)
.width(Length::Fill)
@@ -67,6 +62,11 @@ impl Application for Arc {
fn theme(&self) -> Theme {
Theme::Dark
}
+
+ fn subscription(&self) -> Subscription<Message> {
+ iced::time::every(std::time::Duration::from_millis(10))
+ .map(|_| Message::Tick)
+ }
}
impl<Message> canvas::Program<Message> for Arc {
@@ -114,7 +114,7 @@ impl<Message> canvas::Program<Message> for Arc {
frame.stroke(
&path,
Stroke {
- color: palette.text,
+ style: stroke::Style::Solid(palette.text),
width: 10.0,
..Stroke::default()
},
diff --git a/examples/cached/Cargo.toml b/examples/cached/Cargo.toml
new file mode 100644
index 00000000..2c7edde2
--- /dev/null
+++ b/examples/cached/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "cached"
+version = "0.1.0"
+authors = ["Nick Senger <dev@nsenger.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["debug"] }
+iced_lazy = { path = "../../lazy" }
diff --git a/examples/cached/src/main.rs b/examples/cached/src/main.rs
new file mode 100644
index 00000000..8845b874
--- /dev/null
+++ b/examples/cached/src/main.rs
@@ -0,0 +1,139 @@
+use iced::theme;
+use iced::widget::{
+ button, column, horizontal_space, row, scrollable, text, text_input,
+};
+use iced::{Element, Length, Sandbox, Settings};
+use iced_lazy::lazy;
+
+use std::collections::HashSet;
+
+pub fn main() -> iced::Result {
+ App::run(Settings::default())
+}
+
+struct App {
+ options: HashSet<String>,
+ input: String,
+ order: Order,
+}
+
+impl Default for App {
+ fn default() -> Self {
+ Self {
+ options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"]
+ .into_iter()
+ .map(ToString::to_string)
+ .collect(),
+ input: Default::default(),
+ order: Order::Ascending,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ InputChanged(String),
+ ToggleOrder,
+ DeleteOption(String),
+ AddOption(String),
+}
+
+impl Sandbox for App {
+ type Message = Message;
+
+ fn new() -> Self {
+ Self::default()
+ }
+
+ fn title(&self) -> String {
+ String::from("Cached - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::InputChanged(input) => {
+ self.input = input;
+ }
+ Message::ToggleOrder => {
+ self.order = match self.order {
+ Order::Ascending => Order::Descending,
+ Order::Descending => Order::Ascending,
+ }
+ }
+ Message::AddOption(option) => {
+ self.options.insert(option);
+ self.input.clear();
+ }
+ Message::DeleteOption(option) => {
+ self.options.remove(&option);
+ }
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let options = lazy((&self.order, self.options.len()), || {
+ let mut options: Vec<_> = self.options.iter().collect();
+
+ options.sort_by(|a, b| match self.order {
+ Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()),
+ Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()),
+ });
+
+ column(
+ options
+ .into_iter()
+ .map(|option| {
+ row![
+ text(option),
+ horizontal_space(Length::Fill),
+ button("Delete")
+ .on_press(Message::DeleteOption(
+ option.to_string(),
+ ),)
+ .style(theme::Button::Destructive)
+ ]
+ .into()
+ })
+ .collect(),
+ )
+ .spacing(10)
+ });
+
+ column![
+ scrollable(options).height(Length::Fill),
+ row![
+ text_input(
+ "Add a new option",
+ &self.input,
+ Message::InputChanged,
+ )
+ .on_submit(Message::AddOption(self.input.clone())),
+ button(text(format!("Toggle Order ({})", self.order)))
+ .on_press(Message::ToggleOrder)
+ ]
+ .spacing(10)
+ ]
+ .spacing(20)
+ .padding(20)
+ .into()
+ }
+}
+
+#[derive(Debug, Hash)]
+enum Order {
+ Ascending,
+ Descending,
+}
+
+impl std::fmt::Display for Order {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Self::Ascending => "Ascending",
+ Self::Descending => "Descending",
+ }
+ )
+ }
+}
diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs
index 8818fb54..a389c54f 100644
--- a/examples/clock/src/main.rs
+++ b/examples/clock/src/main.rs
@@ -1,5 +1,7 @@
use iced::executor;
-use iced::widget::canvas::{Cache, Cursor, Geometry, LineCap, Path, Stroke};
+use iced::widget::canvas::{
+ stroke, Cache, Cursor, Geometry, LineCap, Path, Stroke,
+};
use iced::widget::{canvas, container};
use iced::{
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
@@ -24,9 +26,9 @@ enum Message {
}
impl Application for Clock {
+ type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
- type Executor = executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
@@ -59,15 +61,6 @@ impl Application for Clock {
Command::none()
}
- fn subscription(&self) -> Subscription<Message> {
- iced::time::every(std::time::Duration::from_millis(500)).map(|_| {
- Message::Tick(
- time::OffsetDateTime::now_local()
- .unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
- )
- })
- }
-
fn view(&self) -> Element<Message> {
let canvas = canvas(self as &Self)
.width(Length::Fill)
@@ -79,6 +72,15 @@ impl Application for Clock {
.padding(20)
.into()
}
+
+ fn subscription(&self) -> Subscription<Message> {
+ iced::time::every(std::time::Duration::from_millis(500)).map(|_| {
+ Message::Tick(
+ time::OffsetDateTime::now_local()
+ .unwrap_or_else(|_| time::OffsetDateTime::now_utc()),
+ )
+ })
+ }
}
impl<Message> canvas::Program<Message> for Clock {
@@ -104,33 +106,41 @@ impl<Message> canvas::Program<Message> for Clock {
let long_hand =
Path::line(Point::ORIGIN, Point::new(0.0, -0.8 * radius));
- let thin_stroke = Stroke {
- width: radius / 100.0,
- color: Color::WHITE,
- line_cap: LineCap::Round,
- ..Stroke::default()
+ let width = radius / 100.0;
+
+ let thin_stroke = || -> Stroke {
+ Stroke {
+ width,
+ style: stroke::Style::Solid(Color::WHITE),
+ line_cap: LineCap::Round,
+ ..Stroke::default()
+ }
};
- let wide_stroke = Stroke {
- width: thin_stroke.width * 3.0,
- ..thin_stroke
+ let wide_stroke = || -> Stroke {
+ Stroke {
+ width: width * 3.0,
+ style: stroke::Style::Solid(Color::WHITE),
+ line_cap: LineCap::Round,
+ ..Stroke::default()
+ }
};
frame.translate(Vector::new(center.x, center.y));
frame.with_save(|frame| {
frame.rotate(hand_rotation(self.now.hour(), 12));
- frame.stroke(&short_hand, wide_stroke);
+ frame.stroke(&short_hand, wide_stroke());
});
frame.with_save(|frame| {
frame.rotate(hand_rotation(self.now.minute(), 60));
- frame.stroke(&long_hand, wide_stroke);
+ frame.stroke(&long_hand, wide_stroke());
});
frame.with_save(|frame| {
frame.rotate(hand_rotation(self.now.second(), 60));
- frame.stroke(&long_hand, thin_stroke);
+ frame.stroke(&long_hand, thin_stroke());
})
});
diff --git a/examples/custom_quad/Cargo.toml b/examples/custom_quad/Cargo.toml
new file mode 100644
index 00000000..39154786
--- /dev/null
+++ b/examples/custom_quad/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "custom_quad"
+version = "0.1.0"
+authors = ["Robert Krahn"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../.." }
+iced_native = { path = "../../native" }
diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs
new file mode 100644
index 00000000..6509887c
--- /dev/null
+++ b/examples/custom_quad/src/main.rs
@@ -0,0 +1,160 @@
+//! This example showcases a drawing a quad.
+mod quad {
+ use iced_native::layout::{self, Layout};
+ use iced_native::renderer;
+ use iced_native::widget::{self, Widget};
+ use iced_native::{Color, Element, Length, Point, Rectangle, Size};
+
+ pub struct CustomQuad {
+ size: f32,
+ radius: [f32; 4],
+ border_width: f32,
+ }
+
+ impl CustomQuad {
+ pub fn new(size: f32, radius: [f32; 4], border_width: f32) -> Self {
+ Self {
+ size,
+ radius,
+ border_width,
+ }
+ }
+ }
+
+ impl<Message, Renderer> Widget<Message, Renderer> for CustomQuad
+ where
+ Renderer: renderer::Renderer,
+ {
+ fn width(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn height(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn layout(
+ &self,
+ _renderer: &Renderer,
+ _limits: &layout::Limits,
+ ) -> layout::Node {
+ layout::Node::new(Size::new(self.size, self.size))
+ }
+
+ fn draw(
+ &self,
+ _state: &widget::Tree,
+ renderer: &mut Renderer,
+ _theme: &Renderer::Theme,
+ _style: &renderer::Style,
+ layout: Layout<'_>,
+ _cursor_position: Point,
+ _viewport: &Rectangle,
+ ) {
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: layout.bounds(),
+ border_radius: self.radius.into(),
+ border_width: self.border_width,
+ border_color: Color::from_rgb(1.0, 0.0, 0.0),
+ },
+ Color::BLACK,
+ );
+ }
+ }
+
+ impl<'a, Message, Renderer> From<CustomQuad> for Element<'a, Message, Renderer>
+ where
+ Renderer: renderer::Renderer,
+ {
+ fn from(circle: CustomQuad) -> Self {
+ Self::new(circle)
+ }
+ }
+}
+
+use iced::widget::{column, container, slider, text};
+use iced::{Alignment, Element, Length, Sandbox, Settings};
+
+pub fn main() -> iced::Result {
+ Example::run(Settings::default())
+}
+
+struct Example {
+ radius: [f32; 4],
+ border_width: f32,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[allow(clippy::enum_variant_names)]
+enum Message {
+ RadiusTopLeftChanged(f32),
+ RadiusTopRightChanged(f32),
+ RadiusBottomRightChanged(f32),
+ RadiusBottomLeftChanged(f32),
+ BorderWidthChanged(f32),
+}
+
+impl Sandbox for Example {
+ type Message = Message;
+
+ fn new() -> Self {
+ Self {
+ radius: [50.0; 4],
+ border_width: 0.0,
+ }
+ }
+
+ fn title(&self) -> String {
+ String::from("Custom widget - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ let [tl, tr, br, bl] = self.radius;
+ match message {
+ Message::RadiusTopLeftChanged(radius) => {
+ self.radius = [radius, tr, br, bl];
+ }
+ Message::RadiusTopRightChanged(radius) => {
+ self.radius = [tl, radius, br, bl];
+ }
+ Message::RadiusBottomRightChanged(radius) => {
+ self.radius = [tl, tr, radius, bl];
+ }
+ Message::RadiusBottomLeftChanged(radius) => {
+ self.radius = [tl, tr, br, radius];
+ }
+ Message::BorderWidthChanged(width) => {
+ self.border_width = width;
+ }
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let [tl, tr, br, bl] = self.radius;
+
+ let content = column![
+ quad::CustomQuad::new(200.0, self.radius, self.border_width),
+ text(format!("Radius: {tl:.2}/{tr:.2}/{br:.2}/{bl:.2}")),
+ slider(1.0..=100.0, tl, Message::RadiusTopLeftChanged).step(0.01),
+ slider(1.0..=100.0, tr, Message::RadiusTopRightChanged).step(0.01),
+ slider(1.0..=100.0, br, Message::RadiusBottomRightChanged)
+ .step(0.01),
+ slider(1.0..=100.0, bl, Message::RadiusBottomLeftChanged)
+ .step(0.01),
+ slider(1.0..=10.0, self.border_width, Message::BorderWidthChanged)
+ .step(0.01),
+ ]
+ .padding(20)
+ .spacing(20)
+ .max_width(500)
+ .align_items(Alignment::Center);
+
+ container(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y()
+ .into()
+ }
+}
diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs
index c37a1a12..f6bb3b1e 100644
--- a/examples/custom_widget/src/main.rs
+++ b/examples/custom_widget/src/main.rs
@@ -61,7 +61,7 @@ mod circle {
renderer.fill_quad(
renderer::Quad {
bounds: layout.bounds(),
- border_radius: self.radius,
+ border_radius: self.radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index d8b99ab3..9bacce7f 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -11,22 +11,18 @@ mod rainbow {
// if you wish to, by creating your own `Renderer` trait, which could be
// implemented by `iced_wgpu` and other renderers.
use iced_graphics::renderer::{self, Renderer};
+ use iced_graphics::triangle::ColoredVertex2D;
use iced_graphics::{Backend, Primitive};
+ use iced_native::layout;
use iced_native::widget::{self, Widget};
use iced_native::{
- layout, Element, Layout, Length, Point, Rectangle, Size, Vector,
+ Element, Layout, Length, Point, Rectangle, Size, Vector,
};
- #[derive(Default)]
+ #[derive(Debug, Clone, Copy, Default)]
pub struct Rainbow;
- impl Rainbow {
- pub fn new() -> Self {
- Self
- }
- }
-
pub fn rainbow() -> Rainbow {
Rainbow
}
@@ -63,7 +59,7 @@ mod rainbow {
cursor_position: Point,
_viewport: &Rectangle,
) {
- use iced_graphics::triangle::{Mesh2D, Vertex2D};
+ use iced_graphics::triangle::Mesh2D;
use iced_native::Renderer as _;
let b = layout.bounds();
@@ -95,43 +91,43 @@ mod rainbow {
let posn_bl = [0.0, b.height];
let posn_l = [0.0, b.height / 2.0];
- let mesh = Primitive::Mesh2D {
+ let mesh = Primitive::SolidMesh {
size: b.size(),
buffers: Mesh2D {
vertices: vec![
- Vertex2D {
+ ColoredVertex2D {
position: posn_center,
color: [1.0, 1.0, 1.0, 1.0],
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_tl,
color: color_r,
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_t,
color: color_o,
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_tr,
color: color_y,
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_r,
color: color_g,
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_br,
color: color_gb,
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_b,
color: color_b,
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_bl,
color: color_i,
},
- Vertex2D {
+ ColoredVertex2D {
position: posn_l,
color: color_v,
},
@@ -166,7 +162,7 @@ mod rainbow {
}
use iced::widget::{column, container, scrollable};
-use iced::{Alignment, Element, Length, Sandbox, Settings};
+use iced::{Element, Length, Sandbox, Settings};
use rainbow::rainbow;
pub fn main() -> iced::Result {
@@ -179,7 +175,7 @@ impl Sandbox for Example {
type Message = ();
fn new() -> Self {
- Example
+ Self
}
fn title(&self) -> String {
@@ -202,8 +198,7 @@ impl Sandbox for Example {
]
.padding(20)
.spacing(20)
- .max_width(500)
- .align_items(Alignment::Start);
+ .max_width(500);
let scrollable =
scrollable(container(content).width(Length::Fill).center_x());
diff --git a/examples/lazy/Cargo.toml b/examples/lazy/Cargo.toml
new file mode 100644
index 00000000..79255c25
--- /dev/null
+++ b/examples/lazy/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "lazy"
+version = "0.1.0"
+authors = ["Nick Senger <dev@nsenger.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["debug"] }
+iced_lazy = { path = "../../lazy" }
diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs
new file mode 100644
index 00000000..8845b874
--- /dev/null
+++ b/examples/lazy/src/main.rs
@@ -0,0 +1,139 @@
+use iced::theme;
+use iced::widget::{
+ button, column, horizontal_space, row, scrollable, text, text_input,
+};
+use iced::{Element, Length, Sandbox, Settings};
+use iced_lazy::lazy;
+
+use std::collections::HashSet;
+
+pub fn main() -> iced::Result {
+ App::run(Settings::default())
+}
+
+struct App {
+ options: HashSet<String>,
+ input: String,
+ order: Order,
+}
+
+impl Default for App {
+ fn default() -> Self {
+ Self {
+ options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"]
+ .into_iter()
+ .map(ToString::to_string)
+ .collect(),
+ input: Default::default(),
+ order: Order::Ascending,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ InputChanged(String),
+ ToggleOrder,
+ DeleteOption(String),
+ AddOption(String),
+}
+
+impl Sandbox for App {
+ type Message = Message;
+
+ fn new() -> Self {
+ Self::default()
+ }
+
+ fn title(&self) -> String {
+ String::from("Cached - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::InputChanged(input) => {
+ self.input = input;
+ }
+ Message::ToggleOrder => {
+ self.order = match self.order {
+ Order::Ascending => Order::Descending,
+ Order::Descending => Order::Ascending,
+ }
+ }
+ Message::AddOption(option) => {
+ self.options.insert(option);
+ self.input.clear();
+ }
+ Message::DeleteOption(option) => {
+ self.options.remove(&option);
+ }
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let options = lazy((&self.order, self.options.len()), || {
+ let mut options: Vec<_> = self.options.iter().collect();
+
+ options.sort_by(|a, b| match self.order {
+ Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()),
+ Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()),
+ });
+
+ column(
+ options
+ .into_iter()
+ .map(|option| {
+ row![
+ text(option),
+ horizontal_space(Length::Fill),
+ button("Delete")
+ .on_press(Message::DeleteOption(
+ option.to_string(),
+ ),)
+ .style(theme::Button::Destructive)
+ ]
+ .into()
+ })
+ .collect(),
+ )
+ .spacing(10)
+ });
+
+ column![
+ scrollable(options).height(Length::Fill),
+ row![
+ text_input(
+ "Add a new option",
+ &self.input,
+ Message::InputChanged,
+ )
+ .on_submit(Message::AddOption(self.input.clone())),
+ button(text(format!("Toggle Order ({})", self.order)))
+ .on_press(Message::ToggleOrder)
+ ]
+ .spacing(10)
+ ]
+ .spacing(20)
+ .padding(20)
+ .into()
+ }
+}
+
+#[derive(Debug, Hash)]
+enum Order {
+ Ascending,
+ Descending,
+}
+
+impl std::fmt::Display for Order {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ Self::Ascending => "Ascending",
+ Self::Descending => "Descending",
+ }
+ )
+ }
+}
diff --git a/examples/modal/Cargo.toml b/examples/modal/Cargo.toml
new file mode 100644
index 00000000..8770acac
--- /dev/null
+++ b/examples/modal/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "modal"
+version = "0.1.0"
+authors = ["tarkah <admin@tarkah.dev>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = [] }
+iced_native = { path = "../../native" }
diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs
new file mode 100644
index 00000000..2f20795c
--- /dev/null
+++ b/examples/modal/src/main.rs
@@ -0,0 +1,475 @@
+use iced::widget::{
+ self, button, column, container, horizontal_space, row, text, text_input,
+};
+use iced::{
+ executor, keyboard, subscription, theme, Alignment, Application, Command,
+ Element, Event, Length, Settings, Subscription,
+};
+
+use self::modal::Modal;
+
+pub fn main() -> iced::Result {
+ App::run(Settings::default())
+}
+
+#[derive(Default)]
+struct App {
+ show_modal: bool,
+ email: String,
+ password: String,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ ShowModal,
+ HideModal,
+ Email(String),
+ Password(String),
+ Submit,
+ Event(Event),
+}
+
+impl Application for App {
+ type Executor = executor::Default;
+ type Message = Message;
+ type Theme = iced::Theme;
+ type Flags = ();
+
+ fn new(_flags: ()) -> (Self, Command<Message>) {
+ (App::default(), Command::none())
+ }
+
+ fn title(&self) -> String {
+ String::from("Modal - Iced")
+ }
+
+ fn subscription(&self) -> Subscription<Self::Message> {
+ subscription::events().map(Message::Event)
+ }
+
+ fn update(&mut self, message: Message) -> Command<Message> {
+ match message {
+ Message::ShowModal => {
+ self.show_modal = true;
+ widget::focus_next()
+ }
+ Message::HideModal => {
+ self.hide_modal();
+ Command::none()
+ }
+ Message::Email(email) => {
+ self.email = email;
+ Command::none()
+ }
+ Message::Password(password) => {
+ self.password = password;
+ Command::none()
+ }
+ Message::Submit => {
+ if !self.email.is_empty() && !self.password.is_empty() {
+ self.hide_modal();
+ }
+
+ Command::none()
+ }
+ Message::Event(event) => match event {
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ key_code: keyboard::KeyCode::Tab,
+ modifiers,
+ }) => {
+ if modifiers.shift() {
+ widget::focus_previous()
+ } else {
+ widget::focus_next()
+ }
+ }
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ key_code: keyboard::KeyCode::Escape,
+ ..
+ }) => {
+ self.hide_modal();
+ Command::none()
+ }
+ _ => Command::none(),
+ },
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let content = container(
+ column![
+ row![
+ text("Top Left"),
+ horizontal_space(Length::Fill),
+ text("Top Right")
+ ]
+ .align_items(Alignment::Start)
+ .height(Length::Fill),
+ container(
+ button(text("Show Modal")).on_press(Message::ShowModal)
+ )
+ .center_x()
+ .center_y()
+ .width(Length::Fill)
+ .height(Length::Fill),
+ row![
+ text("Bottom Left"),
+ horizontal_space(Length::Fill),
+ text("Bottom Right")
+ ]
+ .align_items(Alignment::End)
+ .height(Length::Fill),
+ ]
+ .height(Length::Fill),
+ )
+ .padding(10)
+ .width(Length::Fill)
+ .height(Length::Fill);
+
+ if self.show_modal {
+ let modal = container(
+ column![
+ text("Sign Up").size(24),
+ column![
+ column![
+ text("Email").size(12),
+ text_input(
+ "abc@123.com",
+ &self.email,
+ Message::Email
+ )
+ .on_submit(Message::Submit)
+ .padding(5),
+ ]
+ .spacing(5),
+ column![
+ text("Password").size(12),
+ text_input("", &self.password, Message::Password)
+ .on_submit(Message::Submit)
+ .password()
+ .padding(5),
+ ]
+ .spacing(5),
+ button(text("Submit")).on_press(Message::HideModal),
+ ]
+ .spacing(10)
+ ]
+ .spacing(20),
+ )
+ .width(Length::Units(300))
+ .padding(10)
+ .style(theme::Container::Box);
+
+ Modal::new(content, modal)
+ .on_blur(Message::HideModal)
+ .into()
+ } else {
+ content.into()
+ }
+ }
+}
+
+impl App {
+ fn hide_modal(&mut self) {
+ self.show_modal = false;
+ self.email.clear();
+ self.password.clear();
+ }
+}
+
+mod modal {
+ use iced_native::alignment::Alignment;
+ use iced_native::widget::{self, Tree};
+ use iced_native::{
+ event, layout, mouse, overlay, renderer, Clipboard, Color, Element,
+ Event, Layout, Length, Point, Rectangle, Shell, Size, Widget,
+ };
+
+ /// A widget that centers a modal element over some base element
+ pub struct Modal<'a, Message, Renderer> {
+ base: Element<'a, Message, Renderer>,
+ modal: Element<'a, Message, Renderer>,
+ on_blur: Option<Message>,
+ }
+
+ impl<'a, Message, Renderer> Modal<'a, Message, Renderer> {
+ /// Returns a new [`Modal`]
+ pub fn new(
+ base: impl Into<Element<'a, Message, Renderer>>,
+ modal: impl Into<Element<'a, Message, Renderer>>,
+ ) -> Self {
+ Self {
+ base: base.into(),
+ modal: modal.into(),
+ on_blur: None,
+ }
+ }
+
+ /// Sets the message that will be produces when the background
+ /// of the [`Modal`] is pressed
+ pub fn on_blur(self, on_blur: Message) -> Self {
+ Self {
+ on_blur: Some(on_blur),
+ ..self
+ }
+ }
+ }
+
+ impl<'a, Message, Renderer> Widget<Message, Renderer>
+ for Modal<'a, Message, Renderer>
+ where
+ Renderer: iced_native::Renderer,
+ Message: Clone,
+ {
+ fn children(&self) -> Vec<Tree> {
+ vec![Tree::new(&self.base), Tree::new(&self.modal)]
+ }
+
+ fn diff(&self, tree: &mut Tree) {
+ tree.diff_children(&[&self.base, &self.modal]);
+ }
+
+ fn width(&self) -> Length {
+ self.base.as_widget().width()
+ }
+
+ fn height(&self) -> Length {
+ self.base.as_widget().height()
+ }
+
+ fn layout(
+ &self,
+ renderer: &Renderer,
+ limits: &layout::Limits,
+ ) -> layout::Node {
+ self.base.as_widget().layout(renderer, limits)
+ }
+
+ fn on_event(
+ &mut self,
+ state: &mut Tree,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ renderer: &Renderer,
+ clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ self.base.as_widget_mut().on_event(
+ &mut state.children[0],
+ event,
+ layout,
+ cursor_position,
+ renderer,
+ clipboard,
+ shell,
+ )
+ }
+
+ fn draw(
+ &self,
+ state: &Tree,
+ renderer: &mut Renderer,
+ theme: &<Renderer as iced_native::Renderer>::Theme,
+ style: &renderer::Style,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ viewport: &Rectangle,
+ ) {
+ self.base.as_widget().draw(
+ &state.children[0],
+ renderer,
+ theme,
+ style,
+ layout,
+ cursor_position,
+ viewport,
+ );
+ }
+
+ fn overlay<'b>(
+ &'b mut self,
+ state: &'b mut Tree,
+ layout: Layout<'_>,
+ _renderer: &Renderer,
+ ) -> Option<overlay::Element<'b, Message, Renderer>> {
+ Some(overlay::Element::new(
+ layout.position(),
+ Box::new(Overlay {
+ content: &mut self.modal,
+ tree: &mut state.children[1],
+ size: layout.bounds().size(),
+ on_blur: self.on_blur.clone(),
+ }),
+ ))
+ }
+
+ fn mouse_interaction(
+ &self,
+ state: &Tree,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ viewport: &Rectangle,
+ renderer: &Renderer,
+ ) -> mouse::Interaction {
+ self.base.as_widget().mouse_interaction(
+ &state.children[0],
+ layout,
+ cursor_position,
+ viewport,
+ renderer,
+ )
+ }
+
+ fn operate(
+ &self,
+ state: &mut Tree,
+ layout: Layout<'_>,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ self.base.as_widget().operate(
+ &mut state.children[0],
+ layout,
+ operation,
+ );
+ }
+ }
+
+ struct Overlay<'a, 'b, Message, Renderer> {
+ content: &'b mut Element<'a, Message, Renderer>,
+ tree: &'b mut Tree,
+ size: Size,
+ on_blur: Option<Message>,
+ }
+
+ impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
+ for Overlay<'a, 'b, Message, Renderer>
+ where
+ Renderer: iced_native::Renderer,
+ Message: Clone,
+ {
+ fn layout(
+ &self,
+ renderer: &Renderer,
+ _bounds: Size,
+ position: Point,
+ ) -> layout::Node {
+ let limits = layout::Limits::new(Size::ZERO, self.size)
+ .width(Length::Fill)
+ .height(Length::Fill);
+
+ let mut child = self.content.as_widget().layout(renderer, &limits);
+ child.align(Alignment::Center, Alignment::Center, limits.max());
+
+ let mut node = layout::Node::with_children(self.size, vec![child]);
+ node.move_to(position);
+
+ node
+ }
+
+ fn on_event(
+ &mut self,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ renderer: &Renderer,
+ clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ let content_bounds = layout.children().next().unwrap().bounds();
+
+ if let Some(message) = self.on_blur.as_ref() {
+ if let Event::Mouse(mouse::Event::ButtonPressed(
+ mouse::Button::Left,
+ )) = &event
+ {
+ if !content_bounds.contains(cursor_position) {
+ shell.publish(message.clone());
+ return event::Status::Captured;
+ }
+ }
+ }
+
+ self.content.as_widget_mut().on_event(
+ self.tree,
+ event,
+ layout.children().next().unwrap(),
+ cursor_position,
+ renderer,
+ clipboard,
+ shell,
+ )
+ }
+
+ fn draw(
+ &self,
+ renderer: &mut Renderer,
+ theme: &Renderer::Theme,
+ style: &renderer::Style,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ ) {
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: layout.bounds(),
+ border_radius: renderer::BorderRadius::from(0.0),
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ Color {
+ a: 0.80,
+ ..Color::BLACK
+ },
+ );
+
+ self.content.as_widget().draw(
+ self.tree,
+ renderer,
+ theme,
+ style,
+ layout.children().next().unwrap(),
+ cursor_position,
+ &layout.bounds(),
+ );
+ }
+
+ fn operate(
+ &mut self,
+ layout: Layout<'_>,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ self.content.as_widget().operate(
+ self.tree,
+ layout.children().next().unwrap(),
+ operation,
+ );
+ }
+
+ fn mouse_interaction(
+ &self,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ viewport: &Rectangle,
+ renderer: &Renderer,
+ ) -> mouse::Interaction {
+ self.content.as_widget().mouse_interaction(
+ self.tree,
+ layout.children().next().unwrap(),
+ cursor_position,
+ viewport,
+ renderer,
+ )
+ }
+ }
+
+ impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>>
+ for Element<'a, Message, Renderer>
+ where
+ Renderer: 'a + iced_native::Renderer,
+ Message: 'a + Clone,
+ {
+ fn from(modal: Modal<'a, Message, Renderer>) -> Self {
+ Element::new(modal)
+ }
+ }
+}
diff --git a/examples/modern_art/Cargo.toml b/examples/modern_art/Cargo.toml
new file mode 100644
index 00000000..4242d209
--- /dev/null
+++ b/examples/modern_art/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "modern_art"
+version = "0.1.0"
+authors = ["Bingus <shankern@protonmail.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
+rand = "0.8.5"
+env_logger = "0.9"
diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs
new file mode 100644
index 00000000..28ed3e21
--- /dev/null
+++ b/examples/modern_art/src/main.rs
@@ -0,0 +1,142 @@
+use iced::widget::canvas::{
+ self, gradient::Location, gradient::Position, Cache, Canvas, Cursor, Frame,
+ Geometry, Gradient,
+};
+use iced::{
+ executor, Application, Color, Command, Element, Length, Point, Rectangle,
+ Renderer, Settings, Size, Theme,
+};
+use rand::{thread_rng, Rng};
+
+fn main() -> iced::Result {
+ env_logger::builder().format_timestamp(None).init();
+
+ ModernArt::run(Settings {
+ antialiasing: true,
+ ..Settings::default()
+ })
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Message {}
+
+struct ModernArt {
+ cache: Cache,
+}
+
+impl Application for ModernArt {
+ type Executor = executor::Default;
+ type Message = Message;
+ type Theme = Theme;
+ type Flags = ();
+
+ fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
+ (
+ ModernArt {
+ cache: Default::default(),
+ },
+ Command::none(),
+ )
+ }
+
+ fn title(&self) -> String {
+ String::from("Modern Art")
+ }
+
+ fn update(&mut self, _message: Message) -> Command<Message> {
+ Command::none()
+ }
+
+ fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
+ Canvas::new(self)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .into()
+ }
+}
+
+impl<Message> canvas::Program<Message> for ModernArt {
+ type State = ();
+
+ fn draw(
+ &self,
+ _state: &Self::State,
+ _theme: &Theme,
+ bounds: Rectangle,
+ _cursor: Cursor,
+ ) -> Vec<Geometry> {
+ let geometry = self.cache.draw(bounds.size(), |frame| {
+ let num_squares = thread_rng().gen_range(0..1200);
+
+ let mut i = 0;
+ while i <= num_squares {
+ generate_box(frame, bounds.size());
+ i += 1;
+ }
+ });
+
+ vec![geometry]
+ }
+}
+
+fn random_direction() -> Location {
+ match thread_rng().gen_range(0..8) {
+ 0 => Location::TopLeft,
+ 1 => Location::Top,
+ 2 => Location::TopRight,
+ 3 => Location::Right,
+ 4 => Location::BottomRight,
+ 5 => Location::Bottom,
+ 6 => Location::BottomLeft,
+ 7 => Location::Left,
+ _ => Location::TopLeft,
+ }
+}
+
+fn generate_box(frame: &mut Frame, bounds: Size) -> bool {
+ let solid = rand::random::<bool>();
+
+ let random_color = || -> Color {
+ Color::from_rgb(
+ thread_rng().gen_range(0.0..1.0),
+ thread_rng().gen_range(0.0..1.0),
+ thread_rng().gen_range(0.0..1.0),
+ )
+ };
+
+ let gradient = |top_left: Point, size: Size| -> Gradient {
+ let mut builder = Gradient::linear(Position::Relative {
+ top_left,
+ size,
+ start: random_direction(),
+ end: random_direction(),
+ });
+ let stops = thread_rng().gen_range(1..15u32);
+
+ let mut i = 0;
+ while i <= stops {
+ builder = builder.add_stop(i as f32 / stops as f32, random_color());
+ i += 1;
+ }
+
+ builder.build().unwrap()
+ };
+
+ let top_left = Point::new(
+ thread_rng().gen_range(0.0..bounds.width),
+ thread_rng().gen_range(0.0..bounds.height),
+ );
+
+ let size = Size::new(
+ thread_rng().gen_range(50.0..200.0),
+ thread_rng().gen_range(50.0..200.0),
+ );
+
+ if solid {
+ frame.fill_rectangle(top_left, size, random_color());
+ } else {
+ frame.fill_rectangle(top_left, size, gradient(top_left, size));
+ };
+
+ solid
+}
diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs
index 0345ceb7..f5faae0f 100644
--- a/examples/multitouch/src/main.rs
+++ b/examples/multitouch/src/main.rs
@@ -2,7 +2,8 @@
//! a circle around each fingertip. This only works on touch-enabled
//! computers like Microsoft Surface.
use iced::widget::canvas::event;
-use iced::widget::canvas::{self, Canvas, Cursor, Geometry, Stroke};
+use iced::widget::canvas::stroke::{self, Stroke};
+use iced::widget::canvas::{self, Canvas, Cursor, Geometry};
use iced::{
executor, touch, window, Application, Color, Command, Element, Length,
Point, Rectangle, Settings, Subscription, Theme,
@@ -186,7 +187,7 @@ impl canvas::Program<Message> for State {
frame.stroke(
&path,
Stroke {
- color: Color::BLACK,
+ style: stroke::Style::Solid(Color::BLACK),
width: 3.0,
..Stroke::default()
},
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index ae8fa22b..c9f1376c 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -29,6 +29,8 @@ enum Message {
Dragged(pane_grid::DragEvent),
Resized(pane_grid::ResizeEvent),
TogglePin(pane_grid::Pane),
+ Maximize(pane_grid::Pane),
+ Restore,
Close(pane_grid::Pane),
CloseFocused,
}
@@ -114,6 +116,10 @@ impl Application for Example {
*is_pinned = !*is_pinned;
}
}
+ Message::Maximize(pane) => self.panes.maximize(&pane),
+ Message::Restore => {
+ self.panes.restore();
+ }
Message::Close(pane) => {
if let Some((_, sibling)) = self.panes.close(&pane) {
self.focus = Some(sibling);
@@ -157,7 +163,7 @@ impl Application for Example {
let focus = self.focus;
let total_panes = self.panes.len();
- let pane_grid = PaneGrid::new(&self.panes, |id, pane| {
+ let pane_grid = PaneGrid::new(&self.panes, |id, pane, is_maximized| {
let is_focused = focus == Some(id);
let pin_button = button(
@@ -178,7 +184,12 @@ impl Application for Example {
.spacing(5);
let title_bar = pane_grid::TitleBar::new(title)
- .controls(view_controls(id, total_panes, pane.is_pinned))
+ .controls(view_controls(
+ id,
+ total_panes,
+ pane.is_pinned,
+ is_maximized,
+ ))
.padding(10)
.style(if is_focused {
style::title_bar_focused
@@ -314,16 +325,35 @@ fn view_controls<'a>(
pane: pane_grid::Pane,
total_panes: usize,
is_pinned: bool,
+ is_maximized: bool,
) -> Element<'a, Message> {
- let mut button = button(text("Close").size(14))
+ let mut row = row![].spacing(5);
+
+ if total_panes > 1 {
+ let toggle = {
+ let (content, message) = if is_maximized {
+ ("Restore", Message::Restore)
+ } else {
+ ("Maximize", Message::Maximize(pane))
+ };
+ button(text(content).size(14))
+ .style(theme::Button::Secondary)
+ .padding(3)
+ .on_press(message)
+ };
+
+ row = row.push(toggle);
+ }
+
+ let mut close = button(text("Close").size(14))
.style(theme::Button::Destructive)
.padding(3);
if total_panes > 1 && !is_pinned {
- button = button.on_press(Message::Close(pane));
+ close = close.on_press(Message::Close(pane));
}
- button.into()
+ row.push(close).into()
}
mod style {
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index b7b3dedc..6eba34e2 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -14,9 +14,15 @@ struct ScrollableDemo {
variants: Vec<Variant>,
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum ThemeType {
+ Light,
+ Dark,
+}
+
#[derive(Debug, Clone)]
enum Message {
- ThemeChanged(Theme),
+ ThemeChanged(ThemeType),
ScrollToTop(usize),
ScrollToBottom(usize),
Scrolled(usize, f32),
@@ -45,7 +51,10 @@ impl Application for ScrollableDemo {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::ThemeChanged(theme) => {
- self.theme = theme;
+ self.theme = match theme {
+ ThemeType::Light => Theme::Light,
+ ThemeType::Dark => Theme::Dark,
+ };
Command::none()
}
@@ -78,17 +87,15 @@ impl Application for ScrollableDemo {
}
fn view(&self) -> Element<Message> {
- let ScrollableDemo {
- theme, variants, ..
- } = self;
+ let ScrollableDemo { variants, .. } = self;
- let choose_theme = [Theme::Light, Theme::Dark].iter().fold(
+ let choose_theme = [ThemeType::Light, ThemeType::Dark].iter().fold(
column!["Choose a theme:"].spacing(10),
|column, option| {
column.push(radio(
format!("{:?}", option),
*option,
- Some(*theme),
+ Some(*option),
Message::ThemeChanged,
))
},
@@ -198,7 +205,7 @@ impl Application for ScrollableDemo {
}
fn theme(&self) -> Theme {
- self.theme
+ self.theme.clone()
}
}
diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs
index c59d73a8..9e303576 100644
--- a/examples/solar_system/src/main.rs
+++ b/examples/solar_system/src/main.rs
@@ -11,7 +11,9 @@ use iced::executor;
use iced::theme::{self, Theme};
use iced::time;
use iced::widget::canvas;
-use iced::widget::canvas::{Cursor, Path, Stroke};
+use iced::widget::canvas::gradient::{self, Gradient};
+use iced::widget::canvas::stroke::{self, Stroke};
+use iced::widget::canvas::{Cursor, Path};
use iced::window;
use iced::{
Application, Color, Command, Element, Length, Point, Rectangle, Settings,
@@ -37,9 +39,9 @@ enum Message {
}
impl Application for SolarSystem {
+ type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
- type Executor = executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
@@ -65,10 +67,6 @@ impl Application for SolarSystem {
Command::none()
}
- fn subscription(&self) -> Subscription<Message> {
- time::every(std::time::Duration::from_millis(10)).map(Message::Tick)
- }
-
fn view(&self) -> Element<Message> {
canvas(&self.state)
.width(Length::Fill)
@@ -81,10 +79,18 @@ impl Application for SolarSystem {
}
fn style(&self) -> theme::Application {
- theme::Application::Custom(|_theme| application::Appearance {
- background_color: Color::BLACK,
- text_color: Color::WHITE,
- })
+ fn dark_background(_theme: &Theme) -> application::Appearance {
+ application::Appearance {
+ background_color: Color::BLACK,
+ text_color: Color::WHITE,
+ }
+ }
+
+ theme::Application::from(dark_background as fn(&Theme) -> _)
+ }
+
+ fn subscription(&self) -> Subscription<Message> {
+ time::every(time::Duration::from_millis(10)).map(Message::Tick)
}
}
@@ -178,8 +184,10 @@ impl<Message> canvas::Program<Message> for State {
frame.stroke(
&orbit,
Stroke {
+ style: stroke::Style::Solid(Color::from_rgba8(
+ 0, 153, 255, 0.1,
+ )),
width: 1.0,
- color: Color::from_rgba8(0, 153, 255, 0.1),
line_dash: canvas::LineDash {
offset: 0,
segments: &[3.0, 6.0],
@@ -198,15 +206,18 @@ impl<Message> canvas::Program<Message> for State {
frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0));
let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS);
- let shadow = Path::rectangle(
- Point::new(0.0, -Self::EARTH_RADIUS),
- Size::new(
- Self::EARTH_RADIUS * 4.0,
- Self::EARTH_RADIUS * 2.0,
- ),
- );
- frame.fill(&earth, Color::from_rgb8(0x6B, 0x93, 0xD6));
+ let earth_fill =
+ Gradient::linear(gradient::Position::Absolute {
+ start: Point::new(-Self::EARTH_RADIUS, 0.0),
+ end: Point::new(Self::EARTH_RADIUS, 0.0),
+ })
+ .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0))
+ .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47))
+ .build()
+ .expect("Build Earth fill gradient");
+
+ frame.fill(&earth, earth_fill);
frame.with_save(|frame| {
frame.rotate(rotation * 10.0);
@@ -215,14 +226,6 @@ impl<Message> canvas::Program<Message> for State {
let moon = Path::circle(Point::ORIGIN, Self::MOON_RADIUS);
frame.fill(&moon, Color::WHITE);
});
-
- frame.fill(
- &shadow,
- Color {
- a: 0.7,
- ..Color::BLACK
- },
- );
});
});
diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs
index cda53e87..e16860ad 100644
--- a/examples/styling/src/main.rs
+++ b/examples/styling/src/main.rs
@@ -1,9 +1,10 @@
+use iced::theme::{self, Theme};
use iced::widget::{
button, checkbox, column, container, horizontal_rule, progress_bar, radio,
row, scrollable, slider, text, text_input, toggler, vertical_rule,
vertical_space,
};
-use iced::{Alignment, Element, Length, Sandbox, Settings, Theme};
+use iced::{Alignment, Color, Element, Length, Sandbox, Settings};
pub fn main() -> iced::Result {
Styling::run(Settings::default())
@@ -18,9 +19,16 @@ struct Styling {
toggler_value: bool,
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+enum ThemeType {
+ Light,
+ Dark,
+ Custom,
+}
+
#[derive(Debug, Clone)]
enum Message {
- ThemeChanged(Theme),
+ ThemeChanged(ThemeType),
InputChanged(String),
ButtonPressed,
SliderChanged(f32),
@@ -41,7 +49,19 @@ impl Sandbox for Styling {
fn update(&mut self, message: Message) {
match message {
- Message::ThemeChanged(theme) => self.theme = theme,
+ Message::ThemeChanged(theme) => {
+ self.theme = match theme {
+ ThemeType::Light => Theme::Light,
+ ThemeType::Dark => Theme::Dark,
+ ThemeType::Custom => Theme::custom(theme::Palette {
+ background: Color::from_rgb(1.0, 0.9, 1.0),
+ text: Color::BLACK,
+ primary: Color::from_rgb(0.5, 0.5, 0.0),
+ success: Color::from_rgb(0.0, 1.0, 0.0),
+ danger: Color::from_rgb(1.0, 0.0, 0.0),
+ }),
+ }
+ }
Message::InputChanged(value) => self.input_value = value,
Message::ButtonPressed => {}
Message::SliderChanged(value) => self.slider_value = value,
@@ -51,17 +71,24 @@ impl Sandbox for Styling {
}
fn view(&self) -> Element<Message> {
- let choose_theme = [Theme::Light, Theme::Dark].iter().fold(
- column![text("Choose a theme:")].spacing(10),
- |column, theme| {
- column.push(radio(
- format!("{:?}", theme),
- *theme,
- Some(self.theme),
- Message::ThemeChanged,
- ))
- },
- );
+ let choose_theme =
+ [ThemeType::Light, ThemeType::Dark, ThemeType::Custom]
+ .iter()
+ .fold(
+ column![text("Choose a theme:")].spacing(10),
+ |column, theme| {
+ column.push(radio(
+ format!("{:?}", theme),
+ *theme,
+ Some(match self.theme {
+ Theme::Light => ThemeType::Light,
+ Theme::Dark => ThemeType::Dark,
+ Theme::Custom { .. } => ThemeType::Custom,
+ }),
+ Message::ThemeChanged,
+ ))
+ },
+ );
let text_input = text_input(
"Type something...",
@@ -132,6 +159,6 @@ impl Sandbox for Styling {
}
fn theme(&self) -> Theme {
- self.theme
+ self.theme.clone()
}
}
diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs
index 27d175da..4dc92416 100644
--- a/examples/svg/src/main.rs
+++ b/examples/svg/src/main.rs
@@ -1,39 +1,76 @@
-use iced::widget::{container, svg};
-use iced::{Element, Length, Sandbox, Settings};
+use iced::theme;
+use iced::widget::{checkbox, column, container, svg};
+use iced::{color, Element, Length, Sandbox, Settings};
pub fn main() -> iced::Result {
Tiger::run(Settings::default())
}
-struct Tiger;
+#[derive(Debug, Default)]
+struct Tiger {
+ apply_color_filter: bool,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum Message {
+ ToggleColorFilter(bool),
+}
impl Sandbox for Tiger {
- type Message = ();
+ type Message = Message;
fn new() -> Self {
- Tiger
+ Tiger::default()
}
fn title(&self) -> String {
String::from("SVG - Iced")
}
- fn update(&mut self, _message: ()) {}
+ fn update(&mut self, message: Self::Message) {
+ match message {
+ Message::ToggleColorFilter(apply_color_filter) => {
+ self.apply_color_filter = apply_color_filter;
+ }
+ }
+ }
- fn view(&self) -> Element<()> {
- let svg = svg(svg::Handle::from_path(format!(
+ fn view(&self) -> Element<Self::Message> {
+ let handle = svg::Handle::from_path(format!(
"{}/resources/tiger.svg",
env!("CARGO_MANIFEST_DIR")
- )))
- .width(Length::Fill)
- .height(Length::Fill);
+ ));
+
+ let svg = svg(handle).width(Length::Fill).height(Length::Fill).style(
+ if self.apply_color_filter {
+ theme::Svg::custom_fn(|_theme| svg::Appearance {
+ color: Some(color!(0x0000ff)),
+ })
+ } else {
+ theme::Svg::Default
+ },
+ );
- container(svg)
+ let apply_color_filter = checkbox(
+ "Apply a color filter",
+ self.apply_color_filter,
+ Message::ToggleColorFilter,
+ );
+
+ container(
+ column![
+ svg,
+ container(apply_color_filter).width(Length::Fill).center_x()
+ ]
+ .spacing(20)
.width(Length::Fill)
- .height(Length::Fill)
- .padding(20)
- .center_x()
- .center_y()
- .into()
+ .height(Length::Fill),
+ )
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .padding(20)
+ .center_x()
+ .center_y()
+ .into()
}
}
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index be48ae8c..690d9c09 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -131,7 +131,11 @@ impl Application for Todos {
task.update(task_message);
if should_focus {
- text_input::focus(Task::text_input_id(i))
+ let id = Task::text_input_id(i);
+ Command::batch(vec![
+ text_input::focus(id.clone()),
+ text_input::select_all(id),
+ ])
} else {
Command::none()
}