Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow drag-and-dropping multiple containers and views in the blueprint tree #8334

Merged
merged 22 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a2cece8
- initiate drag from inside `ctx.select_hovered_on_click()`
abey79 Dec 4, 2024
9a92eff
post rebase
abey79 Dec 5, 2024
ded7265
Better display of the drag item
abey79 Dec 5, 2024
0f9b14e
Renamed `select_hovered_on_click` to `handle_select_hover_drag_intera…
abey79 Dec 6, 2024
7b15fed
70% opacity for the bluepill
abey79 Dec 6, 2024
1c09ca9
post rebase
abey79 Dec 6, 2024
701b666
Merge branch 'main' into antoine/dnd-multi-select-content
abey79 Dec 9, 2024
306ff86
Properly deal with entity vs. instance
abey79 Dec 9, 2024
d97bdae
No longer draw the pill with invalid selection
abey79 Dec 9, 2024
4cede3e
Cleanup moving code
abey79 Dec 9, 2024
efbd590
Merge branch 'main' into antoine/dnd-multi-select-content
abey79 Dec 9, 2024
0ceb786
remove unnecessary icon
abey79 Dec 9, 2024
9419e30
Merge branch 'main' into antoine/dnd-multi-select-content
abey79 Dec 10, 2024
81fd640
Merge branch 'refs/heads/main' into antoine/dnd-multi-select-content
abey79 Dec 10, 2024
912eb74
Merge branch 'main' into antoine/dnd-multi-select-content
abey79 Dec 10, 2024
0a80f79
Merge branch 'main' into antoine/dnd-multi-select-content
abey79 Dec 11, 2024
3a37556
Merge branch 'main' into antoine/dnd-multi-select-content
abey79 Dec 11, 2024
ba14e6a
Addressed some review comments
abey79 Dec 11, 2024
5242225
Update crates/viewer/re_viewer_context/src/drag_and_drop.rs
abey79 Dec 11, 2024
b6753cd
More review comments
abey79 Dec 11, 2024
c1b0aca
Merge branch 'main' into antoine/dnd-multi-select-content
abey79 Dec 11, 2024
245210a
Add TODO + fix doclink
abey79 Dec 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1934,7 +1934,7 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "ecolor"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"bytemuck",
"color-hex",
Expand All @@ -1951,7 +1951,7 @@ checksum = "18aade80d5e09429040243ce1143ddc08a92d7a22820ac512610410a4dd5214f"
[[package]]
name = "eframe"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"ahash",
"bytemuck",
Expand Down Expand Up @@ -1990,7 +1990,7 @@ dependencies = [
[[package]]
name = "egui"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"accesskit",
"ahash",
Expand All @@ -2007,7 +2007,7 @@ dependencies = [
[[package]]
name = "egui-wgpu"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"ahash",
"bytemuck",
Expand All @@ -2026,7 +2026,7 @@ dependencies = [
[[package]]
name = "egui-winit"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"accesskit_winit",
"ahash",
Expand Down Expand Up @@ -2068,7 +2068,7 @@ dependencies = [
[[package]]
name = "egui_extras"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"ahash",
"egui",
Expand All @@ -2085,7 +2085,7 @@ dependencies = [
[[package]]
name = "egui_glow"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"ahash",
"bytemuck",
Expand All @@ -2103,7 +2103,7 @@ dependencies = [
[[package]]
name = "egui_kittest"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"dify",
"egui",
Expand Down Expand Up @@ -2172,7 +2172,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "emath"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"bytemuck",
"serde",
Expand Down Expand Up @@ -2288,7 +2288,7 @@ dependencies = [
[[package]]
name = "epaint"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"
dependencies = [
"ab_glyph",
"ahash",
Expand All @@ -2307,7 +2307,7 @@ dependencies = [
[[package]]
name = "epaint_default_fonts"
version = "0.29.1"
source = "git+https://github.com/emilk/egui.git?rev=577ee8d22810752540636febac5660a5119c6550#577ee8d22810752540636febac5660a5119c6550"
source = "git+https://github.com/emilk/egui.git?rev=13352d606496d7b1c5fd6fcfbe3c85baae39c040#13352d606496d7b1c5fd6fcfbe3c85baae39c040"

[[package]]
name = "equivalent"
Expand Down
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -559,13 +559,13 @@ significant_drop_tightening = "allow" # An update of parking_lot made this trigg
# As a last resport, patch with a commit to our own repository.
# ALWAYS document what PR the commit hash is part of, or when it was merged into the upstream trunk.

ecolor = { git = "https://github.com/emilk/egui.git", rev = "577ee8d22810752540636febac5660a5119c6550" } # egui master 2024-12-04
eframe = { git = "https://github.com/emilk/egui.git", rev = "577ee8d22810752540636febac5660a5119c6550" } # egui master 2024-12-04
egui = { git = "https://github.com/emilk/egui.git", rev = "577ee8d22810752540636febac5660a5119c6550" } # egui master 2024-12-04
egui_extras = { git = "https://github.com/emilk/egui.git", rev = "577ee8d22810752540636febac5660a5119c6550" } # egui master 2024-12-04
egui_kittest = { git = "https://github.com/emilk/egui.git", rev = "577ee8d22810752540636febac5660a5119c6550" } # egui master 2024-12-04
egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "577ee8d22810752540636febac5660a5119c6550" } # egui master 2024-12-04
emath = { git = "https://github.com/emilk/egui.git", rev = "577ee8d22810752540636febac5660a5119c6550" } # egui master 2024-12-04
ecolor = { git = "https://github.com/emilk/egui.git", rev = "13352d606496d7b1c5fd6fcfbe3c85baae39c040" } # egui master 2024-12-09
eframe = { git = "https://github.com/emilk/egui.git", rev = "13352d606496d7b1c5fd6fcfbe3c85baae39c040" } # egui master 2024-12-09
egui = { git = "https://github.com/emilk/egui.git", rev = "13352d606496d7b1c5fd6fcfbe3c85baae39c040" } # egui master 2024-12-09
egui_extras = { git = "https://github.com/emilk/egui.git", rev = "13352d606496d7b1c5fd6fcfbe3c85baae39c040" } # egui master 2024-12-09
egui_kittest = { git = "https://github.com/emilk/egui.git", rev = "13352d606496d7b1c5fd6fcfbe3c85baae39c040" } # egui master 2024-12-09
egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "13352d606496d7b1c5fd6fcfbe3c85baae39c040" } # egui master 2024-12-09
emath = { git = "https://github.com/emilk/egui.git", rev = "13352d606496d7b1c5fd6fcfbe3c85baae39c040" } # egui master 2024-12-09

# Useful while developing:
# ecolor = { path = "../../egui/crates/ecolor" }
Expand Down
95 changes: 52 additions & 43 deletions crates/viewer/re_blueprint_tree/src/blueprint_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use re_types::blueprint::components::Visible;
use re_ui::{drag_and_drop::DropTarget, list_item, ContextExt as _, DesignTokens, UiExt as _};
use re_viewer_context::{
contents_name_style, icon_for_container_kind, CollapseScope, Contents, DataResultNodeOrPath,
SystemCommandSender,
DragAndDropPayload, SystemCommandSender,
};
use re_viewer_context::{
ContainerId, DataQueryResult, DataResultNode, HoverHighlight, Item, SpaceViewId, ViewerContext,
Expand Down Expand Up @@ -168,7 +168,7 @@ impl BlueprintTree {
let item_response = ui
.list_item()
.selected(ctx.selection().contains_item(&item))
.draggable(false)
.draggable(true) // allowed for consistency but results in an invalid drag
.drop_target_style(self.is_candidate_drop_parent_container(&container_id))
.show_flat(
ui,
Expand All @@ -189,7 +189,7 @@ impl BlueprintTree {
SelectionUpdateBehavior::UseSelection,
);
self.scroll_to_me_if_needed(ui, &item, &item_response);
ctx.select_hovered_on_click(&item_response, item);
ctx.handle_select_hover_drag_interactions(&item_response, item, true);

self.handle_root_container_drag_and_drop_interaction(
viewport,
Expand Down Expand Up @@ -270,12 +270,11 @@ impl BlueprintTree {
SelectionUpdateBehavior::UseSelection,
);
self.scroll_to_me_if_needed(ui, &item, &response);
ctx.select_hovered_on_click(&response, item);
ctx.handle_select_hover_drag_interactions(&response, item, true);

viewport.set_content_visibility(ctx, &content, visible);

self.handle_drag_and_drop_interaction(
ctx,
viewport,
ui,
content,
Expand Down Expand Up @@ -410,13 +409,12 @@ impl BlueprintTree {
SelectionUpdateBehavior::UseSelection,
);
self.scroll_to_me_if_needed(ui, &item, &response);
ctx.select_hovered_on_click(&response, item);
ctx.handle_select_hover_drag_interactions(&response, item, true);

let content = Contents::SpaceView(*space_view_id);

viewport.set_content_visibility(ctx, &content, visible);
self.handle_drag_and_drop_interaction(
ctx,
viewport,
ui,
content,
Expand Down Expand Up @@ -498,6 +496,7 @@ impl BlueprintTree {

let list_item = ui
.list_item()
.draggable(true)
.selected(is_selected)
.force_hovered(is_item_hovered);

Expand Down Expand Up @@ -603,7 +602,7 @@ impl BlueprintTree {
SelectionUpdateBehavior::UseSelection,
);
self.scroll_to_me_if_needed(ui, &item, &response);
ctx.select_hovered_on_click(&response, item);
ctx.handle_select_hover_drag_interactions(&response, item, true);
}

/// Add a button to trigger the addition of a new space view or container.
Expand Down Expand Up @@ -643,16 +642,21 @@ impl BlueprintTree {
response: &egui::Response,
) {
//
// check if a drag is in progress and set the cursor accordingly
// check if a drag with acceptable content is in progress
//

let Some(dragged_item_id) = egui::DragAndDrop::payload(ui.ctx()).map(|payload| *payload)
let Some(dragged_payload) = egui::DragAndDrop::payload::<DragAndDropPayload>(ui.ctx())
else {
// nothing is being dragged, so nothing to do
return;
};

ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing);
let DragAndDropPayload::Contents {
contents: dragged_contents,
} = dragged_payload.as_ref()
else {
// nothing we care about is being dragged
return;
};

//
// find the drop target
Expand All @@ -675,39 +679,34 @@ impl BlueprintTree {
);

if let Some(drop_target) = drop_target {
self.handle_drop_target(viewport, ui, dragged_item_id, &drop_target);
self.handle_contents_drop_target(viewport, ui, dragged_contents, &drop_target);
}
}

fn handle_drag_and_drop_interaction(
&mut self,
ctx: &ViewerContext<'_>,
viewport: &ViewportBlueprint,
ui: &egui::Ui,
contents: Contents,
response: &egui::Response,
body_response: Option<&egui::Response>,
) {
//
// initiate drag and force single-selection
// check if a drag with acceptable content is in progress
//

if response.drag_started() {
ctx.selection_state().set_selection(contents.as_item());
egui::DragAndDrop::set_payload(ui.ctx(), contents);
}

//
// check if a drag is in progress and set the cursor accordingly
//

let Some(dragged_item_id) = egui::DragAndDrop::payload(ui.ctx()).map(|payload| *payload)
let Some(dragged_payload) = egui::DragAndDrop::payload::<DragAndDropPayload>(ui.ctx())
else {
// nothing is being dragged, so nothing to do
return;
};

ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing);
let DragAndDropPayload::Contents {
contents: dragged_contents,
} = dragged_payload.as_ref()
else {
// nothing we care about is being dragged
return;
};

//
// find our parent, our position within parent, and the previous container (if any)
Expand Down Expand Up @@ -759,7 +758,7 @@ impl BlueprintTree {
);

if let Some(drop_target) = drop_target {
self.handle_drop_target(viewport, ui, dragged_item_id, &drop_target);
self.handle_contents_drop_target(viewport, ui, dragged_contents, &drop_target);
}
}

Expand All @@ -770,16 +769,21 @@ impl BlueprintTree {
empty_space: egui::Rect,
) {
//
// check if a drag is in progress and set the cursor accordingly
// check if a drag with acceptable content is in progress
//

let Some(dragged_item_id) = egui::DragAndDrop::payload(ui.ctx()).map(|payload| *payload)
let Some(dragged_payload) = egui::DragAndDrop::payload::<DragAndDropPayload>(ui.ctx())
else {
// nothing is being dragged, so nothing to do
return;
};

ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing);
let DragAndDropPayload::Contents {
contents: dragged_contents,
} = dragged_payload.as_ref()
else {
// nothing we care about is being dragged
return;
};

//
// prepare a drop target corresponding to "insert last in root container"
Expand All @@ -795,25 +799,30 @@ impl BlueprintTree {
usize::MAX,
);

self.handle_drop_target(viewport, ui, dragged_item_id, &drop_target);
self.handle_contents_drop_target(viewport, ui, dragged_contents, &drop_target);
}
}

fn handle_drop_target(
fn handle_contents_drop_target(
&mut self,
viewport: &ViewportBlueprint,
ui: &Ui,
dragged_item_id: Contents,
dragged_contents: &[Contents],
drop_target: &DropTarget<Contents>,
) {
// We cannot allow the target location to be "inside" the dragged item, because that would amount moving
// myself inside of me.
if let Contents::Container(dragged_container_id) = &dragged_item_id {
if viewport
.is_contents_in_container(&drop_target.target_parent_id, dragged_container_id)
{
return;
// We cannot allow the target location to be "inside" any of the dragged items, because that
// would amount to moving myself inside of me.
if dragged_contents.iter().any(|dragged_contents| {
if let Contents::Container(dragged_container_id) = dragged_contents {
if viewport
.is_contents_in_container(&drop_target.target_parent_id, dragged_container_id)
{
return true;
}
}
false
}) {
abey79 marked this conversation as resolved.
Show resolved Hide resolved
return;
}

ui.painter().hline(
Expand All @@ -829,7 +838,7 @@ impl BlueprintTree {

if ui.input(|i| i.pointer.any_released()) {
viewport.move_contents(
dragged_item_id,
dragged_contents,
target_container_id,
drop_target.target_position_index,
);
Expand Down
2 changes: 1 addition & 1 deletion crates/viewer/re_data_ui/src/instance_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ fn component_list_ui(
});

if interactive {
ctx.select_hovered_on_click(&response, item);
ctx.handle_select_hover_drag_interactions(&response, item, false);
}
}
});
Expand Down
2 changes: 1 addition & 1 deletion crates/viewer/re_data_ui/src/item_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ pub fn cursor_interact_with_selectable(
let is_item_hovered =
ctx.selection_state().highlight_for_ui_element(&item) == HoverHighlight::Hovered;

ctx.select_hovered_on_click(&response, item);
ctx.handle_select_hover_drag_interactions(&response, item, false);
// TODO(andreas): How to deal with shift click for selecting ranges?

if is_item_hovered {
Expand Down
4 changes: 2 additions & 2 deletions crates/viewer/re_selection_panel/src/selection_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ fn list_existing_data_blueprints(

// We don't use item_ui::cursor_interact_with_selectable here because the forced
// hover background is distracting and not useful.
ctx.select_hovered_on_click(&response, item);
ctx.handle_select_hover_drag_interactions(&response, item, false);
}
}
}
Expand Down Expand Up @@ -929,7 +929,7 @@ fn show_list_item_for_container_child(
&response,
SelectionUpdateBehavior::Ignore,
);
ctx.select_hovered_on_click(&response, item);
ctx.handle_select_hover_drag_interactions(&response, item, false);

if remove_contents {
viewport.mark_user_interaction(ctx);
Expand Down
Loading
Loading