mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-27 20:06:53 -06:00
Merge branch 'pr-518' into batch/t5
# Conflicts: # contributors/people.toml
This commit is contained in:
@@ -41,3 +41,6 @@ real_name = "Justin John"
|
||||
|
||||
[people.Nael-Nathanael]
|
||||
sync_github_name = true
|
||||
|
||||
[people.volkovskey]
|
||||
real_name = "Andrii Volkov"
|
||||
|
||||
@@ -190,6 +190,25 @@ fn a_toast_wakes_the_ui_only_to_go_away() {
|
||||
assert!(h.app.ui.toast.is_none(), "gone after 3 s");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_toast_raised_by_a_slow_frame_still_shows_for_3_s() {
|
||||
let (mut h, _) = document();
|
||||
h.settle();
|
||||
// The Open dialog and a failed open block the frame that raises the error: the next frame
|
||||
// comes 5 s after it started.
|
||||
h.app.toast("can't open file.fig");
|
||||
h.frame(vec![]);
|
||||
h.time += 5.0;
|
||||
h.frame(vec![]);
|
||||
assert!(h.app.ui.toast.is_some(), "shown, not expired by the wait");
|
||||
h.time += 2.0;
|
||||
h.frame(vec![]);
|
||||
assert!(h.app.ui.toast.is_some());
|
||||
h.time += 1.5;
|
||||
h.frame(vec![]);
|
||||
assert!(h.app.ui.toast.is_none(), "gone after 3 s");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_finished_background_render_wakes_the_ui() {
|
||||
let ctx = egui::Context::default();
|
||||
|
||||
@@ -337,6 +337,17 @@ pub type AutoSaveEncoding = std::thread::JoinHandle<(figcraft_engine::cmd::file:
|
||||
/// How long a toast shows (seconds).
|
||||
const TOAST_SECS: f64 = 3.0;
|
||||
|
||||
/// How long a toast has been showing ([`UiState::toast`]).
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ToastClock {
|
||||
/// Raised, not drawn yet.
|
||||
Raised,
|
||||
/// Drawn once; the next frame starts the clock.
|
||||
Drawn,
|
||||
/// Showing since this time (UI clock, seconds).
|
||||
Since(f64),
|
||||
}
|
||||
|
||||
/// Draw another frame `secs` from now (nothing else needs drawing until then: an idle window draws
|
||||
/// no frames).
|
||||
#[track_caller]
|
||||
@@ -402,8 +413,8 @@ pub struct UiState {
|
||||
pub title_bar: TitleBar,
|
||||
/// Every icon on one screen, for reviewing the set (control channel `ui.set {iconGallery}`).
|
||||
pub icon_gallery: bool,
|
||||
/// A message at the bottom of the window and when it first showed (UI clock, seconds).
|
||||
pub toast: Option<(String, Option<f64>)>,
|
||||
/// A message at the bottom of the window and how long it has been showing.
|
||||
pub toast: Option<(String, ToastClock)>,
|
||||
pub present: Option<present::Presenter>,
|
||||
/// The full-window presenter's options (how it sizes the prototype), kept between
|
||||
/// presentations.
|
||||
@@ -740,7 +751,7 @@ impl FigApp {
|
||||
}
|
||||
|
||||
pub fn toast(&mut self, msg: impl Into<String>) {
|
||||
self.ui.toast = Some((msg.into(), None));
|
||||
self.ui.toast = Some((msg.into(), ToastClock::Raised));
|
||||
}
|
||||
|
||||
/// Say that `n` documents left unsaved by a crash were opened again from recovery copies.
|
||||
@@ -1379,16 +1390,32 @@ impl FigApp {
|
||||
}
|
||||
|
||||
fn toast_ui(&mut self, ctx: &egui::Context) {
|
||||
let Some((msg, shown)) = &mut self.ui.toast else { return };
|
||||
let Some((msg, clock)) = &mut self.ui.toast else { return };
|
||||
let now = ctx.input(|i| i.time);
|
||||
let left = TOAST_SECS - (now - *shown.get_or_insert(now));
|
||||
// The clock starts on the frame after the one that first draws the toast: that frame's
|
||||
// time was taken before whatever raised it ran (a file dialog, a long open), and would
|
||||
// count that wait against it, so a failed open's message expired before it was seen.
|
||||
let left = match *clock {
|
||||
ToastClock::Raised => {
|
||||
*clock = ToastClock::Drawn;
|
||||
ctx.request_repaint();
|
||||
TOAST_SECS
|
||||
}
|
||||
ToastClock::Drawn => {
|
||||
*clock = ToastClock::Since(now);
|
||||
TOAST_SECS
|
||||
}
|
||||
ToastClock::Since(t) => TOAST_SECS - (now - t),
|
||||
};
|
||||
if left <= 0.0 {
|
||||
self.ui.toast = None;
|
||||
return;
|
||||
}
|
||||
let msg = msg.clone();
|
||||
// Nothing moves while it shows: the next frame it needs is the one that hides it.
|
||||
repaint_in(ctx, left);
|
||||
if matches!(clock, ToastClock::Since(_)) {
|
||||
repaint_in(ctx, left);
|
||||
}
|
||||
let screen = ctx.content_rect();
|
||||
egui::Area::new(egui::Id::new("toast"))
|
||||
.fixed_pos(egui::pos2(screen.center().x, screen.max.y - 96.0))
|
||||
|
||||
Reference in New Issue
Block a user