diff --git a/FlipViewer/src/FlipBook.tsx b/FlipViewer/src/FlipBook.tsx index 18ee920..9afe9a9 100644 --- a/FlipViewer/src/FlipBook.tsx +++ b/FlipViewer/src/FlipBook.tsx @@ -2,7 +2,7 @@ import { createRoot } from 'react-dom/client'; import styles from './styles.module.css'; import React, { createRef } from 'react'; import { renderImage } from "./Render"; -import { ImageContainer, OnClickHandler } from './ImageContainer'; +import { ImageContainer, OnClickHandler, OnWheelHandler, OnMouseOverHandler, ImageContainerState, OnKeyHandler, setKeyPressed } from './ImageContainer'; import { ToneMapControls } from './ToneMapControls'; import { MethodList } from './MethodList'; import { Tools } from './Tools'; @@ -11,9 +11,35 @@ import { ToneMapSettings, ZoomLevel } from './flipviewer'; const UPDATE_INTERVAL_MS = 100; +// Registry to update flipbooks ------------- +export type BookRef = React.RefObject; +const registry = new Map>(); + +export function getBooks(key: string): BookRef[] { + return Array.from(registry.get(key) ?? new Set()); +} +export function registerBook(key: string, ref: BookRef) { + if (!key) return; + const set = registry.get(key) ?? new Set(); + set.add(ref); + registry.set(key, set); +} +export function unregisterBook(key: string, ref: BookRef) { + const set = registry.get(key); + if (!set) return; + set.delete(ref); + if (set.size === 0) registry.delete(key); +} +// --------------------------------------------------------------- + +// to get only key presses and not is down state +// Idea is to only fire events if state of key changes +const keyPressed = new Set(); + export class ToneMappingImage { currentTMO: string; dirty: boolean; + isPixelUpdate: boolean; canvas: HTMLCanvasElement; pixels: Float32Array | ImageData; @@ -25,9 +51,10 @@ export class ToneMappingImage { let hdrImg = this; setInterval(function() { - if (!hdrImg.dirty) return; - hdrImg.dirty = false; + if (!hdrImg.dirty || hdrImg.isPixelUpdate) + return; renderImage(hdrImg.canvas, hdrImg.pixels, hdrImg.currentTMO); + hdrImg.dirty = false; onAfterRender(); }, UPDATE_INTERVAL_MS) } @@ -35,16 +62,46 @@ export class ToneMappingImage { this.currentTMO = tmo; this.dirty = true; } + setPixels(p: Float32Array | ImageData) { + this.isPixelUpdate = true; + this.pixels = p; + this.dirty = false; + renderImage(this.canvas, this.pixels, this.currentTMO); + this.isPixelUpdate = false; + } } + type SelectUpdateFn = (groupName: string, newIdx: number) => void; var selectUpdateListeners: SelectUpdateFn[] = []; + +type TMOUpdateFn = (groupName: string, newTMOSettings: ToneMapSettings) => void; +var tmoUpdateListeners: TMOUpdateFn[] = []; + +type imageConStateUpdateFn = (groupName: string, newImgConState: ImageContainerState) => void; +var imgConStateUpdateListeners: imageConStateUpdateFn[] = []; + export function SetGroupIndex(groupName: string, newIdx: number) { for (let fn of selectUpdateListeners) fn(groupName, newIdx); } + +export function SetGroupTMOSettings(groupName: string, newTMOSettings: ToneMapSettings) +{ + for (let fn of tmoUpdateListeners) + fn(groupName, newTMOSettings); +} + + +export function SetGroupImageContainerSettings(groupName: string, newImgConState: ImageContainerState) +{ + for (let fn of imgConStateUpdateListeners) + fn(groupName, newImgConState); +} + + export interface FlipProps { names: string[]; width: number; @@ -57,11 +114,15 @@ export interface FlipProps { initialTMOOverrides: ToneMapSettings[]; style?: React.CSSProperties; onClick?: OnClickHandler; + onWheel?: OnWheelHandler; + onMouseOver?: OnMouseOverHandler; + onKeyIC?: OnKeyHandler; groupName?: string; hideTools: boolean; + keyStr: string; } -interface FlipState { +export interface FlipState { selectedIdx: number; popupContent?: React.ReactNode; popupDurationMs?: number; @@ -75,6 +136,7 @@ export class FlipBook extends React.Component { constructor(props : FlipProps) { super(props); + this.state = { selectedIdx: 0, hideTools: props.hideTools @@ -85,10 +147,35 @@ export class FlipBook extends React.Component { this.tools = createRef(); this.onKeyDown = this.onKeyDown.bind(this); + this.onKeyUp = this.onKeyUp.bind(this); this.onSelectUpdate = this.onSelectUpdate.bind(this); + this.onTMOUpdate = this.onTMOUpdate.bind(this); + } + + onKeyUp(evt: React.KeyboardEvent) { + // c# listener + if(this.props.onKeyIC && keyPressed.has(evt.key)) + { + keyPressed.delete(evt.key); + evt.preventDefault(); + + if(keyPressed.size == 0) + setKeyPressed(false); + + this.props.onKeyIC(this.state.selectedIdx, this.props.keyStr, evt.key, false); + } } onKeyDown(evt: React.KeyboardEvent) { + // c# listener + if(this.props.onKeyIC && !keyPressed.has(evt.key)) + { + keyPressed.add(evt.key); + evt.preventDefault(); + setKeyPressed(true); + this.props.onKeyIC(this.state.selectedIdx, this.props.keyStr, evt.key, true); + } + let newIdx = this.state.selectedIdx; if (evt.key === "ArrowLeft" || evt.key === "ArrowDown") { newIdx = this.state.selectedIdx - 1; @@ -138,7 +225,13 @@ export class FlipBook extends React.Component { evt.stopPropagation(); } - if (evt.key === "r") { + if (evt.ctrlKey && evt.key === 'r') { + this.tmoCtrls.current.state.globalSettings.exposure = 0; + evt.stopPropagation(); + evt.preventDefault(); + } + + if (!evt.ctrlKey && evt.key === "r") { this.reset(); evt.stopPropagation(); } @@ -147,6 +240,9 @@ export class FlipBook extends React.Component { this.setState({hideTools: !this.state.hideTools}); evt.stopPropagation(); } + + + this.updateTMOSettings(this.tmoCtrls.current.state.globalSettings); } reset() { @@ -236,6 +332,13 @@ export class FlipBook extends React.Component { else this.setState({selectedIdx: newIdx}); } + + updateTMOSettings(newTMOSettings: ToneMapSettings){ + if (this.props.groupName) SetGroupTMOSettings(this.props.groupName, newTMOSettings); + else this.tmoCtrls.current.applySettings(newTMOSettings); + + } + render(): React.ReactNode { let popup = null; if (this.state.popupContent) { @@ -249,7 +352,7 @@ export class FlipBook extends React.Component { } return ( -
+
{ selectedIdx={this.state.selectedIdx} onZoom={(zoom) => this.tools.current.onZoom(zoom)} onClick={this.props.onClick} + onWheel={this.props.onWheel} + onMouseOver={this.props.onMouseOver} + onStateChange={(st) => this.onImageContainerUpdate(st)} > {popup}