1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-27 23:13:13 -04:00

Change layout at runtime

This commit is contained in:
Emil Ernerfeldt
2023-04-25 16:26:53 +02:00
parent ea47d36de7
commit e714b33940
2 changed files with 78 additions and 61 deletions

View File

@@ -79,17 +79,17 @@ pub enum Node<Leaf> {
}
impl<Leaf> Node<Leaf> {
fn branch_type(&self) -> Option<BranchType> {
fn layout(&self) -> Option<Layout> {
match self {
Node::Leaf(_) => None,
Node::Branch(branch) => Some(branch.typ),
Node::Branch(branch) => Some(branch.layout),
}
}
}
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Branch {
pub typ: BranchType,
pub layout: Layout,
pub children: Vec<NodeId>,
/// Only if [`Self.typ`] == [`BranchType::Tab`]
@@ -100,10 +100,10 @@ pub struct Branch {
}
impl Branch {
pub fn new(typ: BranchType, children: Vec<NodeId>) -> Self {
pub fn new(layout: Layout, children: Vec<NodeId>) -> Self {
let active_tab = children.first().copied().unwrap_or_default();
Self {
typ,
layout,
children,
active_tab,
..Default::default()
@@ -111,7 +111,7 @@ impl Branch {
}
pub fn new_tabs(children: Vec<NodeId>) -> Self {
Self::new(BranchType::Tabs, children)
Self::new(Layout::Tabs, children)
}
}
@@ -144,28 +144,32 @@ impl Shares {
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum BranchType {
pub enum Layout {
#[default]
Tabs,
Horizontal,
Vertical,
}
impl Layout {
pub const ALL: [Self; 3] = [Self::Tabs, Self::Horizontal, Self::Vertical];
}
#[derive(Clone, Copy, Debug)]
struct InsertionPoint {
parent_id: NodeId,
branch_type: BranchType,
layout: Layout,
/// Where in the parent?
index: usize,
}
impl InsertionPoint {
fn new(parent_id: NodeId, branch_type: BranchType, index: usize) -> Self {
fn new(parent_id: NodeId, layout: Layout, index: usize) -> Self {
Self {
parent_id,
branch_type,
layout,
index,
}
}
@@ -215,7 +219,7 @@ pub trait Behavior<Leaf> {
fn tab_text_for_node(&mut self, nodes: &Nodes<Leaf>, node_id: NodeId) -> WidgetText {
match &nodes.nodes[&node_id] {
Node::Leaf(leaf) => self.tab_text_for_leaf(leaf),
Node::Branch(branch) => format!("{:?}", branch.typ).into(),
Node::Branch(branch) => format!("{:?}", branch.layout).into(),
}
}
@@ -311,12 +315,12 @@ impl<Leaf> Nodes<Leaf> {
#[must_use]
pub fn insert_horizontal_node(&mut self, children: Vec<NodeId>) -> NodeId {
self.insert_node(Node::Branch(Branch::new(BranchType::Horizontal, children)))
self.insert_node(Node::Branch(Branch::new(Layout::Horizontal, children)))
}
#[must_use]
pub fn insert_vertical_node(&mut self, children: Vec<NodeId>) -> NodeId {
self.insert_node(Node::Branch(Branch::new(BranchType::Vertical, children)))
self.insert_node(Node::Branch(Branch::new(Layout::Vertical, children)))
}
fn parent(&self, it: NodeId, needle_child: NodeId) -> Option<NodeId> {
@@ -354,7 +358,7 @@ impl<Leaf> Nodes<Leaf> {
fn insert(&mut self, insertion_point: InsertionPoint, child_id: NodeId) {
let InsertionPoint {
parent_id,
branch_type,
layout: branch_type,
index,
} = insertion_point;
let Some(mut node) = self.nodes.remove(&parent_id) else {
@@ -362,9 +366,9 @@ impl<Leaf> Nodes<Leaf> {
return;
};
match branch_type {
BranchType::Tabs => {
Layout::Tabs => {
if let Node::Branch(Branch {
typ: BranchType::Tabs,
layout: Layout::Tabs,
children,
..
}) = &mut node
@@ -379,9 +383,9 @@ impl<Leaf> Nodes<Leaf> {
self.nodes.insert(parent_id, Node::Branch(branch));
}
}
BranchType::Horizontal => {
Layout::Horizontal => {
if let Node::Branch(Branch {
typ: BranchType::Horizontal,
layout: Layout::Horizontal,
children,
..
}) = &mut node
@@ -391,14 +395,14 @@ impl<Leaf> Nodes<Leaf> {
self.nodes.insert(parent_id, node);
} else {
let new_node_id = self.insert_node(node);
let mut branch = Branch::new(BranchType::Horizontal, vec![new_node_id]);
let mut branch = Branch::new(Layout::Horizontal, vec![new_node_id]);
branch.children.insert(index.min(1), child_id);
self.nodes.insert(parent_id, Node::Branch(branch));
}
}
BranchType::Vertical => {
Layout::Vertical => {
if let Node::Branch(Branch {
typ: BranchType::Vertical,
layout: Layout::Vertical,
children,
..
}) = &mut node
@@ -408,7 +412,7 @@ impl<Leaf> Nodes<Leaf> {
self.nodes.insert(parent_id, node);
} else {
let new_node_id = self.insert_node(node);
let mut branch = Branch::new(BranchType::Vertical, vec![new_node_id]);
let mut branch = Branch::new(Layout::Vertical, vec![new_node_id]);
branch.children.insert(index.min(1), child_id);
self.nodes.insert(parent_id, Node::Branch(branch));
}
@@ -689,10 +693,10 @@ impl<Leaf> Nodes<Leaf> {
rect: Rect,
branch: &Branch,
) {
match branch.typ {
BranchType::Tabs => self.layout_tabs(style, behavior, rect, branch),
BranchType::Horizontal => self.layout_horizontal(style, behavior, rect, branch),
BranchType::Vertical => self.layout_vertical(style, behavior, rect, branch),
match branch.layout {
Layout::Tabs => self.layout_tabs(style, behavior, rect, branch),
Layout::Horizontal => self.layout_horizontal(style, behavior, rect, branch),
Layout::Vertical => self.layout_vertical(style, behavior, rect, branch),
}
}
@@ -790,24 +794,24 @@ impl DropContext {
return;
}
if node.branch_type() != Some(BranchType::Horizontal) {
if node.layout() != Some(Layout::Horizontal) {
self.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Horizontal, 0),
InsertionPoint::new(parent_id, Layout::Horizontal, 0),
rect.split_left_right_at_fraction(0.5).0,
);
self.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Horizontal, usize::MAX),
InsertionPoint::new(parent_id, Layout::Horizontal, usize::MAX),
rect.split_left_right_at_fraction(0.5).1,
);
}
if node.branch_type() != Some(BranchType::Vertical) {
if node.layout() != Some(Layout::Vertical) {
self.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Vertical, 0),
InsertionPoint::new(parent_id, Layout::Vertical, 0),
rect.split_top_bottom_at_fraction(0.5).0,
);
self.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Vertical, usize::MAX),
InsertionPoint::new(parent_id, Layout::Vertical, usize::MAX),
rect.split_top_bottom_at_fraction(0.5).1,
);
}
@@ -852,7 +856,7 @@ impl<Leaf> Nodes<Leaf> {
drop_context.on_node(node_id, rect, &node);
drop_context.suggest_rect(
InsertionPoint::new(node_id, BranchType::Tabs, usize::MAX),
InsertionPoint::new(node_id, Layout::Tabs, usize::MAX),
rect.split_top_bottom_at_y(rect.top() + behavior.tab_bar_height(ui.style()))
.1,
);
@@ -882,14 +886,14 @@ impl<Leaf> Nodes<Leaf> {
node_id: NodeId,
branch: &mut Branch,
) {
match branch.typ {
BranchType::Tabs => {
match branch.layout {
Layout::Tabs => {
self.tabs_ui(behavior, drop_context, ui, rect, node_id, branch);
}
BranchType::Horizontal => {
Layout::Horizontal => {
self.horizontal_ui(behavior, drop_context, ui, node_id, branch);
}
BranchType::Vertical => {
Layout::Vertical => {
self.vertical_ui(behavior, drop_context, ui, node_id, branch);
}
}
@@ -956,7 +960,7 @@ impl<Leaf> Nodes<Leaf> {
};
drop_context.suggest_rect(
InsertionPoint::new(node_id, BranchType::Tabs, insertion_index),
InsertionPoint::new(node_id, Layout::Tabs, insertion_index),
Rect::from_center_size(before_point, vec2(4.0, rect.height())),
);
}
@@ -964,7 +968,7 @@ impl<Leaf> Nodes<Leaf> {
if i + 1 == branch.children.len() {
// suggest dropping after last tab:
drop_context.suggest_rect(
InsertionPoint::new(node_id, BranchType::Tabs, insertion_index + 1),
InsertionPoint::new(node_id, Layout::Tabs, insertion_index + 1),
Rect::from_center_size(rect.right_center(), vec2(4.0, rect.height())),
);
}
@@ -998,23 +1002,21 @@ impl<Leaf> Nodes<Leaf> {
if is_being_dragged(ui.ctx(), child) {
// Leave a hole, and suggest that hole as drop-target:
drop_context.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Horizontal, i),
rect,
);
drop_context
.suggest_rect(InsertionPoint::new(parent_id, Layout::Horizontal, i), rect);
} else {
self.node_ui(behavior, drop_context, ui, child);
if let Some(prev_rect) = prev_rect {
// Suggest dropping between the rects:
drop_context.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Horizontal, insertion_index),
InsertionPoint::new(parent_id, Layout::Horizontal, insertion_index),
Rect::from_min_max(prev_rect.center_top(), rect.center_bottom()),
);
} else {
// Suggest dropping before the first child:
drop_context.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Horizontal, 0),
InsertionPoint::new(parent_id, Layout::Horizontal, 0),
rect.split_left_right_at_fraction(0.66).0,
);
}
@@ -1022,7 +1024,7 @@ impl<Leaf> Nodes<Leaf> {
if i + 1 == branch.children.len() {
// Suggest dropping after the last child:
drop_context.suggest_rect(
InsertionPoint::new(parent_id, BranchType::Horizontal, insertion_index + 1),
InsertionPoint::new(parent_id, Layout::Horizontal, insertion_index + 1),
rect.split_left_right_at_fraction(0.33).1,
);
}
@@ -1061,7 +1063,12 @@ impl<Leaf> Nodes<Leaf> {
fn simplify(&mut self, options: &SimplificationOptions, it: NodeId) -> SimplifyAction {
let Some(mut node) = self.nodes.remove(&it) else { return SimplifyAction::Remove; };
if let Node::Branch(Branch { typ, children, .. }) = &mut node {
if let Node::Branch(Branch {
layout: typ,
children,
..
}) = &mut node
{
// TODO: join nested versions of the same horizontal/vertical layouts
children.retain_mut(|child| match self.simplify(options, *child) {
@@ -1074,7 +1081,7 @@ impl<Leaf> Nodes<Leaf> {
});
match typ {
BranchType::Tabs => {
Layout::Tabs => {
if options.prune_empty_tabs && children.is_empty() {
log::debug!("Simplify: removing empty tabs node");
return SimplifyAction::Remove;
@@ -1090,7 +1097,7 @@ impl<Leaf> Nodes<Leaf> {
}
}
}
BranchType::Horizontal | BranchType::Vertical => {
Layout::Horizontal | Layout::Vertical => {
if options.prune_empty_layouts && children.is_empty() {
log::debug!("Simplify: removing empty layout node");
return SimplifyAction::Remove;
@@ -1123,8 +1130,12 @@ impl<Leaf> Nodes<Leaf> {
return;
}
}
Node::Branch(Branch { typ, children, .. }) => {
let is_tabs = *typ == BranchType::Tabs;
Node::Branch(Branch {
layout: typ,
children,
..
}) => {
let is_tabs = *typ == Layout::Tabs;
for child in children {
self.make_all_leaves_children_of_tabs(is_tabs, *child);
}

View File

@@ -141,7 +141,7 @@ impl eframe::App for MyApp {
);
ui.separator();
tree_ui(ui, &mut self.behavior, &self.dock.nodes, self.dock.root);
tree_ui(ui, &mut self.behavior, &mut self.dock.nodes, self.dock.root);
});
egui::CentralPanel::default().show(ctx, |ui| {
@@ -157,30 +157,36 @@ impl eframe::App for MyApp {
fn tree_ui(
ui: &mut egui::Ui,
behavior: &mut dyn dock::Behavior<View>,
nodes: &dock::Nodes<View>,
nodes: &mut dock::Nodes<View>,
node_id: dock::NodeId,
) {
let Some(node) = nodes.get(node_id) else { return; };
// if let dock::NodeLayout::Leaf(view) = node {
// ui.label(&view.title);
// return;
// }
let text = format!(
"{} - {node_id:?}",
behavior.tab_text_for_node(nodes, node_id).text()
);
let Some(mut node) = nodes.nodes.remove(&node_id) else { return; };
egui::CollapsingHeader::new(text)
.id_source((node_id, "tree"))
.default_open(true)
.show(ui, |ui| match node {
.show(ui, |ui| match &mut node {
dock::Node::Leaf(_) => {}
dock::Node::Branch(branch) => {
egui::ComboBox::from_label("Layout")
.selected_text(format!("{:?}", branch.layout))
.show_ui(ui, |ui| {
for typ in dock::Layout::ALL {
ui.selectable_value(&mut branch.layout, typ, format!("{:?}", typ))
.clicked();
}
});
for &child in &branch.children {
tree_ui(ui, behavior, nodes, child);
}
}
});
nodes.nodes.insert(node_id, node);
}