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