feat(segmented_button): add FileNav style with related widget improvements

This commit is contained in:
Michael Aaron Murphy 2025-08-19 11:13:28 +02:00
parent 8412dd5939
commit c10695600b
No known key found for this signature in database
GPG key ID: B2732D4240C9212C
3 changed files with 100 additions and 57 deletions

View file

@ -67,7 +67,7 @@ where
/ num as f32;
}
let segmetned_control = matches!(self.style, crate::theme::SegmentedButton::Control);
let is_control = matches!(self.style, crate::theme::SegmentedButton::Control);
Box::new(
self.model
@ -93,7 +93,7 @@ where
let button_bounds = ItemBounds::Button(key, layout_bounds);
let mut divider = None;
if self.dividers && segmetned_control && nth + 1 < num {
if self.dividers && is_control && nth + 1 < num {
divider = Some(ItemBounds::Divider(
Rectangle {
width: 1.0,
@ -143,7 +143,7 @@ where
let max_size = limits.height(Length::Fixed(max_height)).resolve(
Length::Fill,
max_height,
Size::new(f32::MAX, max_height),
Size::new(limits.max().width, max_height),
);
let mut visible_width = 0.0;
@ -152,7 +152,7 @@ where
for (button_size, _actual_size) in &state.internal_layout {
visible_width += button_size.width;
if max_size.width >= visible_width {
if max_size.width - spacing >= visible_width {
state.buttons_visible += 1;
} else {
visible_width = max_size.width - max_height;

View file

@ -619,20 +619,19 @@ where
for key in self.model.order.iter().copied() {
if let Some(text) = self.model.text.get(key) {
let (font, button_state) = if self.button_is_focused(state, key) {
(self.font_active, 0)
let font = if self.button_is_focused(state, key) {
self.font_active
} else if state.show_context.is_some() || self.button_is_hovered(state, key) {
(self.font_hovered, 1)
self.font_hovered
} else if self.model.is_active(key) {
(self.font_active, 2)
self.font_active
} else {
(self.font_inactive, 3)
self.font_inactive
};
let mut hasher = DefaultHasher::new();
text.hash(&mut hasher);
font.hash(&mut hasher);
button_state.hash(&mut hasher);
let text_hash = hasher.finish();
if let Some(prev_hash) = state.text_hashes.insert(key, text_hash) {
@ -1293,6 +1292,15 @@ where
);
}
let rad_0 = THEME.lock().unwrap().cosmic().corner_radii.radius_0;
let divider_background = Background::Color(
crate::theme::active()
.cosmic()
.primary_component_divider()
.into(),
);
// Draw each of the items in the widget.
let mut nth = 0;
self.variant_bounds(state, bounds).for_each(move |item| {
@ -1337,7 +1345,7 @@ where
let key_is_active = self.model.is_active(key);
let key_is_focused = state.focused_visible && self.button_is_focused(state, key);
let key_is_hovered = self.button_is_hovered(state, key);
let status_appearance = if self.button_is_pressed(state, key) && key_is_hovered {
let status_appearance = if self.button_is_pressed(state, key) {
appearance.pressed
} else if key_is_hovered || menu_open() {
appearance.hover
@ -1355,11 +1363,87 @@ where
status_appearance.middle
};
// Draw the active hint on tabs
if appearance.active_width > 0.0 {
let active_width = if key_is_active {
appearance.active_width
} else {
1.0
};
renderer.fill_quad(
renderer::Quad {
bounds: if Self::VERTICAL {
Rectangle {
x: bounds.x + bounds.width - active_width,
width: active_width,
..bounds
}
} else {
Rectangle {
y: bounds.y + bounds.height - active_width,
height: active_width,
..bounds
}
},
border: Border {
radius: rad_0.into(),
..Default::default()
},
shadow: Shadow::default(),
},
appearance.active.text_color,
);
}
let original_bounds = bounds;
bounds.x += f32::from(self.button_padding[0]);
bounds.width -= f32::from(self.button_padding[0]) - f32::from(self.button_padding[2]);
let mut indent_padding = 0.0;
// Adjust bounds by indent
if let Some(indent) = self.model.indent(key) {
if indent > 0 {
let adjustment = f32::from(indent) * f32::from(self.indent_spacing);
bounds.x += adjustment;
bounds.width -= adjustment;
// Draw indent line
if let crate::theme::SegmentedButton::FileNav = self.style {
if indent > 1 {
indent_padding = 7.0;
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle {
x: bounds.x - self.indent_spacing as f32 + indent_padding,
width: 1.0,
..bounds
},
border: Border {
radius: rad_0.into(),
..Default::default()
},
shadow: Shadow::default(),
},
divider_background,
);
indent_padding += 4.0;
}
}
}
}
// Render the background of the button.
if key_is_focused || status_appearance.background.is_some() {
renderer.fill_quad(
renderer::Quad {
bounds,
bounds: Rectangle {
x: bounds.x - f32::from(self.button_padding[0]) + indent_padding,
width: bounds.width + f32::from(self.button_padding[0])
- f32::from(self.button_padding[2])
- indent_padding,
..bounds
},
border: if key_is_focused {
Border {
width: 1.0,
@ -1377,49 +1461,6 @@ where
);
}
// Draw the active hint on tabs
if appearance.active_width > 0.0 {
let rad_0 = THEME.lock().unwrap().cosmic().corner_radii.radius_0;
let active_width = if key_is_active {
appearance.active_width
} else {
1.0
};
let mut bounds = bounds;
if Self::VERTICAL {
bounds.x += bounds.height - active_width;
bounds.width = active_width;
} else {
bounds.y += bounds.height - active_width;
bounds.height = active_width;
}
renderer.fill_quad(
renderer::Quad {
bounds,
border: Border {
radius: rad_0.into(),
..Default::default()
},
shadow: Shadow::default(),
},
appearance.active.text_color,
);
}
let original_bounds = bounds;
bounds.x += f32::from(self.button_padding[0]);
bounds.width -= f32::from(self.button_padding[0]) - f32::from(self.button_padding[2]);
// Adjust bounds by indent
if let Some(indent) = self.model.indent(key) {
let adjustment = f32::from(indent) * f32::from(self.indent_spacing);
bounds.x += adjustment;
bounds.width -= adjustment;
}
// Align contents of the button to the requested `button_alignment`.
{
let actual_width = state.internal_layout[nth].1.width;