commit 078dba2472b6dee8a17f7cefcf76451b770503fc
parent 5ead523b74cbff96461c176a10046384631c80bc
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Wed, 16 Oct 2024 19:49:29 -0700
Add multi-selection
Multi-selection is going to be very important for moving multiple
points, etc. At the moment it's still fairly limited and only useful
for saving a bit of time when deleting a few different things or
changing pointmap types etc., but I'll rework various handlers to
support moving multiple objects as a group in the future (see
thoughts below).
In this selection mode I want to have press, drag, and release
create a box from the press to the release which selects everything
inside of it.
I'm thinking that while it sounds a little tedious it might be a
good idea to have changes to the SVG elements themselves push diffs
to the backend. I think this would end up making all this front-end
code—and I can't overstate this enough—a lot easier. For instance
moving or rotating multiple objects would just mean putting them
in a group, transforming them, then have svg.js take them out of a
group and apply the transform to each individually. Of course there
are some issues I see with this already but I still think it might
be a good idea.
Diffstat:
1 file changed, 63 insertions(+), 23 deletions(-)
diff --git a/files/floorplans/floorplan/main.js b/files/floorplans/floorplan/main.js
@@ -8,6 +8,7 @@ import * as geometry from "./geometry.js"
import * as backend from "./backend.js"
import * as api from "/lib/api.js"
+const defaultMode = "Precise"
const messageTimeout = 4000
const buttons = {
@@ -41,6 +42,16 @@ const modes = {
select: selectHandler,
reselect: selectHandler
}
+ },
+ Select: {
+ points: true,
+ handlers: {
+ contextmenu: preventDefaultHandler,
+ pointerdown: selectionHandler,
+ keydown: keyHandler,
+ select: selectHandler,
+ reselect: selectHandler
+ }
}
}
@@ -49,6 +60,7 @@ let State = {
pointOp: 'Create',
snapAngle: true,
snapPoints: true,
+ selectMode: false,
lastClick: null
}
@@ -134,7 +146,7 @@ function run(editor) {
for (let mode in modes) {
editor.addMode(mode, modes[mode])
}
- editor.useMode("Precise")
+ editor.useMode(defaultMode)
let toolbar = document.querySelector("header")
.appendChild(document.createElement("ul"))
@@ -195,6 +207,12 @@ function run(editor) {
on: function() { State.snapPoints = true },
value: State.snapPoints
})))
+ toolbar.append(item(checkToggle("Select mode", {
+ title: "Enter selection mode",
+ off: function() { editor.useMode(defaultMode); State.selectMode = false },
+ on: function() { editor.useMode("Select"); State.selectMode = true },
+ value: State.selectMode
+ })))
if (debug) {
toolbar.append(item(
@@ -282,7 +300,8 @@ function checkToggle(name, params) {
input.setAttribute("title", params.title)
}
- if (params.value) {
+ if (params.value != undefined) {
+ input.checked = params.value
run(params.value)
}
return c
@@ -304,6 +323,10 @@ function selectHandler(event, editor, state) {
selectHandler(event, editor, state)
}
+ c.append(ui.button("Unselect", "Deselect selection", null, {
+ handlers: { click: function() { editor.draw.select() } }
+ }))
+
c.append(ui.input("Delete", "Delete selected objects", {
attributes: { type: "button", value: "Delete" },
handlers: { click: function() {
@@ -519,6 +542,8 @@ function selector(things, select, options) {
// pointerdown
function selectionHandler(event, editor) {
+ let sel
+
if (event.pointerType === "mouse" && event.button === buttons.right) {
return
}
@@ -526,32 +551,47 @@ function selectionHandler(event, editor) {
let p = editor.draw.point(event.clientX, event.clientY)
State.lastClick = structuredClone(p)
let order = [ "#" + editor.layoutG(), "#points", "#pointmaps" ]
- for (let i = 0; i < order.length; ++i) {
- let x = editor.thingAt(p, order[i])
- if (x) {
- x.select()
- return
+ for (let i = 0; !sel && i < order.length; ++i) {
+ sel = editor.thingAt(p, order[i])
+ }
+
+ if (!sel) {
+ let close = editor.thingsAt(p, order.join(","), { method: "touching", minsize: 3500 })
+ let dist
+ let closest
+ for (let i = 0; i < close.length; ++i) {
+ let tmp = close[i].distanceTo(p.x, p.y)
+ if (dist == null || tmp < dist) {
+ dist = tmp
+ closest = close[i]
+ }
}
+ sel = closest
}
- let close = editor.thingsAt(p, order.join(","), { method: "touching", minsize: 3500 })
- let dist
- let closest
- for (let i = 0; i < close.length; ++i) {
- let tmp = close[i].distanceTo(p.x, p.y)
- if (dist == null || tmp < dist) {
- dist = tmp
- closest = close[i]
+ if (sel != null) {
+ if (!State.selectMode) {
+ sel.select()
+ } else {
+ let selection = editor.draw.find(".selected")
+ let i = selection.indexOf(sel)
+ if (i >= 0) {
+ selection.splice(i, 1)
+ } else {
+ selection.push(sel)
+ }
+ if (selection.length === 0) {
+ editor.draw.select()
+ } else {
+ selection.selectList()
+ }
}
+ } else {
+ if (!State.selectMode) {
+ editor.draw.select()
+ }
+ escape()
}
-
- if (closest != null) {
- closest.select()
- return
- }
-
- editor.draw.select()
- escape()
}
function keyHandler(ev, editor) {