Squash all skewer_selection commits to date

This commit is contained in:
Tom Donaldson
2026-05-08 11:47:56 -04:00
committed by Matthieu Baumann
parent 44fdfad61c
commit 6ce7e9365d
9 changed files with 530 additions and 74 deletions
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
<path d="M.7,12.3l11.3-4.9,11.3,4.9-11.1,7.1L.7,12.3ZM12,16.6l7.4-4.1-7.1-3.4-7.4,3.4s7.1,4.1,7.1,4.1Z"/>
<polygon points="4.9 1.8 5 8 6.3 9.1 7.6 8 7.6 1.8 4.9 1.8"/>
<polygon points="24 24 20.4 24 24 20.4 24 24"/>
</svg>

After

Width:  |  Height:  |  Size: 453 B

+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
<path d="M.7,12.3l11.3-4.9,11.3,4.9-11.1,7.1L.7,12.3ZM12,16.6l7.4-4.1-7.1-3.4-7.4,3.4,7.1,4.1Z"/>
<polygon points="4.9 1.8 5 8 6.3 9.1 7.6 8 7.6 1.8 4.9 1.8"/>
</svg>

After

Width:  |  Height:  |  Size: 395 B

+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
<polygon points="11.6 7.9 14 7.9 14 4.4 14 4.4 14 2.9 14 2.9 14 .8 11.6 .8 11.6 2.4 11.6 2.4 11.6 4 11.6 4 11.6 7.9"/>
<path d="M22.5,7.5l-7.5-3.2v1.7l3.5,1.7-6,3.6-6.5-3.5,4.7-2.2v-1.7L2.2,7.5l4.2,2.5-5,2.6,11.1,6.5,10.8-6.6-5.1-2.4,4.2-2.6s.1,0,.1,0ZM19.8,12.4l-7.2,4-7.7-4.1,2.9-1.5,4.7,2.8,4.3-2.6,3,1.4Z"/>
<polygon points="11.3 19.6 11.3 22.1 12.6 23.3 14 22.1 14 19.5 12.5 20.3 11.3 19.6"/>
<polygon points="24 24 20.4 24 24 20.4 24 24"/>
</svg>

After

Width:  |  Height:  |  Size: 685 B

+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
<!-- Generator: Adobe Illustrator 30.2.1, SVG Export Plug-In . SVG Version: 2.1.1 Build 1) -->
<polygon points="11.6 7.9 14 7.9 14 4.4 14 4.4 14 2.9 14 2.9 14 .8 11.6 .8 11.6 2.4 11.6 2.4 11.6 4 11.6 4 11.6 7.9"/>
<path d="M22.5,7.5l-7.5-3.2v1.7c0,0,3.5,1.7,3.5,1.7l-6,3.6-6.5-3.5,4.7-2.2v-1.7S2.2,7.5,2.2,7.5l4.2,2.5-5,2.6,11.1,6.5,10.8-6.6-5.1-2.4,4.2-2.6h.1ZM19.8,12.4l-7.2,4-7.7-4.1,2.9-1.5,4.7,2.8,4.3-2.6s3,1.4,3,1.4Z"/>
<polygon points="11.3 19.6 11.3 22.1 12.6 23.3 14 22.1 14 19.5 12.5 20.3 11.3 19.6"/>
</svg>

After

Width:  |  Height:  |  Size: 655 B

+13 -3
View File
@@ -74,6 +74,7 @@ import { SimbadPointer } from "./gui/Button/SimbadPointer";
import { ColorPicker } from "./gui/Button/ColorPicker";
import { GridEnabler } from "./gui/Button/GridEnabler";
import { CooFrame } from "./gui/Input/CooFrame";
import { SelectionMode } from "./gui/Button/SelectionMode";
import { Circle } from "./shapes/Circle";
import { Ellipse } from "./shapes/Ellipse";
import { Polyline } from "./shapes/Polyline";
@@ -110,6 +111,8 @@ import { Polyline } from "./shapes/Polyline";
* CSS class for that button is `aladin-grid-control`
* @property {boolean} [showSettingsControl=false] - Whether to show the settings control toolbar.
* CSS class for that button is `aladin-settings-control`
* @property {boolean} [showSelectionModeControl=false] - Whether to show the selection mode menu opener button.
* CSS class for that button is `aladin-selectionMode-control`
* @property {boolean} [showColorPickerControl=false] - Whether to show the color picker tool.
* CSS class for that button is `aladin-colorPicker-control`
* @property {boolean} [showShareControl=false] - Whether to show the share control toolbar.
@@ -155,6 +158,7 @@ import { Polyline } from "./shapes/Polyline";
* @property {boolean} [pixelateCanvas=true] - Whether to pixelate the canvas.
* @property {boolean} [manualSelection=false] - When set to true, no selection will be performed, only events will be generated.
* @property {string} [mode] - Interface theme, can be either 'dark' or 'light'. If not set, the mode will be retrieved from your browser preference or your localStorage.
* @property {string} [selectionMode="edge"] - When set to 'skewer' footprints are selected by clicking inside them. For 'edge' footprints are selected by clicking on their edges.
* @property {Object} [selector] - More options for the the selector.
* @property {string} [selector.color] - Color of the selector, defaults to the color of the reticle. Can be a hex color or a function returning a hex color.
* @property {number} [selector.lineWidth=2] - Width of the selector line.
@@ -674,15 +678,19 @@ export let Aladin = (function () {
widgets["simbad"] = simbad
}
// Add the projection control
// Add the coo grid control
if (options.showCooGridControl) {
let grid = new GridEnabler(this);
widgets["grid"] = grid;
}
// Add the projection control
// Add the coo grid control
// Show selection mode control
if (options.showSelectionModeControl) {
let selectionMode = new SelectionMode(this);
widgets["selectionMode"] = selectionMode
}
// Add the color picker control
if (options.showColorPickerControl) {
let picker = new ColorPicker(this);
widgets["picker"] = picker;
@@ -698,6 +706,7 @@ export let Aladin = (function () {
this.toolbar.add(name, widget);
}
// Add the projection control
if (options.showProjectionControl) {
this.projBtn = new ProjectionActionButton(this);
this.addUI(this.projBtn);
@@ -784,6 +793,7 @@ export let Aladin = (function () {
showSimbadPointerControl: false,
showCooGridControl: false,
showSettingsControl: false,
showSelectionModeControl: false,
showColorPickerControl: false,
// Share toolbar
showShareControl: false,
+62 -5
View File
@@ -27,15 +27,16 @@ import { PolySelect } from "./FiniteStateMachine/PolySelect";
import { LineSelect } from "./FiniteStateMachine/LineSelect";
import { RectSelect } from "./FiniteStateMachine/RectSelect";
import { ALEvent } from "./events/ALEvent";
import { Utils } from './Utils';
/******************************************************************************
* Aladin Lite project
*
*
* Class Selector
*
*
* A selector
*
*
* Author: Matthieu Baumann[CDS]
*
*
*****************************************************************************/
export class Selector {
@@ -121,7 +122,7 @@ export class Selector {
continue;
}
sources = cat.getSources();
for (var l = 0; l < sources.length; l++) {
s = sources[l];
@@ -172,4 +173,60 @@ export class Selector {
return objList;
}
/**
* Retrieves objects skewered by the cursor position or specified coordinates. An object is
* skewered if it is a shape that contains the specified coordinate, or is a catalog object within 3 pixels
* of the specified coordinate.
*
* If e is a mouse event (as opposed to an object with x and y values), the mouse coordinates
* of the event are used.
*
* This is implemented by simulating the interactive selection of a circle region with a 3 pixel radius)
* around the given coordinates and returns all catalog sources and overlay items intersecting with it.
*
* @param {Event|Object} e - Mouse coordinate via mouse event or object with x and y properties
* @param {Object} view - The Aladin View instance containing catalogs and overlays
* @returns {Array<Array>} Array of object lists, where each subarray contains objects
* from a single catalog or overlay that intersect with the selection region.
* Returns empty array if no objects are found.
*/
static getSkewerObjects(e, view) {
// Get the xy from the event
let xymouse;
if (e instanceof Event) {
xymouse = Utils.relMouseCoords(e);
} else {
xymouse = e;
}
const x = xymouse.x;
const y = xymouse.y;
// Perform a selection using a circle around x, y as if drawn by dragging 3 pixels.
const r2 = 9;
const r = Math.sqrt(r2);
let selectorObject = {
x, y, r,
label: 'circle',
contains(s) {
let dx = (s.x - x)
let dy = (s.y - y);
return dx*dx + dy*dy <= r2;
},
bbox() {
return {
x: x - r,
y: y - r,
w: 2*r,
h: 2*r
}
}
};
let objList = Selector.getObjects(selectorObject, view);
return objList;
}
}
+277 -66
View File
@@ -273,6 +273,13 @@ export let View = (function () {
this.selector = new Selector(this, this.options.selector);
this.manualSelection = (this.options && this.options.manualSelection) || false;
// Selection mode
this.selectionMode = View.SELECTION_MODE_EDGE;
if (this.options.selectionMode === 'skewer') {
this.selectionMode = View.SELECTION_MODE_SKEWER;
}
// current reference image survey displayed
this.imageLayers = new Map();
@@ -359,6 +366,10 @@ export let View = (function () {
View.TOOL_SIMBAD_POINTER = 2;
View.TOOL_COLOR_PICKER = 3;
// Selection modes
View.SELECTION_MODE_EDGE = 0;
View.SELECTION_MODE_SKEWER = 1;
// TODO: should be put as an option at layer level
View.DRAW_SOURCES_WHILE_DRAGGING = true;
View.DRAW_MOCS_WHILE_DRAGGING = true;
@@ -514,6 +525,13 @@ export let View = (function () {
}
View.prototype.setMode = function (mode, params) {
// Undo the specialized cursors, if any.
const prevMode = this.mode;
if (prevMode == View.TOOL_SIMBAD_POINTER) {
this.catalogCanvas.classList.remove('aladin-sp-cursor');
}
// hide the picker tooltip
this.colorPickerTool.domElement.style.display = "none";
// in case we are in the selection mode
@@ -546,6 +564,14 @@ export let View = (function () {
ALEvent.MODE.dispatchedTo(this.aladin.aladinDiv, {mode});
};
View.prototype.setSelectionMode = function (selectionMode) {
this.selectionMode = selectionMode;
};
View.prototype.getSelectionMode = function () {
return this.selectionMode;
};
View.prototype.setCursor = function (cursor) {
if (this.catalogCanvas.style.cursor == cursor) {
return;
@@ -713,12 +739,10 @@ export let View = (function () {
var showContextMenu = true;
var xystart;
var handleSelect = function(xy, tolerance) {
var handleSelect = function(xy, tolerance, withModifierKey=false) {
tolerance = tolerance || 5;
var objs = view.closestObjects(xy.x, xy.y, tolerance);
view.unselectObjects();
if (objs) {
var objClickedFunction = view.aladin.callbacksByEventName['objectClicked'];
var footprintClickedFunction = view.aladin.callbacksByEventName['footprintClicked'];
@@ -757,11 +781,14 @@ export let View = (function () {
if (shapes.length > 0) {
objs.push(shapes)
}
view.selectObjects(objs);
view.selectObjects(objs, withModifierKey);
view.lastClickedObject = objs;
} else {
view.unselectObjects();
// If there is a past clicked object
if (view.lastClickedObject) {
// TODO: do we need to keep that triggering ?
@@ -772,6 +799,95 @@ export let View = (function () {
}
}
}
/**
* Perform a skewer-based selection of objects at the specified mouse position.
*
* @param {Event|Object} e - Mouse coordinate via mouse event or object with x and y properties
* @param {boolean} withModifierKey - If true, toggles selection (adds/removes from existing selection);
* if false, replaces current selection
*/
var handleSkewerSelect = function(e, withModifierKey) {
const objList = Selector.getSkewerObjects(e, view);
view.selectObjects(objList, withModifierKey);
}
/**
* Make the selected objects appear hovered and make all other objects appear unhovered,
* firing the appropriate callbacks for both cases.
*
* The also sets the cursor to a pointer to indicate that some object(s) would be
* select on click in the current mouse position.
*
* @param {Array} objects - Array of objects to apply hover state to
* @param {Object} xymouse - Mouse coordinates with x and y properties
*/
var hoverObjects = function(objects, xymouse) {
var objHoveredFunction = view.aladin.callbacksByEventName['objectHovered'];
var footprintHoveredFunction = view.aladin.callbacksByEventName['footprintHovered'];
view.setCursor('pointer');
for (let o of objects) {
if (typeof objHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
var ret = objHoveredFunction(o, xymouse);
}
if (o.isFootprint()) {
if (typeof footprintHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
var ret = footprintHoveredFunction(o, xymouse);
}
}
if (!view.lastHoveredObject || !view.lastHoveredObject.includes(o)) {
o.hover();
}
}
// unhover the objects in lastHoveredObjects that are not in closest anymore
if (view.lastHoveredObject) {
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
for (let lho of view.lastHoveredObject) {
if (!objects.includes(lho)) {
lho.unhover();
if (typeof objHoveredStopFunction === 'function') {
objHoveredStopFunction(lho, xymouse);
}
}
}
}
view.lastHoveredObject = objects;
}
/**
* Removes hover state from all previously hovered objects and fires the
* apropriate callbacks.
*
* The also resets the cursor to the default to indicate that no objects would be
* selected on click in the current mouse position.
*
* @param {Object} xymouse - Mouse coordinates with x and y properties
*/
var unhoverObjects = function(xymouse) {
view.setCursor('default');
if (view.lastHoveredObject) {
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
for (let lho of view.lastHoveredObject) {
lho.unhover();
if (typeof objHoveredStopFunction === 'function') {
objHoveredStopFunction(lho, xymouse);
}
}
}
view.lastHoveredObject = null;
}
var touchStartTime;
Utils.on(view.catalogCanvas, "mousedown touchstart", function (e) {
e.stopPropagation();
@@ -918,6 +1034,7 @@ export let View = (function () {
// reacting on 'click' rather on 'mouseup' is more reliable when panning the view
Utils.on(view.catalogCanvas, "mouseup mouseout touchend touchcancel", function (e) {
const xymouse = Utils.relMouseCoords(e);
const withModifierKey = e.ctrlKey || e.metaKey;
ALEvent.CANVAS_EVENT.dispatchedTo(view.aladinDiv, {
state: {
@@ -1006,11 +1123,19 @@ export let View = (function () {
const elapsedTime = Date.now() - touchStartTime;
if (elapsedTime < 100) {
view.updateObjectsLookup();
handleSelect(xymouse, 15);
if (view.selectionMode === View.SELECTION_MODE_SKEWER) {
handleSkewerSelect(e, withModifierKey)
} else {
handleSelect(xymouse, 15, withModifierKey);
}
}
}
} else {
handleSelect(xymouse);
if (view.selectionMode === View.SELECTION_MODE_EDGE) {
handleSelect(xymouse, 5, withModifierKey);
} else {
handleSkewerSelect(e, withModifierKey);
}
}
}
@@ -1193,71 +1318,31 @@ export let View = (function () {
lastMouseMovePos = pos;
}
// closestObjects is very costly, we would like to not do it
// especially if the objectHovered function is not defined.
var closests = view.closestObjects(xymouse.x, xymouse.y, 5);
if (view.selectionMode === View.SELECTION_MODE_EDGE) {
// We're in edge selection mode for footprints. closestObjects() will find those footprints by closeness to a footprint edge.
// closestObjects is very costly, we would like to not do it
// especially if the objectHovered function is not defined.
var closests = view.closestObjects(xymouse.x, xymouse.y, 5);
if (closests) {
var objHoveredFunction = view.aladin.callbacksByEventName['objectHovered'];
var footprintHoveredFunction = view.aladin.callbacksByEventName['footprintHovered'];
if (closests) {
hoverObjects(closests, xymouse);
} else {
unhoverObjects(view, xymouse);
}
} else if (view.selectionMode === View.SELECTION_MODE_SKEWER) {
// We're in skewer mode. Let's see what would be selected.
const skewerTargetsByLayer = Selector.getSkewerObjects(e, view);
const skewerObjects = skewerTargetsByLayer.flat();
view.setCursor('pointer');
for (let o of closests) {
if (typeof objHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
var ret = objHoveredFunction(o, xymouse);
}
if (o.isFootprint()) {
if (typeof footprintHoveredFunction === 'function' && (!view.lastHoveredObject || !view.lastHoveredObject.includes(o))) {
var ret = footprintHoveredFunction(o, xymouse);
}
}
if (!view.lastHoveredObject || !view.lastHoveredObject.includes(o)) {
o.hover();
}
if (skewerObjects.length > 0) {
hoverObjects(skewerObjects, xymouse);
} else {
unhoverObjects(view, xymouse);
}
// unhover the objects in lastHoveredObjects that are not in closest anymore
if (view.lastHoveredObject) {
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
for (let lho of view.lastHoveredObject) {
if (!closests.includes(lho)) {
lho.unhover();
if (typeof objHoveredStopFunction === 'function') {
objHoveredStopFunction(lho, xymouse);
}
}
}
}
view.lastHoveredObject = closests;
} else {
view.setCursor('default');
if (view.lastHoveredObject) {
var objHoveredStopFunction = view.aladin.callbacksByEventName['objectHoveredStop'];
/*if (typeof objHoveredStopFunction === 'function') {
// call callback function to notify we left the hovered object
var ret = objHoveredStopFunction(view.lastHoveredObject, xymouse);
}
view.lastHoveredObject.unhover();*/
for (let lho of view.lastHoveredObject) {
lho.unhover();
if (typeof objHoveredStopFunction === 'function') {
objHoveredStopFunction(lho, xymouse);
}
}
}
view.lastHoveredObject = null;
}
if (e.type === "mousemove") {
return;
}
@@ -1391,6 +1476,7 @@ export let View = (function () {
view.displayHpxGrid = false;
view.displayCatalog = false;
view.skewerEnabled = false;
};
View.prototype.requestRedrawAtDate = function (date) {
@@ -1636,6 +1722,9 @@ export let View = (function () {
return imageData;
};
/**
* Unselects all currently selected objects.
*/
View.prototype.unselectObjects = function() {
if (this.manualSelection) {
return;
@@ -1654,11 +1743,24 @@ export let View = (function () {
this.requestRedraw();
}
View.prototype.selectObjects = function(selection) {
/**
* Selects the specified objects in the view.
*
* If withModifierKey is true, it modifies the existing selection (adds/removes).
* Otherwise, it replaces the current selection.
*
* @param {Array|Object} selection - The objects to select, either an array or a selector object.
* @param {boolean} [withModifierKey=false] - Whether to modify (versus replace) the existing selections.
*/
View.prototype.selectObjects = function(selection, withModifierKey=false) {
if (this.manualSelection) {
return;
}
if (Array.isArray(selection) && withModifierKey) {
selection = this.computeModifiedSelection(selection)
}
// unselect the previous selection
this.unselectObjects();
@@ -1736,6 +1838,113 @@ export let View = (function () {
}
}
View.prototype._getLayerForObj = function(obj) {
let layer = null;
if (obj.getCatalog) {
layer = obj.getCatalog()
} else {
layer = obj.overlay
}
return layer
}
View.prototype._copySelectionsToStage = function(selections, stage, overlays, exclude) {
for (const group of selections) {
for (const obj of group) {
const objExcluded = exclude.includes(obj)
if (!objExcluded) {
const layer = this._getLayerForObj(obj)
const idx = overlays.findIndex(item => item.uuid === layer.uuid);
if (idx >= 0) {
stage[idx].push(obj)
} else {
console.warn("Layer not found for selected obj: " + obj)
}
}
}
}
}
/**
* Computes the full set of selections that should result if the specified (pending) objects were
* selected with a modifier key pressed.
*
* If there are existing selections, it adds pending items that aren't selected,
* or removes all pending items if they are all already selected.
*
* Organizes selections by overlay layers as expected by selectObjects.
*
* @param {Array<Array>} pending - Array of array of objects to potentially add/remove from selection.
* @returns {Array<Array>} The modified selection array.
*/
View.prototype.computeModifiedSelection = function(pending) {
const current = this.selection
let modSelection = pending
if (current && current.length > 0) {
// There are some items already selected.
// We will be adding all the pending selections that are not already selected,
// UNLESS all of the pending selections are already selected, in which case
// they will all be unselected.
const toAdd = []
let mightRemove = []
modSelection = [] // We will build a new selection list from the current and pending selections
// stage will have one row for each existing overlay in which to collect all desired selections.
const overlays = this.aladin.getOverlays()
const stage = new Array(overlays.length).fill(null).map(() => []);
// Put already-selected items in mightRemove and not-yet-selected items in toAdd.
for (const group of pending) {
for (const obj of group) {
if (obj.isSelected) {
mightRemove.push(obj)
} else {
toAdd.push(obj)
}
}
}
// If there is anything in toAdd, then clear mightRemove since they will be left selected.
if (toAdd.length > 0) {
mightRemove = []
}
// Copy current selections to stage except for anything in mightRemove
this._copySelectionsToStage(current, stage, overlays, mightRemove)
// Copy toAdd selections to stage
this._copySelectionsToStage([toAdd], stage, overlays, [])
// Build new modified selections list from stage.
// I can preserve the layer order, but I don't know how to preserve the order within
// layers without looping through all objects. Hopefully that order doesn't matter.
for (let i=0; i<stage.length; i++) {
if (stage[i].length > 0) {
// We have selected objects in this layer so will add the layer (or list of overlays) to modSelection
if (overlays[i].type === 'catalog') {
// The layer is a catalog so we add one entry for all its selections
const catLayer = []
modSelection.push(catLayer)
for (const obj of stage[i]) {
catLayer.push(obj)
}
} else {
// Assume it's a graphicalOverlay and add separate entries for each selected obj
// (That is the way objects in graphicalOverlays are currently selected. If they start
// being selected all in one list per overlay, then that can change here.)
for (const obj of stage[i]) {
modSelection.push([obj])
}
}
}
}
}
return modSelection;
}
View.prototype.getVisibleCells = function (norder) {
return this.wasm.getVisibleCells(norder);
};
@@ -2429,3 +2638,5 @@ export let View = (function () {
return View;
})();
+149
View File
@@ -0,0 +1,149 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU Lesser General Public License version 3
// or (at your option) any later version.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Aladin Lite. If not, see <https://www.gnu.org/licenses/>.
//
import { CtxMenuActionButtonOpener } from "./CtxMenuOpener";
import skewerSelectionIconArrow from '../../../../assets/icons/skewer_selection-arrow.svg';
import skewerSelectionIcon from '../../../../assets/icons/skewer_selection_black.svg';
import edgeSelectionIconArrow from '../../../../assets/icons/edge_selection-arrow.svg';
import edgeSelectionIcon from '../../../../assets/icons/edge_selection.svg';
import { View } from "../../View.js";
/******************************************************************************
* Aladin Lite project
*
* File gui/Button/SelectionMode.js
*
* Class representing a button for bringing up a menu for choosing selection mode.
* The appearance of the button changes depending on which selection mode (View.getSelectionMode())
* is active.
*
* There are two possible selection modes, Edge and Skewer, which affect how footprints are
* interactively selected.
*
* In Edge mode (View.SELECTION_MODE_EDGE), footprints are selected by clicking on their edges.
*
* In Skewer mode (View.SELECTION_MODE_SKEWER), footprints are selecting by clicking anywhere
* inside the footprint.
*
* Using a modifier key (Cmd on Mac, Ctrl otherwise) during select toggles the potential selections:
* - If any of the potential selections are not already selected, those objects are added to the current selections.
* - If all of the potential selections are already selected, then they are deselected.
*
* This uses the CSS class aladin-selectionMode-control.
*
* Author: Tom Donaldson (STScI)
*
*****************************************************************************/
export class SelectionMode extends CtxMenuActionButtonOpener {
/**
* Class representing a button for bringing up a menu for choosing selection mode.
* @param {Aladin} aladin - The aladin instance.
*/
constructor(aladin, options) {
// If we're on Mac, the modifier key will be Cmd instead of Ctrl.
let modifierKey = 'Ctrl';
const userAgent = window.navigator.userAgent.toLowerCase();
if (userAgent.indexOf('mac') > -1) {
modifierKey = 'Cmd';
}
// Set the initial button icon based on the current View selection mode.
const initialMode = aladin.view.getSelectionMode();
let initialIcon = edgeSelectionIconArrow;
if (initialMode === View.SELECTION_MODE_SKEWER) {
initialIcon = skewerSelectionIconArrow;
}
super({
icon: {
size: 'medium',
monochrome: true,
url: initialIcon,
},
classList: ['aladin-selectionMode-control'],
tooltip: {
content: 'Choose the selection mode<br />(' + modifierKey + ' for multiselect)',
position: { direction: 'top right', top: '10%', left: '80%' },
},
ctxMenu: undefined,
...options
}, aladin);
this.aladin = aladin;
this.modifierKey = modifierKey;
let ctxMenu = this._buildLayout()
this.update({ctxMenu})
}
setCustomIcon(icon) {
this.update({icon: {
size: 'medium',
monochrome: true,
url: icon
}})
}
_buildLayout() {
let self = this;
let aladin = this.aladin;
return [
{
label: {
icon: {
url: skewerSelectionIcon,
monochrome: true,
},
tooltip: {
content: 'Click inside shapes to select.<br />Multiselect with ' + self.modifierKey + '.',
position: { direction: 'top right', left: '50%' },
},
content: "Skewer Selection",
},
action: (e) => {
aladin.view.setSelectionMode(View.SELECTION_MODE_SKEWER);
self.setCustomIcon(skewerSelectionIconArrow);
},
},
{
label: {
icon: {
url: edgeSelectionIcon,
monochrome: true,
},
tooltip: {
content: 'Click on objects to select.<br />Multiselect with ' + self.modifierKey + '.',
position: { direction: 'top right', left: '60%' },
},
content: "Edge Selection",
},
action: (e) => {
aladin.view.setSelectionMode(View.SELECTION_MODE_EDGE);
self.setCustomIcon(edgeSelectionIconArrow);
},
},
]
}
}
+1
View File
@@ -11,6 +11,7 @@ There are distincts CSS class names for users wanting to personnalize the defaul
* `aladin-simbadPointer-control` targets the Simbad pointer control button
* `aladin-grid-control` targets the coordinate grid trigger button
* `aladin-settings-control` targets the settings menu opener button
* `aladin-selectionMode-control` targets the selection mode menu opener button
* `aladin-share-control` targets the share menu opener button
* `aladin-projection-control` targets the projection selector button
* `aladin-stack-box` targets the stack box