Add support for a floating selection
This commit is contained in:
parent
2183e7de3c
commit
18b9fd6d4d
@ -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>
|
||||||
|
|||||||
@ -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,9 +536,15 @@ 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 {
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user