mirror of
https://github.com/emilk/egui.git
synced 2026-06-28 07:23:13 -04:00
126 lines
4.1 KiB
Rust
126 lines
4.1 KiB
Rust
use crate::{paint::PaintCmd, style::WidgetVisuals, *};
|
|
|
|
pub fn combo_box_with_label(
|
|
ui: &mut Ui,
|
|
label: impl Into<Label>,
|
|
selected: impl Into<Label>,
|
|
menu_contents: impl FnOnce(&mut Ui),
|
|
) -> Response {
|
|
let label = label.into();
|
|
let button_id = ui.make_persistent_id(label.text());
|
|
|
|
ui.horizontal(|ui| {
|
|
let mut response = combo_box(ui, button_id, selected, menu_contents);
|
|
response |= ui.add(label);
|
|
response
|
|
})
|
|
.0
|
|
}
|
|
|
|
pub fn combo_box(
|
|
ui: &mut Ui,
|
|
button_id: Id,
|
|
selected: impl Into<Label>,
|
|
menu_contents: impl FnOnce(&mut Ui),
|
|
) -> Response {
|
|
const MAX_COMBO_HEIGHT: f32 = 128.0;
|
|
|
|
let popup_id = button_id.with("popup");
|
|
let selected = selected.into();
|
|
|
|
let button_active = ui.memory().is_popup_open(popup_id);
|
|
let button_response = button_frame(ui, button_id, button_active, Sense::click(), |ui| {
|
|
ui.horizontal(|ui| {
|
|
// We don't want to change width when user selects something new
|
|
let full_minimum_width = ui.style().spacing.slider_width;
|
|
let icon_width = ui.style().spacing.icon_width;
|
|
|
|
selected.ui(ui);
|
|
|
|
let advance = full_minimum_width - icon_width - ui.min_rect().width();
|
|
ui.advance_cursor(advance.at_least(0.0));
|
|
|
|
let (_, icon_rect) = ui.allocate_space(Vec2::splat(icon_width));
|
|
let button_rect = ui.min_rect().expand2(ui.style().spacing.button_padding);
|
|
let mut response = ui.interact(button_rect, button_id, Sense::click());
|
|
response.active |= button_active;
|
|
paint_icon(ui.painter(), icon_rect, ui.style().interact(&response));
|
|
});
|
|
});
|
|
if button_response.clicked {
|
|
ui.memory().toggle_popup(popup_id);
|
|
}
|
|
|
|
if ui.memory().is_popup_open(popup_id) {
|
|
let parent_clip_rect = ui.clip_rect();
|
|
|
|
Area::new(popup_id)
|
|
.order(Order::Foreground)
|
|
.fixed_pos(button_response.rect.left_bottom())
|
|
.show(ui.ctx(), |ui| {
|
|
ui.set_clip_rect(parent_clip_rect); // for when the combo-box is in a scroll area.
|
|
let frame = Frame::popup(ui.style());
|
|
let frame_margin = frame.margin;
|
|
frame.show(ui, |ui| {
|
|
ui.with_layout(Layout::top_down_justified(Align::left()), |ui| {
|
|
ui.set_width(button_response.rect.width() - 2.0 * frame_margin.x);
|
|
ScrollArea::from_max_height(MAX_COMBO_HEIGHT).show(ui, menu_contents);
|
|
});
|
|
});
|
|
});
|
|
|
|
if ui.input().key_pressed(Key::Escape) || ui.input().mouse.click && !button_response.clicked
|
|
{
|
|
ui.memory().close_popup();
|
|
}
|
|
}
|
|
|
|
button_response
|
|
}
|
|
|
|
fn button_frame(
|
|
ui: &mut Ui,
|
|
id: Id,
|
|
button_active: bool,
|
|
sense: Sense,
|
|
add_contents: impl FnOnce(&mut Ui),
|
|
) -> Response {
|
|
let margin = ui.style().spacing.button_padding;
|
|
let outer_rect_bounds = ui.available_rect_before_wrap();
|
|
let inner_rect = outer_rect_bounds.shrink2(margin);
|
|
let where_to_put_background = ui.painter().add(PaintCmd::Noop);
|
|
let mut content_ui = ui.child_ui(inner_rect, *ui.layout());
|
|
add_contents(&mut content_ui);
|
|
|
|
let outer_rect = Rect::from_min_max(outer_rect_bounds.min, content_ui.min_rect().max + margin);
|
|
|
|
let mut response = ui.interact(outer_rect, id, sense);
|
|
response.active |= button_active;
|
|
let visuals = ui.style().interact(&response);
|
|
|
|
ui.painter().set(
|
|
where_to_put_background,
|
|
PaintCmd::Rect {
|
|
rect: outer_rect,
|
|
corner_radius: visuals.corner_radius,
|
|
fill: visuals.bg_fill,
|
|
stroke: visuals.bg_stroke,
|
|
},
|
|
);
|
|
|
|
ui.allocate_space(outer_rect.size());
|
|
|
|
response
|
|
}
|
|
|
|
fn paint_icon(painter: &Painter, rect: Rect, visuals: &WidgetVisuals) {
|
|
let rect = Rect::from_center_size(
|
|
rect.center(),
|
|
vec2(rect.width() * 0.7, rect.height() * 0.45),
|
|
);
|
|
painter.add(PaintCmd::closed_line(
|
|
vec![rect.left_top(), rect.right_top(), rect.center_bottom()],
|
|
visuals.fg_stroke,
|
|
));
|
|
}
|