Teach the editor to do partial redraws

This commit is contained in:
Eevee (Evelyn Woods) 2021-01-03 18:21:52 -07:00
parent 90fa352a50
commit 1ce704864c
2 changed files with 86 additions and 34 deletions

View File

@ -10,8 +10,20 @@ class TileEditorOverlay extends TransientOverlay {
this.tile = null; this.tile = null;
} }
edit_tile(tile) { edit_tile(tile, cell) {
this.tile = tile; this.tile = tile;
this.cell = cell;
}
mark_dirty() {
if (this.cell) {
this.editor.mark_cell_dirty(this.cell);
}
else {
// TODO i guess i'm just kind of assuming it's the palette selection, but it doesn't
// necessarily have to be, if i... do something later
this.editor.redraw_palette_selection();
}
} }
static configure_tile_defaults(tile) { static configure_tile_defaults(tile) {
@ -46,13 +58,13 @@ class LetterTileEditor extends TileEditorOverlay {
list.addEventListener('change', ev => { list.addEventListener('change', ev => {
if (this.tile) { if (this.tile) {
this.tile.overlaid_glyph = this.root.elements['glyph'].value; this.tile.overlaid_glyph = this.root.elements['glyph'].value;
this.editor.mark_tile_dirty(this.tile); this.mark_dirty();
} }
}); });
} }
edit_tile(tile) { edit_tile(tile, cell) {
super.edit_tile(tile); super.edit_tile(tile, cell);
this.root.elements['glyph'].value = tile.overlaid_glyph; this.root.elements['glyph'].value = tile.overlaid_glyph;
} }
@ -75,8 +87,8 @@ class HintTileEditor extends TileEditorOverlay {
}); });
} }
edit_tile(tile) { edit_tile(tile, cell) {
super.edit_tile(tile); super.edit_tile(tile, cell);
this.text.value = tile.hint_text ?? ""; this.text.value = tile.hint_text ?? "";
} }
@ -144,13 +156,13 @@ class FrameBlockTileEditor extends TileEditorOverlay {
else { else {
this.tile.arrows.delete(ev.target.value); this.tile.arrows.delete(ev.target.value);
} }
this.editor.mark_tile_dirty(this.tile); this.mark_dirty();
}); });
this.root.append(arrow_list); this.root.append(arrow_list);
} }
edit_tile(tile) { edit_tile(tile, cell) {
super.edit_tile(tile); super.edit_tile(tile, cell);
for (let input of this.root.elements['direction']) { for (let input of this.root.elements['direction']) {
input.checked = tile.arrows.has(input.value); input.checked = tile.arrows.has(input.value);
@ -197,7 +209,7 @@ class RailroadTileEditor extends TileEditorOverlay {
else { else {
this.tile.tracks &= ~bit; this.tile.tracks &= ~bit;
} }
this.editor.mark_tile_dirty(this.tile); this.mark_dirty();
} }
}); });
this.root.append(track_list); this.root.append(track_list);
@ -213,7 +225,7 @@ class RailroadTileEditor extends TileEditorOverlay {
switch_list.addEventListener('change', ev => { switch_list.addEventListener('change', ev => {
if (this.tile) { if (this.tile) {
this.tile.track_switch = parseInt(ev.target.value, 10); this.tile.track_switch = parseInt(ev.target.value, 10);
this.editor.mark_tile_dirty(this.tile); this.mark_dirty();
} }
}); });
this.root.append(switch_list); this.root.append(switch_list);
@ -222,8 +234,8 @@ class RailroadTileEditor extends TileEditorOverlay {
// TODO initial actor facing (maybe only if there's an actor in the cell) // TODO initial actor facing (maybe only if there's an actor in the cell)
} }
edit_tile(tile) { edit_tile(tile, cell) {
super.edit_tile(tile); super.edit_tile(tile, cell);
for (let input of this.root.elements['track']) { for (let input of this.root.elements['track']) {
input.checked = !! (tile.tracks & (1 << input.value)); input.checked = !! (tile.tracks & (1 << input.value));

View File

@ -869,7 +869,7 @@ class WireOperation extends DrawOperation {
// Draw // Draw
tile.wire_directions |= DIRECTIONS[wire_direction].bit; tile.wire_directions |= DIRECTIONS[wire_direction].bit;
} }
// TODO this.editor.mark_tile_dirty(tile); this.editor.mark_cell_dirty(cell);
break; break;
} }
@ -921,7 +921,7 @@ class AdjustOperation extends MouseOperation {
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 // TODO use the tile's bbox, not the mouse position
this.editor.open_tile_prop_overlay(tile, this.mx0, this.my0); this.editor.open_tile_prop_overlay(tile, cell, this.mx0, this.my0);
break; break;
} }
} }
@ -942,7 +942,7 @@ class AdjustOperation extends MouseOperation {
rotated = this.editor.rotate_tile_right(tile); rotated = this.editor.rotate_tile_right(tile);
} }
if (rotated) { if (rotated) {
this.editor.mark_tile_dirty(tile); this.editor.mark_cell_dirty(cell);
break; break;
} }
@ -950,7 +950,7 @@ class AdjustOperation extends MouseOperation {
let other = (this.alt_mode ? ADJUST_TOGGLES_CCW : ADJUST_TOGGLES_CW)[tile.type.name]; let other = (this.alt_mode ? ADJUST_TOGGLES_CCW : ADJUST_TOGGLES_CW)[tile.type.name];
if (other) { if (other) {
tile.type = TILE_TYPES[other]; tile.type = TILE_TYPES[other];
this.editor.mark_tile_dirty(tile); this.editor.mark_cell_dirty(cell);
break; break;
} }
} }
@ -2397,6 +2397,7 @@ export class Editor extends PrimaryView {
} }
else if (this.palette_selection) { else if (this.palette_selection) {
this.rotate_tile_left(this.palette_selection); this.rotate_tile_left(this.palette_selection);
this.redraw_palette_selection();
} }
} }
else if (ev.key === '.') { else if (ev.key === '.') {
@ -2405,6 +2406,7 @@ export class Editor extends PrimaryView {
} }
else if (this.palette_selection) { else if (this.palette_selection) {
this.rotate_tile_right(this.palette_selection); this.rotate_tile_right(this.palette_selection);
this.redraw_palette_selection();
} }
} }
}); });
@ -2497,7 +2499,7 @@ export class Editor extends PrimaryView {
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 // FIXME use tile bounds
this.open_tile_prop_overlay(this.palette_selection, ev.clientX, ev.clientY); this.open_tile_prop_overlay(this.palette_selection, null, ev.clientX, ev.clientY);
} }
}); });
// TODO ones for the palette too?? // TODO ones for the palette too??
@ -2506,10 +2508,12 @@ export class Editor extends PrimaryView {
let rotate_right_button = mk('button.--image', {type: 'button'}, mk('img', {src: 'icons/rotate-right.png'})); let rotate_right_button = mk('button.--image', {type: 'button'}, mk('img', {src: 'icons/rotate-right.png'}));
rotate_right_button.addEventListener('click', ev => { rotate_right_button.addEventListener('click', ev => {
this.rotate_tile_right(this.palette_selection); this.rotate_tile_right(this.palette_selection);
this.redraw_palette_selection();
}); });
let rotate_left_button = mk('button.--image', {type: 'button'}, mk('img', {src: 'icons/rotate-left.png'})); let rotate_left_button = mk('button.--image', {type: 'button'}, mk('img', {src: 'icons/rotate-left.png'}));
rotate_left_button.addEventListener('click', ev => { rotate_left_button.addEventListener('click', ev => {
this.rotate_tile_left(this.palette_selection); this.rotate_tile_left(this.palette_selection);
this.redraw_palette_selection();
}); });
this.root.querySelector('.controls').append( this.root.querySelector('.controls').append(
mk('div.editor-tile-controls', rotate_right_button, this.selected_tile_el, rotate_left_button)); mk('div.editor-tile-controls', rotate_right_button, this.selected_tile_el, rotate_left_button));
@ -2721,6 +2725,15 @@ export class Editor extends PrimaryView {
activate() { activate() {
super.activate(); super.activate();
this.redraw_entire_level(); this.redraw_entire_level();
this._schedule_redraw_loop();
}
deactivate() {
if (this._redraw_handle) {
window.cancelAnimationFrame(this._redraw_handle);
this._redraw_handle = null;
}
super.deactivate();
} }
// Level creation, management, and saving // Level creation, management, and saving
@ -3028,7 +3041,7 @@ export class Editor extends PrimaryView {
this.palette_selection_el.classList.add('--selected'); this.palette_selection_el.classList.add('--selected');
} }
this.mark_tile_dirty(tile); this.redraw_palette_selection();
// Some tools obviously don't work with a palette selection, in which case changing tiles // Some tools obviously don't work with a palette selection, in which case changing tiles
// should default you back to the pencil // should default you back to the pencil
@ -3048,7 +3061,6 @@ export class Editor extends PrimaryView {
return false; return false;
} }
this.mark_tile_dirty(tile);
return true; return true;
} }
@ -3063,7 +3075,6 @@ export class Editor extends PrimaryView {
return false; return false;
} }
this.mark_tile_dirty(tile);
return true; return true;
} }
@ -3075,26 +3086,55 @@ export class Editor extends PrimaryView {
// -- Drawing -- // -- Drawing --
mark_tile_dirty(tile) { redraw_palette_selection() {
// TODO partial redraws! until then, redraw everything
if (tile === this.palette_selection) {
// FIXME should redraw in an existing canvas // FIXME should redraw in an existing canvas
this.selected_tile_el.textContent = ''; this.selected_tile_el.textContent = '';
this.selected_tile_el.append(this.renderer.create_tile_type_canvas(tile.type.name, tile)); this.selected_tile_el.append(this.renderer.create_tile_type_canvas(
} this.palette_selection.type.name, this.palette_selection));
else {
this.redraw_entire_level();
}
} }
mark_cell_dirty(cell) { mark_cell_dirty(cell) {
this.redraw_entire_level(); if (! this._dirty_rect) {
this._dirty_rect = new DOMRect(cell.x, cell.y, 1, 1);
}
else {
let rect = this._dirty_rect;
if (cell.x < rect.left) {
rect.width = rect.right - cell.x;
rect.x = cell.x;
}
else if (cell.x >= rect.right) {
rect.width = cell.x - rect.left + 1;
}
if (cell.y < rect.top) {
rect.height = rect.bottom - cell.y;
rect.y = cell.y;
}
else if (cell.y >= rect.bottom) {
rect.height = cell.y - rect.top + 1;
}
}
} }
redraw_entire_level() { redraw_entire_level() {
this.renderer.draw_static_region(0, 0, this.stored_level.size_x, this.stored_level.size_y); this.renderer.draw_static_region(0, 0, this.stored_level.size_x, this.stored_level.size_y);
} }
_schedule_redraw_loop() {
this._redraw_handle = window.requestAnimationFrame(this._redraw_dirty.bind(this));
this._dirty_rect = null;
}
// Automatically redraw only what's changed
_redraw_dirty() {
if (this._dirty_rect) {
this.renderer.draw_static_region(
this._dirty_rect.left, this._dirty_rect.top,
this._dirty_rect.right, this._dirty_rect.bottom);
this._schedule_redraw_loop();
}
}
// -- Utility/inspection -- // -- Utility/inspection --
is_in_bounds(x, y) { is_in_bounds(x, y) {
@ -3179,12 +3219,12 @@ export class Editor extends PrimaryView {
// -- Misc?? -- // -- Misc?? --
open_tile_prop_overlay(tile, x0, y0) { open_tile_prop_overlay(tile, cell, x0, y0) {
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];
let overlay = new overlay_class(this.conductor); let overlay = new overlay_class(this.conductor);
overlay.edit_tile(tile); overlay.edit_tile(tile, cell);
overlay.open(); overlay.open();
// FIXME move this into TransientOverlay or some other base class // FIXME move this into TransientOverlay or some other base class