mirror of https://codeberg.org/topola/topola.git
feat(egui/overlay/bbox): allow multiple selection kinds of drag-selected BBoxes
Toggling is triggered by holding down `Ctrl` during `drag_start`. Addition is triggered by holding down `Shift` during `drag_start`. Substitution is used otherwise/by default.
This commit is contained in:
parent
044457e6bb
commit
e9ad380a58
|
|
@ -22,10 +22,17 @@ use topola::{
|
|||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SelectionMode {
|
||||
Addition,
|
||||
Substitution,
|
||||
Toggling,
|
||||
}
|
||||
|
||||
pub struct Overlay {
|
||||
ratsnest: Ratsnest,
|
||||
selection: Selection,
|
||||
reselect_bbox: Option<Point>,
|
||||
reselect_bbox: Option<(SelectionMode, Point)>,
|
||||
active_layer: usize,
|
||||
}
|
||||
|
||||
|
|
@ -54,29 +61,52 @@ impl Overlay {
|
|||
self.reselect_bbox = None;
|
||||
}
|
||||
|
||||
pub fn drag_start(&mut self, board: &Board<impl AccessMesadata>, at: Point) {
|
||||
pub fn drag_start(
|
||||
&mut self,
|
||||
board: &Board<impl AccessMesadata>,
|
||||
at: Point,
|
||||
modifiers: &egui::Modifiers,
|
||||
) {
|
||||
if self.reselect_bbox.is_none() {
|
||||
// handle bounding box selection
|
||||
self.reselect_bbox = Some(at);
|
||||
let selmode = if modifiers.ctrl {
|
||||
SelectionMode::Toggling
|
||||
} else if modifiers.shift {
|
||||
SelectionMode::Addition
|
||||
} else {
|
||||
SelectionMode::Substitution
|
||||
};
|
||||
self.reselect_bbox = Some((selmode, at));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drag_stop(&mut self, board: &Board<impl AccessMesadata>, at: Point) {
|
||||
if let Some(aabb) = self.get_bbox_reselect(at) {
|
||||
if let Some((selmode, aabb)) = self.get_bbox_reselect(at) {
|
||||
// handle bounding box selection
|
||||
self.select_all_in_bbox(board, &aabb);
|
||||
self.reselect_bbox = None;
|
||||
|
||||
match selmode {
|
||||
SelectionMode::Substitution => {
|
||||
self.selection = Selection::new();
|
||||
self.select_all_in_bbox(board, &aabb);
|
||||
}
|
||||
SelectionMode::Addition => {
|
||||
self.select_all_in_bbox(board, &aabb);
|
||||
}
|
||||
SelectionMode::Toggling => {
|
||||
let old_selection = self.take_selection();
|
||||
self.select_all_in_bbox(board, &aabb);
|
||||
self.selection ^= &old_selection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn click(&mut self, board: &Board<impl AccessMesadata>, at: Point) {
|
||||
if let Some(pt) = self.reselect_bbox.take() {
|
||||
if self.reselect_bbox.is_some() {
|
||||
// handle bounding box selection (takes precendence over other interactions)
|
||||
// this is mostly in order to allow the user to recover from a missed/dropped drag_stop event
|
||||
self.select_all_in_bbox(
|
||||
board,
|
||||
&AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()]),
|
||||
);
|
||||
self.drag_stop(board, at);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -105,23 +135,8 @@ impl Overlay {
|
|||
board: &Board<impl AccessMesadata>,
|
||||
aabb: &AABB<[f64; 2]>,
|
||||
) {
|
||||
use rstar::Envelope;
|
||||
for &geom in board
|
||||
.layout()
|
||||
.drawing()
|
||||
.rtree()
|
||||
.locate_in_envelope_intersecting(&AABB::<[f64; 3]>::from_corners(
|
||||
[aabb.lower()[0], aabb.lower()[1], -f64::INFINITY],
|
||||
[aabb.upper()[0], aabb.upper()[1], f64::INFINITY],
|
||||
))
|
||||
{
|
||||
let node = geom.data;
|
||||
if aabb.contains_envelope(&board.layout().node_bbox(node))
|
||||
&& board.layout().is_node_in_layer(node, self.active_layer)
|
||||
{
|
||||
self.selection.select_at_node(board, node);
|
||||
}
|
||||
}
|
||||
self.selection
|
||||
.select_all_in_bbox(board, aabb, self.active_layer);
|
||||
}
|
||||
|
||||
pub fn ratsnest(&self) -> &Ratsnest {
|
||||
|
|
@ -133,8 +148,12 @@ impl Overlay {
|
|||
}
|
||||
|
||||
/// Returns the currently selected bounding box of a bounding-box reselect
|
||||
pub fn get_bbox_reselect(&self, at: Point) -> Option<AABB<[f64; 2]>> {
|
||||
self.reselect_bbox
|
||||
.map(|pt| AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()]))
|
||||
pub fn get_bbox_reselect(&self, at: Point) -> Option<(SelectionMode, AABB<[f64; 2]>)> {
|
||||
self.reselect_bbox.map(|(selmode, pt)| {
|
||||
(
|
||||
selmode,
|
||||
AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()]),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,10 +93,15 @@ impl Viewport {
|
|||
overlay.click(board, latest_point);
|
||||
}
|
||||
} else if response.drag_started_by(egui::PointerButton::Primary) {
|
||||
overlay.drag_start(board, latest_point);
|
||||
overlay.drag_start(
|
||||
board,
|
||||
latest_point,
|
||||
&response.ctx.input(|i| i.modifiers),
|
||||
);
|
||||
} else if response.drag_stopped_by(egui::PointerButton::Primary) {
|
||||
overlay.drag_stop(board, latest_point);
|
||||
} else if let Some(cur_bbox) = overlay.get_bbox_reselect(latest_point) {
|
||||
} else if let Some((_, cur_bbox)) = overlay.get_bbox_reselect(latest_point)
|
||||
{
|
||||
painter.paint_bbox_with_color(cur_bbox, egui::Color32::RED);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use rstar::AABB;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -145,6 +146,29 @@ impl Selection {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
pub fn select_all_in_bbox(
|
||||
&mut self,
|
||||
board: &Board<impl AccessMesadata>,
|
||||
aabb: &AABB<[f64; 2]>,
|
||||
active_layer: usize,
|
||||
) {
|
||||
use rstar::Envelope;
|
||||
let layout = board.layout();
|
||||
for &geom in layout.drawing().rtree().locate_in_envelope_intersecting(
|
||||
&AABB::<[f64; 3]>::from_corners(
|
||||
[aabb.lower()[0], aabb.lower()[1], -f64::INFINITY],
|
||||
[aabb.upper()[0], aabb.upper()[1], f64::INFINITY],
|
||||
),
|
||||
) {
|
||||
let node = geom.data;
|
||||
if aabb.contains_envelope(&layout.node_bbox(node))
|
||||
&& layout.is_node_in_layer(node, active_layer)
|
||||
{
|
||||
self.select_at_node(board, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_at_node(&mut self, board: &Board<impl AccessMesadata>, node: NodeIndex) {
|
||||
if let Some(selector) = PinSelector::try_from_node(board, node) {
|
||||
self.pin_selection.0.insert(selector);
|
||||
|
|
|
|||
Loading…
Reference in New Issue