mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 22:30:03 -04:00
Resize vertical layouts
This commit is contained in:
@@ -120,7 +120,7 @@ impl Linear {
|
|||||||
let mut insertion_index = 0; // skips over drag-source, if any, beacuse it will be removed then re-inserted
|
let mut insertion_index = 0; // skips over drag-source, if any, beacuse it will be removed then re-inserted
|
||||||
|
|
||||||
for (i, &child) in self.children.iter().enumerate() {
|
for (i, &child) in self.children.iter().enumerate() {
|
||||||
let Some(rect) = nodes.rect(child) else { continue; };
|
let rect = nodes.rect(child);
|
||||||
|
|
||||||
if is_being_dragged(ui.ctx(), child) {
|
if is_being_dragged(ui.ctx(), child) {
|
||||||
// Leave a hole, and suggest that hole as drop-target:
|
// Leave a hole, and suggest that hole as drop-target:
|
||||||
@@ -167,12 +167,12 @@ impl Linear {
|
|||||||
// ------------------------
|
// ------------------------
|
||||||
// resizing:
|
// resizing:
|
||||||
|
|
||||||
let parent_rect = nodes.rect(parent_id).unwrap();
|
let parent_rect = nodes.rect(parent_id);
|
||||||
for (i, (left, right)) in self.children.iter().tuple_windows().enumerate() {
|
for (i, (left, right)) in self.children.iter().copied().tuple_windows().enumerate() {
|
||||||
let resize_id = egui::Id::new((parent_id, "resize", i));
|
let resize_id = egui::Id::new((parent_id, "resize", i));
|
||||||
|
|
||||||
let left_rect = nodes.rect(*left).unwrap();
|
let left_rect = nodes.rect(left);
|
||||||
let right_rect = nodes.rect(*right).unwrap();
|
let right_rect = nodes.rect(right);
|
||||||
let x = egui::lerp(left_rect.right()..=right_rect.left(), 0.5);
|
let x = egui::lerp(left_rect.right()..=right_rect.left(), 0.5);
|
||||||
|
|
||||||
let mut resize_state = ResizeState::Idle;
|
let mut resize_state = ResizeState::Idle;
|
||||||
@@ -185,41 +185,16 @@ impl Linear {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
let response = ui.interact(line_rect, resize_id, egui::Sense::click_and_drag());
|
let response = ui.interact(line_rect, resize_id, egui::Sense::click_and_drag());
|
||||||
if response.double_clicked() {
|
resize_state = resize_interaction(
|
||||||
// double-click to center the split between left and right:
|
behavior,
|
||||||
let mean = 0.5 * (self.shares[*left] + self.shares[*right]);
|
&mut self.shares,
|
||||||
self.shares.insert(*left, mean);
|
&self.children,
|
||||||
self.shares.insert(*right, mean);
|
&response,
|
||||||
} else if response.dragged() {
|
[left, right],
|
||||||
resize_state = ResizeState::Dragging;
|
ui.painter().round_to_pixel(pointer.x) - x,
|
||||||
} else if response.hovered() {
|
i,
|
||||||
resize_state = ResizeState::Hovering;
|
|node_id: NodeId| nodes.rect(node_id).width(),
|
||||||
}
|
);
|
||||||
|
|
||||||
if resize_state == ResizeState::Dragging {
|
|
||||||
let node_width = |node_id: NodeId| nodes.rect(node_id).unwrap().width();
|
|
||||||
|
|
||||||
let dx = pointer.x - x;
|
|
||||||
if pointer.x < x {
|
|
||||||
// Expand right, shrink stuff to the left:
|
|
||||||
self.shares[*right] += shrink_shares(
|
|
||||||
behavior,
|
|
||||||
&mut self.shares,
|
|
||||||
&self.children[0..=i].iter().copied().rev().collect_vec(),
|
|
||||||
dx.abs(),
|
|
||||||
node_width,
|
|
||||||
);
|
|
||||||
} else if x < pointer.x {
|
|
||||||
// Expand the left, shrink stuff to the right:
|
|
||||||
self.shares[*left] += shrink_shares(
|
|
||||||
behavior,
|
|
||||||
&mut self.shares,
|
|
||||||
&self.children[i + 1..],
|
|
||||||
dx.abs(),
|
|
||||||
node_width,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if resize_state != ResizeState::Idle {
|
if resize_state != ResizeState::Idle {
|
||||||
ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeHorizontal);
|
ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeHorizontal);
|
||||||
@@ -244,7 +219,86 @@ impl Linear {
|
|||||||
nodes.node_ui(behavior, drop_context, ui, *child);
|
nodes.node_ui(behavior, drop_context, ui, *child);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: resizing
|
// ------------------------
|
||||||
|
// resizing:
|
||||||
|
|
||||||
|
let parent_rect = nodes.rect(parent_id);
|
||||||
|
for (i, (top, bottom)) in self.children.iter().copied().tuple_windows().enumerate() {
|
||||||
|
let resize_id = egui::Id::new((parent_id, "resize", i));
|
||||||
|
|
||||||
|
let top_rect = nodes.rect(top);
|
||||||
|
let bottom_rect = nodes.rect(bottom);
|
||||||
|
let y = egui::lerp(top_rect.bottom()..=bottom_rect.top(), 0.5);
|
||||||
|
|
||||||
|
let mut resize_state = ResizeState::Idle;
|
||||||
|
if let Some(pointer) = ui.ctx().pointer_latest_pos() {
|
||||||
|
let line_rect = Rect::from_center_size(
|
||||||
|
pos2(parent_rect.center().x, y),
|
||||||
|
vec2(
|
||||||
|
parent_rect.width(),
|
||||||
|
2.0 * ui.style().interaction.resize_grab_radius_side,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let response = ui.interact(line_rect, resize_id, egui::Sense::click_and_drag());
|
||||||
|
resize_state = resize_interaction(
|
||||||
|
behavior,
|
||||||
|
&mut self.shares,
|
||||||
|
&self.children,
|
||||||
|
&response,
|
||||||
|
[top, bottom],
|
||||||
|
ui.painter().round_to_pixel(pointer.y) - y,
|
||||||
|
i,
|
||||||
|
|node_id: NodeId| nodes.rect(node_id).height(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if resize_state != ResizeState::Idle {
|
||||||
|
ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeVertical);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stroke = behavior.resize_stroke(ui.style(), resize_state);
|
||||||
|
ui.painter().hline(parent_rect.x_range(), y, stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn resize_interaction<Leaf>(
|
||||||
|
behavior: &mut dyn Behavior<Leaf>,
|
||||||
|
shares: &mut Shares,
|
||||||
|
children: &[NodeId],
|
||||||
|
splitter_response: &egui::Response,
|
||||||
|
[left, right]: [NodeId; 2],
|
||||||
|
dx: f32,
|
||||||
|
i: usize,
|
||||||
|
node_width: impl Fn(NodeId) -> f32,
|
||||||
|
) -> ResizeState {
|
||||||
|
if splitter_response.double_clicked() {
|
||||||
|
// double-click to center the split between left and right:
|
||||||
|
let mean = 0.5 * (shares[left] + shares[right]);
|
||||||
|
shares[left] = mean;
|
||||||
|
shares[right] = mean;
|
||||||
|
ResizeState::Hovering
|
||||||
|
} else if splitter_response.dragged() {
|
||||||
|
if dx < 0.0 {
|
||||||
|
// Expand right, shrink stuff to the left:
|
||||||
|
shares[right] += shrink_shares(
|
||||||
|
behavior,
|
||||||
|
shares,
|
||||||
|
&children[0..=i].iter().copied().rev().collect_vec(),
|
||||||
|
dx.abs(),
|
||||||
|
node_width,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Expand the left, shrink stuff to the right:
|
||||||
|
shares[left] +=
|
||||||
|
shrink_shares(behavior, shares, &children[i + 1..], dx.abs(), node_width);
|
||||||
|
}
|
||||||
|
ResizeState::Dragging
|
||||||
|
} else if splitter_response.hovered() {
|
||||||
|
ResizeState::Hovering
|
||||||
|
} else {
|
||||||
|
ResizeState::Idle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ pub struct Shares {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Shares {
|
impl Shares {
|
||||||
pub fn insert(&mut self, id: NodeId, share: f32) {
|
|
||||||
self.shares.insert(id, share);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn replace_with(&mut self, a: NodeId, b: NodeId) {
|
pub fn replace_with(&mut self, a: NodeId, b: NodeId) {
|
||||||
if let Some(share) = self.shares.remove(&a) {
|
if let Some(share) = self.shares.remove(&a) {
|
||||||
self.shares.insert(b, share);
|
self.shares.insert(b, share);
|
||||||
|
|||||||
@@ -253,10 +253,16 @@ impl<Leaf> Dock<Leaf> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Leaf> Nodes<Leaf> {
|
impl<Leaf> Nodes<Leaf> {
|
||||||
pub fn rect(&self, node_id: NodeId) -> Option<Rect> {
|
pub fn try_rect(&self, node_id: NodeId) -> Option<Rect> {
|
||||||
self.rects.get(&node_id).copied()
|
self.rects.get(&node_id).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rect(&self, node_id: NodeId) -> Rect {
|
||||||
|
let rect = self.try_rect(node_id);
|
||||||
|
debug_assert!(rect.is_some(), "Failed to find rect for {node_id:?}");
|
||||||
|
rect.unwrap_or(egui::Rect::from_min_max(Pos2::ZERO, Pos2::ZERO))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, node_id: NodeId) -> Option<&Node<Leaf>> {
|
pub fn get(&self, node_id: NodeId) -> Option<&Node<Leaf>> {
|
||||||
self.nodes.get(&node_id)
|
self.nodes.get(&node_id)
|
||||||
}
|
}
|
||||||
@@ -483,7 +489,7 @@ impl<Leaf> Dock<Leaf> {
|
|||||||
let preview_color = preview_stroke.color;
|
let preview_color = preview_stroke.color;
|
||||||
|
|
||||||
if let Some(insertion_point) = &drop_context.best_insertion {
|
if let Some(insertion_point) = &drop_context.best_insertion {
|
||||||
if let Some(parent_rect) = self.nodes.rect(insertion_point.parent_id) {
|
if let Some(parent_rect) = self.nodes.try_rect(insertion_point.parent_id) {
|
||||||
// Show which parent we will be dropped into
|
// Show which parent we will be dropped into
|
||||||
ui.painter().rect_stroke(parent_rect, 1.0, preview_stroke);
|
ui.painter().rect_stroke(parent_rect, 1.0, preview_stroke);
|
||||||
}
|
}
|
||||||
@@ -770,7 +776,7 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
) {
|
) {
|
||||||
let (Some(rect), Some(mut node)) = (self.rect(node_id), self.nodes.remove(&node_id)) else { return };
|
let (Some(rect), Some(mut node)) = (self.try_rect(node_id), self.nodes.remove(&node_id)) else { return };
|
||||||
|
|
||||||
let drop_context_was_enabled = drop_context.enabled;
|
let drop_context_was_enabled = drop_context.enabled;
|
||||||
if Some(node_id) == drop_context.dragged_node_id {
|
if Some(node_id) == drop_context.dragged_node_id {
|
||||||
|
|||||||
@@ -171,6 +171,14 @@ impl Default for MyApp {
|
|||||||
let e = nodes.insert_leaf(gen_view());
|
let e = nodes.insert_leaf(gen_view());
|
||||||
nodes.insert_horizontal_node(vec![a, b, c, d, e])
|
nodes.insert_horizontal_node(vec![a, b, c, d, e])
|
||||||
});
|
});
|
||||||
|
tabs.push({
|
||||||
|
let a = nodes.insert_leaf(gen_view());
|
||||||
|
let b = nodes.insert_leaf(gen_view());
|
||||||
|
let c = nodes.insert_leaf(gen_view());
|
||||||
|
let d = nodes.insert_leaf(gen_view());
|
||||||
|
let e = nodes.insert_leaf(gen_view());
|
||||||
|
nodes.insert_vertical_node(vec![a, b, c, d, e])
|
||||||
|
});
|
||||||
tabs.push({
|
tabs.push({
|
||||||
let a = nodes.insert_leaf(gen_view());
|
let a = nodes.insert_leaf(gen_view());
|
||||||
let b = nodes.insert_leaf(gen_view());
|
let b = nodes.insert_leaf(gen_view());
|
||||||
|
|||||||
Reference in New Issue
Block a user