Add support for a floating selection

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-31 14:49:37 -07:00
parent 2183e7de3c
commit 18b9fd6d4d
2 changed files with 133 additions and 12 deletions

View File

@ -338,8 +338,6 @@
</div> </div>
</div> </div>
<nav class="controls"> <nav class="controls">
<div id="editor-tile">
</div>
<!-- <!--
<p style> <p style>
Tip: Right click to color drop.<br> Tip: Right click to color drop.<br>

View File

@ -484,20 +484,49 @@ class PencilOperation extends DrawOperation {
class SelectOperation extends MouseOperation { class SelectOperation extends MouseOperation {
start() { start() {
if (! this.editor.selection.is_empty && this.editor.selection.contains(this.gx0, this.gy0)) {
// Move existing selection
this.mode = 'float';
if (this.modifier === 'ctrl') {
this.make_copy = true;
}
}
else {
// Create new selection
this.pending_selection = this.editor.selection.create_pending(); this.pending_selection = this.editor.selection.create_pending();
this.update_pending_selection(); this.update_pending_selection();
}
this.has_moved = false; this.has_moved = false;
} }
step(mx, my, gxf, gyf, gx, gy) { step(mx, my, gxf, gyf, gx, gy) {
this.has_moved = true; if (this.mode === 'float') {
if (! this.has_moved) {
if (this.make_copy) {
// FIXME support directly making a copy of a copy
this.editor.selection.defloat();
this.editor.selection.enfloat(true);
}
else if (! this.editor.selection.is_floating) {
this.editor.selection.enfloat();
}
}
this.editor.selection.move_by(Math.floor(gx - this.gx1), Math.floor(gy - this.gy1));
}
else {
this.update_pending_selection(); this.update_pending_selection();
} }
this.has_moved = true;
}
update_pending_selection() { update_pending_selection() {
this.pending_selection.set_extrema(this.gx0, this.gy0, this.gx1, this.gy1); this.pending_selection.set_extrema(this.gx0, this.gy0, this.gx1, this.gy1);
} }
commit() { commit() {
if (this.mode === 'float') {
}
else {
if (! this.has_moved) { if (! this.has_moved) {
// Plain click clears selection // Plain click clears selection
this.pending_selection.discard(); this.pending_selection.discard();
@ -507,10 +536,16 @@ class SelectOperation extends MouseOperation {
this.pending_selection.commit(); this.pending_selection.commit();
} }
} }
}
abort() { abort() {
if (this.mode === 'float') {
// Nothing to do really
}
else {
this.pending_selection.discard(); this.pending_selection.discard();
} }
} }
}
class ForceFloorOperation extends DrawOperation { class ForceFloorOperation extends DrawOperation {
start() { start() {
@ -2173,13 +2208,18 @@ class Selection {
this.element = mk_svg('rect.overlay-selection.overlay-transient'); this.element = mk_svg('rect.overlay-selection.overlay-transient');
this.svg_group.append(this.element); this.svg_group.append(this.element);
this.floating_contents = null; this.floated_cells = null;
this.floated_element = null;
} }
get is_empty() { get is_empty() {
return this.rect === null; return this.rect === null;
} }
get is_floating() {
return !! this.floated_cells;
}
contains(x, y) { contains(x, y) {
// Empty selection means everything is selected? // Empty selection means everything is selected?
if (this.rect === null) if (this.rect === null)
@ -2189,6 +2229,8 @@ class Selection {
} }
create_pending() { create_pending() {
this.defloat();
return new PendingSelection(this); return new PendingSelection(this);
} }
@ -2201,11 +2243,92 @@ class Selection {
this.element.setAttribute('height', this.rect.height); this.element.setAttribute('height', this.rect.height);
} }
move_by(dx, dy) {
if (! this.rect)
return;
this.rect.x += dx;
this.rect.y += dy;
this.element.setAttribute('x', this.rect.x);
this.element.setAttribute('y', this.rect.y);
if (! this.floated_element)
return;
let bbox = this.rect;
this.floated_element.setAttribute('transform', `translate(${bbox.x} ${bbox.y})`);
}
clear() { clear() {
this.defloat();
this.rect = null; this.rect = null;
this.element.classList.remove('--visible'); this.element.classList.remove('--visible');
} }
*iter_cells() {
if (! this.rect)
return;
for (let x = this.rect.left; x < this.rect.right; x++) {
for (let y = this.rect.top; y < this.rect.bottom; y++) {
yield [x, y];
}
}
}
enfloat(copy = false) {
if (this.floated_cells)
console.error("Trying to float a selection that's already floating");
this.floated_cells = [];
let tileset = this.editor.conductor.tileset;
let stored_level = this.editor.stored_level;
let bbox = this.rect;
let canvas = mk('canvas', {width: bbox.width * tileset.size_x, height: bbox.height * tileset.size_y});
let ctx = canvas.getContext('2d');
ctx.drawImage(
this.editor.renderer.canvas,
bbox.x * tileset.size_x, bbox.y * tileset.size_y, bbox.width * tileset.size_x, bbox.height * tileset.size_y,
0, 0, bbox.width * tileset.size_x, bbox.height * tileset.size_y);
for (let [x, y] of this.iter_cells()) {
let n = stored_level.coords_to_scalar(x, y);
if (copy) {
this.floated_cells.push(stored_level.linear_cells[n].map(tile => ({...tile})));
}
else {
this.floated_cells.push(stored_level.linear_cells[n]);
stored_level.linear_cells[n] = [{type: TILE_TYPES['floor']}];
this.editor.mark_cell_dirty(stored_level.linear_cells[n]);
}
}
this.floated_element = mk_svg('g', mk_svg('foreignObject', {
x: 0, y: 0,
width: canvas.width, height: canvas.height,
transform: `scale(${1/tileset.size_x} ${1/tileset.size_y})`,
}, canvas));
this.floated_element.setAttribute('transform', `translate(${bbox.x} ${bbox.y})`);
this.svg_group.append(this.floated_element);
}
defloat() {
if (! this.floated_element)
return;
let bbox = this.rect;
let stored_level = this.editor.stored_level;
let i = 0;
for (let [x, y] of this.iter_cells()) {
let n = stored_level.coords_to_scalar(x, y);
stored_level.linear_cells[n] = this.floated_cells[i];
this.editor.mark_cell_dirty(stored_level.linear_cells[n]);
i += 1;
}
this.floated_element.remove();
this.floated_element = null;
this.floated_cells = null;
}
// TODO allow floating/dragging, ctrl-dragging to copy, anchoring... // 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) // TODO make more stuff respect this (more things should go through Editor for undo reasons anyway)
} }