From c352e3535636269243e26156c99d0438a3177c37 Mon Sep 17 00:00:00 2001 From: Lionel Han <47760119+IGoByJoe@users.noreply.github.com> Date: Thu, 3 Dec 2020 01:15:52 -0800 Subject: working timeline for video --- src/client/views/nodes/VideoBox.scss | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) (limited to 'src/client/views/nodes/VideoBox.scss') diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index 05714f665..b32ee7581 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -9,6 +9,129 @@ .inkingCanvas-paths-markers { opacity : 0.4; // we shouldn't have to do this, but since chrome crawls to a halt with z-index unset in videoBox-content, this is a workaround } + + .audiobox-timeline { + position: absolute; + height: 20%; + width: 100%; + bottom: -60px; + background: white; + border: gray solid 1px; + border-radius: 3px; + z-index: 1000; + overflow: hidden; + + .audiobox-current { + width: 1px; + height: 100%; + background-color: red; + position: absolute; + top: 0px; + } + + .audiobox-container { + position: absolute; + width: 10px; + top: 2.5%; + height: 0px; + background: lightblue; + border-radius: 5px; + // box-shadow: black 2px 2px 1px; + opacity: 0.3; + z-index: 500; + border-style: solid; + border-color: darkblue; + border-width: 1px; + } + + .audiobox-marker-container, + .audiobox-marker-minicontainer { + position: absolute; + width: 10px; + height: 10px; + top: 2.5%; + background: gray; + border-radius: 50%; + box-shadow: black 2px 2px 1px; + overflow: visible; + cursor: pointer; + + .audiobox-marker { + position: relative; + height: 100%; + // height: calc(100% - 15px); + width: 100%; + //margin-top: 15px; + } + + .audio-marker:hover { + border: orange 2px solid; + } + } + + .audiobox-marker-container1, + .audiobox-marker-minicontainer { + position: absolute; + width: 10px; + height: 90%; + top: 2.5%; + background: gray; + border-radius: 5px; + box-shadow: black 2px 2px 1px; + opacity: 0.3; + + .audiobox-marker { + position: relative; + height: calc(100% - 15px); + margin-top: 15px; + } + + .audio-marker:hover { + border: orange 2px solid; + } + + .resizer { + position: absolute; + right: 0; + cursor: ew-resize; + height: 100%; + width: 2px; + z-index: 100; + } + + .click { + position: relative; + height: 100%; + width: 100%; + z-index: 100; + } + + .left-resizer { + position: absolute; + left: 0; + cursor: ew-resize; + height: 100%; + width: 2px; + z-index: 100; + } + } + + .audiobox-marker-container1:hover, + .audiobox-marker-minicontainer:hover { + opacity: 0.8; + } + + .audiobox-marker-minicontainer { + width: 5px; + border-radius: 1px; + + .audiobox-marker { + position: relative; + height: calc(100% - 8px); + margin-top: 8px; + } + } + } } .videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen, @@ -48,6 +171,15 @@ transform-origin: left top; pointer-events:all; } + +.timeline-button { + position: absolute; + bottom: 15px; + right: 17%; + color: lightgrey; + width: 20px; + +} .videoBox-play { width: 25px; height: 20px; -- cgit v1.2.3-70-g09d2 From b9326dfc3e15683190a7d520daca6791ef049dea Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 22 Jan 2021 14:12:41 -0500 Subject: fixed up videoBox timeline. changed video annotations to use displayTimecode and undisplayTimecode --- src/client/documents/Documents.ts | 3 +- src/client/views/DocComponent.tsx | 6 +- src/client/views/MarqueeAnnotator.tsx | 4 +- src/client/views/StyleProvider.tsx | 3 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 7 +- src/client/views/nodes/AudioBox.tsx | 13 +- src/client/views/nodes/LabelBox.tsx | 2 +- src/client/views/nodes/VideoBox.scss | 24 ++-- src/client/views/nodes/VideoBox.tsx | 143 +++++++++++---------- src/client/views/nodes/WebBox.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- .../views/nodes/formattedText/RichTextMenu.tsx | 2 +- 12 files changed, 119 insertions(+), 96 deletions(-) (limited to 'src/client/views/nodes/VideoBox.scss') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index df08345f9..286b7afa9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -152,6 +152,7 @@ export interface DocumentOptions { _currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds _currentFrame?: number; // the current frame of a frame-based collection (e.g., progressive slide) displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) + undisplayTimecode?: number; // the time that a document should be hidden lastFrame?: number; // the last frame of a frame-based collection (e.g., progressive slide) activeFrame?: number; // the active frame of a document in a frame base collection appearFrame?: number; // the frame in which the document appears @@ -227,7 +228,7 @@ export interface DocumentOptions { isLabel?: boolean; // whether the document is a label or not (video / audio) useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox audioStart?: number; // the time frame where the audio should begin playing - audioEnd?: number; // the time frame where the audio should stop playing + audioEnd?: number; // the time frame where the audio should stop playing border?: string; //for searchbox hovercolor?: string; } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 2c7d15ae0..db0b626a1 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -124,7 +124,7 @@ export function ViewBoxAnnotatableComponent

effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); @@ -132,13 +132,13 @@ export function ViewBoxAnnotatableComponent

doc.isPushpin = doc.annotationOn = undefined); const targetDataDoc = this.dataDoc; - const value = DocListCast(targetDataDoc[this.annotationKey]); + const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); const toRemove = value.filter(v => docs.includes(v)); if (toRemove.length !== 0) { const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc; toRemove.forEach(doc => { - Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey + "-annotations", doc); + Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); }); return true; diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 0ab2d1ecf..8ef69802b 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -163,13 +163,13 @@ export class MarqueeAnnotator extends React.Component { const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100); FormattedTextBox.SelectOnLoad = target[Id]; return target; - } + }; const anchorCreator = () => { const annoDoc = this.highlight("rgba(173, 216, 230, 0.75)"); // hyperlink color annoDoc.isLinkButton = true; // prevents link button from showing up --- maybe not a good thing? this.props.addDocument(annoDoc); return annoDoc; - } + }; DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.rootDoc, anchorCreator, targetCreator), e.pageX, e.pageY, { dragComplete: e => { if (!e.aborted && e.annoDragData && e.annoDragData.annotationDocument && e.annoDragData.dropDocument && !e.linkDocument) { diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 1d822439a..95a28b33e 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -108,9 +108,10 @@ export function DefaultStyleProvider(doc: Opt, props: Opt DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true)); } - public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document._currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } + public isCurrent(doc: Doc) { + 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); + } public getActiveDocuments = () => { return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 77777ff76..4ddb0502b 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -29,6 +29,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment"; import { LinkDocPreview } from "./LinkDocPreview"; import "./AudioBox.scss"; +import { Id } from "../../../fields/FieldSymbols"; declare class MediaRecorder { // whatever MediaRecorder has @@ -390,9 +391,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent {this.waveform} - {drawMarkers.map((d, i) => { + {drawMarkers.map(d => { const m = d.marker; const left = NumCast(m.audioStart) / this.audioDuration * timelineContentWidth; const top = d.level / maxLevel * timelineContentHeight; const timespan = m.audioEnd === undefined ? 10 / timelineContentWidth * this.audioDuration : NumCast(m.audioEnd) - NumCast(m.audioStart); return this.layoutDoc.hideMarkers ? (null) : -

{ this.playFrom(NumCast(m.audioStart), Cast(m.audioEnd, "number", null)); e.stopPropagation(); }} > {this.renderMarker(m, this.rangeClickScript, this.rangePlayScript, diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 3448a4abd..87d5b07a2 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -67,7 +67,7 @@ export class LabelBox extends ViewBoxBaseComponent !this.paramsDoc[p]); params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ... - const label = StrCast(this.rootDoc[this.fieldKey], StrCast(this.rootDoc.title)); + const label = typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); return (
this.clicked = !this.clicked)} diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index 76edda847..8bba5d1ff 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -13,15 +13,13 @@ .audiobox-timeline { position: absolute; - height: 20%; width: 100%; - bottom: 0px; - background: white; + background: beige; border: gray solid 1px; border-radius: 3px; z-index: 1000; overflow: hidden; - left: 0px; + bottom: 0; .audiobox-current { width: 1px; @@ -46,18 +44,23 @@ border-width: 1px; } - .audiobox-marker-container, + .audiobox-marker-timeline, .audiobox-marker-minicontainer { position: absolute; width: 10px; height: 10px; top: 2.5%; - background: gray; border-radius: 50%; box-shadow: black 2px 2px 1px; overflow: visible; cursor: pointer; + .left-resizer { + background: dimgrey; + } + .resizer { + background: dimgrey; + } .audiobox-marker { position: relative; height: 100%; @@ -77,10 +80,8 @@ width: 10px; height: 90%; top: 2.5%; - background: gray; border-radius: 5px; box-shadow: black 2px 2px 1px; - opacity: 0.3; .audiobox-marker { position: relative; @@ -94,10 +95,12 @@ .resizer { position: absolute; + top: 0; right: 0; + pointer-events: all; cursor: ew-resize; height: 100%; - width: 2px; + width: 10px; z-index: 100; } @@ -111,9 +114,10 @@ .left-resizer { position: absolute; left: 0; + top: 0; cursor: ew-resize; height: 100%; - width: 2px; + width: 10px; z-index: 100; } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 2ac105545..608d7daa3 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -26,10 +26,13 @@ import { List } from "../../../fields/List"; import { DocumentView } from "./DocumentView"; import { LinkDocPreview } from "./LinkDocPreview"; import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment"; -import { Transform } from "../../util/Transform"; import { StyleProp } from "../StyleProvider"; import { computedFn } from "mobx-utils"; import { DocumentManager } from "../../util/DocumentManager"; +import { Dictionary } from "typescript-collections"; +import { MarqueeAnnotator } from "../MarqueeAnnotator"; +import { Id } from "../../../fields/FieldSymbols"; +import { LabelBox } from "./LabelBox"; const path = require('path'); export const timeSchema = createSchema({ @@ -47,16 +50,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent = React.createRef(); + private _annotationLayer: React.RefObject = React.createRef(); + @observable _marqueeing: number[] | undefined; + @observable _savedAnnotations: Dictionary = new Dictionary(); _play: any = null; _timeline: Opt; _audioRef = React.createRef(); @@ -75,7 +78,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { @@ -240,7 +243,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent this.player && this.Play(), delay); - setTimeout(() => { this.Document._videoStart = undefined; }, 10 + delay); + setTimeout(() => this.Document._videoStart = undefined, 10 + delay); } } }, @@ -349,9 +352,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent Not supported. - {this.uIButtons}
- {this.renderTimeline}
; } @@ -412,9 +413,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent,
@@ -557,17 +560,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent([marker]); + this.dataDoc[this.annotationKey + "-timeline"] = new List([marker]); } } @@ -609,14 +614,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + getLevel = (m: any, placed: { videoStart: number, videoEnd: number, level: number }[]) => { const timelineContentWidth = this.props.PanelWidth(); - const x1 = m.audioStart; - const x2 = m.audioEnd === undefined ? m.audioStart + 10 / timelineContentWidth * this.videoDuration : m.audioEnd; + const x1 = m.displayTimecode; + const x2 = m.undisplayTimecode === undefined ? m.displayTimecode + 10 / timelineContentWidth * this.videoDuration : m.undisplayTimecode; let max = 0; const overlappedLevels = new Set(placed.map(p => { - const y1 = p.audioStart; - const y2 = p.audioEnd; + const y1 = p.videoStart; + const y2 = p.videoEnd; if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) || (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) { max = Math.max(max, p.level); @@ -626,23 +631,25 @@ export class VideoBox extends ViewBoxAnnotatableComponent= 0; j--) !overlappedLevels.has(j) && (level = j); - placed.push({ audioStart: x1, audioEnd: x2, level }); + placed.push({ videoStart: x1, videoEnd: x2, level }); return level; } // renders the markers as a document - renderInner = computedFn(function (this: VideoBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) { + renderInner = computedFn(function (this: VideoBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number, annotationKey: string) { const marker = observable({ view: undefined as any }); return { - marker, view: marker.view = r)} + marker, view: marker.view = r)} Document={mark} + DataDoc={undefined} PanelWidth={() => width} PanelHeight={() => height} rootSelected={returnFalse} LayoutTemplate={undefined} + LayoutTemplateString={LabelBox.LayoutString("data")} ContainingCollectionDoc={this.props.Document} - removeDocument={this.removeDocument} - ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x - 4, -y - 3)} + removeDocument={(doc: Doc | Doc[]) => this.removeDocument(doc, annotationKey)} + ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).translate(-x - 4, -y - 3)} parentActive={(out) => this.props.isSelected(out) || this._isChildActive} whenActiveChanged={action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive))} onClick={script} @@ -653,8 +660,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) { - const inner = this.renderInner(mark, script, doublescript, x, y, width, height); + renderMarker = computedFn(function (this: VideoBox, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number, annotationKey: string) { + const inner = this.renderInner(mark, script, doublescript, x, y, width, height, annotationKey); return <> {inner.view} {!inner.marker.view || !SelectionManager.IsSelected(inner.marker.view) ? (null) : @@ -667,32 +674,34 @@ export class VideoBox extends ViewBoxAnnotatableComponent ({ level: this.getLevel(m, overlaps), marker: m })); + const timelineContentHeight = this.props.PanelHeight() * VideoBox.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) : -
{ e.stopPropagation(); e.preventDefault(); }} style={{ height: `${VideoBox.heightPercent}%` }} - onPointerDown={e => e.button === 0 && !e.ctrlKey && this.onPointerDownTimeline(e)}> - {drawMarkers.map((d, i) => { +
{ 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); + } + }}> + {drawMarkers.map(d => { const m = d.marker; - const left = NumCast(m.audioStart) / this.videoDuration; - const l = `${NumCast(m.audioStart) / this.videoDuration * 100}%`; + const start = NumCast(m.displayTimecode, NumCast(m.displayTimecode, null)); + const left = start / this.videoDuration * timelineContentWidth; const top = d.level / maxLevel * timelineContentHeight; - const timespan = m.audioEnd === undefined ? 10 / timelineContentWidth * this.videoDuration : NumCast(m.audioEnd) - NumCast(m.audioStart); + const timespan = m.undisplayTimecode === undefined ? 10 / timelineContentWidth * this.videoDuration : NumCast(m.undisplayTimecode) - NumCast(m.displayTimecode); return this.layoutDoc.hideMarkers ? (null) : -
{ this.playFrom(NumCast(m.audioStart), Cast(m.audioEnd, "number", null)); e.stopPropagation(); }} > +
{ this.playFrom(start, Cast(m.undisplayTimecode, "number", null)); e.stopPropagation(); }} > {this.renderMarker(m, this.rangeClickScript, this.rangePlayScript, left, - top, + top + (this.props.PanelHeight() - timelineContentHeight), timelineContentWidth * timespan / this.videoDuration, - timelineContentHeight / maxLevel)} + timelineContentHeight / maxLevel, this.annotationKey + (m.useLinkSmallAnchor ? "-timeline" : ""))}
; })} {this.selectionContainer} @@ -703,13 +712,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - DocListCast(this.dataDoc[this.annotationKey]).filter(marker => this.isSame(marker, m)).forEach(marker => - this._left ? marker.audioStart = time : marker.audioEnd = time); + this.markerDocs.filter(marker => this.isSame(marker, m)).forEach(marker => + this._left ? marker.displayTimecode = time : marker.undisplayTimecode = time); } // checks if the two markers are the same with start and end time isSame = (m1: any, m2: any) => { - return m1.audioStart === m2.audioStart && m1.audioEnd === m2.audioEnd; + return m1.displayTimecode === m2.displayTimecode && m1.undisplayTimecode === m2.undisplayTimecode; } // returns the blue container when dragging @@ -753,17 +762,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent [this.youtubeVideoId ? this.youtubeContent : this.content]; @computed get annotationLayer() { - return
; + return
; } marqueeDown = action((e: React.PointerEvent) => { if (!e.altKey && e.button === 0 && this.active(true)) this._marqueeing = [e.clientX, e.clientY]; - }) + }); finishMarquee = action(() => { this._marqueeing = undefined; this.props.select(true); - }) + }); render() { const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); @@ -781,7 +790,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent 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} @@ -790,14 +801,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent {this.contentFunc} + {this.uIButtons} + {this.annotationLayer} + {this.renderTimeline} + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : + }
-<<<<<<< HEAD -======= - {this.uIButtons} - {this.annotationLayer} - {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - } ->>>>>>> 10b759d2bd09af3a8e8a4effbc8fd2312dd873d2
); } } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 69f797880..37f268823 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -194,7 +194,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { e.preventDefault(); } + onUrlDragover = (e: React.DragEvent) => { e.preventDefault(); }; @undoBatch @action @@ -280,8 +280,8 @@ export class WebBox extends ViewBoxAnnotatableComponent { this._ignore = e.timeStamp; } - onPrePointer = (e: React.PointerEvent) => { this._ignore = e.timeStamp; } + onPreWheel = (e: React.WheelEvent) => { this._ignore = e.timeStamp; }; + onPrePointer = (e: React.PointerEvent) => { this._ignore = e.timeStamp; }; onPostPointer = (e: React.PointerEvent) => { if (this._ignore !== e.timeStamp) e.stopPropagation(); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ac5ea66ff..7348ebdd2 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -230,7 +230,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.rootDoc.title, 0, 0, 100, 100); FormattedTextBox.SelectOnLoad = target[Id]; return target; - } + }; DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.rootDoc, () => this.rootDoc, targetCreator), e.pageX, e.pageY, { dragComplete: e => { diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 992194e2b..dc630af74 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -604,7 +604,7 @@ export class RichTextMenu extends AntimodeMenu { e.stopPropagation(); self.TextView.endUndoTypingBatch(); UndoManager.RunInBatch(() => self.view && self.fillBrush(self.view.state, self.view.dispatch), "rt brush"); - } + }; let label = "Stored marks: "; if (this.brushMarks && this.brushMarks.size > 0) { -- cgit v1.2.3-70-g09d2 From d0025200b23f57ec25d3452d425de8725a032c6a Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 22 Jan 2021 21:14:37 -0500 Subject: fixed selection bounds for video box annotations, especially when in full screen view. --- src/client/views/collections/TabDocView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- src/client/views/nodes/AudioBox.tsx | 18 ++-- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/VideoBox.scss | 20 ++-- src/client/views/nodes/VideoBox.tsx | 113 +++++++++++---------- 6 files changed, 88 insertions(+), 77 deletions(-) (limited to 'src/client/views/nodes/VideoBox.scss') 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 { } 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 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 { - 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 func().result === true ? this.props.select(false) : "", "on click"); } else func(); }; if (this.onDoubleClickHandler) { @@ -935,7 +935,9 @@ export class DocumentView extends React.Component { 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 = React.createRef(); private _annotationLayer: React.RefObject = React.createRef(); - @observable _marqueeing: number[] | undefined; - @observable _savedAnnotations: Dictionary = new Dictionary(); _play: any = null; _timeline: Opt; _audioRef = React.createRef(); @@ -68,7 +66,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent = []; _duration = 0; _start: boolean = true; - private _currMarker: any; + _currMarker: any; + @observable _marqueeing: number[] | undefined; + @observable _savedAnnotations: Dictionary = new Dictionary(); + @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 { @@ -311,7 +313,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { const field = Cast(this.dataDoc[this.props.fieldKey], VideoField); if (field) { @@ -337,10 +339,9 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading
:
-
+
,
, @@ -520,9 +517,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent 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 { - 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 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 ({ 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) : -
{ if (this._isChildActive || this.props.isSelected()) { e.stopPropagation(); e.preventDefault(); } }} +
{ + 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 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 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 (
-
- 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} - +
+
+ + {this.contentFunc} + +
{this.uIButtons} {this.annotationLayer} {this.renderTimeline} -- cgit v1.2.3-70-g09d2