From 8cf372c9b9359effacf07ecc09e6bbfc94a06e57 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Fri, 21 Mar 2025 03:17:59 +0100 Subject: [PATCH] perf: inline public getters/setters, and use non-generic inner functions To reduce compile-times and avoid some overhead to binary size, this will modify some of our generic functions to use non-generic inner functions where possible. The inner functions are marked carefully with `#[inline(never)]` to prevent being inlined by LLVM at their callsites While looking for generic functions to optimize, I have also taken the opportunity to annotate public non-generic getters and setters with `#[inline]` to ensure that LLVM will inline them across crate boundaries. By default, only generic functions are automatically inlined, and only when enabling fat LTO are constant functions reliably inlined across crate boundaries. --- cosmic-config/src/dbus.rs | 1 + cosmic-config/src/lib.rs | 12 +- cosmic-config/src/subscription.rs | 2 + cosmic-theme/src/lib.rs | 2 +- cosmic-theme/src/model/cosmic_palette.rs | 7 + cosmic-theme/src/model/derivation.rs | 5 + cosmic-theme/src/model/layout.rs | 5 +- cosmic-theme/src/model/mode.rs | 5 + cosmic-theme/src/model/theme.rs | 115 +++++++++++++ cosmic-theme/src/output/gtk4_output.rs | 6 + cosmic-theme/src/output/mod.rs | 3 + cosmic-theme/src/output/vs_code.rs | 2 + src/app/cosmic.rs | 16 +- src/app/mod.rs | 13 +- src/config/mod.rs | 1 + src/core.rs | 54 ++++-- src/dbus_activation.rs | 8 +- src/desktop.rs | 1 + src/font.rs | 5 + src/icon_theme.rs | 2 + src/keyboard_nav.rs | 3 +- src/malloc.rs | 2 + src/process.rs | 3 +- src/surface/mod.rs | 1 + src/theme/mod.rs | 20 ++- src/theme/portal.rs | 5 +- src/widget/aspect_ratio.rs | 11 +- src/widget/autosize.rs | 7 + src/widget/button/icon.rs | 6 +- src/widget/button/image.rs | 8 +- src/widget/button/link.rs | 1 + src/widget/button/mod.rs | 4 +- src/widget/button/text.rs | 3 +- src/widget/button/widget.rs | 30 +++- src/widget/calendar.rs | 7 +- src/widget/context_drawer/widget.rs | 158 ++++++++++-------- src/widget/flex_row/widget.rs | 5 +- src/widget/frames.rs | 52 +++--- src/widget/grid/widget.rs | 1 + src/widget/header_bar.rs | 5 +- src/widget/icon/handle.rs | 35 ++-- src/widget/icon/named.rs | 5 + src/widget/layer_container.rs | 9 + src/widget/list/column.rs | 51 +++--- src/widget/nav_bar.rs | 10 +- src/widget/nav_bar_toggle.rs | 2 +- src/widget/rectangle_tracker/subscription.rs | 10 +- src/widget/segmented_button/model/builder.rs | 9 + src/widget/segmented_button/model/entity.rs | 13 +- src/widget/segmented_button/model/mod.rs | 32 +++- .../segmented_button/model/selection.rs | 9 + src/widget/segmented_button/widget.rs | 4 +- src/widget/settings/item.rs | 47 ++++-- src/widget/text.rs | 117 +++++++++---- src/widget/toaster/mod.rs | 7 +- 55 files changed, 702 insertions(+), 255 deletions(-) diff --git a/cosmic-config/src/dbus.rs b/cosmic-config/src/dbus.rs index e689acc..e66d855 100644 --- a/cosmic-config/src/dbus.rs +++ b/cosmic-config/src/dbus.rs @@ -20,6 +20,7 @@ pub struct Watcher { impl Deref for Watcher { type Target = ConfigProxy<'static>; + #[inline] fn deref(&self) -> &Self::Target { &self.proxy } diff --git a/cosmic-config/src/lib.rs b/cosmic-config/src/lib.rs index 0c0f4db..beab95f 100644 --- a/cosmic-config/src/lib.rs +++ b/cosmic-config/src/lib.rs @@ -40,6 +40,7 @@ pub enum Error { } impl fmt::Display for Error { + #[cold] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::AtomicWrites(err) => err.fmt(f), @@ -61,6 +62,7 @@ impl Error { /// Whether the reason for the missing config is caused by an error. /// /// Useful for determining if it is appropriate to log as an error. + #[inline] pub fn is_err(&self) -> bool { !matches!(self, Self::NoConfigDirectory | Self::NotFound) } @@ -134,11 +136,6 @@ fn sanitize_name(name: &str) -> Result<&Path, Error> { } impl Config { - /// Get the config for the libcosmic toolkit - pub fn libcosmic() -> Result { - Self::new("com.system76.libcosmic", 1) - } - /// Get a system config for the given name and config version pub fn system(name: &str, version: u64) -> Result { let path = sanitize_name(name)?.join(format!("v{version}")); @@ -235,6 +232,7 @@ impl Config { } // Start a transaction (to set multiple configs at the same time) + #[inline] pub fn transaction(&self) -> ConfigTransaction { ConfigTransaction { config: self, @@ -368,7 +366,7 @@ pub struct ConfigTransaction<'a> { updates: Mutex>, } -impl<'a> ConfigTransaction<'a> { +impl ConfigTransaction<'_> { /// Apply all pending changes from ConfigTransaction //TODO: apply all changes at once pub fn commit(self) -> Result<(), Error> { @@ -386,7 +384,7 @@ impl<'a> ConfigTransaction<'a> { // Setting any setting in this way will do one transaction for all settings // when commit finishes that transaction -impl<'a> ConfigSet for ConfigTransaction<'a> { +impl ConfigSet for ConfigTransaction<'_> { fn set(&self, key: &str, value: T) -> Result<(), Error> { //TODO: sanitize key (no slashes, cannot be . or ..) let key_path = self.config.key_path(key)?; diff --git a/cosmic-config/src/subscription.rs b/cosmic-config/src/subscription.rs index 7468af0..6425595 100644 --- a/cosmic-config/src/subscription.rs +++ b/cosmic-config/src/subscription.rs @@ -16,6 +16,7 @@ pub enum ConfigUpdate { Failed, } +#[cold] pub fn config_subscription< I: 'static + Copy + Send + Sync + Hash, T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry, @@ -27,6 +28,7 @@ pub fn config_subscription< iced_futures::Subscription::run_with_id(id, watcher_stream(config_id, config_version, false)) } +#[cold] pub fn config_state_subscription< I: 'static + Copy + Send + Sync + Hash, T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry, diff --git a/cosmic-theme/src/lib.rs b/cosmic-theme/src/lib.rs index c30234b..5d59ccd 100644 --- a/cosmic-theme/src/lib.rs +++ b/cosmic-theme/src/lib.rs @@ -19,6 +19,6 @@ pub mod composite; pub mod steps; /// name of cosmic theme -pub const NAME: &'static str = "com.system76.CosmicTheme"; +pub const NAME: &str = "com.system76.CosmicTheme"; pub use palette; diff --git a/cosmic-theme/src/model/cosmic_palette.rs b/cosmic-theme/src/model/cosmic_palette.rs index 3b916c1..6a18908 100644 --- a/cosmic-theme/src/model/cosmic_palette.rs +++ b/cosmic-theme/src/model/cosmic_palette.rs @@ -26,6 +26,7 @@ pub enum CosmicPalette { impl CosmicPalette { /// extract the inner palette + #[inline] pub fn inner(self) -> CosmicPaletteInner { match self { CosmicPalette::Dark(p) => p, @@ -37,6 +38,7 @@ impl CosmicPalette { } impl AsMut for CosmicPalette { + #[inline] fn as_mut(&mut self) -> &mut CosmicPaletteInner { match self { CosmicPalette::Dark(p) => p, @@ -48,6 +50,7 @@ impl AsMut for CosmicPalette { } impl AsRef for CosmicPalette { + #[inline] fn as_ref(&self) -> &CosmicPaletteInner { match self { CosmicPalette::Dark(p) => p, @@ -60,6 +63,7 @@ impl AsRef for CosmicPalette { impl CosmicPalette { /// check if the palette is dark + #[inline] pub fn is_dark(&self) -> bool { match self { CosmicPalette::Dark(_) | CosmicPalette::HighContrastDark(_) => true, @@ -68,6 +72,7 @@ impl CosmicPalette { } /// check if the palette is high_contrast + #[inline] pub fn is_high_contrast(&self) -> bool { match self { CosmicPalette::HighContrastLight(_) | CosmicPalette::HighContrastDark(_) => true, @@ -77,6 +82,7 @@ impl CosmicPalette { } impl Default for CosmicPalette { + #[inline] fn default() -> Self { CosmicPalette::Dark(Default::default()) } @@ -164,6 +170,7 @@ pub struct CosmicPaletteInner { impl CosmicPalette { /// name of the palette + #[inline] pub fn name(&self) -> &str { match &self { CosmicPalette::Dark(p) => &p.name, diff --git a/cosmic-theme/src/model/derivation.rs b/cosmic-theme/src/model/derivation.rs index 9276142..f4147c2 100644 --- a/cosmic-theme/src/model/derivation.rs +++ b/cosmic-theme/src/model/derivation.rs @@ -77,26 +77,31 @@ pub struct Component { #[allow(clippy::must_use_candidate)] #[allow(clippy::doc_markdown)] impl Component { + #[inline] /// get @hover_state_color pub fn hover_state_color(&self) -> Srgba { self.hover } + #[inline] /// get @pressed_state_color pub fn pressed_state_color(&self) -> Srgba { self.pressed } + #[inline] /// get @selected_state_color pub fn selected_state_color(&self) -> Srgba { self.selected } + #[inline] /// get @selected_state_text_color pub fn selected_state_text_color(&self) -> Srgba { self.selected_text } + #[inline] /// get @focus_color pub fn focus_color(&self) -> Srgba { self.focus diff --git a/cosmic-theme/src/model/layout.rs b/cosmic-theme/src/model/layout.rs index 79456dc..a476b63 100644 --- a/cosmic-theme/src/model/layout.rs +++ b/cosmic-theme/src/model/layout.rs @@ -1,5 +1,4 @@ #[derive(Default)] pub struct Layout { - corner_radii: [u32;4], - -} \ No newline at end of file + corner_radii: [u32; 4], +} diff --git a/cosmic-theme/src/model/mode.rs b/cosmic-theme/src/model/mode.rs index f57c653..ce16697 100644 --- a/cosmic-theme/src/model/mode.rs +++ b/cosmic-theme/src/model/mode.rs @@ -16,6 +16,7 @@ pub struct ThemeMode { } impl Default for ThemeMode { + #[inline] fn default() -> Self { Self { is_dark: true, @@ -25,15 +26,19 @@ impl Default for ThemeMode { } impl ThemeMode { + #[inline] /// Check if the theme is currently using dark mode pub fn is_dark(config: &Config) -> Result { config.get::("is_dark") } + #[inline] + /// The current version of the theme mode config. pub const fn version() -> u64 { Self::VERSION } + #[inline] /// Get the config for the theme mode pub fn config() -> Result { Config::new(THEME_MODE_ID, Self::VERSION) diff --git a/cosmic-theme/src/model/theme.rs b/cosmic-theme/src/model/theme.rs index a36bbda..d159b40 100644 --- a/cosmic-theme/src/model/theme.rs +++ b/cosmic-theme/src/model/theme.rs @@ -103,6 +103,7 @@ pub struct Theme { } impl Default for Theme { + #[inline] fn default() -> Self { Self::preferred_theme() } @@ -121,36 +122,43 @@ impl Theme { NAME } + #[inline] /// Get the config for the current dark theme pub fn dark_config() -> Result { Config::new(DARK_THEME_ID, Self::VERSION) } + #[inline] /// Get the config for the current light theme pub fn light_config() -> Result { Config::new(LIGHT_THEME_ID, Self::VERSION) } + #[inline] /// get the built in light theme pub fn light_default() -> Self { LIGHT_PALETTE.clone().into() } + #[inline] /// get the built in dark theme pub fn dark_default() -> Self { DARK_PALETTE.clone().into() } + #[inline] /// get the built in high contrast dark theme pub fn high_contrast_dark_default() -> Self { CosmicPalette::HighContrastDark(DARK_PALETTE.as_ref().clone()).into() } + #[inline] /// get the built in high contrast light theme pub fn high_contrast_light_default() -> Self { CosmicPalette::HighContrastLight(LIGHT_PALETTE.as_ref().clone()).into() } + #[inline] /// Convert the theme to a high-contrast variant pub fn to_high_contrast(&self) -> Self { todo!(); @@ -159,6 +167,7 @@ impl Theme { // TODO convenient getter functions for each named color variable #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @accent_color pub fn accent_color(&self) -> Srgba { self.accent.base @@ -166,6 +175,7 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @success_color pub fn success_color(&self) -> Srgba { self.success.base @@ -173,6 +183,7 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @destructive_color pub fn destructive_color(&self) -> Srgba { self.destructive.base @@ -180,6 +191,7 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @warning_color pub fn warning_color(&self) -> Srgba { self.warning.base @@ -187,6 +199,7 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @small_widget_divider pub fn small_widget_divider(&self) -> Srgba { let mut neutral_9 = self.palette.neutral_9; @@ -197,42 +210,55 @@ impl Theme { // Containers #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @bg_color pub fn bg_color(&self) -> Srgba { self.background.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @bg_component_color pub fn bg_component_color(&self) -> Srgba { self.background.component.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @primary_container_color pub fn primary_container_color(&self) -> Srgba { self.primary.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @primary_component_color pub fn primary_component_color(&self) -> Srgba { self.primary.component.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @secondary_container_color pub fn secondary_container_color(&self) -> Srgba { self.secondary.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @secondary_component_color pub fn secondary_component_color(&self) -> Srgba { self.secondary.component.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @button_bg_color pub fn button_bg_color(&self) -> Srgba { self.button.base @@ -241,90 +267,119 @@ impl Theme { // Text #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_bg_color pub fn on_bg_color(&self) -> Srgba { self.background.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_bg_component_color pub fn on_bg_component_color(&self) -> Srgba { self.background.component.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_primary_color pub fn on_primary_container_color(&self) -> Srgba { self.primary.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_primary_component_color pub fn on_primary_component_color(&self) -> Srgba { self.primary.component.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_secondary_color pub fn on_secondary_container_color(&self) -> Srgba { self.secondary.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_secondary_component_color pub fn on_secondary_component_color(&self) -> Srgba { self.secondary.component.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @accent_text_color pub fn accent_text_color(&self) -> Srgba { self.accent_text.unwrap_or(self.accent.base) } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @success_text_color pub fn success_text_color(&self) -> Srgba { self.success.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @warning_text_color pub fn warning_text_color(&self) -> Srgba { self.warning.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @destructive_text_color pub fn destructive_text_color(&self) -> Srgba { self.destructive.base } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_accent_color pub fn on_accent_color(&self) -> Srgba { self.accent.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_success_color pub fn on_success_color(&self) -> Srgba { self.success.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_warning_color pub fn on_warning_color(&self) -> Srgba { self.warning.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @on_destructive_color pub fn on_destructive_color(&self) -> Srgba { self.destructive.on } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @button_color pub fn button_color(&self) -> Srgba { self.button.on @@ -333,36 +388,47 @@ impl Theme { // Borders and Dividers #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @bg_divider pub fn bg_divider(&self) -> Srgba { self.background.divider } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @bg_component_divider pub fn bg_component_divider(&self) -> Srgba { self.background.component.divider } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @primary_container_divider pub fn primary_container_divider(&self) -> Srgba { self.primary.divider } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @primary_component_divider pub fn primary_component_divider(&self) -> Srgba { self.primary.component.divider } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @secondary_container_divider pub fn secondary_container_divider(&self) -> Srgba { self.secondary.divider } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @button_divider pub fn button_divider(&self) -> Srgba { self.button.divider @@ -370,6 +436,7 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @window_header_bg pub fn window_header_bg(&self) -> Srgba { self.background.base @@ -377,60 +444,79 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_none pub fn space_none(&self) -> u16 { self.spacing.space_none } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_xxxs pub fn space_xxxs(&self) -> u16 { self.spacing.space_xxxs } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_xxs pub fn space_xxs(&self) -> u16 { self.spacing.space_xxs } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_xs pub fn space_xs(&self) -> u16 { self.spacing.space_xs } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_s pub fn space_s(&self) -> u16 { self.spacing.space_s } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_m pub fn space_m(&self) -> u16 { self.spacing.space_m } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_l pub fn space_l(&self) -> u16 { self.spacing.space_l } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_xl pub fn space_xl(&self) -> u16 { self.spacing.space_xl } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_xxl pub fn space_xxl(&self) -> u16 { self.spacing.space_xxl } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @space_xxxl pub fn space_xxxl(&self) -> u16 { self.spacing.space_xxxl @@ -438,36 +524,47 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @radius_0 pub fn radius_0(&self) -> [f32; 4] { self.corner_radii.radius_0 } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @radius_xs pub fn radius_xs(&self) -> [f32; 4] { self.corner_radii.radius_xs } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @radius_s pub fn radius_s(&self) -> [f32; 4] { self.corner_radii.radius_s } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @radius_m pub fn radius_m(&self) -> [f32; 4] { self.corner_radii.radius_m } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @radius_l pub fn radius_l(&self) -> [f32; 4] { self.corner_radii.radius_l } + #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @radius_xl pub fn radius_xl(&self) -> [f32; 4] { self.corner_radii.radius_xl @@ -475,6 +572,7 @@ impl Theme { #[must_use] #[allow(clippy::doc_markdown)] + #[inline] /// get @shade_color pub fn shade_color(&self) -> Srgba { self.shade @@ -631,6 +729,7 @@ impl Default for ThemeBuilder { } impl ThemeBuilder { + #[inline] /// Get a builder that is initialized with the default dark theme pub fn dark() -> Self { Self { @@ -639,6 +738,7 @@ impl ThemeBuilder { } } + #[inline] /// Get a builder that is initialized with the default light theme pub fn light() -> Self { Self { @@ -647,6 +747,7 @@ impl ThemeBuilder { } } + #[inline] /// Get a builder that is initialized with the default dark high contrast theme pub fn dark_high_contrast() -> Self { let palette: CosmicPalette = DARK_PALETTE.to_owned(); @@ -656,6 +757,7 @@ impl ThemeBuilder { } } + #[inline] /// Get a builder that is initialized with the default light high contrast theme pub fn light_high_contrast() -> Self { let palette: CosmicPalette = LIGHT_PALETTE.to_owned(); @@ -665,6 +767,7 @@ impl ThemeBuilder { } } + #[inline] /// Get a builder that is initialized with the provided palette pub fn palette(palette: CosmicPalette) -> Self { Self { @@ -673,60 +776,70 @@ impl ThemeBuilder { } } + #[inline] /// set the spacing of the builder pub fn spacing(mut self, spacing: Spacing) -> Self { self.spacing = spacing; self } + #[inline] /// set the corner radii of the builder pub fn corner_radii(mut self, corner_radii: CornerRadii) -> Self { self.corner_radii = corner_radii; self } + #[inline] /// apply a neutral tint to the palette pub fn neutral_tint(mut self, tint: Srgb) -> Self { self.neutral_tint = Some(tint); self } + #[inline] /// apply a text tint to the palette pub fn text_tint(mut self, tint: Srgb) -> Self { self.text_tint = Some(tint); self } + #[inline] /// apply a background color to the palette pub fn bg_color(mut self, c: Srgba) -> Self { self.bg_color = Some(c); self } + #[inline] /// apply a primary container background color to the palette pub fn primary_container_bg(mut self, c: Srgba) -> Self { self.primary_container_bg = Some(c); self } + #[inline] /// apply a accent color to the palette pub fn accent(mut self, c: Srgb) -> Self { self.accent = Some(c); self } + #[inline] /// apply a success color to the palette pub fn success(mut self, c: Srgb) -> Self { self.success = Some(c); self } + #[inline] /// apply a warning color to the palette pub fn warning(mut self, c: Srgb) -> Self { self.warning = Some(c); self } + #[inline] /// apply a destructive color to the palette pub fn destructive(mut self, c: Srgb) -> Self { self.destructive = Some(c); @@ -1139,11 +1252,13 @@ impl ThemeBuilder { theme } + #[inline] /// Get the builder for the dark config pub fn dark_config() -> Result { Config::new(DARK_THEME_BUILDER_ID, Self::VERSION) } + #[inline] /// Get the builder for the light config pub fn light_config() -> Result { Config::new(LIGHT_THEME_BUILDER_ID, Self::VERSION) diff --git a/cosmic-theme/src/output/gtk4_output.rs b/cosmic-theme/src/output/gtk4_output.rs index d0ab0c0..c172e4e 100644 --- a/cosmic-theme/src/output/gtk4_output.rs +++ b/cosmic-theme/src/output/gtk4_output.rs @@ -11,6 +11,7 @@ use super::{to_rgba, OutputError}; impl Theme { #[must_use] + #[cold] /// turn the theme into css pub fn as_gtk4(&self) -> String { let Self { @@ -145,6 +146,7 @@ impl Theme { /// # Errors /// /// Returns an `OutputError` if there is an error writing the CSS file. + #[cold] pub fn write_gtk4(&self) -> Result<(), OutputError> { let css_str = self.as_gtk4(); let Some(config_dir) = dirs::config_dir() else { @@ -174,6 +176,7 @@ impl Theme { /// # Errors /// /// Returns an `OutputError` if there is an error applying the CSS file. + #[cold] pub fn apply_gtk(is_dark: bool) -> Result<(), OutputError> { let Some(config_dir) = dirs::config_dir() else { return Err(OutputError::MissingConfigDir); @@ -213,6 +216,7 @@ impl Theme { /// # Errors /// /// Returns an `OutputError` if there is an error resetting the CSS file. + #[cold] pub fn reset_gtk() -> Result<(), OutputError> { let Some(config_dir) = dirs::config_dir() else { return Err(OutputError::MissingConfigDir); @@ -229,6 +233,7 @@ impl Theme { res } + #[cold] fn backup_non_cosmic_css(path: &Path, cosmic_css: &Path) -> io::Result<()> { if !Self::is_cosmic_css(path, cosmic_css)?.unwrap_or(true) { let backup_path = path.with_extension("css.bak"); @@ -237,6 +242,7 @@ impl Theme { Ok(()) } + #[cold] fn reset_cosmic_css(path: &Path, cosmic_css: &Path) -> io::Result<()> { if Self::is_cosmic_css(path, cosmic_css)?.unwrap_or_default() { fs::remove_file(path)?; diff --git a/cosmic-theme/src/output/mod.rs b/cosmic-theme/src/output/mod.rs index f282233..f2eb6b4 100644 --- a/cosmic-theme/src/output/mod.rs +++ b/cosmic-theme/src/output/mod.rs @@ -19,6 +19,7 @@ pub enum OutputError { } impl Theme { + #[inline] pub fn apply_exports(&self) -> Result<(), OutputError> { let gtk_res = Theme::apply_gtk(self.is_dark); let vs_res = self.clone().apply_vs_code(); @@ -27,12 +28,14 @@ impl Theme { Ok(()) } + #[inline] pub fn write_exports(&self) -> Result<(), OutputError> { let gtk_res = self.write_gtk4(); gtk_res?; Ok(()) } + #[inline] pub fn reset_exports() -> Result<(), OutputError> { let gtk_res = Theme::reset_gtk(); let vs_res = Theme::reset_vs_code(); diff --git a/cosmic-theme/src/output/vs_code.rs b/cosmic-theme/src/output/vs_code.rs index 11403db..5c770cd 100644 --- a/cosmic-theme/src/output/vs_code.rs +++ b/cosmic-theme/src/output/vs_code.rs @@ -266,6 +266,7 @@ impl From for VsTheme { } impl Theme { + #[cold] pub fn apply_vs_code(self) -> Result<(), OutputError> { let vs_theme = VsTheme::from(self); let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?; @@ -289,6 +290,7 @@ impl Theme { Ok(()) } + #[cold] pub fn reset_vs_code() -> Result<(), OutputError> { let config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?; let vs_code_dir = config_dir.join("Code").join("User"); diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 919e604..ec129d3 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -6,16 +6,16 @@ use std::collections::HashMap; use std::sync::Arc; use super::{Action, Application, ApplicationExt, Subscription}; -use crate::theme::{Theme, ThemeType, THEME}; -use crate::{keyboard_nav, Core, Element}; +use crate::theme::{THEME, Theme, ThemeType}; +use crate::{Core, Element, keyboard_nav}; #[cfg(feature = "wayland")] use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState}; use cosmic_theme::ThemeMode; -#[cfg(feature = "wayland")] -use iced::event::wayland; #[cfg(not(any(feature = "multi-window", feature = "wayland")))] use iced::Application as IcedApplication; -use iced::{window, Task}; +#[cfg(feature = "wayland")] +use iced::event::wayland; +use iced::{Task, window}; use iced_futures::event::listen_with; use palette::color_difference::EuclideanDistance; @@ -243,6 +243,7 @@ where } #[allow(clippy::too_many_lines)] + #[cold] pub fn subscription(&self) -> Subscription> { let window_events = listen_with(|event, _, id| { match event { @@ -410,6 +411,7 @@ where impl Cosmic { #[allow(clippy::unused_self)] + #[cold] pub fn close(&mut self) -> iced::Task> { if let Some(id) = self.app.core().main_window_id() { iced::window::close(id) @@ -490,10 +492,10 @@ impl Cosmic { Action::KeyboardNav(message) => match message { keyboard_nav::Action::FocusNext => { - return iced::widget::focus_next().map(crate::Action::Cosmic) + return iced::widget::focus_next().map(crate::Action::Cosmic); } keyboard_nav::Action::FocusPrevious => { - return iced::widget::focus_previous().map(crate::Action::Cosmic) + return iced::widget::focus_previous().map(crate::Action::Cosmic); } keyboard_nav::Action::Escape => return self.app.on_escape(), keyboard_nav::Action::Search => return self.app.on_search(), diff --git a/src/app/mod.rs b/src/app/mod.rs index 4af3f34..2f4192b 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -17,10 +17,10 @@ pub mod settings; pub type Task = iced::Task>; +pub use crate::Core; use crate::prelude::*; use crate::theme::THEME; use crate::widget::{container, horizontal_space, id_container, menu, nav_bar, popover}; -pub use crate::Core; use apply::Apply; use context_drawer::ContextDrawer; use iced::window; @@ -28,6 +28,7 @@ use iced::{Length, Subscription}; pub use settings::Settings; use std::borrow::Cow; +#[cold] pub(crate) fn iced_settings( settings: Settings, flags: App::Flags, @@ -681,11 +682,10 @@ impl ApplicationExt for App { }; // Ensures visually aligned radii for content and window corners - let window_corner_radius = - crate::theme::active() - .cosmic() - .radius_s() - .map(|x| if x < 4.0 { x } else { x + 4.0 }); + let window_corner_radius = crate::theme::active() + .cosmic() + .radius_s() + .map(|x| if x < 4.0 { x } else { x + 4.0 }); let view_column = crate::widget::column::with_capacity(2) .push_maybe(if core.window.show_headerbar { @@ -811,6 +811,7 @@ const EMBEDDED_FONTS: &[&[u8]] = &[ include_bytes!("../../res/noto/NotoSansMono-Bold.ttf"), ]; +#[cold] fn preload_fonts() { let mut font_system = iced::advanced::graphics::text::font_system() .write() diff --git a/src/config/mod.rs b/src/config/mod.rs index 1e82bec..dedadbc 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -134,6 +134,7 @@ impl Default for CosmicTk { } impl CosmicTk { + #[inline] pub fn config() -> Result { Config::new(ID, Self::VERSION) } diff --git a/src/core.rs b/src/core.rs index fdeeba5..744b33b 100644 --- a/src/core.rs +++ b/src/core.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use crate::widget::nav_bar; use cosmic_config::CosmicConfigEntry; use cosmic_theme::ThemeMode; -use iced::{window, Limits, Size}; +use iced::{Limits, Size, window}; use iced_core::window::Id; use palette::Srgba; use slotmap::Key; @@ -161,45 +161,52 @@ impl Default for Core { impl Core { /// Whether the window is too small for the nav bar + main content. #[must_use] - pub fn is_condensed(&self) -> bool { + #[inline] + pub const fn is_condensed(&self) -> bool { self.is_condensed } /// The scaling factor used by the application. #[must_use] - pub fn scale_factor(&self) -> f32 { + #[inline] + pub const fn scale_factor(&self) -> f32 { self.scale_factor } /// Enable or disable keyboard navigation - pub fn set_keyboard_nav(&mut self, enabled: bool) { + #[inline] + pub const fn set_keyboard_nav(&mut self, enabled: bool) { self.keyboard_nav = enabled; } - #[must_use] /// Enable or disable keyboard navigation - pub fn keyboard_nav(&self) -> bool { + #[must_use] + #[inline] + pub const fn keyboard_nav(&self) -> bool { self.keyboard_nav } /// Changes the scaling factor used by the application. + #[cold] pub(crate) fn set_scale_factor(&mut self, factor: f32) { self.scale_factor = factor; self.is_condensed_update(); } /// Set header bar title + #[inline] pub fn set_header_title(&mut self, title: String) { self.window.header_title = title; } + #[inline] /// Whether to show or hide the main window's content. pub(crate) fn show_content(&self) -> bool { !self.is_condensed || !self.nav_bar.toggled_condensed } - /// Call this whenever the scaling factor or window width has changed. #[allow(clippy::cast_precision_loss)] + /// Call this whenever the scaling factor or window width has changed. fn is_condensed_update(&mut self) { // Nav bar (280px) + padding (8px) + content (360px) let mut breakpoint = 280.0 + 8.0 + 360.0; @@ -212,6 +219,7 @@ impl Core { self.nav_bar_update(); } + #[inline] fn condensed_conflict(&self) -> bool { // There is a conflict if the view is condensed and both the nav bar and context drawer are open on the same layer self.is_condensed @@ -220,6 +228,7 @@ impl Core { && !self.window.context_is_overlay } + #[inline] pub(crate) fn context_width(&self, has_nav: bool) -> f32 { let window_width = self.window.width / self.scale_factor; @@ -230,12 +239,14 @@ impl Core { reserved_width += 280.0 + 8.0; } + #[allow(clippy::manual_clamp)] // This logic is to ensure the context drawer does not take up too much of the content's space // The minimum width is 344px and the maximum with is 480px // We want to keep the content at least 360px until going down to the minimum width (window_width - reserved_width).min(480.0).max(344.0) } + #[cold] pub fn set_show_context(&mut self, show: bool) { self.window.show_context = show; self.is_condensed_update(); @@ -246,38 +257,46 @@ impl Core { } } + #[inline] pub fn main_window_is(&self, id: iced::window::Id) -> bool { self.main_window_id().is_some_and(|main_id| main_id == id) } /// Whether the nav panel is visible or not #[must_use] - pub fn nav_bar_active(&self) -> bool { + #[inline] + pub const fn nav_bar_active(&self) -> bool { self.nav_bar.active } + #[inline] pub fn nav_bar_toggle(&mut self) { self.nav_bar.toggled = !self.nav_bar.toggled; self.nav_bar_set_toggled_condensed(self.nav_bar.toggled); } + #[inline] pub fn nav_bar_toggle_condensed(&mut self) { self.nav_bar_set_toggled_condensed(!self.nav_bar.toggled_condensed); } - pub(crate) fn nav_bar_context(&self) -> nav_bar::Id { + #[inline] + pub(crate) const fn nav_bar_context(&self) -> nav_bar::Id { self.nav_bar.context_id } + #[inline] pub(crate) fn nav_bar_set_context(&mut self, id: nav_bar::Id) { self.nav_bar.context_id = id; } + #[inline] pub fn nav_bar_set_toggled(&mut self, toggled: bool) { self.nav_bar.toggled = toggled; self.nav_bar_set_toggled_condensed(self.nav_bar.toggled); } + #[cold] pub(crate) fn nav_bar_set_toggled_condensed(&mut self, toggled: bool) { self.nav_bar.toggled_condensed = toggled; self.nav_bar_update(); @@ -293,6 +312,7 @@ impl Core { } } + #[inline] pub(crate) fn nav_bar_update(&mut self) { self.nav_bar.active = if self.is_condensed { self.nav_bar.toggled_condensed @@ -301,25 +321,29 @@ impl Core { }; } + #[inline] /// Set the height of the main window. - pub(crate) fn set_window_height(&mut self, new_height: f32) { + pub(crate) const fn set_window_height(&mut self, new_height: f32) { self.window.height = new_height; } + #[inline] /// Set the width of the main window. pub(crate) fn set_window_width(&mut self, new_width: f32) { self.window.width = new_width; self.is_condensed_update(); } + #[inline] /// Get the current system theme - pub fn system_theme(&self) -> &Theme { + pub const fn system_theme(&self) -> &Theme { &self.system_theme } + #[inline] #[must_use] /// Get the current system theme mode - pub fn system_theme_mode(&self) -> ThemeMode { + pub const fn system_theme_mode(&self) -> ThemeMode { self.system_theme_mode } @@ -359,12 +383,14 @@ impl Core { /// Get the current focused window if it exists #[must_use] - pub fn focused_window(&self) -> Option { + #[inline] + pub const fn focused_window(&self) -> Option { self.focused_window } /// Whether the application should use a dark theme, according to the system #[must_use] + #[inline] pub fn system_is_dark(&self) -> bool { self.portal_is_dark .unwrap_or(self.system_theme_mode.is_dark) @@ -372,11 +398,13 @@ impl Core { /// The [`Id`] of the main window #[must_use] + #[inline] pub fn main_window_id(&self) -> Option { self.main_window.filter(|id| iced::window::Id::NONE != *id) } /// Reset the tracked main window to a new value + #[inline] pub fn set_main_window_id(&mut self, mut id: Option) -> Option { std::mem::swap(&mut self.main_window, &mut id); id diff --git a/src/dbus_activation.rs b/src/dbus_activation.rs index 84b5d00..4fffa42 100644 --- a/src/dbus_activation.rs +++ b/src/dbus_activation.rs @@ -5,14 +5,15 @@ use { crate::ApplicationExt, iced::Subscription, iced_futures::futures::{ - channel::mpsc::{Receiver, Sender}, SinkExt, + channel::mpsc::{Receiver, Sender}, }, std::{any::TypeId, collections::HashMap}, url::Url, zbus::{interface, proxy, zvariant::Value}, }; +#[cold] pub fn subscription() -> Subscription> { use iced_futures::futures::StreamExt; iced_futures::Subscription::run_with_id( @@ -105,10 +106,12 @@ pub struct DbusActivation(Option>); impl DbusActivation { #[must_use] + #[inline] pub fn new() -> Self { Self(None) } + #[inline] pub fn rx(&mut self) -> Receiver { let (tx, rx) = iced_futures::futures::channel::mpsc::channel(10); self.0 = Some(tx); @@ -139,6 +142,7 @@ pub trait DbusActivationInterface { #[interface(name = "org.freedesktop.DbusActivation")] impl DbusActivation { + #[cold] async fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) { if let Some(tx) = &mut self.0 { let _ = tx @@ -159,6 +163,7 @@ impl DbusActivation { } } + #[cold] async fn open(&mut self, uris: Vec<&str>, platform_data: HashMap<&str, Value<'_>>) { if let Some(tx) = &mut self.0 { let _ = tx @@ -181,6 +186,7 @@ impl DbusActivation { } } + #[cold] async fn activate_action( &mut self, action_name: &str, diff --git a/src/desktop.rs b/src/desktop.rs index 3f5f575..ee35887 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -240,6 +240,7 @@ impl DesktopEntryData { } #[cfg(not(windows))] +#[cold] pub async fn spawn_desktop_exec(exec: S, env_vars: I, app_id: Option<&str>) where S: AsRef, diff --git a/src/font.rs b/src/font.rs index c390cb6..e0eb474 100644 --- a/src/font.rs +++ b/src/font.rs @@ -6,10 +6,12 @@ pub use iced::Font; use iced_core::font::Weight; +#[inline] pub fn default() -> Font { Font::from(crate::config::interface_font()) } +#[inline] pub fn light() -> Font { Font { weight: Weight::Light, @@ -17,6 +19,7 @@ pub fn light() -> Font { } } +#[inline] pub fn semibold() -> Font { Font { weight: Weight::Semibold, @@ -24,6 +27,7 @@ pub fn semibold() -> Font { } } +#[inline] pub fn bold() -> Font { Font { weight: Weight::Bold, @@ -31,6 +35,7 @@ pub fn bold() -> Font { } } +#[inline] pub fn mono() -> Font { Font::from(crate::config::monospace_font()) } diff --git a/src/icon_theme.rs b/src/icon_theme.rs index 277d2cf..69fe584 100644 --- a/src/icon_theme.rs +++ b/src/icon_theme.rs @@ -13,12 +13,14 @@ pub(crate) static DEFAULT: Mutex> = Mutex::new(Cow::Borrowed(C /// The fallback icon theme to search if no icon theme was specified. #[must_use] #[allow(clippy::missing_panics_doc)] +#[inline] pub fn default() -> String { DEFAULT.lock().unwrap().to_string() } /// Set the fallback icon theme to search when loading system icons. #[allow(clippy::missing_panics_doc)] +#[cold] pub fn set_default(name: impl Into>) { *DEFAULT.lock().unwrap() = name.into(); } diff --git a/src/keyboard_nav.rs b/src/keyboard_nav.rs index 6521146..961a423 100644 --- a/src/keyboard_nav.rs +++ b/src/keyboard_nav.rs @@ -3,7 +3,7 @@ //! Subscribe to common application keyboard shortcuts. -use iced::{event, keyboard, Event, Subscription}; +use iced::{Event, Subscription, event, keyboard}; use iced_core::keyboard::key::Named; use iced_futures::event::listen_raw; @@ -16,6 +16,7 @@ pub enum Action { Search, } +#[cold] pub fn subscription() -> Subscription { listen_raw(|event, status, _| { if event::Status::Ignored != status { diff --git a/src/malloc.rs b/src/malloc.rs index a56323f..b99a66f 100644 --- a/src/malloc.rs +++ b/src/malloc.rs @@ -11,6 +11,7 @@ unsafe extern "C" { fn mallopt(param: c_int, value: c_int) -> c_int; } +#[inline] pub fn trim(pad: usize) { unsafe { malloc_trim(pad); @@ -18,6 +19,7 @@ pub fn trim(pad: usize) { } /// Prevents glibc from hoarding memory via memory fragmentation. +#[inline] pub fn limit_mmap_threshold(threshold: i32) { unsafe { mallopt(M_MMAP_THRESHOLD, threshold as c_int); diff --git a/src/process.rs b/src/process.rs index b1a3ae0..1ad048d 100644 --- a/src/process.rs +++ b/src/process.rs @@ -5,7 +5,7 @@ use smol::io::AsyncReadExt; use std::io; use std::os::fd::OwnedFd; -use std::process::{exit, Command, Stdio}; +use std::process::{Command, Stdio, exit}; #[cfg(feature = "tokio")] use tokio::io::AsyncReadExt; @@ -24,6 +24,7 @@ async fn read_from_pipe(read: OwnedFd) -> Option { } /// Performs a double fork with setsid to spawn and detach a command. +#[cold] pub async fn spawn(mut command: Command) -> Option { // NOTE: Windows platform is not supported command diff --git a/src/surface/mod.rs b/src/surface/mod.rs index c08108e..3041fa5 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -50,6 +50,7 @@ pub enum Action { } impl std::fmt::Debug for Action { + #[cold] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::AppSubsurface(arg0, arg1) => f diff --git a/src/theme/mod.rs b/src/theme/mod.rs index f2e4203..e0b0a42 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -53,36 +53,41 @@ pub(crate) static THEME: Mutex = Mutex::new(Theme { }); /// Currently-defined theme. +#[inline] #[allow(clippy::missing_panics_doc)] pub fn active() -> Theme { THEME.lock().unwrap().clone() } /// Currently-defined theme type. +#[inline] #[allow(clippy::missing_panics_doc)] pub fn active_type() -> ThemeType { THEME.lock().unwrap().theme_type.clone() } /// Preferred interface spacing parameters defined by the active theme. -#[inline(always)] +#[inline] pub fn spacing() -> Spacing { active().cosmic().spacing } /// Whether the active theme has a dark preference. +#[inline] #[must_use] pub fn is_dark() -> bool { active_type().is_dark() } /// Whether the active theme is high contrast. +#[inline] #[must_use] pub fn is_high_contrast() -> bool { active_type().is_high_contrast() } /// Watches for changes to the system's theme preference. +#[cold] pub fn subscription(is_dark: bool) -> Subscription { config_subscription::<_, crate::cosmic_theme::Theme>( ( @@ -173,6 +178,7 @@ pub enum ThemeType { impl ThemeType { /// Whether the theme has a dark preference. #[must_use] + #[inline] pub fn is_dark(&self) -> bool { match self { Self::Dark | Self::HighContrastDark => true, @@ -182,6 +188,7 @@ impl ThemeType { } /// Whether the theme has a high contrast. + #[inline] #[must_use] pub fn is_high_contrast(&self) -> bool { match self { @@ -191,6 +198,7 @@ impl ThemeType { } } + #[inline] /// Prefer dark or light theme. /// If `None`, the system preference is used. pub fn prefer_dark(&mut self, new_prefer_dark: Option) { @@ -208,6 +216,7 @@ pub struct Theme { } impl Theme { + #[inline] pub fn cosmic(&self) -> &cosmic_theme::Theme { match self.theme_type { ThemeType::Dark => &COSMIC_DARK, @@ -218,6 +227,7 @@ impl Theme { } } + #[inline] pub fn dark() -> Self { Self { theme_type: ThemeType::Dark, @@ -225,6 +235,7 @@ impl Theme { } } + #[inline] pub fn light() -> Self { Self { theme_type: ThemeType::Light, @@ -232,6 +243,7 @@ impl Theme { } } + #[inline] pub fn dark_hc() -> Self { Self { theme_type: ThemeType::HighContrastDark, @@ -239,6 +251,7 @@ impl Theme { } } + #[inline] pub fn light_hc() -> Self { Self { theme_type: ThemeType::HighContrastLight, @@ -246,6 +259,7 @@ impl Theme { } } + #[inline] pub fn custom(theme: Arc) -> Self { Self { theme_type: ThemeType::Custom(theme), @@ -253,6 +267,7 @@ impl Theme { } } + #[inline] pub fn system(theme: Arc) -> Self { Self { theme_type: ThemeType::System { @@ -263,6 +278,7 @@ impl Theme { } } + #[inline] /// get current container /// can be used in a component that is intended to be a child of a `CosmicContainer` pub fn current_container(&self) -> &cosmic_theme::Container { @@ -273,6 +289,7 @@ impl Theme { } } + #[inline] /// set the theme pub fn set_theme(&mut self, theme: ThemeType) { self.theme_type = theme; @@ -280,6 +297,7 @@ impl Theme { } impl LayeredTheme for Theme { + #[inline] fn set_layer(&mut self, layer: cosmic_theme::Layer) { self.layer = layer; } diff --git a/src/theme/portal.rs b/src/theme/portal.rs index e3dc751..f0c88c0 100644 --- a/src/theme/portal.rs +++ b/src/theme/portal.rs @@ -1,6 +1,6 @@ -use ashpd::desktop::settings::{ColorScheme, Contrast}; use ashpd::desktop::Color; -use iced::futures::{self, select, FutureExt, SinkExt, StreamExt}; +use ashpd::desktop::settings::{ColorScheme, Contrast}; +use iced::futures::{self, FutureExt, SinkExt, StreamExt, select}; use iced_futures::stream; use tracing::error; @@ -11,6 +11,7 @@ pub enum Desktop { Contrast(Contrast), } +#[cold] pub fn desktop_settings() -> iced_futures::Subscription { iced_futures::Subscription::run_with_id( std::any::TypeId::of::(), diff --git a/src/widget/aspect_ratio.rs b/src/widget/aspect_ratio.rs index 39ac9c5..e66c14d 100644 --- a/src/widget/aspect_ratio.rs +++ b/src/widget/aspect_ratio.rs @@ -1,7 +1,7 @@ //! A container which constraints itself to a specific aspect ratio. -use iced::widget::Container; use iced::Size; +use iced::widget::Container; use iced_core::event::{self, Event}; use iced_core::layout; use iced_core::mouse; @@ -76,6 +76,7 @@ where /// Sets the width of the [`self.`]. #[must_use] + #[inline] pub fn width(mut self, width: Length) -> Self { self.container = self.container.width(width); self @@ -83,6 +84,7 @@ where /// Sets the height of the [`Container`]. #[must_use] + #[inline] pub fn height(mut self, height: Length) -> Self { self.container = self.container.height(height); self @@ -90,6 +92,7 @@ where /// Sets the maximum width of the [`Container`]. #[must_use] + #[inline] pub fn max_width(mut self, max_width: f32) -> Self { self.container = self.container.max_width(max_width); self @@ -97,6 +100,7 @@ where /// Sets the maximum height of the [`Container`] in pixels. #[must_use] + #[inline] pub fn max_height(mut self, max_height: f32) -> Self { self.container = self.container.max_height(max_height); self @@ -104,6 +108,7 @@ where /// Sets the content alignment for the horizontal axis of the [`Container`]. #[must_use] + #[inline] pub fn align_x(mut self, alignment: Alignment) -> Self { self.container = self.container.align_x(alignment); self @@ -111,6 +116,7 @@ where /// Sets the content alignment for the vertical axis of the [`Container`]. #[must_use] + #[inline] pub fn align_y(mut self, alignment: Alignment) -> Self { self.container = self.container.align_y(alignment); self @@ -118,6 +124,7 @@ where /// Centers the contents in the horizontal axis of the [`Container`]. #[must_use] + #[inline] pub fn center_x(mut self, width: Length) -> Self { self.container = self.container.center_x(width); self @@ -125,6 +132,7 @@ where /// Centers the contents in the vertical axis of the [`Container`]. #[must_use] + #[inline] pub fn center_y(mut self, height: Length) -> Self { self.container = self.container.center_y(height); self @@ -132,6 +140,7 @@ where /// Centers the contents in the horizontal and vertical axis of the [`Container`]. #[must_use] + #[inline] pub fn center(mut self, length: Length) -> Self { self.container = self.container.center(length); self diff --git a/src/widget/autosize.rs b/src/widget/autosize.rs index 3f60835..172d505 100644 --- a/src/widget/autosize.rs +++ b/src/widget/autosize.rs @@ -54,36 +54,43 @@ where } } + #[inline] pub fn limits(mut self, limits: layout::Limits) -> Self { self.limits = limits; self } + #[inline] pub fn auto_width(mut self, auto_width: bool) -> Self { self.auto_width = auto_width; self } + #[inline] pub fn auto_height(mut self, auto_height: bool) -> Self { self.auto_height = auto_height; self } + #[inline] pub fn max_width(mut self, v: f32) -> Self { self.limits = self.limits.max_width(v); self } + #[inline] pub fn max_height(mut self, v: f32) -> Self { self.limits = self.limits.max_height(v); self } + #[inline] pub fn min_width(mut self, v: f32) -> Self { self.limits = self.limits.min_width(v); self } + #[inline] pub fn min_height(mut self, v: f32) -> Self { self.limits = self.limits.min_height(v); self diff --git a/src/widget/button/icon.rs b/src/widget/button/icon.rs index 3b46d9d..0bb3c84 100644 --- a/src/widget/button/icon.rs +++ b/src/widget/button/icon.rs @@ -2,13 +2,13 @@ // SPDX-License-Identifier: MPL-2.0 use super::{Builder, ButtonClass}; +use crate::Element; use crate::widget::{ icon::{self, Handle}, tooltip, }; -use crate::Element; use apply::Apply; -use iced_core::{font::Weight, text::LineHeight, widget::Id, Alignment, Length, Padding}; +use iced_core::{Alignment, Length, Padding, font::Weight, text::LineHeight, widget::Id}; use std::borrow::Cow; pub type Button<'a, Message> = Builder<'a, Message, Icon>; @@ -114,11 +114,13 @@ impl Button<'_, Message> { self } + #[inline] pub fn selected(mut self, selected: bool) -> Self { self.variant.selected = selected; self } + #[inline] pub fn vertical(mut self, vertical: bool) -> Self { self.variant.vertical = vertical; self.class = ButtonClass::IconVertical; diff --git a/src/widget/button/image.rs b/src/widget/button/image.rs index 74e3b37..6a5c47b 100644 --- a/src/widget/button/image.rs +++ b/src/widget/button/image.rs @@ -3,10 +3,10 @@ use super::Builder; use crate::{ - widget::{self, image::Handle}, Element, + widget::{self, image::Handle}, }; -use iced_core::{font::Weight, widget::Id, Length, Padding}; +use iced_core::{Length, Padding, font::Weight, widget::Id}; use std::borrow::Cow; pub type Button<'a, Message> = Builder<'a, Message, Image<'a, Handle, Message>>; @@ -28,6 +28,7 @@ pub struct Image<'a, Handle, Message> { } impl<'a, Message> Button<'a, Message> { + #[inline] pub fn new(variant: Image<'a, Handle, Message>) -> Self { Self { id: Id::unique(), @@ -47,16 +48,19 @@ impl<'a, Message> Button<'a, Message> { } } + #[inline] pub fn on_remove(mut self, message: Message) -> Self { self.variant.on_remove = Some(message); self } + #[inline] pub fn on_remove_maybe(mut self, message: Option) -> Self { self.variant.on_remove = message; self } + #[inline] pub fn selected(mut self, selected: bool) -> Self { self.variant.selected = selected; self diff --git a/src/widget/button/link.rs b/src/widget/button/link.rs index 77527ec..dcfc6a3 100644 --- a/src/widget/button/link.rs +++ b/src/widget/button/link.rs @@ -55,6 +55,7 @@ impl<'a, Message> Button<'a, Message> { } } +#[inline(never)] pub fn icon() -> Handle { icon::from_svg_bytes(&include_bytes!("external-link.svg")[..]).symbolic(true) } diff --git a/src/widget/button/mod.rs b/src/widget/button/mod.rs index 9928628..b654b27 100644 --- a/src/widget/button/mod.rs +++ b/src/widget/button/mod.rs @@ -45,7 +45,7 @@ use std::borrow::Cow; /// A button with a custom element for its content. pub fn custom<'a, Message>(content: impl Into>) -> Button<'a, Message> { - Button::new(content) + Button::new(content.into()) } /// An image button which may contain any widget as its content. @@ -53,7 +53,7 @@ pub fn custom_image_button<'a, Message>( content: impl Into>, on_remove: Option, ) -> Button<'a, Message> { - Button::new_image(content, on_remove) + Button::new_image(content.into(), on_remove) } /// A builder for constructing a custom [`Button`]. diff --git a/src/widget/button/text.rs b/src/widget/button/text.rs index 1b68239..da5f94f 100644 --- a/src/widget/button/text.rs +++ b/src/widget/button/text.rs @@ -3,8 +3,7 @@ use super::{Builder, ButtonClass}; use crate::widget::{icon, row, tooltip}; -use crate::{ext::CollectionWidget, Element}; -use apply::Apply; +use crate::{Apply, Element}; use iced_core::{font::Weight, text::LineHeight, widget::Id, Alignment, Length, Padding}; use std::borrow::Cow; diff --git a/src/widget/button/widget.rs b/src/widget/button/widget.rs index 3b6d557..3a1df24 100644 --- a/src/widget/button/widget.rs +++ b/src/widget/button/widget.rs @@ -7,19 +7,19 @@ //! A [`Button`] has some local [`State`]. use iced_runtime::core::widget::Id; -use iced_runtime::{keyboard, task, Action, Task}; +use iced_runtime::{Action, Task, keyboard, task}; use iced_core::event::{self, Event}; use iced_core::renderer::{self, Quad, Renderer}; use iced_core::touch; -use iced_core::widget::tree::{self, Tree}; use iced_core::widget::Operation; -use iced_core::{layout, svg}; -use iced_core::{mouse, Border}; -use iced_core::{overlay, Shadow}; +use iced_core::widget::tree::{self, Tree}; use iced_core::{ Background, Clipboard, Color, Layout, Length, Padding, Point, Rectangle, Shell, Vector, Widget, }; +use iced_core::{Border, mouse}; +use iced_core::{Shadow, overlay}; +use iced_core::{layout, svg}; use iced_renderer::core::widget::operation; use crate::theme::THEME; @@ -118,24 +118,28 @@ impl<'a, Message> Button<'a, Message> { } /// Sets the [`Id`] of the [`Button`]. + #[inline] pub fn id(mut self, id: Id) -> Self { self.id = id; self } /// Sets the width of the [`Button`]. + #[inline] pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); self } /// Sets the height of the [`Button`]. + #[inline] pub fn height(mut self, height: impl Into) -> Self { self.height = height.into(); self } /// Sets the [`Padding`] of the [`Button`]. + #[inline] pub fn padding>(mut self, padding: P) -> Self { self.padding = padding.into(); self @@ -144,6 +148,7 @@ impl<'a, Message> Button<'a, Message> { /// Sets the message that will be produced when the [`Button`] is pressed and released. /// /// Unless `on_press` or `on_press_down` is called, the [`Button`] will be disabled. + #[inline] pub fn on_press(mut self, on_press: Message) -> Self { self.on_press = Some(on_press); self @@ -152,6 +157,7 @@ impl<'a, Message> Button<'a, Message> { /// Sets the message that will be produced when the [`Button`] is pressed, /// /// Unless `on_press` or `on_press_down` is called, the [`Button`] will be disabled. + #[inline] pub fn on_press_down(mut self, on_press: Message) -> Self { self.on_press_down = Some(on_press); self @@ -161,12 +167,14 @@ impl<'a, Message> Button<'a, Message> { /// if `Some`. /// /// If `None`, the [`Button`] will be disabled. + #[inline] pub fn on_press_maybe(mut self, on_press: Option) -> Self { self.on_press = on_press; self } /// Sets the the [`Button`] to enabled whether or not it has handlers for on press. + #[inline] pub fn force_enabled(mut self, enabled: bool) -> Self { self.force_enabled = enabled; self @@ -175,6 +183,7 @@ impl<'a, Message> Button<'a, Message> { /// Sets the widget to a selected state. /// /// Displays a selection indicator on image buttons. + #[inline] pub fn selected(mut self, selected: bool) -> Self { self.selected = selected; @@ -182,6 +191,7 @@ impl<'a, Message> Button<'a, Message> { } /// Sets the style variant of this [`Button`]. + #[inline] pub fn class(mut self, style: crate::theme::Button) -> Self { self.style = style; self @@ -579,8 +589,8 @@ impl<'a, Message: 'a + Clone> Widget p: mouse::Cursor, ) -> iced_accessibility::A11yTree { use iced_accessibility::{ - accesskit::{Action, DefaultActionVerb, NodeBuilder, NodeId, Rect, Role}, A11yNode, A11yTree, + accesskit::{Action, DefaultActionVerb, NodeBuilder, NodeId, Rect, Role}, }; // TODO why is state None sometimes? if matches!(state.state, iced_core::widget::tree::State::None) { @@ -668,26 +678,31 @@ pub struct State { impl State { /// Creates a new [`State`]. + #[inline] pub fn new() -> Self { Self::default() } /// Returns whether the [`Button`] is currently focused or not. + #[inline] pub fn is_focused(self) -> bool { self.is_focused } /// Returns whether the [`Button`] is currently hovered or not. + #[inline] pub fn is_hovered(self) -> bool { self.is_hovered } /// Focuses the [`Button`]. + #[inline] pub fn focus(&mut self) { self.is_focused = true; } /// Unfocuses the [`Button`]. + #[inline] pub fn unfocus(&mut self) { self.is_focused = false; } @@ -951,14 +966,17 @@ pub fn focus(id: Id) -> Task { } impl operation::Focusable for State { + #[inline] fn is_focused(&self) -> bool { Self::is_focused(*self) } + #[inline] fn focus(&mut self) { Self::focus(self); } + #[inline] fn unfocus(&mut self) { Self::unfocus(self); } diff --git a/src/widget/calendar.rs b/src/widget/calendar.rs index ea96360..046a95d 100644 --- a/src/widget/calendar.rs +++ b/src/widget/calendar.rs @@ -6,7 +6,7 @@ use std::cmp; use crate::iced_core::{Alignment, Length, Padding}; -use crate::widget::{button, column, grid, icon, row, text, Grid}; +use crate::widget::{Grid, button, column, grid, icon, row, text}; use chrono::{Datelike, Days, Local, Months, NaiveDate, Weekday}; /// A widget that displays an interactive calendar. @@ -58,6 +58,7 @@ impl CalendarModel { } } + #[inline] pub fn new(selected: NaiveDate, visible: NaiveDate) -> Self { CalendarModel { selected, visible } } @@ -80,16 +81,19 @@ impl CalendarModel { self.visible = next_month_date; } + #[inline] pub fn set_prev_month(&mut self) { self.show_prev_month(); self.selected = self.visible; } + #[inline] pub fn set_next_month(&mut self) { self.show_next_month(); self.selected = self.visible; } + #[inline] pub fn set_selected_visible(&mut self, selected: NaiveDate) { self.selected = selected; self.visible = self.selected; @@ -225,6 +229,7 @@ fn padded_control<'a, Message>( .width(Length::Fill) } +#[inline] fn menu_control_padding() -> Padding { let guard = crate::theme::THEME.lock().unwrap(); let cosmic = guard.cosmic(); diff --git a/src/widget/context_drawer/widget.rs b/src/widget/context_drawer/widget.rs index c59ae40..f36578e 100644 --- a/src/widget/context_drawer/widget.rs +++ b/src/widget/context_drawer/widget.rs @@ -3,17 +3,17 @@ use std::borrow::Cow; -use crate::widget::{button, column, container, icon, row, scrollable, text, LayerContainer}; +use crate::widget::{LayerContainer, button, column, container, icon, row, scrollable, text}; use crate::{Apply, Element, Renderer, Theme}; use super::overlay::Overlay; +use iced_core::Alignment; use iced_core::event::{self, Event}; use iced_core::widget::{Operation, Tree}; -use iced_core::Alignment; use iced_core::{ - layout, mouse, overlay as iced_overlay, renderer, Clipboard, Layout, Length, Rectangle, Shell, - Vector, Widget, + Clipboard, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse, + overlay as iced_overlay, renderer, }; #[must_use] @@ -37,76 +37,97 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> { where Drawer: Into>, { - let cosmic_theme::Spacing { - space_xxs, - space_s, - space_m, - space_l, - .. - } = crate::theme::active().cosmic().spacing; + #[inline(never)] + fn inner<'a, Message: Clone + 'static>( + title: Option>, + header_actions: Vec>, + header_opt: Option>, + footer_opt: Option>, + drawer: Element<'a, Message>, + on_close: Message, + max_width: f32, + ) -> Element<'a, Message> { + let cosmic_theme::Spacing { + space_xxs, + space_s, + space_m, + space_l, + .. + } = crate::theme::active().cosmic().spacing; - let horizontal_padding = if max_width < 392.0 { space_s } else { space_l }; + let horizontal_padding = if max_width < 392.0 { space_s } else { space_l }; - let header_row = row::with_capacity(3) - .width(Length::Fixed(480.0)) - .align_y(Alignment::Center) - .push( - row::with_children(header_actions) - .spacing(space_xxs) - .width(Length::FillPortion(1)), - ) - .push_maybe( - title.map(|title| text::heading(title).width(Length::FillPortion(1)).center()), - ) - .push( - button::text("Close") - .trailing_icon(icon::from_name("go-next-symbolic")) - .on_press(on_close) - .apply(container) - .width(Length::FillPortion(1)) - .align_x(Alignment::End), - ); - let header = column::with_capacity(2) - .width(Length::Fixed(480.0)) - .align_x(Alignment::Center) - .spacing(space_m) - .padding([space_m, horizontal_padding]) - .push(header_row) - .push_maybe(header_opt); - let footer = footer_opt.map(|element| { - container(element) + let header_row = row::with_capacity(3) .width(Length::Fixed(480.0)) .align_y(Alignment::Center) - .padding([space_xxs, horizontal_padding]) - }); - let pane = column::with_capacity(3) - .push(header) - .push( - scrollable(container(drawer.into()).padding([ - 0, - horizontal_padding, - if footer.is_some() { 0 } else { space_l }, - horizontal_padding, - ])) - .height(Length::Fill) - .width(Length::Shrink), - ) - .push_maybe(footer); + .push( + row::with_children(header_actions) + .spacing(space_xxs) + .width(Length::FillPortion(1)), + ) + .push_maybe( + title.map(|title| text::heading(title).width(Length::FillPortion(1)).center()), + ) + .push( + button::text("Close") + .trailing_icon(icon::from_name("go-next-symbolic")) + .on_press(on_close) + .apply(container) + .width(Length::FillPortion(1)) + .align_x(Alignment::End), + ); + let header = column::with_capacity(2) + .width(Length::Fixed(480.0)) + .align_x(Alignment::Center) + .spacing(space_m) + .padding([space_m, horizontal_padding]) + .push(header_row) + .push_maybe(header_opt); + let footer = footer_opt.map(|element| { + container(element) + .width(Length::Fixed(480.0)) + .align_y(Alignment::Center) + .padding([space_xxs, horizontal_padding]) + }); + let pane = column::with_capacity(3) + .push(header) + .push( + scrollable(container(drawer).padding([ + 0, + horizontal_padding, + if footer.is_some() { 0 } else { space_l }, + horizontal_padding, + ])) + .height(Length::Fill) + .width(Length::Shrink), + ) + .push_maybe(footer); - // XXX new limits do not exactly handle the max width well for containers - // XXX this is a hack to get around that - container( - LayerContainer::new(pane) - .layer(cosmic_theme::Layer::Primary) - .class(crate::style::Container::ContextDrawer) - .width(Length::Fill) - .height(Length::Fill) - .max_width(max_width), + // XXX new limits do not exactly handle the max width well for containers + // XXX this is a hack to get around that + container( + LayerContainer::new(pane) + .layer(cosmic_theme::Layer::Primary) + .class(crate::style::Container::ContextDrawer) + .width(Length::Fill) + .height(Length::Fill) + .max_width(max_width), + ) + .width(Length::Fill) + .height(Length::Fill) + .align_x(Alignment::End) + .into() + } + + inner( + title, + header_actions, + header_opt, + footer_opt, + drawer.into(), + on_close, + max_width, ) - .width(Length::Fill) - .height(Length::Fill) - .align_x(Alignment::End) - .into() } /// Creates an empty [`ContextDrawer`]. @@ -149,6 +170,7 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> { } // Optionally assigns message to `on_close` event. + #[inline] pub fn on_close_maybe(mut self, message: Option) -> Self { self.on_close = message; self diff --git a/src/widget/flex_row/widget.rs b/src/widget/flex_row/widget.rs index e343757..264201c 100644 --- a/src/widget/flex_row/widget.rs +++ b/src/widget/flex_row/widget.rs @@ -6,8 +6,8 @@ use derive_setters::Setters; use iced_core::event::{self, Event}; use iced_core::widget::{Operation, Tree}; use iced_core::{ - layout, mouse, overlay, renderer, Clipboard, Layout, Length, Padding, Rectangle, Shell, Vector, - Widget, + Clipboard, Layout, Length, Padding, Rectangle, Shell, Vector, Widget, layout, mouse, overlay, + renderer, }; /// Responsively generates rows and columns of widgets based on its dimensions. @@ -78,6 +78,7 @@ impl<'a, Message> FlexRow<'a, Message> { } /// Sets the space between each column and row. + #[inline] pub const fn spacing(mut self, spacing: u16) -> Self { self.column_spacing = spacing; self.row_spacing = spacing; diff --git a/src/widget/frames.rs b/src/widget/frames.rs index 7b6783c..1c379ac 100644 --- a/src/widget/frames.rs +++ b/src/widget/frames.rs @@ -10,17 +10,17 @@ use std::time::{Duration, Instant}; use ::image as image_rs; use iced_core::image::Renderer as ImageRenderer; use iced_core::mouse::Cursor; -use iced_core::widget::{tree, Tree}; +use iced_core::widget::{Tree, tree}; use iced_core::{ - event, layout, renderer, window, Clipboard, ContentFit, Element, Event, Layout, Length, - Rectangle, Shell, Size, Vector, Widget, + Clipboard, ContentFit, Element, Event, Layout, Length, Rectangle, Shell, Size, Vector, Widget, + event, layout, renderer, window, }; use iced_runtime::Command; use iced_widget::image::{self, Handle}; +use image_rs::AnimationDecoder; use image_rs::codecs::gif::GifDecoder; use image_rs::codecs::png::PngDecoder; use image_rs::codecs::webp::WebPDecoder; -use image_rs::AnimationDecoder; #[cfg(not(feature = "tokio"))] use iced_futures::futures::{AsyncRead, AsyncReadExt}; @@ -61,6 +61,7 @@ pub struct Frames { } impl fmt::Debug for Frames { + #[cold] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Frames").finish() } @@ -95,31 +96,36 @@ impl Frames { /// Load [`Frames`] from the supplied path pub fn load_from_path(path: impl AsRef) -> Command> { - #[cfg(feature = "tokio")] - use tokio::fs::File; - #[cfg(feature = "tokio")] - use tokio::io::BufReader; + #[inline(never)] + fn inner(path: &Path) -> Command> { + #[cfg(feature = "tokio")] + use tokio::fs::File; + #[cfg(feature = "tokio")] + use tokio::io::BufReader; - #[cfg(not(feature = "tokio"))] - use async_fs::File; - #[cfg(not(feature = "tokio"))] - use iced_futures::futures::io::BufReader; + #[cfg(not(feature = "tokio"))] + use async_fs::File; + #[cfg(not(feature = "tokio"))] + use iced_futures::futures::io::BufReader; - let path = path.as_ref().to_path_buf(); + let path = path.as_ref().to_path_buf(); - let f = async move { - let image_type = match &path.extension() { - Some(ext) if ext == &OsStr::new("gif") => ImageType::Gif, - Some(ext) if ext == &OsStr::new("apng") => ImageType::Apng, - Some(ext) if ext == &OsStr::new("webp") => ImageType::WebP, - _ => return Err(Error::Extension), + let f = async move { + let image_type = match &path.extension() { + Some(ext) if ext == &OsStr::new("gif") => ImageType::Gif, + Some(ext) if ext == &OsStr::new("apng") => ImageType::Apng, + Some(ext) if ext == &OsStr::new("webp") => ImageType::WebP, + _ => return Err(Error::Extension), + }; + let reader = BufReader::new(File::open(path).await?); + + Self::from_reader(reader, image_type).await }; - let reader = BufReader::new(File::open(path).await?); - Self::from_reader(reader, image_type).await - }; + Command::perform(f, std::convert::identity) + } - Command::perform(f, std::convert::identity) + inner(path.as_ref()) } /// Decode [`Frames`] from the supplied async reader diff --git a/src/widget/grid/widget.rs b/src/widget/grid/widget.rs index f09b373..8e9efc7 100644 --- a/src/widget/grid/widget.rs +++ b/src/widget/grid/widget.rs @@ -105,6 +105,7 @@ impl<'a, Message> Grid<'a, Message> { self } + #[inline] pub fn insert_row(mut self) -> Self { self.row += 1; self.column = 1; diff --git a/src/widget/header_bar.rs b/src/widget/header_bar.rs index 32bfa27..638bff5 100644 --- a/src/widget/header_bar.rs +++ b/src/widget/header_bar.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: MPL-2.0 use crate::cosmic_theme::{Density, Spacing}; -use crate::{theme, widget, Element}; +use crate::{Element, theme, widget}; use apply::Apply; use derive_setters::Setters; use iced::Length; -use iced_core::{widget::tree, Vector, Widget}; +use iced_core::{Vector, Widget, widget::tree}; use std::borrow::Cow; #[must_use] @@ -109,6 +109,7 @@ impl<'a, Message: Clone + 'static> HeaderBar<'a, Message> { /// Build the widget #[must_use] + #[inline] pub fn build(self) -> HeaderBarWidget<'a, Message> { HeaderBarWidget { header_bar_inner: self.view(), diff --git a/src/widget/icon/handle.rs b/src/widget/icon/handle.rs index 3a2db2c..2d30427 100644 --- a/src/widget/icon/handle.rs +++ b/src/widget/icon/handle.rs @@ -17,6 +17,7 @@ pub struct Handle { } impl Handle { + #[inline] pub fn icon(self) -> Icon { super::icon(self) } @@ -53,13 +54,17 @@ pub fn from_raster_bytes( + std::marker::Sync + 'static, ) -> Handle { - Handle { - symbolic: false, - data: match bytes.into() { - Cow::Owned(b) => Data::Image(image::Handle::from_bytes(b)), - Cow::Borrowed(b) => Data::Image(image::Handle::from_bytes(b)), - }, + fn inner(bytes: Cow<'static, [u8]>) -> Handle { + Handle { + symbolic: false, + data: match bytes { + Cow::Owned(b) => Data::Image(image::Handle::from_bytes(b)), + Cow::Borrowed(b) => Data::Image(image::Handle::from_bytes(b)), + }, + } } + + inner(bytes.into()) } /// Create an image handle from RGBA data, where you must define the width and height. @@ -71,13 +76,19 @@ pub fn from_raster_pixels( + std::marker::Send + std::marker::Sync, ) -> Handle { - Handle { - symbolic: false, - data: match pixels.into() { - Cow::Owned(pixels) => Data::Image(image::Handle::from_rgba(width, height, pixels)), - Cow::Borrowed(pixels) => Data::Image(image::Handle::from_rgba(width, height, pixels)), - }, + fn inner(width: u32, height: u32, pixels: Cow<'static, [u8]>) -> Handle { + Handle { + symbolic: false, + data: match pixels { + Cow::Owned(pixels) => Data::Image(image::Handle::from_rgba(width, height, pixels)), + Cow::Borrowed(pixels) => { + Data::Image(image::Handle::from_rgba(width, height, pixels)) + } + }, + } } + + inner(width, height, pixels.into()) } /// Create a SVG handle from memory. diff --git a/src/widget/icon/named.rs b/src/widget/icon/named.rs index 055b2e4..da5c467 100644 --- a/src/widget/icon/named.rs +++ b/src/widget/icon/named.rs @@ -113,6 +113,7 @@ impl Named { None } + #[inline] pub fn handle(self) -> Handle { Handle { symbolic: self.symbolic, @@ -120,6 +121,7 @@ impl Named { } } + #[inline] pub fn icon(self) -> Icon { let size = self.size; @@ -133,18 +135,21 @@ impl Named { } impl From for Handle { + #[inline] fn from(builder: Named) -> Self { builder.handle() } } impl From for Icon { + #[inline] fn from(builder: Named) -> Self { builder.icon() } } impl From for crate::Element<'_, Message> { + #[inline] fn from(builder: Named) -> Self { builder.icon().into() } diff --git a/src/widget/layer_container.rs b/src/widget/layer_container.rs index f442bc5..74521b3 100644 --- a/src/widget/layer_container.rs +++ b/src/widget/layer_container.rs @@ -69,6 +69,7 @@ where /// Sets the width of the [`self.`]. #[must_use] + #[inline] pub fn width(mut self, width: Length) -> Self { self.container = self.container.width(width); self @@ -76,6 +77,7 @@ where /// Sets the height of the [`LayerContainer`]. #[must_use] + #[inline] pub fn height(mut self, height: Length) -> Self { self.container = self.container.height(height); self @@ -83,6 +85,7 @@ where /// Sets the maximum width of the [`LayerContainer`]. #[must_use] + #[inline] pub fn max_width(mut self, max_width: f32) -> Self { self.container = self.container.max_width(max_width); self @@ -90,6 +93,7 @@ where /// Sets the maximum height of the [`LayerContainer`] in pixels. #[must_use] + #[inline] pub fn max_height(mut self, max_height: f32) -> Self { self.container = self.container.max_height(max_height); self @@ -97,6 +101,7 @@ where /// Sets the content alignment for the horizontal axis of the [`LayerContainer`]. #[must_use] + #[inline] pub fn align_x(mut self, alignment: Alignment) -> Self { self.container = self.container.align_x(alignment); self @@ -104,6 +109,7 @@ where /// Sets the content alignment for the vertical axis of the [`LayerContainer`]. #[must_use] + #[inline] pub fn align_y(mut self, alignment: Alignment) -> Self { self.container = self.container.align_y(alignment); self @@ -111,6 +117,7 @@ where /// Centers the contents in the horizontal axis of the [`LayerContainer`]. #[must_use] + #[inline] pub fn center_x(mut self, width: Length) -> Self { self.container = self.container.center_x(width); self @@ -118,6 +125,7 @@ where /// Centers the contents in the vertical axis of the [`LayerContainer`]. #[must_use] + #[inline] pub fn center_y(mut self, height: Length) -> Self { self.container = self.container.center_y(height); self @@ -125,6 +133,7 @@ where /// Centers the contents in the horizontal and vertical axis of the [`Container`]. #[must_use] + #[inline] pub fn center(mut self, length: Length) -> Self { self.container = self.container.center(length); self diff --git a/src/widget/list/column.rs b/src/widget/list/column.rs index 1a9b734..d79d520 100644 --- a/src/widget/list/column.rs +++ b/src/widget/list/column.rs @@ -5,11 +5,11 @@ use iced_core::Padding; use iced_widget::container::Catalog; use crate::{ - theme, + Apply, Element, theme, widget::{container, divider, vertical_space}, - Apply, Element, }; +#[inline] pub fn list_column<'a, Message: 'static>() -> ListColumn<'a, Message> { ListColumn::default() } @@ -42,48 +42,61 @@ impl Default for ListColumn<'_, Message> { } impl<'a, Message: 'static> ListColumn<'a, Message> { + #[inline] pub fn new() -> Self { Self::default() } #[allow(clippy::should_implement_trait)] - pub fn add(mut self, item: impl Into>) -> Self { - if !self.children.is_empty() { - self.children.push( - container(divider::horizontal::default()) - .padding([0, self.divider_padding]) - .into(), - ); + pub fn add(self, item: impl Into>) -> Self { + #[inline(never)] + fn inner<'a, Message: 'static>( + mut this: ListColumn<'a, Message>, + item: Element<'a, Message>, + ) -> ListColumn<'a, Message> { + if !this.children.is_empty() { + this.children.push( + container(divider::horizontal::default()) + .padding([0, this.divider_padding]) + .into(), + ); + } + + // Ensure a minimum height of 32. + let list_item = iced::widget::row![ + container(item).align_y(iced::Alignment::Center), + vertical_space().height(iced::Length::Fixed(32.)) + ] + .padding(this.list_item_padding) + .align_y(iced::Alignment::Center); + + this.children.push(list_item.into()); + this } - // Ensure a minimum height of 32. - let list_item = iced::widget::row![ - container(item).align_y(iced::Alignment::Center), - vertical_space().height(iced::Length::Fixed(32.)) - ] - .padding(self.list_item_padding) - .align_y(iced::Alignment::Center); - - self.children.push(list_item.into()); - self + inner(self, item.into()) } + #[inline] pub fn spacing(mut self, spacing: u16) -> Self { self.spacing = spacing; self } /// Sets the style variant of this [`Circular`]. + #[inline] pub fn style(mut self, style: ::Class<'a>) -> Self { self.style = style; self } + #[inline] pub fn padding(mut self, padding: impl Into) -> Self { self.padding = padding.into(); self } + #[inline] pub fn divider_padding(mut self, padding: u16) -> Self { self.divider_padding = padding; self diff --git a/src/widget/nav_bar.rs b/src/widget/nav_bar.rs index 5a32b4a..fef3cbe 100644 --- a/src/widget/nav_bar.rs +++ b/src/widget/nav_bar.rs @@ -7,13 +7,13 @@ use apply::Apply; use iced::{ - clipboard::{dnd::DndAction, mime::AllowedMimeTypes}, Background, Length, + clipboard::{dnd::DndAction, mime::AllowedMimeTypes}, }; use iced_core::{Border, Color, Shadow}; -use crate::widget::{container, menu, scrollable, segmented_button, Container, Icon}; -use crate::{theme, Theme}; +use crate::widget::{Container, Icon, container, menu, scrollable, segmented_button}; +use crate::{Theme, theme}; use super::dnd_destination::DragId; @@ -62,16 +62,19 @@ pub struct NavBar<'a, Message> { } impl<'a, Message: Clone + 'static> NavBar<'a, Message> { + #[inline] pub fn close_icon(mut self, close_icon: Icon) -> Self { self.segmented_button = self.segmented_button.close_icon(close_icon); self } + #[inline] pub fn context_menu(mut self, context_menu: Option>>) -> Self { self.segmented_button = self.segmented_button.context_menu(context_menu); self } + #[inline] pub fn drag_id(mut self, id: DragId) -> Self { self.segmented_button = self.segmented_button.drag_id(id); self @@ -79,6 +82,7 @@ impl<'a, Message: Clone + 'static> NavBar<'a, Message> { /// Pre-convert this widget into the [`Container`] widget that it becomes. #[must_use] + #[inline] pub fn into_container(self) -> Container<'a, Message, crate::Theme, crate::Renderer> { Container::from(self) } diff --git a/src/widget/nav_bar_toggle.rs b/src/widget/nav_bar_toggle.rs index 2a31568..dd4b788 100644 --- a/src/widget/nav_bar_toggle.rs +++ b/src/widget/nav_bar_toggle.rs @@ -16,7 +16,7 @@ pub struct NavBarToggle { } #[must_use] -pub fn nav_bar_toggle() -> NavBarToggle { +pub const fn nav_bar_toggle() -> NavBarToggle { NavBarToggle { active: false, on_toggle: None, diff --git a/src/widget/rectangle_tracker/subscription.rs b/src/widget/rectangle_tracker/subscription.rs index e49946d..541862c 100644 --- a/src/widget/rectangle_tracker/subscription.rs +++ b/src/widget/rectangle_tracker/subscription.rs @@ -1,15 +1,17 @@ use iced::{ - futures::{ - channel::mpsc::{unbounded, UnboundedReceiver}, - stream, StreamExt, - }, Rectangle, + futures::{ + StreamExt, + channel::mpsc::{UnboundedReceiver, unbounded}, + stream, + }, }; use iced_futures::Subscription; use std::{collections::HashMap, fmt::Debug, hash::Hash}; use super::RectangleTracker; +#[cold] pub fn rectangle_tracker_subscription< I: 'static + Hash + Copy + Send + Sync + Debug, R: 'static + Hash + Copy + Send + Sync + Debug + Eq, diff --git a/src/widget/segmented_button/model/builder.rs b/src/widget/segmented_button/model/builder.rs index c7e3239..d8070aa 100644 --- a/src/widget/segmented_button/model/builder.rs +++ b/src/widget/segmented_button/model/builder.rs @@ -32,6 +32,7 @@ where } /// Consumes the builder and returns the model. + #[inline] pub fn build(self) -> Model { self.0 } @@ -43,6 +44,7 @@ where { /// Activates the newly-inserted item. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn activate(mut self) -> Self { self.model.0.activate(self.id); self @@ -50,6 +52,7 @@ where /// Defines that the close button should appear #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn closable(mut self) -> Self { self.model.0.closable_set(self.id, true); self @@ -60,6 +63,7 @@ where /// The secondary map internally uses a `Vec`, so should only be used for data that /// is commonly associated. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn secondary(self, map: &mut SecondaryMap, data: Data) -> Self { map.insert(self.id, data); self @@ -69,6 +73,7 @@ where /// /// Sparse maps internally use a `HashMap`, for data that is sparsely associated. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn secondary_sparse( self, map: &mut SparseSecondaryMap, @@ -90,11 +95,13 @@ where /// .build() /// ``` #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn data(mut self, data: Data) -> Self { self.model.0.data_set(self.id, data); self } + #[inline] pub fn divider_above(mut self) -> Self { self.model.0.divider_above_set(self.id, true); self @@ -115,6 +122,7 @@ where /// Define the position of the newly-inserted item. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn position(mut self, position: u16) -> Self { self.model.0.position_set(self.id, position); self @@ -122,6 +130,7 @@ where /// Swap the position with another item in the model. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn position_swap(mut self, other: Entity) -> Self { self.model.0.position_swap(self.id, other); self diff --git a/src/widget/segmented_button/model/entity.rs b/src/widget/segmented_button/model/entity.rs index 02a7d37..a382124 100644 --- a/src/widget/segmented_button/model/entity.rs +++ b/src/widget/segmented_button/model/entity.rs @@ -25,6 +25,7 @@ where /// model.insert().text("Item A").activate(); /// ``` #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn activate(self) -> Self { self.model.activate(self.id); self @@ -40,6 +41,7 @@ where /// model.insert().text("Item A").secondary(&mut secondary_data, String::new("custom data")); /// ``` #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn secondary(self, map: &mut SecondaryMap, data: Data) -> Self { map.insert(self.id, data); self @@ -54,6 +56,7 @@ where /// model.insert().text("Item A").secondary(&mut secondary_data, String::new("custom data")); /// ``` #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn secondary_sparse( self, map: &mut SparseSecondaryMap, @@ -65,6 +68,7 @@ where /// Shows a close button for this item. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn closable(self) -> Self { self.model.closable_set(self.id, true); self @@ -78,12 +82,14 @@ where /// model.insert().text("Item A").data(String::from("custom string")); /// ``` #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn data(self, data: Data) -> Self { self.model.data_set(self.id, data); self } #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn divider_above(self, divider_above: bool) -> Self { self.model.divider_above_set(self.id, divider_above); self @@ -95,6 +101,7 @@ where /// model.insert().text("Item A").icon(IconSource::from("icon-a")); /// ``` #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn icon(self, icon: impl Into) -> Self { self.model.icon_set(self.id, icon.into()); self @@ -106,11 +113,13 @@ where /// let id = model.insert("Item A").id(); /// ``` #[must_use] - pub fn id(self) -> Entity { + #[inline] + pub const fn id(self) -> Entity { self.id } #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn indent(self, indent: u16) -> Self { self.model.indent_set(self.id, indent); self @@ -118,6 +127,7 @@ where /// Define the position of the item. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn position(self, position: u16) -> Self { self.model.position_set(self.id, position); self @@ -125,6 +135,7 @@ where /// Swap the position with another item in the model. #[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)] + #[inline] pub fn position_swap(self, other: Entity) -> Self { self.model.position_swap(self.id, other); self diff --git a/src/widget/segmented_button/model/mod.rs b/src/widget/segmented_button/model/mod.rs index c24fe85..83a1702 100644 --- a/src/widget/segmented_button/model/mod.rs +++ b/src/widget/segmented_button/model/mod.rs @@ -89,11 +89,13 @@ where /// ```ignore /// model.activate(id); /// ``` + #[inline] pub fn activate(&mut self, id: Entity) { Selectable::activate(self, id); } /// Activates the item at the given position, returning true if it was activated. + #[inline] pub fn activate_position(&mut self, position: u16) -> bool { if let Some(entity) = self.entity_at(position) { self.activate(entity); @@ -113,6 +115,7 @@ where /// .build(); /// ``` #[must_use] + #[inline] pub fn builder() -> ModelBuilder { ModelBuilder::default() } @@ -126,6 +129,7 @@ where /// ```ignore /// model.clear(); /// ``` + #[inline] pub fn clear(&mut self) { for entity in self.order.clone() { self.remove(entity); @@ -133,6 +137,7 @@ where } /// Shows or hides the item's close button. + #[inline] pub fn closable_set(&mut self, id: Entity, closable: bool) { if let Some(settings) = self.items.get_mut(id) { settings.closable = closable; @@ -146,6 +151,7 @@ where /// println!("ID is still valid"); /// } /// ``` + #[inline] pub fn contains_item(&self, id: Entity) -> bool { self.items.contains_key(id) } @@ -203,6 +209,7 @@ where .and_then(|storage| storage.remove(id)); } + #[inline] pub fn divider_above(&self, id: Entity) -> Option { self.divider_aboves.get(id).copied() } @@ -215,6 +222,7 @@ where self.divider_aboves.insert(id, divider_above) } + #[inline] pub fn divider_above_remove(&mut self, id: Entity) -> Option { self.divider_aboves.remove(id) } @@ -224,6 +232,7 @@ where /// ```ignore /// model.enable(id, true); /// ``` + #[inline] pub fn enable(&mut self, id: Entity, enable: bool) { if let Some(e) = self.items.get_mut(id) { e.enabled = enable; @@ -232,6 +241,7 @@ where /// Get the item that is located at a given position. #[must_use] + #[inline] pub fn entity_at(&mut self, position: u16) -> Option { self.order.get(position as usize).copied() } @@ -243,6 +253,7 @@ where /// println!("has icon: {:?}", icon); /// } /// ``` + #[inline] pub fn icon(&self, id: Entity) -> Option<&Icon> { self.icons.get(id) } @@ -254,6 +265,7 @@ where /// println!("previously had icon: {:?}", old_icon); /// } /// ``` + #[inline] pub fn icon_set(&mut self, id: Entity, icon: Icon) -> Option { if !self.contains_item(id) { return None; @@ -268,6 +280,7 @@ where /// if let Some(old_icon) = model.icon_remove(id) { /// println!("previously had icon: {:?}", old_icon); /// } + #[inline] pub fn icon_remove(&mut self, id: Entity) -> Option { self.icons.remove(id) } @@ -278,6 +291,7 @@ where /// let id = model.insert().text("Item A").icon("custom-icon").id(); /// ``` #[must_use] + #[inline] pub fn insert(&mut self) -> EntityMut { let id = self.items.insert(Settings::default()); self.order.push_back(id); @@ -286,14 +300,16 @@ where /// Check if the given ID is the active ID. #[must_use] + #[inline] pub fn is_active(&self, id: Entity) -> bool { ::is_active(self, id) } /// Whether the item should contain a close button. #[must_use] + #[inline] pub fn is_closable(&self, id: Entity) -> bool { - self.items.get(id).map_or(false, |e| e.closable) + self.items.get(id).map(|e| e.closable).unwrap_or_default() } /// Check if the item is enabled. @@ -306,11 +322,13 @@ where /// } /// ``` #[must_use] + #[inline] pub fn is_enabled(&self, id: Entity) -> bool { - self.items.get(id).map_or(false, |e| e.enabled) + self.items.get(id).map(|e| e.enabled).unwrap_or_default() } /// Get number of items in the model. + #[inline] pub fn len(&self) -> usize { self.order.len() } @@ -320,10 +338,12 @@ where self.order.iter().copied() } + #[inline] pub fn indent(&self, id: Entity) -> Option { self.indents.get(id).copied() } + #[inline] pub fn indent_set(&mut self, id: Entity, indent: u16) -> Option { if !self.contains_item(id) { return None; @@ -332,6 +352,7 @@ where self.indents.insert(id, indent) } + #[inline] pub fn indent_remove(&mut self, id: Entity) -> Option { self.indents.remove(id) } @@ -343,6 +364,7 @@ where /// println!("found item at {}", position); /// } #[must_use] + #[inline] pub fn position(&self, id: Entity) -> Option { #[allow(clippy::cast_possible_truncation)] self.order.iter().position(|k| *k == id).map(|v| v as u16) @@ -356,9 +378,7 @@ where /// } /// ``` pub fn position_set(&mut self, id: Entity, position: u16) -> Option { - let Some(index) = self.position(id) else { - return None; - }; + let index = self.position(id)?; self.order.remove(index as usize); @@ -415,6 +435,7 @@ where /// println!("{:?} has text {text}", id); /// } /// ``` + #[inline] pub fn text(&self, id: Entity) -> Option<&str> { self.text.get(id).map(Cow::as_ref) } @@ -439,6 +460,7 @@ where /// if let Some(old_text) = model.text_remove(id) { /// println!("{:?} had text {}", id, old_text); /// } + #[inline] pub fn text_remove(&mut self, id: Entity) -> Option> { self.text.remove(id) } diff --git a/src/widget/segmented_button/model/selection.rs b/src/widget/segmented_button/model/selection.rs index 1366c18..c092765 100644 --- a/src/widget/segmented_button/model/selection.rs +++ b/src/widget/segmented_button/model/selection.rs @@ -39,6 +39,7 @@ impl Selectable for Model { } } + #[inline] fn is_active(&self, id: Entity) -> bool { self.selection.active == id } @@ -47,23 +48,27 @@ impl Selectable for Model { impl Model { /// Get an immutable reference to the data associated with the active item. #[must_use] + #[inline] pub fn active_data(&self) -> Option<&Data> { self.data(self.active()) } /// Get a mutable reference to the data associated with the active item. #[must_use] + #[inline] pub fn active_data_mut(&mut self) -> Option<&mut Data> { self.data_mut(self.active()) } /// Deactivates the active item. + #[inline] pub fn deactivate(&mut self) { Selectable::deactivate(self, Entity::default()); } /// The ID of the active item. #[must_use] + #[inline] pub fn active(&self) -> Entity { self.selection.active } @@ -86,10 +91,12 @@ impl Selectable for Model { } } + #[inline] fn deactivate(&mut self, id: Entity) { self.selection.active.remove(&id); } + #[inline] fn is_active(&self, id: Entity) -> bool { self.selection.active.contains(&id) } @@ -97,11 +104,13 @@ impl Selectable for Model { impl Model { /// Deactivates the item in the model. + #[inline] pub fn deactivate(&mut self, id: Entity) { Selectable::deactivate(self, id); } /// The IDs of the active items. + #[inline] pub fn active(&self) -> impl Iterator + '_ { self.selection.active.iter().copied() } diff --git a/src/widget/segmented_button/widget.rs b/src/widget/segmented_button/widget.rs index 9bdc9ad..2a05d44 100644 --- a/src/widget/segmented_button/widget.rs +++ b/src/widget/segmented_button/widget.rs @@ -20,7 +20,7 @@ use iced::{ event, keyboard, mouse, touch, }; use iced_core::mouse::ScrollDelta; -use iced_core::text::{LineHeight, Paragraph, Renderer as TextRenderer, Shaping, Wrapping}; +use iced_core::text::{LineHeight, Renderer as TextRenderer, Shaping, Wrapping}; use iced_core::widget::{self, operation, tree}; use iced_core::{Border, Gradient, Point, Renderer as IcedRenderer, Shadow, Text}; use iced_core::{Clipboard, Layout, Shell, Widget, layout, renderer, widget::Tree}; @@ -158,6 +158,7 @@ where Model: Selectable, SelectionMode: Default, { + #[inline] pub fn new(model: &'a Model) -> Self { Self { model, @@ -1726,6 +1727,7 @@ impl Id { /// /// This function produces a different [`Id`] every time it is called. #[must_use] + #[inline] pub fn unique() -> Self { Self(widget::Id::unique()) } diff --git a/src/widget/settings/item.rs b/src/widget/settings/item.rs index 36cc555..0a5406f 100644 --- a/src/widget/settings/item.rs +++ b/src/widget/settings/item.rs @@ -19,11 +19,19 @@ pub fn item<'a, Message: 'static>( title: impl Into> + 'a, widget: impl Into> + 'a, ) -> Row<'a, Message> { - item_row(vec![ - text(title).wrapping(Wrapping::Word).into(), - horizontal_space().into(), - widget.into(), - ]) + #[inline(never)] + fn inner<'a, Message: 'static>( + title: Cow<'a, str>, + widget: Element<'a, Message>, + ) -> Row<'a, Message> { + item_row(vec![ + text(title).wrapping(Wrapping::Word).into(), + horizontal_space().into(), + widget, + ]) + } + + inner(title.into(), widget.into()) } /// A settings item aligned in a row @@ -41,13 +49,21 @@ pub fn flex_item<'a, Message: 'static>( title: impl Into> + 'a, widget: impl Into> + 'a, ) -> FlexRow<'a, Message> { - flex_item_row(vec![ - text(title) - .wrapping(Wrapping::Word) - .width(Length::Fill) - .into(), - container(widget).into(), - ]) + #[inline(never)] + fn inner<'a, Message: 'static>( + title: Cow<'a, str>, + widget: Element<'a, Message>, + ) -> FlexRow<'a, Message> { + flex_item_row(vec![ + text(title) + .wrapping(Wrapping::Word) + .width(Length::Fill) + .into(), + container(widget).into(), + ]) + } + + inner(title.into(), widget.into()) } /// A settings item aligned in a flex row @@ -88,15 +104,16 @@ pub struct Item<'a, Message> { impl<'a, Message: 'static> Item<'a, Message> { /// Assigns a control to the item. pub fn control(self, widget: impl Into>) -> Row<'a, Message> { - item_row(self.control_(widget)) + item_row(self.control_(widget.into())) } /// Assigns a control which flexes. pub fn flex_control(self, widget: impl Into>) -> FlexRow<'a, Message> { - flex_item_row(self.control_(widget)) + flex_item_row(self.control_(widget.into())) } - fn control_(self, widget: impl Into>) -> Vec> { + #[inline(never)] + fn control_(self, widget: Element<'a, Message>) -> Vec> { let mut contents = Vec::with_capacity(4); if let Some(icon) = self.icon { diff --git a/src/widget/text.rs b/src/widget/text.rs index b542f2e..37e85b8 100644 --- a/src/widget/text.rs +++ b/src/widget/text.rs @@ -26,72 +26,117 @@ pub enum Typography { /// [`Text`] widget with the Title 1 typography preset. pub fn title1<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(35.0) - .line_height(LineHeight::Absolute(52.0.into())) - .font(crate::font::semibold()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(35.0) + .line_height(LineHeight::Absolute(52.0.into())) + .font(crate::font::semibold()) + } + + inner(text.into()) } /// [`Text`] widget with the Title 2 typography preset. pub fn title2<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(29.0) - .line_height(LineHeight::Absolute(43.0.into())) - .font(crate::font::semibold()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(29.0) + .line_height(LineHeight::Absolute(43.0.into())) + .font(crate::font::semibold()) + } + + inner(text.into()) } /// [`Text`] widget with the Title 3 typography preset. pub fn title3<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(24.0) - .line_height(LineHeight::Absolute(36.0.into())) - .font(crate::font::bold()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(24.0) + .line_height(LineHeight::Absolute(36.0.into())) + .font(crate::font::bold()) + } + + inner(text.into()) } /// [`Text`] widget with the Title 4 typography preset. pub fn title4<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(20.0) - .line_height(LineHeight::Absolute(30.0.into())) - .font(crate::font::bold()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(20.0) + .line_height(LineHeight::Absolute(30.0.into())) + .font(crate::font::bold()) + } + + inner(text.into()) } /// [`Text`] widget with the Heading typography preset. pub fn heading<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(14.0) - .line_height(LineHeight::Absolute(iced::Pixels(21.0))) - .font(crate::font::bold()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(14.0) + .line_height(LineHeight::Absolute(iced::Pixels(21.0))) + .font(crate::font::bold()) + } + + inner(text.into()) } /// [`Text`] widget with the Caption Heading typography preset. pub fn caption_heading<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(12.0) - .line_height(LineHeight::Absolute(iced::Pixels(17.0))) - .font(crate::font::semibold()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(12.0) + .line_height(LineHeight::Absolute(iced::Pixels(17.0))) + .font(crate::font::semibold()) + } + + inner(text.into()) } /// [`Text`] widget with the Body typography preset. pub fn body<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(14.0) - .line_height(LineHeight::Absolute(21.0.into())) - .font(crate::font::default()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(14.0) + .line_height(LineHeight::Absolute(21.0.into())) + .font(crate::font::default()) + } + + inner(text.into()) } /// [`Text`] widget with the Caption typography preset. pub fn caption<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(12.0) - .line_height(LineHeight::Absolute(17.0.into())) - .font(crate::font::default()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(12.0) + .line_height(LineHeight::Absolute(17.0.into())) + .font(crate::font::default()) + } + + inner(text.into()) } /// [`Text`] widget with the Monotext typography preset. pub fn monotext<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text.into()) - .size(14.0) - .line_height(LineHeight::Absolute(20.0.into())) - .font(crate::font::mono()) + #[inline(never)] + fn inner(text: Cow) -> Text { + Text::new(text) + .size(14.0) + .line_height(LineHeight::Absolute(20.0.into())) + .font(crate::font::mono()) + } + + inner(text.into()) } diff --git a/src/widget/toaster/mod.rs b/src/widget/toaster/mod.rs index 1acbce0..efd93a9 100644 --- a/src/widget/toaster/mod.rs +++ b/src/widget/toaster/mod.rs @@ -6,16 +6,14 @@ use std::collections::VecDeque; use std::rc::Rc; -use crate::widget::container; use crate::widget::Column; +use crate::widget::container; use iced::Task; use iced_core::Element; -use slotmap::new_key_type; use slotmap::SlotMap; +use slotmap::new_key_type; use widget::Toaster; -use crate::ext::CollectionWidget; - use super::column; use super::{button, icon, row, text}; @@ -106,6 +104,7 @@ pub struct Action { } impl std::fmt::Debug for Action { + #[cold] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Action") .field("description", &self.description)