| //! Scheduler for emitting events at a specific time in the future. |
| |
| use std::collections::VecDeque; |
| use std::time::{Duration, Instant}; |
| |
| use glutin::event_loop::EventLoopProxy; |
| use glutin::window::WindowId; |
| |
| use crate::event::Event; |
| |
| /// ID uniquely identifying a timer. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| pub struct TimerId { |
| topic: Topic, |
| window_id: WindowId, |
| } |
| |
| impl TimerId { |
| pub fn new(topic: Topic, window_id: WindowId) -> Self { |
| Self { topic, window_id } |
| } |
| } |
| |
| /// Available timer topics. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| pub enum Topic { |
| SelectionScrolling, |
| DelayedSearch, |
| BlinkCursor, |
| } |
| |
| /// Event scheduled to be emitted at a specific time. |
| pub struct Timer { |
| pub deadline: Instant, |
| pub event: Event, |
| pub id: TimerId, |
| |
| interval: Option<Duration>, |
| } |
| |
| /// Scheduler tracking all pending timers. |
| pub struct Scheduler { |
| timers: VecDeque<Timer>, |
| event_proxy: EventLoopProxy<Event>, |
| } |
| |
| impl Scheduler { |
| pub fn new(event_proxy: EventLoopProxy<Event>) -> Self { |
| Self { timers: VecDeque::new(), event_proxy } |
| } |
| |
| /// Process all pending timers. |
| /// |
| /// If there are still timers pending after all ready events have been processed, the closest |
| /// pending deadline will be returned. |
| pub fn update(&mut self) -> Option<Instant> { |
| let now = Instant::now(); |
| |
| while !self.timers.is_empty() && self.timers[0].deadline <= now { |
| if let Some(timer) = self.timers.pop_front() { |
| // Automatically repeat the event. |
| if let Some(interval) = timer.interval { |
| self.schedule(timer.event.clone(), interval, true, timer.id); |
| } |
| |
| let _ = self.event_proxy.send_event(timer.event); |
| } |
| } |
| |
| self.timers.get(0).map(|timer| timer.deadline) |
| } |
| |
| /// Schedule a new event. |
| pub fn schedule(&mut self, event: Event, interval: Duration, repeat: bool, timer_id: TimerId) { |
| let deadline = Instant::now() + interval; |
| |
| // Get insert position in the schedule. |
| let index = self |
| .timers |
| .iter() |
| .position(|timer| timer.deadline > deadline) |
| .unwrap_or_else(|| self.timers.len()); |
| |
| // Set the automatic event repeat rate. |
| let interval = if repeat { Some(interval) } else { None }; |
| |
| self.timers.insert(index, Timer { interval, deadline, event, id: timer_id }); |
| } |
| |
| /// Cancel a scheduled event. |
| pub fn unschedule(&mut self, id: TimerId) -> Option<Timer> { |
| let index = self.timers.iter().position(|timer| timer.id == id)?; |
| self.timers.remove(index) |
| } |
| |
| /// Check if a timer is already scheduled. |
| pub fn scheduled(&mut self, id: TimerId) -> bool { |
| self.timers.iter().any(|timer| timer.id == id) |
| } |
| |
| /// Remove all timers scheduled for a window. |
| /// |
| /// This must be called when a window is removed to ensure that timers on intervals do not |
| /// stick around forever and cause a memory leak. |
| pub fn unschedule_window(&mut self, window_id: WindowId) { |
| self.timers.retain(|timer| timer.id.window_id != window_id); |
| } |
| } |