Fix the editor's partial redrawing; place popup editors by bbox, not mouse position

This commit is contained in:
Eevee (Evelyn Woods) 2021-01-07 17:55:11 -07:00
parent f389f4d027
commit 7ceab97472
2 changed files with 41 additions and 22 deletions

View File

@ -149,6 +149,8 @@ class EditorLevelMetaOverlay extends DialogOverlay {
), ),
); );
this.root.elements['viewport'].value = stored_level.viewport_size; this.root.elements['viewport'].value = stored_level.viewport_size;
// FIXME this isn't actually saved lol but also it's 24 bytes into the damn options. also
// it should default to 2, the good one
this.root.elements['blob_behavior'].value = stored_level.blob_behavior; this.root.elements['blob_behavior'].value = stored_level.blob_behavior;
// TODO: // TODO:
// - chips? // - chips?
@ -458,9 +460,9 @@ class PencilOperation extends DrawOperation {
} }
let template = this.editor.palette_selection; let template = this.editor.palette_selection;
let cell = this.cell(x, y);
if (this.alt_mode) { if (this.alt_mode) {
// Erase // Erase
let cell = this.cell(x, y);
if (this.modifier === 'shift') { if (this.modifier === 'shift') {
// Aggressive mode: erase the entire cell // Aggressive mode: erase the entire cell
for (let layer = 0; layer < LAYERS.MAX; layer++) { for (let layer = 0; layer < LAYERS.MAX; layer++) {
@ -479,7 +481,6 @@ class PencilOperation extends DrawOperation {
return; return;
if (this.modifier === 'shift') { if (this.modifier === 'shift') {
// Aggressive mode: erase whatever's already in the cell // Aggressive mode: erase whatever's already in the cell
let cell = this.cell(x, y);
for (let layer = 0; layer < LAYERS.MAX; layer++) { for (let layer = 0; layer < LAYERS.MAX; layer++) {
cell[layer] = null; cell[layer] = null;
} }
@ -494,6 +495,7 @@ class PencilOperation extends DrawOperation {
this.editor.place_in_cell(x, y, template); this.editor.place_in_cell(x, y, template);
} }
} }
this.editor.mark_cell_dirty(cell);
} }
} }
@ -922,8 +924,8 @@ class AdjustOperation extends MouseOperation {
if (this.modifier === 'ctrl') { if (this.modifier === 'ctrl') {
for (let tile of cell) { for (let tile of cell) {
if (tile && TILES_WITH_PROPS[tile.type.name] !== undefined) { if (tile && TILES_WITH_PROPS[tile.type.name] !== undefined) {
// TODO use the tile's bbox, not the mouse position this.editor.open_tile_prop_overlay(
this.editor.open_tile_prop_overlay(tile, cell, this.mx0, this.my0); tile, cell, this.editor.renderer.get_cell_rect(cell.x, cell.y));
break; break;
} }
} }
@ -2052,9 +2054,11 @@ const SPECIAL_PALETTE_BEHAVIOR = {
} }
}, },
rotate_left(tile) { rotate_left(tile) {
tile.direction = DIRECTIONS[tile.direction].left;
tile.arrows = new Set(Array.from(tile.arrows, arrow => DIRECTIONS[arrow].left)); tile.arrows = new Set(Array.from(tile.arrows, arrow => DIRECTIONS[arrow].left));
}, },
rotate_right(tile) { rotate_right(tile) {
tile.direction = DIRECTIONS[tile.direction].right;
tile.arrows = new Set(Array.from(tile.arrows, arrow => DIRECTIONS[arrow].right)); tile.arrows = new Set(Array.from(tile.arrows, arrow => DIRECTIONS[arrow].right));
}, },
}, },
@ -2450,9 +2454,6 @@ export class Editor extends PrimaryView {
this.mouse_op = new op_type(this, ev); this.mouse_op = new op_type(this, ev);
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
// FIXME eventually this should be automatic
this.redraw_entire_level();
} }
else if (ev.button === 1) { else if (ev.button === 1) {
// Middle button: always pan // Middle button: always pan
@ -2470,8 +2471,6 @@ export class Editor extends PrimaryView {
this.mouse_op = new op_type(this, ev); this.mouse_op = new op_type(this, ev);
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
this.redraw_entire_level();
} }
}); });
// Once the mouse is down, we should accept mouse movement anywhere // Once the mouse is down, we should accept mouse movement anywhere
@ -2497,9 +2496,6 @@ export class Editor extends PrimaryView {
} }
this.mouse_op.do_mousemove(ev); this.mouse_op.do_mousemove(ev);
// FIXME !!!
this.redraw_entire_level();
}); });
// TODO should this happen for a mouseup anywhere? // TODO should this happen for a mouseup anywhere?
this.viewport_el.addEventListener('mouseup', ev => { this.viewport_el.addEventListener('mouseup', ev => {
@ -2524,8 +2520,8 @@ export class Editor extends PrimaryView {
this.selected_tile_el.id = 'editor-tile'; this.selected_tile_el.id = 'editor-tile';
this.selected_tile_el.addEventListener('click', ev => { this.selected_tile_el.addEventListener('click', ev => {
if (this.palette_selection && TILES_WITH_PROPS[this.palette_selection.type.name]) { if (this.palette_selection && TILES_WITH_PROPS[this.palette_selection.type.name]) {
// FIXME use tile bounds this.open_tile_prop_overlay(
this.open_tile_prop_overlay(this.palette_selection, null, ev.clientX, ev.clientY); this.palette_selection, null, this.selected_tile_el.getBoundingClientRect());
} }
}); });
// TODO ones for the palette too?? // TODO ones for the palette too??
@ -2954,6 +2950,7 @@ export class Editor extends PrimaryView {
// TODO support a game too i guess // TODO support a game too i guess
this.stored_level = stored_level; this.stored_level = stored_level;
this.update_viewport_size(); this.update_viewport_size();
this.update_cell_coordinates();
// Load connections // Load connections
this.connections_g.textContent = ''; this.connections_g.textContent = '';
@ -2981,6 +2978,13 @@ export class Editor extends PrimaryView {
} }
} }
update_cell_coordinates() {
// We rely on each StoredCell having .x and .y for partial redrawing
for (let [i, cell] of this.stored_level.linear_cells.entries()) {
[cell.x, cell.y] = this.stored_level.scalar_to_coords(i);
}
}
update_viewport_size() { update_viewport_size() {
this.renderer.set_viewport_size(this.stored_level.size_x, this.stored_level.size_y); this.renderer.set_viewport_size(this.stored_level.size_x, this.stored_level.size_y);
this.svg_overlay.setAttribute('viewBox', `0 0 ${this.stored_level.size_x} ${this.stored_level.size_y}`); this.svg_overlay.setAttribute('viewBox', `0 0 ${this.stored_level.size_x} ${this.stored_level.size_y}`);
@ -3158,8 +3162,8 @@ export class Editor extends PrimaryView {
this.renderer.draw_static_region( this.renderer.draw_static_region(
this._dirty_rect.left, this._dirty_rect.top, this._dirty_rect.left, this._dirty_rect.top,
this._dirty_rect.right, this._dirty_rect.bottom); this._dirty_rect.right, this._dirty_rect.bottom);
this._schedule_redraw_loop();
} }
this._schedule_redraw_loop();
} }
// -- Utility/inspection -- // -- Utility/inspection --
@ -3188,6 +3192,7 @@ export class Editor extends PrimaryView {
return; return;
let cell = this.cell(x, y); let cell = this.cell(x, y);
this.mark_cell_dirty(cell);
// Replace whatever's on the same layer // Replace whatever's on the same layer
// TODO should preserve wiring if possible too // TODO should preserve wiring if possible too
let existing_tile = cell[tile.type.layer]; let existing_tile = cell[tile.type.layer];
@ -3218,6 +3223,7 @@ export class Editor extends PrimaryView {
tile = this.palette_selection; tile = this.palette_selection;
} }
this.mark_cell_dirty(cell);
let existing_tile = cell[tile.type.layer]; let existing_tile = cell[tile.type.layer];
if (existing_tile) { if (existing_tile) {
// If we find a tile of the same type as the one being drawn, see if it has custom // If we find a tile of the same type as the one being drawn, see if it has custom
@ -3241,12 +3247,11 @@ export class Editor extends PrimaryView {
if (tile.type.layer === LAYERS.terrain) { if (tile.type.layer === LAYERS.terrain) {
cell[LAYERS.terrain] = {type: TILE_TYPES.floor}; cell[LAYERS.terrain] = {type: TILE_TYPES.floor};
} }
this.mark_cell_dirty(cell);
} }
// -- Misc?? -- // -- Misc?? --
open_tile_prop_overlay(tile, cell, x0, y0) { open_tile_prop_overlay(tile, cell, rect) {
this.cancel_mouse_operation(); this.cancel_mouse_operation();
// FIXME keep these around, don't recreate them constantly // FIXME keep these around, don't recreate them constantly
let overlay_class = TILES_WITH_PROPS[tile.type.name]; let overlay_class = TILES_WITH_PROPS[tile.type.name];
@ -3256,16 +3261,17 @@ export class Editor extends PrimaryView {
// FIXME move this into TransientOverlay or some other base class // FIXME move this into TransientOverlay or some other base class
let root = overlay.root; let root = overlay.root;
let spacing = 2;
// Vertical position: either above or below, preferring the side that has more space // Vertical position: either above or below, preferring the side that has more space
if (y0 > document.body.clientHeight / 2) { if (rect.top - 0 > document.body.clientHeight - rect.bottom) {
// Above // Above
root.classList.add('--above'); root.classList.add('--above');
root.style.top = `${y0 - root.offsetHeight}px`; root.style.top = `${rect.top - root.offsetHeight - spacing}px`;
} }
else { else {
// Below // Below
root.classList.remove('--above'); root.classList.remove('--above');
root.style.top = `${y0}px`; root.style.top = `${rect.bottom + spacing}px`;
} }
// Horizontal position: centered, but kept within the screen // Horizontal position: centered, but kept within the screen
let left; let left;
@ -3276,10 +3282,10 @@ export class Editor extends PrimaryView {
} }
else { else {
left = Math.max(margin, Math.min(document.body.clientWidth - root.offsetWidth - margin, left = Math.max(margin, Math.min(document.body.clientWidth - root.offsetWidth - margin,
x0 - root.offsetWidth / 2)); (rect.left + rect.right - root.offsetWidth) / 2));
} }
root.style.left = `${left}px`; root.style.left = `${left}px`;
root.style.setProperty('--chevron-offset', `${x0 - left}px`); root.style.setProperty('--chevron-offset', `${(rect.left + rect.right) / 2 - left}px`);
} }
cancel_mouse_operation() { cancel_mouse_operation() {
@ -3301,6 +3307,7 @@ export class Editor extends PrimaryView {
this.stored_level.size_x = size_x; this.stored_level.size_x = size_x;
this.stored_level.size_y = size_y; this.stored_level.size_y = size_y;
this.update_viewport_size(); this.update_viewport_size();
this.update_cell_coordinates();
this.redraw_entire_level(); this.redraw_entire_level();
} }
} }

View File

@ -52,6 +52,18 @@ export class CanvasRenderer {
this.viewport_dirty = true; this.viewport_dirty = true;
} }
get_cell_rect(x, y) {
let rect = this.canvas.getBoundingClientRect();
let scale_x = rect.width / this.canvas.width;
let scale_y = rect.height / this.canvas.height;
let tile_w = scale_x * this.tileset.size_x;
let tile_h = scale_y * this.tileset.size_y;
return new DOMRect(
rect.x + (x - this.viewport_x) * tile_w,
rect.y + (y - this.viewport_y) * tile_h,
tile_w, tile_h);
}
cell_coords_from_event(ev) { cell_coords_from_event(ev) {
let rect = this.canvas.getBoundingClientRect(); let rect = this.canvas.getBoundingClientRect();
let scale_x = rect.width / this.canvas.width; let scale_x = rect.width / this.canvas.width;