segmented_button: honour WindowControlsPosition for the tab close button

The close button of closable tabs was pinned to the trailing edge, so
enabling macOS-style window controls left the window buttons on the
left and the tab crosses on the right.

Read crate::config::window_controls_position() and, when Start, place
the close button before the tab contents and shift them accordingly.
The hit-test, the text width and the drawn icon all derive from the
same position so they stay in sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lionel DARNIS 2026-08-19 21:18:31 +02:00
parent 28f7ab4651
commit 5392d9d5ae

View file

@ -9,7 +9,7 @@ use crate::widget::menu::{
self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight, menu_roots_children,
menu_roots_diff,
};
use crate::widget::{Icon, icon};
use crate::widget::{Icon, WindowControlsPosition, icon};
use crate::{Element, Renderer};
use derive_setters::Setters;
use iced::clipboard::dnd::{
@ -1608,8 +1608,11 @@ where
}
}
let close_button_bounds =
close_bounds(bounds, f32::from(self.close_icon.size));
let close_button_bounds = close_bounds(
bounds,
f32::from(self.close_icon.size),
crate::config::window_controls_position(),
);
let over_close_button = self.model.items[key].closable
&& cursor_position.is_over(close_button_bounds);
@ -2420,6 +2423,25 @@ where
}
}
// Whether to show the close button on this tab.
let show_close_button =
(key_is_active || !self.show_close_icon_on_hover || key_is_hovered)
&& self.model.is_closable(key);
// Width of the icon used by the close button, which we will subtract from the text bounds.
let close_icon_width = if show_close_button {
f32::from(self.close_icon.size)
} else {
0.0
};
// With macOS-style controls the close button precedes the contents, so
// shift them right to make room for it.
let close_position = crate::config::window_controls_position();
if show_close_button && close_position == WindowControlsPosition::Start {
bounds.x += close_icon_width + f32::from(self.button_spacing);
}
// Draw the image beside the text.
if let Some(icon) = self.model.icon(key) {
let mut image_bounds = bounds;
@ -2477,21 +2499,17 @@ where
}
}
// Whether to show the close button on this tab.
let show_close_button =
(key_is_active || !self.show_close_icon_on_hover || key_is_hovered)
&& self.model.is_closable(key);
// Width of the icon used by the close button, which we will subtract from the text bounds.
let close_icon_width = if show_close_button {
f32::from(self.close_icon.size)
// Room reserved after the contents: only needed when the close button
// trails them, since a leading one is already counted in `bounds.x`.
let trailing_close_width = if close_position == WindowControlsPosition::End {
close_icon_width
} else {
0.0
};
bounds.width = original_bounds.width
- (bounds.x - original_bounds.x)
- close_icon_width
- trailing_close_width
- f32::from(self.button_padding[2]);
bounds.y = center_y;
@ -2517,7 +2535,8 @@ where
// Draw a close button if set.
if show_close_button {
let close_button_bounds = close_bounds(original_bounds, close_icon_width);
let close_button_bounds =
close_bounds(original_bounds, close_icon_width, close_position);
draw_icon::<Message>(
renderer,
@ -3070,9 +3089,16 @@ impl From<Id> for widget::Id {
}
/// Calculates the bounds of the close button within the area of an item.
fn close_bounds(area: Rectangle<f32>, icon_size: f32) -> Rectangle<f32> {
fn close_bounds(
area: Rectangle<f32>,
icon_size: f32,
position: WindowControlsPosition,
) -> Rectangle<f32> {
Rectangle {
x: area.x + area.width - icon_size - 8.0,
x: match position {
WindowControlsPosition::Start => area.x + 8.0,
WindowControlsPosition::End => area.x + area.width - icon_size - 8.0,
},
y: area.center_y() - (icon_size / 2.0),
width: icon_size,
height: icon_size,