mirror of https://codeberg.org/topola/topola.git
feat(selection): BBox selection should span all currently visible layers
In Via, is_in_layer, `from_layer` and `to_layer` were swapped, this has been also fixed here.
This commit is contained in:
parent
a16eba8891
commit
ea6df23b95
|
|
@ -246,7 +246,9 @@ impl MenuBar {
|
||||||
workspace.overlay.unselect_all();
|
workspace.overlay.unselect_all();
|
||||||
} else if actions.edit.select_all.consume_key_triggered(ctx, ui) {
|
} else if actions.edit.select_all.consume_key_triggered(ctx, ui) {
|
||||||
let board = workspace.interactor.invoker().autorouter().board();
|
let board = workspace.interactor.invoker().autorouter().board();
|
||||||
workspace.overlay.select_all(board);
|
workspace
|
||||||
|
.overlay
|
||||||
|
.select_all(board, &workspace.appearance_panel);
|
||||||
} else if actions.place.place_via.consume_key_enabled(
|
} else if actions.place.place_via.consume_key_enabled(
|
||||||
ctx,
|
ctx,
|
||||||
ui,
|
ui,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ use topola::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::appearance_panel::AppearancePanel;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum SelectionMode {
|
pub enum SelectionMode {
|
||||||
Addition,
|
Addition,
|
||||||
|
|
@ -55,9 +57,14 @@ impl Overlay {
|
||||||
core::mem::replace(&mut self.selection, Selection::new())
|
core::mem::replace(&mut self.selection, Selection::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_all(&mut self, board: &Board<impl AccessMesadata>) {
|
pub fn select_all(
|
||||||
|
&mut self,
|
||||||
|
board: &Board<impl AccessMesadata>,
|
||||||
|
appearance_panel: &AppearancePanel,
|
||||||
|
) {
|
||||||
self.select_all_in_bbox(
|
self.select_all_in_bbox(
|
||||||
board,
|
board,
|
||||||
|
appearance_panel,
|
||||||
&AABB::from_corners([-INF, -INF], [INF, INF]),
|
&AABB::from_corners([-INF, -INF], [INF, INF]),
|
||||||
BboxSelectionKind::CompletelyInside,
|
BboxSelectionKind::CompletelyInside,
|
||||||
);
|
);
|
||||||
|
|
@ -71,6 +78,7 @@ impl Overlay {
|
||||||
pub fn drag_start(
|
pub fn drag_start(
|
||||||
&mut self,
|
&mut self,
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
|
appearance_panel: &AppearancePanel,
|
||||||
at: Point,
|
at: Point,
|
||||||
modifiers: &egui::Modifiers,
|
modifiers: &egui::Modifiers,
|
||||||
) {
|
) {
|
||||||
|
|
@ -87,7 +95,12 @@ impl Overlay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drag_stop(&mut self, board: &Board<impl AccessMesadata>, at: Point) {
|
pub fn drag_stop(
|
||||||
|
&mut self,
|
||||||
|
board: &Board<impl AccessMesadata>,
|
||||||
|
appearance_panel: &AppearancePanel,
|
||||||
|
at: Point,
|
||||||
|
) {
|
||||||
if let Some((selmode, bsk, aabb)) = self.get_bbox_reselect(at) {
|
if let Some((selmode, bsk, aabb)) = self.get_bbox_reselect(at) {
|
||||||
// handle bounding box selection
|
// handle bounding box selection
|
||||||
self.reselect_bbox = None;
|
self.reselect_bbox = None;
|
||||||
|
|
@ -95,25 +108,30 @@ impl Overlay {
|
||||||
match selmode {
|
match selmode {
|
||||||
SelectionMode::Substitution => {
|
SelectionMode::Substitution => {
|
||||||
self.selection = Selection::new();
|
self.selection = Selection::new();
|
||||||
self.select_all_in_bbox(board, &aabb, bsk);
|
self.select_all_in_bbox(board, appearance_panel, &aabb, bsk);
|
||||||
}
|
}
|
||||||
SelectionMode::Addition => {
|
SelectionMode::Addition => {
|
||||||
self.select_all_in_bbox(board, &aabb, bsk);
|
self.select_all_in_bbox(board, appearance_panel, &aabb, bsk);
|
||||||
}
|
}
|
||||||
SelectionMode::Toggling => {
|
SelectionMode::Toggling => {
|
||||||
let old_selection = self.take_selection();
|
let old_selection = self.take_selection();
|
||||||
self.select_all_in_bbox(board, &aabb, bsk);
|
self.select_all_in_bbox(board, appearance_panel, &aabb, bsk);
|
||||||
self.selection ^= &old_selection;
|
self.selection ^= &old_selection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn click(&mut self, board: &Board<impl AccessMesadata>, at: Point) {
|
pub fn click(
|
||||||
|
&mut self,
|
||||||
|
board: &Board<impl AccessMesadata>,
|
||||||
|
appearance_panel: &AppearancePanel,
|
||||||
|
at: Point,
|
||||||
|
) {
|
||||||
if self.reselect_bbox.is_some() {
|
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.drag_stop(board, at);
|
self.drag_stop(board, appearance_panel, at);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,11 +159,12 @@ impl Overlay {
|
||||||
pub fn select_all_in_bbox(
|
pub fn select_all_in_bbox(
|
||||||
&mut self,
|
&mut self,
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
|
appearance_panel: &AppearancePanel,
|
||||||
aabb: &AABB<[f64; 2]>,
|
aabb: &AABB<[f64; 2]>,
|
||||||
bsk: BboxSelectionKind,
|
bsk: BboxSelectionKind,
|
||||||
) {
|
) {
|
||||||
self.selection
|
self.selection
|
||||||
.select_all_in_bbox(board, aabb, self.active_layer, bsk);
|
.select_all_in_bbox(board, aabb, &appearance_panel.visible[..], bsk);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ratsnest(&self) -> &Ratsnest {
|
pub fn ratsnest(&self) -> &Ratsnest {
|
||||||
|
|
|
||||||
|
|
@ -90,16 +90,17 @@ impl Viewport {
|
||||||
maybe_net: Some(1234),
|
maybe_net: Some(1234),
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
overlay.click(board, latest_point);
|
overlay.click(board, layers, latest_point);
|
||||||
}
|
}
|
||||||
} else if response.drag_started_by(egui::PointerButton::Primary) {
|
} else if response.drag_started_by(egui::PointerButton::Primary) {
|
||||||
overlay.drag_start(
|
overlay.drag_start(
|
||||||
board,
|
board,
|
||||||
|
layers,
|
||||||
latest_point,
|
latest_point,
|
||||||
&response.ctx.input(|i| i.modifiers),
|
&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, layers, latest_point);
|
||||||
} else if let Some((_, bsk, cur_bbox)) =
|
} else if let Some((_, bsk, cur_bbox)) =
|
||||||
overlay.get_bbox_reselect(latest_point)
|
overlay.get_bbox_reselect(latest_point)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ impl Selection {
|
||||||
&mut self,
|
&mut self,
|
||||||
board: &Board<impl AccessMesadata>,
|
board: &Board<impl AccessMesadata>,
|
||||||
aabb: &AABB<[f64; 2]>,
|
aabb: &AABB<[f64; 2]>,
|
||||||
active_layer: usize,
|
layers: &[bool],
|
||||||
kind: BboxSelectionKind,
|
kind: BboxSelectionKind,
|
||||||
) {
|
) {
|
||||||
const INF: f64 = f64::INFINITY;
|
const INF: f64 = f64::INFINITY;
|
||||||
|
|
@ -208,7 +208,7 @@ impl Selection {
|
||||||
&AABB::<[f64; 3]>::from_corners([-INF, -INF, -INF], [INF, INF, INF]),
|
&AABB::<[f64; 3]>::from_corners([-INF, -INF, -INF], [INF, INF, INF]),
|
||||||
) {
|
) {
|
||||||
let node = geom.data;
|
let node = geom.data;
|
||||||
if layout.drawing().is_node_in_layer(node, active_layer) {
|
if layout.drawing().is_node_in_any_layer_of(node, layers) {
|
||||||
if let Some(rsel) = ResolvedSelector::try_from_node(board, node) {
|
if let Some(rsel) = ResolvedSelector::try_from_node(board, node) {
|
||||||
let rseli = selectors.entry(rsel).or_default();
|
let rseli = selectors.entry(rsel).or_default();
|
||||||
rseli.0.insert(node);
|
rseli.0.insert(node);
|
||||||
|
|
@ -236,7 +236,7 @@ impl Selection {
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
let node = geom.data;
|
let node = geom.data;
|
||||||
if layout.drawing().is_node_in_layer(node, active_layer)
|
if layout.drawing().is_node_in_any_layer_of(node, layers)
|
||||||
&& kind.matches(aabb, &layout.node_shape(node))
|
&& kind.matches(aabb, &layout.node_shape(node))
|
||||||
{
|
{
|
||||||
if let Some(rsel) = ResolvedSelector::try_from_node(board, node) {
|
if let Some(rsel) = ResolvedSelector::try_from_node(board, node) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use crate::{
|
||||||
collect::Collect,
|
collect::Collect,
|
||||||
dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, LooseDotIndex, LooseDotWeight},
|
dot::{DotIndex, DotWeight, FixedDotIndex, FixedDotWeight, LooseDotIndex, LooseDotWeight},
|
||||||
gear::{GearIndex, GetNextGear},
|
gear::{GearIndex, GetNextGear},
|
||||||
graph::{GetLayer, GetMaybeNet, MakePrimitive, PrimitiveIndex, PrimitiveWeight},
|
graph::{GetLayer, GetMaybeNet, IsInLayer, MakePrimitive, PrimitiveIndex, PrimitiveWeight},
|
||||||
guide::Guide,
|
guide::Guide,
|
||||||
loose::{GetPrevNextLoose, Loose, LooseIndex},
|
loose::{GetPrevNextLoose, Loose, LooseIndex},
|
||||||
primitive::{
|
primitive::{
|
||||||
|
|
@ -1039,6 +1039,24 @@ impl<CW: Copy, R: AccessRules> Drawing<CW, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_node_in_any_layer_of(
|
||||||
|
&self,
|
||||||
|
index: GenericNode<PrimitiveIndex, GenericIndex<CW>>,
|
||||||
|
layers: &[bool],
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
CW: IsInLayer,
|
||||||
|
{
|
||||||
|
match index {
|
||||||
|
GenericNode::Primitive(primitive) => {
|
||||||
|
primitive.primitive(self).is_in_any_layer_of(layers)
|
||||||
|
}
|
||||||
|
GenericNode::Compound(compound) => {
|
||||||
|
self.compound_weight(compound).is_in_any_layer_of(layers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn are_connectable(&self, node1: PrimitiveIndex, node2: PrimitiveIndex) -> bool {
|
fn are_connectable(&self, node1: PrimitiveIndex, node2: PrimitiveIndex) -> bool {
|
||||||
if let (Some(node1_net_id), Some(node2_net_id)) = (
|
if let (Some(node1_net_id), Some(node2_net_id)) = (
|
||||||
node1.primitive(self).maybe_net(),
|
node1.primitive(self).maybe_net(),
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ pub trait GetLayer {
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait IsInLayer {
|
pub trait IsInLayer {
|
||||||
fn is_in_layer(&self, layer: usize) -> bool;
|
fn is_in_layer(&self, layer: usize) -> bool;
|
||||||
|
|
||||||
|
fn is_in_any_layer_of(&self, layers: &[bool]) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: GetLayer> IsInLayer for T {
|
impl<T: GetLayer> IsInLayer for T {
|
||||||
|
|
@ -38,6 +40,10 @@ impl<T: GetLayer> IsInLayer for T {
|
||||||
fn is_in_layer(&self, layer: usize) -> bool {
|
fn is_in_layer(&self, layer: usize) -> bool {
|
||||||
self.layer() == layer
|
self.layer() == layer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_in_any_layer_of(&self, layers: &[bool]) -> bool {
|
||||||
|
*layers.get(self.layer()).unwrap_or(&false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,14 @@ impl GetMaybeNet for ViaWeight {
|
||||||
|
|
||||||
impl IsInLayer for ViaWeight {
|
impl IsInLayer for ViaWeight {
|
||||||
fn is_in_layer(&self, layer: usize) -> bool {
|
fn is_in_layer(&self, layer: usize) -> bool {
|
||||||
self.from_layer >= layer && self.to_layer <= layer
|
(self.from_layer..=self.to_layer).contains(&layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_in_any_layer_of(&self, layers: &[bool]) -> bool {
|
||||||
|
layers
|
||||||
|
.get(self.from_layer..=core::cmp::min(self.to_layer, layers.len()))
|
||||||
|
.map(|i| i.iter().any(|j| *j))
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue