diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/DocComponent.tsx | 2 | ||||
-rw-r--r-- | src/client/views/MarqueeAnnotator.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackedTimeline.tsx | 16 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 24 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/nodes_rts.ts | 2 |
6 files changed, 40 insertions, 19 deletions
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 99f13295d..b800ba777 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -74,6 +74,7 @@ export interface ViewBoxAnnotatableProps { fieldKey: string; layerProvider?: (doc: Doc) => boolean; active: () => boolean; + select: (isCtrlPressed: boolean) => void; whenActiveChanged: (isActive: boolean) => void; isSelected: (outsideReaction?: boolean) => boolean; rootSelected: (outsideReaction?: boolean) => boolean; @@ -145,6 +146,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); }); + this.props.select(false); return true; } } diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 8ef69802b..4ffccbc90 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -21,6 +21,7 @@ export interface MarqueeAnnotatorProps { rootDoc: Doc; down: number[]; scaling?: () => number; + containerOffset?: () => number[]; mainCont: HTMLDivElement; savedAnnotations: Dictionary<number, HTMLDivElement[]>; annotationLayer: HTMLDivElement; @@ -69,9 +70,10 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { if ((this.props.savedAnnotations.values()[0][0] as any).marqueeing) { const scale = this.props.scaling?.() || 1; const anno = this.props.savedAnnotations.values()[0][0]; + const containerOffset = this.props.containerOffset?.() || [0, 0]; const mainAnnoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title }); - if (anno.style.left) mainAnnoDoc.x = parseInt(anno.style.left) / scale; - if (anno.style.top) mainAnnoDoc.y = (NumCast(this.props.rootDoc._scrollTop) + parseInt(anno.style.top)) / scale; + if (anno.style.left) mainAnnoDoc.x = (parseInt(anno.style.left) - containerOffset[0]) / scale; + if (anno.style.top) mainAnnoDoc.y = (parseInt(anno.style.top) - containerOffset[1] + NumCast(this.props.rootDoc._scrollTop)) / scale; if (anno.style.height) mainAnnoDoc._height = parseInt(anno.style.height) / scale; if (anno.style.width) mainAnnoDoc._width = parseInt(anno.style.width) / scale; mainAnnoDoc.group = mainAnnoDoc; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 02e88d939..a59ac109f 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { action, computed, IReactionDisposer, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; -import { Doc, Opt } from "../../../fields/Doc"; +import { Doc, Opt, DocListCast } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; import { listSpec, makeInterface } from "../../../fields/Schema"; @@ -31,6 +31,7 @@ export type CollectionStackedTimelineProps = { isChildActive: () => boolean; startTag: string; endTag: string; + fieldKeySuffix?: string; }; @observer @@ -46,7 +47,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument @observable _markerEnd: number = 0; get duration() { return this.props.duration; } - @computed get anchorDocs() { return this.childDocs; } + @computed get anchorDocs() { return this.props.fieldKeySuffix ? this.childDocs.concat(...DocListCast(this.rootDoc[this.props.fieldKey + this.props.fieldKeySuffix])) : this.childDocs; } @computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); } @computed get selectionContainer() { return CollectionStackedTimeline.SelectingRegion !== this ? (null) : <div className="collectionStackedTimeline-selector" style={{ @@ -71,7 +72,10 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument } anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag])); - anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this.props.endTag], val)); + anchorEnd = (anchor: Doc, val: any = null) => { + const endVal = NumCast(anchor[this.props.endTag], val); + return NumCast(anchor._timecodeToHide, endVal === undefined ? null : endVal); + } toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.duration, screen_delta / width * this.duration)); rangeClickScript = () => CollectionStackedTimeline.RangeScript; labelClickScript = () => CollectionStackedTimeline.LabelScript; @@ -88,7 +92,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument this._markerStart = this._markerEnd = this.currentTime; CollectionStackedTimeline.SelectingRegion = this; } else { - this.createAnchor(this._markerStart, this.currentTime); + CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime); CollectionStackedTimeline.SelectingRegion = undefined; } } @@ -132,13 +136,13 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument this._markerEnd = tmp; } if (!isClick) { - CollectionStackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createAnchor(this._markerStart, this._markerEnd); + CollectionStackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15) && CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag); } (!isClick || !wasSelecting) && (CollectionStackedTimeline.SelectingRegion = undefined); }), (e, doubleTap) => { this.props.select(false); - e.shiftKey && this.createAnchor(this.currentTime); + e.shiftKey && CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime); !wasPlaying && doubleTap && this.props.Play(); }, this.props.isSelected(true) || this.props.isChildActive(), undefined, diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 57b5f3ec7..692eaae66 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -326,15 +326,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD playing = () => this.audioState === "playing"; playLink = (link: Doc) => { + const stack = this._stackedTimeline.current; if (link.annotationOn === this.rootDoc) { - if (this.layoutDoc.playOnSelect) this.playFrom(this._stackedTimeline.current?.anchorStart(link) || 0, this._stackedTimeline.current?.anchorEnd(link)); - else this._ele!.currentTime = this.layoutDoc._currentTimecode = (this._stackedTimeline.current?.anchorStart(link) || 0); + if (this.layoutDoc.playOnSelect) this.playFrom(stack?.anchorStart(link) || 0, stack?.anchorEnd(link)); + else this._ele!.currentTime = this.layoutDoc._currentTimecode = (stack?.anchorStart(link) || 0); } else { this.links.filter(l => l.anchor1 === link || l.anchor2 === link).forEach(l => { const { la1, la2 } = this.getLinkData(l); - const startTime = this._stackedTimeline.current?.anchorStart(la1) || this._stackedTimeline.current?.anchorStart(la2); - const endTime = this._stackedTimeline.current?.anchorEnd(la1) || this._stackedTimeline.current?.anchorEnd(la2); + const startTime = stack?.anchorStart(la1) || stack?.anchorStart(la2); + const endTime = stack?.anchorEnd(la1) || stack?.anchorEnd(la2); if (startTime !== undefined) { if (this.layoutDoc.playOnSelect) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime); else this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 79d584a1d..8a1cefbd9 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -70,7 +70,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD } getAnchor = () => { - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "videoStart", "videoEnd", Cast(this.layoutDoc._currentTimecode, "number", null)) || this.rootDoc; + return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey + "-timeline", "videoStart", "videoEnd", Cast(this.layoutDoc._currentTimecode, "number", null)) || this.rootDoc; } choosePath(url: string) { @@ -167,7 +167,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD //convert to desired file format const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png' // if you want to preview the captured image, - const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"))); + const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, ""); + const filename = path.basename(encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"))); VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && this.createRealSummaryLink(returnedFilename)); } @@ -179,14 +180,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD const height = this.layoutDoc._height || 0; const imageSummary = Docs.Create.ImageDocument(url, { _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc), - x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), + x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), isLinkButton: true, _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-" }); Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc)); Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc)); - imageSummary.isLinkButton = true; this.props.addDocument?.(imageSummary); - DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot"); + DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot"); } @action @@ -495,6 +495,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD renderDepth={this.props.renderDepth + 1} startTag={"videoStart"} endTag={"videoEnd"} + fieldKeySuffix={"-timeline"} focus={emptyFunction} bringToFront={emptyFunction} CollectionView={undefined} @@ -536,6 +537,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling(); return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent); } + marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100; + marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0]; render() { const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); @@ -570,7 +573,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD {this.annotationLayer} {this.renderTimeline} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocWithTimecode} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />} + <MarqueeAnnotator + rootDoc={this.rootDoc} + down={this._marqueeing} + scaling={this.marqueeFitScaling} + containerOffset={this.marqueeOffset} + addDocument={this.addDocWithTimecode} + finishMarquee={this.finishMarquee} + savedAnnotations={this._savedAnnotations} + annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} + />} </div> </div >); } diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts index f5bc05a2d..722c0a836 100644 --- a/src/client/views/nodes/formattedText/nodes_rts.ts +++ b/src/client/views/nodes/formattedText/nodes_rts.ts @@ -36,7 +36,7 @@ export const nodes: { [index: string]: NodeSpec } = { "data-audioid": node.attrs.audioId, }, formatAudioTime(node.attrs.timeCode.toString()) - ] + ]; }, parseDOM: [ { |