mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 06:10:06 -04:00
Turn Branch into an enum
This commit is contained in:
@@ -4,7 +4,7 @@ use egui::{pos2, vec2, NumExt as _, Rect};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
is_being_dragged, is_possible_drag, sizes_from_shares, Behavior, DropContext, GridLoc,
|
is_being_dragged, is_possible_drag, sizes_from_shares, Behavior, DropContext, GridLoc,
|
||||||
InsertionPoint, LayoutInsertion, NodeId, Nodes,
|
InsertionPoint, LayoutInsertion, NodeId, Nodes, SimplifyAction,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -60,40 +60,168 @@ impl Shares {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Branch {
|
pub enum Branch {
|
||||||
pub layout: Layout,
|
Tabs(Tabs),
|
||||||
pub children: Vec<NodeId>,
|
Linear(Linear),
|
||||||
|
Grid(Grid),
|
||||||
/// Only if [`Self.typ`] == [`Layout::Tab`]
|
|
||||||
pub active_tab: NodeId,
|
|
||||||
|
|
||||||
/// Only for linear layouts (horizontal or vertical).
|
|
||||||
pub linear_shares: Shares,
|
|
||||||
|
|
||||||
/// Only if [`Self.typ`] == [`Layout::Grid`].
|
|
||||||
/// col,row
|
|
||||||
pub grid_locations: HashMap<NodeId, GridLoc>,
|
|
||||||
|
|
||||||
/// Share of the avilable width assigned to each column.
|
|
||||||
pub grid_col_shares: Vec<f32>,
|
|
||||||
/// Share of the avilable height assigned to each row.
|
|
||||||
pub grid_row_shares: Vec<f32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Branch {
|
impl Branch {
|
||||||
pub fn new(layout: Layout, children: Vec<NodeId>) -> Self {
|
pub fn new_linear(dir: LinearDir, children: Vec<NodeId>) -> Self {
|
||||||
let active_tab = children.first().copied().unwrap_or_default();
|
Self::Linear(Linear::new(dir, children))
|
||||||
Self {
|
|
||||||
layout,
|
|
||||||
children,
|
|
||||||
active_tab,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_tabs(children: Vec<NodeId>) -> Self {
|
pub fn new_tabs(children: Vec<NodeId>) -> Self {
|
||||||
Self::new(Layout::Tabs, children)
|
Self::Tabs(Tabs::new(children))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_grid(children: Vec<NodeId>) -> Self {
|
||||||
|
Self::Grid(Grid::new(children))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.children().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children(&self) -> &[NodeId] {
|
||||||
|
match self {
|
||||||
|
Self::Tabs(tabs) => &tabs.children,
|
||||||
|
Self::Linear(linear) => &linear.children,
|
||||||
|
Self::Grid(grid) => &grid.children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_layout(&self) -> Layout {
|
||||||
|
match self {
|
||||||
|
Self::Tabs(_) => Layout::Tabs,
|
||||||
|
Self::Linear(linear) => match linear.dir {
|
||||||
|
LinearDir::Horizontal => Layout::Horizontal,
|
||||||
|
LinearDir::Vertical => Layout::Vertical,
|
||||||
|
},
|
||||||
|
Self::Grid(_) => Layout::Grid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_layout(&mut self, layout: Layout) {
|
||||||
|
if layout == self.get_layout() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*self = match layout {
|
||||||
|
Layout::Tabs => Self::Tabs(Tabs::new(self.children().to_vec())),
|
||||||
|
Layout::Horizontal => {
|
||||||
|
Self::Linear(Linear::new(LinearDir::Horizontal, self.children().to_vec()))
|
||||||
|
}
|
||||||
|
Layout::Vertical => {
|
||||||
|
Self::Linear(Linear::new(LinearDir::Vertical, self.children().to_vec()))
|
||||||
|
}
|
||||||
|
Layout::Grid => Self::Grid(Grid::new(self.children().to_vec())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn retain(&mut self, mut retain: impl FnMut(NodeId) -> bool) {
|
||||||
|
let retain = |node_id: &NodeId| retain(*node_id);
|
||||||
|
match self {
|
||||||
|
Self::Tabs(tabs) => tabs.children.retain(retain),
|
||||||
|
Self::Linear(linear) => linear.children.retain(retain),
|
||||||
|
Self::Grid(grid) => grid.children.retain(retain),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn simplify_children(&mut self, mut simplify: impl FnMut(NodeId) -> SimplifyAction) {
|
||||||
|
match self {
|
||||||
|
Self::Tabs(tabs) => tabs.children.retain_mut(|child| match simplify(*child) {
|
||||||
|
SimplifyAction::Remove => false,
|
||||||
|
SimplifyAction::Keep => true,
|
||||||
|
SimplifyAction::Replace(new) => {
|
||||||
|
if tabs.active == *child {
|
||||||
|
tabs.active = new;
|
||||||
|
}
|
||||||
|
*child = new;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Self::Linear(linear) => linear.children.retain_mut(|child| match simplify(*child) {
|
||||||
|
SimplifyAction::Remove => false,
|
||||||
|
SimplifyAction::Keep => true,
|
||||||
|
SimplifyAction::Replace(new) => {
|
||||||
|
linear.shares.replace_with(*child, new);
|
||||||
|
*child = new;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Self::Grid(grid) => grid.children.retain_mut(|child| match simplify(*child) {
|
||||||
|
SimplifyAction::Remove => false,
|
||||||
|
SimplifyAction::Keep => true,
|
||||||
|
SimplifyAction::Replace(new) => {
|
||||||
|
if let Some(loc) = grid.locations.remove(child) {
|
||||||
|
grid.locations.insert(new, loc);
|
||||||
|
}
|
||||||
|
*child = new;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Tabs {
|
||||||
|
pub children: Vec<NodeId>,
|
||||||
|
pub active: NodeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tabs {
|
||||||
|
pub fn new(children: Vec<NodeId>) -> Self {
|
||||||
|
let active = children.first().copied().unwrap_or_default();
|
||||||
|
Self { children, active }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum LinearDir {
|
||||||
|
#[default]
|
||||||
|
Horizontal,
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Horizontal or vertical layout.
|
||||||
|
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Linear {
|
||||||
|
pub children: Vec<NodeId>,
|
||||||
|
pub dir: LinearDir,
|
||||||
|
pub shares: Shares,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Linear {
|
||||||
|
pub fn new(dir: LinearDir, children: Vec<NodeId>) -> Self {
|
||||||
|
Self {
|
||||||
|
children,
|
||||||
|
dir,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Grid {
|
||||||
|
pub children: Vec<NodeId>,
|
||||||
|
|
||||||
|
pub locations: HashMap<NodeId, GridLoc>,
|
||||||
|
|
||||||
|
/// Share of the avilable width assigned to each column.
|
||||||
|
pub col_shares: Vec<f32>,
|
||||||
|
/// Share of the avilable height assigned to each row.
|
||||||
|
pub row_shares: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grid {
|
||||||
|
pub fn new(children: Vec<NodeId>) -> Self {
|
||||||
|
Self {
|
||||||
|
children,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,21 +238,22 @@ impl Branch {
|
|||||||
rect: Rect,
|
rect: Rect,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
) {
|
) {
|
||||||
if self.children.is_empty() {
|
if self.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.layout {
|
match self {
|
||||||
Layout::Tabs => self.layout_tabs(nodes, style, behavior, drop_context, rect),
|
Branch::Tabs(tabs) => tabs.layout(nodes, style, behavior, drop_context, rect),
|
||||||
Layout::Horizontal => {
|
Branch::Linear(linear) => {
|
||||||
self.layout_horizontal(nodes, style, behavior, drop_context, rect);
|
linear.layout(nodes, style, behavior, drop_context, rect);
|
||||||
}
|
}
|
||||||
Layout::Vertical => self.layout_vertical(nodes, style, behavior, drop_context, rect),
|
Branch::Grid(grid) => grid.layout(nodes, style, behavior, drop_context, rect, node_id),
|
||||||
Layout::Grid => self.layout_grid(nodes, style, behavior, drop_context, rect, node_id),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn layout_tabs<Leaf>(
|
impl Tabs {
|
||||||
|
fn layout<Leaf>(
|
||||||
&self,
|
&self,
|
||||||
nodes: &mut Nodes<Leaf>,
|
nodes: &mut Nodes<Leaf>,
|
||||||
style: &egui::Style,
|
style: &egui::Style,
|
||||||
@@ -136,7 +265,7 @@ impl Branch {
|
|||||||
active_rect.min.y += behavior.tab_bar_height(style);
|
active_rect.min.y += behavior.tab_bar_height(style);
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
nodes.layout_node(style, behavior, drop_context, active_rect, self.active_tab);
|
nodes.layout_node(style, behavior, drop_context, active_rect, self.active);
|
||||||
} else {
|
} else {
|
||||||
// Layout all nodes in case the user switches active tab
|
// Layout all nodes in case the user switches active tab
|
||||||
// TODO: only layout active tab, or don't register drop-zones during layout.
|
// TODO: only layout active tab, or don't register drop-zones during layout.
|
||||||
@@ -145,6 +274,24 @@ impl Branch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Linear {
|
||||||
|
fn layout<Leaf>(
|
||||||
|
&mut self,
|
||||||
|
nodes: &mut Nodes<Leaf>,
|
||||||
|
style: &egui::Style,
|
||||||
|
behavior: &mut dyn Behavior<Leaf>,
|
||||||
|
drop_context: &mut DropContext,
|
||||||
|
rect: Rect,
|
||||||
|
) {
|
||||||
|
match self.dir {
|
||||||
|
LinearDir::Horizontal => {
|
||||||
|
self.layout_horizontal(nodes, style, behavior, drop_context, rect);
|
||||||
|
}
|
||||||
|
LinearDir::Vertical => self.layout_vertical(nodes, style, behavior, drop_context, rect),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn layout_horizontal<Leaf>(
|
fn layout_horizontal<Leaf>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -159,7 +306,7 @@ impl Branch {
|
|||||||
let total_gap_width = gap_width * num_gaps as f32;
|
let total_gap_width = gap_width * num_gaps as f32;
|
||||||
let available_width = (rect.width() - total_gap_width).at_least(0.0);
|
let available_width = (rect.width() - total_gap_width).at_least(0.0);
|
||||||
|
|
||||||
let widths = self.linear_shares.split(&self.children, available_width);
|
let widths = self.shares.split(&self.children, available_width);
|
||||||
|
|
||||||
let mut x = rect.min.x;
|
let mut x = rect.min.x;
|
||||||
for (child, width) in self.children.iter().zip(widths) {
|
for (child, width) in self.children.iter().zip(widths) {
|
||||||
@@ -182,7 +329,7 @@ impl Branch {
|
|||||||
let total_gap_height = gap_height * num_gaps as f32;
|
let total_gap_height = gap_height * num_gaps as f32;
|
||||||
let available_height = (rect.height() - total_gap_height).at_least(0.0);
|
let available_height = (rect.height() - total_gap_height).at_least(0.0);
|
||||||
|
|
||||||
let heights = self.linear_shares.split(&self.children, available_height);
|
let heights = self.shares.split(&self.children, available_height);
|
||||||
|
|
||||||
let mut y = rect.min.y;
|
let mut y = rect.min.y;
|
||||||
for (child, height) in self.children.iter().zip(heights) {
|
for (child, height) in self.children.iter().zip(heights) {
|
||||||
@@ -191,8 +338,10 @@ impl Branch {
|
|||||||
y += height + gap_height;
|
y += height + gap_height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn layout_grid<Leaf>(
|
impl Grid {
|
||||||
|
fn layout<Leaf>(
|
||||||
&mut self,
|
&mut self,
|
||||||
nodes: &mut Nodes<Leaf>,
|
nodes: &mut Nodes<Leaf>,
|
||||||
style: &egui::Style,
|
style: &egui::Style,
|
||||||
@@ -207,7 +356,7 @@ impl Branch {
|
|||||||
|
|
||||||
// Where to place each node?
|
// Where to place each node?
|
||||||
let mut node_id_from_location: BTreeMap<GridLoc, NodeId> = Default::default();
|
let mut node_id_from_location: BTreeMap<GridLoc, NodeId> = Default::default();
|
||||||
self.grid_locations.retain(|&child_id, &mut loc| {
|
self.locations.retain(|&child_id, &mut loc| {
|
||||||
if child_ids.contains(&child_id) {
|
if child_ids.contains(&child_id) {
|
||||||
match node_id_from_location.entry(loc) {
|
match node_id_from_location.entry(loc) {
|
||||||
std::collections::btree_map::Entry::Occupied(_) => {
|
std::collections::btree_map::Entry::Occupied(_) => {
|
||||||
@@ -227,8 +376,7 @@ impl Branch {
|
|||||||
// Find location for nodes that don't have one yet
|
// Find location for nodes that don't have one yet
|
||||||
let mut next_pos = 0;
|
let mut next_pos = 0;
|
||||||
for &child_id in &self.children {
|
for &child_id in &self.children {
|
||||||
if let std::collections::hash_map::Entry::Vacant(entry) =
|
if let std::collections::hash_map::Entry::Vacant(entry) = self.locations.entry(child_id)
|
||||||
self.grid_locations.entry(child_id)
|
|
||||||
{
|
{
|
||||||
// find a position:
|
// find a position:
|
||||||
loop {
|
loop {
|
||||||
@@ -248,12 +396,12 @@ impl Branch {
|
|||||||
let num_rows = node_id_from_location.keys().last().unwrap().row + 1;
|
let num_rows = node_id_from_location.keys().last().unwrap().row + 1;
|
||||||
|
|
||||||
// Figure out where each column and row goes:
|
// Figure out where each column and row goes:
|
||||||
self.grid_col_shares.resize(num_cols, 1.0);
|
self.col_shares.resize(num_cols, 1.0);
|
||||||
self.grid_row_shares.resize(num_rows, 1.0);
|
self.row_shares.resize(num_rows, 1.0);
|
||||||
|
|
||||||
let gap = behavior.gap_width(style);
|
let gap = behavior.gap_width(style);
|
||||||
let col_widths = sizes_from_shares(&self.grid_col_shares, rect.width(), gap);
|
let col_widths = sizes_from_shares(&self.col_shares, rect.width(), gap);
|
||||||
let row_heights = sizes_from_shares(&self.grid_row_shares, rect.height(), gap);
|
let row_heights = sizes_from_shares(&self.row_shares, rect.height(), gap);
|
||||||
|
|
||||||
let mut col_x = vec![rect.left()];
|
let mut col_x = vec![rect.left()];
|
||||||
for &width in &col_widths {
|
for &width in &col_widths {
|
||||||
@@ -266,12 +414,11 @@ impl Branch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Each child now has a location. Use this to order them, in case we will ater do auto-layouts:
|
// Each child now has a location. Use this to order them, in case we will ater do auto-layouts:
|
||||||
self.children
|
self.children.sort_by_key(|&child| self.locations[&child]);
|
||||||
.sort_by_key(|&child| self.grid_locations[&child]);
|
|
||||||
|
|
||||||
// Place each child:
|
// Place each child:
|
||||||
for &child in &self.children {
|
for &child in &self.children {
|
||||||
let loc = self.grid_locations[&child];
|
let loc = self.locations[&child];
|
||||||
let child_rect = Rect::from_min_size(
|
let child_rect = Rect::from_min_size(
|
||||||
pos2(col_x[loc.col], row_y[loc.row]),
|
pos2(col_x[loc.col], row_y[loc.row]),
|
||||||
vec2(col_widths[loc.col], row_heights[loc.row]),
|
vec2(col_widths[loc.col], row_heights[loc.row]),
|
||||||
@@ -310,23 +457,22 @@ impl Branch {
|
|||||||
rect: Rect,
|
rect: Rect,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
) {
|
) {
|
||||||
match self.layout {
|
match self {
|
||||||
Layout::Tabs => {
|
Branch::Tabs(tabs) => {
|
||||||
self.tabs_ui(nodes, behavior, drop_context, ui, rect, node_id);
|
tabs.ui(nodes, behavior, drop_context, ui, rect, node_id);
|
||||||
}
|
}
|
||||||
Layout::Horizontal => {
|
Branch::Linear(linear) => {
|
||||||
self.horizontal_ui(nodes, behavior, drop_context, ui, node_id);
|
linear.ui(nodes, behavior, drop_context, ui, node_id);
|
||||||
}
|
}
|
||||||
Layout::Vertical => {
|
Branch::Grid(grid) => {
|
||||||
self.vertical_ui(nodes, behavior, drop_context, ui, node_id);
|
grid.grid_ui(nodes, behavior, drop_context, ui);
|
||||||
}
|
|
||||||
Layout::Grid => {
|
|
||||||
self.grid_ui(nodes, behavior, drop_context, ui);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn tabs_ui<Leaf>(
|
impl Tabs {
|
||||||
|
fn ui<Leaf>(
|
||||||
&mut self,
|
&mut self,
|
||||||
nodes: &mut Nodes<Leaf>,
|
nodes: &mut Nodes<Leaf>,
|
||||||
behavior: &mut dyn Behavior<Leaf>,
|
behavior: &mut dyn Behavior<Leaf>,
|
||||||
@@ -335,9 +481,9 @@ impl Branch {
|
|||||||
rect: Rect,
|
rect: Rect,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
) {
|
) {
|
||||||
if !self.children.iter().any(|&child| child == self.active_tab) {
|
if !self.children.iter().any(|&child| child == self.active) {
|
||||||
// Make sure something is active:
|
// Make sure something is active:
|
||||||
self.active_tab = self.children.first().copied().unwrap_or_default();
|
self.active = self.children.first().copied().unwrap_or_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
let tab_bar_height = behavior.tab_bar_height(ui.style());
|
let tab_bar_height = behavior.tab_bar_height(ui.style());
|
||||||
@@ -354,19 +500,19 @@ impl Branch {
|
|||||||
continue; // leave a gap!
|
continue; // leave a gap!
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected = child_id == self.active_tab;
|
let selected = child_id == self.active;
|
||||||
let id = child_id.id();
|
let id = child_id.id();
|
||||||
|
|
||||||
let response = behavior.tab_ui(nodes, ui, id, child_id, selected);
|
let response = behavior.tab_ui(nodes, ui, id, child_id, selected);
|
||||||
let response = response.on_hover_cursor(egui::CursorIcon::Grab);
|
let response = response.on_hover_cursor(egui::CursorIcon::Grab);
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
self.active_tab = child_id;
|
self.active = child_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mouse_pos) = drop_context.mouse_pos {
|
if let Some(mouse_pos) = drop_context.mouse_pos {
|
||||||
if drop_context.dragged_node_id.is_some() && response.rect.contains(mouse_pos) {
|
if drop_context.dragged_node_id.is_some() && response.rect.contains(mouse_pos) {
|
||||||
// Expand this tab - maybe the user wants to drop something into it!
|
// Expand this tab - maybe the user wants to drop something into it!
|
||||||
self.active_tab = child_id;
|
self.active = child_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,10 +548,26 @@ impl Branch {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// When dragged, don't show it (it is "being held")
|
// When dragged, don't show it (it is "being held")
|
||||||
let is_active_being_dragged = ui.memory(|mem| mem.is_being_dragged(self.active_tab.id()))
|
let is_active_being_dragged =
|
||||||
&& is_possible_drag(ui.ctx());
|
ui.memory(|mem| mem.is_being_dragged(self.active.id())) && is_possible_drag(ui.ctx());
|
||||||
if !is_active_being_dragged {
|
if !is_active_being_dragged {
|
||||||
nodes.node_ui(behavior, drop_context, ui, self.active_tab);
|
nodes.node_ui(behavior, drop_context, ui, self.active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Linear {
|
||||||
|
fn ui<Leaf>(
|
||||||
|
&mut self,
|
||||||
|
nodes: &mut Nodes<Leaf>,
|
||||||
|
behavior: &mut dyn Behavior<Leaf>,
|
||||||
|
drop_context: &mut DropContext,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
node_id: NodeId,
|
||||||
|
) {
|
||||||
|
match self.dir {
|
||||||
|
LinearDir::Horizontal => self.horizontal_ui(nodes, behavior, drop_context, ui, node_id),
|
||||||
|
LinearDir::Vertical => self.vertical_ui(nodes, behavior, drop_context, ui, node_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +641,9 @@ impl Branch {
|
|||||||
nodes.node_ui(behavior, drop_context, ui, *child);
|
nodes.node_ui(behavior, drop_context, ui, *child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Grid {
|
||||||
fn grid_ui<Leaf>(
|
fn grid_ui<Leaf>(
|
||||||
&mut self,
|
&mut self,
|
||||||
nodes: &mut Nodes<Leaf>,
|
nodes: &mut Nodes<Leaf>,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use egui::{Id, Key, NumExt, Pos2, Rect, Response, Sense, TextStyle, Ui, WidgetTe
|
|||||||
|
|
||||||
mod branch;
|
mod branch;
|
||||||
|
|
||||||
pub use branch::{Branch, Layout};
|
pub use branch::{Branch, Grid, Layout, Linear, LinearDir, Tabs};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Types required for state
|
// Types required for state
|
||||||
@@ -83,7 +83,7 @@ impl<Leaf> Node<Leaf> {
|
|||||||
fn layout(&self) -> Option<Layout> {
|
fn layout(&self) -> Option<Layout> {
|
||||||
match self {
|
match self {
|
||||||
Node::Leaf(_) => None,
|
Node::Leaf(_) => None,
|
||||||
Node::Branch(branch) => Some(branch.layout),
|
Node::Branch(branch) => Some(branch.get_layout()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,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.layout).into(),
|
Node::Branch(branch) => format!("{:?}", branch.get_layout()).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,24 +279,30 @@ 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(Layout::Horizontal, children)))
|
self.insert_node(Node::Branch(Branch::new_linear(
|
||||||
|
LinearDir::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(Layout::Vertical, children)))
|
self.insert_node(Node::Branch(Branch::new_linear(
|
||||||
|
LinearDir::Vertical,
|
||||||
|
children,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn insert_grid_node(&mut self, children: Vec<NodeId>) -> NodeId {
|
pub fn insert_grid_node(&mut self, children: Vec<NodeId>) -> NodeId {
|
||||||
self.insert_node(Node::Branch(Branch::new(Layout::Grid, children)))
|
self.insert_node(Node::Branch(Branch::new_grid(children)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent(&self, it: NodeId, needle_child: NodeId) -> Option<NodeId> {
|
fn parent(&self, it: NodeId, needle_child: NodeId) -> Option<NodeId> {
|
||||||
match &self.nodes.get(&it)? {
|
match &self.nodes.get(&it)? {
|
||||||
Node::Leaf(_) => None,
|
Node::Leaf(_) => None,
|
||||||
Node::Branch(branch) => {
|
Node::Branch(branch) => {
|
||||||
for &child in &branch.children {
|
for &child in branch.children() {
|
||||||
if child == needle_child {
|
if child == needle_child {
|
||||||
return Some(it);
|
return Some(it);
|
||||||
}
|
}
|
||||||
@@ -316,9 +322,7 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
}
|
}
|
||||||
let Some(mut node) = self.nodes.remove(&it) else { return GcAction::Remove; };
|
let Some(mut node) = self.nodes.remove(&it) else { return GcAction::Remove; };
|
||||||
if let Node::Branch(branch) = &mut node {
|
if let Node::Branch(branch) = &mut node {
|
||||||
branch
|
branch.retain(|child| self.remove_node_id_from_parent(child, remove) == GcAction::Keep);
|
||||||
.children
|
|
||||||
.retain(|&child| self.remove_node_id_from_parent(child, remove) == GcAction::Keep);
|
|
||||||
}
|
}
|
||||||
self.nodes.insert(it, node);
|
self.nodes.insert(it, node);
|
||||||
GcAction::Keep
|
GcAction::Keep
|
||||||
@@ -337,73 +341,66 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
|
|
||||||
match insertion {
|
match insertion {
|
||||||
LayoutInsertion::Tabs(index) => {
|
LayoutInsertion::Tabs(index) => {
|
||||||
if let Node::Branch(Branch {
|
if let Node::Branch(Branch::Tabs(tabs)) = &mut node {
|
||||||
layout: Layout::Tabs,
|
let index = index.min(tabs.children.len());
|
||||||
children,
|
tabs.children.insert(index, child_id);
|
||||||
..
|
|
||||||
}) = &mut node
|
|
||||||
{
|
|
||||||
let index = index.min(children.len());
|
|
||||||
children.insert(index, child_id);
|
|
||||||
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_tabs(vec![new_node_id]);
|
let mut tabs = Tabs::new(vec![new_node_id]);
|
||||||
branch.children.insert(index.min(1), child_id);
|
tabs.children.insert(index.min(1), child_id);
|
||||||
self.nodes.insert(parent_id, Node::Branch(branch));
|
self.nodes
|
||||||
|
.insert(parent_id, Node::Branch(Branch::Tabs(tabs)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LayoutInsertion::Horizontal(index) => {
|
LayoutInsertion::Horizontal(index) => {
|
||||||
if let Node::Branch(Branch {
|
if let Node::Branch(Branch::Linear(Linear {
|
||||||
layout: Layout::Horizontal,
|
dir: LinearDir::Horizontal,
|
||||||
children,
|
children,
|
||||||
..
|
..
|
||||||
}) = &mut node
|
})) = &mut node
|
||||||
{
|
{
|
||||||
let index = index.min(children.len());
|
let index = index.min(children.len());
|
||||||
children.insert(index, child_id);
|
children.insert(index, child_id);
|
||||||
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(Layout::Horizontal, vec![new_node_id]);
|
let mut linear = Linear::new(LinearDir::Horizontal, vec![new_node_id]);
|
||||||
branch.children.insert(index.min(1), child_id);
|
linear.children.insert(index.min(1), child_id);
|
||||||
self.nodes.insert(parent_id, Node::Branch(branch));
|
self.nodes
|
||||||
|
.insert(parent_id, Node::Branch(Branch::Linear(linear)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LayoutInsertion::Vertical(index) => {
|
LayoutInsertion::Vertical(index) => {
|
||||||
if let Node::Branch(Branch {
|
if let Node::Branch(Branch::Linear(Linear {
|
||||||
layout: Layout::Vertical,
|
dir: LinearDir::Vertical,
|
||||||
children,
|
children,
|
||||||
..
|
..
|
||||||
}) = &mut node
|
})) = &mut node
|
||||||
{
|
{
|
||||||
let index = index.min(children.len());
|
let index = index.min(children.len());
|
||||||
children.insert(index, child_id);
|
children.insert(index, child_id);
|
||||||
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(Layout::Vertical, vec![new_node_id]);
|
let mut linear = Linear::new(LinearDir::Vertical, vec![new_node_id]);
|
||||||
branch.children.insert(index.min(1), child_id);
|
linear.children.insert(index.min(1), child_id);
|
||||||
self.nodes.insert(parent_id, Node::Branch(branch));
|
self.nodes
|
||||||
|
.insert(parent_id, Node::Branch(Branch::Linear(linear)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LayoutInsertion::Grid(insert_location) => {
|
LayoutInsertion::Grid(insert_location) => {
|
||||||
if let Node::Branch(Branch {
|
if let Node::Branch(Branch::Grid(grid)) = &mut node {
|
||||||
layout: Layout::Grid,
|
grid.locations.retain(|_, pos| *pos != insert_location);
|
||||||
grid_locations,
|
grid.locations.insert(child_id, insert_location);
|
||||||
children,
|
grid.children.push(child_id);
|
||||||
..
|
|
||||||
}) = &mut node
|
|
||||||
{
|
|
||||||
grid_locations.retain(|_, pos| *pos != insert_location);
|
|
||||||
grid_locations.insert(child_id, insert_location);
|
|
||||||
children.push(child_id);
|
|
||||||
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(Layout::Grid, vec![new_node_id, child_id]);
|
let mut grid = Grid::new(vec![new_node_id, child_id]);
|
||||||
branch.grid_locations.insert(child_id, insert_location);
|
grid.locations.insert(child_id, insert_location);
|
||||||
self.nodes.insert(parent_id, Node::Branch(branch));
|
self.nodes
|
||||||
|
.insert(parent_id, Node::Branch(Branch::Grid(grid)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -645,9 +642,7 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Node::Branch(branch) => {
|
Node::Branch(branch) => {
|
||||||
branch
|
branch.retain(|child| self.gc_node_id(behavior, visited, child) == GcAction::Keep);
|
||||||
.children
|
|
||||||
.retain(|&child| self.gc_node_id(behavior, visited, child) == GcAction::Keep);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.nodes.insert(node_id, node);
|
self.nodes.insert(node_id, node);
|
||||||
@@ -820,48 +815,31 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
if let Node::Branch(branch) = &mut node {
|
if let Node::Branch(branch) = &mut node {
|
||||||
// TODO: join nested versions of the same horizontal/vertical layouts
|
// TODO: join nested versions of the same horizontal/vertical layouts
|
||||||
|
|
||||||
branch
|
branch.simplify_children(|child| self.simplify(options, child));
|
||||||
.children
|
|
||||||
.retain_mut(|child| match self.simplify(options, *child) {
|
|
||||||
SimplifyAction::Remove => false,
|
|
||||||
SimplifyAction::Keep => true,
|
|
||||||
SimplifyAction::Replace(new) => {
|
|
||||||
if branch.active_tab == *child {
|
|
||||||
branch.active_tab = new;
|
|
||||||
}
|
|
||||||
branch.linear_shares.replace_with(*child, new);
|
|
||||||
if let Some(loc) = branch.grid_locations.remove(child) {
|
|
||||||
branch.grid_locations.insert(new, loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
*child = new;
|
if branch.get_layout() == Layout::Tabs {
|
||||||
true
|
if options.prune_empty_tabs && branch.is_empty() {
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if branch.layout == Layout::Tabs {
|
|
||||||
if options.prune_empty_tabs && branch.children.is_empty() {
|
|
||||||
log::debug!("Simplify: removing empty tabs node");
|
log::debug!("Simplify: removing empty tabs node");
|
||||||
return SimplifyAction::Remove;
|
return SimplifyAction::Remove;
|
||||||
}
|
}
|
||||||
if options.prune_single_child_tabs && branch.children.len() == 1 {
|
if options.prune_single_child_tabs && branch.children().len() == 1 {
|
||||||
if options.all_leaves_must_have_tabs
|
if options.all_leaves_must_have_tabs
|
||||||
&& matches!(self.get(branch.children[0]), Some(Node::Leaf(_)))
|
&& matches!(self.get(branch.children()[0]), Some(Node::Leaf(_)))
|
||||||
{
|
{
|
||||||
// Keep it
|
// Keep it
|
||||||
} else {
|
} else {
|
||||||
log::debug!("Simplify: collapsing single-child tabs node");
|
log::debug!("Simplify: collapsing single-child tabs node");
|
||||||
return SimplifyAction::Replace(branch.children[0]);
|
return SimplifyAction::Replace(branch.children()[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if options.prune_empty_layouts && branch.children.is_empty() {
|
if options.prune_empty_layouts && branch.is_empty() {
|
||||||
log::debug!("Simplify: removing empty layout node");
|
log::debug!("Simplify: removing empty layout node");
|
||||||
return SimplifyAction::Remove;
|
return SimplifyAction::Remove;
|
||||||
}
|
}
|
||||||
if options.prune_single_child_layouts && branch.children.len() == 1 {
|
if options.prune_single_child_layouts && branch.children().len() == 1 {
|
||||||
log::debug!("Simplify: collapsing single-child layout node");
|
log::debug!("Simplify: collapsing single-child layout node");
|
||||||
return SimplifyAction::Replace(branch.children[0]);
|
return SimplifyAction::Replace(branch.children()[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -888,8 +866,8 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Node::Branch(branch) => {
|
Node::Branch(branch) => {
|
||||||
let is_tabs = branch.layout == Layout::Tabs;
|
let is_tabs = branch.get_layout() == Layout::Tabs;
|
||||||
for &child in &branch.children {
|
for &child in branch.children() {
|
||||||
self.make_all_leaves_children_of_tabs(is_tabs, child);
|
self.make_all_leaves_children_of_tabs(is_tabs, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,16 +232,20 @@ fn tree_ui(
|
|||||||
.show(ui, |ui| match &mut node {
|
.show(ui, |ui| match &mut node {
|
||||||
dock::Node::Leaf(_) => {}
|
dock::Node::Leaf(_) => {}
|
||||||
dock::Node::Branch(branch) => {
|
dock::Node::Branch(branch) => {
|
||||||
|
let mut layout = branch.get_layout();
|
||||||
egui::ComboBox::from_label("Layout")
|
egui::ComboBox::from_label("Layout")
|
||||||
.selected_text(format!("{:?}", branch.layout))
|
.selected_text(format!("{:?}", layout))
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for typ in dock::Layout::ALL {
|
for typ in dock::Layout::ALL {
|
||||||
ui.selectable_value(&mut branch.layout, typ, format!("{:?}", typ))
|
ui.selectable_value(&mut layout, typ, format!("{:?}", typ))
|
||||||
.clicked();
|
.clicked();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if layout != branch.get_layout() {
|
||||||
|
branch.set_layout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
for &child in &branch.children {
|
for &child in branch.children() {
|
||||||
tree_ui(ui, behavior, nodes, child);
|
tree_ui(ui, behavior, nodes, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user