1mod state;
3mod window_manager;
4
5pub use state::State;
6
7use crate::conversion;
8use crate::core;
9use crate::core::mouse;
10use crate::core::renderer;
11use crate::core::theme;
12use crate::core::time::Instant;
13use crate::core::widget::operation;
14use crate::core::window;
15use crate::core::{Element, Point, Size};
16use crate::futures::futures::channel::mpsc;
17use crate::futures::futures::channel::oneshot;
18use crate::futures::futures::task;
19use crate::futures::futures::{Future, StreamExt};
20use crate::futures::subscription::{self, Subscription};
21use crate::futures::{Executor, Runtime};
22use crate::graphics;
23use crate::graphics::{Compositor, compositor};
24use crate::runtime::Debug;
25use crate::runtime::user_interface::{self, UserInterface};
26use crate::runtime::{self, Action, Task};
27use crate::{Clipboard, Error, Proxy, Settings};
28
29use window_manager::WindowManager;
30
31use rustc_hash::FxHashMap;
32use std::borrow::Cow;
33use std::mem::ManuallyDrop;
34use std::sync::Arc;
35
36pub trait Program
48where
49 Self: Sized,
50 Self::Theme: theme::Base,
51{
52 type Message: std::fmt::Debug + Send;
54
55 type Theme;
57
58 type Executor: Executor;
65
66 type Renderer: core::Renderer + core::text::Renderer;
68
69 type Flags;
71
72 fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);
81
82 fn title(&self, window: window::Id) -> String;
87
88 fn update(&mut self, message: Self::Message) -> Task<Self::Message>;
97
98 fn view(
102 &self,
103 window: window::Id,
104 ) -> Element<'_, Self::Message, Self::Theme, Self::Renderer>;
105
106 fn theme(&self, window: window::Id) -> Self::Theme;
108
109 fn style(&self, theme: &Self::Theme) -> theme::Style {
111 theme::Base::base(theme)
112 }
113
114 fn subscription(&self) -> Subscription<Self::Message> {
124 Subscription::none()
125 }
126
127 #[allow(unused_variables)]
137 fn scale_factor(&self, window: window::Id) -> f64 {
138 1.0
139 }
140}
141
142pub fn run<P, C>(
145 settings: Settings,
146 graphics_settings: graphics::Settings,
147 window_settings: Option<window::Settings>,
148 flags: P::Flags,
149) -> Result<(), Error>
150where
151 P: Program + 'static,
152 C: Compositor<Renderer = P::Renderer> + 'static,
153 P::Theme: theme::Base,
154{
155 use winit::event_loop::EventLoop;
156
157 let mut debug = Debug::new();
158 debug.startup_started();
159
160 let event_loop = EventLoop::with_user_event()
161 .build()
162 .expect("Create event loop");
163
164 let (proxy, worker) = Proxy::new(event_loop.create_proxy());
165
166 let mut runtime = {
167 let executor =
168 P::Executor::new().map_err(Error::ExecutorCreationFailed)?;
169 executor.spawn(worker);
170
171 Runtime::new(executor, proxy.clone())
172 };
173
174 let (program, task) = runtime.enter(|| P::new(flags));
175 let is_daemon = window_settings.is_none();
176
177 let task = if let Some(window_settings) = window_settings {
178 let mut task = Some(task);
179
180 let (_id, open) = runtime::window::open(window_settings);
181
182 open.then(move |_| task.take().unwrap_or(Task::none()))
183 } else {
184 task
185 };
186
187 if let Some(stream) = runtime::task::into_stream(task) {
188 runtime.run(stream);
189 }
190
191 runtime.track(subscription::into_recipes(
192 runtime.enter(|| program.subscription().map(Action::Output)),
193 ));
194
195 let (event_sender, event_receiver) = mpsc::unbounded();
196 let (control_sender, control_receiver) = mpsc::unbounded();
197
198 let instance = Box::pin(run_instance::<P, C>(
199 program,
200 runtime,
201 proxy.clone(),
202 debug,
203 event_receiver,
204 control_sender,
205 is_daemon,
206 graphics_settings,
207 settings.fonts,
208 ));
209
210 let context = task::Context::from_waker(task::noop_waker_ref());
211
212 struct Runner<Message: 'static, F> {
213 instance: std::pin::Pin<Box<F>>,
214 context: task::Context<'static>,
215 id: Option<String>,
216 sender: mpsc::UnboundedSender<Event<Action<Message>>>,
217 receiver: mpsc::UnboundedReceiver<Control>,
218 error: Option<Error>,
219
220 #[cfg(target_arch = "wasm32")]
221 canvas: Option<web_sys::HtmlCanvasElement>,
222 }
223
224 let runner = Runner {
225 instance,
226 context,
227 id: settings.id,
228 sender: event_sender,
229 receiver: control_receiver,
230 error: None,
231
232 #[cfg(target_arch = "wasm32")]
233 canvas: None,
234 };
235
236 impl<Message, F> winit::application::ApplicationHandler<Action<Message>>
237 for Runner<Message, F>
238 where
239 Message: std::fmt::Debug,
240 F: Future<Output = ()>,
241 {
242 fn resumed(
243 &mut self,
244 _event_loop: &winit::event_loop::ActiveEventLoop,
245 ) {
246 }
247
248 fn new_events(
249 &mut self,
250 event_loop: &winit::event_loop::ActiveEventLoop,
251 cause: winit::event::StartCause,
252 ) {
253 self.process_event(
254 event_loop,
255 Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)),
256 );
257 }
258
259 fn window_event(
260 &mut self,
261 event_loop: &winit::event_loop::ActiveEventLoop,
262 window_id: winit::window::WindowId,
263 event: winit::event::WindowEvent,
264 ) {
265 #[cfg(target_os = "windows")]
266 let is_move_or_resize = matches!(
267 event,
268 winit::event::WindowEvent::Resized(_)
269 | winit::event::WindowEvent::Moved(_)
270 );
271
272 self.process_event(
273 event_loop,
274 Event::EventLoopAwakened(winit::event::Event::WindowEvent {
275 window_id,
276 event,
277 }),
278 );
279
280 #[cfg(target_os = "windows")]
285 {
286 if is_move_or_resize {
287 self.process_event(
288 event_loop,
289 Event::EventLoopAwakened(
290 winit::event::Event::AboutToWait,
291 ),
292 );
293 }
294 }
295 }
296
297 fn user_event(
298 &mut self,
299 event_loop: &winit::event_loop::ActiveEventLoop,
300 action: Action<Message>,
301 ) {
302 self.process_event(
303 event_loop,
304 Event::EventLoopAwakened(winit::event::Event::UserEvent(
305 action,
306 )),
307 );
308 }
309
310 fn received_url(
311 &mut self,
312 event_loop: &winit::event_loop::ActiveEventLoop,
313 url: String,
314 ) {
315 self.process_event(
316 event_loop,
317 Event::EventLoopAwakened(
318 winit::event::Event::PlatformSpecific(
319 winit::event::PlatformSpecific::MacOS(
320 winit::event::MacOS::ReceivedUrl(url),
321 ),
322 ),
323 ),
324 );
325 }
326
327 fn about_to_wait(
328 &mut self,
329 event_loop: &winit::event_loop::ActiveEventLoop,
330 ) {
331 self.process_event(
332 event_loop,
333 Event::EventLoopAwakened(winit::event::Event::AboutToWait),
334 );
335 }
336 }
337
338 impl<Message, F> Runner<Message, F>
339 where
340 F: Future<Output = ()>,
341 {
342 fn process_event(
343 &mut self,
344 event_loop: &winit::event_loop::ActiveEventLoop,
345 event: Event<Action<Message>>,
346 ) {
347 if event_loop.exiting() {
348 return;
349 }
350
351 self.sender.start_send(event).expect("Send event");
352
353 loop {
354 let poll = self.instance.as_mut().poll(&mut self.context);
355
356 match poll {
357 task::Poll::Pending => match self.receiver.try_next() {
358 Ok(Some(control)) => match control {
359 Control::ChangeFlow(flow) => {
360 use winit::event_loop::ControlFlow;
361
362 match (event_loop.control_flow(), flow) {
363 (
364 ControlFlow::WaitUntil(current),
365 ControlFlow::WaitUntil(new),
366 ) if current < new => {}
367 (
368 ControlFlow::WaitUntil(target),
369 ControlFlow::Wait,
370 ) if target > Instant::now() => {}
371 _ => {
372 event_loop.set_control_flow(flow);
373 }
374 }
375 }
376 Control::CreateWindow {
377 id,
378 settings,
379 title,
380 monitor,
381 on_open,
382 } => {
383 let exit_on_close_request =
384 settings.exit_on_close_request;
385
386 let visible = settings.visible;
387
388 #[cfg(target_arch = "wasm32")]
389 let target =
390 settings.platform_specific.target.clone();
391
392 let window_attributes =
393 conversion::window_attributes(
394 settings,
395 &title,
396 monitor
397 .or(event_loop.primary_monitor()),
398 self.id.clone(),
399 )
400 .with_visible(false);
401
402 #[cfg(target_arch = "wasm32")]
403 let window_attributes = {
404 use winit::platform::web::WindowAttributesExtWebSys;
405 window_attributes
406 .with_canvas(self.canvas.take())
407 };
408
409 log::info!(
410 "Window attributes for id `{id:#?}`: {window_attributes:#?}"
411 );
412
413 #[cfg(target_os = "macos")]
417 let mut window_attributes = window_attributes;
418
419 #[cfg(target_os = "macos")]
420 let position =
421 window_attributes.position.take();
422
423 let window = event_loop
424 .create_window(window_attributes)
425 .expect("Create window");
426
427 #[cfg(target_os = "macos")]
428 if let Some(position) = position {
429 window.set_outer_position(position);
430 }
431
432 #[cfg(target_arch = "wasm32")]
433 {
434 use winit::platform::web::WindowExtWebSys;
435
436 let canvas = window
437 .canvas()
438 .expect("Get window canvas");
439
440 let _ = canvas.set_attribute(
441 "style",
442 "display: block; width: 100%; height: 100%",
443 );
444
445 let window = web_sys::window().unwrap();
446 let document = window.document().unwrap();
447 let body = document.body().unwrap();
448
449 let target = target.and_then(|target| {
450 body.query_selector(&format!(
451 "#{target}"
452 ))
453 .ok()
454 .unwrap_or(None)
455 });
456
457 match target {
458 Some(node) => {
459 let _ = node
460 .replace_with_with_node_1(
461 &canvas,
462 )
463 .expect(&format!(
464 "Could not replace #{}",
465 node.id()
466 ));
467 }
468 None => {
469 let _ = body
470 .append_child(&canvas)
471 .expect(
472 "Append canvas to HTML body",
473 );
474 }
475 };
476 }
477
478 self.process_event(
479 event_loop,
480 Event::WindowCreated {
481 id,
482 window: Arc::new(window),
483 exit_on_close_request,
484 make_visible: visible,
485 on_open,
486 },
487 );
488 }
489 Control::Exit => {
490 event_loop.exit();
491 }
492 Control::Crash(error) => {
493 self.error = Some(error);
494 event_loop.exit();
495 }
496 },
497 _ => {
498 break;
499 }
500 },
501 task::Poll::Ready(_) => {
502 event_loop.exit();
503 break;
504 }
505 };
506 }
507 }
508 }
509
510 #[cfg(not(target_arch = "wasm32"))]
511 {
512 let mut runner = runner;
513 let _ = event_loop.run_app(&mut runner);
514
515 runner.error.map(Err).unwrap_or(Ok(()))
516 }
517
518 #[cfg(target_arch = "wasm32")]
519 {
520 use winit::platform::web::EventLoopExtWebSys;
521 let _ = event_loop.spawn_app(runner);
522
523 Ok(())
524 }
525}
526
527#[derive(Debug)]
528enum Event<Message: 'static> {
529 WindowCreated {
530 id: window::Id,
531 window: Arc<winit::window::Window>,
532 exit_on_close_request: bool,
533 make_visible: bool,
534 on_open: oneshot::Sender<window::Id>,
535 },
536 EventLoopAwakened(winit::event::Event<Message>),
537}
538
539#[derive(Debug)]
540enum Control {
541 ChangeFlow(winit::event_loop::ControlFlow),
542 Exit,
543 Crash(Error),
544 CreateWindow {
545 id: window::Id,
546 settings: window::Settings,
547 title: String,
548 monitor: Option<winit::monitor::MonitorHandle>,
549 on_open: oneshot::Sender<window::Id>,
550 },
551}
552
553async fn run_instance<P, C>(
554 mut program: P,
555 mut runtime: Runtime<P::Executor, Proxy<P::Message>, Action<P::Message>>,
556 mut proxy: Proxy<P::Message>,
557 mut debug: Debug,
558 mut event_receiver: mpsc::UnboundedReceiver<Event<Action<P::Message>>>,
559 mut control_sender: mpsc::UnboundedSender<Control>,
560 is_daemon: bool,
561 graphics_settings: graphics::Settings,
562 default_fonts: Vec<Cow<'static, [u8]>>,
563) where
564 P: Program + 'static,
565 C: Compositor<Renderer = P::Renderer> + 'static,
566 P::Theme: theme::Base,
567{
568 use winit::event;
569 use winit::event_loop::ControlFlow;
570
571 let mut window_manager = WindowManager::new();
572 let mut is_window_opening = !is_daemon;
573
574 let mut compositor = None;
575 let mut events = Vec::new();
576 let mut messages = Vec::new();
577 let mut actions = 0;
578
579 let mut ui_caches = FxHashMap::default();
580 let mut user_interfaces = ManuallyDrop::new(FxHashMap::default());
581 let mut clipboard = Clipboard::unconnected();
582 let mut compositor_receiver: Option<oneshot::Receiver<_>> = None;
583
584 debug.startup_finished();
585
586 loop {
587 let event = if compositor_receiver.is_some() {
588 let compositor_receiver =
589 compositor_receiver.take().expect("Waiting for compositor");
590
591 match compositor_receiver.await {
592 Ok(Ok((new_compositor, event))) => {
593 compositor = Some(new_compositor);
594
595 Some(event)
596 }
597 Ok(Err(error)) => {
598 control_sender
599 .start_send(Control::Crash(
600 Error::GraphicsCreationFailed(error),
601 ))
602 .expect("Send control action");
603 break;
604 }
605 Err(error) => {
606 panic!("Compositor initialization failed: {error}")
607 }
608 }
609 } else if let Ok(event) = event_receiver.try_next() {
611 event
612 } else {
613 event_receiver.next().await
614 };
615
616 let Some(event) = event else {
617 break;
618 };
619
620 match event {
621 Event::WindowCreated {
622 id,
623 window,
624 exit_on_close_request,
625 make_visible,
626 on_open,
627 } => {
628 if compositor.is_none() {
629 let (compositor_sender, new_compositor_receiver) =
630 oneshot::channel();
631
632 compositor_receiver = Some(new_compositor_receiver);
633
634 let create_compositor = {
635 let default_fonts = default_fonts.clone();
636
637 async move {
638 let mut compositor =
639 C::new(graphics_settings, window.clone()).await;
640
641 if let Ok(compositor) = &mut compositor {
642 for font in default_fonts {
643 compositor.load_font(font.clone());
644 }
645 }
646
647 compositor_sender
648 .send(compositor.map(|compositor| {
649 (
650 compositor,
651 Event::WindowCreated {
652 id,
653 window,
654 exit_on_close_request,
655 make_visible,
656 on_open,
657 },
658 )
659 }))
660 .ok()
661 .expect("Send compositor");
662 }
663 };
664
665 #[cfg(not(target_arch = "wasm32"))]
666 crate::futures::futures::executor::block_on(
667 create_compositor,
668 );
669
670 #[cfg(target_arch = "wasm32")]
671 {
672 wasm_bindgen_futures::spawn_local(create_compositor);
673 }
674
675 continue;
676 }
677
678 let window = window_manager.insert(
679 id,
680 window,
681 &program,
682 compositor
683 .as_mut()
684 .expect("Compositor must be initialized"),
685 exit_on_close_request,
686 );
687
688 let logical_size = window.state.logical_size();
689
690 let _ = user_interfaces.insert(
691 id,
692 build_user_interface(
693 &program,
694 user_interface::Cache::default(),
695 &mut window.renderer,
696 logical_size,
697 &mut debug,
698 id,
699 ),
700 );
701 let _ = ui_caches.insert(id, user_interface::Cache::default());
702
703 if make_visible {
704 window.raw.set_visible(true);
705 }
706
707 events.push((
708 id,
709 core::Event::Window(window::Event::Opened {
710 position: window.position(),
711 size: window.size(),
712 }),
713 ));
714
715 if clipboard.window_id().is_none() {
716 clipboard = Clipboard::connect(window.raw.clone());
717 }
718
719 let _ = on_open.send(id);
720 is_window_opening = false;
721 }
722 Event::EventLoopAwakened(event) => {
723 match event {
724 event::Event::NewEvents(event::StartCause::Init) => {
725 for (_id, window) in window_manager.iter_mut() {
726 window.raw.request_redraw();
727 }
728 }
729 event::Event::NewEvents(
730 event::StartCause::ResumeTimeReached { .. },
731 ) => {
732 let now = Instant::now();
733
734 for (_id, window) in window_manager.iter_mut() {
735 if let Some(redraw_at) = window.redraw_at {
736 if redraw_at <= now {
737 window.raw.request_redraw();
738 window.redraw_at = None;
739 }
740 }
741 }
742
743 if let Some(redraw_at) = window_manager.redraw_at() {
744 let _ =
745 control_sender.start_send(Control::ChangeFlow(
746 ControlFlow::WaitUntil(redraw_at),
747 ));
748 } else {
749 let _ = control_sender.start_send(
750 Control::ChangeFlow(ControlFlow::Wait),
751 );
752 }
753 }
754 event::Event::PlatformSpecific(
755 event::PlatformSpecific::MacOS(
756 event::MacOS::ReceivedUrl(url),
757 ),
758 ) => {
759 runtime.broadcast(
760 subscription::Event::PlatformSpecific(
761 subscription::PlatformSpecific::MacOS(
762 subscription::MacOS::ReceivedUrl(url),
763 ),
764 ),
765 );
766 }
767 event::Event::UserEvent(action) => {
768 run_action(
769 action,
770 &program,
771 &mut compositor,
772 &mut events,
773 &mut messages,
774 &mut clipboard,
775 &mut control_sender,
776 &mut debug,
777 &mut user_interfaces,
778 &mut window_manager,
779 &mut ui_caches,
780 &mut is_window_opening,
781 );
782 actions += 1;
783 }
784 event::Event::WindowEvent {
785 window_id: id,
786 event: event::WindowEvent::RedrawRequested,
787 ..
788 } => {
789 let Some(compositor) = &mut compositor else {
790 continue;
791 };
792
793 let Some((id, window)) =
794 window_manager.get_mut_alias(id)
795 else {
796 continue;
797 };
798
799 let physical_size = window.state.physical_size();
800
801 if physical_size.width == 0 || physical_size.height == 0
802 {
803 continue;
804 }
805
806 if window.viewport_version
807 != window.state.viewport_version()
808 {
809 let logical_size = window.state.logical_size();
810
811 debug.layout_started();
812 let ui = user_interfaces
813 .remove(&id)
814 .expect("Remove user interface");
815
816 let _ = user_interfaces.insert(
817 id,
818 ui.relayout(logical_size, &mut window.renderer),
819 );
820 debug.layout_finished();
821
822 compositor.configure_surface(
823 &mut window.surface,
824 physical_size.width,
825 physical_size.height,
826 );
827
828 window.viewport_version =
829 window.state.viewport_version();
830 }
831
832 let redraw_event = core::Event::Window(
833 window::Event::RedrawRequested(Instant::now()),
834 );
835
836 let cursor = window.state.cursor();
837
838 let ui = user_interfaces
839 .get_mut(&id)
840 .expect("Get user interface");
841
842 let (ui_state, _) = ui.update(
843 &[redraw_event.clone()],
844 cursor,
845 &mut window.renderer,
846 &mut clipboard,
847 &mut messages,
848 );
849
850 debug.draw_started();
851 let new_mouse_interaction = ui.draw(
852 &mut window.renderer,
853 window.state.theme(),
854 &renderer::Style {
855 text_color: window.state.text_color(),
856 },
857 cursor,
858 );
859 debug.draw_finished();
860
861 if new_mouse_interaction != window.mouse_interaction {
862 window.raw.set_cursor(
863 conversion::mouse_interaction(
864 new_mouse_interaction,
865 ),
866 );
867
868 window.mouse_interaction = new_mouse_interaction;
869 }
870
871 runtime.broadcast(subscription::Event::Interaction {
872 window: id,
873 event: redraw_event,
874 status: core::event::Status::Ignored,
875 });
876
877 if let user_interface::State::Updated {
878 redraw_request,
879 input_method,
880 } = ui_state
881 {
882 window.request_redraw(redraw_request);
883 window.request_input_method(input_method);
884 }
885
886 window.draw_preedit();
887
888 debug.render_started();
889 match compositor.present(
890 &mut window.renderer,
891 &mut window.surface,
892 window.state.viewport(),
893 window.state.background_color(),
894 &debug.overlay(),
895 ) {
896 Ok(()) => {
897 debug.render_finished();
898 }
899 Err(error) => match error {
900 compositor::SurfaceError::OutOfMemory => {
902 panic!("{:?}", error);
903 }
904 _ => {
905 debug.render_finished();
906
907 log::error!(
908 "Error {error:?} when \
909 presenting surface."
910 );
911
912 for (_id, window) in
914 window_manager.iter_mut()
915 {
916 window.raw.request_redraw();
917 }
918 }
919 },
920 }
921 }
922 event::Event::WindowEvent {
923 event: window_event,
924 window_id,
925 } => {
926 if !is_daemon
927 && matches!(
928 window_event,
929 winit::event::WindowEvent::Destroyed
930 )
931 && !is_window_opening
932 && window_manager.is_empty()
933 {
934 control_sender
935 .start_send(Control::Exit)
936 .expect("Send control action");
937
938 continue;
939 }
940
941 let Some((id, window)) =
942 window_manager.get_mut_alias(window_id)
943 else {
944 continue;
945 };
946
947 if matches!(
948 window_event,
949 winit::event::WindowEvent::Resized(_)
950 ) {
951 window.raw.request_redraw();
952 }
953
954 if matches!(
955 window_event,
956 winit::event::WindowEvent::CloseRequested
957 ) && window.exit_on_close_request
958 {
959 run_action(
960 Action::Window(runtime::window::Action::Close(
961 id,
962 )),
963 &program,
964 &mut compositor,
965 &mut events,
966 &mut messages,
967 &mut clipboard,
968 &mut control_sender,
969 &mut debug,
970 &mut user_interfaces,
971 &mut window_manager,
972 &mut ui_caches,
973 &mut is_window_opening,
974 );
975 } else {
976 window.state.update(
977 &window.raw,
978 &window_event,
979 &mut debug,
980 );
981
982 if let Some(event) = conversion::window_event(
983 window_event,
984 window.state.scale_factor(),
985 window.state.modifiers(),
986 ) {
987 events.push((id, event));
988 }
989 }
990 }
991 event::Event::AboutToWait => {
992 if actions > 0 {
993 proxy.free_slots(actions);
994 actions = 0;
995 }
996
997 if events.is_empty()
998 && messages.is_empty()
999 && window_manager.is_idle()
1000 {
1001 continue;
1002 }
1003
1004 debug.event_processing_started();
1005 let mut uis_stale = false;
1006
1007 for (id, window) in window_manager.iter_mut() {
1008 let mut window_events = vec![];
1009
1010 events.retain(|(window_id, event)| {
1011 if *window_id == id {
1012 window_events.push(event.clone());
1013 false
1014 } else {
1015 true
1016 }
1017 });
1018
1019 if window_events.is_empty() && messages.is_empty() {
1020 continue;
1021 }
1022
1023 let (ui_state, statuses) = user_interfaces
1024 .get_mut(&id)
1025 .expect("Get user interface")
1026 .update(
1027 &window_events,
1028 window.state.cursor(),
1029 &mut window.renderer,
1030 &mut clipboard,
1031 &mut messages,
1032 );
1033
1034 #[cfg(feature = "unconditional-rendering")]
1035 window.request_redraw(
1036 window::RedrawRequest::NextFrame,
1037 );
1038
1039 match ui_state {
1040 user_interface::State::Updated {
1041 redraw_request: _redraw_request,
1042 ..
1043 } => {
1044 #[cfg(not(
1045 feature = "unconditional-rendering"
1046 ))]
1047 window.request_redraw(_redraw_request);
1048 }
1049 user_interface::State::Outdated => {
1050 uis_stale = true;
1051 }
1052 }
1053
1054 for (event, status) in window_events
1055 .into_iter()
1056 .zip(statuses.into_iter())
1057 {
1058 runtime.broadcast(
1059 subscription::Event::Interaction {
1060 window: id,
1061 event,
1062 status,
1063 },
1064 );
1065 }
1066 }
1067
1068 for (id, event) in events.drain(..) {
1069 runtime.broadcast(
1070 subscription::Event::Interaction {
1071 window: id,
1072 event,
1073 status: core::event::Status::Ignored,
1074 },
1075 );
1076 }
1077
1078 debug.event_processing_finished();
1079
1080 if !messages.is_empty() || uis_stale {
1081 let cached_interfaces: FxHashMap<
1082 window::Id,
1083 user_interface::Cache,
1084 > = ManuallyDrop::into_inner(user_interfaces)
1085 .drain()
1086 .map(|(id, ui)| (id, ui.into_cache()))
1087 .collect();
1088
1089 update(
1090 &mut program,
1091 &mut runtime,
1092 &mut debug,
1093 &mut messages,
1094 );
1095
1096 for (id, window) in window_manager.iter_mut() {
1097 window.state.synchronize(
1098 &program,
1099 id,
1100 &window.raw,
1101 );
1102
1103 window.raw.request_redraw();
1104 }
1105
1106 user_interfaces =
1107 ManuallyDrop::new(build_user_interfaces(
1108 &program,
1109 &mut debug,
1110 &mut window_manager,
1111 cached_interfaces,
1112 ));
1113 }
1114
1115 if let Some(redraw_at) = window_manager.redraw_at() {
1116 let _ =
1117 control_sender.start_send(Control::ChangeFlow(
1118 ControlFlow::WaitUntil(redraw_at),
1119 ));
1120 } else {
1121 let _ = control_sender.start_send(
1122 Control::ChangeFlow(ControlFlow::Wait),
1123 );
1124 }
1125 }
1126 _ => {}
1127 }
1128 }
1129 }
1130 }
1131
1132 let _ = ManuallyDrop::into_inner(user_interfaces);
1133}
1134
1135fn build_user_interface<'a, P: Program>(
1137 program: &'a P,
1138 cache: user_interface::Cache,
1139 renderer: &mut P::Renderer,
1140 size: Size,
1141 debug: &mut Debug,
1142 id: window::Id,
1143) -> UserInterface<'a, P::Message, P::Theme, P::Renderer>
1144where
1145 P::Theme: theme::Base,
1146{
1147 debug.view_started();
1148 let view = program.view(id);
1149 debug.view_finished();
1150
1151 debug.layout_started();
1152 let user_interface = UserInterface::build(view, size, cache, renderer);
1153 debug.layout_finished();
1154
1155 user_interface
1156}
1157
1158fn update<P: Program, E: Executor>(
1159 program: &mut P,
1160 runtime: &mut Runtime<E, Proxy<P::Message>, Action<P::Message>>,
1161 debug: &mut Debug,
1162 messages: &mut Vec<P::Message>,
1163) where
1164 P::Theme: theme::Base,
1165{
1166 for message in messages.drain(..) {
1167 debug.log_message(&message);
1168 debug.update_started();
1169
1170 let task = runtime.enter(|| program.update(message));
1171 debug.update_finished();
1172
1173 if let Some(stream) = runtime::task::into_stream(task) {
1174 runtime.run(stream);
1175 }
1176 }
1177
1178 let subscription = runtime.enter(|| program.subscription());
1179 runtime.track(subscription::into_recipes(subscription.map(Action::Output)));
1180}
1181
1182fn run_action<P, C>(
1183 action: Action<P::Message>,
1184 program: &P,
1185 compositor: &mut Option<C>,
1186 events: &mut Vec<(window::Id, core::Event)>,
1187 messages: &mut Vec<P::Message>,
1188 clipboard: &mut Clipboard,
1189 control_sender: &mut mpsc::UnboundedSender<Control>,
1190 debug: &mut Debug,
1191 interfaces: &mut FxHashMap<
1192 window::Id,
1193 UserInterface<'_, P::Message, P::Theme, P::Renderer>,
1194 >,
1195 window_manager: &mut WindowManager<P, C>,
1196 ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,
1197 is_window_opening: &mut bool,
1198) where
1199 P: Program,
1200 C: Compositor<Renderer = P::Renderer> + 'static,
1201 P::Theme: theme::Base,
1202{
1203 use crate::runtime::clipboard;
1204 use crate::runtime::system;
1205 use crate::runtime::window;
1206
1207 match action {
1208 Action::Output(message) => {
1209 messages.push(message);
1210 }
1211 Action::Clipboard(action) => match action {
1212 clipboard::Action::Read { target, channel } => {
1213 let _ = channel.send(clipboard.read(target));
1214 }
1215 clipboard::Action::Write { target, contents } => {
1216 clipboard.write(target, contents);
1217 }
1218 },
1219 Action::Window(action) => match action {
1220 window::Action::Open(id, settings, channel) => {
1221 let monitor = window_manager.last_monitor();
1222
1223 control_sender
1224 .start_send(Control::CreateWindow {
1225 id,
1226 settings,
1227 title: program.title(id),
1228 monitor,
1229 on_open: channel,
1230 })
1231 .expect("Send control action");
1232
1233 *is_window_opening = true;
1234 }
1235 window::Action::Close(id) => {
1236 let _ = ui_caches.remove(&id);
1237 let _ = interfaces.remove(&id);
1238
1239 if let Some(window) = window_manager.remove(id) {
1240 if clipboard.window_id() == Some(window.raw.id()) {
1241 *clipboard = window_manager
1242 .first()
1243 .map(|window| window.raw.clone())
1244 .map(Clipboard::connect)
1245 .unwrap_or_else(Clipboard::unconnected);
1246 }
1247
1248 events.push((
1249 id,
1250 core::Event::Window(core::window::Event::Closed),
1251 ));
1252 }
1253
1254 if window_manager.is_empty() {
1255 *compositor = None;
1256 }
1257 }
1258 window::Action::GetOldest(channel) => {
1259 let id =
1260 window_manager.iter_mut().next().map(|(id, _window)| id);
1261
1262 let _ = channel.send(id);
1263 }
1264 window::Action::GetLatest(channel) => {
1265 let id =
1266 window_manager.iter_mut().last().map(|(id, _window)| id);
1267
1268 let _ = channel.send(id);
1269 }
1270 window::Action::Drag(id) => {
1271 if let Some(window) = window_manager.get_mut(id) {
1272 let _ = window.raw.drag_window();
1273 }
1274 }
1275 window::Action::DragResize(id, direction) => {
1276 if let Some(window) = window_manager.get_mut(id) {
1277 let _ = window.raw.drag_resize_window(
1278 conversion::resize_direction(direction),
1279 );
1280 }
1281 }
1282 window::Action::Resize(id, size) => {
1283 if let Some(window) = window_manager.get_mut(id) {
1284 let _ = window.raw.request_inner_size(
1285 winit::dpi::LogicalSize {
1286 width: size.width,
1287 height: size.height,
1288 },
1289 );
1290 }
1291 }
1292 window::Action::SetMinSize(id, size) => {
1293 if let Some(window) = window_manager.get_mut(id) {
1294 window.raw.set_min_inner_size(size.map(|size| {
1295 winit::dpi::LogicalSize {
1296 width: size.width,
1297 height: size.height,
1298 }
1299 }));
1300 }
1301 }
1302 window::Action::SetMaxSize(id, size) => {
1303 if let Some(window) = window_manager.get_mut(id) {
1304 window.raw.set_max_inner_size(size.map(|size| {
1305 winit::dpi::LogicalSize {
1306 width: size.width,
1307 height: size.height,
1308 }
1309 }));
1310 }
1311 }
1312 window::Action::SetResizeIncrements(id, increments) => {
1313 if let Some(window) = window_manager.get_mut(id) {
1314 window.raw.set_resize_increments(increments.map(|size| {
1315 winit::dpi::LogicalSize {
1316 width: size.width,
1317 height: size.height,
1318 }
1319 }));
1320 }
1321 }
1322 window::Action::SetResizable(id, resizable) => {
1323 if let Some(window) = window_manager.get_mut(id) {
1324 window.raw.set_resizable(resizable);
1325 }
1326 }
1327 window::Action::GetSize(id, channel) => {
1328 if let Some(window) = window_manager.get_mut(id) {
1329 let size = window
1330 .raw
1331 .inner_size()
1332 .to_logical(window.raw.scale_factor());
1333
1334 let _ = channel.send(Size::new(size.width, size.height));
1335 }
1336 }
1337 window::Action::GetMaximized(id, channel) => {
1338 if let Some(window) = window_manager.get_mut(id) {
1339 let _ = channel.send(window.raw.is_maximized());
1340 }
1341 }
1342 window::Action::Maximize(id, maximized) => {
1343 if let Some(window) = window_manager.get_mut(id) {
1344 window.raw.set_maximized(maximized);
1345 }
1346 }
1347 window::Action::GetMinimized(id, channel) => {
1348 if let Some(window) = window_manager.get_mut(id) {
1349 let _ = channel.send(window.raw.is_minimized());
1350 }
1351 }
1352 window::Action::Minimize(id, minimized) => {
1353 if let Some(window) = window_manager.get_mut(id) {
1354 window.raw.set_minimized(minimized);
1355 }
1356 }
1357 window::Action::GetPosition(id, channel) => {
1358 if let Some(window) = window_manager.get(id) {
1359 let position = window
1360 .raw
1361 .outer_position()
1362 .map(|position| {
1363 let position = position
1364 .to_logical::<f32>(window.raw.scale_factor());
1365
1366 Point::new(position.x, position.y)
1367 })
1368 .ok();
1369
1370 let _ = channel.send(position);
1371 }
1372 }
1373 window::Action::GetScaleFactor(id, channel) => {
1374 if let Some(window) = window_manager.get_mut(id) {
1375 let scale_factor = window.raw.scale_factor();
1376
1377 let _ = channel.send(scale_factor as f32);
1378 }
1379 }
1380 window::Action::Move(id, position) => {
1381 if let Some(window) = window_manager.get_mut(id) {
1382 window.raw.set_outer_position(
1383 winit::dpi::LogicalPosition {
1384 x: position.x,
1385 y: position.y,
1386 },
1387 );
1388 }
1389 }
1390 window::Action::SetMode(id, mode) => {
1391 if let Some(window) = window_manager.get_mut(id) {
1392 window.raw.set_visible(conversion::visible(mode));
1393 window.raw.set_fullscreen(conversion::fullscreen(
1394 window.raw.current_monitor(),
1395 mode,
1396 ));
1397 }
1398 }
1399 window::Action::SetIcon(id, icon) => {
1400 if let Some(window) = window_manager.get_mut(id) {
1401 window.raw.set_window_icon(conversion::icon(icon));
1402 }
1403 }
1404 window::Action::GetMode(id, channel) => {
1405 if let Some(window) = window_manager.get_mut(id) {
1406 let mode = if window.raw.is_visible().unwrap_or(true) {
1407 conversion::mode(window.raw.fullscreen())
1408 } else {
1409 core::window::Mode::Hidden
1410 };
1411
1412 let _ = channel.send(mode);
1413 }
1414 }
1415 window::Action::ToggleMaximize(id) => {
1416 if let Some(window) = window_manager.get_mut(id) {
1417 window.raw.set_maximized(!window.raw.is_maximized());
1418 }
1419 }
1420 window::Action::ToggleDecorations(id) => {
1421 if let Some(window) = window_manager.get_mut(id) {
1422 window.raw.set_decorations(!window.raw.is_decorated());
1423 }
1424 }
1425 window::Action::RequestUserAttention(id, attention_type) => {
1426 if let Some(window) = window_manager.get_mut(id) {
1427 window.raw.request_user_attention(
1428 attention_type.map(conversion::user_attention),
1429 );
1430 }
1431 }
1432 window::Action::GainFocus(id) => {
1433 if let Some(window) = window_manager.get_mut(id) {
1434 window.raw.focus_window();
1435 }
1436 }
1437 window::Action::SetLevel(id, level) => {
1438 if let Some(window) = window_manager.get_mut(id) {
1439 window
1440 .raw
1441 .set_window_level(conversion::window_level(level));
1442 }
1443 }
1444 window::Action::ShowSystemMenu(id) => {
1445 if let Some(window) = window_manager.get_mut(id) {
1446 if let mouse::Cursor::Available(point) =
1447 window.state.cursor()
1448 {
1449 window.raw.show_window_menu(
1450 winit::dpi::LogicalPosition {
1451 x: point.x,
1452 y: point.y,
1453 },
1454 );
1455 }
1456 }
1457 }
1458 window::Action::GetRawId(id, channel) => {
1459 if let Some(window) = window_manager.get_mut(id) {
1460 let _ = channel.send(window.raw.id().into());
1461 }
1462 }
1463 window::Action::RunWithHandle(id, f) => {
1464 use window::raw_window_handle::HasWindowHandle;
1465
1466 if let Some(handle) = window_manager
1467 .get_mut(id)
1468 .and_then(|window| window.raw.window_handle().ok())
1469 {
1470 f(handle);
1471 }
1472 }
1473 window::Action::Screenshot(id, channel) => {
1474 if let Some(window) = window_manager.get_mut(id) {
1475 if let Some(compositor) = compositor {
1476 let bytes = compositor.screenshot(
1477 &mut window.renderer,
1478 window.state.viewport(),
1479 window.state.background_color(),
1480 &debug.overlay(),
1481 );
1482
1483 let _ = channel.send(core::window::Screenshot::new(
1484 bytes,
1485 window.state.physical_size(),
1486 window.state.viewport().scale_factor(),
1487 ));
1488 }
1489 }
1490 }
1491 window::Action::EnableMousePassthrough(id) => {
1492 if let Some(window) = window_manager.get_mut(id) {
1493 let _ = window.raw.set_cursor_hittest(false);
1494 }
1495 }
1496 window::Action::DisableMousePassthrough(id) => {
1497 if let Some(window) = window_manager.get_mut(id) {
1498 let _ = window.raw.set_cursor_hittest(true);
1499 }
1500 }
1501 },
1502 Action::System(action) => match action {
1503 system::Action::QueryInformation(_channel) => {
1504 #[cfg(feature = "system")]
1505 {
1506 if let Some(compositor) = compositor {
1507 let graphics_info = compositor.fetch_information();
1508
1509 let _ = std::thread::spawn(move || {
1510 let information =
1511 crate::system::information(graphics_info);
1512
1513 let _ = _channel.send(information);
1514 });
1515 }
1516 }
1517 }
1518 },
1519 Action::Widget(operation) => {
1520 let mut current_operation = Some(operation);
1521
1522 while let Some(mut operation) = current_operation.take() {
1523 for (id, ui) in interfaces.iter_mut() {
1524 if let Some(window) = window_manager.get_mut(*id) {
1525 ui.operate(&window.renderer, operation.as_mut());
1526 }
1527 }
1528
1529 match operation.finish() {
1530 operation::Outcome::None => {}
1531 operation::Outcome::Some(()) => {}
1532 operation::Outcome::Chain(next) => {
1533 current_operation = Some(next);
1534 }
1535 }
1536 }
1537 }
1538 Action::LoadFont { bytes, channel } => {
1539 if let Some(compositor) = compositor {
1540 compositor.load_font(bytes.clone());
1542
1543 let _ = channel.send(Ok(()));
1544 }
1545 }
1546 Action::Exit => {
1547 control_sender
1548 .start_send(Control::Exit)
1549 .expect("Send control action");
1550 }
1551 }
1552}
1553
1554pub fn build_user_interfaces<'a, P: Program, C>(
1556 program: &'a P,
1557 debug: &mut Debug,
1558 window_manager: &mut WindowManager<P, C>,
1559 mut cached_user_interfaces: FxHashMap<window::Id, user_interface::Cache>,
1560) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>>
1561where
1562 C: Compositor<Renderer = P::Renderer>,
1563 P::Theme: theme::Base,
1564{
1565 cached_user_interfaces
1566 .drain()
1567 .filter_map(|(id, cache)| {
1568 let window = window_manager.get_mut(id)?;
1569
1570 Some((
1571 id,
1572 build_user_interface(
1573 program,
1574 cache,
1575 &mut window.renderer,
1576 window.state.logical_size(),
1577 debug,
1578 id,
1579 ),
1580 ))
1581 })
1582 .collect()
1583}
1584
1585pub fn user_force_quit(
1588 event: &winit::event::WindowEvent,
1589 _modifiers: winit::keyboard::ModifiersState,
1590) -> bool {
1591 match event {
1592 #[cfg(target_os = "macos")]
1593 winit::event::WindowEvent::KeyboardInput {
1594 event:
1595 winit::event::KeyEvent {
1596 logical_key: winit::keyboard::Key::Character(c),
1597 state: winit::event::ElementState::Pressed,
1598 ..
1599 },
1600 ..
1601 } if c == "q" && _modifiers.super_key() => true,
1602 _ => false,
1603 }
1604}