perf: reduce memory allocations

This also changes `widget::column::with_children` and
`widget::row::with_children` to take an `impl IntoIterator` instead
of a `Vec`, like the `iced` variants of these functions do.

This shouldn't be a breaking change since passing in a `Vec` will still
compile and function exactly as before.

(Using `iced::widget::Column::from_vec` or
`iced::widget::Row::from_vec` isn't possible, since the elements of the
`Vec` aren't checked, so the size of the resulting `Column` or `Row`
won't adapt to the size of its children. Perhaps a new function could
be added to mirror `iced`'s?)
This commit is contained in:
Cheong Lau 2025-10-11 13:36:35 +10:00 committed by Michael Murphy
parent 840ef21e4d
commit bd438a8581
20 changed files with 83 additions and 88 deletions

View file

@ -76,7 +76,7 @@ impl From<String> for PanelType {
match value.as_str() {
"Panel" => PanelType::Panel,
"Dock" => PanelType::Dock,
other => PanelType::Other(other.to_string()),
_ => PanelType::Other(value),
}
}
}
@ -470,8 +470,8 @@ pub fn run<App: Application>(flags: App::Flags) -> iced::Result {
crate::malloc::limit_mmap_threshold(threshold);
}
if let Some(icon_theme) = settings.default_icon_theme.clone() {
crate::icon_theme::set_default(icon_theme);
if let Some(icon_theme) = settings.default_icon_theme.as_ref() {
crate::icon_theme::set_default(icon_theme.clone());
}
THEME

View file

@ -162,8 +162,8 @@ pub(crate) fn wayland_handler(
exit: false,
tx,
seat_state: SeatState::new(&globals, &qh),
queue_handle: qh.clone(),
activation_state: ActivationState::bind::<AppData>(&globals, &qh).ok(),
queue_handle: qh,
registry_state,
};

View file

@ -361,8 +361,12 @@ impl Core {
config_id: &'static str,
) -> iced::Subscription<cosmic_config::Update<T>> {
#[cfg(all(feature = "dbus-config", target_os = "linux"))]
if let Some(settings_daemon) = self.settings_daemon.clone() {
return cosmic_config::dbus::watcher_subscription(settings_daemon, config_id, false);
if let Some(settings_daemon) = self.settings_daemon.as_ref() {
return cosmic_config::dbus::watcher_subscription(
settings_daemon.clone(),
config_id,
false,
);
}
cosmic_config::config_subscription(
std::any::TypeId::of::<T>(),
@ -378,8 +382,12 @@ impl Core {
state_id: &'static str,
) -> iced::Subscription<cosmic_config::Update<T>> {
#[cfg(all(feature = "dbus-config", target_os = "linux"))]
if let Some(settings_daemon) = self.settings_daemon.clone() {
return cosmic_config::dbus::watcher_subscription(settings_daemon, state_id, true);
if let Some(settings_daemon) = self.settings_daemon.as_ref() {
return cosmic_config::dbus::watcher_subscription(
settings_daemon.clone(),
state_id,
true,
);
}
cosmic_config::config_subscription(
std::any::TypeId::of::<T>(),

View file

@ -113,10 +113,7 @@ pub fn about<'a, Message: Clone + 'static>(
let section = |list: &'a Vec<(String, String)>, title: String| {
(!list.is_empty()).then_some({
let items: Vec<Element<Message>> = list
.iter()
.map(|(name, url)| section_button(name, url))
.collect();
let items = list.iter().map(|(name, url)| section_button(name, url));
widget::settings::section().title(title).extend(items)
})
};

View file

@ -179,8 +179,8 @@ where
));
}
let content_list = column::with_children(vec![
row::with_children(vec![
let content_list = column::with_children([
row::with_children([
date.into(),
crate::widget::Space::with_width(Length::Fill).into(),
month_controls.into(),

View file

@ -455,21 +455,16 @@ where
// TODO get global colors from some cache?
// TODO how to handle overflow? should this use a grid widget for the list or a horizontal scroll and a limit for the max?
crate::widget::scrollable(
Row::with_children(
self.recent_colors
.iter()
.map(|c| {
let initial_srgb = palette::Srgb::from(*c);
let hsv = palette::Hsv::from_color(initial_srgb);
color_button(
Some(on_update(ColorPickerUpdate::ActiveColor(hsv))),
Some(*c),
Length::FillPortion(12),
)
.into()
})
.collect::<Vec<_>>(),
)
Row::with_children(self.recent_colors.iter().map(|c| {
let initial_srgb = palette::Srgb::from(*c);
let hsv = palette::Hsv::from_color(initial_srgb);
color_button(
Some(on_update(ColorPickerUpdate::ActiveColor(hsv))),
Some(*c),
Length::FillPortion(12),
)
.into()
}))
.padding([0.0, 0.0, f32::from(spacing.space_m), 0.0])
.spacing(spacing.space_xxs),
)

View file

@ -158,8 +158,7 @@ impl<'a, Message: Clone + 'static> From<Dialog<'a, Message>> for Element<'a, Mes
}
let mut container = widget::container(
widget::column::with_children(vec![content_row.into(), button_row.into()])
.spacing(space_l),
widget::column::with_children([content_row.into(), button_row.into()]).spacing(space_l),
)
.class(style::Container::Dialog)
.padding(space_m)

View file

@ -477,8 +477,8 @@ where
if cursor.is_over(layout.bounds()) {
if let Some(index) = *hovered_guard {
shell.publish((self.on_selected)(index));
if let Some(close_on_selected) = self.close_on_selected.clone() {
shell.publish(close_on_selected);
if let Some(close_on_selected) = self.close_on_selected.as_ref() {
shell.publish(close_on_selected.clone());
}
return event::Status::Captured;
}
@ -521,8 +521,8 @@ where
if let Some(index) = *hovered_guard {
shell.publish((self.on_selected)(index));
if let Some(close_on_selected) = self.close_on_selected.clone() {
shell.publish(close_on_selected);
if let Some(close_on_selected) = self.close_on_selected.as_ref() {
shell.publish(close_on_selected.clone());
}
return event::Status::Captured;
}

View file

@ -521,8 +521,8 @@ pub fn draw<'a, S, Item: Clone + PartialEq + 'static>(
style.background,
);
if let Some(handle) = state.icon.clone() {
let svg_handle = iced_core::Svg::new(handle).color(style.text_color);
if let Some(handle) = state.icon.as_ref() {
let svg_handle = iced_core::Svg::new(handle.clone()).color(style.text_color);
svg::Renderer::draw_svg(
renderer,
svg_handle,

View file

@ -1653,7 +1653,7 @@ fn get_children_layout<Message>(
let child_sizes: Vec<Size> = match item_height {
ItemHeight::Uniform(u) => {
let count = menu_tree.children.len();
(0..count).map(|_| Size::new(width, f32::from(u))).collect()
vec![Size::new(width, f32::from(u)); count]
}
ItemHeight::Static(s) => menu_tree
.children

View file

@ -144,7 +144,7 @@ where
Message: std::clone::Clone + 'a,
{
widget::button::custom(
widget::Row::with_children(children)
widget::Row::from_vec(children)
.align_y(Alignment::Center)
.height(Length::Fill)
.width(Length::Fill),
@ -252,7 +252,7 @@ pub fn menu_items<
let l: Cow<'static, str> = label.into();
let key = find_key(&action, key_binds);
let mut items = vec![
widget::text(l.clone()).into(),
widget::text(l).into(),
widget::horizontal_space().into(),
widget::text(key).class(key_class).into(),
];
@ -272,7 +272,7 @@ pub fn menu_items<
let key = find_key(&action, key_binds);
let mut items = vec![
widget::text(l.clone()).into(),
widget::text(l).into(),
widget::horizontal_space().into(),
widget::text(key).class(key_class).into(),
];

View file

@ -147,12 +147,14 @@ pub mod column {
#[must_use]
/// A pre-allocated [`column`].
pub fn with_capacity<'a, Message>(capacity: usize) -> Column<'a, Message> {
Column::with_children(Vec::with_capacity(capacity))
Column::with_capacity(capacity)
}
#[must_use]
/// A [`column`] that will be assigned a [`Vec`] of children.
pub fn with_children<Message>(children: Vec<crate::Element<Message>>) -> Column<Message> {
/// A [`column`] that will be assigned an [`Iterator`] of children.
pub fn with_children<'a, Message>(
children: impl IntoIterator<Item = crate::Element<'a, Message>>,
) -> Column<'a, Message> {
Column::with_children(children)
}
}
@ -298,12 +300,14 @@ pub mod row {
#[must_use]
/// A pre-allocated [`row`].
pub fn with_capacity<'a, Message>(capacity: usize) -> Row<'a, Message> {
Row::with_children(Vec::with_capacity(capacity))
Row::with_capacity(capacity)
}
#[must_use]
/// A [`row`] that will be assigned a [`Vec`] of children.
pub fn with_children<Message>(children: Vec<crate::Element<Message>>) -> Row<Message> {
/// A [`row`] that will be assigned an [`Iterator`] of children.
pub fn with_children<'a, Message>(
children: impl IntoIterator<Item = crate::Element<'a, Message>>,
) -> Row<'a, Message> {
Row::with_children(children)
}
}

View file

@ -141,14 +141,14 @@ where
if matches!(event, Event::Mouse(_) | Event::Touch(_)) {
return event::Status::Captured;
}
} else if let Some(on_close) = self.on_close.clone() {
} else if let Some(on_close) = self.on_close.as_ref() {
if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(_))
| Event::Touch(touch::Event::FingerPressed { .. })
) && !cursor_position.is_over(layout.bounds())
{
shell.publish(on_close);
shell.publish(on_close.clone());
}
}
}

View file

@ -274,7 +274,7 @@ where
self.on_dnd_drop = Some(Box::new(move |entity, data, mime, action| {
dnd_drop_handler(entity, D::try_from((data, mime)).ok(), action)
}));
self.mimes = D::allowed().iter().cloned().collect();
self.mimes = D::allowed().into_owned();
self
}
@ -1867,7 +1867,7 @@ fn draw_icon<Message: 'static>(
});
Widget::<Message, crate::Theme, Renderer>::draw(
Element::<Message>::from(icon.clone()).as_widget(),
Element::<Message>::from(icon).as_widget(),
&Tree::empty(),
renderer,
theme,

View file

@ -170,7 +170,6 @@ where
)
.apply(Element::from)
})
.collect::<Vec<Element<'a, Message>>>()
.apply(widget::column::with_children)
.spacing(val.item_spacing)
.padding(val.element_padding)

View file

@ -125,7 +125,6 @@ where
.apply(|mouse_area| widget::context_menu(mouse_area, cat_context_tree))
.apply(Element::from)
})
.collect::<Vec<Element<'a, Message>>>()
.apply(widget::row::with_children)
.apply(Element::from);
// Build the items
@ -166,7 +165,6 @@ where
.align_y(Alignment::Center)
.apply(Element::from)
})
.collect::<Vec<Element<'static, Message>>>()
.apply(widget::row::with_children)
.apply(container)
.padding(val.item_padding)

View file

@ -760,14 +760,14 @@ where
if state.dirty {
state.dirty = false;
let value = if self.is_secure {
self.value.secure()
&self.value.secure()
} else {
self.value.clone()
&self.value
};
replace_paragraph(
state,
Layout::new(&res),
&value,
value,
font,
iced::Pixels(size),
line_height,
@ -2022,7 +2022,7 @@ pub fn update<'a, Message: Clone + 'static>(
if let DndOfferState::HandlingOffer(mime_types, _action) = state.dnd_offer.clone() {
let Some(mime_type) = SUPPORTED_TEXT_MIME_TYPES
.iter()
.find(|m| mime_types.contains(&(**m).to_string()))
.find(|&&m| mime_types.iter().any(|t| t == m))
else {
state.dnd_offer = DndOfferState::None;
return event::Status::Captured;
@ -2057,7 +2057,7 @@ pub fn update<'a, Message: Clone + 'static>(
{
cold();
let state = state();
if let DndOfferState::Dropped = state.dnd_offer.clone() {
if matches!(&state.dnd_offer, DndOfferState::Dropped) {
state.dnd_offer = DndOfferState::None;
if !SUPPORTED_TEXT_MIME_TYPES.contains(&mime_type.as_str()) || data.is_empty() {
return event::Status::Captured;
@ -2536,7 +2536,7 @@ impl AsMimeTypes for TextInputString {
fn as_bytes(&self, mime_type: &str) -> Option<Cow<'static, [u8]>> {
if SUPPORTED_TEXT_MIME_TYPES.contains(&mime_type) {
Some(Cow::Owned(self.0.clone().as_bytes().to_vec()))
Some(Cow::Owned(self.0.clone().into_bytes()))
} else {
None
}