diff --git a/src/assets/hud/share_screen_off-hover.png b/src/assets/hud/share_screen_off-hover.png index b376dfee7af6683e1c43b94bf65ac584662b8e5a..03225ed3d84483bff45bf236ff866bfe9a46d4e1 100644 Binary files a/src/assets/hud/share_screen_off-hover.png and b/src/assets/hud/share_screen_off-hover.png differ diff --git a/src/assets/hud/share_screen_off.png b/src/assets/hud/share_screen_off.png index 57942952bacb062f0b7ab1d715d919f96f6d7e3a..a4954fff5106a3d3bae1fdc294cda31dacab7e8a 100644 Binary files a/src/assets/hud/share_screen_off.png and b/src/assets/hud/share_screen_off.png differ diff --git a/src/assets/hud/share_screen_on-hover.png b/src/assets/hud/share_screen_on-hover.png index 228140b32fcc89029ac8fe104fec788918e91cf7..b7e806879fcfb65d652d0217b0dfc0ece7a68e96 100644 Binary files a/src/assets/hud/share_screen_on-hover.png and b/src/assets/hud/share_screen_on-hover.png differ diff --git a/src/assets/hud/share_screen_on.png b/src/assets/hud/share_screen_on.png index 5d6546984d095e69644c2f7a5858b2c4d276b2e6..ab30c7e2932c458f296757f43ad74c63f637b8c5 100644 Binary files a/src/assets/hud/share_screen_on.png and b/src/assets/hud/share_screen_on.png differ diff --git a/src/assets/stylesheets/2d-hud.scss b/src/assets/stylesheets/2d-hud.scss index 6eba978bd005a72d1795d62507a21de971a385bf..834223deb7562cee1bc1a3ea01f39a3c27b864a2 100644 --- a/src/assets/stylesheets/2d-hud.scss +++ b/src/assets/stylesheets/2d-hud.scss @@ -50,13 +50,19 @@ display: flex; flex-direction: column; border-radius: 30px; - padding: 5px 5px 8px 5px; - background-color: rgba(79, 79, 79, 0.2); + padding: 3px 3px 6px 3px; + background-color: rgba(72, 72, 72, 0.2); + border: 2px solid rgba(72, 72, 72, 0.3); + pointer-events: auto; z-index: 1; :local(.iconButton) { margin-top: 4px; } + + :local(.iconButton):first-child { + margin-top: 0; + } } } diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js index 2b4b375482e70fb9bca4871a3dbc2f6d07ac3b71..79bca2f933717ce4ded9fff1f458147d90284982 100644 --- a/src/react-components/2d-hud.js +++ b/src/react-components/2d-hud.js @@ -9,55 +9,94 @@ class TopHUD extends Component { static propTypes = { muted: PropTypes.bool, frozen: PropTypes.bool, - videoShareSource: PropTypes.bool, + videoShareSource: PropTypes.string, + availableVREntryTypes: PropTypes.object, onToggleMute: PropTypes.func, onToggleFreeze: PropTypes.func, onSpawnPen: PropTypes.func, onSpawnCamera: PropTypes.func, - onShareVideo: PropTypes.func + onShareVideo: PropTypes.func, + onEndShareVideo: PropTypes.func }; state = { showVideoShareOptions: false }; - handleVideoShareClicked = source => {}; + handleVideoShareClicked = source => { + if (this.props.videoShareSource) { + this.props.onEndShareVideo(); + } else { + this.props.onShareVideo(source); + } + }; + + buildVideoSharingButtons = () => { + if (this.props.availableVREntryTypes.isInHMD) return null; + + const videoShareExtraOptionTypes = []; + const primaryVideoShareType = this.props.videoShareSource || (AFRAME.utils.device.isMobile() ? "camera" : "screen"); + + if (this.state.showVideoShareOptions) { + videoShareExtraOptionTypes.push(primaryVideoShareType); + + ["screen", "window", "camera"].forEach(t => { + if (videoShareExtraOptionTypes.indexOf(t) === -1) { + videoShareExtraOptionTypes.push(t); + } + }); + } + + const showExtrasOnHover = () => { + clearTimeout(this.hideVideoSharingButtonTimeout); + this.setState({ showVideoShareOptions: true }); + }; + + const hideExtrasOnOut = () => { + this.hideVideoSharingButtonTimeout = setTimeout(() => { + this.setState({ showVideoShareOptions: false }); + }, 250); + }; - render() { return ( - <div className={cx(styles.container, styles.top, styles.unselectable)}> - <div className={cx(uiStyles.uiInteractive, styles.panel, styles.left)}> - <div - className={cx(styles.iconButton, styles.share_screen, { - [styles.active]: this.props.videoShareSource === "screen" - })} - title={this.props.videoShareSource === "screen" ? "Stop Screen Sharing" : "Share Screen"} - onClick={() => this.handleVideoShareClicked("screen")} - > - <div className={cx(styles.videoShareExtraOptions)}> - <div - className={cx(styles.iconButton, styles.share_window, { - [styles.active]: this.props.videoShareSource === "window" - })} - title={this.props.videoShareSource === "window" ? "Stop Window Sharing" : "Share Window"} - onClick={() => this.handleVideoShareClicked("window")} - /> - <div - className={cx(styles.iconButton, styles.share_window, { - [styles.active]: this.props.videoShareSource === "window" - })} - title={this.props.videoShareSource === "window" ? "Stop Window Sharing" : "Share Window"} - onClick={() => this.handleVideoShareClicked("window")} - /> + <div + className={cx(styles.iconButton, styles.share_screen, { + [styles.active]: this.props.videoShareSource === primaryVideoShareType + })} + title={this.props.videoShareSource !== null ? "Stop sharing" : `Share ${primaryVideoShareType}`} + onClick={() => { + if (!this.state.showVideoShareOptions) { + this.handleVideoShareClicked(primaryVideoShareType); + } + }} + onMouseOver={showExtrasOnHover} + > + {videoShareExtraOptionTypes.length > 0 && ( + <div className={cx(styles.videoShareExtraOptions)} onMouseOut={hideExtrasOnOut}> + {videoShareExtraOptionTypes.map(type => ( <div - className={cx(styles.iconButton, styles.share_camera, { - [styles.active]: this.props.videoShareSource === "camera" + key={type} + className={cx(styles.iconButton, styles[`share_${type}`], { + [styles.active]: this.props.videoShareSource === type })} - title={this.props.videoShareSource === "camera" ? "Stop Camera Sharing" : "Share Camera"} - onClick={() => this.handleVideoShareClicked("camera")} + title={this.props.videoShareSource === type ? "Stop sharing" : `Share ${type}`} + onClick={() => this.handleVideoShareClicked(type)} + onMouseOver={showExtrasOnHover} /> - </div> + ))} </div> + )} + </div> + ); + }; + + render() { + const videoSharingButtons = this.buildVideoSharingButtons(); + + return ( + <div className={cx(styles.container, styles.top, styles.unselectable)}> + <div className={cx(uiStyles.uiInteractive, styles.panel, styles.left)}> + {videoSharingButtons} <div className={cx(styles.iconButton, styles.mute, { [styles.active]: this.props.muted })} title={this.props.muted ? "Unmute Mic" : "Mute Mic"} diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js index c0156b72f8fee6bade1def35ab8beaafbbd0c749..9aee690248a5c45cc6db6e84080295195eeeaa28 100644 --- a/src/react-components/ui-root.js +++ b/src/react-components/ui-root.js @@ -1057,6 +1057,7 @@ class UIRoot extends Component { muted={this.state.muted} frozen={this.state.frozen} spacebubble={this.state.spacebubble} + availableVREntryTypes={this.props.availableVREntryTypes} onToggleMute={this.toggleMute} onToggleFreeze={this.toggleFreeze} onToggleSpaceBubble={this.toggleSpaceBubble} diff --git a/src/scene-entry-manager.js b/src/scene-entry-manager.js index d67266e83691f3217a92272e86f6cfe6c71acec1..f58454b0ec2b12793b845fdf5ccb8a2ddae9aa2d 100644 --- a/src/scene-entry-manager.js +++ b/src/scene-entry-manager.js @@ -246,31 +246,16 @@ export default class SceneEntryManager { if (isHandlingVideoShare) return; isHandlingVideoShare = true; - if (!currentVideoShareEntity) { - const newStream = await navigator.mediaDevices.getUserMedia(constraints); - const videoTracks = newStream ? newStream.getVideoTracks() : []; - - if (videoTracks.length > 0) { - newStream.getVideoTracks().forEach(track => mediaStream.addTrack(track)); - NAF.connection.adapter.setLocalMediaStream(mediaStream); - currentVideoShareEntity = spawnMediaInfrontOfPlayer(mediaStream, undefined); - } - - this.scene.emit("share_video_enabled", { constraints }); - } else { - currentVideoShareEntity.parentNode.removeChild(currentVideoShareEntity); - - for (const track of mediaStream.getVideoTracks()) { - mediaStream.removeTrack(track); - } + const newStream = await navigator.mediaDevices.getUserMedia(constraints); + const videoTracks = newStream ? newStream.getVideoTracks() : []; + if (videoTracks.length > 0) { + newStream.getVideoTracks().forEach(track => mediaStream.addTrack(track)); NAF.connection.adapter.setLocalMediaStream(mediaStream); - currentVideoShareEntity = null; - - this.scene.emit("share_video_disabled"); + currentVideoShareEntity = spawnMediaInfrontOfPlayer(mediaStream, undefined); } - isHandlingVideoShare = false; + this.scene.emit("share_video_enabled", { source: constraints.video.mediaSource }); }; this.scene.addEventListener("action_share_camera", () => { @@ -308,6 +293,22 @@ export default class SceneEntryManager { } }); }); + + this.scene.addEventListener("action_end_video_sharing", () => { + if (isHandlingVideoShare) return; + isHandlingVideoShare = true; + currentVideoShareEntity.parentNode.removeChild(currentVideoShareEntity); + + for (const track of mediaStream.getVideoTracks()) { + mediaStream.removeTrack(track); + } + + NAF.connection.adapter.setLocalMediaStream(mediaStream); + currentVideoShareEntity = null; + + this.scene.emit("share_video_disabled"); + isHandlingVideoShare = false; + }); }; _setupCamera = () => {