import type {Interaction} from './useCanvasInteraction';
import type {Size} from './geometry';
import memoize from 'memoize-one';
import {View} from './View';
import {zeroPoint} from './geometry';
import {DPR} from '../content-views/constants';
export type ViewRefs = {
activeView: View | null,
hoveredView: View | null,
};
function configureRetinaCanvas(
canvas: HTMLCanvasElement,
height: number,
width: number,
) {
canvas.width = width * DPR;
canvas.height = height * DPR;
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
}
const getCanvasContext = memoize(
(
canvas: HTMLCanvasElement,
height: number,
width: number,
scaleCanvas: boolean = true,
): CanvasRenderingContext2D => {
const context = canvas.getContext('2d', {alpha: false});
if (scaleCanvas) {
configureRetinaCanvas(canvas, height, width);
context.scale(DPR, DPR);
}
return context;
},
);
type ResetHoveredEventFn = () => void;
export class Surface {
rootView: ?View;
_context: ?CanvasRenderingContext2D;
_canvasSize: ?Size;
_resetHoveredEvent: ResetHoveredEventFn;
_viewRefs: ViewRefs = {
activeView: null,
hoveredView: null,
};
constructor(resetHoveredEvent: ResetHoveredEventFn) {
this._resetHoveredEvent = resetHoveredEvent;
}
hasActiveView(): boolean {
return this._viewRefs.activeView !== null;
}
setCanvas(canvas: HTMLCanvasElement, canvasSize: Size) {
this._context = getCanvasContext(
canvas,
canvasSize.height,
canvasSize.width,
);
this._canvasSize = canvasSize;
if (this.rootView) {
this.rootView.setNeedsDisplay();
}
}
displayIfNeeded() {
const {rootView, _canvasSize, _context} = this;
if (!rootView || !_context || !_canvasSize) {
return;
}
rootView.setFrame({
origin: zeroPoint,
size: _canvasSize,
});
rootView.setVisibleArea({
origin: zeroPoint,
size: _canvasSize,
});
rootView.displayIfNeeded(_context, this._viewRefs);
}
getCurrentCursor(): string | null {
const {activeView, hoveredView} = this._viewRefs;
if (activeView !== null) {
return activeView.currentCursor;
} else if (hoveredView !== null) {
return hoveredView.currentCursor;
} else {
return null;
}
}
handleInteraction(interaction: Interaction) {
const rootView = this.rootView;
if (rootView != null) {
const viewRefs = this._viewRefs;
switch (interaction.type) {
case 'mousemove':
case 'wheel-control':
case 'wheel-meta':
case 'wheel-plain':
case 'wheel-shift':
const hoveredView = viewRefs.hoveredView;
viewRefs.hoveredView = null;
rootView.handleInteractionAndPropagateToSubviews(
interaction,
viewRefs,
);
if (hoveredView !== null && viewRefs.hoveredView === null) {
this._resetHoveredEvent();
}
break;
default:
rootView.handleInteractionAndPropagateToSubviews(
interaction,
viewRefs,
);
break;
}
}
}
}