refactor(widget): improvements to button and icon widgets

This commit is contained in:
Michael Aaron Murphy 2023-09-13 15:47:32 +02:00 committed by Michael Murphy
parent 7f0943924a
commit 9dbc1be269
20 changed files with 399 additions and 558 deletions

View file

@ -68,23 +68,23 @@ where
let spacing = THEME.with(|t| t.borrow().cosmic().space_xxs());
let input = TextInput::new(placeholder, value)
.padding([0, spacing, 0, spacing])
.style(super::style::TextInput::Search)
.start_icon(
iced_widget::container(
crate::widget::icon::handle::from_name("system-search-symbolic")
.size(16)
.icon(),
)
.padding([spacing, spacing, spacing, spacing])
.into(),
.style(crate::theme::TextInput::Search)
.leading_icon(
crate::widget::icon::from_name("system-search-symbolic")
.size(16)
.apply(crate::widget::container)
.padding([spacing, spacing, spacing, spacing])
.into(),
);
if let Some(msg) = on_clear {
input.end_icon(
crate::widget::icon::handle::from_name("edit-clear-symbolic")
input.trailing_icon(
crate::widget::icon::from_name("edit-clear-symbolic")
.size(16)
.handle()
.apply(crate::widget::button::icon)
.apply(crate::widget::button)
.style(crate::theme::Button::Icon)
.width(32)
.height(32)
.on_press(msg)
.padding([spacing, spacing, spacing, spacing])
.into(),
@ -108,12 +108,11 @@ where
let spacing = THEME.with(|t| t.borrow().cosmic().space_xxs());
let mut input = TextInput::new(placeholder, value)
.padding([0, spacing, 0, spacing])
.style(super::style::TextInput::Default)
.start_icon(
crate::widget::icon::handle::from_name("system-lock-screen-symbolic")
.style(crate::theme::TextInput::Default)
.leading_icon(
crate::widget::icon::from_name("system-lock-screen-symbolic")
.size(16)
.icon()
.apply(iced_widget::container)
.apply(crate::widget::container)
.padding([spacing, spacing, spacing, spacing])
.into(),
);
@ -121,11 +120,11 @@ where
input = input.password();
}
if let Some(msg) = on_visible_toggle {
input.end_icon(
crate::widget::icon::handle::from_name("document-properties-symbolic")
input.trailing_icon(
crate::widget::icon::from_name("document-properties-symbolic")
.size(16)
.handle()
.apply(crate::widget::button::icon)
.apply(crate::widget::button)
.style(crate::theme::Button::Icon)
.on_press(msg)
.padding([spacing, spacing, spacing, spacing])
.into(),
@ -145,7 +144,7 @@ where
let spacing = THEME.with(|t| t.borrow().cosmic().space_xxs());
TextInput::new("", value)
.style(super::style::TextInput::Inline)
.style(crate::theme::TextInput::Inline)
.padding([spacing, spacing, spacing, spacing])
}
@ -204,8 +203,8 @@ pub struct TextInput<'a, Message> {
on_input: Option<Box<dyn Fn(String) -> Message + 'a>>,
on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>,
on_submit: Option<Message>,
start_icon: Option<Element<'a, Message, crate::Renderer>>,
end_element: Option<Element<'a, Message, crate::Renderer>>,
leading_icon: Option<Element<'a, Message, crate::Renderer>>,
trailing_icon: Option<Element<'a, Message, crate::Renderer>>,
style: <<crate::Renderer as iced_core::Renderer>::Theme as StyleSheet>::Style,
// (text_input::State, mime_type, dnd_action) -> Message
on_create_dnd_source: Option<Box<dyn Fn(State) -> Message + 'a>>,
@ -242,10 +241,10 @@ where
on_input: None,
on_paste: None,
on_submit: None,
start_icon: None,
end_element: None,
leading_icon: None,
trailing_icon: None,
error: None,
style: super::style::TextInput::default(),
style: crate::theme::TextInput::default(),
on_dnd_command_produced: None,
on_create_dnd_source: None,
surface_ids: None,
@ -333,14 +332,14 @@ where
}
/// Sets the start [`Icon`] of the [`TextInput`].
pub fn start_icon(mut self, icon: Element<'a, Message, crate::Renderer>) -> Self {
self.start_icon = Some(icon);
pub fn leading_icon(mut self, icon: Element<'a, Message, crate::Renderer>) -> Self {
self.leading_icon = Some(icon);
self
}
/// Sets the end [`Icon`] of the [`TextInput`].
pub fn end_icon(mut self, icon: Element<'a, Message, crate::Renderer>) -> Self {
self.end_element = Some(icon);
pub fn trailing_icon(mut self, icon: Element<'a, Message, crate::Renderer>) -> Self {
self.trailing_icon = Some(icon);
self
}
@ -398,8 +397,8 @@ where
self.font,
self.on_input.is_none(),
self.is_secure,
self.start_icon.as_ref(),
self.end_element.as_ref(),
self.leading_icon.as_ref(),
self.trailing_icon.as_ref(),
&self.style,
self.dnd_icon,
self.line_height,
@ -485,18 +484,18 @@ where
state.dragging_state = None;
}
let mut children: Vec<_> = self
.start_icon
.leading_icon
.iter_mut()
.chain(self.end_element.iter_mut())
.chain(self.trailing_icon.iter_mut())
.map(iced_core::Element::as_widget_mut)
.collect();
tree.diff_children(children.as_mut_slice());
}
fn children(&self) -> Vec<Tree> {
self.start_icon
self.leading_icon
.iter()
.chain(self.end_element.iter())
.chain(self.trailing_icon.iter())
.map(|icon| Tree::new(icon))
.collect()
}
@ -536,8 +535,8 @@ where
self.width,
self.padding,
self.size,
self.start_icon.as_ref(),
self.end_element.as_ref(),
self.leading_icon.as_ref(),
self.trailing_icon.as_ref(),
self.line_height,
self.label,
self.helper_text,
@ -573,13 +572,13 @@ where
) -> event::Status {
let text_layout = self.text_layout(layout);
let mut child_state = tree.children.iter_mut();
if let (Some(start_icon), Some(tree)) = (self.start_icon.as_mut(), child_state.next()) {
if let (Some(leading_icon), Some(tree)) = (self.leading_icon.as_mut(), child_state.next()) {
let mut children = text_layout.children();
children.next();
let start_icon_layout = children.next().unwrap();
let leading_icon_layout = children.next().unwrap();
if cursor_position.is_over(start_icon_layout.bounds()) {
return start_icon.as_widget_mut().on_event(
if cursor_position.is_over(leading_icon_layout.bounds()) {
return leading_icon.as_widget_mut().on_event(
tree,
event.clone(),
layout,
@ -591,14 +590,15 @@ where
);
}
}
if let (Some(end_icon), Some(tree)) = (self.end_element.as_mut(), child_state.next()) {
if let (Some(trailing_icon), Some(tree)) = (self.trailing_icon.as_mut(), child_state.next())
{
let mut children = text_layout.children();
children.next();
children.next();
let end_icon_layout = children.next().unwrap();
let trailing_icon_layout = children.next().unwrap();
if cursor_position.is_over(end_icon_layout.bounds()) {
return end_icon.as_widget_mut().on_event(
if cursor_position.is_over(trailing_icon_layout.bounds()) {
return trailing_icon.as_widget_mut().on_event(
tree,
event.clone(),
layout,
@ -656,8 +656,8 @@ where
self.font,
self.on_input.is_none(),
self.is_secure,
self.start_icon.as_ref(),
self.end_element.as_ref(),
self.leading_icon.as_ref(),
self.trailing_icon.as_ref(),
&self.style,
self.dnd_icon,
self.line_height,
@ -681,15 +681,15 @@ where
) -> mouse::Interaction {
let layout = self.text_layout(layout);
let mut index = 0;
if let (Some(start_icon), Some(tree)) =
(self.start_icon.as_ref(), state.children.get(index))
if let (Some(leading_icon), Some(tree)) =
(self.leading_icon.as_ref(), state.children.get(index))
{
let mut children = layout.children();
children.next();
let start_icon_layout = children.next().unwrap();
let leading_icon_layout = children.next().unwrap();
if cursor_position.is_over(start_icon_layout.bounds()) {
return start_icon.mouse_interaction(
if cursor_position.is_over(leading_icon_layout.bounds()) {
return leading_icon.mouse_interaction(
tree,
layout,
cursor_position,
@ -700,15 +700,16 @@ where
index += 1;
}
if let (Some(end_icon), Some(tree)) = (self.end_element.as_ref(), state.children.get(index))
if let (Some(trailing_icon), Some(tree)) =
(self.trailing_icon.as_ref(), state.children.get(index))
{
let mut children = layout.children();
children.next();
children.next();
let end_icon_layout = children.next().unwrap();
let trailing_icon_layout = children.next().unwrap();
if cursor_position.is_over(end_icon_layout.bounds()) {
return end_icon.mouse_interaction(
if cursor_position.is_over(trailing_icon_layout.bounds()) {
return trailing_icon.mouse_interaction(
tree,
layout,
cursor_position,
@ -770,8 +771,8 @@ pub fn layout<Message>(
width: Length,
padding: Padding,
size: Option<f32>,
start_icon: Option<&Element<'_, Message, crate::Renderer>>,
end_icon: Option<&Element<'_, Message, crate::Renderer>>,
leading_icon: Option<&Element<'_, Message, crate::Renderer>>,
trailing_icon: Option<&Element<'_, Message, crate::Renderer>>,
line_height: text::LineHeight,
label: Option<&str>,
helper_text: Option<&str>,
@ -804,13 +805,13 @@ pub fn layout<Message>(
let mut text_input_height = text_size * 1.2;
let padding = padding.fit(Size::ZERO, limits.max());
let helper_pos = if start_icon.is_some() || end_icon.is_some() {
let helper_pos = if leading_icon.is_some() || trailing_icon.is_some() {
// TODO configurable icon spacing, maybe via appearance
let limits_copy = limits;
let limits = limits.pad(padding);
let icon_spacing = 8.0;
let (start_icon_width, mut start_icon) = if let Some(icon) = start_icon.as_ref() {
let (leading_icon_width, mut leading_icon) = if let Some(icon) = leading_icon.as_ref() {
let icon_node = icon.layout(
renderer,
&Limits::NONE
@ -823,7 +824,7 @@ pub fn layout<Message>(
(0.0, None)
};
let (end_icon_width, mut end_icon) = if let Some(icon) = end_icon.as_ref() {
let (trailing_icon_width, mut trailing_icon) = if let Some(icon) = trailing_icon.as_ref() {
let icon_node = icon.layout(
renderer,
&Limits::NONE
@ -839,11 +840,12 @@ pub fn layout<Message>(
let text_bounds = text_limits.resolve(Size::ZERO);
let mut text_node =
layout::Node::new(text_bounds - Size::new(start_icon_width + end_icon_width, 0.0));
let mut text_node = layout::Node::new(
text_bounds - Size::new(leading_icon_width + trailing_icon_width, 0.0),
);
text_node.move_to(Point::new(
padding.left + start_icon_width,
padding.left + leading_icon_width,
padding.top + ((text_input_height - text_size * 1.2) / 2.0).max(0.0),
));
let mut node_list: Vec<_> = Vec::with_capacity(3);
@ -851,23 +853,23 @@ pub fn layout<Message>(
let text_node_bounds = text_node.bounds();
node_list.push(text_node);
if let Some(mut start_icon) = start_icon.take() {
start_icon.move_to(Point::new(
if let Some(mut leading_icon) = leading_icon.take() {
leading_icon.move_to(Point::new(
padding.left,
padding.top + ((text_size * 1.2 - start_icon.bounds().height) / 2.0).max(0.0),
padding.top + ((text_size * 1.2 - leading_icon.bounds().height) / 2.0).max(0.0),
));
node_list.push(start_icon);
node_list.push(leading_icon);
}
if let Some(mut end_icon) = end_icon.take() {
end_icon.move_to(Point::new(
if let Some(mut trailing_icon) = trailing_icon.take() {
trailing_icon.move_to(Point::new(
text_node_bounds.x + text_node_bounds.width + f32::from(spacing),
padding.top + ((text_size * 1.2 - end_icon.bounds().height) / 2.0).max(0.0),
padding.top + ((text_size * 1.2 - trailing_icon.bounds().height) / 2.0).max(0.0),
));
node_list.push(end_icon);
node_list.push(trailing_icon);
}
let text_input_size = Size::new(
text_node_bounds.x + text_node_bounds.width + end_icon_width,
text_node_bounds.x + text_node_bounds.width + trailing_icon_width,
text_input_height,
)
.pad(padding);
@ -987,6 +989,7 @@ where
let font: <Renderer as text::Renderer>::Font =
font.unwrap_or_else(|| renderer.default_font());
if is_clicked {
let Some(pos) = cursor_position.position() else {
return event::Status::Ignored;
@ -1737,7 +1740,7 @@ pub fn draw<'a, Message>(
is_disabled: bool,
is_secure: bool,
icon: Option<&Element<'a, Message, crate::Renderer>>,
end_element: Option<&Element<'a, Message, crate::Renderer>>,
trailing_icon: Option<&Element<'a, Message, crate::Renderer>>,
style: &<crate::Theme as StyleSheet>::Style,
dnd_icon: bool,
line_height: text::LineHeight,
@ -1848,9 +1851,9 @@ pub fn draw<'a, Message>(
});
}
let mut child_index = 0;
let start_icon_tree = children.get(child_index);
let leading_icon_tree = children.get(child_index);
// draw the start icon in the text input
if let (Some(icon), Some(tree)) = (icon, start_icon_tree) {
if let (Some(icon), Some(tree)) = (icon, leading_icon_tree) {
let icon_layout = children_layout.next().unwrap();
icon.as_widget().draw(
@ -1858,7 +1861,7 @@ pub fn draw<'a, Message>(
renderer,
theme,
&renderer::Style {
icon_color: renderer_style.icon_color,
icon_color: appearance.icon_color,
text_color: appearance.text_color,
scale_factor: renderer_style.scale_factor,
},
@ -1977,12 +1980,6 @@ pub fn draw<'a, Message>(
text::Shaping::Advanced,
);
let color = if text.is_empty() {
theme.placeholder_color(style)
} else {
appearance.text_color
};
let render = |renderer: &mut crate::Renderer| {
if let Some((cursor, color)) = cursor {
renderer.fill_quad(cursor, color);
@ -1992,7 +1989,11 @@ pub fn draw<'a, Message>(
renderer.fill_text(Text {
content: if text.is_empty() { placeholder } else { &text },
color,
color: if text.is_empty() {
appearance.placeholder_color
} else {
appearance.text_color
},
font,
bounds: Rectangle {
y: text_bounds.center_y(),
@ -2015,10 +2016,10 @@ pub fn draw<'a, Message>(
render(renderer);
}
let end_icon_tree = children.get(child_index);
let trailing_icon_tree = children.get(child_index);
// draw the end icon in the text input
if let (Some(icon), Some(tree)) = (end_element, end_icon_tree) {
if let (Some(icon), Some(tree)) = (trailing_icon, trailing_icon_tree) {
let icon_layout = children_layout.next().unwrap();
icon.as_widget().draw(
@ -2042,7 +2043,7 @@ pub fn draw<'a, Message>(
content: helper_text,
size: helper_text_size,
font,
color,
color: appearance.text_color,
bounds: helper_text_layout.bounds(),
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,