From e2ddef74387bcd81859b56e47316c47d7b739a01 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Thu, 12 Jan 2023 05:18:25 +0100
Subject: Replace `Option<Instant>` with `RedrawRequest` enum

---
 glutin/src/application.rs           | 12 ++++++++++--
 native/src/shell.rs                 | 22 ++++++++++-----------
 native/src/user_interface.rs        | 23 ++++++++++------------
 native/src/widget/text_input.rs     |  4 ++--
 native/src/window.rs                |  2 ++
 native/src/window/redraw_request.rs | 38 +++++++++++++++++++++++++++++++++++++
 winit/src/application.rs            | 12 ++++++++++--
 winit/src/window.rs                 |  2 +-
 8 files changed, 84 insertions(+), 31 deletions(-)
 create mode 100644 native/src/window/redraw_request.rs

diff --git a/glutin/src/application.rs b/glutin/src/application.rs
index a3cef829..3bb9e61a 100644
--- a/glutin/src/application.rs
+++ b/glutin/src/application.rs
@@ -270,6 +270,7 @@ async fn run_instance<A, E, C>(
                 redraw_pending = matches!(
                     start_cause,
                     event::StartCause::Init
+                        | event::StartCause::Poll
                         | event::StartCause::ResumeTimeReached { .. }
                 );
             }
@@ -379,8 +380,15 @@ async fn run_instance<A, E, C>(
 
                 let _ = control_sender.start_send(match interface_state {
                     user_interface::State::Updated {
-                        redraw_requested_at: Some(at),
-                    } => ControlFlow::WaitUntil(at),
+                        redraw_request: Some(redraw_request),
+                    } => match redraw_request {
+                        crate::window::RedrawRequest::NextFrame => {
+                            ControlFlow::Poll
+                        }
+                        crate::window::RedrawRequest::At(at) => {
+                            ControlFlow::WaitUntil(at)
+                        }
+                    },
                     _ => ControlFlow::Wait,
                 });
 
diff --git a/native/src/shell.rs b/native/src/shell.rs
index 81d2a0e6..f1ddb48e 100644
--- a/native/src/shell.rs
+++ b/native/src/shell.rs
@@ -1,4 +1,4 @@
-use std::time::Instant;
+use crate::window;
 
 /// A connection to the state of a shell.
 ///
@@ -9,7 +9,7 @@ use std::time::Instant;
 #[derive(Debug)]
 pub struct Shell<'a, Message> {
     messages: &'a mut Vec<Message>,
-    redraw_requested_at: Option<Instant>,
+    redraw_request: Option<window::RedrawRequest>,
     is_layout_invalid: bool,
     are_widgets_invalid: bool,
 }
@@ -19,7 +19,7 @@ impl<'a, Message> Shell<'a, Message> {
     pub fn new(messages: &'a mut Vec<Message>) -> Self {
         Self {
             messages,
-            redraw_requested_at: None,
+            redraw_request: None,
             is_layout_invalid: false,
             are_widgets_invalid: false,
         }
@@ -31,21 +31,21 @@ impl<'a, Message> Shell<'a, Message> {
     }
 
     /// Requests a new frame to be drawn at the given [`Instant`].
-    pub fn request_redraw(&mut self, at: Instant) {
-        match self.redraw_requested_at {
+    pub fn request_redraw(&mut self, request: window::RedrawRequest) {
+        match self.redraw_request {
             None => {
-                self.redraw_requested_at = Some(at);
+                self.redraw_request = Some(request);
             }
-            Some(current) if at < current => {
-                self.redraw_requested_at = Some(at);
+            Some(current) if request < current => {
+                self.redraw_request = Some(request);
             }
             _ => {}
         }
     }
 
     /// Returns the requested [`Instant`] a redraw should happen, if any.
-    pub fn redraw_requested_at(&self) -> Option<Instant> {
-        self.redraw_requested_at
+    pub fn redraw_request(&self) -> Option<window::RedrawRequest> {
+        self.redraw_request
     }
 
     /// Returns whether the current layout is invalid or not.
@@ -90,7 +90,7 @@ impl<'a, Message> Shell<'a, Message> {
     pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) {
         self.messages.extend(other.messages.drain(..).map(f));
 
-        if let Some(at) = other.redraw_requested_at {
+        if let Some(at) = other.redraw_request {
             self.request_redraw(at);
         }
 
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 49a6b00e..025f28a1 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -5,10 +5,9 @@ use crate::layout;
 use crate::mouse;
 use crate::renderer;
 use crate::widget;
+use crate::window;
 use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
 
-use std::time::Instant;
-
 /// A set of interactive graphical elements with a specific [`Layout`].
 ///
 /// It can be updated and drawn.
@@ -191,7 +190,7 @@ where
         use std::mem::ManuallyDrop;
 
         let mut outdated = false;
-        let mut redraw_requested_at = None;
+        let mut redraw_request = None;
 
         let mut manual_overlay =
             ManuallyDrop::new(self.root.as_widget_mut().overlay(
@@ -221,12 +220,12 @@ where
 
                 event_statuses.push(event_status);
 
-                match (redraw_requested_at, shell.redraw_requested_at()) {
+                match (redraw_request, shell.redraw_request()) {
                     (None, Some(at)) => {
-                        redraw_requested_at = Some(at);
+                        redraw_request = Some(at);
                     }
                     (Some(current), Some(new)) if new < current => {
-                        redraw_requested_at = Some(new);
+                        redraw_request = Some(new);
                     }
                     _ => {}
                 }
@@ -303,12 +302,12 @@ where
                     self.overlay = None;
                 }
 
-                match (redraw_requested_at, shell.redraw_requested_at()) {
+                match (redraw_request, shell.redraw_request()) {
                     (None, Some(at)) => {
-                        redraw_requested_at = Some(at);
+                        redraw_request = Some(at);
                     }
                     (Some(current), Some(new)) if new < current => {
-                        redraw_requested_at = Some(new);
+                        redraw_request = Some(new);
                     }
                     _ => {}
                 }
@@ -334,9 +333,7 @@ where
             if outdated {
                 State::Outdated
             } else {
-                State::Updated {
-                    redraw_requested_at,
-                }
+                State::Updated { redraw_request }
             },
             event_statuses,
         )
@@ -594,6 +591,6 @@ pub enum State {
     /// rebuilding.
     Updated {
         /// The [`Instant`] when a redraw should be performed.
-        redraw_requested_at: Option<Instant>,
+        redraw_request: Option<window::RedrawRequest>,
     },
 }
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index f88022fa..ae289069 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -793,9 +793,9 @@ where
                     - (now - focus.updated_at).as_millis()
                         % CURSOR_BLINK_INTERVAL_MILLIS;
 
-                shell.request_redraw(
+                shell.request_redraw(window::RedrawRequest::At(
                     now + Duration::from_millis(millis_until_redraw as u64),
-                );
+                ));
             }
         }
         _ => {}
diff --git a/native/src/window.rs b/native/src/window.rs
index 4bccc471..6ebe15b1 100644
--- a/native/src/window.rs
+++ b/native/src/window.rs
@@ -2,11 +2,13 @@
 mod action;
 mod event;
 mod mode;
+mod redraw_request;
 mod user_attention;
 
 pub use action::Action;
 pub use event::Event;
 pub use mode::Mode;
+pub use redraw_request::RedrawRequest;
 pub use user_attention::UserAttention;
 
 use crate::subscription::{self, Subscription};
diff --git a/native/src/window/redraw_request.rs b/native/src/window/redraw_request.rs
new file mode 100644
index 00000000..1377823a
--- /dev/null
+++ b/native/src/window/redraw_request.rs
@@ -0,0 +1,38 @@
+use std::time::Instant;
+
+/// A request to redraw a window.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum RedrawRequest {
+    /// Redraw the next frame.
+    NextFrame,
+
+    /// Redraw at the given time.
+    At(Instant),
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::time::{Duration, Instant};
+
+    #[test]
+    fn ordering() {
+        let now = Instant::now();
+        let later = now + Duration::from_millis(10);
+
+        assert_eq!(RedrawRequest::NextFrame, RedrawRequest::NextFrame);
+        assert_eq!(RedrawRequest::At(now), RedrawRequest::At(now));
+
+        assert!(RedrawRequest::NextFrame < RedrawRequest::At(now));
+        assert!(RedrawRequest::At(now) > RedrawRequest::NextFrame);
+        assert!(RedrawRequest::At(now) < RedrawRequest::At(later));
+        assert!(RedrawRequest::At(later) > RedrawRequest::At(now));
+
+        assert!(RedrawRequest::NextFrame <= RedrawRequest::NextFrame);
+        assert!(RedrawRequest::NextFrame <= RedrawRequest::At(now));
+        assert!(RedrawRequest::At(now) >= RedrawRequest::NextFrame);
+        assert!(RedrawRequest::At(now) <= RedrawRequest::At(now));
+        assert!(RedrawRequest::At(now) <= RedrawRequest::At(later));
+        assert!(RedrawRequest::At(later) >= RedrawRequest::At(now));
+    }
+}
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 0f5309d2..b6485cb7 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -332,6 +332,7 @@ async fn run_instance<A, E, C>(
                 redraw_pending = matches!(
                     start_cause,
                     event::StartCause::Init
+                        | event::StartCause::Poll
                         | event::StartCause::ResumeTimeReached { .. }
                 );
             }
@@ -440,8 +441,15 @@ async fn run_instance<A, E, C>(
 
                 let _ = control_sender.start_send(match interface_state {
                     user_interface::State::Updated {
-                        redraw_requested_at: Some(at),
-                    } => ControlFlow::WaitUntil(at),
+                        redraw_request: Some(redraw_request),
+                    } => match redraw_request {
+                        crate::window::RedrawRequest::NextFrame => {
+                            ControlFlow::Poll
+                        }
+                        crate::window::RedrawRequest::At(at) => {
+                            ControlFlow::WaitUntil(at)
+                        }
+                    },
                     _ => ControlFlow::Wait,
                 });
 
diff --git a/winit/src/window.rs b/winit/src/window.rs
index 0b9e4c46..2306bdf1 100644
--- a/winit/src/window.rs
+++ b/winit/src/window.rs
@@ -2,7 +2,7 @@
 use crate::command::{self, Command};
 use iced_native::window;
 
-pub use window::{frames, Event, Mode, UserAttention};
+pub use window::{frames, Event, Mode, RedrawRequest, UserAttention};
 
 /// Closes the current window and exits the application.
 pub fn close<Message>() -> Command<Message> {
-- 
cgit