Stub out a selection tool for the editor

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-30 17:28:41 -07:00
parent d700561c0f
commit fb2f79823c
5 changed files with 145 additions and 5 deletions

Binary file not shown.

BIN
icons/tool-select-box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

After

Width:  |  Height:  |  Size: 259 B

View File

@ -482,6 +482,36 @@ class PencilOperation extends DrawOperation {
}
}
class SelectOperation extends MouseOperation {
start() {
this.pending_selection = this.editor.selection.create_pending();
this.update_pending_selection();
this.has_moved = false;
}
step(mx, my, gxf, gyf, gx, gy) {
this.has_moved = true;
this.update_pending_selection();
}
update_pending_selection() {
this.pending_selection.set_extrema(this.gx0, this.gy0, this.gx1, this.gy1);
}
commit() {
if (! this.has_moved) {
// Plain click clears selection
this.pending_selection.discard();
this.editor.selection.clear();
}
else {
this.pending_selection.commit();
}
}
abort() {
this.pending_selection.discard();
}
}
class ForceFloorOperation extends DrawOperation {
start() {
// Begin by placing an all-way force floor under the mouse
@ -1094,6 +1124,12 @@ const EDITOR_TOOLS = {
desc: "Flood-fill an area with tiles",
uses_palette: true,
},
select_box: {
icon: 'icons/tool-select-box.png',
name: "Box select",
desc: "Select and manipulate rectangles.",
op1: SelectOperation,
},
'force-floors': {
icon: 'icons/tool-force-floors.png',
name: "Force floors",
@ -1141,7 +1177,7 @@ const EDITOR_TOOLS = {
// slade when you have some selected?
// TODO ah, railroads...
};
const EDITOR_TOOL_ORDER = ['pencil', 'adjust', 'force-floors', 'tracks', 'wire', 'camera'];
const EDITOR_TOOL_ORDER = ['pencil', 'select_box', 'adjust', 'force-floors', 'tracks', 'wire', 'camera'];
// TODO this MUST use a LL tileset!
const EDITOR_PALETTE = [{
@ -2063,6 +2099,81 @@ for (let cycle of [
}
}
// TODO probably need to combine this with Selection somehow since it IS one, just not committed yet
class PendingSelection {
constructor(owner) {
this.owner = owner;
this.element = mk_svg('rect.overlay-pending-selection');
this.owner.svg_group.append(this.element);
this.rect = null;
}
set_extrema(x0, y0, x1, y1) {
this.rect = new DOMRect(Math.min(x0, x1), Math.min(y0, y1), Math.abs(x0 - x1) + 1, Math.abs(y0 - y1) + 1);
this.element.classList.add('--visible');
this.element.setAttribute('x', this.rect.x);
this.element.setAttribute('y', this.rect.y);
this.element.setAttribute('width', this.rect.width);
this.element.setAttribute('height', this.rect.height);
}
commit() {
this.owner.set_from_rect(this.rect);
this.element.remove();
}
discard() {
this.element.remove();
}
}
class Selection {
constructor(editor) {
this.editor = editor;
this.svg_group = mk_svg('g');
this.editor.svg_overlay.append(this.svg_group);
this.rect = null;
this.element = mk_svg('rect.overlay-selection.overlay-transient');
this.svg_group.append(this.element);
this.floating_contents = null;
}
get is_empty() {
return this.rect === null;
}
contains(x, y) {
// Empty selection means everything is selected?
if (this.rect === null)
return true;
return this.rect.left <= x && x < this.rect.right && this.rect.top <= y && y < this.rect.bottom;
}
create_pending() {
return new PendingSelection(this);
}
set_from_rect(rect) {
this.rect = rect;
this.element.classList.add('--visible');
this.element.setAttribute('x', this.rect.x);
this.element.setAttribute('y', this.rect.y);
this.element.setAttribute('width', this.rect.width);
this.element.setAttribute('height', this.rect.height);
}
clear() {
this.rect = null;
this.element.classList.remove('--visible');
}
// TODO allow floating/dragging, ctrl-dragging to copy, anchoring...
// TODO make more stuff respect this (more things should go through Editor for undo reasons anyway)
}
export class Editor extends PrimaryView {
constructor(conductor) {
@ -2098,7 +2209,7 @@ export class Editor extends PrimaryView {
setup() {
// Add more bits to SVG overlay
this.svg_cursor = mk_svg('rect.overlay-cursor', {x: 0, y: 0, width: 1, height: 1});
this.svg_cursor = mk_svg('rect.overlay-transient.overlay-cursor', {x: 0, y: 0, width: 1, height: 1});
this.svg_overlay.append(this.svg_cursor);
// Keyboard shortcuts
@ -2389,6 +2500,8 @@ export class Editor extends PrimaryView {
this.palette_selection = null;
this.select_palette('floor', true);
this.selection = new Selection(this);
}
activate() {
@ -2743,6 +2856,9 @@ export class Editor extends PrimaryView {
if (! tile)
return;
if (! this.selection.contains(x, y))
return;
let cell = this.cell(x, y);
// Replace whatever's on the same layer
// TODO should preserve wiring if possible too
@ -2769,6 +2885,8 @@ export class Editor extends PrimaryView {
}
erase_tile(cell, tile = null) {
// TODO respect selection
if (tile === null) {
tile = this.palette_selection;
}

View File

@ -1413,13 +1413,35 @@ body.--debug #player-debug {
stroke-width: 0.0625;
fill: none;
}
#editor .level-editor-overlay rect.overlay-cursor {
#editor .level-editor-overlay .overlay-transient {
display: none;
}
#editor .level-editor-overlay .overlay-transient.--visible {
display: initial;
}
#editor .level-editor-overlay rect.overlay-cursor {
x-stroke: hsla(225, 100%, 60%, 0.5);
fill: hsla(225, 100%, 75%, 0.25);
}
#editor .level-editor-overlay rect.overlay-pending-selection {
stroke: hsla(225, 100%, 60%, 0.5);
fill: hsla(225, 100%, 75%, 0.25);
}
#editor .level-editor-overlay rect.overlay-cursor.--visible {
display: initial;
#editor .level-editor-overlay rect.overlay-selection {
stroke: #000c;
fill: hsla(225, 0%, 75%, 0.25);
stroke-dasharray: 0.125, 0.125;
animation: marching-ants 1s linear infinite;
pointer-events: auto;
cursor: move;
}
@keyframes marching-ants {
0% {
stroke-dashoffset: 0.25;
}
100% {
stroke-dashoffset: 0;
}
}
#editor .level-editor-overlay rect.overlay-cxn {
stroke: red;