mirror of https://codeberg.org/topola/topola.git
Make containment mode dependent on left-to-right, right-to-left mouse drag
This commit is contained in:
parent
15a7bc65f1
commit
5a3a870a13
|
|
@ -3,10 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use egui::Pos2;
|
||||
use topola::{
|
||||
DragSelectionInteractor, InteractiveInput, SelectionCombineMode, SelectionContainMode,
|
||||
SelectionOptions, Vector2, Vector3,
|
||||
};
|
||||
use topola::{InteractiveInput, SelectionCombineMode, SelectionInteractor, Vector2};
|
||||
|
||||
use crate::{display::Display, workspace::Workspace};
|
||||
|
||||
|
|
@ -14,7 +11,7 @@ pub struct Viewport {
|
|||
pub scene_rect: egui::Rect,
|
||||
pub ref_scene_rect: egui::Rect,
|
||||
pub scheduled_zoom_to_fit: bool,
|
||||
drag_selection_interactor: Option<DragSelectionInteractor>,
|
||||
selection_interactor: Option<SelectionInteractor>,
|
||||
}
|
||||
|
||||
impl Viewport {
|
||||
|
|
@ -23,7 +20,7 @@ impl Viewport {
|
|||
scene_rect: egui::Rect::from_min_max(egui::pos2(-1.0, -1.0), egui::pos2(1.0, 1.0)),
|
||||
ref_scene_rect: egui::Rect::from_min_max(egui::pos2(-1.0, -1.0), egui::pos2(1.0, 1.0)),
|
||||
scheduled_zoom_to_fit: false,
|
||||
drag_selection_interactor: None,
|
||||
selection_interactor: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +52,7 @@ impl Viewport {
|
|||
|
||||
if let Some(workspace) = workspace {
|
||||
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
|
||||
self.drag_selection_interactor = None;
|
||||
self.selection_interactor = None;
|
||||
}
|
||||
|
||||
let primary_pressed =
|
||||
|
|
@ -64,49 +61,46 @@ impl Viewport {
|
|||
ctx.input(|i| i.pointer.button_down(egui::PointerButton::Primary));
|
||||
let primary_released =
|
||||
ctx.input(|i| i.pointer.button_released(egui::PointerButton::Primary));
|
||||
let mut maybe_pointer_on_scene: Option<Vector2<i64>> = None;
|
||||
|
||||
if let Some(pointer_viewport_pos) = ctx.input(|i| i.pointer.interact_pos()) {
|
||||
let pointer_scene_pos = scene_to_viewport.inverse() * pointer_viewport_pos;
|
||||
let pointer_scene =
|
||||
Vector2::new(pointer_scene_pos.x as i64, pointer_scene_pos.y as i64);
|
||||
let pointer_on_scene_pos =
|
||||
scene_to_viewport.inverse() * pointer_viewport_pos;
|
||||
let pointer_on_scene = Vector2::new(
|
||||
pointer_on_scene_pos.x as i64,
|
||||
pointer_on_scene_pos.y as i64,
|
||||
);
|
||||
maybe_pointer_on_scene = Some(pointer_on_scene);
|
||||
|
||||
if primary_pressed && response.hovered() {
|
||||
self.drag_selection_interactor = Some(DragSelectionInteractor::new(
|
||||
pointer_scene,
|
||||
self.selection_interactor = Some(SelectionInteractor::new(
|
||||
pointer_on_scene,
|
||||
workspace.selection.clone(),
|
||||
SelectionOptions::new(
|
||||
SelectionCombineMode::Replace,
|
||||
SelectionContainMode::Crossing,
|
||||
),
|
||||
SelectionCombineMode::Replace,
|
||||
));
|
||||
}
|
||||
|
||||
if response.clicked() {
|
||||
if let Some(pin_selector) = workspace
|
||||
.autorouter
|
||||
.router()
|
||||
.navmesher_board()
|
||||
.board()
|
||||
.locate_pin_at_point(Vector3::new(
|
||||
pointer_scene.x,
|
||||
pointer_scene.y,
|
||||
workspace.appearance_panel.active.index() as i64,
|
||||
))
|
||||
{
|
||||
workspace.selection.pins.xor(std::iter::once(pin_selector));
|
||||
}
|
||||
} else if let Some(interactor) = self.drag_selection_interactor.as_mut() {
|
||||
if primary_down || primary_released {
|
||||
if let Some(interactor) = self.selection_interactor.as_mut() {
|
||||
if primary_down {
|
||||
let _ = interactor.update(
|
||||
workspace.autorouter.router().navmesher_board().board(),
|
||||
InteractiveInput::new(pointer_scene, false),
|
||||
workspace.appearance_panel.active,
|
||||
InteractiveInput::new(pointer_on_scene, false, false),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if primary_released {
|
||||
if let Some(interactor) = self.drag_selection_interactor.take() {
|
||||
if let Some(mut interactor) = self.selection_interactor.take() {
|
||||
let pointer_for_scene =
|
||||
maybe_pointer_on_scene.unwrap_or(*interactor.origin());
|
||||
let _ = interactor.update(
|
||||
workspace.autorouter.router().navmesher_board().board(),
|
||||
workspace.appearance_panel.active,
|
||||
InteractiveInput::new(pointer_for_scene, true, false),
|
||||
);
|
||||
|
||||
workspace.selection = interactor.selection().clone();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,31 +3,37 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_getters::Getters;
|
||||
use derive_more::Constructor;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
Rect3, Vector2, Vector3,
|
||||
board::{
|
||||
Board,
|
||||
interactors::{
|
||||
InteractiveInput, SelectionCombineMode, SelectionContainMode, SelectionOptions,
|
||||
},
|
||||
interactors::{InteractiveInput, SelectionCombineMode, SelectionContainMode},
|
||||
selections::PersistableSelection,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Constructor, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct DragSelectionOptions {
|
||||
combine: SelectionCombineMode,
|
||||
contain: SelectionContainMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Getters, Ord, PartialEq, PartialOrd)]
|
||||
pub struct DragSelectionInteractor {
|
||||
origin: Vector2<i64>,
|
||||
original_selection: PersistableSelection,
|
||||
selection: PersistableSelection,
|
||||
options: SelectionOptions,
|
||||
options: DragSelectionOptions,
|
||||
}
|
||||
|
||||
impl DragSelectionInteractor {
|
||||
pub fn new(
|
||||
origin: Vector2<i64>,
|
||||
original_selection: PersistableSelection,
|
||||
options: SelectionOptions,
|
||||
options: DragSelectionOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
origin,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
mod drag_selection;
|
||||
mod selection;
|
||||
|
||||
use derive_more::Constructor;
|
||||
pub use drag_selection::DragSelectionInteractor;
|
||||
pub use drag_selection::{DragSelectionInteractor, DragSelectionOptions};
|
||||
pub use selection::SelectionInteractor;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Vector2;
|
||||
|
|
@ -13,12 +14,17 @@ use crate::Vector2;
|
|||
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct InteractiveInput {
|
||||
pointer: Vector2<i64>,
|
||||
released: bool,
|
||||
cancel: bool,
|
||||
}
|
||||
|
||||
impl InteractiveInput {
|
||||
pub fn new(pointer: Vector2<i64>, cancel: bool) -> Self {
|
||||
Self { pointer, cancel }
|
||||
pub fn new(pointer: Vector2<i64>, released: bool, cancel: bool) -> Self {
|
||||
Self {
|
||||
pointer,
|
||||
released,
|
||||
cancel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -35,9 +41,3 @@ pub enum SelectionContainMode {
|
|||
Crossing,
|
||||
Window,
|
||||
}
|
||||
|
||||
#[derive(Clone, Constructor, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct SelectionOptions {
|
||||
combine: SelectionCombineMode,
|
||||
contain: SelectionContainMode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_getters::Getters;
|
||||
|
||||
use crate::{
|
||||
Vector2, Vector3,
|
||||
board::{
|
||||
Board,
|
||||
interactors::{
|
||||
DragSelectionInteractor, DragSelectionOptions, InteractiveInput, SelectionCombineMode,
|
||||
SelectionContainMode,
|
||||
},
|
||||
selections::PersistableSelection,
|
||||
},
|
||||
layout::LayerId,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Getters, Ord, PartialEq, PartialOrd)]
|
||||
pub struct SelectionInteractor {
|
||||
origin: Vector2<i64>,
|
||||
original_selection: PersistableSelection,
|
||||
selection: PersistableSelection,
|
||||
combine: SelectionCombineMode,
|
||||
}
|
||||
|
||||
impl SelectionInteractor {
|
||||
pub fn new(
|
||||
origin: Vector2<i64>,
|
||||
original_selection: PersistableSelection,
|
||||
combine: SelectionCombineMode,
|
||||
) -> Self {
|
||||
Self {
|
||||
origin,
|
||||
original_selection,
|
||||
selection: PersistableSelection::new(),
|
||||
combine,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
board: &Board,
|
||||
layer: LayerId,
|
||||
input: InteractiveInput,
|
||||
) -> Option<PersistableSelection> {
|
||||
if input.cancel {
|
||||
self.selection = self.original_selection.clone();
|
||||
return Some(self.selection.clone());
|
||||
}
|
||||
|
||||
if input.released && input.pointer == self.origin {
|
||||
let mut selection = self.original_selection.clone();
|
||||
|
||||
// Pins have intentional precedence over nets and components.
|
||||
if let Some(pin_selector) = board.locate_pin_at_point(Vector3::new(
|
||||
input.pointer.x,
|
||||
input.pointer.y,
|
||||
layer.index() as i64,
|
||||
)) {
|
||||
selection.pins.xor(std::iter::once(pin_selector));
|
||||
} else if let Some(net_selector) = board.locate_net_at_point(Vector3::new(
|
||||
input.pointer.x,
|
||||
input.pointer.y,
|
||||
layer.index() as i64,
|
||||
)) {
|
||||
selection.nets.xor(std::iter::once(net_selector));
|
||||
} else if let Some(component_selector) = board.locate_component_at_point(Vector3::new(
|
||||
input.pointer.x,
|
||||
input.pointer.y,
|
||||
layer.index() as i64,
|
||||
)) {
|
||||
selection
|
||||
.components
|
||||
.xor(std::iter::once(component_selector));
|
||||
}
|
||||
|
||||
self.selection = selection.clone();
|
||||
return Some(selection);
|
||||
}
|
||||
|
||||
let contain = if input.pointer.x >= self.origin.x {
|
||||
SelectionContainMode::Window
|
||||
} else {
|
||||
SelectionContainMode::Crossing
|
||||
};
|
||||
|
||||
let options = DragSelectionOptions::new(self.combine.clone(), contain);
|
||||
let mut drag_selection_interactor =
|
||||
DragSelectionInteractor::new(self.origin, self.original_selection.clone(), options);
|
||||
let selection = drag_selection_interactor.update(board, input)?;
|
||||
|
||||
self.selection = selection.clone();
|
||||
Some(selection)
|
||||
}
|
||||
}
|
||||
|
|
@ -79,21 +79,24 @@ impl Board {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn locate_nets_at_point(
|
||||
&self,
|
||||
point: Vector3<i64>,
|
||||
) -> impl Iterator<Item = NetSelector> + '_ {
|
||||
let mut selectors = BTreeSet::new();
|
||||
|
||||
for net_id in self.layout.locate_nets_at_point(point) {
|
||||
let Some(net_name) = self.net_name(net_id) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
selectors.insert(NetSelector::new(net_name.to_string()));
|
||||
pub fn locate_net_at_point(&self, point: Vector3<i64>) -> Option<NetSelector> {
|
||||
if let Some(joint_id) = self.layout.locate_joints_at_point(point).next() {
|
||||
return self.joint_net_selector(joint_id);
|
||||
}
|
||||
|
||||
selectors.into_iter()
|
||||
if let Some(segment_id) = self.layout.locate_segments_at_point(point).next() {
|
||||
return self.segment_net_selector(segment_id);
|
||||
}
|
||||
|
||||
if let Some(via_id) = self.layout.locate_vias_at_point(point).next() {
|
||||
return self.via_net_selector(via_id);
|
||||
}
|
||||
|
||||
if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() {
|
||||
return self.polygon_net_selector(polygon_id);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn locate_nets_intersecting_rect(
|
||||
|
|
@ -131,6 +134,11 @@ impl Board {
|
|||
}
|
||||
|
||||
pub fn locate_pin_at_point(&self, point: Vector3<i64>) -> Option<PinSelector> {
|
||||
// Polygons have intentional precedence for pins.
|
||||
if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() {
|
||||
return self.polygon_pin_selector(polygon_id);
|
||||
}
|
||||
|
||||
if let Some(joint_id) = self.layout.locate_joints_at_point(point).next() {
|
||||
return self.joint_pin_selector(joint_id);
|
||||
}
|
||||
|
|
@ -143,10 +151,6 @@ impl Board {
|
|||
return self.via_pin_selector(via_id);
|
||||
}
|
||||
|
||||
if let Some(polygon_id) = self.layout.locate_polygons_at_point(point).next() {
|
||||
return self.polygon_pin_selector(polygon_id);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,38 @@ impl Board {
|
|||
.contains(&NetSelector::new(net_name.to_string()))
|
||||
}
|
||||
|
||||
pub fn joint_net_selector(&self, id: JointId) -> Option<NetSelector> {
|
||||
let joint = self.layout.joint(id);
|
||||
|
||||
Some(NetSelector {
|
||||
net: self.net_name(joint.spec.net)?.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn segment_net_selector(&self, id: SegmentId) -> Option<NetSelector> {
|
||||
let segment = self.layout.segment(id);
|
||||
|
||||
Some(NetSelector {
|
||||
net: self.net_name(segment.net)?.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn via_net_selector(&self, id: ViaId) -> Option<NetSelector> {
|
||||
let via = self.layout.via(id);
|
||||
|
||||
Some(NetSelector {
|
||||
net: self.net_name(via.net)?.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn polygon_net_selector(&self, id: PolygonId) -> Option<NetSelector> {
|
||||
let polygon = self.layout.polygon(id);
|
||||
|
||||
Some(NetSelector {
|
||||
net: self.net_name(polygon.net)?.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pins_contain_joint(&self, selection: &PinSelection, id: JointId) -> bool {
|
||||
let Some(selector) = self.joint_pin_selector(id) else {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -121,28 +121,6 @@ impl Layout {
|
|||
.map(|geom_with_data| geom_with_data.data)
|
||||
}
|
||||
|
||||
pub fn locate_nets_at_point(&self, point: Vector3<i64>) -> impl Iterator<Item = NetId> {
|
||||
let mut nets = BTreeSet::new();
|
||||
|
||||
for joint_id in self.locate_joints_at_point(point) {
|
||||
nets.insert(self.joint(joint_id).spec.net);
|
||||
}
|
||||
|
||||
for segment_id in self.locate_segments_at_point(point) {
|
||||
nets.insert(self.segment(segment_id).net);
|
||||
}
|
||||
|
||||
for via_id in self.locate_vias_at_point(point) {
|
||||
nets.insert(self.via(via_id).net);
|
||||
}
|
||||
|
||||
for polygon_id in self.locate_polygons_at_point(point) {
|
||||
nets.insert(self.polygon(polygon_id).net);
|
||||
}
|
||||
|
||||
nets.into_iter()
|
||||
}
|
||||
|
||||
pub fn locate_nets_intersecting_rect(&self, rect: Rect3<i64>) -> impl Iterator<Item = NetId> {
|
||||
let mut nets = BTreeSet::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ pub use crate::board::LayerDesc;
|
|||
pub use crate::board::LayerSide;
|
||||
pub use crate::board::LayerType;
|
||||
pub use crate::board::interactors::{
|
||||
DragSelectionInteractor, InteractiveInput, SelectionCombineMode, SelectionContainMode,
|
||||
SelectionOptions,
|
||||
DragSelectionInteractor, DragSelectionOptions, InteractiveInput, SelectionCombineMode,
|
||||
SelectionContainMode, SelectionInteractor,
|
||||
};
|
||||
pub use crate::board::selections;
|
||||
pub use crate::layout::LayerId;
|
||||
|
|
|
|||
Loading…
Reference in New Issue