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;
}
edit_tile(tile) {
edit_tile(tile, cell) {
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) {
@ -46,13 +58,13 @@ class LetterTileEditor extends TileEditorOverlay {
list.addEventListener('change', ev => {
if (this.tile) {
this.tile.overlaid_glyph = this.root.elements['glyph'].value;
this.editor.mark_tile_dirty(this.tile);
this.mark_dirty();
}
});
}
edit_tile(tile) {
super.edit_tile(tile);
edit_tile(tile, cell) {
super.edit_tile(tile, cell);
this.root.elements['glyph'].value = tile.overlaid_glyph;
}
@ -75,8 +87,8 @@ class HintTileEditor extends TileEditorOverlay {
});
}
edit_tile(tile) {
super.edit_tile(tile);
edit_tile(tile, cell) {
super.edit_tile(tile, cell);
this.text.value = tile.hint_text ?? "";
}
@ -144,13 +156,13 @@ class FrameBlockTileEditor extends TileEditorOverlay {
else {
this.tile.arrows.delete(ev.target.value);
}
this.editor.mark_tile_dirty(this.tile);
this.mark_dirty();
});
this.root.append(arrow_list);
}
edit_tile(tile) {
super.edit_tile(tile);
edit_tile(tile, cell) {
super.edit_tile(tile, cell);
for (let input of this.root.elements['direction']) {
input.checked = tile.arrows.has(input.value);
@ -197,7 +209,7 @@ class RailroadTileEditor extends TileEditorOverlay {
else {
this.tile.tracks &= ~bit;
}
this.editor.mark_tile_dirty(this.tile);
this.mark_dirty();
}
});
this.root.append(track_list);
@ -213,7 +225,7 @@ class RailroadTileEditor extends TileEditorOverlay {
switch_list.addEventListener('change', ev => {
if (this.tile) {
this.tile.track_switch = parseInt(ev.target.value, 10);
this.editor.mark_tile_dirty(this.tile);
this.mark_dirty();
}
});
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)
}
edit_tile(tile) {
super.edit_tile(tile);
edit_tile(tile, cell) {
super.edit_tile(tile, cell);
for (let input of this.root.elements['track']) {
input.checked = !! (tile.tracks & (1 << input.value));

View File

@ -869,7 +869,7 @@ class WireOperation extends DrawOperation {
// Draw
tile.wire_directions |= DIRECTIONS[wire_direction].bit;
}
// TODO this.editor.mark_tile_dirty(tile);
this.editor.mark_cell_dirty(cell);
break;
}
@ -921,7 +921,7 @@ class AdjustOperation extends MouseOperation {
for (let tile of cell) {
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(tile, this.mx0, this.my0);
this.editor.open_tile_prop_overlay(tile, cell, this.mx0, this.my0);
break;
}
}
@ -942,7 +942,7 @@ class AdjustOperation extends MouseOperation {
rotated = this.editor.rotate_tile_right(tile);
}
if (rotated) {
this.editor.mark_tile_dirty(tile);
this.editor.mark_cell_dirty(cell);
break;
}
@ -950,7 +950,7 @@ class AdjustOperation extends MouseOperation {
let other = (this.alt_mode ? ADJUST_TOGGLES_CCW : ADJUST_TOGGLES_CW)[tile.type.name];
if (other) {
tile.type = TILE_TYPES[other];
this.editor.mark_tile_dirty(tile);
this.editor.mark_cell_dirty(cell);
break;
}
}
@ -2397,6 +2397,7 @@ export class Editor extends PrimaryView {
}
else if (this.palette_selection) {
this.rotate_tile_left(this.palette_selection);
this.redraw_palette_selection();
}
}
else if (ev.key === '.') {
@ -2405,6 +2406,7 @@ export class Editor extends PrimaryView {
}
else if (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 => {
if (this.palette_selection && TILES_WITH_PROPS[this.palette_selection.type.name]) {
// 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??
@ -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'}));
rotate_right_button.addEventListener('click', ev => {
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'}));
rotate_left_button.addEventListener('click', ev => {
this.rotate_tile_left(this.palette_selection);
this.redraw_palette_selection();
});
this.root.querySelector('.controls').append(
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() {
super.activate();
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
@ -3028,7 +3041,7 @@ export class Editor extends PrimaryView {
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
// should default you back to the pencil
@ -3048,7 +3061,6 @@ export class Editor extends PrimaryView {
return false;
}
this.mark_tile_dirty(tile);
return true;
}
@ -3063,7 +3075,6 @@ export class Editor extends PrimaryView {
return false;
}
this.mark_tile_dirty(tile);
return true;
}
@ -3075,26 +3086,55 @@ export class Editor extends PrimaryView {
// -- Drawing --
mark_tile_dirty(tile) {
// TODO partial redraws! until then, redraw everything
if (tile === this.palette_selection) {
redraw_palette_selection() {
// FIXME should redraw in an existing canvas
this.selected_tile_el.textContent = '';
this.selected_tile_el.append(this.renderer.create_tile_type_canvas(tile.type.name, tile));
}
else {
this.redraw_entire_level();
}
this.selected_tile_el.append(this.renderer.create_tile_type_canvas(
this.palette_selection.type.name, this.palette_selection));
}
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() {
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 --
is_in_bounds(x, y) {
@ -3179,12 +3219,12 @@ export class Editor extends PrimaryView {
// -- Misc?? --
open_tile_prop_overlay(tile, x0, y0) {
open_tile_prop_overlay(tile, cell, x0, y0) {
this.cancel_mouse_operation();
// FIXME keep these around, don't recreate them constantly
let overlay_class = TILES_WITH_PROPS[tile.type.name];
let overlay = new overlay_class(this.conductor);
overlay.edit_tile(tile);
overlay.edit_tile(tile, cell);
overlay.open();
// FIXME move this into TransientOverlay or some other base class