1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 14:20:04 -04:00

Refactor: register grid drop-zones during the ui pass

This commit is contained in:
Emil Ernerfeldt
2023-05-04 10:57:46 +02:00
parent f5301b1761
commit e9b446b577
6 changed files with 98 additions and 54 deletions

View File

@@ -1,4 +1,7 @@
use std::collections::{BTreeMap, HashMap, HashSet}; use std::{
collections::{BTreeMap, HashMap, HashSet},
ops::RangeInclusive,
};
use egui::{pos2, vec2, Rect}; use egui::{pos2, vec2, Rect};
@@ -6,6 +9,41 @@ use crate::dock::{
sizes_from_shares, Behavior, DropContext, InsertionPoint, LayoutInsertion, NodeId, Nodes, sizes_from_shares, Behavior, DropContext, InsertionPoint, LayoutInsertion, NodeId, Nodes,
}; };
/// Includive range of floats, i.e. `min..=max`, but more ergonomic than [`RangeInclusive`].
#[derive(Clone, Copy, Debug, PartialEq)]
struct Rangef {
pub min: f32,
pub max: f32,
}
impl Rangef {
#[inline]
pub fn new(min: f32, max: f32) -> Self {
Self { min, max }
}
}
impl From<RangeInclusive<f32>> for Rangef {
#[inline]
fn from(range: RangeInclusive<f32>) -> Self {
Self::new(*range.start(), *range.end())
}
}
impl From<&RangeInclusive<f32>> for Rangef {
#[inline]
fn from(range: &RangeInclusive<f32>) -> Self {
Self::new(*range.start(), *range.end())
}
}
impl From<Rangef> for RangeInclusive<f32> {
#[inline]
fn from(Rangef { min, max }: Rangef) -> Self {
min..=max
}
}
/// Where in a grid? /// Where in a grid?
#[derive( #[derive(
Clone, Clone,
@@ -42,6 +80,14 @@ pub struct Grid {
pub col_shares: Vec<f32>, pub col_shares: Vec<f32>,
/// Share of the available height assigned to each row. /// Share of the available height assigned to each row.
pub row_shares: Vec<f32>, pub row_shares: Vec<f32>,
/// ui point x ranges for each column, recomputed during layout
#[serde(skip)]
col_ranges: Vec<Rangef>,
/// ui point y ranges for each row, recomputed during layout
#[serde(skip)]
row_ranges: Vec<Rangef>,
} }
impl Grid { impl Grid {
@@ -57,9 +103,7 @@ impl Grid {
nodes: &mut Nodes<Leaf>, nodes: &mut Nodes<Leaf>,
style: &egui::Style, style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>, behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect, rect: Rect,
node_id: NodeId,
) { ) {
let child_ids: HashSet<NodeId> = self.children.iter().copied().collect(); let child_ids: HashSet<NodeId> = self.children.iter().copied().collect();
@@ -114,14 +158,21 @@ impl Grid {
let col_widths = sizes_from_shares(&self.col_shares, rect.width(), gap); let col_widths = sizes_from_shares(&self.col_shares, rect.width(), gap);
let row_heights = sizes_from_shares(&self.row_shares, rect.height(), gap); let row_heights = sizes_from_shares(&self.row_shares, rect.height(), gap);
let mut col_x = vec![rect.left()]; {
for &width in &col_widths { let mut x = rect.left();
col_x.push(col_x.last().unwrap() + width + gap); self.col_ranges.clear();
for &width in &col_widths {
self.col_ranges.push(Rangef::new(x, x + width));
x += width + gap;
}
} }
{
let mut row_y = vec![rect.top()]; let mut y = rect.top();
for &height in &row_heights { self.row_ranges.clear();
row_y.push(row_y.last().unwrap() + height + gap); for &height in &row_heights {
self.row_ranges.push(Rangef::new(y, y + height));
y += height + gap;
}
} }
// 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:
@@ -130,20 +181,30 @@ impl Grid {
// Place each child: // Place each child:
for &child in &self.children { for &child in &self.children {
let loc = self.locations[&child]; let loc = self.locations[&child];
let child_rect = Rect::from_min_size( let child_rect =
pos2(col_x[loc.col], row_y[loc.row]), Rect::from_x_y_ranges(self.col_ranges[loc.col], self.row_ranges[loc.row]);
vec2(col_widths[loc.col], row_heights[loc.row]), nodes.layout_node(style, behavior, child_rect, child);
); }
nodes.layout_node(style, behavior, drop_context, child_rect, child); }
pub 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,
) {
// Grid drops are handled during layout. TODO: handle here instead.
for &child in &self.children {
nodes.node_ui(behavior, drop_context, ui, child);
} }
// Register drop-zones: // Register drop-zones:
for col in 0..num_cols { for (col, &x_range) in self.col_ranges.iter().enumerate() {
for row in 0..num_rows { for (row, &y_range) in self.row_ranges.iter().enumerate() {
let cell_rect = Rect::from_min_size( let cell_rect = Rect::from_x_y_ranges(x_range, y_range);
pos2(col_x[col], row_y[row]),
vec2(col_widths[col], row_heights[row]),
);
drop_context.suggest_rect( drop_context.suggest_rect(
InsertionPoint::new( InsertionPoint::new(
node_id, node_id,
@@ -154,18 +215,4 @@ impl Grid {
} }
} }
} }
pub fn ui<Leaf>(
&mut self,
nodes: &mut Nodes<Leaf>,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
ui: &mut egui::Ui,
) {
// Grid drops are handled during layout. TODO: handle here instead.
for &child in &self.children {
nodes.node_ui(behavior, drop_context, ui, child);
}
}
} }

View File

@@ -37,14 +37,13 @@ impl Linear {
nodes: &mut Nodes<Leaf>, nodes: &mut Nodes<Leaf>,
style: &egui::Style, style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>, behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect, rect: Rect,
) { ) {
match self.dir { match self.dir {
LinearDir::Horizontal => { LinearDir::Horizontal => {
self.layout_horizontal(nodes, style, behavior, drop_context, rect); self.layout_horizontal(nodes, style, behavior, rect);
} }
LinearDir::Vertical => self.layout_vertical(nodes, style, behavior, drop_context, rect), LinearDir::Vertical => self.layout_vertical(nodes, style, behavior, rect),
} }
} }
@@ -53,7 +52,6 @@ impl Linear {
nodes: &mut Nodes<Leaf>, nodes: &mut Nodes<Leaf>,
style: &egui::Style, style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>, behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect, rect: Rect,
) { ) {
let num_gaps = self.children.len().saturating_sub(1); let num_gaps = self.children.len().saturating_sub(1);
@@ -66,7 +64,7 @@ impl Linear {
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) {
let child_rect = Rect::from_min_size(pos2(x, rect.min.y), vec2(width, rect.height())); let child_rect = Rect::from_min_size(pos2(x, rect.min.y), vec2(width, rect.height()));
nodes.layout_node(style, behavior, drop_context, child_rect, *child); nodes.layout_node(style, behavior, child_rect, *child);
x += width + gap_width; x += width + gap_width;
} }
} }
@@ -76,7 +74,6 @@ impl Linear {
nodes: &mut Nodes<Leaf>, nodes: &mut Nodes<Leaf>,
style: &egui::Style, style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>, behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect, rect: Rect,
) { ) {
let num_gaps = self.children.len().saturating_sub(1); let num_gaps = self.children.len().saturating_sub(1);
@@ -89,7 +86,7 @@ impl Linear {
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) {
let child_rect = Rect::from_min_size(pos2(rect.min.x, y), vec2(rect.width(), height)); let child_rect = Rect::from_min_size(pos2(rect.min.x, y), vec2(rect.width(), height));
nodes.layout_node(style, behavior, drop_context, child_rect, *child); nodes.layout_node(style, behavior, child_rect, *child);
y += height + gap_height; y += height + gap_height;
} }
} }

View File

@@ -188,20 +188,18 @@ impl Branch {
nodes: &mut Nodes<Leaf>, nodes: &mut Nodes<Leaf>,
style: &egui::Style, style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>, behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect, rect: Rect,
node_id: NodeId,
) { ) {
if self.is_empty() { if self.is_empty() {
return; return;
} }
match self { match self {
Branch::Tabs(tabs) => tabs.layout(nodes, style, behavior, drop_context, rect), Branch::Tabs(tabs) => tabs.layout(nodes, style, behavior, rect),
Branch::Linear(linear) => { Branch::Linear(linear) => {
linear.layout(nodes, style, behavior, drop_context, rect); linear.layout(nodes, style, behavior, rect);
} }
Branch::Grid(grid) => grid.layout(nodes, style, behavior, drop_context, rect, node_id), Branch::Grid(grid) => grid.layout(nodes, style, behavior, rect),
} }
} }
} }
@@ -224,7 +222,7 @@ impl Branch {
linear.ui(nodes, behavior, drop_context, ui, node_id); linear.ui(nodes, behavior, drop_context, ui, node_id);
} }
Branch::Grid(grid) => { Branch::Grid(grid) => {
grid.ui(nodes, behavior, drop_context, ui); grid.ui(nodes, behavior, drop_context, ui, node_id);
} }
} }
} }

View File

@@ -24,14 +24,13 @@ impl Tabs {
nodes: &mut Nodes<Leaf>, nodes: &mut Nodes<Leaf>,
style: &egui::Style, style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>, behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect, rect: Rect,
) { ) {
let mut active_rect = rect; let mut active_rect = rect;
active_rect.min.y += behavior.tab_bar_height(style); active_rect.min.y += behavior.tab_bar_height(style);
// Only lay out the active tab (saves CPU): // Only lay out the active tab (saves CPU):
nodes.layout_node(style, behavior, drop_context, active_rect, self.active); nodes.layout_node(style, behavior, active_rect, self.active);
} }
pub fn ui<Leaf>( pub fn ui<Leaf>(

View File

@@ -455,7 +455,6 @@ impl<Leaf> Dock<Leaf> {
self.nodes.layout_node( self.nodes.layout_node(
ui.style(), ui.style(),
behavior, behavior,
&mut drop_context,
ui.available_rect_before_wrap(), ui.available_rect_before_wrap(),
self.root, self.root,
); );
@@ -675,7 +674,6 @@ impl<Leaf> Nodes<Leaf> {
&mut self, &mut self,
style: &egui::Style, style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>, behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect, rect: Rect,
node_id: NodeId, node_id: NodeId,
) { ) {
@@ -683,7 +681,7 @@ impl<Leaf> Nodes<Leaf> {
self.rects.insert(node_id, rect); self.rects.insert(node_id, rect);
if let Node::Branch(branch) = &mut node { if let Node::Branch(branch) = &mut node {
branch.layout(self, style, behavior, drop_context, rect, node_id); branch.layout(self, style, behavior, rect);
} }
self.nodes.insert(node_id, node); self.nodes.insert(node_id, node);

View File

@@ -82,7 +82,12 @@ impl Rect {
} }
#[inline(always)] #[inline(always)]
pub fn from_x_y_ranges(x_range: RangeInclusive<f32>, y_range: RangeInclusive<f32>) -> Self { pub fn from_x_y_ranges(
x_range: impl Into<RangeInclusive<f32>>,
y_range: impl Into<RangeInclusive<f32>>,
) -> Self {
let x_range = x_range.into();
let y_range = y_range.into();
Rect { Rect {
min: pos2(*x_range.start(), *y_range.start()), min: pos2(*x_range.start(), *y_range.start()),
max: pos2(*x_range.end(), *y_range.end()), max: pos2(*x_range.end(), *y_range.end()),