Merge remote-tracking branch 'origin/master' into feat/multi-window-support
# Conflicts: # native/src/command/action.rs # native/src/window/action.rs # winit/src/window.rs
This commit is contained in:
commit
63fb608d8b
55 changed files with 1218 additions and 224 deletions
|
|
@ -177,7 +177,7 @@ impl Download {
|
|||
.into()
|
||||
}
|
||||
State::Downloading { .. } => {
|
||||
text(format!("Downloading... {:.2}%", current_progress)).into()
|
||||
text(format!("Downloading... {current_progress:.2}%")).into()
|
||||
}
|
||||
State::Errored => column![
|
||||
"Something went wrong :(",
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl Application for Events {
|
|||
let events = Column::with_children(
|
||||
self.last
|
||||
.iter()
|
||||
.map(|event| text(format!("{:?}", event)).size(40))
|
||||
.map(|event| text(format!("{event:?}")).size(40))
|
||||
.map(Element::from)
|
||||
.collect(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ fn view_controls<'a>(
|
|||
|
||||
let speed_controls = row![
|
||||
slider(1.0..=1000.0, speed as f32, Message::SpeedChanged),
|
||||
text(format!("x{}", speed)).size(16),
|
||||
text(format!("x{speed}")).size(16),
|
||||
]
|
||||
.width(Length::Fill)
|
||||
.align_items(Alignment::Center)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum Preset {
|
||||
Custom,
|
||||
#[default]
|
||||
Xkcd,
|
||||
Glider,
|
||||
SmallExploder,
|
||||
|
|
@ -114,12 +115,6 @@ impl Preset {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Preset {
|
||||
fn default() -> Preset {
|
||||
Preset::Xkcd
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Preset {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ impl Program for Controls {
|
|||
)
|
||||
.push(sliders)
|
||||
.push(
|
||||
Text::new(format!("{:?}", background_color))
|
||||
Text::new(format!("{background_color:?}"))
|
||||
.size(14)
|
||||
.style(Color::WHITE),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ impl Scene {
|
|||
.expect("Cannot create shader");
|
||||
gl.shader_source(
|
||||
shader,
|
||||
&format!("{}\n{}", shader_version, shader_source),
|
||||
&format!("{shader_version}\n{shader_source}"),
|
||||
);
|
||||
gl.compile_shader(shader);
|
||||
if !gl.get_shader_compile_status(shader) {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ impl Program for Controls {
|
|||
)
|
||||
.push(sliders)
|
||||
.push(
|
||||
Text::new(format!("{:?}", background_color))
|
||||
Text::new(format!("{background_color:?}"))
|
||||
.size(14)
|
||||
.style(Color::WHITE),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ pub fn main() {
|
|||
}
|
||||
Err(error) => match error {
|
||||
wgpu::SurfaceError::OutOfMemory => {
|
||||
panic!("Swapchain error: {}. Rendering cannot continue.", error)
|
||||
panic!("Swapchain error: {error}. Rendering cannot continue.")
|
||||
}
|
||||
_ => {
|
||||
// Try rendering again next frame.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ impl Application for Pokedex {
|
|||
Pokedex::Errored { .. } => "Whoops!",
|
||||
};
|
||||
|
||||
format!("{} - Pokédex", subtitle)
|
||||
format!("{subtitle} - Pokédex")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
|
|
@ -157,8 +157,7 @@ impl Pokemon {
|
|||
};
|
||||
|
||||
let fetch_entry = async {
|
||||
let url =
|
||||
format!("https://pokeapi.co/api/v2/pokemon-species/{}", id);
|
||||
let url = format!("https://pokeapi.co/api/v2/pokemon-species/{id}");
|
||||
|
||||
reqwest::get(&url).await?.json().await
|
||||
};
|
||||
|
|
@ -187,8 +186,7 @@ impl Pokemon {
|
|||
|
||||
async fn fetch_image(id: u16) -> Result<image::Handle, reqwest::Error> {
|
||||
let url = format!(
|
||||
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png",
|
||||
id
|
||||
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{id}.png"
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ impl Sandbox for Styling {
|
|||
column![text("Choose a theme:")].spacing(10),
|
||||
|column, theme| {
|
||||
column.push(radio(
|
||||
format!("{:?}", theme),
|
||||
format!("{theme:?}"),
|
||||
*theme,
|
||||
Some(match self.theme {
|
||||
Theme::Light => ThemeType::Light,
|
||||
|
|
|
|||
|
|
@ -114,13 +114,12 @@ impl Application for Example {
|
|||
{
|
||||
let memory_readable = ByteSize::kb(memory_used).to_string();
|
||||
|
||||
format!("{} kb ({})", memory_used, memory_readable)
|
||||
format!("{memory_used} kb ({memory_readable})")
|
||||
} else {
|
||||
String::from("None")
|
||||
};
|
||||
|
||||
let memory_used =
|
||||
text(format!("Memory (used): {}", memory_text));
|
||||
let memory_used = text(format!("Memory (used): {memory_text}"));
|
||||
|
||||
let graphics_adapter = text(format!(
|
||||
"Graphics adapter: {}",
|
||||
|
|
|
|||
10
examples/toast/Cargo.toml
Normal file
10
examples/toast/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "toast"
|
||||
version = "0.1.0"
|
||||
authors = ["tarkah <admin@tarkah.dev>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = [] }
|
||||
iced_native = { path = "../../native" }
|
||||
670
examples/toast/src/main.rs
Normal file
670
examples/toast/src/main.rs
Normal file
|
|
@ -0,0 +1,670 @@
|
|||
use iced::widget::{
|
||||
self, button, column, container, pick_list, row, slider, text, text_input,
|
||||
};
|
||||
use iced::{
|
||||
executor, keyboard, subscription, Alignment, Application, Command, Element,
|
||||
Event, Length, Settings, Subscription,
|
||||
};
|
||||
|
||||
use toast::{Status, Toast};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
App::run(Settings::default())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct App {
|
||||
toasts: Vec<Toast>,
|
||||
editing: Toast,
|
||||
timeout_secs: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
enum Message {
|
||||
Add,
|
||||
Close(usize),
|
||||
Title(String),
|
||||
Body(String),
|
||||
Status(Status),
|
||||
Timeout(f64),
|
||||
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 {
|
||||
toasts: vec![Toast {
|
||||
title: "Example Toast".into(),
|
||||
body: "Add more toasts in the form below!".into(),
|
||||
status: Status::Primary,
|
||||
}],
|
||||
timeout_secs: toast::DEFAULT_TIMEOUT,
|
||||
..Default::default()
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Toast - Iced")
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Self::Message> {
|
||||
subscription::events().map(Message::Event)
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Add => {
|
||||
if !self.editing.title.is_empty()
|
||||
&& !self.editing.body.is_empty()
|
||||
{
|
||||
self.toasts.push(std::mem::take(&mut self.editing));
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
Message::Close(index) => {
|
||||
self.toasts.remove(index);
|
||||
Command::none()
|
||||
}
|
||||
Message::Title(title) => {
|
||||
self.editing.title = title;
|
||||
Command::none()
|
||||
}
|
||||
Message::Body(body) => {
|
||||
self.editing.body = body;
|
||||
Command::none()
|
||||
}
|
||||
Message::Status(status) => {
|
||||
self.editing.status = status;
|
||||
Command::none()
|
||||
}
|
||||
Message::Timeout(timeout) => {
|
||||
self.timeout_secs = timeout as u64;
|
||||
Command::none()
|
||||
}
|
||||
Message::Event(Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key_code: keyboard::KeyCode::Tab,
|
||||
modifiers,
|
||||
})) if modifiers.shift() => widget::focus_previous(),
|
||||
Message::Event(Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key_code: keyboard::KeyCode::Tab,
|
||||
..
|
||||
})) => widget::focus_next(),
|
||||
Message::Event(_) => Command::none(),
|
||||
}
|
||||
}
|
||||
|
||||
fn view<'a>(&'a self) -> Element<'a, Message> {
|
||||
let subtitle = |title, content: Element<'a, Message>| {
|
||||
column![text(title).size(14), content]
|
||||
.width(Length::Fill)
|
||||
.spacing(5)
|
||||
};
|
||||
|
||||
let mut add_toast = button("Add Toast");
|
||||
|
||||
if !self.editing.body.is_empty() && !self.editing.title.is_empty() {
|
||||
add_toast = add_toast.on_press(Message::Add);
|
||||
}
|
||||
|
||||
let content = container(
|
||||
column![
|
||||
subtitle(
|
||||
"Title",
|
||||
text_input("", &self.editing.title, Message::Title)
|
||||
.on_submit(Message::Add)
|
||||
.into()
|
||||
),
|
||||
subtitle(
|
||||
"Message",
|
||||
text_input("", &self.editing.body, Message::Body)
|
||||
.on_submit(Message::Add)
|
||||
.into()
|
||||
),
|
||||
subtitle(
|
||||
"Status",
|
||||
pick_list(
|
||||
toast::Status::ALL,
|
||||
Some(self.editing.status),
|
||||
Message::Status
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
),
|
||||
subtitle(
|
||||
"Timeout",
|
||||
row![
|
||||
text(format!("{:0>2} sec", self.timeout_secs)),
|
||||
slider(
|
||||
1.0..=30.0,
|
||||
self.timeout_secs as f64,
|
||||
Message::Timeout
|
||||
)
|
||||
.step(1.0)
|
||||
.width(Length::Fill)
|
||||
]
|
||||
.spacing(5)
|
||||
.into()
|
||||
),
|
||||
column![add_toast]
|
||||
.width(Length::Fill)
|
||||
.align_items(Alignment::End)
|
||||
]
|
||||
.spacing(10)
|
||||
.max_width(200),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y();
|
||||
|
||||
toast::Manager::new(content, &self.toasts, Message::Close)
|
||||
.timeout(self.timeout_secs)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
mod toast {
|
||||
use std::fmt;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use iced::theme;
|
||||
use iced::widget::{
|
||||
button, column, container, horizontal_rule, horizontal_space, row, text,
|
||||
};
|
||||
use iced::{
|
||||
Alignment, Element, Length, Point, Rectangle, Renderer, Size, Theme,
|
||||
Vector,
|
||||
};
|
||||
use iced_native::widget::{tree, Operation, Tree};
|
||||
use iced_native::{event, layout, mouse, overlay, renderer, window};
|
||||
use iced_native::{Clipboard, Event, Layout, Shell, Widget};
|
||||
|
||||
pub const DEFAULT_TIMEOUT: u64 = 5;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum Status {
|
||||
#[default]
|
||||
Primary,
|
||||
Secondary,
|
||||
Success,
|
||||
Danger,
|
||||
}
|
||||
|
||||
impl Status {
|
||||
pub const ALL: &[Self] =
|
||||
&[Self::Primary, Self::Secondary, Self::Success, Self::Danger];
|
||||
}
|
||||
|
||||
impl container::StyleSheet for Status {
|
||||
type Style = Theme;
|
||||
|
||||
fn appearance(&self, theme: &Theme) -> container::Appearance {
|
||||
let palette = theme.extended_palette();
|
||||
|
||||
let pair = match self {
|
||||
Status::Primary => palette.primary.weak,
|
||||
Status::Secondary => palette.secondary.weak,
|
||||
Status::Success => palette.success.weak,
|
||||
Status::Danger => palette.danger.weak,
|
||||
};
|
||||
|
||||
container::Appearance {
|
||||
background: pair.color.into(),
|
||||
text_color: pair.text.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Status {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Status::Primary => "Primary",
|
||||
Status::Secondary => "Secondary",
|
||||
Status::Success => "Success",
|
||||
Status::Danger => "Danger",
|
||||
}
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Toast {
|
||||
pub title: String,
|
||||
pub body: String,
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
pub struct Manager<'a, Message> {
|
||||
content: Element<'a, Message>,
|
||||
toasts: Vec<Element<'a, Message>>,
|
||||
timeout_secs: u64,
|
||||
on_close: Box<dyn Fn(usize) -> Message + 'a>,
|
||||
}
|
||||
|
||||
impl<'a, Message> Manager<'a, Message>
|
||||
where
|
||||
Message: 'a + Clone,
|
||||
{
|
||||
pub fn new(
|
||||
content: impl Into<Element<'a, Message>>,
|
||||
toasts: &'a [Toast],
|
||||
on_close: impl Fn(usize) -> Message + 'a,
|
||||
) -> Self {
|
||||
let toasts = toasts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, toast)| {
|
||||
container(column![
|
||||
container(
|
||||
row![
|
||||
text(toast.title.as_str()),
|
||||
horizontal_space(Length::Fill),
|
||||
button("X")
|
||||
.on_press((on_close)(index))
|
||||
.padding(3),
|
||||
]
|
||||
.align_items(Alignment::Center)
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.padding(5)
|
||||
.style(
|
||||
theme::Container::Custom(Box::new(toast.status))
|
||||
),
|
||||
horizontal_rule(1),
|
||||
container(text(toast.body.as_str()))
|
||||
.width(Length::Fill)
|
||||
.padding(5)
|
||||
.style(theme::Container::Box),
|
||||
])
|
||||
.max_width(200)
|
||||
.into()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
content: content.into(),
|
||||
toasts,
|
||||
timeout_secs: DEFAULT_TIMEOUT,
|
||||
on_close: Box::new(on_close),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn timeout(self, seconds: u64) -> Self {
|
||||
Self {
|
||||
timeout_secs: seconds,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message> Widget<Message, Renderer> for Manager<'a, Message> {
|
||||
fn width(&self) -> Length {
|
||||
self.content.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.content.as_widget().height()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
self.content.as_widget().layout(renderer, limits)
|
||||
}
|
||||
|
||||
fn tag(&self) -> tree::Tag {
|
||||
struct Marker(Vec<Instant>);
|
||||
iced_native::widget::tree::Tag::of::<Marker>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
iced_native::widget::tree::State::new(Vec::<Option<Instant>>::new())
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
std::iter::once(Tree::new(&self.content))
|
||||
.chain(self.toasts.iter().map(Tree::new))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
let instants = tree.state.downcast_mut::<Vec<Option<Instant>>>();
|
||||
|
||||
// Invalidating removed instants to None allows us to remove
|
||||
// them here so that diffing for removed / new toast instants
|
||||
// is accurate
|
||||
instants.retain(Option::is_some);
|
||||
|
||||
match (instants.len(), self.toasts.len()) {
|
||||
(old, new) if old > new => {
|
||||
instants.truncate(new);
|
||||
}
|
||||
(old, new) if old < new => {
|
||||
instants.extend(
|
||||
std::iter::repeat(Some(Instant::now())).take(new - old),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
tree.diff_children(
|
||||
&std::iter::once(&self.content)
|
||||
.chain(self.toasts.iter())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
state: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn Operation<Message>,
|
||||
) {
|
||||
operation.container(None, &mut |operation| {
|
||||
self.content.as_widget().operate(
|
||||
&mut state.children[0],
|
||||
layout,
|
||||
renderer,
|
||||
operation,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
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.content.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: &Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
self.content.as_widget().draw(
|
||||
&state.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
state: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.content.as_widget().mouse_interaction(
|
||||
&state.children[0],
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
state: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
let instants = state.state.downcast_mut::<Vec<Option<Instant>>>();
|
||||
|
||||
let (content_state, toasts_state) = state.children.split_at_mut(1);
|
||||
|
||||
let content = self.content.as_widget_mut().overlay(
|
||||
&mut content_state[0],
|
||||
layout,
|
||||
renderer,
|
||||
);
|
||||
|
||||
let toasts = (!self.toasts.is_empty()).then(|| {
|
||||
overlay::Element::new(
|
||||
layout.bounds().position(),
|
||||
Box::new(Overlay {
|
||||
toasts: &mut self.toasts,
|
||||
state: toasts_state,
|
||||
instants,
|
||||
on_close: &self.on_close,
|
||||
timeout_secs: self.timeout_secs,
|
||||
}),
|
||||
)
|
||||
});
|
||||
let overlays =
|
||||
content.into_iter().chain(toasts).collect::<Vec<_>>();
|
||||
|
||||
(!overlays.is_empty())
|
||||
.then(|| overlay::Group::with_children(overlays).overlay())
|
||||
}
|
||||
}
|
||||
|
||||
struct Overlay<'a, 'b, Message> {
|
||||
toasts: &'b mut [Element<'a, Message>],
|
||||
state: &'b mut [Tree],
|
||||
instants: &'b mut [Option<Instant>],
|
||||
on_close: &'b dyn Fn(usize) -> Message,
|
||||
timeout_secs: u64,
|
||||
}
|
||||
|
||||
impl<'a, 'b, Message> overlay::Overlay<Message, Renderer>
|
||||
for Overlay<'a, 'b, Message>
|
||||
{
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
bounds: Size,
|
||||
position: Point,
|
||||
) -> layout::Node {
|
||||
let limits = layout::Limits::new(Size::ZERO, bounds)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
layout::flex::resolve(
|
||||
layout::flex::Axis::Vertical,
|
||||
renderer,
|
||||
&limits,
|
||||
10.into(),
|
||||
10.0,
|
||||
Alignment::End,
|
||||
self.toasts,
|
||||
)
|
||||
.translate(Vector::new(position.x, position.y))
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
if let Event::Window(window::Event::RedrawRequested(now)) = &event {
|
||||
let mut next_redraw: Option<window::RedrawRequest> = None;
|
||||
|
||||
self.instants.iter_mut().enumerate().for_each(
|
||||
|(index, maybe_instant)| {
|
||||
if let Some(instant) = maybe_instant.as_mut() {
|
||||
let remaining =
|
||||
Duration::from_secs(self.timeout_secs)
|
||||
.saturating_sub(instant.elapsed());
|
||||
|
||||
if remaining == Duration::ZERO {
|
||||
maybe_instant.take();
|
||||
shell.publish((self.on_close)(index));
|
||||
next_redraw =
|
||||
Some(window::RedrawRequest::NextFrame);
|
||||
} else {
|
||||
let redraw_at =
|
||||
window::RedrawRequest::At(*now + remaining);
|
||||
next_redraw = next_redraw
|
||||
.map(|redraw| redraw.min(redraw_at))
|
||||
.or(Some(redraw_at));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(redraw) = next_redraw {
|
||||
shell.request_redraw(redraw);
|
||||
}
|
||||
}
|
||||
|
||||
self.toasts
|
||||
.iter_mut()
|
||||
.zip(self.state.iter_mut())
|
||||
.zip(layout.children())
|
||||
.zip(self.instants.iter_mut())
|
||||
.map(|(((child, state), layout), instant)| {
|
||||
let mut local_messages = vec![];
|
||||
let mut local_shell = Shell::new(&mut local_messages);
|
||||
|
||||
let status = child.as_widget_mut().on_event(
|
||||
state,
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
&mut local_shell,
|
||||
);
|
||||
|
||||
if !local_shell.is_empty() {
|
||||
instant.take();
|
||||
}
|
||||
|
||||
shell.merge(local_shell, std::convert::identity);
|
||||
|
||||
status
|
||||
})
|
||||
.fold(event::Status::Ignored, event::Status::merge)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &<Renderer as iced_native::Renderer>::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) {
|
||||
let viewport = layout.bounds();
|
||||
|
||||
for ((child, state), layout) in self
|
||||
.toasts
|
||||
.iter()
|
||||
.zip(self.state.iter())
|
||||
.zip(layout.children())
|
||||
{
|
||||
child.as_widget().draw(
|
||||
state,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
&viewport,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn iced_native::widget::Operation<Message>,
|
||||
) {
|
||||
operation.container(None, &mut |operation| {
|
||||
self.toasts
|
||||
.iter()
|
||||
.zip(self.state.iter_mut())
|
||||
.zip(layout.children())
|
||||
.for_each(|((child, state), layout)| {
|
||||
child
|
||||
.as_widget()
|
||||
.operate(state, layout, renderer, operation);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.toasts
|
||||
.iter()
|
||||
.zip(self.state.iter())
|
||||
.zip(layout.children())
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget().mouse_interaction(
|
||||
state,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
})
|
||||
.max()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
|
||||
layout
|
||||
.children()
|
||||
.any(|layout| layout.bounds().contains(cursor_position))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message> From<Manager<'a, Message>> for Element<'a, Message>
|
||||
where
|
||||
Message: 'a,
|
||||
{
|
||||
fn from(manager: Manager<'a, Message>) -> Self {
|
||||
Element::new(manager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -303,7 +303,7 @@ pub enum TaskMessage {
|
|||
|
||||
impl Task {
|
||||
fn text_input_id(i: usize) -> text_input::Id {
|
||||
text_input::Id::new(format!("task-{}", i))
|
||||
text_input::Id::new(format!("task-{i}"))
|
||||
}
|
||||
|
||||
fn new(description: String) -> Self {
|
||||
|
|
|
|||
|
|
@ -388,7 +388,7 @@ impl<'a> Step {
|
|||
|
||||
let spacing_section = column![
|
||||
slider(0..=80, spacing, StepMessage::SpacingChanged),
|
||||
text(format!("{} px", spacing))
|
||||
text(format!("{spacing} px"))
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(alignment::Horizontal::Center),
|
||||
]
|
||||
|
|
@ -412,7 +412,7 @@ impl<'a> Step {
|
|||
fn text(size: u16, color: Color) -> Column<'a, StepMessage> {
|
||||
let size_section = column![
|
||||
"You can change its size:",
|
||||
text(format!("This text is {} pixels", size)).size(size),
|
||||
text(format!("This text is {size} pixels")).size(size),
|
||||
slider(10..=70, size, StepMessage::TextSizeChanged),
|
||||
]
|
||||
.padding(20)
|
||||
|
|
@ -427,7 +427,7 @@ impl<'a> Step {
|
|||
|
||||
let color_section = column![
|
||||
"And its color:",
|
||||
text(format!("{:?}", color)).style(color),
|
||||
text(format!("{color:?}")).style(color),
|
||||
color_sliders,
|
||||
]
|
||||
.padding(20)
|
||||
|
|
@ -497,7 +497,7 @@ impl<'a> Step {
|
|||
.push(ferris(width))
|
||||
.push(slider(100..=500, width, StepMessage::ImageWidthChanged))
|
||||
.push(
|
||||
text(format!("Width: {} px", width))
|
||||
text(format!("Width: {width} px"))
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(alignment::Horizontal::Center),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ impl fmt::Display for Message {
|
|||
Message::Disconnected => {
|
||||
write!(f, "Connection lost... Retrying...")
|
||||
}
|
||||
Message::User(message) => write!(f, "{}", message),
|
||||
Message::User(message) => write!(f, "{message}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ async fn user_connected(ws: WebSocket) {
|
|||
tokio::task::spawn(async move {
|
||||
while let Some(message) = rx.next().await {
|
||||
user_ws_tx.send(message).await.unwrap_or_else(|e| {
|
||||
eprintln!("websocket send error: {}", e);
|
||||
eprintln!("websocket send error: {e}");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue