diff options
Diffstat (limited to 'remoting/webapp')
-rw-r--r-- | remoting/webapp/background.js | 3 | ||||
-rw-r--r-- | remoting/webapp/client_session.js | 86 | ||||
-rw-r--r-- | remoting/webapp/event_handlers.js | 6 | ||||
-rw-r--r-- | remoting/webapp/fullscreen_v2.js | 2 | ||||
-rw-r--r-- | remoting/webapp/html/template_main.html | 92 | ||||
-rw-r--r-- | remoting/webapp/html/toolbar.html | 12 | ||||
-rw-r--r-- | remoting/webapp/html/window_frame.html | 29 | ||||
-rw-r--r-- | remoting/webapp/js_proto/chrome_proto.js | 1 | ||||
-rw-r--r-- | remoting/webapp/main.css | 4 | ||||
-rw-r--r-- | remoting/webapp/remoting.js | 2 | ||||
-rw-r--r-- | remoting/webapp/toolbar.css | 2 | ||||
-rw-r--r-- | remoting/webapp/window_frame.css | 174 | ||||
-rw-r--r-- | remoting/webapp/window_frame.js | 175 |
13 files changed, 508 insertions, 80 deletions
diff --git a/remoting/webapp/background.js b/remoting/webapp/background.js index 118b7e57..1976b91 100644 --- a/remoting/webapp/background.js +++ b/remoting/webapp/background.js @@ -8,7 +8,8 @@ var kNewWindowId = 'new-window'; function createWindow() { chrome.app.window.create('main.html', { 'width': 800, - 'height': 600 + 'height': 600, + 'frame': 'none' }); }; diff --git a/remoting/webapp/client_session.js b/remoting/webapp/client_session.js index 7b0d80e..23c8814 100644 --- a/remoting/webapp/client_session.js +++ b/remoting/webapp/client_session.js @@ -176,28 +176,29 @@ remoting.ClientSession.prototype.updateScrollbarVisibility = function() { if (!this.shrinkToFit_) { // Determine whether or not horizontal or vertical scrollbars are // required, taking into account their width. - needsVerticalScroll = window.innerHeight < this.plugin_.desktopHeight; - needsHorizontalScroll = window.innerWidth < this.plugin_.desktopWidth; + var clientArea = this.getClientArea_(); + needsVerticalScroll = clientArea.height < this.plugin_.desktopHeight; + needsHorizontalScroll = clientArea.width < this.plugin_.desktopWidth; var kScrollBarWidth = 16; if (needsHorizontalScroll && !needsVerticalScroll) { needsVerticalScroll = - window.innerHeight - kScrollBarWidth < this.plugin_.desktopHeight; + clientArea.height - kScrollBarWidth < this.plugin_.desktopHeight; } else if (!needsHorizontalScroll && needsVerticalScroll) { needsHorizontalScroll = - window.innerWidth - kScrollBarWidth < this.plugin_.desktopWidth; + clientArea.width - kScrollBarWidth < this.plugin_.desktopWidth; } } - var htmlNode = /** @type {HTMLElement} */ (document.documentElement); + var scroller = document.getElementById('scroller'); if (needsHorizontalScroll) { - htmlNode.classList.remove('no-horizontal-scroll'); + scroller.classList.remove('no-horizontal-scroll'); } else { - htmlNode.classList.add('no-horizontal-scroll'); + scroller.classList.add('no-horizontal-scroll'); } if (needsVerticalScroll) { - htmlNode.classList.remove('no-vertical-scroll'); + scroller.classList.remove('no-vertical-scroll'); } else { - htmlNode.classList.add('no-vertical-scroll'); + scroller.classList.add('no-vertical-scroll'); } }; @@ -568,6 +569,9 @@ remoting.ClientSession.prototype.removePlugin = function() { function() { remoting.fullscreen.removeListener(listener); }); + if (remoting.windowFrame) { + remoting.windowFrame.setConnected(false); + } // Remove mediasource-rendering class from video-contained - this will also // hide the <video> element. @@ -766,9 +770,10 @@ remoting.ClientSession.prototype.onSetScreenMode_ = function(event) { remoting.ClientSession.prototype.setScreenMode_ = function(shrinkToFit, resizeToClient) { if (resizeToClient && !this.resizeToClient_) { - this.plugin_.notifyClientResolution(window.innerWidth, - window.innerHeight, - window.devicePixelRatio); + var clientArea = this.getClientArea_(); + this.plugin_.notifyClientResolution(clientArea.width, + clientArea.height, + window.devicePixelRatio); } // If enabling shrink, reset bump-scroll offsets. @@ -953,13 +958,18 @@ remoting.ClientSession.prototype.onConnectionStatusUpdate_ = this.setFocusHandlers_(); this.onDesktopSizeChanged_(); if (this.resizeToClient_) { - this.plugin_.notifyClientResolution(window.innerWidth, - window.innerHeight, - window.devicePixelRatio); + var clientArea = this.getClientArea_(); + this.plugin_.notifyClientResolution(clientArea.width, + clientArea.height, + window.devicePixelRatio); } - // Start listening for full-screen related events. + // Activate full-screen related UX. remoting.fullscreen.addListener(this.callOnFullScreenChanged_); remoting.fullscreen.syncWithMaximize(true); + if (remoting.windowFrame) { + remoting.windowFrame.setConnected(true); + } + } else if (status == remoting.ClientSession.State.FAILED) { switch (error) { case remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE: @@ -1019,9 +1029,10 @@ remoting.ClientSession.prototype.onSetCapabilities_ = function(capabilities) { this.capabilities_ = capabilities; if (this.hasCapability_( remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION)) { - this.plugin_.notifyClientResolution(window.innerWidth, - window.innerHeight, - window.devicePixelRatio); + var clientArea = this.getClientArea_(); + this.plugin_.notifyClientResolution(clientArea.width, + clientArea.height, + window.devicePixelRatio); } }; @@ -1080,10 +1091,11 @@ remoting.ClientSession.prototype.onResize = function() { remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS)) { kResizeRateLimitMs = 250; } + var clientArea = this.getClientArea_(); this.notifyClientResolutionTimer_ = window.setTimeout( this.plugin_.notifyClientResolution.bind(this.plugin_, - window.innerWidth, - window.innerHeight, + clientArea.width, + clientArea.height, window.devicePixelRatio), kResizeRateLimitMs); } @@ -1148,8 +1160,7 @@ remoting.ClientSession.prototype.updateDimensions = function() { return; } - var windowWidth = window.innerWidth; - var windowHeight = window.innerHeight; + var clientArea = this.getClientArea_(); var desktopWidth = this.plugin_.desktopWidth; var desktopHeight = this.plugin_.desktopHeight; @@ -1174,8 +1185,9 @@ remoting.ClientSession.prototype.updateDimensions = function() { if (this.shrinkToFit_) { // Reduce the scale, if necessary, to fit the whole desktop in the window. - var scaleFitWidth = Math.min(scale, 1.0 * windowWidth / desktopWidth); - var scaleFitHeight = Math.min(scale, 1.0 * windowHeight / desktopHeight); + var scaleFitWidth = Math.min(scale, 1.0 * clientArea.width / desktopWidth); + var scaleFitHeight = + Math.min(scale, 1.0 * clientArea.height / desktopHeight); scale = Math.min(scaleFitHeight, scaleFitWidth); // If we're running full-screen then try to handle common side-by-side @@ -1334,12 +1346,13 @@ remoting.ClientSession.prototype.scroll_ = function(dx, dy) { }; var stopX = { stop: false }; + var clientArea = this.getClientArea_(); style.marginLeft = adjustMargin(style.marginLeft, dx, - window.innerWidth, plugin.clientWidth, stopX); + clientArea.width, plugin.clientWidth, stopX); var stopY = { stop: false }; - style.marginTop = adjustMargin(style.marginTop, dy, - window.innerHeight, plugin.clientHeight, stopY); + style.marginTop = adjustMargin( + style.marginTop, dy, clientArea.height, plugin.clientHeight, stopY); return stopX.stop && stopY.stop; }; @@ -1396,8 +1409,9 @@ remoting.ClientSession.prototype.onMouseMove_ = function(event) { return 0; }; - var dx = computeDelta(event.x, window.innerWidth); - var dy = computeDelta(event.y, window.innerHeight); + var clientArea = this.getClientArea_(); + var dx = computeDelta(event.x, clientArea.width); + var dy = computeDelta(event.y, clientArea.height); if (dx != 0 || dy != 0) { /** @type {remoting.ClientSession} */ @@ -1475,3 +1489,15 @@ remoting.ClientSession.prototype.createGnubbyAuthHandler_ = function() { this.sendGnubbyAuthMessage({'type': 'control', 'option': 'auth-v1'}); } }; + +/** + * @return {{width: number, height: number}} The height of the window's client + * area. This differs between apps v1 and apps v2 due to the custom window + * borders used by the latter. + * @private + */ +remoting.ClientSession.prototype.getClientArea_ = function() { + return remoting.windowFrame ? + remoting.windowFrame.getClientArea() : + { 'width': window.innerWidth, 'height': window.innerHeight }; +}
\ No newline at end of file diff --git a/remoting/webapp/event_handlers.js b/remoting/webapp/event_handlers.js index bcbc201..e864c2d 100644 --- a/remoting/webapp/event_handlers.js +++ b/remoting/webapp/event_handlers.js @@ -101,6 +101,12 @@ function onLoad() { remoting.init(); window.addEventListener('resize', remoting.onResize, false); + // When a window goes full-screen, a resize event is triggered, but the + // Fullscreen.isActive call is not guaranteed to return true until the + // full-screen event is triggered. In apps v2, the size of the window's + // client area is calculated differently in full-screen mode, so register + // for both events. + remoting.fullscreen.addListener(remoting.onResize); if (!remoting.isAppsV2) { window.addEventListener('beforeunload', remoting.promptClose, false); window.addEventListener('unload', remoting.disconnect, false); diff --git a/remoting/webapp/fullscreen_v2.js b/remoting/webapp/fullscreen_v2.js index 4e19f34..3aaaab16 100644 --- a/remoting/webapp/fullscreen_v2.js +++ b/remoting/webapp/fullscreen_v2.js @@ -102,6 +102,7 @@ remoting.FullscreenAppsV2.prototype.syncWithMaximize = function(sync) { remoting.FullscreenAppsV2.prototype.onFullscreened_ = function() { this.notifyCallbacksOnRestore_ = true; this.eventSource_.raiseEvent(this.kEventName_, true); + document.body.classList.add('fullscreen'); }; remoting.FullscreenAppsV2.prototype.onMaximized_ = function() { @@ -111,6 +112,7 @@ remoting.FullscreenAppsV2.prototype.onMaximized_ = function() { }; remoting.FullscreenAppsV2.prototype.onRestored_ = function() { + document.body.classList.remove('fullscreen'); if (this.hookingWindowEvents_) { this.activate(false); } diff --git a/remoting/webapp/html/template_main.html b/remoting/webapp/html/template_main.html index e690e3f..78e738e 100644 --- a/remoting/webapp/html/template_main.html +++ b/remoting/webapp/html/template_main.html @@ -5,7 +5,7 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> -<html class="scrollable full-height"> +<html class="full-height"> <head> <meta charset="utf-8"> <link rel="icon" type="image/png" href="chromoting16.webp"> @@ -14,13 +14,16 @@ found in the LICENSE file. <link rel="stylesheet" href="main.css"> <link rel="stylesheet" href="menu_button.css"> <link rel="stylesheet" href="toolbar.css"> + <link rel="stylesheet" href="window_frame.css"> <meta-include type="javascript"/> <title i18n-content="PRODUCT_NAME"></title> </head> - <body class="full-height"> + <body class="full-height inner-border-for-apps-v2"> + + <meta-include src="webapp/html/window_frame.html"/> <!-- loading-mode is initially visible, but becomes hidden as soon as an AppMode is selected by remoting.init. All other divs are initially @@ -34,62 +37,65 @@ found in the LICENSE file. <iframe id="wcs-sandbox" src="wcs_sandbox.html" hidden></iframe> - <div class="inset" data-ui-mode="home" hidden> + <div id="scroller"> + <div class="inset" data-ui-mode="home" hidden> - <meta-include src="webapp/html/ui_header.html"/> - <meta-include src="webapp/html/butterbar.html"/> - <meta-include src="webapp/html/ui_it2me.html"/> - <meta-include src="webapp/html/ui_me2me.html"/> + <meta-include src="webapp/html/ui_header.html"/> + <meta-include src="webapp/html/butterbar.html"/> + <meta-include src="webapp/html/ui_it2me.html"/> + <meta-include src="webapp/html/ui_me2me.html"/> - </div> + </div> <!-- inset --> - <meta-include src="webapp/html/dialog_auth.html"/> + <meta-include src="webapp/html/dialog_auth.html"/> - <div class="dialog-screen" - data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed home.manage-pairings" - hidden></div> + <div class="dialog-screen" + data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed home.manage-pairings" + hidden></div> - <div class="dialog-container" - data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-install home.host-setup home.token-refresh-failed home.manage-pairings" - hidden> + <div class="dialog-container" + data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-install home.host-setup home.token-refresh-failed home.manage-pairings" + hidden> - <meta-include src="webapp/html/dialog_token_refresh_failed.html"/> - <meta-include src="webapp/html/dialog_host_setup.html"/> - <meta-include src="webapp/html/dialog_host_install.html"/> - <meta-include src="webapp/html/dialog_host.html"/> + <meta-include src="webapp/html/dialog_token_refresh_failed.html"/> + <meta-include src="webapp/html/dialog_host_setup.html"/> + <meta-include src="webapp/html/dialog_host_install.html"/> + <meta-include src="webapp/html/dialog_host.html"/> - <div id="client-dialog" - class="kd-modaldialog" - data-ui-mode="home.client"> + <div id="client-dialog" + class="kd-modaldialog" + data-ui-mode="home.client"> - <meta-include src="webapp/html/dialog_client_unconnected.html"/> - <meta-include src="webapp/html/dialog_client_connecting.html"/> - <meta-include src="webapp/html/dialog_client_host_needs_upgrade.html"/> - <meta-include src="webapp/html/dialog_client_pin_prompt.html"/> - <meta-include src="webapp/html/dialog_client_third_party_auth.html"/> - <meta-include src="webapp/html/dialog_client_connect_failed.html"/> - <meta-include src="webapp/html/dialog_client_session_finished.html"/> + <meta-include src="webapp/html/dialog_client_unconnected.html"/> + <meta-include src="webapp/html/dialog_client_connecting.html"/> + <meta-include src="webapp/html/dialog_client_host_needs_upgrade.html"/> + <meta-include src="webapp/html/dialog_client_pin_prompt.html"/> + <meta-include src="webapp/html/dialog_client_third_party_auth.html"/> + <meta-include src="webapp/html/dialog_client_connect_failed.html"/> + <meta-include src="webapp/html/dialog_client_session_finished.html"/> - </div> + </div> - <meta-include src="webapp/html/dialog_connection_history.html"/> - <meta-include src="webapp/html/dialog_confirm_host_delete.html"/> - <meta-include src="webapp/html/dialog_manage_pairings.html"/> + <meta-include src="webapp/html/dialog_connection_history.html"/> + <meta-include src="webapp/html/dialog_confirm_host_delete.html"/> + <meta-include src="webapp/html/dialog_manage_pairings.html"/> - </div> <!-- dialog-container --> + </div> <!-- dialog-container --> - <div id="session-mode" - data-ui-mode="in-session home.client" - class="full-height" - hidden> + <div id="session-mode" + data-ui-mode="in-session home.client" + class="full-height" + hidden> - <meta-include src="webapp/html/toolbar.html"/> - <meta-include src="webapp/html/client_plugin.html"/> + <meta-include src="webapp/html/toolbar.html"/> + <meta-include src="webapp/html/client_plugin.html"/> - </div> + </div> <!-- session-mode --> + + <div id="statistics" dir="ltr" class="selectable" hidden> + </div> - <div id="statistics" dir="ltr" class="selectable" hidden> - </div> + </div> <!-- scroller -> </body> </html> diff --git a/remoting/webapp/html/toolbar.html b/remoting/webapp/html/toolbar.html index eeead15..74fecbd 100644 --- a/remoting/webapp/html/toolbar.html +++ b/remoting/webapp/html/toolbar.html @@ -23,7 +23,8 @@ found in the LICENSE file. <button id="toolbar-disconnect" type="button" - i18n-content="DISCONNECT_MYSELF_BUTTON"> + i18n-content="DISCONNECT_MYSELF_BUTTON" + class="apps-v1-only"> </button> <span class="menu-button" id="send-keys-menu"> @@ -47,9 +48,12 @@ found in the LICENSE file. <ul> <li id="screen-resize-to-client" i18n-content="RESIZE_TO_CLIENT"></li> - <li id="screen-shrink-to-fit" i18n-content="SHRINK_TO_FIT"></li> - <li class="menu-separator"></li> - <li id="toggle-full-screen" i18n-content="FULL_SCREEN"></li> + <li id="screen-shrink-to-fit" + i18n-content="SHRINK_TO_FIT"></li> + <li class="menu-separator apps-v1-only"></li> + <li id="toggle-full-screen" + i18n-content="FULL_SCREEN" + class="apps-v1-only"></li> </ul> </span> diff --git a/remoting/webapp/html/window_frame.html b/remoting/webapp/html/window_frame.html new file mode 100644 index 0000000..3b24d48 --- /dev/null +++ b/remoting/webapp/html/window_frame.html @@ -0,0 +1,29 @@ +<!-- +Copyright 2014 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<div id="title-bar" class="title-bar apps-v2-only"> + <span class="window-title"> </span> + <span class="window-controls-hover-target"> + <div class="window-controls"> + <span i18n-title="DISCONNECT_MYSELF_BUTTON" + class="window-control window-disconnect"> + <img src="icon_disconnect.webp"> + </span> + <span i18n-title="MINIMIZE_WINDOW" + class="window-control window-minimize"> + <img src="icon_minimize.webp"> + </span> + <span i18n-title="MAXIMIZE_WINDOW" + class="window-control window-maximize-restore"> + <img src="icon_maximize_restore.webp"> + </span> + <span i18n-title="CLOSE_WINDOW" + class="window-control window-close"> + <img src="icon_close.webp"> + </span> + </div> + <div class="window-controls-stub"> </div> + </span> +</div> diff --git a/remoting/webapp/js_proto/chrome_proto.js b/remoting/webapp/js_proto/chrome_proto.js index 273910e..62ddc12 100644 --- a/remoting/webapp/js_proto/chrome_proto.js +++ b/remoting/webapp/js_proto/chrome_proto.js @@ -287,6 +287,7 @@ var AppWindow = function() { AppWindow.prototype.close = function() {}; AppWindow.prototype.drawAttention = function() {}; +AppWindow.prototype.maximize = function() {}; AppWindow.prototype.minimize = function() {}; AppWindow.prototype.restore = function() {}; AppWindow.prototype.fullscreen = function() {}; diff --git a/remoting/webapp/main.css b/remoting/webapp/main.css index ec616a8..ea741ed 100644 --- a/remoting/webapp/main.css +++ b/remoting/webapp/main.css @@ -20,6 +20,7 @@ tfoot, thead, tr, th, td, button { .inset { padding: 20px 20px 0 20px; + position: relative; } body { @@ -30,6 +31,7 @@ body { direction: __MSG_@@bidi_dir__; } + /* * The "app-v2" class is added to the <html> node by remoting.init if it's * running as a V2 app. @@ -621,7 +623,7 @@ button { } .dialog-screen { - position: fixed; + position: absolute; top: 0; left: 0; width: 100%; diff --git a/remoting/webapp/remoting.js b/remoting/webapp/remoting.js index 893edfd..adc26a7 100644 --- a/remoting/webapp/remoting.js +++ b/remoting/webapp/remoting.js @@ -69,6 +69,8 @@ remoting.init = function() { if (remoting.isAppsV2) { remoting.identity = new remoting.Identity(consentRequired_); remoting.fullscreen = new remoting.FullscreenAppsV2(); + remoting.windowFrame = new remoting.WindowFrame( + document.getElementById('title-bar')); } else { remoting.oauth2 = new remoting.OAuth2(); if (!remoting.oauth2.isAuthenticated()) { diff --git a/remoting/webapp/toolbar.css b/remoting/webapp/toolbar.css index 3726a29..a039227 100644 --- a/remoting/webapp/toolbar.css +++ b/remoting/webapp/toolbar.css @@ -4,7 +4,7 @@ */ .toolbar-container { - position: fixed; + position: absolute; top: -48px; width: 640px; -webkit-transition: top 0.15s ease; diff --git a/remoting/webapp/window_frame.css b/remoting/webapp/window_frame.css new file mode 100644 index 0000000..a079ec1 --- /dev/null +++ b/remoting/webapp/window_frame.css @@ -0,0 +1,174 @@ +/* Copyright 2014 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +html.apps-v2, +html.apps-v2 body { + height: 100%; + width: 100%; +} + +html.apps-v2 body:not(.fullscreen) { + border: 1px solid gray; /* This is the window border. */ +} + +html.apps-v2 .title-bar { + border-bottom: 1px solid gray; + z-index: 100; +} + +.window-title, +.window-controls-hover-target { + height: 32px; + line-height: 32px; + font-size: 16px; + background-color: #c4c4c4; +} + +.title-bar .window-title { + padding-__MSG_@@bidi_start_edge__: 12px; + width: 100%; + display: inline-block; + -webkit-app-region: drag; +} + +.window-controls-hover-target { + -webkit-app-region: no-drag; + position: fixed; + top: 1px; + __MSG_@@bidi_end_edge__: 1px; +} + +.window-controls-hover-target { + display: table; +} + +.window-controls-hover-target > div:first-child { + display: table-row; +} + +.window-control { + height: 32px; + width: 32px; + text-align: center; + display: inline-block; + border-__MSG_@@bidi_start_edge__: 1px solid rgba(0, 0, 0, 0.2); +} + +.window-control:hover { + background-color: #d5d5d5; +} + +.window-control:active { + background-color: #a6a6a6; +} + +.window-control > img { + margin-bottom: -2px; +} + +.window-controls-stub { + display: none; + -webkit-column-span: all; + line-height: 3px; + background: url("drag.webp"); + border-top: 1px solid rgba(0, 0, 0, 0.2); +} + +#scroller { + height: 100%; + width: 100%; + overflow: auto; + position: relative; +} + +html.apps-v2 #scroller { + height: calc(100% - 32px); /** Allow space for the title-bar */ +} + +/* Add an etched border to the window controls, title bar and stub */ +.title-bar, +.window-control, +.window-controls-stub { + position: relative; +} + +.title-bar:after, +.window-control:after, +.window-controls-stub:after { + content: ""; + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + border-left: 1px solid rgba(255, 255, 255, 0.2); + border-top: 1px solid rgba(255, 255, 255, 0.2); + pointer-events: none; +} + + +/* When connected to a host, the Disconnect button is displayed. */ +body:not(.connected) .window-disconnect { + display: none; +} + + +/* + * When in full-screen mode, significant changes are made: + * - The scroll-bars are removed. + * - The window controls have a border (so the left-border of the first button + * is not needed). + * - The title-bar (and its bottom border) are not displayed. + * - The stub is visible. + * - The window controls gain transition effects for position and opacity and + * auto-hide behind the top edge of the screen. + * - A border is added to the window controls to ensure they stand out against + * any desktop. + * - The window border is removed. + */ + +html.apps-v2 body.fullscreen #scroller { + height: 100%; + overflow: hidden; +} + +body.fullscreen .window-controls-hover-target { + border: 1px solid #a6a6a6; +} + +body.fullscreen .window-control:first-child { + border-__MSG_@@bidi_start_edge__: none; +} + +body.fullscreen .window-title { + display: none; +} + +body.fullscreen .title-bar { + border-bottom: none; +} + +body.fullscreen .window-controls-stub { + display: table-cell; +} + +body.fullscreen .window-controls-hover-target { + transition-property: opacity, box-shadow, top; + transition-duration: 0.3s; + opacity: 0.7; + top: -33px; + __MSG_@@bidi_end_edge__: 8px; +} + +body.fullscreen .window-controls-hover-target:hover, +body.fullscreen .window-controls-hover-target.opened { + top: -4px; + opacity: 1.0; + box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.5); +} + +.fullscreen .window-controls-hover-target.opened .window-controls-stub { + background-color: #a6a6a6; +} diff --git a/remoting/webapp/window_frame.js b/remoting/webapp/window_frame.js new file mode 100644 index 0000000..b4f3d74 --- /dev/null +++ b/remoting/webapp/window_frame.js @@ -0,0 +1,175 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * Apps v2 custom title bar implementation + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** + * @param {HTMLElement} titleBar The root node of the title-bar DOM hierarchy. + * @constructor + */ +remoting.WindowFrame = function(titleBar) { + /** + * @type {boolean} + * @private + */ + this.isConnected_ = false; + + /** + * @type {HTMLElement} + * @private + */ + this.titleBar_ = titleBar; + + /** + * @type {HTMLElement} + * @private + */ + this.hoverTarget_ = /** @type {HTMLElement} */ + (titleBar.querySelector('.window-controls-hover-target')); + base.debug.assert(this.hoverTarget_ != null); + + /** + * @type {HTMLElement} + * @private + */ + this.maximizeRestoreControl_ = /** @type {HTMLElement} */ + (titleBar.querySelector('.window-maximize-restore')); + base.debug.assert(this.maximizeRestoreControl_ != null); + + /** + * @type {Array.<{cls:string, fn: function()}>} + */ + var handlers = [ + { cls: 'window-disconnect', fn: this.disconnectSession_.bind(this) }, + { cls: 'window-maximize-restore', + fn: this.maximizeOrRestoreWindow_.bind(this) }, + { cls: 'window-minimize', fn: this.minimizeWindow_.bind(this) }, + { cls: 'window-close', fn: window.close.bind(window) }, + { cls: 'window-controls-stub', fn: this.toggleWindowControls_.bind(this) } + ]; + for (var i = 0; i < handlers.length; ++i) { + var element = titleBar.querySelector('.' + handlers[i].cls); + base.debug.assert(element != null); + element.addEventListener('click', handlers[i].fn, false); + } + + // Ensure that tool-tips are always correct. + this.updateMaximizeOrRestoreIconTitle_(); + chrome.app.window.current().onMaximized.addListener( + this.updateMaximizeOrRestoreIconTitle_.bind(this)); + chrome.app.window.current().onRestored.addListener( + this.updateMaximizeOrRestoreIconTitle_.bind(this)); + chrome.app.window.current().onFullscreened.addListener( + this.updateMaximizeOrRestoreIconTitle_.bind(this)); +}; + +/** + * @param {boolean} isConnected True if there is a connection active. + */ +remoting.WindowFrame.prototype.setConnected = function(isConnected) { + this.isConnected_ = isConnected; + if (this.isConnected_) { + document.body.classList.add('connected'); + } else { + document.body.classList.remove('connected'); + } + this.updateMaximizeOrRestoreIconTitle_(); +}; + +/** + * @return {{width: number, height: number}} The size of the window, ignoring + * the title-bar and window borders, if visible. + */ +remoting.WindowFrame.prototype.getClientArea = function() { + if (chrome.app.window.current().isFullscreen()) { + return { 'height': window.innerHeight, 'width': window.innerWidth }; + } else { + var kBorderWidth = 1; + var titleHeight = this.titleBar_.clientHeight; + return { + 'height': window.innerHeight - titleHeight - 2 * kBorderWidth, + 'width': window.innerWidth - 2 * kBorderWidth + }; + } +}; + +/** + * @private + */ +remoting.WindowFrame.prototype.disconnectSession_ = function() { + // When the user disconnects, exit full-screen mode. This should not be + // necessary, as we do the same thing in client_session.js when the plugin + // is removed. However, there seems to be a bug in chrome.AppWindow.restore + // that causes it to get stuck in full-screen mode without this. + if (chrome.app.window.current().isFullscreen()) { + chrome.app.window.current().restore(); + chrome.app.window.current().restore(); + } + remoting.disconnect(); +}; + +/** + * @private + */ +remoting.WindowFrame.prototype.maximizeOrRestoreWindow_ = function() { + /** @type {boolean} */ + var restore = + chrome.app.window.current().isFullscreen() || + chrome.app.window.current().isMaximized(); + if (restore) { + // Restore twice: once to exit full-screen and once to exit maximized. + // If the app is not full-screen, or went full-screen without first + // being maximized, then the second restore has no effect. + chrome.app.window.current().restore(); + chrome.app.window.current().restore(); + } else { + chrome.app.window.current().maximize(); + } +}; + +/** + * @private + */ +remoting.WindowFrame.prototype.minimizeWindow_ = function() { + chrome.app.window.current().minimize(); +}; + +/** + * @private + */ +remoting.WindowFrame.prototype.toggleWindowControls_ = function() { + this.hoverTarget_.classList.toggle('opened'); +}; + +/** + * Update the tool-top for the maximize/full-screen/restore icon to reflect + * its current behaviour. + * + * @private + */ +remoting.WindowFrame.prototype.updateMaximizeOrRestoreIconTitle_ = function() { + /** @type {string} */ + var tag = ''; + if (chrome.app.window.current().isFullscreen()) { + tag = /*i18n-content*/'EXIT_FULL_SCREEN'; + } else if (chrome.app.window.current().isMaximized()) { + tag = /*i18n-content*/'RESTORE_WINDOW'; + } else if (this.isConnected_) { + tag = /*i18n-content*/'FULL_SCREEN'; + } else { + tag = /*i18n-content*/'MAXIMIZE_WINDOW'; + } + this.maximizeRestoreControl_.title = l10n.getTranslationOrError(tag); +}; + +/** @type {remoting.WindowFrame} */ +remoting.windowFrame = null;
\ No newline at end of file |