1pub mod action_label;
13pub mod battery;
14pub mod button;
15pub mod calculator;
16pub mod clock;
17pub mod common;
18pub mod device_auth;
19pub mod dialog;
20pub mod dictionary;
21pub mod file_chooser;
22pub mod filler;
23pub mod frontlight;
24pub mod github;
25pub mod home;
26pub mod icon;
27pub mod image;
28pub mod input_field;
29pub mod intermission;
30pub mod key;
31pub mod keyboard;
32pub mod label;
33pub mod labeled_icon;
34pub mod menu;
35pub mod menu_entry;
36pub mod named_input;
37pub mod navigation;
38pub mod notification;
39pub mod ota;
40
41pub use self::notification::NotificationEvent;
42pub mod page_label;
43pub mod preset;
44pub mod presets_list;
45pub mod progress_bar;
46pub mod reader;
47pub mod rotation_values;
48pub mod rounded_button;
49pub mod search_bar;
50pub mod settings_editor;
51pub mod sketch;
52pub mod slider;
53pub mod startup;
54pub mod toggle;
55pub mod toggleable_keyboard;
56pub mod top_bar;
57pub mod touch_events;
58
59use self::calculator::LineOrigin;
60use self::github::GithubEvent;
61use self::key::KeyKind;
62use crate::color::Color;
63use crate::context::Context;
64use crate::document::{Location, TextLocation};
65use crate::font::Fonts;
66use crate::framebuffer::{Framebuffer, UpdateMode};
67use crate::geom::{Boundary, CycleDir, LinearDir, Rectangle};
68use crate::gesture::GestureEvent;
69use crate::input::{DeviceEvent, FingerStatus};
70use crate::metadata::{
71 Info, Margin, PageScheme, ScrollMode, SimpleStatus, SortMethod, TextAlign, ZoomMode,
72};
73use crate::settings::{
74 self, ButtonScheme, FinishedAction, FirstColumn, RotationLock, SecondColumn,
75};
76use crate::view::ota::OtaEntryId;
77use downcast_rs::{impl_downcast, Downcast};
78use fxhash::FxHashMap;
79use std::collections::VecDeque;
80use std::fmt::{self, Debug};
81use std::ops::{Deref, DerefMut};
82use std::path::PathBuf;
83use std::sync::atomic::{AtomicU64, Ordering};
84use std::sync::mpsc::Sender;
85use std::time::{Duration, Instant};
86use tracing::error;
87use unic_langid::LanguageIdentifier;
88
89pub const THICKNESS_SMALL: f32 = 1.0;
91pub const THICKNESS_MEDIUM: f32 = 2.0;
92pub const THICKNESS_LARGE: f32 = 3.0;
93
94pub const BORDER_RADIUS_SMALL: f32 = 6.0;
96pub const BORDER_RADIUS_MEDIUM: f32 = 9.0;
97pub const BORDER_RADIUS_LARGE: f32 = 12.0;
98
99pub const SMALL_BAR_HEIGHT: f32 = 121.0;
102pub const BIG_BAR_HEIGHT: f32 = 163.0;
103
104pub const CLOSE_IGNITION_DELAY: Duration = Duration::from_millis(150);
105
106pub type Bus = VecDeque<Event>;
107pub type Hub = Sender<Event>;
108
109pub trait View: Downcast {
110 fn handle_event(
111 &mut self,
112 evt: &Event,
113 hub: &Hub,
114 bus: &mut Bus,
115 rq: &mut RenderQueue,
116 context: &mut Context,
117 ) -> bool;
118 fn render(&self, fb: &mut dyn Framebuffer, rect: Rectangle, fonts: &mut Fonts);
119 fn rect(&self) -> &Rectangle;
120 fn rect_mut(&mut self) -> &mut Rectangle;
121 fn children(&self) -> &Vec<Box<dyn View>>;
122 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>>;
123 fn id(&self) -> Id;
124
125 fn render_rect(&self, _rect: &Rectangle) -> Rectangle {
126 *self.rect()
127 }
128
129 fn resize(
130 &mut self,
131 rect: Rectangle,
132 _hub: &Hub,
133 _rq: &mut RenderQueue,
134 _context: &mut Context,
135 ) {
136 *self.rect_mut() = rect;
137 }
138
139 fn child(&self, index: usize) -> &dyn View {
140 self.children()[index].as_ref()
141 }
142
143 fn child_mut(&mut self, index: usize) -> &mut dyn View {
144 self.children_mut()[index].as_mut()
145 }
146
147 fn len(&self) -> usize {
148 self.children().len()
149 }
150
151 fn might_skip(&self, _evt: &Event) -> bool {
152 false
153 }
154
155 fn might_rotate(&self) -> bool {
156 true
157 }
158
159 fn is_background(&self) -> bool {
160 false
161 }
162
163 fn view_id(&self) -> Option<ViewId> {
164 None
165 }
166}
167
168impl_downcast!(View);
169
170impl Debug for Box<dyn View> {
171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 write!(f, "Box<dyn View>")
173 }
174}
175
176#[cfg_attr(feature = "tracing", tracing::instrument(skip(view, hub, parent_bus, rq, context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
183pub fn handle_event(
184 view: &mut dyn View,
185 evt: &Event,
186 hub: &Hub,
187 parent_bus: &mut Bus,
188 rq: &mut RenderQueue,
189 context: &mut Context,
190) -> bool {
191 if view.len() > 0 {
192 let mut captured = false;
193
194 if view.might_skip(evt) {
195 return captured;
196 }
197
198 let mut child_bus: Bus = VecDeque::with_capacity(1);
199
200 for i in (0..view.len()).rev() {
201 if handle_event(view.child_mut(i), evt, hub, &mut child_bus, rq, context) {
202 captured = true;
203 break;
204 }
205 }
206
207 let mut temp_bus: Bus = VecDeque::with_capacity(1);
208
209 child_bus
210 .retain(|child_evt| !view.handle_event(child_evt, hub, &mut temp_bus, rq, context));
211
212 parent_bus.append(&mut child_bus);
213 parent_bus.append(&mut temp_bus);
214
215 captured || view.handle_event(evt, hub, parent_bus, rq, context)
216 } else {
217 view.handle_event(evt, hub, parent_bus, rq, context)
218 }
219}
220
221#[cfg_attr(feature = "tracing", tracing::instrument(skip(view, ids, rects, bgs, fb, fonts, updating), fields(wait = wait)))]
225pub fn render(
226 view: &dyn View,
227 wait: bool,
228 ids: &FxHashMap<Id, Vec<Rectangle>>,
229 rects: &mut Vec<Rectangle>,
230 bgs: &mut Vec<Rectangle>,
231 fb: &mut dyn Framebuffer,
232 fonts: &mut Fonts,
233 updating: &mut Vec<UpdateData>,
234) {
235 let mut render_rects = Vec::new();
236
237 if view.len() == 0 || view.is_background() {
238 for rect in ids
239 .get(&view.id())
240 .cloned()
241 .into_iter()
242 .flatten()
243 .chain(rects.iter().filter_map(|r| r.intersection(view.rect())))
244 .chain(bgs.iter().filter_map(|r| r.intersection(view.rect())))
245 {
246 let render_rect = view.render_rect(&rect);
247
248 if wait {
249 updating.retain(|update| {
250 let overlaps = render_rect.overlaps(&update.rect);
251 if overlaps && !update.has_completed() {
252 fb.wait(update.token)
253 .map_err(|e| {
254 error!("Can't wait for {}, {}: {:#}", update.token, update.rect, e)
255 })
256 .ok();
257 }
258 !overlaps
259 });
260 }
261
262 view.render(fb, rect, fonts);
263 render_rects.push(render_rect);
264
265 if *view.rect() == render_rect {
267 break;
268 }
269 }
270 } else {
271 bgs.extend(ids.get(&view.id()).cloned().into_iter().flatten());
272 }
273
274 for rect in render_rects.into_iter() {
276 if rects.is_empty() {
277 rects.push(rect);
278 } else {
279 if let Some(last) = rects.last_mut() {
280 if rect.extends(last) {
281 last.absorb(&rect);
282 let mut i = rects.len();
283 while i > 1 && rects[i - 1].extends(&rects[i - 2]) {
284 if let Some(rect) = rects.pop() {
285 if let Some(last) = rects.last_mut() {
286 last.absorb(&rect);
287 }
288 }
289 i -= 1;
290 }
291 } else {
292 let mut i = rects.len();
293 while i > 0 && !rects[i - 1].contains(&rect) {
294 i -= 1;
295 }
296 if i == 0 {
297 rects.push(rect);
298 }
299 }
300 }
301 }
302 }
303
304 for i in 0..view.len() {
305 render(view.child(i), wait, ids, rects, bgs, fb, fonts, updating);
306 }
307}
308
309#[inline]
310pub fn process_render_queue(
311 view: &dyn View,
312 rq: &mut RenderQueue,
313 context: &mut Context,
314 updating: &mut Vec<UpdateData>,
315) {
316 for ((mode, wait), pairs) in rq.drain() {
317 let mut ids = FxHashMap::default();
318 let mut rects = Vec::new();
319 let mut bgs = Vec::new();
320
321 for (id, rect) in pairs.into_iter().rev() {
322 if let Some(id) = id {
323 ids.entry(id).or_insert_with(Vec::new).push(rect);
324 } else {
325 bgs.push(rect);
326 }
327 }
328
329 render(
330 view,
331 wait,
332 &ids,
333 &mut rects,
334 &mut bgs,
335 context.fb.as_mut(),
336 &mut context.fonts,
337 updating,
338 );
339
340 for rect in rects {
341 match context.fb.update(&rect, mode) {
342 Ok(token) => {
343 updating.push(UpdateData {
344 token,
345 rect,
346 time: Instant::now(),
347 });
348 }
349 Err(err) => {
350 error!("Can't update {}: {:#}.", rect, err);
351 }
352 }
353 }
354 }
355}
356
357#[inline]
358pub fn wait_for_all(updating: &mut Vec<UpdateData>, context: &mut Context) {
359 for update in updating.drain(..) {
360 if update.has_completed() {
361 continue;
362 }
363 context
364 .fb
365 .wait(update.token)
366 .map_err(|e| error!("Can't wait for {}, {}: {:#}", update.token, update.rect, e))
367 .ok();
368 }
369}
370
371#[derive(Debug, Clone, PartialEq, Eq)]
372pub enum ToggleEvent {
373 View(ViewId),
374 Setting(settings_editor::ToggleSettings),
375}
376
377#[derive(Debug, Clone)]
378pub enum Event {
379 Device(DeviceEvent),
380 Gesture(GestureEvent),
381 Keyboard(KeyboardEvent),
382 Key(KeyKind),
383 Open(Box<Info>),
384 OpenHtml(String, Option<String>),
385 LoadPixmap(usize),
386 Update(UpdateMode),
387 RefreshBookPreview(PathBuf),
388 Invalid(PathBuf),
389 Notification(NotificationEvent),
390 Page(CycleDir),
391 ResultsPage(CycleDir),
392 GoTo(usize),
393 GoToLocation(Location),
394 ResultsGoTo(usize),
395 CropMargins(Box<Margin>),
396 Chapter(CycleDir),
397 SelectDirectory(PathBuf),
398 ToggleSelectDirectory(PathBuf),
399 NavigationBarResized(i32),
400 Focus(Option<ViewId>),
442 Select(EntryId),
443 PropagateSelect(EntryId),
444 EditLanguages,
445 Define(String),
446 Submit(ViewId, String),
447 Slider(SliderId, f32, FingerStatus),
448 ToggleNear(ViewId, Rectangle),
449 ToggleInputHistoryMenu(ViewId, Rectangle),
450 ToggleBookMenu(Rectangle, usize),
451 TogglePresetMenu(Rectangle, usize),
452 SubMenu(Rectangle, Vec<EntryKind>),
453 OpenSettingsCategory(settings_editor::Category),
454 SelectSettingsCategory(settings_editor::Category),
455 UpdateSettings(Box<settings::Settings>),
456 EditLibrary(usize),
457 UpdateLibrary(usize, Box<settings::LibrarySettings>),
458 AddLibrary,
459 DeleteLibrary(usize),
460 OpenRefreshRateEditor,
462 EditRefreshRateByKind(settings::FileExtension),
464 UpdateRefreshRateByKind(settings::FileExtension, Box<settings::RefreshRatePair>),
466 DeleteRefreshRateByKind(settings::FileExtension),
468 ProcessLine(LineOrigin, String),
469 History(CycleDir, bool),
470 Toggle(ToggleEvent),
471 Show(ViewId),
472 Close(ViewId),
473 CloseSub(ViewId),
474 Search(String),
475 SearchResult(usize, Vec<Boundary>),
476 FetcherAddDocument(u32, Box<Info>),
477 FetcherRemoveDocument(u32, PathBuf),
478 FetcherSearch {
479 id: u32,
480 path: Option<PathBuf>,
481 query: Option<String>,
482 sort_by: Option<(SortMethod, bool)>,
483 },
484 CheckFetcher(u32),
485 EndOfSearch,
486 Finished,
487 ClockTick,
488 BatteryTick,
489 ToggleFrontlight,
490 Load(PathBuf),
491 LoadPreset(usize),
492 Scroll(i32),
493 Save,
494 Guess,
495 CheckBattery,
496 SetWifi(bool),
497 MightSuspend,
498 PrepareSuspend,
499 Suspend,
500 Share,
501 PrepareShare,
502 Validate,
503 Cancel,
504 Reseed,
505 Back,
506 Quit,
507 WakeUp,
508 Hold(EntryId),
509 FileChooserClosed(Option<PathBuf>),
512 Github(GithubEvent),
514 Settings(settings_editor::SettingsEvent),
516 OpenNamedInput {
521 view_id: ViewId,
523 label: String,
525 max_chars: usize,
527 initial_text: String,
529 },
530 OtaDownloadProgress {
535 label: String,
536 percent: u8,
537 },
538 StartStableReleaseDownload,
543 DictionaryInstallComplete {
549 lang: String,
550 result: Result<(), String>,
551 },
552 ImportLibrary {
554 library_index: Option<usize>,
555 },
556 ImportFinished {
558 library_index: Option<usize>,
559 },
560 ReindexDictionaries,
567 ReloadDictionaries,
576}
577
578#[derive(Debug, Clone, Eq, PartialEq)]
579pub enum AppCmd {
580 Sketch,
581 Calculator,
582 Dictionary { query: String, language: String },
583 SettingsEditor,
584 TouchEvents,
585 RotationValues,
586}
587
588#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
589pub enum ViewId {
590 Home,
591 Reader,
592 SortMenu,
593 MainMenu,
594 TitleMenu,
595 SelectionMenu,
596 AnnotationMenu,
597 BatteryMenu,
598 ClockMenu,
599 SearchTargetMenu,
600 InputHistoryMenu,
601 KeyboardLayoutMenu,
602 Frontlight,
603 Dictionary,
604 FontSizeMenu,
605 TextAlignMenu,
606 FontFamilyMenu,
607 MarginWidthMenu,
608 ContrastExponentMenu,
609 ContrastGrayMenu,
610 LineHeightMenu,
611 DirectoryMenu,
612 BookMenu,
613 LibraryMenu,
614 PageMenu,
615 PresetMenu,
616 MarginCropperMenu,
617 SearchMenu,
618 SettingsMenu,
620 SettingsValueMenu,
621 SettingsCategoryEditor,
622 LibraryEditor,
623 LibraryRename,
624 LibraryRenameInput,
625 AutoSuspendInput,
626 AutoPowerOffInput,
627 SettingsRetentionInput,
628 IntermissionSuspendInput,
629 IntermissionPowerOffInput,
630 IntermissionShareInput,
631 OtlpEndpointInput,
632 PyroscopeEndpointInput,
633 RefreshRateByKindEditor,
634 RefreshRateKindPairEditor,
635 RefreshRateRegularInput,
636 RefreshRateInvertedInput,
637 RefreshRateByKindRegularInput,
638 RefreshRateByKindInvertedInput,
639 SketchMenu,
640 RenameDocument,
641 RenameDocumentInput,
642 GoToPage,
643 GoToPageInput,
644 GoToResultsPage,
645 GoToResultsPageInput,
646 NamePage,
647 NamePageInput,
648 EditNote,
649 EditNoteInput,
650 EditLanguages,
651 EditLanguagesInput,
652 HomeSearchInput,
653 ReaderSearchInput,
654 DictionarySearchInput,
655 CalculatorInput,
656 SearchBar,
657 AddressBar,
658 AddressBarInput,
659 Keyboard,
660 AboutDialog,
661 ShareDialog,
662 MarginCropper,
663 TopBottomBars,
664 TableOfContents,
665 MessageNotif(Id),
666 SubMenu(u8),
667 Ota(ota::OtaViewId),
668 FileChooser,
669}
670
671#[derive(Debug, Copy, Clone, Eq, PartialEq)]
672pub enum SliderId {
673 FontSize,
674 LightIntensity,
675 LightWarmth,
676 ContrastExponent,
677 ContrastGray,
678}
679
680impl SliderId {
681 pub fn label(self) -> String {
682 match self {
683 SliderId::LightIntensity => "Intensity".to_string(),
684 SliderId::LightWarmth => "Warmth".to_string(),
685 SliderId::FontSize => "Font Size".to_string(),
686 SliderId::ContrastExponent => "Contrast Exponent".to_string(),
687 SliderId::ContrastGray => "Contrast Gray".to_string(),
688 }
689 }
690}
691
692#[derive(Debug, Clone)]
693pub enum Align {
694 Left(i32),
695 Right(i32),
696 Center,
697}
698
699impl Align {
700 #[inline]
701 pub fn offset(&self, width: i32, container_width: i32) -> i32 {
702 match *self {
703 Align::Left(dx) => dx,
704 Align::Right(dx) => container_width - width - dx,
705 Align::Center => (container_width - width) / 2,
706 }
707 }
708}
709
710#[derive(Debug, Copy, Clone)]
711pub enum KeyboardEvent {
712 Append(char),
713 Partial(char),
714 Move { target: TextKind, dir: LinearDir },
715 Delete { target: TextKind, dir: LinearDir },
716 Submit,
717}
718
719#[derive(Debug, Copy, Clone)]
720pub enum TextKind {
721 Char,
722 Word,
723 Extremum,
724}
725
726#[derive(Debug, Clone)]
727pub enum EntryKind {
728 Message(String, Option<String>),
729 Command(String, EntryId),
730 CheckBox(String, EntryId, bool),
731 RadioButton(String, EntryId, bool),
732 SubMenu(String, Vec<EntryKind>),
733 More(Vec<EntryKind>),
734 Separator,
735}
736
737#[derive(Debug, Clone, Eq, PartialEq)]
738pub enum EntryId {
739 About,
740 SystemInfo,
741 OpenDocumentation,
742 LoadLibrary(usize),
743 Load(PathBuf),
744 Flush,
745 Save,
746 Import,
747 CleanUp,
748 Sort(SortMethod),
749 ReverseOrder,
750 EmptyTrash,
751 Rename(PathBuf),
752 Remove(PathBuf),
753 CopyTo(PathBuf, usize),
754 MoveTo(PathBuf, usize),
755 AddDirectory(PathBuf),
756 SelectDirectory(PathBuf),
757 ToggleSelectDirectory(PathBuf),
758 SetStatus(PathBuf, SimpleStatus),
759 SearchAuthor(String),
760 RemovePreset(usize),
761 FirstColumn(FirstColumn),
762 SecondColumn(SecondColumn),
763 ThumbnailPreviews,
764 ApplyCroppings(usize, PageScheme),
765 RemoveCroppings,
766 SetZoomMode(ZoomMode),
767 SetScrollMode(ScrollMode),
768 SetPageName,
769 RemovePageName,
770 HighlightSelection,
771 AnnotateSelection,
772 DefineSelection,
773 SearchForSelection,
774 AdjustSelection,
775 Annotations,
776 Bookmarks,
777 RemoveAnnotation([TextLocation; 2]),
778 EditAnnotationNote([TextLocation; 2]),
779 RemoveAnnotationNote([TextLocation; 2]),
780 GoTo(usize),
781 GoToSelectedPageName,
782 SearchDirection(LinearDir),
783 SetButtonScheme(ButtonScheme),
784 SetFontFamily(String),
785 SetFontSize(i32),
786 SetTextAlign(TextAlign),
787 SetMarginWidth(i32),
788 SetLineHeight(i32),
789 SetContrastExponent(i32),
790 SetContrastGray(i32),
791 SetRotationLock(Option<RotationLock>),
792 SetSearchTarget(Option<String>),
793 SetInputText(ViewId, String),
794 SetKeyboardLayout(String),
795 SetLocale(Option<LanguageIdentifier>),
796 EditLibraryName,
798 EditLibraryPath,
799 DeleteLibrary(usize),
800 SetFinishedAction(FinishedAction),
801 SetLibraryFinishedAction(usize, FinishedAction),
802 ClearLibraryFinishedAction(usize),
803 SetIntermission(settings::IntermKind, settings::IntermissionDisplay),
804 EditIntermissionImage(settings::IntermKind),
805 ToggleShowHidden,
806 #[deprecated(note = "Use ToggleEvent::Settings instead")]
807 ToggleSleepCover,
808 #[deprecated(note = "Use ToggleEvent::Settings instead")]
809 ToggleAutoShare,
810 EditAutoSuspend,
811 EditAutoPowerOff,
812 EditSettingsRetention,
813 SetLogLevel(tracing::Level),
814 EditOtlpEndpoint,
815 EditPyroscopeEndpoint,
816 ToggleFuzzy,
817 ToggleInverted,
818 ToggleDithered,
819 ToggleWifi,
820 Rotate(i8),
821 Launch(AppCmd),
822 SetPenSize(i32),
823 SetPenColor(Color),
824 TogglePenDynamism,
825 ReloadDictionaries,
826 DownloadDictionary(String),
827 RedownloadDictionary(String),
828 DeleteDictionary(String),
829 New,
830 Refresh,
831 TakeScreenshot,
832 Restart,
833 Reboot,
834 Quit,
835 Suspend,
836 PowerOff,
837 CheckForUpdates,
838 FileEntry(PathBuf),
839 Ota(OtaEntryId),
840 EditRefreshRateByKind(settings::FileExtension),
842 DeleteRefreshRateByKind(settings::FileExtension),
844 AddRefreshRateByKind,
846}
847
848impl EntryKind {
849 pub fn is_separator(&self) -> bool {
850 matches!(*self, EntryKind::Separator)
851 }
852
853 pub fn text(&self) -> &str {
854 match *self {
855 EntryKind::Message(ref s, ..)
856 | EntryKind::Command(ref s, ..)
857 | EntryKind::CheckBox(ref s, ..)
858 | EntryKind::RadioButton(ref s, ..)
859 | EntryKind::SubMenu(ref s, ..) => s,
860 EntryKind::More(..) => "More",
861 _ => "",
862 }
863 }
864
865 pub fn get(&self) -> Option<bool> {
866 match *self {
867 EntryKind::CheckBox(_, _, v) | EntryKind::RadioButton(_, _, v) => Some(v),
868 _ => None,
869 }
870 }
871
872 pub fn set(&mut self, value: bool) {
873 match *self {
874 EntryKind::CheckBox(_, _, ref mut v) | EntryKind::RadioButton(_, _, ref mut v) => {
875 *v = value
876 }
877 _ => (),
878 }
879 }
880}
881
882pub struct RenderData {
883 pub id: Option<Id>,
884 pub rect: Rectangle,
885 pub mode: UpdateMode,
886 pub wait: bool,
887}
888
889impl RenderData {
890 pub fn new(id: Id, rect: Rectangle, mode: UpdateMode) -> RenderData {
891 RenderData {
892 id: Some(id),
893 rect,
894 mode,
895 wait: true,
896 }
897 }
898
899 pub fn no_wait(id: Id, rect: Rectangle, mode: UpdateMode) -> RenderData {
900 RenderData {
901 id: Some(id),
902 rect,
903 mode,
904 wait: false,
905 }
906 }
907
908 pub fn expose(rect: Rectangle, mode: UpdateMode) -> RenderData {
909 RenderData {
910 id: None,
911 rect,
912 mode,
913 wait: true,
914 }
915 }
916}
917
918pub struct UpdateData {
919 pub token: u32,
920 pub time: Instant,
921 pub rect: Rectangle,
922}
923
924pub const MAX_UPDATE_DELAY: Duration = Duration::from_millis(600);
925
926impl UpdateData {
927 pub fn has_completed(&self) -> bool {
928 self.time.elapsed() >= MAX_UPDATE_DELAY
929 }
930}
931
932type RQ = FxHashMap<(UpdateMode, bool), Vec<(Option<Id>, Rectangle)>>;
933pub struct RenderQueue(RQ);
934
935impl RenderQueue {
936 pub fn new() -> RenderQueue {
937 RenderQueue(FxHashMap::default())
938 }
939
940 pub fn add(&mut self, data: RenderData) {
941 self.entry((data.mode, data.wait))
942 .or_insert_with(|| Vec::new())
943 .push((data.id, data.rect));
944 }
945
946 #[cfg(test)]
947 pub fn is_empty(&self) -> bool {
948 self.0.is_empty()
949 }
950
951 #[cfg(test)]
952 pub fn len(&self) -> usize {
953 self.0.values().map(|v| v.len()).sum()
954 }
955}
956
957impl Default for RenderQueue {
958 fn default() -> Self {
959 Self::new()
960 }
961}
962
963impl Deref for RenderQueue {
964 type Target = RQ;
965
966 fn deref(&self) -> &Self::Target {
967 &self.0
968 }
969}
970
971impl DerefMut for RenderQueue {
972 fn deref_mut(&mut self) -> &mut Self::Target {
973 &mut self.0
974 }
975}
976
977pub static ID_FEEDER: IdFeeder = IdFeeder::new(1);
978pub struct IdFeeder(AtomicU64);
979pub type Id = u64;
980
981impl IdFeeder {
982 pub const fn new(id: Id) -> Self {
983 IdFeeder(AtomicU64::new(id))
984 }
985
986 pub fn next(&self) -> Id {
987 self.0.fetch_add(1, Ordering::Relaxed)
988 }
989}