Merge branch 'master' into feat/slider-orientation
This commit is contained in:
commit
2e6d90f141
174 changed files with 7170 additions and 1989 deletions
|
|
@ -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()
|
||||
},
|
||||
|
|
|
|||
10
examples/cached/Cargo.toml
Normal file
10
examples/cached/Cargo.toml
Normal file
|
|
@ -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" }
|
||||
139
examples/cached/src/main.rs
Normal file
139
examples/cached/src/main.rs
Normal file
|
|
@ -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",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
|||
10
examples/custom_quad/Cargo.toml
Normal file
10
examples/custom_quad/Cargo.toml
Normal file
|
|
@ -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" }
|
||||
160
examples/custom_quad/src/main.rs
Normal file
160
examples/custom_quad/src/main.rs
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
10
examples/lazy/Cargo.toml
Normal file
10
examples/lazy/Cargo.toml
Normal file
|
|
@ -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" }
|
||||
139
examples/lazy/src/main.rs
Normal file
139
examples/lazy/src/main.rs
Normal file
|
|
@ -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",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
10
examples/modal/Cargo.toml
Normal file
10
examples/modal/Cargo.toml
Normal file
|
|
@ -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" }
|
||||
475
examples/modal/src/main.rs
Normal file
475
examples/modal/src/main.rs
Normal file
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
11
examples/modern_art/Cargo.toml
Normal file
11
examples/modern_art/Cargo.toml
Normal file
|
|
@ -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"
|
||||
142
examples/modern_art/src/main.rs
Normal file
142
examples/modern_art/src/main.rs
Normal file
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
));
|
||||
|
||||
container(svg)
|
||||
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
|
||||
},
|
||||
);
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue