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

Better re-ordering of grid cells

This commit is contained in:
Emil Ernerfeldt
2023-04-25 21:51:49 +02:00
parent 7b26f3b79f
commit 6563522939
2 changed files with 126 additions and 90 deletions

View File

@@ -191,22 +191,27 @@ impl Layout {
pub const ALL: [Self; 4] = [Self::Tabs, Self::Horizontal, Self::Vertical, Self::Grid];
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LayoutInsertion {
Tabs(usize),
Horizontal(usize),
Vertical(usize),
Grid(GridLoc),
}
#[derive(Clone, Copy, Debug)]
struct InsertionPoint {
parent_id: NodeId,
layout: Layout,
/// Where in the parent?
index: usize,
insertion: LayoutInsertion,
}
impl InsertionPoint {
fn new(parent_id: NodeId, layout: Layout, index: usize) -> Self {
fn new(parent_id: NodeId, insertion: LayoutInsertion) -> Self {
Self {
parent_id,
layout,
index,
insertion,
}
}
}
@@ -394,15 +399,14 @@ impl<Leaf> Nodes<Leaf> {
fn insert(&mut self, insertion_point: InsertionPoint, child_id: NodeId) {
let InsertionPoint {
parent_id,
layout: branch_type,
index,
insertion,
} = insertion_point;
let Some(mut node) = self.nodes.remove(&parent_id) else {
log::warn!("Failed to insert: could not find parent {parent_id:?}");
return;
};
match branch_type {
Layout::Tabs => {
match insertion {
LayoutInsertion::Tabs(index) => {
if let Node::Branch(Branch {
layout: Layout::Tabs,
children,
@@ -419,7 +423,7 @@ impl<Leaf> Nodes<Leaf> {
self.nodes.insert(parent_id, Node::Branch(branch));
}
}
Layout::Horizontal => {
LayoutInsertion::Horizontal(index) => {
if let Node::Branch(Branch {
layout: Layout::Horizontal,
children,
@@ -436,7 +440,7 @@ impl<Leaf> Nodes<Leaf> {
self.nodes.insert(parent_id, Node::Branch(branch));
}
}
Layout::Vertical => {
LayoutInsertion::Vertical(index) => {
if let Node::Branch(Branch {
layout: Layout::Vertical,
children,
@@ -453,27 +457,22 @@ impl<Leaf> Nodes<Leaf> {
self.nodes.insert(parent_id, Node::Branch(branch));
}
}
Layout::Grid => {
LayoutInsertion::Grid(insert_location) => {
if let Node::Branch(Branch {
layout: Layout::Grid,
grid_locations,
children,
grid_locations: grid_positions,
..
}) = &mut node
{
// The order of the children only matters with auto-layout
// Swap the positions of the children
let index = index.min(children.len());
let replaced_node_id = children.get(index).copied().unwrap_or_default();
children.insert(index, child_id);
if let Some(pos) = grid_positions.remove(&replaced_node_id) {
grid_positions.insert(child_id, pos);
}
grid_locations.retain(|_, pos| *pos != insert_location);
grid_locations.insert(child_id, insert_location);
children.push(child_id);
self.nodes.insert(parent_id, node);
} else {
let new_node_id = self.insert_node(node);
let mut branch = Branch::new(Layout::Grid, vec![new_node_id]);
branch.children.insert(index.min(1), child_id);
let mut branch = Branch::new(Layout::Grid, vec![new_node_id, child_id]);
branch.grid_locations.insert(child_id, insert_location);
self.nodes.insert(parent_id, Node::Branch(branch));
}
}
@@ -487,22 +486,7 @@ impl<Leaf> Dock<Leaf> {
self.root
}
fn smooth_preview_rect(&mut self, ctx: &egui::Context, new_rect: Rect) -> Rect {
let dt = ctx.input(|input| input.stable_dt).at_most(0.1);
let t = egui::emath::exponential_smooth_factor(0.9, 0.05, dt);
let smoothed = self.smoothed_preview_rect.get_or_insert(new_rect);
*smoothed = smoothed.lerp_towards(&new_rect, t);
let diff = smoothed.min.distance(new_rect.min) + smoothed.max.distance(new_rect.max);
if diff < 0.5 {
*smoothed = new_rect;
} else {
ctx.request_repaint();
}
*smoothed
}
/// Show all the leaves in the dock.
pub fn ui(&mut self, behavior: &mut dyn Behavior<Leaf>, ui: &mut Ui) {
let options = behavior.simplification_options();
self.simplify(&options);
@@ -513,12 +497,6 @@ impl<Leaf> Dock<Leaf> {
self.nodes.gc_root(behavior, self.root);
self.nodes.rects.clear();
self.nodes.layout_node(
ui.style(),
behavior,
ui.available_rect_before_wrap(),
self.root,
);
// Check if anything is being dragged:
let mut drop_context = DropContext {
@@ -530,6 +508,14 @@ impl<Leaf> Dock<Leaf> {
preview_rect: None,
};
self.nodes.layout_node(
ui.style(),
behavior,
&mut drop_context,
ui.available_rect_before_wrap(),
self.root,
);
self.nodes
.node_ui(behavior, &mut drop_context, ui, self.root);
@@ -600,6 +586,22 @@ impl<Leaf> Dock<Leaf> {
}
}
fn smooth_preview_rect(&mut self, ctx: &egui::Context, new_rect: Rect) -> Rect {
let dt = ctx.input(|input| input.stable_dt).at_most(0.1);
let t = egui::emath::exponential_smooth_factor(0.9, 0.05, dt);
let smoothed = self.smoothed_preview_rect.get_or_insert(new_rect);
*smoothed = smoothed.lerp_towards(&new_rect, t);
let diff = smoothed.min.distance(new_rect.min) + smoothed.max.distance(new_rect.max);
if diff < 0.5 {
*smoothed = new_rect;
} else {
ctx.request_repaint();
}
*smoothed
}
fn simplify(&mut self, options: &SimplificationOptions) {
match self.nodes.simplify(options, self.root) {
SimplifyAction::Remove => {
@@ -614,9 +616,8 @@ impl<Leaf> Dock<Leaf> {
fn move_node(&mut self, moved_node_id: NodeId, insertion_point: InsertionPoint) {
log::debug!(
"Moving {moved_node_id:?} into {:?} {:?}",
insertion_point.layout,
insertion_point.parent_id
"Moving {moved_node_id:?} into {:?}",
insertion_point.insertion
);
self.remove_node_id_from_parent(moved_node_id);
self.nodes.insert(insertion_point, moved_node_id);
@@ -731,17 +732,15 @@ impl<Leaf> Nodes<Leaf> {
&mut self,
style: &Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
node_id: NodeId,
) {
let Some(mut node) = self.nodes.remove(&node_id) else { return; };
self.rects.insert(node_id, rect);
match &mut node {
Node::Leaf(_) => {}
Node::Branch(branch) => {
self.layout_branch(style, behavior, rect, branch);
}
if let Node::Branch(branch) = &mut node {
self.layout_branch(style, behavior, drop_context, rect, node_id, branch);
}
self.nodes.insert(node_id, node);
@@ -751,14 +750,18 @@ impl<Leaf> Nodes<Leaf> {
&mut self,
style: &Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
node_id: NodeId,
branch: &mut 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),
Layout::Grid => self.layout_grid(style, behavior, rect, branch),
Layout::Tabs => self.layout_tabs(style, behavior, drop_context, rect, branch),
Layout::Horizontal => {
self.layout_horizontal(style, behavior, drop_context, rect, branch);
}
Layout::Vertical => self.layout_vertical(style, behavior, drop_context, rect, branch),
Layout::Grid => self.layout_grid(style, behavior, drop_context, rect, node_id, branch),
}
}
@@ -766,6 +769,7 @@ impl<Leaf> Nodes<Leaf> {
&mut self,
style: &Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
branch: &Branch,
) {
@@ -773,11 +777,17 @@ impl<Leaf> Nodes<Leaf> {
active_rect.min.y += behavior.tab_bar_height(style);
if false {
self.layout_node(style, behavior, active_rect, branch.active_tab);
self.layout_node(
style,
behavior,
drop_context,
active_rect,
branch.active_tab,
);
} else {
// Layout all nodes in case the user switches active tab
for &child_id in &branch.children {
self.layout_node(style, behavior, active_rect, child_id);
self.layout_node(style, behavior, drop_context, active_rect, child_id);
}
}
}
@@ -786,6 +796,7 @@ impl<Leaf> Nodes<Leaf> {
&mut self,
style: &Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
branch: &Branch,
) {
@@ -804,7 +815,7 @@ impl<Leaf> Nodes<Leaf> {
let mut x = rect.min.x;
for (child, width) in branch.children.iter().zip(widths) {
let child_rect = Rect::from_min_size(pos2(x, rect.min.y), vec2(width, rect.height()));
self.layout_node(style, behavior, child_rect, *child);
self.layout_node(style, behavior, drop_context, child_rect, *child);
x += width + gap_width;
}
}
@@ -813,6 +824,7 @@ impl<Leaf> Nodes<Leaf> {
&mut self,
style: &Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
branch: &Branch,
) {
@@ -831,7 +843,7 @@ impl<Leaf> Nodes<Leaf> {
let mut y = rect.min.y;
for (child, height) in branch.children.iter().zip(heights) {
let child_rect = Rect::from_min_size(pos2(rect.min.x, y), vec2(rect.width(), height));
self.layout_node(style, behavior, child_rect, *child);
self.layout_node(style, behavior, drop_context, child_rect, *child);
y += height + gap_height;
}
}
@@ -840,7 +852,9 @@ impl<Leaf> Nodes<Leaf> {
&mut self,
style: &Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
node_id: NodeId,
branch: &mut Branch,
) {
if branch.children.is_empty() {
@@ -911,14 +925,36 @@ impl<Leaf> Nodes<Leaf> {
row_y.push(row_y.last().unwrap() + height + gap);
}
// Place each node:
for child in &branch.children {
let loc = branch.grid_locations[child];
// Each child now has a location. Use this to order them, in case we will ater do auto-layouts:
branch
.children
.sort_by_key(|&child| branch.grid_locations[&child]);
// Place each child:
for &child in &branch.children {
let loc = branch.grid_locations[&child];
let child_rect = Rect::from_min_size(
pos2(col_x[loc.col], row_y[loc.row]),
vec2(col_widths[loc.col], row_heights[loc.row]),
);
self.layout_node(style, behavior, child_rect, *child);
self.layout_node(style, behavior, drop_context, child_rect, child);
}
// Register drop-zones:
for col in 0..num_cols {
for row in 0..num_rows {
let cell_rect = Rect::from_min_size(
pos2(col_x[col], row_y[row]),
vec2(col_widths[col], row_heights[row]),
);
drop_context.suggest_rect(
InsertionPoint::new(
node_id,
LayoutInsertion::Grid(GridLoc::from_col_row(col, row)),
),
cell_rect,
);
}
}
}
}
@@ -960,22 +996,22 @@ impl DropContext {
if node.layout() != Some(Layout::Horizontal) {
self.suggest_rect(
InsertionPoint::new(parent_id, Layout::Horizontal, 0),
InsertionPoint::new(parent_id, LayoutInsertion::Horizontal(0)),
rect.split_left_right_at_fraction(0.5).0,
);
self.suggest_rect(
InsertionPoint::new(parent_id, Layout::Horizontal, usize::MAX),
InsertionPoint::new(parent_id, LayoutInsertion::Horizontal(usize::MAX)),
rect.split_left_right_at_fraction(0.5).1,
);
}
if node.layout() != Some(Layout::Vertical) {
self.suggest_rect(
InsertionPoint::new(parent_id, Layout::Vertical, 0),
InsertionPoint::new(parent_id, LayoutInsertion::Vertical(0)),
rect.split_top_bottom_at_fraction(0.5).0,
);
self.suggest_rect(
InsertionPoint::new(parent_id, Layout::Vertical, usize::MAX),
InsertionPoint::new(parent_id, LayoutInsertion::Vertical(usize::MAX)),
rect.split_top_bottom_at_fraction(0.5).1,
);
}
@@ -1020,7 +1056,7 @@ impl<Leaf> Nodes<Leaf> {
drop_context.on_node(node_id, rect, &node);
drop_context.suggest_rect(
InsertionPoint::new(node_id, Layout::Tabs, usize::MAX),
InsertionPoint::new(node_id, LayoutInsertion::Tabs(usize::MAX)),
rect.split_top_bottom_at_y(rect.top() + behavior.tab_bar_height(ui.style()))
.1,
);
@@ -1061,7 +1097,7 @@ impl<Leaf> Nodes<Leaf> {
self.vertical_ui(behavior, drop_context, ui, node_id, branch);
}
Layout::Grid => {
self.grid_ui(behavior, drop_context, ui, node_id, branch);
self.grid_ui(behavior, drop_context, ui, branch);
}
}
}
@@ -1127,7 +1163,7 @@ impl<Leaf> Nodes<Leaf> {
};
drop_context.suggest_rect(
InsertionPoint::new(node_id, Layout::Tabs, insertion_index),
InsertionPoint::new(node_id, LayoutInsertion::Tabs(insertion_index)),
Rect::from_center_size(before_point, vec2(4.0, rect.height())),
);
}
@@ -1135,7 +1171,7 @@ impl<Leaf> Nodes<Leaf> {
if i + 1 == branch.children.len() {
// suggest dropping after last tab:
drop_context.suggest_rect(
InsertionPoint::new(node_id, Layout::Tabs, insertion_index + 1),
InsertionPoint::new(node_id, LayoutInsertion::Tabs(insertion_index + 1)),
Rect::from_center_size(rect.right_center(), vec2(4.0, rect.height())),
);
}
@@ -1169,21 +1205,26 @@ 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, Layout::Horizontal, i), rect);
drop_context.suggest_rect(
InsertionPoint::new(parent_id, LayoutInsertion::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, Layout::Horizontal, insertion_index),
InsertionPoint::new(
parent_id,
LayoutInsertion::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, Layout::Horizontal, 0),
InsertionPoint::new(parent_id, LayoutInsertion::Horizontal(0)),
rect.split_left_right_at_fraction(0.66).0,
);
}
@@ -1191,7 +1232,10 @@ impl<Leaf> Nodes<Leaf> {
if i + 1 == branch.children.len() {
// Suggest dropping after the last child:
drop_context.suggest_rect(
InsertionPoint::new(parent_id, Layout::Horizontal, insertion_index + 1),
InsertionPoint::new(
parent_id,
LayoutInsertion::Horizontal(insertion_index + 1),
),
rect.split_left_right_at_fraction(0.33).1,
);
}
@@ -1221,16 +1265,11 @@ impl<Leaf> Nodes<Leaf> {
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
ui: &mut Ui,
parent_id: NodeId,
branch: &mut Branch,
) {
// TODO: drag-and-drop
for (i, &child) in branch.children.iter().enumerate() {
drop_context.suggest_rect(
InsertionPoint::new(parent_id, Layout::Grid, i),
self.rect(child).unwrap(),
);
// Grid drops are handled during layout
for &child in &branch.children {
self.node_ui(behavior, drop_context, ui, child);
}
}

View File

@@ -43,10 +43,7 @@ impl View {
pub fn ui(&mut self, ui: &mut egui::Ui) -> dock::UiResponse {
ui.painter().rect_filled(ui.max_rect(), 0.0, self.color);
let dragged = ui
.add(
egui::Button::new(format!("Contents of {}. Drag me!", self.title))
.sense(egui::Sense::drag()),
)
.allocate_rect(ui.max_rect(), egui::Sense::drag())
.on_hover_cursor(egui::CursorIcon::Grab)
.dragged();
if dragged {