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.
This commit is contained in:
Michael Aaron Murphy 2025-03-21 03:17:59 +01:00
parent c538d672df
commit 8cf372c9b9
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
55 changed files with 702 additions and 255 deletions

View file

@ -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

View file

@ -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

View file

@ -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<Message> 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;

View file

@ -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<Message>) -> Self {
self.variant.on_remove = message;
self
}
#[inline]
pub fn selected(mut self, selected: bool) -> Self {
self.variant.selected = selected;
self

View file

@ -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)
}

View file

@ -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<crate::Element<'a, Message>>) -> 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<crate::Element<'a, Message>>,
on_remove: Option<Message>,
) -> Button<'a, Message> {
Button::new_image(content, on_remove)
Button::new_image(content.into(), on_remove)
}
/// A builder for constructing a custom [`Button`].

View file

@ -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;

View file

@ -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<Length>) -> Self {
self.width = width.into();
self
}
/// Sets the height of the [`Button`].
#[inline]
pub fn height(mut self, height: impl Into<Length>) -> Self {
self.height = height.into();
self
}
/// Sets the [`Padding`] of the [`Button`].
#[inline]
pub fn padding<P: Into<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<Message>) -> 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<Message, crate::Theme, crate::Renderer>
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<Message: 'static>(id: Id) -> Task<Message> {
}
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);
}

View file

@ -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();

View file

@ -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<Element<'a, Message>>,
{
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<Cow<'a, str>>,
header_actions: Vec<Element<'a, Message>>,
header_opt: Option<Element<'a, Message>>,
footer_opt: Option<Element<'a, Message>>,
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<Message>) -> Self {
self.on_close = message;
self

View file

@ -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;

View file

@ -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<Path>) -> Command<Result<Frames, Error>> {
#[cfg(feature = "tokio")]
use tokio::fs::File;
#[cfg(feature = "tokio")]
use tokio::io::BufReader;
#[inline(never)]
fn inner(path: &Path) -> Command<Result<Frames, Error>> {
#[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

View file

@ -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;

View file

@ -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(),

View file

@ -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.

View file

@ -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<Named> for Handle {
#[inline]
fn from(builder: Named) -> Self {
builder.handle()
}
}
impl From<Named> for Icon {
#[inline]
fn from(builder: Named) -> Self {
builder.icon()
}
}
impl<Message: 'static> From<Named> for crate::Element<'_, Message> {
#[inline]
fn from(builder: Named) -> Self {
builder.icon().into()
}

View file

@ -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

View file

@ -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<Message: 'static> 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<Element<'a, Message>>) -> 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<Element<'a, Message>>) -> 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: <crate::Theme as Catalog>::Class<'a>) -> Self {
self.style = style;
self
}
#[inline]
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
self.padding = padding.into();
self
}
#[inline]
pub fn divider_padding(mut self, padding: u16) -> Self {
self.divider_padding = padding;
self

View file

@ -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<Vec<menu::Tree<'a, Message>>>) -> 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)
}

View file

@ -16,7 +16,7 @@ pub struct NavBarToggle<Message> {
}
#[must_use]
pub fn nav_bar_toggle<Message>() -> NavBarToggle<Message> {
pub const fn nav_bar_toggle<Message>() -> NavBarToggle<Message> {
NavBarToggle {
active: false,
on_toggle: None,

View file

@ -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,

View file

@ -32,6 +32,7 @@ where
}
/// Consumes the builder and returns the model.
#[inline]
pub fn build(self) -> Model<SelectionMode> {
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<Data>(self, map: &mut SecondaryMap<Entity, Data>, 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<Data>(
self,
map: &mut SparseSecondaryMap<Entity, Data>,
@ -90,11 +95,13 @@ where
/// .build()
/// ```
#[allow(clippy::must_use_candidate, clippy::return_self_not_must_use)]
#[inline]
pub fn data<Data: 'static>(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

View file

@ -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<Data>(self, map: &mut SecondaryMap<Entity, Data>, 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<Data>(
self,
map: &mut SparseSecondaryMap<Entity, Data>,
@ -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<Data: 'static>(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<Icon>) -> 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

View file

@ -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<SelectionMode> {
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<bool> {
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<bool> {
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<Entity> {
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<Icon> {
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<Icon> {
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<SelectionMode> {
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 {
<Self as Selectable>::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<u16> {
self.indents.get(id).copied()
}
#[inline]
pub fn indent_set(&mut self, id: Entity, indent: u16) -> Option<u16> {
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<u16> {
self.indents.remove(id)
}
@ -343,6 +364,7 @@ where
/// println!("found item at {}", position);
/// }
#[must_use]
#[inline]
pub fn position(&self, id: Entity) -> Option<u16> {
#[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<usize> {
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<Cow<'static, str>> {
self.text.remove(id)
}

View file

@ -39,6 +39,7 @@ impl Selectable for Model<SingleSelect> {
}
}
#[inline]
fn is_active(&self, id: Entity) -> bool {
self.selection.active == id
}
@ -47,23 +48,27 @@ impl Selectable for Model<SingleSelect> {
impl Model<SingleSelect> {
/// Get an immutable reference to the data associated with the active item.
#[must_use]
#[inline]
pub fn active_data<Data: 'static>(&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<Data: 'static>(&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<MultiSelect> {
}
}
#[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<MultiSelect> {
impl Model<MultiSelect> {
/// 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<Item = Entity> + '_ {
self.selection.active.iter().copied()
}

View file

@ -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<SelectionMode>: Selectable,
SelectionMode: Default,
{
#[inline]
pub fn new(model: &'a Model<SelectionMode>) -> 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())
}

View file

@ -19,11 +19,19 @@ pub fn item<'a, Message: 'static>(
title: impl Into<Cow<'a, str>> + 'a,
widget: impl Into<Element<'a, Message>> + '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<Cow<'a, str>> + 'a,
widget: impl Into<Element<'a, Message>> + '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<Element<'a, Message>>) -> 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<Element<'a, Message>>) -> FlexRow<'a, Message> {
flex_item_row(self.control_(widget))
flex_item_row(self.control_(widget.into()))
}
fn control_(self, widget: impl Into<Element<'a, Message>>) -> Vec<Element<'a, Message>> {
#[inline(never)]
fn control_(self, widget: Element<'a, Message>) -> Vec<Element<'a, Message>> {
let mut contents = Vec::with_capacity(4);
if let Some(icon) = self.icon {

View file

@ -26,72 +26,117 @@ pub enum Typography {
/// [`Text`] widget with the Title 1 typography preset.
pub fn title1<'a>(text: impl Into<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
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<Cow<'a, str>> + '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<str>) -> Text<crate::Theme, Renderer> {
Text::new(text)
.size(14.0)
.line_height(LineHeight::Absolute(20.0.into()))
.font(crate::font::mono())
}
inner(text.into())
}

View file

@ -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<Message> {
}
impl<Message> std::fmt::Debug for Action<Message> {
#[cold]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Action")
.field("description", &self.description)