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

Drag to re-arrange tabs

This commit is contained in:
Emil Ernerfeldt
2023-04-24 20:46:21 +02:00
parent 13ec65cf08
commit 240989df8b
2 changed files with 80 additions and 34 deletions

View File

@@ -440,7 +440,9 @@ impl<Leaf> Dock<Leaf> {
.current_pos(mouse_pos) .current_pos(mouse_pos)
.interactable(false) .interactable(false)
.show(ui.ctx(), |ui| { .show(ui.ctx(), |ui| {
egui::Frame::popup(ui.style()).show(ui, |ui| { let mut frame = egui::Frame::popup(ui.style());
frame.fill = frame.fill.gamma_multiply(0.5); // Make see-through
frame.show(ui, |ui| {
// TODO: preview contents // TODO: preview contents
let text = behavior.tab_text_for_node(&self.nodes, dragged_node_id); let text = behavior.tab_text_for_node(&self.nodes, dragged_node_id);
ui.label(text); ui.label(text);
@@ -704,6 +706,16 @@ struct InsertionPoint {
index: usize, index: usize,
} }
impl InsertionPoint {
fn new(parent_id: NodeId, layout_type: LayoutType, index: usize) -> Self {
Self {
parent_id,
layout_type,
index,
}
}
}
struct DropContext { struct DropContext {
active: bool, active: bool,
dragged_node_id: Option<NodeId>, dragged_node_id: Option<NodeId>,
@@ -716,48 +728,31 @@ struct DropContext {
impl DropContext { impl DropContext {
fn on_node(&mut self, parent_id: NodeId, rect: Rect) { fn on_node(&mut self, parent_id: NodeId, rect: Rect) {
if !self.active {
return;
}
self.suggest_point( self.suggest_point(
InsertionPoint { InsertionPoint::new(parent_id, LayoutType::Horizontal, 0),
parent_id,
layout_type: LayoutType::Horizontal,
index: 0,
},
rect.left_center(), rect.left_center(),
rect.split_left_right_at_fraction(0.5).0, rect.split_left_right_at_fraction(0.5).0,
); );
self.suggest_point( self.suggest_point(
InsertionPoint { InsertionPoint::new(parent_id, LayoutType::Horizontal, usize::MAX),
parent_id,
layout_type: LayoutType::Horizontal,
index: usize::MAX,
},
rect.right_center(), rect.right_center(),
rect.split_left_right_at_fraction(0.5).1, rect.split_left_right_at_fraction(0.5).1,
); );
self.suggest_point( self.suggest_point(
InsertionPoint { InsertionPoint::new(parent_id, LayoutType::Vertical, 0),
parent_id,
layout_type: LayoutType::Vertical,
index: 0,
},
rect.center_top(), rect.center_top(),
rect.split_top_bottom_at_fraction(0.5).0, rect.split_top_bottom_at_fraction(0.5).0,
); );
self.suggest_point( self.suggest_point(
InsertionPoint { InsertionPoint::new(parent_id, LayoutType::Vertical, usize::MAX),
parent_id,
layout_type: LayoutType::Vertical,
index: usize::MAX,
},
rect.center_bottom(), rect.center_bottom(),
rect.split_top_bottom_at_fraction(0.5).1, rect.split_top_bottom_at_fraction(0.5).1,
); );
self.suggest_point( self.suggest_point(
InsertionPoint { InsertionPoint::new(parent_id, LayoutType::Tabs, 1),
parent_id,
layout_type: LayoutType::Tabs,
index: 1,
},
rect.center(), rect.center(),
rect, rect,
); );
@@ -823,7 +818,7 @@ impl<Leaf> Nodes<Leaf> {
drop_context: &mut DropContext, drop_context: &mut DropContext,
ui: &mut Ui, ui: &mut Ui,
rect: Rect, rect: Rect,
parent_id: NodeId, node_id: NodeId,
tabs: &mut Tabs, tabs: &mut Tabs,
) { ) {
if !tabs.children.iter().any(|&child| child == tabs.active) { if !tabs.children.iter().any(|&child| child == tabs.active) {
@@ -837,15 +832,18 @@ impl<Leaf> Nodes<Leaf> {
// Show tab bar: // Show tab bar:
tab_bar_ui.horizontal(|ui| { tab_bar_ui.horizontal(|ui| {
for &child_id in &tabs.children { let mut prev_tab_rect: Option<Rect> = None;
let mut insertion_index = 0; // skips over drag-source, if any
for (child_index, &child_id) in tabs.children.iter().enumerate() {
let selected = child_id == tabs.active; let selected = child_id == tabs.active;
let id = child_id.id(); let id = child_id.id();
// let is_node_being_dragged = ui.memory(|mem| mem.is_being_dragged(id)) let is_node_being_dragged =
// && is_possible_drag(ui.ctx()); ui.memory(|mem| mem.is_being_dragged(id)) && is_possible_drag(ui.ctx());
// if is_node_being_dragged { if is_node_being_dragged {
// continue; // leave a gap! continue; // leave a gap!
// } }
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);
@@ -859,10 +857,49 @@ impl<Leaf> Nodes<Leaf> {
tabs.active = child_id; tabs.active = child_id;
} }
} }
{
// suggest dropping before this tab:
let before_point = if let Some(prev_tab_rect) = prev_tab_rect {
// between
prev_tab_rect
.right_center()
.lerp(response.rect.left_center(), 0.5)
} else {
// before first
response.rect.left_center()
};
drop_context.suggest_point(
InsertionPoint::new(node_id, LayoutType::Tabs, insertion_index),
before_point,
Rect::from_center_size(before_point, vec2(4.0, response.rect.height())),
);
}
if child_index + 1 == tabs.children.len() {
// suggest dropping after last tab:
drop_context.suggest_point(
InsertionPoint::new(node_id, LayoutType::Tabs, insertion_index + 1),
response.rect.right_center(),
Rect::from_center_size(
response.rect.right_center(),
vec2(4.0, response.rect.height()),
),
);
}
prev_tab_rect = Some(response.rect);
insertion_index += 1;
} }
}); });
self.node_ui(behavior, drop_context, ui, tabs.active); // When dragged, don't show it (it is "being held")
let is_active_being_dragged =
ui.memory(|mem| mem.is_being_dragged(tabs.active.id())) && is_possible_drag(ui.ctx());
if !is_active_being_dragged {
self.node_ui(behavior, drop_context, ui, tabs.active);
}
} }
fn horizontal_ui( fn horizontal_ui(
@@ -930,6 +967,7 @@ impl<Leaf> Nodes<Leaf> {
NodeLayout::Horizontal(Horizontal { children, .. }) NodeLayout::Horizontal(Horizontal { children, .. })
| NodeLayout::Vertical(Vertical { children, .. }) => { | NodeLayout::Vertical(Vertical { children, .. }) => {
// TODO: join nested versions of the same thing
children.retain_mut(|child| match self.simplify(options, *child) { children.retain_mut(|child| match self.simplify(options, *child) {
SimplifyAction::Remove => false, SimplifyAction::Remove => false,
SimplifyAction::Keep => true, SimplifyAction::Keep => true,

View File

@@ -188,6 +188,14 @@ impl Pos2 {
y: self.y.clamp(min.y, max.y), y: self.y.clamp(min.y, max.y),
} }
} }
/// Linearly interpolate towards another point, so that `0.0 => self, 1.0 => other`.
pub fn lerp(&self, other: Pos2, t: f32) -> Pos2 {
Pos2 {
x: lerp(self.x..=other.x, t),
y: lerp(self.y..=other.y, t),
}
}
} }
impl std::ops::Index<usize> for Pos2 { impl std::ops::Index<usize> for Pos2 {