Add support for subtracting from the selection

This commit is contained in:
Eevee (Evelyn Woods) 2024-04-17 01:22:45 -06:00
parent eaa3bf6965
commit ed5f76221b
4 changed files with 65 additions and 25 deletions

View File

@ -41,7 +41,7 @@ export const TOOLS = {
select_box: { select_box: {
icon: 'icons/tool-select-box.png', icon: 'icons/tool-select-box.png',
name: "Box select", name: "Box select",
desc: "Select and manipulate rectangles.\n\n[mouse1] Select rectangle\n[shift] [mouse1] Add to selection\n\n[mouse1] Move selection\n[ctrl] [mouse1] Clone selection", desc: "Select and manipulate rectangles.\n\n[mouse1] Select rectangle\n[shift] [mouse1] Add to selection\n[ctrl] [mouse1] Remove from selection\n\n[mouse1] Move selection\n[ctrl] [mouse1] Clone selection",
affects_selection: true, affects_selection: true,
op1: mouseops.SelectOperation, op1: mouseops.SelectOperation,
shortcut: 'm', shortcut: 'm',

View File

@ -33,8 +33,9 @@ export class SVGConnection {
export class PendingRectangularSelection { export class PendingRectangularSelection {
constructor(owner) { constructor(owner, mode) {
this.owner = owner; this.owner = owner;
this.mode = mode ?? 'new'; // new, add, subtract
this.element = mk_svg('rect.overlay-pending-selection'); this.element = mk_svg('rect.overlay-pending-selection');
this.size_text = mk_svg('text.overlay-edit-tip'); this.size_text = mk_svg('text.overlay-edit-tip');
this.owner.svg_group.append(this.element, this.size_text); this.owner.svg_group.append(this.element, this.size_text);
@ -54,7 +55,16 @@ export class PendingRectangularSelection {
} }
commit() { commit() {
this.owner.add_rect(this.rect); if (this.mode === 'new') {
this.owner.clear();
this.owner.add_rect(this.rect);
}
else if (this.mode === 'add') {
this.owner.add_rect(this.rect);
}
else if (this.mode === 'subtract') {
this.owner.subtract_rect(this.rect);
}
this.element.remove(); this.element.remove();
this.size_text.remove(); this.size_text.remove();
} }
@ -116,8 +126,8 @@ export class Selection {
return this.cells.has(this.editor.stored_level.coords_to_scalar(x, y)); return this.cells.has(this.editor.stored_level.coords_to_scalar(x, y));
} }
create_pending() { create_pending(mode) {
return new PendingRectangularSelection(this); return new PendingRectangularSelection(this, mode);
} }
add_rect(rect) { add_rect(rect) {
@ -154,18 +164,36 @@ export class Selection {
Math.max(this.bbox.bottom, rect.bottom) - this.bbox.y); Math.max(this.bbox.bottom, rect.bottom) - this.bbox.y);
} }
// XXX wait what the hell is this doing here? why would we set_from_rect while floating, vs this._update_outline();
// stamping it first? }
if (this.floated_element) {
console.log("what the hell is this doing here"); subtract_rect(rect) {
let tileset = this.editor.renderer.tileset; let old_cells = this.cells;
this.floated_canvas.width = this.bbox.width * tileset.size_x; this.cells = new Set(this.cells);
this.floated_canvas.height = this.bbox.height * tileset.size_y;
let foreign_obj = this.floated_element.querySelector('foreignObject'); this.editor._do(
foreign_obj.setAttribute('width', this.floated_canvas.width); () => this._subtract_rect(rect),
foreign_obj.setAttribute('height', this.floated_canvas.height); () => {
this._set_from_set(old_cells);
},
false,
);
}
_subtract_rect(rect) {
if (this.is_empty)
// Nothing to do
return;
let stored_level = this.editor.stored_level;
for (let y = rect.top; y < rect.bottom; y++) {
for (let x = rect.left; x < rect.right; x++) {
this.cells.delete(stored_level.coords_to_scalar(x, y));
}
} }
// TODO shrink bbox? i guess i only have to check along the edges that the rect intersects?
this._update_outline(); this._update_outline();
} }

View File

@ -525,13 +525,21 @@ export class FillOperation extends MouseOperation {
// TODO also, delete // TODO also, delete
// FIXME i broke transforms // FIXME i broke transforms
// FIXME need to subtract from selection too // FIXME don't show the overlay text until has_moved
// FIXME hide the god damn cursor
export class SelectOperation extends MouseOperation { export class SelectOperation extends MouseOperation {
handle_press() { handle_press() {
if (this.shift) { if (this.shift) {
// Extend selection this.mode = 'select';
this.mode = 'extend'; if (this.ctrl) {
this.pending_selection = this.editor.selection.create_pending(); // Subtract from selection (the normal way is ctrl, but ctrl-shift works even to
// start dragging inside an existing selection)
this.pending_selection = this.editor.selection.create_pending('subtract');
}
else {
// Extend selection
this.pending_selection = this.editor.selection.create_pending('add');
}
this.update_pending_selection(); this.update_pending_selection();
} }
else if (! this.editor.selection.is_empty && else if (! this.editor.selection.is_empty &&
@ -542,9 +550,15 @@ export class SelectOperation extends MouseOperation {
this.make_copy = this.ctrl; this.make_copy = this.ctrl;
} }
else { else {
// Create new selection this.mode = 'select';
this.mode = 'create'; if (this.ctrl) {
this.pending_selection = this.editor.selection.create_pending(); // Subtract from selection (must initiate click outside selection, or it'll float)
this.pending_selection = this.editor.selection.create_pending('subtract');
}
else {
// Create new selection
this.pending_selection = this.editor.selection.create_pending('new');
}
this.update_pending_selection(); this.update_pending_selection();
} }
this.has_moved = false; this.has_moved = false;
@ -598,9 +612,6 @@ export class SelectOperation extends MouseOperation {
// commit it before doing anything else // commit it before doing anything else
this.editor.selection.commit_floating(); this.editor.selection.commit_floating();
if (this.mode === 'create') {
this.editor.selection.clear();
}
this.pending_selection.commit(); this.pending_selection.commit();
} }
else { else {

View File

@ -2244,6 +2244,7 @@ svg.level-editor-overlay path.overlay-selection-background {
svg.level-editor-overlay path.overlay-selection { svg.level-editor-overlay path.overlay-selection {
stroke: hsla(var(--selected-hue), 10%, 10%, 0.75); stroke: hsla(var(--selected-hue), 10%, 10%, 0.75);
fill: hsla(var(--selected-hue), 50%, 75%, 0.375); fill: hsla(var(--selected-hue), 50%, 75%, 0.375);
fill-rule: evenodd;
stroke-width: calc(0.125px / var(--scale)); stroke-width: calc(0.125px / var(--scale));
stroke-dasharray: calc(0.125px / var(--scale)), calc(0.125px / var(--scale)); stroke-dasharray: calc(0.125px / var(--scale)), calc(0.125px / var(--scale));
animation: marching-ants 0.5s linear infinite; animation: marching-ants 0.5s linear infinite;