Implement delay for pop widget 🎉
This commit is contained in:
parent
42f6018487
commit
ffc412d6b7
3 changed files with 50 additions and 39 deletions
|
|
@ -144,9 +144,9 @@ impl<F, A, B, O> Function<A, B, O> for F
|
||||||
where
|
where
|
||||||
F: Fn(A, B) -> O,
|
F: Fn(A, B) -> O,
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
A: Copy,
|
A: Clone,
|
||||||
{
|
{
|
||||||
fn with(self, prefix: A) -> impl Fn(B) -> O {
|
fn with(self, prefix: A) -> impl Fn(B) -> O {
|
||||||
move |result| self(prefix, result)
|
move |result| self(prefix.clone(), result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ mod icon;
|
||||||
use iced::animation;
|
use iced::animation;
|
||||||
use iced::clipboard;
|
use iced::clipboard;
|
||||||
use iced::highlighter;
|
use iced::highlighter;
|
||||||
use iced::task;
|
|
||||||
use iced::time::{self, milliseconds, Instant};
|
use iced::time::{self, milliseconds, Instant};
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
self, button, center_x, container, horizontal_space, hover, image,
|
self, button, center_x, container, horizontal_space, hover, image,
|
||||||
markdown, pop, right, row, scrollable, text_editor, toggler,
|
markdown, pop, right, row, scrollable, text_editor, toggler,
|
||||||
};
|
};
|
||||||
use iced::window;
|
use iced::window;
|
||||||
use iced::{Animation, Element, Fill, Font, Subscription, Task, Theme};
|
use iced::{
|
||||||
|
Animation, Element, Fill, Font, Function, Subscription, Task, Theme,
|
||||||
|
};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
@ -39,9 +40,7 @@ enum Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Image {
|
enum Image {
|
||||||
Loading {
|
Loading,
|
||||||
_download: task::Handle,
|
|
||||||
},
|
|
||||||
Ready {
|
Ready {
|
||||||
handle: image::Handle,
|
handle: image::Handle,
|
||||||
fade_in: Animation<bool>,
|
fade_in: Animation<bool>,
|
||||||
|
|
@ -89,9 +88,6 @@ impl Markdown {
|
||||||
if is_edit {
|
if is_edit {
|
||||||
self.content = markdown::Content::parse(&self.raw.text());
|
self.content = markdown::Content::parse(&self.raw.text());
|
||||||
self.mode = Mode::Preview;
|
self.mode = Mode::Preview;
|
||||||
|
|
||||||
let images = self.content.images();
|
|
||||||
self.images.retain(|url, _image| images.contains(url));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
|
|
@ -107,27 +103,12 @@ impl Markdown {
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (download_image, handle) = Task::future({
|
let _ = self.images.insert(url.clone(), Image::Loading);
|
||||||
let url = url.clone();
|
|
||||||
|
|
||||||
async move {
|
Task::perform(
|
||||||
// Wait half a second for further editions before attempting download
|
download_image(url.clone()),
|
||||||
tokio::time::sleep(milliseconds(500)).await;
|
Message::ImageDownloaded.with(url),
|
||||||
download_image(url).await
|
)
|
||||||
}
|
|
||||||
})
|
|
||||||
.abortable();
|
|
||||||
|
|
||||||
let _ = self.images.insert(
|
|
||||||
url.clone(),
|
|
||||||
Image::Loading {
|
|
||||||
_download: handle.abort_on_drop(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
download_image.map(move |result| {
|
|
||||||
Message::ImageDownloaded(url.clone(), result)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Message::ImageDownloaded(url, result) => {
|
Message::ImageDownloaded(url, result) => {
|
||||||
let _ = self.images.insert(
|
let _ = self.images.insert(
|
||||||
|
|
@ -286,6 +267,7 @@ impl<'a> markdown::Viewer<'a, Message> for CustomViewer<'a> {
|
||||||
} else {
|
} else {
|
||||||
pop(horizontal_space())
|
pop(horizontal_space())
|
||||||
.key(url.as_str())
|
.key(url.as_str())
|
||||||
|
.delay(milliseconds(500))
|
||||||
.on_show(|_size| Message::ImageShown(url.clone()))
|
.on_show(|_size| Message::ImageShown(url.clone()))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::core::mouse;
|
||||||
use crate::core::overlay;
|
use crate::core::overlay;
|
||||||
use crate::core::renderer;
|
use crate::core::renderer;
|
||||||
use crate::core::text;
|
use crate::core::text;
|
||||||
|
use crate::core::time::{Duration, Instant};
|
||||||
use crate::core::widget;
|
use crate::core::widget;
|
||||||
use crate::core::widget::tree::{self, Tree};
|
use crate::core::widget::tree::{self, Tree};
|
||||||
use crate::core::window;
|
use crate::core::window;
|
||||||
|
|
@ -23,6 +24,7 @@ pub struct Pop<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
|
||||||
on_resize: Option<Box<dyn Fn(Size) -> Message + 'a>>,
|
on_resize: Option<Box<dyn Fn(Size) -> Message + 'a>>,
|
||||||
on_hide: Option<Message>,
|
on_hide: Option<Message>,
|
||||||
anticipate: Pixels,
|
anticipate: Pixels,
|
||||||
|
delay: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> Pop<'a, Message, Theme, Renderer>
|
impl<'a, Message, Theme, Renderer> Pop<'a, Message, Theme, Renderer>
|
||||||
|
|
@ -41,6 +43,7 @@ where
|
||||||
on_resize: None,
|
on_resize: None,
|
||||||
on_hide: None,
|
on_hide: None,
|
||||||
anticipate: Pixels::ZERO,
|
anticipate: Pixels::ZERO,
|
||||||
|
delay: Duration::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,11 +89,21 @@ where
|
||||||
self.anticipate = distance.into();
|
self.anticipate = distance.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the amount of time to wait before firing an [`on_show`] or
|
||||||
|
/// [`on_hide`] event; after the content is shown or hidden.
|
||||||
|
///
|
||||||
|
/// When combined with [`key`], this can be useful to debounce key changes.
|
||||||
|
pub fn delay(mut self, delay: impl Into<Duration>) -> Self {
|
||||||
|
self.delay = delay.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct State {
|
struct State {
|
||||||
has_popped_in: bool,
|
has_popped_in: bool,
|
||||||
|
should_notify_at: Option<(bool, Instant)>,
|
||||||
last_size: Option<Size>,
|
last_size: Option<Size>,
|
||||||
last_key: Option<String>,
|
last_key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
@ -128,13 +141,14 @@ where
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
if let Event::Window(window::Event::RedrawRequested(_)) = &event {
|
if let Event::Window(window::Event::RedrawRequested(now)) = &event {
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
|
|
||||||
if state.has_popped_in
|
if state.has_popped_in
|
||||||
&& state.last_key.as_deref() != self.key.as_deref()
|
&& state.last_key.as_deref() != self.key.as_deref()
|
||||||
{
|
{
|
||||||
state.has_popped_in = false;
|
state.has_popped_in = false;
|
||||||
|
state.should_notify_at = None;
|
||||||
state.last_key =
|
state.last_key =
|
||||||
self.key.as_ref().cloned().map(text::Fragment::into_owned);
|
self.key.as_ref().cloned().map(text::Fragment::into_owned);
|
||||||
}
|
}
|
||||||
|
|
@ -157,19 +171,34 @@ where
|
||||||
shell.publish(on_resize(size));
|
shell.publish(on_resize(size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(on_hide) = &self.on_hide {
|
} else if self.on_hide.is_some() {
|
||||||
state.has_popped_in = false;
|
state.has_popped_in = false;
|
||||||
shell.publish(on_hide.clone());
|
state.should_notify_at = Some((false, *now + self.delay));
|
||||||
}
|
}
|
||||||
} else if let Some(on_show) = &self.on_show {
|
} else if self.on_show.is_some() && distance <= self.anticipate.0 {
|
||||||
if distance <= self.anticipate.0 {
|
|
||||||
let size = bounds.size();
|
let size = bounds.size();
|
||||||
|
|
||||||
state.has_popped_in = true;
|
state.has_popped_in = true;
|
||||||
|
state.should_notify_at = Some((true, *now + self.delay));
|
||||||
state.last_size = Some(size);
|
state.last_size = Some(size);
|
||||||
|
|
||||||
shell.publish(on_show(size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match &state.should_notify_at {
|
||||||
|
Some((has_popped_in, at)) if at <= now => {
|
||||||
|
if *has_popped_in {
|
||||||
|
if let Some(on_show) = &self.on_show {
|
||||||
|
shell.publish(on_show(layout.bounds().size()));
|
||||||
|
}
|
||||||
|
} else if let Some(on_hide) = &self.on_hide {
|
||||||
|
shell.publish(on_hide.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
state.should_notify_at = None;
|
||||||
|
}
|
||||||
|
Some((_, at)) => {
|
||||||
|
shell.request_redraw_at(*at);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue