1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-27 15:13:12 -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};
@@ -6,6 +9,41 @@ use crate::dock::{
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?
#[derive(
Clone,
@@ -42,6 +80,14 @@ pub struct Grid {
pub col_shares: Vec<f32>,
/// Share of the available height assigned to each row.
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 {
@@ -57,9 +103,7 @@ impl Grid {
nodes: &mut Nodes<Leaf>,
style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
node_id: NodeId,
) {
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 row_heights = sizes_from_shares(&self.row_shares, rect.height(), gap);
let mut col_x = vec![rect.left()];
for &width in &col_widths {
col_x.push(col_x.last().unwrap() + width + gap);
{
let mut x = rect.left();
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()];
for &height in &row_heights {
row_y.push(row_y.last().unwrap() + height + gap);
{
let mut y = rect.top();
self.row_ranges.clear();
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:
@@ -130,20 +181,30 @@ impl Grid {
// Place each child:
for &child in &self.children {
let loc = self.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]),
);
nodes.layout_node(style, behavior, drop_context, child_rect, child);
let child_rect =
Rect::from_x_y_ranges(self.col_ranges[loc.col], self.row_ranges[loc.row]);
nodes.layout_node(style, behavior, 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:
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]),
);
for (col, &x_range) in self.col_ranges.iter().enumerate() {
for (row, &y_range) in self.row_ranges.iter().enumerate() {
let cell_rect = Rect::from_x_y_ranges(x_range, y_range);
drop_context.suggest_rect(
InsertionPoint::new(
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>,
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);
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>,
style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
) {
let num_gaps = self.children.len().saturating_sub(1);
@@ -66,7 +64,7 @@ impl Linear {
let mut x = rect.min.x;
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()));
nodes.layout_node(style, behavior, drop_context, child_rect, *child);
nodes.layout_node(style, behavior, child_rect, *child);
x += width + gap_width;
}
}
@@ -76,7 +74,6 @@ impl Linear {
nodes: &mut Nodes<Leaf>,
style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
) {
let num_gaps = self.children.len().saturating_sub(1);
@@ -89,7 +86,7 @@ impl Linear {
let mut y = rect.min.y;
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));
nodes.layout_node(style, behavior, drop_context, child_rect, *child);
nodes.layout_node(style, behavior, child_rect, *child);
y += height + gap_height;
}
}

View File

@@ -188,20 +188,18 @@ impl Branch {
nodes: &mut Nodes<Leaf>,
style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
node_id: NodeId,
) {
if self.is_empty() {
return;
}
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) => {
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);
}
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>,
style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
) {
let mut active_rect = rect;
active_rect.min.y += behavior.tab_bar_height(style);
// 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>(

View File

@@ -455,7 +455,6 @@ impl<Leaf> Dock<Leaf> {
self.nodes.layout_node(
ui.style(),
behavior,
&mut drop_context,
ui.available_rect_before_wrap(),
self.root,
);
@@ -675,7 +674,6 @@ impl<Leaf> Nodes<Leaf> {
&mut self,
style: &egui::Style,
behavior: &mut dyn Behavior<Leaf>,
drop_context: &mut DropContext,
rect: Rect,
node_id: NodeId,
) {
@@ -683,7 +681,7 @@ impl<Leaf> Nodes<Leaf> {
self.rects.insert(node_id, rect);
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);

View File

@@ -82,7 +82,12 @@ impl Rect {
}
#[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 {
min: pos2(*x_range.start(), *y_range.start()),
max: pos2(*x_range.end(), *y_range.end()),