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/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 +- 7 files changed, 104 insertions(+), 88 deletions(-) (limited to 'src/client/views/nodes') 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