From 30369cd78c1815a81bfe153c5a2d4551ad90dbe0 Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 2 Dec 2021 16:58:00 -0500 Subject: scrub through timeline with arrow keys, minor video fixes --- src/client/views/nodes/VideoBox.scss | 49 ++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 19 deletions(-) (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 4871599b8..a75248fa0 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,4 +1,6 @@ -.mini-viewer{ +@import "../global/globalCssVariables.scss"; + +.mini-viewer { cursor: grab; position: absolute; right: 10; @@ -14,22 +16,21 @@ height: 100%; position: relative; .videoBox-viewer { - display:flex; + display: flex; flex-direction: column; height: 100%; border-radius: inherit; - opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger + opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger + background: $dark-gray; } .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 - } - .collectionStackedTimeline { - background: beige; + 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 } + .videoBox-stackPanel { z-index: -1; width: 100%; - position: relative; + position: relative; } .videoBox-annotationLayer { @@ -43,18 +44,24 @@ } } -.videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen, -.videoBox-content, .videoBox-content-interactive, .videoBox-cont-fullScreen { +.videoBox-content-YouTube, +.videoBox-content-YouTube-fullScreen, +.videoBox-content, +.videoBox-content-interactive, +.videoBox-cont-fullScreen { width: 100%; z-index: -1; // 0; // logically this should be 0 (or unset) which would give us transparent brush strokes over videos. However, this makes Chrome crawl to a halt position: absolute; } -.videoBox-content, .videoBox-content-interactive, .videoBox-content-fullScreen { - height: Auto; +.videoBox-content, +.videoBox-content-interactive, +.videoBox-content-fullScreen { + height: Auto; } -.videoBox-content-YouTube, .videoBox-content-YouTube-fullScreen { +.videoBox-content-YouTube, +.videoBox-content-YouTube-fullScreen { height: 100%; } @@ -68,17 +75,21 @@ right: 5px; top: 5px; display: none; - background-color: rgba(0, 0, 0, 0.6); + background-color: rgba($dark-gray, 0.6); } -.videoBox-time, .videoBox-snapshot, .videoBox-timelineButton, .videoBox-play, .videoBox-full { - color : white; +.videoBox-time, +.videoBox-snapshot, +.videoBox-timelineButton, +.videoBox-play, +.videoBox-full { + color: white; position: relative; transform-origin: left top; - pointer-events:all; + pointer-events: all; padding-right: 5px; cursor: pointer; &:hover { - background-color: gray; + background-color: $medium-gray; } } @@ -86,4 +97,4 @@ .videoBox-ui { display: flex; } -} \ No newline at end of file +} -- cgit v1.2.3-70-g09d2 From e474d02cf51c5e20e42baa6b7d9c4aeb4ab51967 Mon Sep 17 00:00:00 2001 From: mehekj Date: Sat, 12 Mar 2022 16:40:40 -0500 Subject: video ui in progress, basic functions show up --- .../collections/CollectionStackedTimeline.tsx | 20 +-- .../collectionLinear/CollectionLinearView.tsx | 6 +- src/client/views/nodes/AudioBox.tsx | 7 +- src/client/views/nodes/VideoBox.scss | 90 ++++++++++- src/client/views/nodes/VideoBox.tsx | 172 ++++++++++++++++----- 5 files changed, 231 insertions(+), 64 deletions(-) (limited to 'src/client/views/nodes/VideoBox.scss') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 25f2e0b25..dca5089f4 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -4,7 +4,7 @@ import { computed, IReactionDisposer, observable, - reaction, + reaction } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; @@ -19,35 +19,29 @@ import { formatTime, OmitKeys, returnFalse, - returnOne, - setupMoveUpEvents, - StopEvent, - returnTrue, - smoothScroll, - smoothScrollHorizontal, + returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from "../../../Utils"; import { Docs } from "../../documents/Documents"; +import { DocumentManager } from "../../util/DocumentManager"; +import { DragManager } from "../../util/DragManager"; import { LinkManager } from "../../util/LinkManager"; import { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; +import { SnappingManager } from "../../util/SnappingManager"; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; import { AudioWaveform } from "../AudioWaveform"; import { CollectionSubView } from "../collections/CollectionSubView"; +import { Colors } from "../global/globalEnums"; import { LightboxView } from "../LightboxView"; import { DocAfterFocusFunc, DocFocusFunc, DocumentView, - DocumentViewProps, + DocumentViewProps } from "../nodes/DocumentView"; import { LabelBox } from "../nodes/LabelBox"; import "./CollectionStackedTimeline.scss"; -import { Colors } from "../global/globalEnums"; -import { DocumentManager } from "../../util/DocumentManager"; -import { SnappingManager } from "../../util/SnappingManager"; -import { DragManager } from "../../util/DragManager"; -import { faBreadSlice } from "@fortawesome/free-solid-svg-icons"; type PanZoomDocument = makeInterface<[]>; const PanZoomDocument = makeInterface(); diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 4198a7979..77eed8bfc 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -3,13 +3,14 @@ import { Tooltip } from '@material-ui/core'; import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, HeightSym, Opt, WidthSym, DataSym } from '../../../../fields/Doc'; +import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc'; import { documentSchema } from '../../../../fields/documentSchemas'; import { Id } from '../../../../fields/FieldSymbols'; import { makeInterface } from '../../../../fields/Schema'; import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnTrue, returnFalse, OmitKeys, Utils } from '../../../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils'; import { DocUtils } from '../../../documents/Documents'; +import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { Colors, Shadows } from '../../global/globalEnums'; @@ -20,7 +21,6 @@ import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup'; import { StyleProp } from '../../StyleProvider'; import { CollectionSubView } from '../CollectionSubView'; import { CollectionViewType } from '../CollectionView'; -import { DocumentManager } from "../../../util/DocumentManager"; import "./CollectionLinearView.scss"; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index c33a74325..b9046b61e 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -421,6 +421,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent { if (this._ele) { + if (this._muted) { + this._muted = false; + } this._volume = volume; this._ele.volume = volume; } @@ -503,10 +506,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent - + { e.stopPropagation(); }} onChange={(e: React.ChangeEvent) => { this.setVolume(Number(e.target.value)) }} /> diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index 8a3261c7d..35f5a7e65 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -78,10 +78,49 @@ .videoBox-ui { position: absolute; flex-direction: row; - right: 5px; - top: 5px; - display: none; - background-color: rgba($dark-gray, 0.6); + align-items: center; + justify-content: center; + display: flex; + visibility: none; + opacity: 0; + background-color: $dark-gray; + color: white; + border-radius: 100px; + left: 50%; + transform: translate(-50%, -150%); + transition: top 0.5s, opacity 0.2s, visibility 0s; + height: 5%; + width: 80%; + + .timecode-controls { + display: flex; + flex-direction: row; + margin: 0 5px; + + .timecode { + margin: 0 5px; + } + } + + .videobox-button { + margin: 5px; + cursor: pointer; + width: 30px; + height: 30px; + border-radius: 50%; + background: $dark-gray; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + background: $black; + } + + svg { + width: 20px; + } + } } .videoBox-time, .videoBox-snapshot, @@ -101,6 +140,47 @@ .videoBox:hover { .videoBox-ui { - display: flex; + visibility: visible; + opacity: 1; + z-index: 10000; } } + +video::-webkit-media-controls { + display: none !important; +} + +input[type="range"] { + -webkit-appearance: none; + background: none; + margin: 5px; +} + +input[type="range"]:focus { + outline: none; +} + +input[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 6px; + cursor: pointer; + box-shadow: 0; + background: $light-gray; + border-radius: 3px; +} + +input[type="range"]::-webkit-slider-thumb { + box-shadow: 0; + border: 0; + height: 10px; + width: 10px; + border-radius: 10px; + background: $medium-blue; + cursor: pointer; + -webkit-appearance: none; + margin-top: -2px; +} + +.toolbar-slider.volume { + width: 50px; +} \ No newline at end of file diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index d56363348..bd9423d74 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -59,6 +59,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { - this._fullScreen = true; - this.player && this.player.requestFullscreen(); + if (document.fullscreenElement == this._contentRef) { + this._fullScreen = false; + this.player && this._contentRef && document.exitFullscreen(); + } + else { + this._fullScreen = true; + this.player && this._contentRef && this._contentRef.requestFullscreen(); + } try { this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add"); } catch (e) { @@ -269,13 +278,21 @@ export class VideoBox extends ViewBoxAnnotatableComponent this._fullScreen = vref.webkitDisplayingFullscreen); + // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen); this._disposers.reactionDisposer?.(); this._disposers.reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0), time => !this._playing && (vref.currentTime = time), { fireImmediately: true }); } } + @action + setContentRef = (cref: HTMLDivElement | null) => { + this._contentRef = cref; + if (cref) { + cref.onfullscreenchange = action((e) => this._fullScreen = (document.fullscreenElement == cref)); + } + } + specificContextMenu = (e: React.MouseEvent): void => { const field = Cast(this.dataDoc[this.props.fieldKey], VideoField); if (field) { @@ -292,10 +309,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks, icon: "expand-arrows-alt" }); subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" }); subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" }); - subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" }); - subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" }); - subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" }); - subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" }); + // subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" }); + // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" }); + // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" }); + // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" }); subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" }); } @@ -309,7 +326,8 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading :
-
+
+ {this.uIButtons}
} key="play" placement="bottom"> -
- -
- , - {"timecode"}
} key="time" placement="bottom"> -
- {formatTime(curTime)} - {" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")} -
- , - {"view full screen"}
} key="full" placement="bottom"> -
- -
- ]; - return
- {[...(VideoBox._nativeControls ? [] : nonNativeControls), - {"snapshot current frame"}
} key="snap" placement="bottom"> -
- + // const nonNativeControls = [ + // {"playback"}
} key="play" placement="bottom"> + //
+ // + //
+ // , + // {"timecode"}} key="time" placement="bottom"> + //
+ // {formatTime(curTime)} + // {" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")} + //
+ //
, + // {"view full screen"}} key="full" placement="bottom"> + //
+ // + //
+ //
]; + // return
+ // {[...(VideoBox._nativeControls ? [] : nonNativeControls), + // {"snapshot current frame"}
} key="snap" placement="bottom"> + //
+ // + //
+ // , + // {"show annotation timeline"}} key="timeline" placement="bottom"> + //
+ // + //
+ //
, + // {this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}} key="trim" placement="bottom"> + //
+ // + //
+ //
,]} + // ; + return
+
+ +
+ + {this.timeline &&
+
+ {formatTime(curTime)}
- , - {"show annotation timeline"}
} key="timeline" placement="bottom"> -
- + +
+ { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.setPlayheadTime(Number(e.target.value)) }} + />
- , - {this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
} key="trim" placement="bottom"> -
- + +
+ {formatTime(this.timeline.clipDuration)}
- ,]} -
; +
} + +
+ +
+ + {!this._fullScreen &&
+ +
} + + {!this._fullScreen &&
+ +
} + +
+ +
+ { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.setVolume(Number(e.target.value)) }} + /> + } @computed get renderTimeline() { return
@@ -653,7 +744,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent} {this.renderTimeline} - {this.uIButtons}
); } -- cgit v1.2.3-70-g09d2 From cca2faa0bc4083db7b1a0b9cb6a9c5092e332d31 Mon Sep 17 00:00:00 2001 From: mehekj Date: Wed, 16 Mar 2022 23:05:56 -0400 Subject: basic video UI and functionality complete --- .../collections/CollectionStackedTimeline.tsx | 2 + .../collectionLinear/CollectionLinearView.tsx | 14 +-- src/client/views/nodes/AudioBox.tsx | 29 +++-- src/client/views/nodes/VideoBox.scss | 65 +++++++---- src/client/views/nodes/VideoBox.tsx | 120 ++++++++++++--------- 5 files changed, 134 insertions(+), 96 deletions(-) (limited to 'src/client/views/nodes/VideoBox.scss') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index dca5089f4..793e01822 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -72,6 +72,8 @@ export class CollectionStackedTimeline extends CollectionSubView< CollectionStackedTimelineProps >(PanZoomDocument) { @observable static SelectingRegion: CollectionStackedTimeline | undefined; + @observable public static CurrentlyPlaying: Doc[]; + static RangeScript: ScriptField; static LabelScript: ScriptField; static RangePlayScript: ScriptField; diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 77eed8bfc..70c8c9436 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -19,6 +19,7 @@ import { DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { DocumentView } from '../../nodes/DocumentView'; import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup'; import { StyleProp } from '../../StyleProvider'; +import { CollectionStackedTimeline } from '../CollectionStackedTimeline'; import { CollectionSubView } from '../CollectionSubView'; import { CollectionViewType } from '../CollectionView'; import "./CollectionLinearView.scss"; @@ -231,23 +232,16 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { : null} - {AudioBox.CurrentlyPlaying && AudioBox.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? + {CollectionStackedTimeline.CurrentlyPlaying && CollectionStackedTimeline.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? - Currently listening to: {AudioBox.CurrentlyPlaying.map((clip, i) => + Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => { DocumentManager.Instance.jumpToDocument(clip, true); - }}>{clip.title + (i == AudioBox.CurrentlyPlaying.length - 1 ? "" : ",")} + }}>{clip.title + (i == CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")} )} : null} - {/* THIS RENDERS AUDIOBOX FOR EACH CLIP */} - {/* {AudioBox.CurrentlyPlaying && AudioBox.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ?
-
- {AudioBox.CurrentlyPlaying.map((clip) => this.getDisplayDoc(clip, true))} -
-
: null} */} - ; } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index b9046b61e..eb7b9a773 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -37,7 +37,6 @@ enum media_state { } @observer export class AudioBox extends ViewBoxAnnotatableComponent(AudioDocument) { - @observable public static CurrentlyPlaying: Doc[]; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); } public static SetScrubTime = action((timeInMillisFrom1970: number) => { @@ -47,7 +46,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent { - if (AudioBox.CurrentlyPlaying) { - const index = AudioBox.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc); - index !== -1 && AudioBox.CurrentlyPlaying.splice(index, 1); + if (CollectionStackedTimeline.CurrentlyPlaying) { + const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc); + index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); } } @action addCurrentlyPlaying = () => { - if (!AudioBox.CurrentlyPlaying) { - AudioBox.CurrentlyPlaying = []; + if (!CollectionStackedTimeline.CurrentlyPlaying) { + CollectionStackedTimeline.CurrentlyPlaying = []; } - if (AudioBox.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) { - AudioBox.CurrentlyPlaying.push(this.layoutDoc.doc as Doc); + if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) { + CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc.doc as Doc); } } @@ -421,17 +419,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent { if (this._ele) { - if (this._muted) { - this._muted = false; - } this._volume = volume; this._ele.volume = volume; + if (this._muted) { + this.toggleMute(); + } } } @action - toggleMute = (e: React.PointerEvent) => { - e.stopPropagation(); + toggleMute = () => { if (this._ele) { this._muted = !this._muted; this._ele.muted = this._muted; @@ -505,8 +502,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent
- + onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}> +
this._disposers[d]?.()); } @@ -155,6 +156,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { this._playing = false; + this.removeCurrentlyPlaying(); try { update && this.player?.pause(); update && this._audioPlayer?.pause(); @@ -178,6 +180,7 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading :
-
+
this._fullScreen && e.stopPropagation()}> {this.uIButtons} -
} key="play" placement="bottom"> - //
- // - //
- // , - // {"timecode"}
} key="time" placement="bottom"> - //
- // {formatTime(curTime)} - // {" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")} - //
- // , - // {"view full screen"}
} key="full" placement="bottom"> - //
- // - //
- // ]; - // return
- // {[...(VideoBox._nativeControls ? [] : nonNativeControls), - // {"snapshot current frame"}
} key="snap" placement="bottom"> - //
- // - //
- // , - // {"show annotation timeline"}} key="timeline" placement="bottom"> - //
- // - //
- //
, - // {this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}} key="trim" placement="bottom"> - //
- // - //
- //
,]} - // ; - return
+ return
@@ -618,13 +619,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent -
- { e.stopPropagation(); }} - onChange={(e: React.ChangeEvent) => { this.setPlayheadTime(Number(e.target.value)) }} - /> -
+ {this._fullScreen || this.heightPercent == 100 ? +
+ { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.setPlayheadTime(Number(e.target.value)) }} + /> +
+ : +
/
}
{formatTime(this.timeline.clipDuration)} @@ -649,16 +653,28 @@ export class VideoBox extends ViewBoxAnnotatableComponent
} -
- + onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}> +
{ e.stopPropagation(); }} onChange={(e: React.ChangeEvent) => { this.setVolume(Number(e.target.value)) }} /> + + {!this._fullScreen && this.heightPercent != 100 && + <> +
+ +
+ { e.stopPropagation(); }} + onChange={(e: React.ChangeEvent) => { this.zoom(Number(e.target.value)); }} + /> + }
} @computed get renderTimeline() { -- cgit v1.2.3-70-g09d2 From ec4cf9ea0439701fde3aa49e462b054998c657f1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 11 Apr 2022 17:57:37 -0400 Subject: got pdf's to generate icons when iconifying. --- src/client/util/CurrentUserUtils.ts | 20 ++++++++++- src/client/views/DocumentDecorations.scss | 5 +-- src/client/views/nodes/PDFBox.tsx | 57 +++++++++++++++++++++++++++++-- src/client/views/nodes/VideoBox.scss | 1 + src/client/views/nodes/VideoBox.tsx | 20 +++++++---- src/client/views/pdf/PDFViewer.tsx | 2 +- 6 files changed, 92 insertions(+), 13 deletions(-) (limited to 'src/client/views/nodes/VideoBox.scss') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6d8e2d30c..0e392cc85 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -334,9 +334,27 @@ export class CurrentUserUtils { proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); doc["template-icon-view-col"] = new PrefetchProxy(iconColView); } + if (doc["template-icon-view-video"] === undefined) { + const iconVidView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + iconVidView.isTemplateDoc = makeTemplate(iconVidView, true, "icon_" + DocumentType.VID); + const proto = iconVidView.proto as Doc; + proto["icon-nativeWidth"] = 360 / 4; + proto["icon-nativeHeight"] = 270 / 4; + proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); + doc["template-icon-view-video"] = new PrefetchProxy(iconVidView); + } + if (doc["template-icon-view-pdf"] === undefined) { + const iconPdfView = Docs.Create.ImageDocument("", { title: "icon", _width: 360 / 4, _height: 270 / 4, onClick: ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" }), system: true }); + iconPdfView.isTemplateDoc = makeTemplate(iconPdfView, true, "icon_" + DocumentType.PDF); + const proto = iconPdfView.proto as Doc; + proto["icon-nativeWidth"] = 360 / 4; + proto["icon-nativeHeight"] = 270 / 4; + proto.icon = new ImageField("http://www.cs.brown.edu/~bcz/noImage.png"); + doc["template-icon-view-pdf"] = new PrefetchProxy(iconPdfView); + } if (doc["template-icons"] === undefined) { doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-video"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); } else { const templateIconsDoc = Cast(doc["template-icons"], Doc, null); const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 35e37a2cd..7432d45bf 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -232,12 +232,13 @@ $linkGap: 3px; opacity: 1; width: 100%; grid-column: 2; + grid-column-end: 2; pointer-events: auto; overflow: hidden; text-align: center; display: flex; - padding-left: 5px; - padding-right: 12px; + padding-left: 2px; + padding-right: 2px; height: 20px; position: absolute; border-radius: 8px; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index a2e7d2aa3..3f1771e68 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -2,10 +2,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; +import { CreateImage } from "../nodes/WebBoxRenderer"; import "pdfjs-dist/web/pdf_viewer.css"; -import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Cast, NumCast, StrCast, ImageCast } from '../../../fields/Types'; -import { PdfField } from "../../../fields/URLField"; +import { ImageField, PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; @@ -21,6 +22,8 @@ import { SidebarAnnos } from '../SidebarAnnos'; import { FieldView, FieldViewProps } from './FieldView'; import "./PDFBox.scss"; import React = require("react"); +import { Id } from '../../../fields/FieldSymbols'; +import { VideoBox } from './VideoBox'; @observer export class PDFBox extends ViewBoxAnnotatableComponent() { @@ -52,6 +55,56 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + if (oldDiv.childNodes) { + for (let i = 0; i < oldDiv.childNodes.length; i++) { + this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement); + } + } + if (oldDiv instanceof HTMLCanvasElement) { + const canvas = oldDiv as HTMLCanvasElement; + var img = document.createElement('img'); // create a Image Element + img.src = canvas.toDataURL(); //image source + img.style.width = canvas.style.width; + img.style.height = canvas.style.height; + const newCan = newDiv as HTMLCanvasElement; + const parEle = newCan.parentElement as HTMLElement; + parEle.removeChild(newCan); + parEle.appendChild(img); + } + } + + updateIcon = () => { + const docViewContent = this.props.docViewPath().lastElement().ContentDiv!; + const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; + newDiv.style.width = (this.layoutDoc[WidthSym]()).toString(); + newDiv.style.height = (this.layoutDoc[HeightSym]()).toString(); + this.replaceCanvases(docViewContent, newDiv) + var htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv); + const nativeWidth = this.layoutDoc[WidthSym](); + const nativeHeight = this.layoutDoc[HeightSym](); + + CreateImage( + "", + document.styleSheets, + htmlString?.replace(/docView-hack/g, 'documentView-hack'), + nativeWidth, + nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(), + NumCast(this.layoutDoc._scrollTop) * this.props.PanelHeight() / NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"]) + ).then + ((data_url: any) => { + VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true).then( + returnedfilename => setTimeout(action(() => { + this.dataDoc.icon = new ImageField(returnedfilename); + this.dataDoc["icon-nativeWidth"] = nativeWidth; + this.dataDoc["icon-nativeHeight"] = nativeHeight; + }), 500)); + }) + .catch(function (error: any) { + console.error('oops, something went wrong!', error); + }); + } + componentWillUnmount() { this._selectReactionDisposer?.(); } componentDidMount() { this.props.setContentView?.(this); diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index 3cf10a033..f47a71469 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -81,6 +81,7 @@ align-items: center; justify-content: center; display: flex; + width: 100%; visibility: none; opacity: 0; background-color: $dark-gray; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index ca8dc8515..b7b8ce064 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -4,10 +4,10 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import { basename } from "path"; import * as rp from 'request-promise'; -import { Doc, DocListCast } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc"; import { InkTool } from "../../../fields/InkField"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { AudioField, VideoField } from "../../../fields/URLField"; +import { AudioField, ImageField, VideoField } from "../../../fields/URLField"; import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; @@ -220,16 +220,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent void) => { const width = NumCast(this.layoutDoc._width); const canvas = document.createElement('canvas'); canvas.width = 640; canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1); const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions if (ctx) { - // ctx.rect(0, 0, canvas.width, canvas.height); - // ctx.fillStyle = "blue"; - // ctx.fill(); this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height); } @@ -259,10 +256,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent - returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY)); + returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY)); } } + updateIcon = () => { + const makeIcon = (returnedfilename: string) => { + this.dataDoc.icon = new ImageField(returnedfilename); + this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym](); + this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym](); + } + this.Snapshot(undefined, undefined, makeIcon); + } + // creates link for snapshot createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => { const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 651b0401b..9aaa6e90f 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -74,7 +74,7 @@ export class PDFViewer extends React.Component { private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject = React.createRef(); - private _mainCont: React.RefObject = React.createRef(); + _mainCont: React.RefObject = React.createRef(); private _selectionText: string = ""; private _downX: number = 0; private _downY: number = 0; -- cgit v1.2.3-70-g09d2 From 4d130a85618f4e88cf8fdb4763e1a21bb71e689b Mon Sep 17 00:00:00 2001 From: mehekj Date: Sun, 1 May 2022 22:18:34 -0400 Subject: put videoBox ui into componentUI function --- src/client/views/MainView.tsx | 2 +- src/client/views/nodes/VideoBox.scss | 72 ++++++++++++++++-------------------- src/client/views/nodes/VideoBox.tsx | 26 +++++++++++-- 3 files changed, 54 insertions(+), 46 deletions(-) (limited to 'src/client/views/nodes/VideoBox.scss') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c794c73db..15ccd7b31 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -184,7 +184,7 @@ export class MainView extends React.Component { fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines, - fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]); + fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeMute]); this.initAuthenticationRouters(); } diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index f47a71469..08d0bb035 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -75,54 +75,53 @@ // pointer-events: all; // } +.videoBox-ui-wrapper { + width: 0; + height: 0; +} + .videoBox-ui { position: absolute; flex-direction: row; align-items: center; justify-content: center; display: flex; - width: 100%; - visibility: none; - opacity: 0; + // visibility: none; + // opacity: 0; background-color: $dark-gray; color: white; border-radius: 100px; - top: calc(100% - 20px); - left: 50%; - transform: translate(-50%, -100%); + z-index: 2001; + min-width: 300px; transition: top 0.5s, width 0.5s, opacity 0.2s, visibility 0s; - height: 120px; - padding: 0 20px; + height: 50px; + padding: 0 10px 0 7px; .timecode-controls { display: flex; flex-direction: row; align-items: center; justify-content: center; - margin: 0 5px; + margin: 0 2px; flex-grow: 2; - font-size: 32px; - - .timecode { - margin: 0 5px; - } + font-size: 14px; .timeline-slider { - margin: 0 20px 0 20px; + margin: 5px; flex-grow: 2; } } .toolbar-slider.volume, .toolbar-slider.zoom { - width: 100px; + width: 50px; } .videobox-button { - margin: 5px; + margin: 2px; cursor: pointer; - width: 70px; - height: 70px; + width: 30px; + height: 30px; border-radius: 50%; background: $dark-gray; display: flex; @@ -134,8 +133,8 @@ } svg { - width: 40px; - height: 40px; + width: 17px; + height: 17px; } } } @@ -156,23 +155,15 @@ } } -.videoBox:hover { - .videoBox-ui { - visibility: visible; - opacity: 1; - z-index: 10000; - } -} - .videoBox-content-fullScreen, .videoBox-content-fullScreen-interactive { display: flex; justify-content: center; - align-items: center; + align-items: flex-end; - &:hover { - .videoBox-ui { - opacity: 0; - } + .videoBox-ui { + opacity: 0; + bottom: 50px; + width: 80%; } .videoBox-ui:hover { @@ -187,7 +178,6 @@ video::-webkit-media-controls { input[type="range"] { -webkit-appearance: none; background: none; - margin: 10px; } input[type="range"]:focus { @@ -196,21 +186,21 @@ input[type="range"]:focus { input[type="range"]::-webkit-slider-runnable-track { width: 100%; - height: 20px; + height: 10px; cursor: pointer; box-shadow: 0; background: $light-gray; - border-radius: 20px; + border-radius: 10px; } input[type="range"]::-webkit-slider-thumb { box-shadow: 0; border: 0; - height: 26px; - width: 26px; - border-radius: 20px; + height: 12px; + width: 12px; + border-radius: 10px; background: $medium-blue; cursor: pointer; -webkit-appearance: none; - margin-top: -3px; + margin-top: -1px; } \ No newline at end of file diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index e57cb1abe..80ad63e72 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -383,7 +383,10 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading
:
this._fullScreen && e.stopPropagation()}> - {this.uIButtons} + {/* {this._fullScreen && this.componentUI(0, 0)} */} + {this._fullScreen &&
+ {this.UIButtons} +
}