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 {
|
pub struct Overlay {
|
||||||
ratsnest: Ratsnest,
|
ratsnest: Ratsnest,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
reselect_bbox: Option<Point>,
|
reselect_bbox: Option<(SelectionMode, Point)>,
|
||||||
active_layer: usize,
|
active_layer: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,29 +61,52 @@ impl Overlay {
|
||||||
self.reselect_bbox = None;
|
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() {
|
if self.reselect_bbox.is_none() {
|
||||||
// handle bounding box selection
|
// 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) {
|
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
|
// handle bounding box selection
|
||||||
self.select_all_in_bbox(board, &aabb);
|
|
||||||
self.reselect_bbox = None;
|
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) {
|
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)
|
// 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
|
// this is mostly in order to allow the user to recover from a missed/dropped drag_stop event
|
||||||
self.select_all_in_bbox(
|
self.drag_stop(board, at);
|
||||||
board,
|
|
||||||
&AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()]),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,23 +135,8 @@ impl Overlay {
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
aabb: &AABB<[f64; 2]>,
|
aabb: &AABB<[f64; 2]>,
|
||||||
) {
|
) {
|
||||||
use rstar::Envelope;
|
self.selection
|
||||||
for &geom in board
|
.select_all_in_bbox(board, aabb, self.active_layer);
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ratsnest(&self) -> &Ratsnest {
|
pub fn ratsnest(&self) -> &Ratsnest {
|
||||||
|
|
@ -133,8 +148,12 @@ impl Overlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the currently selected bounding box of a bounding-box reselect
|
/// Returns the currently selected bounding box of a bounding-box reselect
|
||||||
pub fn get_bbox_reselect(&self, at: Point) -> Option<AABB<[f64; 2]>> {
|
pub fn get_bbox_reselect(&self, at: Point) -> Option<(SelectionMode, AABB<[f64; 2]>)> {
|
||||||
self.reselect_bbox
|
self.reselect_bbox.map(|(selmode, pt)| {
|
||||||
.map(|pt| AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()]))
|
(
|
||||||
|
selmode,
|
||||||
|
AABB::from_corners([pt.x(), pt.y()], [at.x(), at.y()]),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,10 +93,15 @@ impl Viewport {
|
||||||
overlay.click(board, latest_point);
|
overlay.click(board, latest_point);
|
||||||
}
|
}
|
||||||
} else if response.drag_started_by(egui::PointerButton::Primary) {
|
} 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) {
|
} else if response.drag_stopped_by(egui::PointerButton::Primary) {
|
||||||
overlay.drag_stop(board, latest_point);
|
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);
|
painter.paint_bbox_with_color(cur_bbox, egui::Color32::RED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use rstar::AABB;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -145,6 +146,29 @@ impl Selection {
|
||||||
Self::default()
|
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) {
|
pub fn select_at_node(&mut self, board: &Board<impl AccessMesadata>, node: NodeIndex) {
|
||||||
if let Some(selector) = PinSelector::try_from_node(board, node) {
|
if let Some(selector) = PinSelector::try_from_node(board, node) {
|
||||||
self.pin_selection.0.insert(selector);
|
self.pin_selection.0.insert(selector);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue