1mod action;
10pub use action::Action;
11use cosmic_config::CosmicConfigEntry;
12pub mod context_drawer;
13pub use context_drawer::{ContextDrawer, context_drawer};
14use iced::application::BootFn;
15pub mod cosmic;
16pub mod settings;
17
18pub type Task<M> = iced::Task<crate::Action<M>>;
19
20pub use crate::Core;
21use crate::prelude::*;
22use crate::theme::THEME;
23use crate::widget::{container, id_container, menu, nav_bar, popover, space};
24use apply::Apply;
25use iced::{Length, Subscription};
26use iced::{theme, window};
27pub use settings::Settings;
28use std::borrow::Cow;
29use std::{cell::RefCell, rc::Rc};
30
31#[cold]
32pub(crate) fn iced_settings<App: Application>(
33 settings: Settings,
34 flags: App::Flags,
35) -> (iced::Settings, (Core, App::Flags), iced::window::Settings) {
36 preload_fonts();
37
38 let mut core = Core::default();
39 core.debug = settings.debug;
40 core.icon_theme_override = settings.default_icon_theme.is_some();
41 core.set_scale_factor(settings.scale_factor);
42 core.set_window_width(settings.size.width);
43 core.set_window_height(settings.size.height);
44
45 if let Some(icon_theme) = settings.default_icon_theme {
46 crate::icon_theme::set_default(icon_theme);
47 } else {
48 crate::icon_theme::set_default(crate::config::icon_theme());
49 }
50
51 THEME.lock().unwrap().set_theme(settings.theme.theme_type);
52
53 if settings.no_main_window {
54 core.main_window = Some(iced::window::Id::NONE);
55 }
56
57 let mut iced = iced::Settings::default();
58
59 iced.antialiasing = settings.antialiasing;
60 iced.default_font = settings.default_font;
61 iced.default_text_size = iced::Pixels(settings.default_text_size);
62 let exit_on_close = settings.exit_on_close;
63 iced.is_daemon = false;
64 iced.exit_on_close_request = settings.is_daemon;
65 let mut window_settings = iced::window::Settings::default();
66 window_settings.exit_on_close_request = exit_on_close;
67 iced.id = Some(App::APP_ID.to_owned());
68 #[cfg(target_os = "linux")]
69 {
70 window_settings.platform_specific.application_id = App::APP_ID.to_string();
71 }
72 core.exit_on_main_window_closed = exit_on_close;
73
74 if let Some(border_size) = settings.resizable {
75 window_settings.resize_border = border_size as u32;
76 window_settings.resizable = true;
77 }
78 window_settings.decorations = !settings.client_decorations;
79 window_settings.size = settings.size;
80 let min_size = settings.size_limits.min();
81 if min_size != iced::Size::ZERO {
82 window_settings.min_size = Some(min_size);
83 }
84 let max_size = settings.size_limits.max();
85 if max_size != iced::Size::INFINITE {
86 window_settings.max_size = Some(max_size);
87 }
88
89 window_settings.transparent = settings.transparent;
90 (iced, (core, flags), window_settings)
91}
92
93pub(crate) struct BootDataInner<A: crate::app::Application> {
94 pub flags: A::Flags,
95 pub core: Core,
96 pub settings: window::Settings,
97}
98
99pub(crate) struct BootData<A: crate::app::Application>(pub Rc<RefCell<Option<BootDataInner<A>>>>);
100
101impl<A: crate::app::Application> BootFn<cosmic::Cosmic<A>, crate::Action<A::Message>>
102 for BootData<A>
103{
104 fn boot(&self) -> (cosmic::Cosmic<A>, iced::Task<crate::Action<A::Message>>) {
105 let mut data = self.0.borrow_mut();
106 let mut data = data.take().unwrap();
107 let mut tasks = Vec::new();
108 #[cfg(feature = "multi-window")]
109 if data.core.main_window_id().is_some() {
110 let window_task = iced_runtime::task::oneshot(|channel| {
111 iced_runtime::Action::Window(iced_runtime::window::Action::Open(
112 window::Id::RESERVED,
113 data.settings,
114 channel,
115 ))
116 });
117 data.core.set_main_window_id(Some(window::Id::RESERVED));
118 tasks.push(window_task.discard());
119 }
120 let (a, t) = cosmic::Cosmic::<A>::init((data.core, data.flags));
121 tasks.push(t);
122 (a, Task::batch(tasks))
123 }
124}
125pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result {
131 #[cfg(all(target_env = "gnu", not(target_os = "windows")))]
132 if let Some(threshold) = settings.default_mmap_threshold {
133 crate::malloc::limit_mmap_threshold(threshold);
134 }
135
136 let default_font = settings.default_font;
137 let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
138 #[cfg(not(feature = "multi-window"))]
139 {
140 core.main_window = Some(iced::window::Id::RESERVED);
141
142 iced::application(
143 BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
144 flags,
145 core,
146 settings: window_settings.clone(),
147 })))),
148 cosmic::Cosmic::update,
149 cosmic::Cosmic::view,
150 )
151 .subscription(cosmic::Cosmic::subscription)
152 .title(cosmic::Cosmic::title)
153 .style(cosmic::Cosmic::style)
154 .theme(cosmic::Cosmic::theme)
155 .window_size((500.0, 800.0))
156 .settings(settings)
157 .window(window_settings)
158 .run()
159 }
160 #[cfg(feature = "multi-window")]
161 {
162 let no_main_window = core.main_window.is_none();
163 if no_main_window {
164 core.main_window = Some(iced_core::window::Id::RESERVED);
166 }
167 let app = iced::daemon(
168 BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
169 flags,
170 core,
171 settings: window_settings,
172 })))),
173 cosmic::Cosmic::update,
174 cosmic::Cosmic::view,
175 );
176
177 app.subscription(cosmic::Cosmic::subscription)
178 .title(cosmic::Cosmic::title)
179 .style(cosmic::Cosmic::style)
180 .theme(cosmic::Cosmic::theme)
181 .settings(settings)
182 .run()
183 }
184}
185
186#[cfg(feature = "single-instance")]
187pub fn run_single_instance<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result
193where
194 App::Flags: CosmicFlags,
195 App::Message: Clone + std::fmt::Debug + Send + 'static,
196{
197 use std::collections::HashMap;
198
199 let activation_token = std::env::var("XDG_ACTIVATION_TOKEN").ok();
200
201 let override_single = std::env::var("COSMIC_SINGLE_INSTANCE")
202 .map(|v| &v.to_lowercase() == "false" || &v == "0")
203 .unwrap_or_default();
204 if override_single {
205 return run::<App>(settings, flags);
206 }
207
208 let path: String = format!("/{}", App::APP_ID.replace('.', "/"));
209
210 let Ok(conn) = zbus::blocking::Connection::session() else {
211 tracing::warn!("Failed to connect to dbus");
212 return run::<App>(settings, flags);
213 };
214
215 if crate::dbus_activation::DbusActivationInterfaceProxyBlocking::builder(&conn)
216 .destination(App::APP_ID)
217 .ok()
218 .and_then(|b| b.path(path).ok())
219 .and_then(|b| b.destination(App::APP_ID).ok())
220 .and_then(|b| b.build().ok())
221 .is_some_and(|mut p| {
222 let res = {
223 let mut platform_data = HashMap::new();
224 if let Some(activation_token) = activation_token {
225 platform_data.insert("activation-token", activation_token.into());
226 }
227 if let Ok(startup_id) = std::env::var("DESKTOP_STARTUP_ID") {
228 platform_data.insert("desktop-startup-id", startup_id.into());
229 }
230 if let Some(action) = flags.action() {
231 let action = action.to_string();
232 p.activate_action(&action, flags.args(), platform_data)
233 } else {
234 p.activate(platform_data)
235 }
236 };
237 match res {
238 Ok(()) => {
239 tracing::info!("Successfully activated another instance");
240 true
241 }
242 Err(err) => {
243 tracing::warn!(?err, "Failed to activate another instance");
244 false
245 }
246 }
247 })
248 {
249 tracing::info!("Another instance is running");
250 Ok(())
251 } else {
252 let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
253 core.single_instance = true;
254
255 #[cfg(not(feature = "multi-window"))]
256 {
257 iced::application(
258 BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
259 flags,
260 core,
261 settings: window_settings.clone(),
262 })))),
263 cosmic::Cosmic::update,
264 cosmic::Cosmic::view,
265 )
266 .subscription(cosmic::Cosmic::subscription)
267 .style(cosmic::Cosmic::style)
268 .theme(cosmic::Cosmic::theme)
269 .window_size((500.0, 800.0))
270 .settings(settings)
271 .window(window_settings)
272 .run()
273 }
274 #[cfg(feature = "multi-window")]
275 {
276 let no_main_window = core.main_window.is_none();
277 if no_main_window {
278 core.main_window = Some(iced_core::window::Id::RESERVED);
280 }
281 let mut app = iced::daemon(
282 BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
283 flags,
284 core,
285 settings: window_settings,
286 })))),
287 cosmic::Cosmic::update,
288 cosmic::Cosmic::view,
289 );
290
291 app.subscription(cosmic::Cosmic::subscription)
292 .style(cosmic::Cosmic::style)
293 .title(cosmic::Cosmic::title)
294 .theme(cosmic::Cosmic::theme)
295 .settings(settings)
296 .run()
297 }
298 }
299}
300
301pub trait CosmicFlags {
302 type SubCommand: ToString + std::fmt::Debug + Clone + Send + 'static;
303 type Args: Into<Vec<String>> + std::fmt::Debug + Clone + Send + 'static;
304 #[must_use]
305 fn action(&self) -> Option<&Self::SubCommand> {
306 None
307 }
308
309 #[must_use]
310 fn args(&self) -> Vec<&str> {
311 Vec::new()
312 }
313}
314
315#[allow(unused_variables)]
317pub trait Application
318where
319 Self: Sized + 'static,
320{
321 type Executor: iced_futures::Executor;
323
324 type Flags;
326
327 type Message: Clone + std::fmt::Debug + Send + 'static;
329
330 const APP_ID: &'static str;
334
335 fn core(&self) -> &Core;
337
338 fn core_mut(&mut self) -> &mut Core;
340
341 fn init(core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>);
343
344 fn context_drawer(&self) -> Option<ContextDrawer<'_, Self::Message>> {
347 None
348 }
349
350 fn dialog(&self) -> Option<Element<'_, Self::Message>> {
352 None
353 }
354
355 fn footer(&self) -> Option<Element<'_, Self::Message>> {
357 None
358 }
359
360 fn header_start(&self) -> Vec<Element<'_, Self::Message>> {
362 Vec::new()
363 }
364
365 fn header_center(&self) -> Vec<Element<'_, Self::Message>> {
367 Vec::new()
368 }
369
370 fn header_end(&self) -> Vec<Element<'_, Self::Message>> {
372 Vec::new()
373 }
374
375 fn nav_bar(&self) -> Option<Element<'_, crate::Action<Self::Message>>> {
377 if !self.core().nav_bar_active() {
378 return None;
379 }
380
381 let nav_model = self.nav_model()?;
382
383 let mut nav =
384 crate::widget::nav_bar(nav_model, |id| crate::Action::Cosmic(Action::NavBar(id)))
385 .on_context(|id| crate::Action::Cosmic(Action::NavBarContext(id)))
386 .context_menu(self.nav_context_menu(self.core().nav_bar_context()))
387 .into_container()
388 .width(iced::Length::Shrink)
389 .height(iced::Length::Fill);
390
391 if !self.core().is_condensed() {
392 nav = nav.max_width(280);
393 }
394
395 Some(Element::from(nav))
396 }
397
398 fn nav_context_menu(
400 &self,
401 id: nav_bar::Id,
402 ) -> Option<Vec<menu::Tree<crate::Action<Self::Message>>>> {
403 None
404 }
405
406 fn nav_model(&self) -> Option<&nav_bar::Model> {
408 None
409 }
410
411 fn on_app_exit(&mut self) -> Option<Self::Message> {
413 None
414 }
415
416 fn on_close_requested(&self, id: window::Id) -> Option<Self::Message> {
418 None
419 }
420
421 fn on_context_drawer(&mut self) -> Task<Self::Message> {
423 Task::none()
424 }
425
426 fn on_escape(&mut self) -> Task<Self::Message> {
428 Task::none()
429 }
430
431 fn on_nav_select(&mut self, id: nav_bar::Id) -> Task<Self::Message> {
433 Task::none()
434 }
435
436 fn on_nav_context(&mut self, id: nav_bar::Id) -> Task<Self::Message> {
438 Task::none()
439 }
440
441 fn on_search(&mut self) -> Task<Self::Message> {
443 Task::none()
444 }
445
446 fn on_window_resize(&mut self, id: window::Id, width: f32, height: f32) {}
448
449 fn subscription(&self) -> Subscription<Self::Message> {
451 Subscription::none()
452 }
453
454 fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
456 Task::none()
457 }
458
459 fn system_theme_update(
461 &mut self,
462 keys: &[&'static str],
463 new_theme: &cosmic_theme::Theme,
464 ) -> Task<Self::Message> {
465 Task::none()
466 }
467
468 fn system_theme_mode_update(
470 &mut self,
471 keys: &[&'static str],
472 new_theme: &cosmic_theme::ThemeMode,
473 ) -> Task<Self::Message> {
474 Task::none()
475 }
476
477 fn view(&self) -> Element<'_, Self::Message>;
479
480 fn view_window(&self, id: window::Id) -> Element<'_, Self::Message> {
482 panic!("no view for window {id:?}");
483 }
484
485 fn style(&self) -> Option<theme::Style> {
487 None
488 }
489
490 #[cfg(feature = "single-instance")]
492 fn dbus_activation(&mut self, msg: crate::dbus_activation::Message) -> Task<Self::Message> {
493 Task::none()
494 }
495
496 #[cfg(feature = "single-instance")]
500 fn dbus_connection(&mut self, conn: zbus::Connection) -> Task<Self::Message> {
501 Task::none()
502 }
503}
504
505pub trait ApplicationExt: Application {
507 fn drag(&mut self) -> Task<Self::Message>;
509
510 fn maximize(&mut self) -> Task<Self::Message>;
512
513 fn minimize(&mut self) -> Task<Self::Message>;
515 #[cfg(not(feature = "multi-window"))]
518 fn title(&self) -> &str;
519
520 #[cfg(feature = "multi-window")]
521 fn title(&self, id: window::Id) -> &str;
523
524 fn set_show_context(&mut self, show: bool) {
526 self.core_mut().set_show_context(show);
527 }
528
529 fn set_header_title(&mut self, title: String) {
531 self.core_mut().set_header_title(title);
532 }
533
534 #[cfg(not(feature = "multi-window"))]
535 fn set_window_title(&mut self, title: String) -> Task<Self::Message>;
537
538 #[cfg(feature = "multi-window")]
539 fn set_window_title(&mut self, title: String, id: window::Id) -> Task<Self::Message>;
541
542 fn view_main(&self) -> Element<'_, crate::Action<Self::Message>>;
544
545 fn watch_config<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone + PartialEq>(
546 &self,
547 id: &'static str,
548 ) -> iced::Subscription<cosmic_config::Update<T>> {
549 self.core().watch_config(id)
550 }
551
552 fn watch_state<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone + PartialEq>(
553 &self,
554 id: &'static str,
555 ) -> iced::Subscription<cosmic_config::Update<T>> {
556 self.core().watch_state(id)
557 }
558}
559
560impl<App: Application> ApplicationExt for App {
561 fn drag(&mut self) -> Task<Self::Message> {
562 self.core().drag(None)
563 }
564
565 fn maximize(&mut self) -> Task<Self::Message> {
566 self.core().maximize(None, true)
567 }
568
569 fn minimize(&mut self) -> Task<Self::Message> {
570 self.core().minimize(None)
571 }
572
573 #[cfg(feature = "multi-window")]
574 fn title(&self, id: window::Id) -> &str {
575 self.core().title.get(&id).map_or("", |s| s.as_str())
576 }
577
578 #[cfg(not(feature = "multi-window"))]
579 fn title(&self) -> &str {
580 self.core()
581 .main_window_id()
582 .and_then(|id| self.core().title.get(&id).map(std::string::String::as_str))
583 .unwrap_or("")
584 }
585
586 #[cfg(feature = "multi-window")]
587 fn set_window_title(&mut self, title: String, id: window::Id) -> Task<Self::Message> {
588 self.core_mut().title.insert(id, title.clone());
589 self.core().set_title(Some(id), title)
590 }
591
592 #[cfg(not(feature = "multi-window"))]
593 fn set_window_title(&mut self, title: String) -> Task<Self::Message> {
594 let Some(id) = self.core().main_window_id() else {
595 return Task::none();
596 };
597
598 self.core_mut().title.insert(id, title.clone());
599 Task::none()
600 }
601
602 #[allow(clippy::too_many_lines)]
603 fn view_main(&self) -> Element<'_, crate::Action<Self::Message>> {
605 let core = self.core();
606 let is_condensed = core.is_condensed();
607 let sharp_corners = core.window.sharp_corners;
608 let maximized = core.window.is_maximized;
609 let content_container = core.window.content_container;
610 let show_context = core.window.show_context;
611 let nav_bar_active = core.nav_bar_active();
612 let focused = core
613 .focus_chain()
614 .iter()
615 .any(|i| Some(*i) == self.core().main_window_id());
616
617 let border_padding = if maximized { 8 } else { 7 };
618
619 let main_content_padding = if !content_container {
620 [0, 0, 0, 0]
621 } else {
622 let right_padding = if show_context { 0 } else { border_padding };
623 let left_padding = if nav_bar_active { 0 } else { border_padding };
624
625 [0, right_padding, 0, left_padding]
626 };
627
628 let content_row = crate::widget::row::with_children({
629 let mut widgets = Vec::with_capacity(3);
630
631 let has_nav = if let Some(nav) = self
633 .nav_bar()
634 .map(|nav| id_container(nav, iced_core::id::Id::new("COSMIC_nav_bar")))
635 {
636 widgets.push(
637 container(nav)
638 .padding([
639 0,
640 if is_condensed { border_padding } else { 8 },
641 border_padding,
642 border_padding,
643 ])
644 .into(),
645 );
646 true
647 } else {
648 false
649 };
650
651 if self.nav_model().is_none() || core.show_content() {
652 let main_content = self.view();
653
654 let context_width = core.context_width(has_nav);
656 if core.window.context_is_overlay && show_context {
657 if let Some(context) = self.context_drawer() {
658 widgets.push(
659 crate::widget::context_drawer(
660 context.title,
661 context.actions,
662 context.header,
663 context.footer,
664 context.on_close,
665 main_content,
666 context.content,
667 context_width,
668 )
669 .apply(|drawer| {
670 Element::from(id_container(
671 drawer,
672 iced_core::id::Id::new("COSMIC_context_drawer"),
673 ))
674 })
675 .apply(container)
676 .padding([0, if content_container { border_padding } else { 0 }, 0, 0])
677 .apply(Element::from)
678 .map(crate::Action::App),
679 );
680 } else {
681 widgets.push(
682 container(main_content.map(crate::Action::App))
683 .padding(main_content_padding)
684 .into(),
685 );
686 }
687 } else {
688 widgets.push(
690 container(main_content.map(crate::Action::App))
691 .padding(main_content_padding)
692 .into(),
693 );
694 if let Some(context) = self.context_drawer() {
695 widgets.push(
696 crate::widget::ContextDrawer::new_inner(
697 context.title,
698 context.actions,
699 context.header,
700 context.footer,
701 context.content,
702 context.on_close,
703 context_width,
704 )
705 .apply(Element::from)
706 .map(crate::Action::App)
707 .apply(container)
708 .width(context_width)
709 .apply(|drawer| {
710 Element::from(id_container(
711 drawer,
712 iced_core::id::Id::new("COSMIC_context_drawer"),
713 ))
714 })
715 .apply(container)
716 .padding(if content_container {
717 [0, border_padding, border_padding, border_padding]
718 } else {
719 [0, 0, 0, 0]
720 })
721 .into(),
722 );
723 } else {
724 widgets.push(space::horizontal().width(Length::Shrink).into());
726 }
727 }
728 }
729
730 widgets
731 });
732
733 let content_col = crate::widget::column::with_capacity(2)
734 .push(content_row)
735 .push_maybe(self.footer().map(|footer| {
736 container(footer.map(crate::Action::App)).padding([
737 0,
738 border_padding,
739 border_padding,
740 border_padding,
741 ])
742 }));
743 let content: Element<_> = if content_container {
744 content_col
745 .width(iced::Length::Fill)
746 .height(iced::Length::Fill)
747 .apply(|w| id_container(w, iced_core::id::Id::new("COSMIC_content_container")))
748 .into()
749 } else {
750 content_col.into()
751 };
752
753 let window_corner_radius = if sharp_corners {
755 crate::theme::active().cosmic().radius_0()
756 } else {
757 crate::theme::active()
758 .cosmic()
759 .radius_s()
760 .map(|x| if x < 4.0 { x } else { x + 4.0 })
761 };
762
763 let view_column = crate::widget::column::with_capacity(2)
764 .push_maybe(if core.window.show_headerbar {
765 Some({
766 let mut header = crate::widget::header_bar()
767 .focused(focused)
768 .maximized(maximized)
769 .sharp_corners(sharp_corners)
770 .transparent(content_container)
771 .title(&core.window.header_title)
772 .on_drag(crate::Action::Cosmic(Action::Drag))
773 .on_right_click(crate::Action::Cosmic(Action::ShowWindowMenu))
774 .on_double_click(crate::Action::Cosmic(Action::Maximize));
775
776 if self.nav_model().is_some() {
777 let toggle = crate::widget::nav_bar_toggle()
778 .active(core.nav_bar_active())
779 .selected(focused)
780 .on_toggle(if is_condensed {
781 crate::Action::Cosmic(Action::ToggleNavBarCondensed)
782 } else {
783 crate::Action::Cosmic(Action::ToggleNavBar)
784 });
785
786 header = header.start(toggle);
787 }
788
789 if core.window.show_close {
790 header = header.on_close(crate::Action::Cosmic(Action::Close));
791 }
792
793 if core.window.show_maximize && crate::config::show_maximize() {
794 header = header.on_maximize(crate::Action::Cosmic(Action::Maximize));
795 }
796
797 if core.window.show_minimize && crate::config::show_minimize() {
798 header = header.on_minimize(crate::Action::Cosmic(Action::Minimize));
799 }
800
801 for element in self.header_start() {
802 header = header.start(element.map(crate::Action::App));
803 }
804
805 for element in self.header_center() {
806 header = header.center(element.map(crate::Action::App));
807 }
808
809 for element in self.header_end() {
810 header = header.end(element.map(crate::Action::App));
811 }
812
813 if content_container {
814 header.apply(|w| id_container(w, iced_core::id::Id::new("COSMIC_header")))
815 } else {
816 header
818 .apply(container)
819 .class(crate::theme::Container::custom(move |theme| {
820 let cosmic = theme.cosmic();
821 container::Style {
822 background: Some(iced::Background::Color(
823 cosmic.background.base.into(),
824 )),
825 border: iced::Border {
826 radius: [
827 (window_corner_radius[0] - 1.0).max(0.0),
828 (window_corner_radius[1] - 1.0).max(0.0),
829 cosmic.radius_0()[2],
830 cosmic.radius_0()[3],
831 ]
832 .into(),
833 ..Default::default()
834 },
835 ..Default::default()
836 }
837 }))
838 .apply(|w| id_container(w, iced_core::id::Id::new("COSMIC_header")))
839 }
840 })
841 } else {
842 None
843 })
844 .push(content)
846 .apply(container)
847 .padding(if maximized { 0 } else { 1 })
848 .class(crate::theme::Container::custom(move |theme| {
849 container::Style {
850 background: if content_container {
851 Some(iced::Background::Color(
852 theme.cosmic().background.base.into(),
853 ))
854 } else {
855 None
856 },
857 border: iced::Border {
858 color: theme.cosmic().bg_divider().into(),
859 width: if maximized { 0.0 } else { 1.0 },
860 radius: window_corner_radius.into(),
861 },
862 ..Default::default()
863 }
864 }));
865
866 let mut popover = popover(view_column).modal(true);
869 if let Some(dialog) = self
870 .dialog()
871 .map(|w| Element::from(id_container(w, iced_core::id::Id::new("COSMIC_dialog"))))
872 {
873 popover = popover.popup(dialog.map(crate::Action::App));
874 }
875
876 let view_element: Element<_> = popover.into();
877 view_element.debug(core.debug)
878 }
879}
880
881const EMBEDDED_FONTS: &[&[u8]] = &[
882 include_bytes!("../../res/open-sans/OpenSans-Light.ttf"),
883 include_bytes!("../../res/open-sans/OpenSans-Regular.ttf"),
884 include_bytes!("../../res/open-sans/OpenSans-Semibold.ttf"),
885 include_bytes!("../../res/open-sans/OpenSans-Bold.ttf"),
886 include_bytes!("../../res/open-sans/OpenSans-ExtraBold.ttf"),
887 include_bytes!("../../res/noto/NotoSansMono-Regular.ttf"),
888 include_bytes!("../../res/noto/NotoSansMono-Bold.ttf"),
889];
890
891#[cold]
892fn preload_fonts() {
893 let mut font_system = iced::advanced::graphics::text::font_system()
894 .write()
895 .unwrap();
896
897 EMBEDDED_FONTS
898 .iter()
899 .for_each(move |font| font_system.load_font(Cow::Borrowed(font)));
900}