diff options
author | bobzel <zzzman@gmail.com> | 2021-01-22 21:14:37 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2021-01-22 21:14:37 -0500 |
commit | d0025200b23f57ec25d3452d425de8725a032c6a (patch) | |
tree | 843ef1f021239af4b7f82c9eee553a9978fe0f46 /src | |
parent | b9326dfc3e15683190a7d520daca6791ef049dea (diff) |
fixed selection bounds for video box annotations, especially when in full screen view.
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/TabDocView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 18 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.scss | 20 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 113 |
6 files changed, 88 insertions, 77 deletions
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 5ca069fb9..e5f05c407 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -358,7 +358,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { } active = () => this._isActive; ScreenToLocalTransform = () => { - const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0]?.firstChild as HTMLElement); + const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement); return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY); } PanelWidth = () => this._panelWidth; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index bc86ecd19..013472a04 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -137,11 +137,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } @computed get cachedCenteringShiftX(): number { const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling; - return !this.props.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / scaling : 0; // shift so pan position is at center of window for non-overlay collections + return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / this.parentScaling / scaling; // shift so pan position is at center of window for non-overlay collections } @computed get cachedCenteringShiftY(): number { const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling; - return !this.props.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / scaling : 0;// shift so pan position is at center of window for non-overlay collections + return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / this.parentScaling / scaling;// shift so pan position is at center of window for non-overlay collections } @computed get cachedGetLocalTransform(): Transform { return Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY()); @@ -200,7 +200,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const dispTime = NumCast(doc.displayTimecode, -1); const endTime = NumCast(doc.undisplayTimecode, dispTime + 1.5); const curTime = NumCast(this.Document._currentTimecode, -1); - return dispTime === -1 || (curTime > dispTime && curTime < endTime); + return dispTime === -1 || ((curTime - dispTime) >= -0.1 && curTime <= endTime); } public getActiveDocuments = () => { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 4ddb0502b..6ebd16bf3 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -91,10 +91,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD AudioBox.Instance = this; // onClick play scripts - AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!; - AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!; - AudioBox.RangePlayScript = AudioBox.RangePlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!; - AudioBox.LabelPlayScript = AudioBox.LabelPlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!; + AudioBox.RangeScript = AudioBox.RangeScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!; + AudioBox.LabelScript = AudioBox.LabelScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!; + AudioBox.RangePlayScript = AudioBox.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, this.audioStart, this.audioEnd)`, { self: Doc.name, scriptContext: "any" })!; + AudioBox.LabelPlayScript = AudioBox.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, this.audioStart)`, { self: Doc.name, scriptContext: "any" })!; } getLinkData(l: Doc) { @@ -189,18 +189,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD // play back the audio from time @action playOnClick = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.audioDuration) => { - DocumentManager.Instance.getDocumentView(anchorDoc)?.select(false); this.playFrom(seekTimeInSeconds, endTime); + return true; } // play back the audio from time @action clickMarker = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.audioDuration) => { - if (this.layoutDoc.playOnClick) this.playOnClick(anchorDoc, seekTimeInSeconds, endTime); - else { - DocumentManager.Instance.getDocumentView(anchorDoc)?.select(false); - this._ele && (this._ele.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds); - } + if (this.layoutDoc.playOnClick) return this.playOnClick(anchorDoc, seekTimeInSeconds, endTime); + this._ele && (this._ele.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds); + return true; } // play back the audio from time @action diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6217f473f..e3da48749 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -410,7 +410,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps if (!Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-undo"] as Doc) && !Doc.AreProtosEqual(this.props.Document, Doc.UserDoc()["dockedBtn-redo"] as Doc) && !this.onClickHandler.script.originalScript.includes("selectMainMenu")) { - UndoManager.RunInBatch(func, "on click"); + UndoManager.RunInBatch(() => func().result === true ? this.props.select(false) : "", "on click"); } else func(); }; if (this.onDoubleClickHandler) { @@ -935,7 +935,9 @@ export class DocumentView extends React.Component<DocumentViewProps> { PanelWidth = () => this.panelWidth; PanelHeight = () => this.panelHeight; ContentScale = () => this.nativeScaling; - screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).scale(1 / this.nativeScaling); + screenToLocalTransform = () => { + return this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).scale(1 / this.nativeScaling); + } componentDidMount() { !BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.AddView(this); diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index 8bba5d1ff..19f605278 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -183,14 +183,18 @@ pointer-events:all; } -// .timeline-button { -// position: absolute; -// bottom: 35px; -// right: 235px; -// color: lightgrey; -// width: 20px; - -// } +.timeline-button { + position: absolute; + display: flex; + align-items: center; + z-index: 1010; + bottom: 35px; + right: 235px; + color: white; + background: dimgrey; + width: 20px; + height: 20px; +} .videoBox-play { width: 25px; height: 20px; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 608d7daa3..c5e61eedd 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -49,7 +49,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD static LabelScript: ScriptField; static RangePlayScript: ScriptField; static LabelPlayScript: ScriptField; - static heightPercent = 20; // height of timeline in percent of height of videoBox. + static heightPercent = 60; // height of timeline in percent of height of videoBox. private _disposers: { [name: string]: IReactionDisposer } = {}; private _youtubePlayer: YT.Player | undefined = undefined; private _videoRef: HTMLVideoElement | null = null; @@ -58,8 +58,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD private _isResetClick = 0; private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); - @observable _marqueeing: number[] | undefined; - @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); _play: any = null; _timeline: Opt<HTMLDivElement>; _audioRef = React.createRef<HTMLDivElement>(); @@ -68,7 +66,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD _count: Array<any> = []; _duration = 0; _start: boolean = true; - private _currMarker: any; + _currMarker: any; + @observable _marqueeing: number[] | undefined; + @observable _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); + @observable _screenCapture = false; + @observable static _showControls: boolean; @observable static SelectingRegion: VideoBox | undefined = undefined; @observable _visible: boolean = false; @observable _markerEnd: number = 0; @@ -76,7 +78,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD @observable _playTimer?: NodeJS.Timeout = undefined; @observable _fullScreen = false; @observable _playing = false; - @observable static _showControls: boolean; + @computed get heightPercent() { return this.layoutDoc._showTimeline ? NumCast(this.layoutDoc._videoTimelineHeightPercent, VideoBox.heightPercent) : 100; } @computed get videoDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); } @computed get markerDocs() { return DocListCast(this.dataDoc[this.annotationKey + "-timeline"]).concat(DocListCast(this.dataDoc[this.annotationKey])); } public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); } @@ -90,10 +92,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD VideoBox.Instance = this; // onClick play scripts - VideoBox.RangeScript = VideoBox.RangeScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, scriptContext: "any" })!; - VideoBox.LabelScript = VideoBox.LabelScript || ScriptField.MakeScript(`scriptContext.clickMarker(self, this.displayTimecode)`, { self: Doc.name, scriptContext: "any" })!; - VideoBox.RangePlayScript = VideoBox.RangePlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, scriptContext: "any" })!; - VideoBox.LabelPlayScript = VideoBox.LabelPlayScript || ScriptField.MakeScript(`scriptContext.playOnClick(self, this.displayTimecode)`, { self: Doc.name, scriptContext: "any" })!; + VideoBox.RangeScript = VideoBox.RangeScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, scriptContext: "any" })!; + VideoBox.LabelScript = VideoBox.LabelScript || ScriptField.MakeFunction(`scriptContext.clickMarker(self, this.displayTimecode)`, { self: Doc.name, scriptContext: "any" })!; + VideoBox.RangePlayScript = VideoBox.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, this.displayTimecode, this.undisplayTimecode)`, { self: Doc.name, scriptContext: "any" })!; + VideoBox.LabelPlayScript = VideoBox.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(self, this.displayTimecode)`, { self: Doc.name, scriptContext: "any" })!; } videoLoad = () => { @@ -311,7 +313,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD console.log("VideoBox :" + e); } } - @observable _screenCapture = false; + specificContextMenu = (e: React.MouseEvent): void => { const field = Cast(this.dataDoc[this.props.fieldKey], VideoField); if (field) { @@ -337,10 +339,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD const field = Cast(this.dataDoc[this.fieldKey], VideoField); const interactive = Doc.GetSelectedTool() !== InkTool.None || !this.props.isSelected() ? "" : "-interactive"; const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive; - const h = this.layoutDoc._showTimeline ? `${100 - VideoBox.heightPercent}%` : "100%"; return !field ? <div>Loading</div> : <div className="container" style={{ pointerEvents: this._isChildActive || this.active() ? "all" : "none" }}> - <div className={`${style}`} style={{ width: "100%", height: h, left: "0px" }}> + <div className={`${style}`} style={{ width: "100%", height: "100%", left: "0px" }}> <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={{ height: "100%", width: "auto", display: "flex", margin: "auto" }} onCanPlay={this.videoLoad} @@ -412,13 +413,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD <FontAwesomeIcon icon="camera" size="lg" /> </div>, <div className="timeline-button" key="timeline-button" onPointerDown={this.toggleTimeline} style={{ - position: "absolute", - bottom: 0, - right: 0, - zIndex: 1001, - color: "white", - background: "dimgrey", - width: "20px" + transform: `scale(${this.scaling()})`, + right: this.scaling() * 10 - 10, + bottom: this.scaling() * 10 - 10 }}> <FontAwesomeIcon icon={this.layoutDoc._showTimeline ? "eye-slash" : "eye"} style={{ width: "100%" }} /> </div>, @@ -520,9 +517,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD toggleTimeline = (e: React.PointerEvent) => this.layoutDoc._showTimeline = !this.layoutDoc._showTimeline // ref for timeline - timelineRef = (timeline: HTMLDivElement) => { - this._timeline = timeline; - } + timelineRef = (timeline: HTMLDivElement) => { this._timeline = timeline; } // starting the drag event creating a range marker @action @@ -579,18 +574,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD // play back the video from time @action playOnClick = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.videoDuration) => { - DocumentManager.Instance.getDocumentView(anchorDoc)?.select(false); this.playFrom(seekTimeInSeconds, endTime); + return true; // select } // play back the video from time @action clickMarker = (anchorDoc: Doc, seekTimeInSeconds: number, endTime: number = this.videoDuration) => { - if (this.layoutDoc.playOnClick) this.playOnClick(anchorDoc, seekTimeInSeconds, endTime); - else { - DocumentManager.Instance.getDocumentView(anchorDoc)?.select(false); - this.player && (this.player.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds); - } + if (this.layoutDoc.playOnClick) return this.playOnClick(anchorDoc, seekTimeInSeconds, endTime); + this.player && (this.player.currentTime = this.layoutDoc._currentTimecode = seekTimeInSeconds); + return true; // select } // starting the drag event for marker resizing @@ -644,12 +637,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD DataDoc={undefined} PanelWidth={() => width} PanelHeight={() => height} + renderDepth={this.props.renderDepth + 1} rootSelected={returnFalse} LayoutTemplate={undefined} LayoutTemplateString={LabelBox.LayoutString("data")} ContainingCollectionDoc={this.props.Document} removeDocument={(doc: Doc | Doc[]) => this.removeDocument(doc, annotationKey)} - ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).translate(-x - 4, -y - 3)} + ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().scale(this.scaling()).translate(-x, -y)} parentActive={(out) => this.props.isSelected(out) || this._isChildActive} whenActiveChanged={action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive))} onClick={script} @@ -675,13 +669,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD // returns the timeline @computed get renderTimeline() { const timelineContentWidth = this.props.PanelWidth(); - const timelineContentHeight = this.props.PanelHeight() * VideoBox.heightPercent / 100; + const timelineContentHeight = this.props.PanelHeight() * (100 - this.heightPercent) / 100; const overlaps: { videoStart: number, videoEnd: number, level: number }[] = []; const drawMarkers: { level: number, marker: Doc }[] = this.markerDocs.map((m, i) => ({ level: this.getLevel(m, overlaps), marker: m })); const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2; return !this.layoutDoc._showTimeline ? (null) : - <div className="audiobox-timeline" ref={this.timelineRef} style={{ height: `${VideoBox.heightPercent}%` }} - onClick={e => { if (this._isChildActive || this.props.isSelected()) { e.stopPropagation(); e.preventDefault(); } }} + <div className="audiobox-timeline" ref={this.timelineRef} style={{ height: `${100 - this.heightPercent}%` }} + onClick={e => { + if (this._isChildActive || this.props.isSelected()) { + e.stopPropagation(); e.preventDefault(); + } + }} onPointerDown={e => { if (this._isChildActive || this.props.isSelected()) { e.button === 0 && !e.ctrlKey && this.onPointerDownTimeline(e); @@ -758,7 +756,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD rangePlayScript = () => VideoBox.RangePlayScript; labelPlayScript = () => VideoBox.LabelPlayScript; - screenToLocalTransform = () => this.props.ScreenToLocalTransform(); contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content]; @computed get annotationLayer() { @@ -774,33 +771,43 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD this.props.select(true); }); + scaling = () => this.props.scaling?.() || 1; + panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100; + panelHeight = () => this.props.PanelHeight() * this.heightPercent / 100; + screenToLocalTransform = () => { + const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling(); + return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent); + } + render() { const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); - const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad; + const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad; return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont} style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined, borderRadius }} > - <div className="videoBox-viewer" onPointerDown={this.marqueeDown}> - <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} - forceScaling={true} - fieldKey={this.annotationKey} - isAnnotationOverlay={true} - select={emptyFunction} - active={this.annotationsActive} - scaling={returnOne} - PanelWidth={() => this.props.PanelWidth() * (this.layoutDoc._showTimeline ? .8 : 1)} - PanelHeight={() => this.props.PanelHeight() * (this.layoutDoc._showTimeline ? .8 : 1)} - ScreenToLocalTransform={() => this.screenToLocalTransform().scale(this.layoutDoc._showTimeline ? 1 / .8 : 1)} - whenActiveChanged={this.whenActiveChanged} - removeDocument={this.removeDocument} - moveDocument={this.moveDocument} - addDocument={this.addDocumentWithTimestamp} - CollectionView={undefined} - renderDepth={this.props.renderDepth + 1}> - {this.contentFunc} - </CollectionFreeFormView> + <div className="videoBox-viewer" onPointerDown={this.marqueeDown} > + <div style={{ position: "absolute", width: this.panelWidth(), height: this.panelHeight(), top: 0, left: `${(100 - this.heightPercent) / 2}%` }}> + <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} + fieldKey={this.annotationKey} + isAnnotationOverlay={true} + forceScaling={true} + select={emptyFunction} + active={this.annotationsActive} + scaling={returnOne} + PanelWidth={this.panelWidth} + PanelHeight={this.panelHeight} + ScreenToLocalTransform={this.screenToLocalTransform} + whenActiveChanged={this.whenActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocumentWithTimestamp} + CollectionView={undefined} + renderDepth={this.props.renderDepth + 1}> + {this.contentFunc} + </CollectionFreeFormView> + </div> {this.uIButtons} {this.annotationLayer} {this.renderTimeline} |