@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
167 lines (122 loc) • 4.51 kB
JavaScript
import Rectangle from "../../core/geom/2d/Rectangle.js";
import { VisualTip } from "./VisualTip.js";
import Signal from "../../core/events/signal/Signal.js";
import ObservedBoolean from "../../core/model/ObservedBoolean.js";
import { DomSizeObserver } from "../util/DomSizeObserver.js";
import { SignalBinding } from "../../core/events/signal/SignalBinding.js";
import { MouseEvents } from "../../engine/input/devices/events/MouseEvents.js";
export class DomTooltipObserver {
/**
*
* @param {View} view
* @param {function} factory
* @param {*} [factoryContext]
*/
constructor(view, factory, factoryContext) {
/**
*
* @type {View}
*/
this.view = view;
/**
*
* @type {Function}
*/
this.factory = factory;
const tipTargetRectangle = new Rectangle();
/**
*
* @type {Rectangle}
*/
this.tipTargetRectangle = tipTargetRectangle;
/**
*
* @type {VisualTip}
*/
this.tip = new VisualTip(tipTargetRectangle, factory, factoryContext);
this.on = {
entered: new Signal(),
exited: new Signal()
};
const isEntered = this.isEntered = new ObservedBoolean(false);
const sizeObserver = new DomSizeObserver();
this.sizeObserver = sizeObserver;
this.bindings = [
new SignalBinding(view.position.onChanged, this.__copyDimensions, this),
new SignalBinding(view.size.onChanged, this.__copyDimensions, this),
new SignalBinding(sizeObserver.dimensions.position.onChanged, this.__copyDimensionsFromBoundingRect, this),
new SignalBinding(sizeObserver.dimensions.size.onChanged, this.__copyDimensionsFromBoundingRect, this)
];
this.handleMouseEnter = () => {
isEntered.set(true);
this.on.entered.send0();
sizeObserver.attach(view.el);
sizeObserver.start();
}
this.handleMouseLeave = () => {
isEntered.set(false);
sizeObserver.stop();
this.on.exited.send0();
}
}
__copyDimensionsFromBoundingRect() {
const d = this.sizeObserver.dimensions;
this.tipTargetRectangle.copy(d);
};
__copyDimensions() {
const view = this.view;
const position = view.position;
const scale = view.scale;
const size = view.size;
const r = this.tipTargetRectangle;
r.position.set(position.x, position.y);
r.size.set(size.x * scale.x, size.y * scale.y);
}
__handleViewLinked() {
const view = this.view;
const el = view.el;
//ensure that the element can capture pointer events
el.style.pointerEvents = "auto";
el.addEventListener(MouseEvents.Enter, this.handleMouseEnter);
el.addEventListener(MouseEvents.Leave, this.handleMouseLeave);
const bindings = this.bindings;
const binding_count = bindings.length;
for (let i = 0; i < binding_count; i++) {
const b = bindings[i];
b.link();
}
this.__copyDimensions();
}
__handleViewUnlinked() {
const view = this.view;
const el = view.el;
el.removeEventListener(MouseEvents.Enter, this.handleMouseEnter);
el.removeEventListener(MouseEvents.Leave, this.handleMouseLeave);
const bindings = this.bindings;
const binding_count = bindings.length;
for (let i = 0; i < binding_count; i++) {
const b = bindings[i];
b.unlink();
}
if (this.isEntered.getValue()) {
//remove tip
this.handleMouseLeave();
}
};
link() {
const view = this.view;
if (view.isLinked) {
this.__handleViewLinked();
}
view.on.linked.add(this.__handleViewLinked, this);
view.on.unlinked.add(this.__handleViewUnlinked, this);
}
unlink() {
const view = this.view;
if (view.isLinked) {
this.__handleViewUnlinked();
}
view.on.linked.remove(this.__handleViewLinked, this);
view.on.unlinked.remove(this.__handleViewUnlinked, this);
}
}