116 lines
4.1 KiB
Rust
116 lines
4.1 KiB
Rust
/// Shared paint logic — draws the crossed lines, using whatever
|
|
/// interact-state visuals (hover/press color, expansion) egui's style
|
|
/// would normally pick for this response.
|
|
fn paint_close_x(ui: &egui::Ui, rect: egui::Rect, response: &egui::Response) {
|
|
if !ui.is_rect_visible(rect) {
|
|
return;
|
|
}
|
|
let visuals = ui.style().interact(response);
|
|
let inner = rect.shrink(4.0).expand(visuals.expansion);
|
|
let stroke = visuals.fg_stroke;
|
|
ui.painter().line_segment([inner.left_top(), inner.right_bottom()], stroke);
|
|
ui.painter().line_segment([inner.right_top(), inner.left_bottom()], stroke);
|
|
}
|
|
|
|
/// Standalone close button — allocates its own space in the current
|
|
/// layout, like a normal widget. Use this in a `ui.horizontal(...)` row,
|
|
/// e.g. next to a label in a flat list (process manager tabs, a file
|
|
/// list with per-row close, etc).
|
|
pub fn close_button(ui: &mut egui::Ui, size: f32) -> egui::Response {
|
|
let (rect, response) = ui.allocate_exact_size(egui::vec2(size, size), egui::Sense::click());
|
|
let response = response.on_hover_cursor(egui::CursorIcon::Default);
|
|
|
|
response.widget_info(|| {
|
|
egui::WidgetInfo::labeled(egui::WidgetType::Button, ui.is_enabled(), "Close")
|
|
});
|
|
|
|
paint_close_x(ui, rect, &response);
|
|
response
|
|
}
|
|
|
|
/// Close button at an explicit rect you've already computed — for
|
|
/// cases like tab_ui, where the button needs to sit inside a larger
|
|
/// region you're laying out manually rather than in normal flow.
|
|
pub fn close_button_at(ui: &mut egui::Ui, rect: egui::Rect, id: egui::Id) -> egui::Response {
|
|
let response = ui
|
|
.interact(rect, id, egui::Sense::click_and_drag())
|
|
.on_hover_cursor(egui::CursorIcon::Default);
|
|
|
|
response.widget_info(|| {
|
|
egui::WidgetInfo::labeled(egui::WidgetType::Button, ui.is_enabled(), "Close")
|
|
});
|
|
|
|
paint_close_x(ui, rect, &response);
|
|
response
|
|
}
|
|
|
|
pub struct TabResponse {
|
|
pub response: egui::Response,
|
|
pub close_clicked: bool,
|
|
}
|
|
|
|
pub fn painted_tab(
|
|
ui: &mut egui::Ui,
|
|
id: egui::Id,
|
|
title: impl Into<egui::WidgetText>,
|
|
selected: bool,
|
|
closable: bool,
|
|
draggable: bool,
|
|
) -> TabResponse {
|
|
let padding = egui::vec2(14.0, 8.0);
|
|
let close_size = 14.0;
|
|
let close_gap = 6.0;
|
|
|
|
let font_id = egui::TextStyle::Button.resolve(ui.style());
|
|
let galley = title.into().into_galley(ui, Some(egui::TextWrapMode::Extend), f32::INFINITY, font_id);
|
|
|
|
let extra_width = if closable { close_size + close_gap } else { 0.0 };
|
|
let desired = egui::vec2(
|
|
galley.size().x + padding.x * 2.0 + extra_width,
|
|
ui.available_height().max(galley.size().y + padding.y * 2.0),
|
|
);
|
|
|
|
let (_, rect) = ui.allocate_space(desired);
|
|
|
|
let sense = if draggable { egui::Sense::click_and_drag() } else { egui::Sense::click() };
|
|
let response = ui.interact(rect, id, sense);
|
|
let response = if draggable {
|
|
response.on_hover_cursor(egui::CursorIcon::Grab)
|
|
} else {
|
|
response
|
|
};
|
|
|
|
response.widget_info(|| {
|
|
egui::WidgetInfo::selected(egui::WidgetType::Button, ui.is_enabled(), selected, galley.text())
|
|
});
|
|
|
|
let mut close_clicked = false;
|
|
|
|
if ui.is_rect_visible(rect) {
|
|
let visuals = ui.style().interact(&response);
|
|
|
|
ui.painter().rect(rect, 4.0, visuals.bg_fill, visuals.bg_stroke, egui::StrokeKind::Inside);
|
|
|
|
let text_color = if selected {
|
|
egui::Color32::WHITE
|
|
} else {
|
|
egui::Color32::DARK_GRAY
|
|
};
|
|
|
|
let text_pos = egui::pos2(rect.min.x + padding.x, rect.center().y - galley.size().y / 2.0);
|
|
ui.painter().galley(text_pos, galley, text_color);
|
|
|
|
if closable {
|
|
let close_rect = egui::Rect::from_min_size(
|
|
egui::pos2(rect.max.x - padding.x - close_size, rect.center().y - close_size / 2.0),
|
|
egui::vec2(close_size, close_size),
|
|
);
|
|
let close_id = id.with("close");
|
|
let close_response = close_button_at(ui, close_rect, close_id);
|
|
|
|
close_clicked = close_response.clicked() || response.clicked_by(egui::PointerButton::Middle);
|
|
}
|
|
}
|
|
|
|
TabResponse { response, close_clicked }
|
|
} |