summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-01-12 02:59:08 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-01-12 02:59:08 +0100
commit7354f68b3ca345767de3f09dccddf168493977bf (patch)
treea09626c11a25ab4260c576733f1700e9ad12894b
parent7ccd87c36b54e0d53f65f5774f140a0528ae4504 (diff)
downloadiced-7354f68b3ca345767de3f09dccddf168493977bf.tar.gz
iced-7354f68b3ca345767de3f09dccddf168493977bf.tar.bz2
iced-7354f68b3ca345767de3f09dccddf168493977bf.zip
Draft `Shell:request_redraw` API
... and implement `TextInput` cursor blink :tada:
Diffstat (limited to '')
-rw-r--r--examples/events/Cargo.toml2
-rw-r--r--examples/events/src/main.rs24
-rw-r--r--examples/exit/src/main.rs26
-rw-r--r--native/src/renderer.rs6
-rw-r--r--native/src/shell.rs60
-rw-r--r--native/src/subscription.rs8
-rw-r--r--native/src/user_interface.rs46
-rw-r--r--native/src/widget/text_input.rs102
-rw-r--r--native/src/window/event.rs6
-rw-r--r--src/application.rs7
-rw-r--r--src/sandbox.rs11
-rw-r--r--winit/src/application.rs72
12 files changed, 263 insertions, 107 deletions
diff --git a/examples/events/Cargo.toml b/examples/events/Cargo.toml
index 8ad04a36..8c56e471 100644
--- a/examples/events/Cargo.toml
+++ b/examples/events/Cargo.toml
@@ -6,5 +6,5 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../.." }
+iced = { path = "../..", features = ["debug"] }
iced_native = { path = "../../native" }
diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs
index 234e1423..4ae8d6fb 100644
--- a/examples/events/src/main.rs
+++ b/examples/events/src/main.rs
@@ -1,11 +1,12 @@
use iced::alignment;
use iced::executor;
use iced::widget::{button, checkbox, container, text, Column};
+use iced::window;
use iced::{
Alignment, Application, Command, Element, Length, Settings, Subscription,
Theme,
};
-use iced_native::{window, Event};
+use iced_native::Event;
pub fn main() -> iced::Result {
Events::run(Settings {
@@ -18,7 +19,6 @@ pub fn main() -> iced::Result {
struct Events {
last: Vec<iced_native::Event>,
enabled: bool,
- should_exit: bool,
}
#[derive(Debug, Clone)]
@@ -50,31 +50,29 @@ impl Application for Events {
if self.last.len() > 5 {
let _ = self.last.remove(0);
}
+
+ Command::none()
}
Message::EventOccurred(event) => {
if let Event::Window(window::Event::CloseRequested) = event {
- self.should_exit = true;
+ window::close()
+ } else {
+ Command::none()
}
}
Message::Toggled(enabled) => {
self.enabled = enabled;
- }
- Message::Exit => {
- self.should_exit = true;
- }
- };
- Command::none()
+ Command::none()
+ }
+ Message::Exit => window::close(),
+ }
}
fn subscription(&self) -> Subscription<Message> {
iced_native::subscription::events().map(Message::EventOccurred)
}
- fn should_exit(&self) -> bool {
- self.should_exit
- }
-
fn view(&self) -> Element<Message> {
let events = Column::with_children(
self.last
diff --git a/examples/exit/src/main.rs b/examples/exit/src/main.rs
index 5d518d2f..6152f627 100644
--- a/examples/exit/src/main.rs
+++ b/examples/exit/src/main.rs
@@ -1,5 +1,7 @@
+use iced::executor;
use iced::widget::{button, column, container};
-use iced::{Alignment, Element, Length, Sandbox, Settings};
+use iced::window;
+use iced::{Alignment, Application, Command, Element, Length, Settings, Theme};
pub fn main() -> iced::Result {
Exit::run(Settings::default())
@@ -8,7 +10,6 @@ pub fn main() -> iced::Result {
#[derive(Default)]
struct Exit {
show_confirm: bool,
- exit: bool,
}
#[derive(Debug, Clone, Copy)]
@@ -17,28 +18,27 @@ enum Message {
Exit,
}
-impl Sandbox for Exit {
+impl Application for Exit {
+ type Executor = executor::Default;
type Message = Message;
+ type Theme = Theme;
+ type Flags = ();
- fn new() -> Self {
- Self::default()
+ fn new(_flags: ()) -> (Self, Command<Message>) {
+ (Self::default(), Command::none())
}
fn title(&self) -> String {
String::from("Exit - Iced")
}
- fn should_exit(&self) -> bool {
- self.exit
- }
-
- fn update(&mut self, message: Message) {
+ fn update(&mut self, message: Message) -> Command<Message> {
match message {
- Message::Confirm => {
- self.exit = true;
- }
+ Message::Confirm => window::close(),
Message::Exit => {
self.show_confirm = true;
+
+ Command::none()
}
}
}
diff --git a/native/src/renderer.rs b/native/src/renderer.rs
index 5e776be6..d5329acd 100644
--- a/native/src/renderer.rs
+++ b/native/src/renderer.rs
@@ -36,11 +36,11 @@ pub trait Renderer: Sized {
f: impl FnOnce(&mut Self),
);
- /// Clears all of the recorded primitives in the [`Renderer`].
- fn clear(&mut self);
-
/// Fills a [`Quad`] with the provided [`Background`].
fn fill_quad(&mut self, quad: Quad, background: impl Into<Background>);
+
+ /// Clears all of the recorded primitives in the [`Renderer`].
+ fn clear(&mut self);
}
/// A polygon with four sides.
diff --git a/native/src/shell.rs b/native/src/shell.rs
index b96d23e5..81d2a0e6 100644
--- a/native/src/shell.rs
+++ b/native/src/shell.rs
@@ -1,3 +1,5 @@
+use std::time::Instant;
+
/// A connection to the state of a shell.
///
/// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application,
@@ -7,6 +9,7 @@
#[derive(Debug)]
pub struct Shell<'a, Message> {
messages: &'a mut Vec<Message>,
+ redraw_requested_at: Option<Instant>,
is_layout_invalid: bool,
are_widgets_invalid: bool,
}
@@ -16,31 +19,40 @@ impl<'a, Message> Shell<'a, Message> {
pub fn new(messages: &'a mut Vec<Message>) -> Self {
Self {
messages,
+ redraw_requested_at: None,
is_layout_invalid: false,
are_widgets_invalid: false,
}
}
- /// Triggers the given function if the layout is invalid, cleaning it in the
- /// process.
- pub fn revalidate_layout(&mut self, f: impl FnOnce()) {
- if self.is_layout_invalid {
- self.is_layout_invalid = false;
+ /// Publish the given `Message` for an application to process it.
+ pub fn publish(&mut self, message: Message) {
+ self.messages.push(message);
+ }
- f()
+ /// Requests a new frame to be drawn at the given [`Instant`].
+ pub fn request_redraw(&mut self, at: Instant) {
+ match self.redraw_requested_at {
+ None => {
+ self.redraw_requested_at = Some(at);
+ }
+ Some(current) if at < current => {
+ self.redraw_requested_at = Some(at);
+ }
+ _ => {}
}
}
+ /// Returns the requested [`Instant`] a redraw should happen, if any.
+ pub fn redraw_requested_at(&self) -> Option<Instant> {
+ self.redraw_requested_at
+ }
+
/// Returns whether the current layout is invalid or not.
pub fn is_layout_invalid(&self) -> bool {
self.is_layout_invalid
}
- /// Publish the given `Message` for an application to process it.
- pub fn publish(&mut self, message: Message) {
- self.messages.push(message);
- }
-
/// Invalidates the current application layout.
///
/// The shell will relayout the application widgets.
@@ -48,6 +60,22 @@ impl<'a, Message> Shell<'a, Message> {
self.is_layout_invalid = true;
}
+ /// Triggers the given function if the layout is invalid, cleaning it in the
+ /// process.
+ pub fn revalidate_layout(&mut self, f: impl FnOnce()) {
+ if self.is_layout_invalid {
+ self.is_layout_invalid = false;
+
+ f()
+ }
+ }
+
+ /// Returns whether the widgets of the current application have been
+ /// invalidated.
+ pub fn are_widgets_invalid(&self) -> bool {
+ self.are_widgets_invalid
+ }
+
/// Invalidates the current application widgets.
///
/// The shell will rebuild and relayout the widget tree.
@@ -62,16 +90,14 @@ 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 {
+ self.request_redraw(at);
+ }
+
self.is_layout_invalid =
self.is_layout_invalid || other.is_layout_invalid;
self.are_widgets_invalid =
self.are_widgets_invalid || other.are_widgets_invalid;
}
-
- /// Returns whether the widgets of the current application have been
- /// invalidated.
- pub fn are_widgets_invalid(&self) -> bool {
- self.are_widgets_invalid
- }
}
diff --git a/native/src/subscription.rs b/native/src/subscription.rs
index c60b1281..980a8116 100644
--- a/native/src/subscription.rs
+++ b/native/src/subscription.rs
@@ -1,5 +1,6 @@
//! Listen to external events in your application.
use crate::event::{self, Event};
+use crate::window;
use crate::Hasher;
use iced_futures::futures::{self, Future, Stream};
@@ -33,7 +34,7 @@ pub type Tracker =
pub use iced_futures::subscription::Recipe;
-/// Returns a [`Subscription`] to all the runtime events.
+/// Returns a [`Subscription`] to all the ignored runtime events.
///
/// This subscription will notify your application of any [`Event`] that was
/// not captured by any widget.
@@ -65,7 +66,10 @@ where
use futures::stream::StreamExt;
events.filter_map(move |(event, status)| {
- future::ready(f(event, status))
+ future::ready(match event {
+ Event::Window(window::Event::RedrawRequested(_)) => None,
+ _ => f(event, status),
+ })
})
},
})
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 2b43829d..49a6b00e 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -7,6 +7,8 @@ use crate::renderer;
use crate::widget;
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.
@@ -188,7 +190,9 @@ where
) -> (State, Vec<event::Status>) {
use std::mem::ManuallyDrop;
- let mut state = State::Updated;
+ let mut outdated = false;
+ let mut redraw_requested_at = None;
+
let mut manual_overlay =
ManuallyDrop::new(self.root.as_widget_mut().overlay(
&mut self.state,
@@ -217,6 +221,16 @@ where
event_statuses.push(event_status);
+ match (redraw_requested_at, shell.redraw_requested_at()) {
+ (None, Some(at)) => {
+ redraw_requested_at = Some(at);
+ }
+ (Some(current), Some(new)) if new < current => {
+ redraw_requested_at = Some(new);
+ }
+ _ => {}
+ }
+
if shell.is_layout_invalid() {
let _ = ManuallyDrop::into_inner(manual_overlay);
@@ -244,7 +258,7 @@ where
}
if shell.are_widgets_invalid() {
- state = State::Outdated;
+ outdated = true;
}
}
@@ -289,6 +303,16 @@ where
self.overlay = None;
}
+ match (redraw_requested_at, shell.redraw_requested_at()) {
+ (None, Some(at)) => {
+ redraw_requested_at = Some(at);
+ }
+ (Some(current), Some(new)) if new < current => {
+ redraw_requested_at = Some(new);
+ }
+ _ => {}
+ }
+
shell.revalidate_layout(|| {
self.base = renderer.layout(
&self.root,
@@ -299,14 +323,23 @@ where
});
if shell.are_widgets_invalid() {
- state = State::Outdated;
+ outdated = true;
}
event_status.merge(overlay_status)
})
.collect();
- (state, event_statuses)
+ (
+ if outdated {
+ State::Outdated
+ } else {
+ State::Updated {
+ redraw_requested_at,
+ }
+ },
+ event_statuses,
+ )
}
/// Draws the [`UserInterface`] with the provided [`Renderer`].
@@ -559,5 +592,8 @@ pub enum State {
/// The [`UserInterface`] is up-to-date and can be reused without
/// rebuilding.
- Updated,
+ Updated {
+ /// The [`Instant`] when a redraw should be performed.
+ redraw_requested_at: Option<Instant>,
+ },
}
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 8b4514e3..9d5dd620 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -22,11 +22,14 @@ use crate::touch;
use crate::widget;
use crate::widget::operation::{self, Operation};
use crate::widget::tree::{self, Tree};
+use crate::window;
use crate::{
Clipboard, Color, Command, Element, Layout, Length, Padding, Point,
Rectangle, Shell, Size, Vector, Widget,
};
+use std::time::{Duration, Instant};
+
pub use iced_style::text_input::{Appearance, StyleSheet};
/// A field that can be filled with text.
@@ -425,7 +428,16 @@ where
let state = state();
let is_clicked = layout.bounds().contains(cursor_position);
- state.is_focused = is_clicked;
+ state.is_focused = if is_clicked {
+ let now = Instant::now();
+
+ Some(Focus {
+ at: now,
+ last_draw: now,
+ })
+ } else {
+ None
+ };
if is_clicked {
let text_layout = layout.children().next().unwrap();
@@ -541,26 +553,30 @@ where
Event::Keyboard(keyboard::Event::CharacterReceived(c)) => {
let state = state();
- if state.is_focused
- && state.is_pasting.is_none()
- && !state.keyboard_modifiers.command()
- && !c.is_control()
- {
- let mut editor = Editor::new(value, &mut state.cursor);
+ if let Some(focus) = &mut state.is_focused {
+ if state.is_pasting.is_none()
+ && !state.keyboard_modifiers.command()
+ && !c.is_control()
+ {
+ let mut editor = Editor::new(value, &mut state.cursor);
- editor.insert(c);
+ editor.insert(c);
- let message = (on_change)(editor.contents());
- shell.publish(message);
+ let message = (on_change)(editor.contents());
+ shell.publish(message);
- return event::Status::Captured;
+ focus.at = Instant::now();
+
+ return event::Status::Captured;
+ }
}
}
Event::Keyboard(keyboard::Event::KeyPressed { key_code, .. }) => {
let state = state();
- if state.is_focused {
+ if let Some(focus) = &mut state.is_focused {
let modifiers = state.keyboard_modifiers;
+ focus.at = Instant::now();
match key_code {
keyboard::KeyCode::Enter
@@ -721,7 +737,7 @@ where
state.cursor.select_all(value);
}
keyboard::KeyCode::Escape => {
- state.is_focused = false;
+ state.is_focused = None;
state.is_dragging = false;
state.is_pasting = None;
@@ -742,7 +758,7 @@ where
Event::Keyboard(keyboard::Event::KeyReleased { key_code, .. }) => {
let state = state();
- if state.is_focused {
+ if state.is_focused.is_some() {
match key_code {
keyboard::KeyCode::V => {
state.is_pasting = None;
@@ -765,6 +781,21 @@ where
state.keyboard_modifiers = modifiers;
}
+ Event::Window(window::Event::RedrawRequested(now)) => {
+ let state = state();
+
+ if let Some(focus) = &mut state.is_focused {
+ focus.last_draw = now;
+
+ let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS
+ - (now - focus.at).as_millis()
+ % CURSOR_BLINK_INTERVAL_MILLIS;
+
+ shell.request_redraw(
+ now + Duration::from_millis(millis_until_redraw as u64),
+ );
+ }
+ }
_ => {}
}
@@ -820,7 +851,7 @@ pub fn draw<Renderer>(
let text = value.to_string();
let size = size.unwrap_or_else(|| renderer.default_size());
- let (cursor, offset) = if state.is_focused() {
+ let (cursor, offset) = if let Some(focus) = &state.is_focused {
match state.cursor.state(value) {
cursor::State::Index(position) => {
let (text_value_width, offset) =
@@ -833,7 +864,13 @@ pub fn draw<Renderer>(
font.clone(),
);
- (
+ let is_cursor_visible = ((focus.last_draw - focus.at)
+ .as_millis()
+ / CURSOR_BLINK_INTERVAL_MILLIS)
+ % 2
+ == 0;
+
+ let cursor = if is_cursor_visible {
Some((
renderer::Quad {
bounds: Rectangle {
@@ -847,9 +884,12 @@ pub fn draw<Renderer>(
border_color: Color::TRANSPARENT,
},
theme.value_color(style),
- )),
- offset,
- )
+ ))
+ } else {
+ None
+ };
+
+ (cursor, offset)
}
cursor::State::Selection { start, end } => {
let left = start.min(end);
@@ -958,7 +998,7 @@ pub fn mouse_interaction(
/// The state of a [`TextInput`].
#[derive(Debug, Default, Clone)]
pub struct State {
- is_focused: bool,
+ is_focused: Option<Focus>,
is_dragging: bool,
is_pasting: Option<Value>,
last_click: Option<mouse::Click>,
@@ -967,6 +1007,12 @@ pub struct State {
// TODO: Add stateful horizontal scrolling offset
}
+#[derive(Debug, Clone, Copy)]
+struct Focus {
+ at: Instant,
+ last_draw: Instant,
+}
+
impl State {
/// Creates a new [`State`], representing an unfocused [`TextInput`].
pub fn new() -> Self {
@@ -976,7 +1022,7 @@ impl State {
/// Creates a new [`State`], representing a focused [`TextInput`].
pub fn focused() -> Self {
Self {
- is_focused: true,
+ is_focused: None,
is_dragging: false,
is_pasting: None,
last_click: None,
@@ -987,7 +1033,7 @@ impl State {
/// Returns whether the [`TextInput`] is currently focused or not.
pub fn is_focused(&self) -> bool {
- self.is_focused
+ self.is_focused.is_some()
}
/// Returns the [`Cursor`] of the [`TextInput`].
@@ -997,13 +1043,19 @@ impl State {
/// Focuses the [`TextInput`].
pub fn focus(&mut self) {
- self.is_focused = true;
+ let now = Instant::now();
+
+ self.is_focused = Some(Focus {
+ at: now,
+ last_draw: now,
+ });
+
self.move_cursor_to_end();
}
/// Unfocuses the [`TextInput`].
pub fn unfocus(&mut self) {
- self.is_focused = false;
+ self.is_focused = None;
}
/// Moves the [`Cursor`] of the [`TextInput`] to the front of the input text.
@@ -1156,3 +1208,5 @@ where
)
.map(text::Hit::cursor)
}
+
+const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500;
diff --git a/native/src/window/event.rs b/native/src/window/event.rs
index 86321ac0..16684222 100644
--- a/native/src/window/event.rs
+++ b/native/src/window/event.rs
@@ -1,4 +1,5 @@
use std::path::PathBuf;
+use std::time::Instant;
/// A window-related event.
#[derive(PartialEq, Eq, Clone, Debug)]
@@ -19,6 +20,11 @@ pub enum Event {
height: u32,
},
+ /// A window redraw was requested.
+ ///
+ /// The [`Instant`] contains the current time.
+ RedrawRequested(Instant),
+
/// The user has requested for the window to close.
///
/// Usually, you will want to terminate the execution whenever this event
diff --git a/src/application.rs b/src/application.rs
index f2b7c955..6d68779b 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -180,13 +180,6 @@ pub trait Application: Sized {
1.0
}
- /// Returns whether the [`Application`] should be terminated.
- ///
- /// By default, it returns `false`.
- fn should_exit(&self) -> bool {
- false
- }
-
/// Runs the [`Application`].
///
/// On native platforms, this method will take control of the current thread
diff --git a/src/sandbox.rs b/src/sandbox.rs
index 47bad831..40c699d9 100644
--- a/src/sandbox.rs
+++ b/src/sandbox.rs
@@ -140,13 +140,6 @@ pub trait Sandbox {
1.0
}
- /// Returns whether the [`Sandbox`] should be terminated.
- ///
- /// By default, it returns `false`.
- fn should_exit(&self) -> bool {
- false
- }
-
/// Runs the [`Sandbox`].
///
/// On native platforms, this method will take control of the current thread
@@ -203,8 +196,4 @@ where
fn scale_factor(&self) -> f64 {
T::scale_factor(self)
}
-
- fn should_exit(&self) -> bool {
- T::should_exit(self)
- }
}
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 74c73815..0f5309d2 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -11,7 +11,7 @@ use crate::mouse;
use crate::renderer;
use crate::widget::operation;
use crate::{
- Command, Debug, Error, Executor, Proxy, Runtime, Settings, Size,
+ Command, Debug, Error, Event, Executor, Proxy, Runtime, Settings, Size,
Subscription,
};
@@ -25,6 +25,7 @@ use iced_native::user_interface::{self, UserInterface};
pub use iced_native::application::{Appearance, StyleSheet};
use std::mem::ManuallyDrop;
+use std::time::Instant;
#[cfg(feature = "trace")]
pub use profiler::Profiler;
@@ -186,7 +187,8 @@ where
let (compositor, renderer) = C::new(compositor_settings, Some(&window))?;
- let (mut sender, receiver) = mpsc::unbounded();
+ let (mut event_sender, event_receiver) = mpsc::unbounded();
+ let (control_sender, mut control_receiver) = mpsc::unbounded();
let mut instance = Box::pin({
let run_instance = run_instance::<A, E, C>(
@@ -196,7 +198,8 @@ where
runtime,
proxy,
debug,
- receiver,
+ event_receiver,
+ control_sender,
init_command,
window,
settings.exit_on_close_request,
@@ -234,13 +237,19 @@ where
};
if let Some(event) = event {
- sender.start_send(event).expect("Send event");
+ event_sender.start_send(event).expect("Send event");
let poll = instance.as_mut().poll(&mut context);
- *control_flow = match poll {
- task::Poll::Pending => ControlFlow::Wait,
- task::Poll::Ready(_) => ControlFlow::Exit,
+ match poll {
+ task::Poll::Pending => {
+ if let Ok(Some(flow)) = control_receiver.try_next() {
+ *control_flow = flow;
+ }
+ }
+ task::Poll::Ready(_) => {
+ *control_flow = ControlFlow::Exit;
+ }
};
}
})
@@ -253,7 +262,10 @@ async fn run_instance<A, E, C>(
mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
mut proxy: winit::event_loop::EventLoopProxy<A::Message>,
mut debug: Debug,
- mut receiver: mpsc::UnboundedReceiver<winit::event::Event<'_, A::Message>>,
+ mut event_receiver: mpsc::UnboundedReceiver<
+ winit::event::Event<'_, A::Message>,
+ >,
+ mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
init_command: Command<A::Message>,
window: winit::window::Window,
exit_on_close_request: bool,
@@ -265,6 +277,7 @@ async fn run_instance<A, E, C>(
{
use iced_futures::futures::stream::StreamExt;
use winit::event;
+ use winit::event_loop::ControlFlow;
let mut clipboard = Clipboard::connect(&window);
let mut cache = user_interface::Cache::default();
@@ -309,13 +322,21 @@ async fn run_instance<A, E, C>(
let mut mouse_interaction = mouse::Interaction::default();
let mut events = Vec::new();
let mut messages = Vec::new();
+ let mut redraw_pending = false;
debug.startup_finished();
- while let Some(event) = receiver.next().await {
+ while let Some(event) = event_receiver.next().await {
match event {
+ event::Event::NewEvents(start_cause) => {
+ redraw_pending = matches!(
+ start_cause,
+ event::StartCause::Init
+ | event::StartCause::ResumeTimeReached { .. }
+ );
+ }
event::Event::MainEventsCleared => {
- if events.is_empty() && messages.is_empty() {
+ if !redraw_pending && events.is_empty() && messages.is_empty() {
continue;
}
@@ -338,7 +359,7 @@ async fn run_instance<A, E, C>(
if !messages.is_empty()
|| matches!(
interface_state,
- user_interface::State::Outdated,
+ user_interface::State::Outdated
)
{
let mut cache =
@@ -376,6 +397,24 @@ async fn run_instance<A, E, C>(
}
}
+ // TODO: Avoid redrawing all the time by forcing widgets to
+ // request redraws on state changes
+ //
+ // Then, we can use the `interface_state` here to decide whether
+ // if a redraw is needed right away, or simply wait until a
+ // specific time.
+ let redraw_event = Event::Window(
+ crate::window::Event::RedrawRequested(Instant::now()),
+ );
+
+ let (interface_state, _) = user_interface.update(
+ &[redraw_event.clone()],
+ state.cursor_position(),
+ &mut renderer,
+ &mut clipboard,
+ &mut messages,
+ );
+
debug.draw_started();
let new_mouse_interaction = user_interface.draw(
&mut renderer,
@@ -396,6 +435,17 @@ async fn run_instance<A, E, C>(
}
window.request_redraw();
+ runtime
+ .broadcast((redraw_event, crate::event::Status::Ignored));
+
+ let _ = control_sender.start_send(match interface_state {
+ user_interface::State::Updated {
+ redraw_requested_at: Some(at),
+ } => ControlFlow::WaitUntil(at),
+ _ => ControlFlow::Wait,
+ });
+
+ redraw_pending = false;
}
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
event::MacOS::ReceivedUrl(url),