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

View File

@@ -141,7 +141,7 @@ impl eframe::App for MyApp {
); );
ui.separator(); 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| { egui::CentralPanel::default().show(ctx, |ui| {
@@ -157,30 +157,36 @@ impl eframe::App for MyApp {
fn tree_ui( fn tree_ui(
ui: &mut egui::Ui, ui: &mut egui::Ui,
behavior: &mut dyn dock::Behavior<View>, behavior: &mut dyn dock::Behavior<View>,
nodes: &dock::Nodes<View>, nodes: &mut dock::Nodes<View>,
node_id: dock::NodeId, 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!( let text = format!(
"{} - {node_id:?}", "{} - {node_id:?}",
behavior.tab_text_for_node(nodes, node_id).text() behavior.tab_text_for_node(nodes, node_id).text()
); );
let Some(mut node) = nodes.nodes.remove(&node_id) else { return; };
egui::CollapsingHeader::new(text) egui::CollapsingHeader::new(text)
.id_source((node_id, "tree")) .id_source((node_id, "tree"))
.default_open(true) .default_open(true)
.show(ui, |ui| match node { .show(ui, |ui| match &mut node {
dock::Node::Leaf(_) => {} dock::Node::Leaf(_) => {}
dock::Node::Branch(branch) => { 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 { for &child in &branch.children {
tree_ui(ui, behavior, nodes, child); tree_ui(ui, behavior, nodes, child);
} }
} }
}); });
nodes.nodes.insert(node_id, node);
} }