diff options
Diffstat (limited to 'ui/DisplayElement.js')
-rw-r--r-- | ui/DisplayElement.js | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/ui/DisplayElement.js b/ui/DisplayElement.js index b7e07b9..87a9cb5 100644 --- a/ui/DisplayElement.js +++ b/ui/DisplayElement.js @@ -14,10 +14,14 @@ module.exports = class DisplayElement extends EventEmitter { constructor() { super() - this.visible = true + this[DisplayElement.drawValues] = {} + this[DisplayElement.lastDrawValues] = {} + this[DisplayElement.scheduledDraw] = false - this.parent = null this.children = [] + this.parent = null + + this.visible = true this.x = 0 this.y = 0 @@ -82,6 +86,68 @@ module.exports = class DisplayElement extends EventEmitter { } } + getDep(key) { + return this[DisplayElement.drawValues][key] + } + + setDep(key, value) { + const oldValue = this[DisplayElement.drawValues][key] + // TODO: new map for old values. we only compare value !== oldValue LATER, at render time, not when choosing to schedule - otherwise intermediate sets e.g. f.y = 1; f.y++, which always has a net effect of f.y = 2, will count as a redraw even though the final value isn't changing between frames + if (value !== this[DisplayElement.drawValues][key]) { + this[DisplayElement.drawValues][key] = value + this.scheduleDraw() + // Grumble: technically it's possible for a root element to not be an + // actual Root. While we don't check for this case most of the time (even + // though we ought to), we do here because it's not unlikely for draw + // dependency values to be changed before the element is actually added + // to a Root element. + if (this.root.scheduleRender) { + this.root.scheduleRender() + } + } + return value + } + + scheduleDrawWithoutPropertyChange() { + // Utility function for when you need to schedule a draw without updating + // any particular draw-dependency property on the element. Works by setting + // an otherwise unused dep to a unique object. (We can't use a symbol here, + // because then Object.entries doesn't notice it.) + this.setDep('drawWithoutProperty', Math.random()) + } + + scheduleDraw() { + this[DisplayElement.scheduledDraw] = true + } + + unscheduleDraw() { + this[DisplayElement.scheduledDraw] = false + } + + hasScheduledDraw() { + if (this[DisplayElement.scheduledDraw]) { + for (const [ key, value ] of Object.entries(this[DisplayElement.drawValues])) { + if (value !== this[DisplayElement.lastDrawValues][key]) { + return true + } + } + } + return false + } + + updateLastDrawValues() { + Object.assign(this[DisplayElement.lastDrawValues], this[DisplayElement.drawValues]) + } + + eachDescendant(fn) { + // Run a function on this element, all of its children, all of their + // children, etc. + fn(this) + for (const child of this.children) { + child.eachDescendant(fn) + } + } + addChild(child, afterIndex = this.children.length, {fixLayout = true} = {}) { // TODO Don't let a direct ancestor of this be added as a child. Don't // let itself be one of its childs either! @@ -230,6 +296,17 @@ module.exports = class DisplayElement extends EventEmitter { return null } + get x() { return this.getDep('x') } + set x(v) { return this.setDep('x', v) } + get y() { return this.getDep('y') } + set y(v) { return this.setDep('y', v) } + get hPadding() { return this.getDep('hPadding') } + set hPadding(v) { return this.setDep('hPadding', v) } + get vPadding() { return this.getDep('vPadding') } + set vPadding(v) { return this.setDep('vPadding', v) } + get visible() { return this.getDep('visible') } + set visible(v) { return this.setDep('visible', v) } + get absX() { if (this.parent) { return this.parent.contentX + this.x @@ -262,3 +339,7 @@ module.exports = class DisplayElement extends EventEmitter { get absTop() { return this.absY } get absBottom() { return this.absY + this.h - 1 } } + +module.exports.drawValues = Symbol('drawValues') +module.exports.lastDrawValues = Symbol('lastDrawValues') +module.exports.scheduledDraw = Symbol('scheduledDraw') |