mirror of
https://github.com/emilk/egui.git
synced 2026-09-03 07:10:04 -04:00
Drag to re-arrange horizontal layout
This commit is contained in:
@@ -263,6 +263,10 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
self.nodes.get(&node_id).map(|node| &node.layout)
|
self.nodes.get(&node_id).map(|node| &node.layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, node_id: NodeId) -> Option<&mut NodeLayout<Leaf>> {
|
||||||
|
self.nodes.get_mut(&node_id).map(|node| &mut node.layout)
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn insert_node(&mut self, node: NodeState<Leaf>) -> NodeId {
|
pub fn insert_node(&mut self, node: NodeState<Leaf>) -> NodeId {
|
||||||
let id = NodeId::random();
|
let id = NodeId::random();
|
||||||
@@ -456,6 +460,20 @@ impl<Leaf> Dock<Leaf> {
|
|||||||
Color32::LIGHT_BLUE.gamma_multiply(0.5),
|
Color32::LIGHT_BLUE.gamma_multiply(0.5),
|
||||||
(1.0, Color32::LIGHT_BLUE),
|
(1.0, Color32::LIGHT_BLUE),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let preview_child = false;
|
||||||
|
if preview_child {
|
||||||
|
// Preview actual child?
|
||||||
|
if preview_rect.width() > 32.0 && preview_rect.height() > 32.0 {
|
||||||
|
if let Some(NodeLayout::Leaf(leaf)) = self.nodes.get_mut(dragged_node_id) {
|
||||||
|
let _ = behavior.leaf_ui(
|
||||||
|
&mut ui.child_ui(preview_rect, *ui.layout()),
|
||||||
|
dragged_node_id,
|
||||||
|
leaf,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui.input(|i| i.pointer.any_released()) {
|
if ui.input(|i| i.pointer.any_released()) {
|
||||||
@@ -530,6 +548,10 @@ fn is_possible_drag(ctx: &egui::Context) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_being_dragged(ctx: &egui::Context, node_id: NodeId) -> bool {
|
||||||
|
ctx.memory(|mem| mem.is_being_dragged(node_id.id())) && is_possible_drag(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// gc
|
// gc
|
||||||
|
|
||||||
@@ -727,35 +749,39 @@ struct DropContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DropContext {
|
impl DropContext {
|
||||||
fn on_node(&mut self, parent_id: NodeId, rect: Rect) {
|
fn on_node<Leaf>(&mut self, parent_id: NodeId, node: &NodeState<Leaf>) {
|
||||||
if !self.active {
|
if !self.active {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.suggest_point(
|
let rect = node.rect;
|
||||||
InsertionPoint::new(parent_id, LayoutType::Horizontal, 0),
|
|
||||||
rect.left_center(),
|
if !matches!(node.layout, NodeLayout::Horizontal(_)) {
|
||||||
rect.split_left_right_at_fraction(0.5).0,
|
self.suggest_rect(
|
||||||
);
|
InsertionPoint::new(parent_id, LayoutType::Horizontal, 0),
|
||||||
self.suggest_point(
|
rect.split_left_right_at_fraction(0.5).0,
|
||||||
InsertionPoint::new(parent_id, LayoutType::Horizontal, usize::MAX),
|
);
|
||||||
rect.right_center(),
|
self.suggest_rect(
|
||||||
rect.split_left_right_at_fraction(0.5).1,
|
InsertionPoint::new(parent_id, LayoutType::Horizontal, usize::MAX),
|
||||||
);
|
rect.split_left_right_at_fraction(0.5).1,
|
||||||
self.suggest_point(
|
);
|
||||||
InsertionPoint::new(parent_id, LayoutType::Vertical, 0),
|
}
|
||||||
rect.center_top(),
|
|
||||||
rect.split_top_bottom_at_fraction(0.5).0,
|
if !matches!(node.layout, NodeLayout::Vertical(_)) {
|
||||||
);
|
self.suggest_rect(
|
||||||
self.suggest_point(
|
InsertionPoint::new(parent_id, LayoutType::Vertical, 0),
|
||||||
InsertionPoint::new(parent_id, LayoutType::Vertical, usize::MAX),
|
rect.split_top_bottom_at_fraction(0.5).0,
|
||||||
rect.center_bottom(),
|
);
|
||||||
rect.split_top_bottom_at_fraction(0.5).1,
|
self.suggest_rect(
|
||||||
);
|
InsertionPoint::new(parent_id, LayoutType::Vertical, usize::MAX),
|
||||||
self.suggest_point(
|
rect.split_top_bottom_at_fraction(0.5).1,
|
||||||
InsertionPoint::new(parent_id, LayoutType::Tabs, 1),
|
);
|
||||||
rect.center(),
|
}
|
||||||
rect,
|
|
||||||
);
|
// self.suggest_rect(InsertionPoint::new(parent_id, LayoutType::Tabs, 1), rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suggest_rect(&mut self, insertion: InsertionPoint, preview_rect: Rect) {
|
||||||
|
self.suggest_point(insertion, preview_rect.center(), preview_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest_point(&mut self, insertion: InsertionPoint, target_point: Pos2, preview_rect: Rect) {
|
fn suggest_point(&mut self, insertion: InsertionPoint, target_point: Pos2, preview_rect: Rect) {
|
||||||
@@ -788,7 +814,14 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
// Can't drag a node onto self or any children
|
// Can't drag a node onto self or any children
|
||||||
drop_context.active = false;
|
drop_context.active = false;
|
||||||
}
|
}
|
||||||
drop_context.on_node(node_id, node.rect);
|
drop_context.on_node(node_id, &node);
|
||||||
|
|
||||||
|
drop_context.suggest_rect(
|
||||||
|
InsertionPoint::new(node_id, LayoutType::Tabs, usize::MAX),
|
||||||
|
node.rect
|
||||||
|
.split_top_bottom_at_y(node.rect.top() + behavior.tab_bar_height(ui.style()))
|
||||||
|
.1,
|
||||||
|
);
|
||||||
|
|
||||||
match &mut node.layout {
|
match &mut node.layout {
|
||||||
NodeLayout::Leaf(leaf) => {
|
NodeLayout::Leaf(leaf) => {
|
||||||
@@ -833,18 +866,16 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
// Show tab bar:
|
// Show tab bar:
|
||||||
tab_bar_ui.horizontal(|ui| {
|
tab_bar_ui.horizontal(|ui| {
|
||||||
let mut prev_tab_rect: Option<Rect> = None;
|
let mut prev_tab_rect: Option<Rect> = None;
|
||||||
let mut insertion_index = 0; // skips over drag-source, if any
|
let mut insertion_index = 0; // skips over drag-source, if any, beacuse it will be removed then re-inserted
|
||||||
|
|
||||||
for (child_index, &child_id) in tabs.children.iter().enumerate() {
|
for (i, &child_id) in tabs.children.iter().enumerate() {
|
||||||
let selected = child_id == tabs.active;
|
if is_being_dragged(ui.ctx(), child_id) {
|
||||||
let id = child_id.id();
|
|
||||||
|
|
||||||
let is_node_being_dragged =
|
|
||||||
ui.memory(|mem| mem.is_being_dragged(id)) && is_possible_drag(ui.ctx());
|
|
||||||
if is_node_being_dragged {
|
|
||||||
continue; // leave a gap!
|
continue; // leave a gap!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let selected = child_id == tabs.active;
|
||||||
|
let id = child_id.id();
|
||||||
|
|
||||||
let response = behavior.tab_ui(self, ui, id, child_id, selected);
|
let response = behavior.tab_ui(self, ui, id, child_id, selected);
|
||||||
let response = response.on_hover_cursor(CursorIcon::Grab);
|
let response = response.on_hover_cursor(CursorIcon::Grab);
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
@@ -858,38 +889,33 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rect = response.rect;
|
||||||
|
|
||||||
{
|
{
|
||||||
// suggest dropping before this tab:
|
// suggest dropping before this tab:
|
||||||
let before_point = if let Some(prev_tab_rect) = prev_tab_rect {
|
let before_point = if let Some(prev_tab_rect) = prev_tab_rect {
|
||||||
// between
|
// between
|
||||||
prev_tab_rect
|
prev_tab_rect.right_center().lerp(rect.left_center(), 0.5)
|
||||||
.right_center()
|
|
||||||
.lerp(response.rect.left_center(), 0.5)
|
|
||||||
} else {
|
} else {
|
||||||
// before first
|
// before first
|
||||||
response.rect.left_center()
|
rect.left_center()
|
||||||
};
|
};
|
||||||
|
|
||||||
drop_context.suggest_point(
|
drop_context.suggest_rect(
|
||||||
InsertionPoint::new(node_id, LayoutType::Tabs, insertion_index),
|
InsertionPoint::new(node_id, LayoutType::Tabs, insertion_index),
|
||||||
before_point,
|
Rect::from_center_size(before_point, vec2(4.0, rect.height())),
|
||||||
Rect::from_center_size(before_point, vec2(4.0, response.rect.height())),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if child_index + 1 == tabs.children.len() {
|
if i + 1 == tabs.children.len() {
|
||||||
// suggest dropping after last tab:
|
// suggest dropping after last tab:
|
||||||
drop_context.suggest_point(
|
drop_context.suggest_rect(
|
||||||
InsertionPoint::new(node_id, LayoutType::Tabs, insertion_index + 1),
|
InsertionPoint::new(node_id, LayoutType::Tabs, insertion_index + 1),
|
||||||
response.rect.right_center(),
|
Rect::from_center_size(rect.right_center(), vec2(4.0, rect.height())),
|
||||||
Rect::from_center_size(
|
|
||||||
response.rect.right_center(),
|
|
||||||
vec2(4.0, response.rect.height()),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_tab_rect = Some(response.rect);
|
prev_tab_rect = Some(rect);
|
||||||
insertion_index += 1;
|
insertion_index += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -910,8 +936,46 @@ impl<Leaf> Nodes<Leaf> {
|
|||||||
parent_id: NodeId,
|
parent_id: NodeId,
|
||||||
horizontal: &mut Horizontal,
|
horizontal: &mut Horizontal,
|
||||||
) {
|
) {
|
||||||
for child in &horizontal.children {
|
let mut prev_rect: Option<Rect> = None;
|
||||||
self.node_ui(behavior, drop_context, ui, *child);
|
let mut insertion_index = 0; // skips over drag-source, if any, beacuse it will be removed then re-inserted
|
||||||
|
|
||||||
|
for (i, &child) in horizontal.children.iter().enumerate() {
|
||||||
|
let rect = self.nodes[&child].rect;
|
||||||
|
|
||||||
|
if is_being_dragged(ui.ctx(), child) {
|
||||||
|
// suggest self as drop-target:
|
||||||
|
drop_context.suggest_rect(
|
||||||
|
InsertionPoint::new(parent_id, LayoutType::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, LayoutType::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, LayoutType::Horizontal, 0),
|
||||||
|
rect.split_left_right_at_fraction(0.66).0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if i + 1 == horizontal.children.len() {
|
||||||
|
// Suggest dropping after the last child:
|
||||||
|
drop_context.suggest_rect(
|
||||||
|
InsertionPoint::new(parent_id, LayoutType::Horizontal, insertion_index + 1),
|
||||||
|
rect.split_left_right_at_fraction(0.33).1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
insertion_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_rect = Some(rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user