mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 14:49:06 -04:00
Add Group container
This commit is contained in:
78
crates/egui/src/containers/group.rs
Normal file
78
crates/egui/src/containers/group.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use emath::NumExt;
|
||||
use crate::{Align2, Id, Rect, Ui, UiBuilder, Vec2};
|
||||
|
||||
pub struct Group {
|
||||
id: Id,
|
||||
rect: Option<Rect>,
|
||||
size: Option<Vec2>,
|
||||
align2: Align2,
|
||||
}
|
||||
|
||||
impl Group {
|
||||
pub fn new(id: impl Into<Id>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
rect: None,
|
||||
size: None,
|
||||
align2: Align2::CENTER_CENTER,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn align2(mut self, align2: Align2) -> Self {
|
||||
self.align2 = align2;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rect(mut self, rect: Rect) -> Self {
|
||||
self.rect = Some(rect);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ui<T>(self, ui: &mut Ui, content: impl FnOnce(&mut Ui) -> T) -> T {
|
||||
let id = ui.id().with(self.id);
|
||||
let data_id = id.with("group");
|
||||
|
||||
let rect = if let Some(rect) = self.rect {
|
||||
rect
|
||||
} else if let Some(size) = self.size {
|
||||
let pos = ui.next_widget_position();
|
||||
Rect::from_min_size(pos, size)
|
||||
} else {
|
||||
ui.available_rect_before_wrap()
|
||||
};
|
||||
|
||||
let last_size = ui.ctx().data(|mem| mem.get_temp(data_id));
|
||||
|
||||
let mut content_rect = if let Some(size) = last_size {
|
||||
let left_top = self.align2.align_size_within_rect(size, rect).left_top();
|
||||
Rect::from_min_size(left_top, rect.size())
|
||||
} else {
|
||||
rect
|
||||
};
|
||||
|
||||
// Clamp the content_rect so it doesn't exceed the top left corner
|
||||
let offset = (rect.min - content_rect.min).at_least(Vec2::ZERO);
|
||||
content_rect = content_rect.translate(offset);
|
||||
|
||||
let mut builder = UiBuilder::new().id_salt(id);
|
||||
|
||||
if last_size.is_none() {
|
||||
builder = builder.invisible();
|
||||
}
|
||||
|
||||
let response = ui.scope_builder(builder.max_rect(content_rect), content);
|
||||
|
||||
let size = response.response.rect.size();
|
||||
|
||||
if last_size != Some(size) {
|
||||
ui.ctx().request_discard("Group size changed");
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
|
||||
ui.ctx().data_mut(|mem| {
|
||||
mem.insert_temp(data_id, size);
|
||||
});
|
||||
|
||||
response.inner
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@ pub mod scroll_area;
|
||||
mod sides;
|
||||
mod tooltip;
|
||||
pub(crate) mod window;
|
||||
mod group;
|
||||
|
||||
pub use group::Group;
|
||||
pub use {
|
||||
area::{Area, AreaState},
|
||||
close_tag::ClosableTag,
|
||||
|
||||
@@ -80,6 +80,7 @@ impl Default for DemoGroups {
|
||||
Box::<super::extra_viewport::ExtraViewport>::default(),
|
||||
Box::<super::font_book::FontBook>::default(),
|
||||
Box::<super::frame_demo::FrameDemo>::default(),
|
||||
Box::<super::group_demo::GroupDemo>::default(),
|
||||
Box::<super::highlighting::Highlighting>::default(),
|
||||
Box::<super::interactive_container::InteractiveContainerDemo>::default(),
|
||||
Box::<super::MiscDemoWindow>::default(),
|
||||
|
||||
76
crates/egui_demo_lib/src/demo/group_demo.rs
Normal file
76
crates/egui_demo_lib/src/demo/group_demo.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use egui::{Align, Align2, Group, ScrollArea};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct GroupDemo {
|
||||
align_x: Align,
|
||||
align_y: Align,
|
||||
}
|
||||
|
||||
impl Default for GroupDemo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
align_x: Align::Center,
|
||||
align_y: Align::Center,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Demo for GroupDemo {
|
||||
fn name(&self) -> &'static str {
|
||||
"Group"
|
||||
}
|
||||
|
||||
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.default_width(300.0)
|
||||
.default_height(300.0)
|
||||
.resizable(true)
|
||||
.constrain_to(ui.available_rect_before_wrap())
|
||||
.show(ui, |ui| {
|
||||
use crate::View as _;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::View for GroupDemo {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add(crate::egui_github_link_file!());
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Horizontal:");
|
||||
ui.selectable_value(&mut self.align_x, Align::Min, "Left");
|
||||
ui.selectable_value(&mut self.align_x, Align::Center, "Center");
|
||||
ui.selectable_value(&mut self.align_x, Align::Max, "Right");
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Vertical:");
|
||||
ui.selectable_value(&mut self.align_y, Align::Min, "Top");
|
||||
ui.selectable_value(&mut self.align_y, Align::Center, "Center");
|
||||
ui.selectable_value(&mut self.align_y, Align::Max, "Bottom");
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
let align2 = Align2([self.align_x, self.align_y]);
|
||||
|
||||
|
||||
Group::new("demo_group").align2(align2).ui(ui, |ui| {
|
||||
ui.label("Hello!");
|
||||
let _ = ui.button("A button");
|
||||
ui.label("More text");
|
||||
ScrollArea::vertical().max_height(50.0).show(ui, |ui| {
|
||||
for _ in 0..100 {
|
||||
ui.label("Even more text");
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.set_height(ui.available_height());
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ pub mod drag_and_drop;
|
||||
pub mod extra_viewport;
|
||||
pub mod font_book;
|
||||
pub mod frame_demo;
|
||||
pub mod group_demo;
|
||||
pub mod highlighting;
|
||||
pub mod interactive_container;
|
||||
pub mod misc_demo_window;
|
||||
|
||||
Reference in New Issue
Block a user