1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 05:10:03 -04:00

Resizing of horizontal layouts

This commit is contained in:
Emil Ernerfeldt
2023-05-02 19:30:59 +02:00
parent f8ce589fd5
commit 4f4261f173
4 changed files with 139 additions and 0 deletions

1
Cargo.lock generated
View File

@@ -1331,6 +1331,7 @@ dependencies = [
"egui",
"getrandom",
"image",
"itertools",
"log",
"rand",
"resvg",

View File

@@ -39,6 +39,7 @@ egui = { version = "0.21.0", path = "../egui", default-features = false }
# For dock:
# required to make rand work on wasm, see https://github.com/rust-random/rand#wasm-support
getrandom = { version = "0.2", features = ["js"] }
itertools = "0.10"
log = { version = "0.4", features = ["std"] }
rand = { version = "0.8.5", features = ["getrandom", "small_rng"] }

View File

@@ -1,7 +1,9 @@
use egui::{pos2, vec2, NumExt, Rect};
use itertools::Itertools as _;
use crate::dock::{
is_being_dragged, Behavior, DropContext, InsertionPoint, LayoutInsertion, NodeId, Nodes,
ResizeState,
};
use super::Shares;
@@ -161,6 +163,74 @@ impl Linear {
prev_rect = Some(rect);
}
// ------------------------
// resizing:
let parent_rect = nodes.rect(parent_id).unwrap();
for (i, (left, right)) in self.children.iter().tuple_windows().enumerate() {
let resize_id = egui::Id::new((parent_id, "resize", i));
let left_rect = nodes.rect(*left).unwrap();
let right_rect = nodes.rect(*right).unwrap();
let x = egui::lerp(left_rect.right()..=right_rect.left(), 0.5);
let mut resize_state = ResizeState::Idle;
if let Some(pointer) = ui.ctx().pointer_latest_pos() {
let we_are_on_top = ui
.ctx()
.layer_id_at(pointer)
.map_or(true, |top_layer_id| top_layer_id == ui.layer_id());
let mouse_over_resize_line = we_are_on_top
&& parent_rect.y_range().contains(&pointer.y)
&& (x - pointer.x).abs() <= ui.style().interaction.resize_grab_radius_side;
if ui.input(|i| i.pointer.any_pressed() && i.pointer.any_down())
&& mouse_over_resize_line
{
ui.memory_mut(|mem| mem.set_dragged_id(resize_id));
}
if ui.memory(|mem| mem.is_being_dragged(resize_id)) {
resize_state = ResizeState::Dragging;
} else {
let dragging_something_else =
ui.input(|i| i.pointer.any_down() || i.pointer.any_pressed());
if mouse_over_resize_line && !dragging_something_else {
resize_state = ResizeState::Hovering;
}
}
if resize_state == ResizeState::Dragging {
let dx = pointer.x - x;
if pointer.x < x {
// Expand right, shrink stuff to the left:
*self.shares.shares.entry(*right).or_insert(1.0) += shrink_shares(
behavior,
nodes,
&mut self.shares,
&self.children[0..=i].iter().copied().rev().collect_vec(),
dx.abs(),
);
} else if x < pointer.x {
// Expand the left, shrink stuff to the right:
*self.shares.shares.entry(*left).or_insert(1.0) += shrink_shares(
behavior,
nodes,
&mut self.shares,
&self.children[i + 1..],
dx.abs(),
);
}
}
if resize_state != ResizeState::Idle {
ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeHorizontal);
}
}
let stroke = behavior.resize_stroke(ui.style(), resize_state);
ui.painter().vline(x, parent_rect.y_range(), stroke);
}
}
fn vertical_ui<Leaf>(
@@ -175,5 +245,45 @@ impl Linear {
for child in &self.children {
nodes.node_ui(behavior, drop_context, ui, *child);
}
// TODO: resizing
}
}
/// Try shrink the children by a total of `target_in_points`,
/// making sure no child gets smaller than its minimum size.
fn shrink_shares<Leaf>(
behavior: &dyn Behavior<Leaf>,
nodes: &Nodes<Leaf>,
shares: &mut Shares,
children: &[NodeId],
target_in_points: f32,
) -> f32 {
assert!(!children.is_empty());
let mut total_shares = 0.0;
let mut total_points = 0.0;
for &child in children {
total_shares += shares.shares.get(&child).copied().unwrap_or(1.0);
total_points += nodes.rect(child).unwrap().width();
}
let shares_per_point = total_shares / total_points;
let min_size_in_points = shares_per_point * behavior.min_size();
let target_in_shares = shares_per_point * target_in_points;
let mut total_shares_lost = 0.0;
for &child in children {
let share = shares.shares.entry(child).or_insert(1.0);
let shrink_by = (target_in_shares - total_shares_lost)
.min(*share - min_size_in_points)
.max(0.0);
*share -= shrink_by;
total_shares_lost += shrink_by;
}
total_shares_lost
}

View File

@@ -1,3 +1,10 @@
// # TODO
// * A new ui for each node, nested
// * Better drag-and-drop around the "empty" drag source
// * Resizing of vertical layouts and grids
// * Styling
// * Handle rects without a lot of unwraps
use std::collections::{HashMap, HashSet};
use egui::{Id, Key, NumExt, Pos2, Rect, Response, Sense, TextStyle, Ui, WidgetText};
@@ -207,9 +214,29 @@ pub trait Behavior<Leaf> {
1.0
}
// No child should shrink below this size
fn min_size(&self) -> f32 {
32.0
}
fn simplification_options(&self) -> SimplificationOptions {
SimplificationOptions::default()
}
fn resize_stroke(&self, style: &egui::Style, resize_state: ResizeState) -> egui::Stroke {
match resize_state {
ResizeState::Idle => egui::Stroke::NONE, // Let the gap speak for itself
ResizeState::Hovering => style.visuals.widgets.hovered.fg_stroke,
ResizeState::Dragging => style.visuals.widgets.active.fg_stroke,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ResizeState {
Idle,
Hovering,
Dragging,
}
// ----------------------------------------------------------------------------