Commit 4434355 for novnc.com
commit 44343556e0356e578ca40d454d90e4993ba2db12
Author: Tobias <tobfa@cendio.se>
Date: Wed Feb 18 10:50:23 2026 +0100
Enable attaching control bar to top and bottom
diff --git a/app/styles/base.css b/app/styles/base.css
index 5834935..9fd9fb3 100644
--- a/app/styles/base.css
+++ b/app/styles/base.css
@@ -117,7 +117,8 @@ html {
.noVNC_center > * {
pointer-events: auto;
}
-.noVNC_vcenter {
+
+.noVNC_crosscenter {
display: flex !important;
flex-direction: column;
justify-content: center;
@@ -129,9 +130,29 @@ html {
padding: 0 !important;
pointer-events: none;
}
-.noVNC_vcenter > * {
+.noVNC_crosscenter > * {
pointer-events: auto;
}
+.noVNC_right .noVNC_crosscenter {
+ left: auto;
+ right: 0;
+}
+.noVNC_top.noVNC_crosscenter,
+.noVNC_top .noVNC_crosscenter {
+ flex-direction: row;
+ width: 100%;
+ height: auto;
+}
+.noVNC_bottom.noVNC_crosscenter,
+.noVNC_bottom .noVNC_crosscenter {
+ flex-direction: row;
+ width: 100%;
+ height: auto;
+}
+.noVNC_bottom .noVNC_crosscenter {
+ top: auto;
+ bottom: 0;
+}
/* ----------------------------------------
* Layering
@@ -231,10 +252,18 @@ html {
:root.noVNC_connected #noVNC_control_bar_anchor.noVNC_idle {
opacity: 0.8;
}
+#noVNC_control_bar_anchor:is(.noVNC_top, .noVNC_bottom) {
+ /* Edge misrenders animations wihthout this */
+ transform: translateY(0);
+}
#noVNC_control_bar_anchor.noVNC_right {
left: auto;
right: 0;
}
+#noVNC_control_bar_anchor.noVNC_bottom {
+ top: auto;
+ bottom: 0;
+}
#noVNC_control_bar {
position: relative;
@@ -249,10 +278,34 @@ html {
-webkit-user-select: none;
-webkit-touch-callout: none; /* Disable iOS image long-press popup */
}
+.noVNC_right #noVNC_control_bar {
+ left: 100%;
+ border-radius: 12px 0 0 12px;
+}
+.noVNC_top #noVNC_control_bar {
+ left: auto;
+ /* FIXME: We want to mirror the left and right modes here and use a
+ relative top offset (-100%), but it doesn't resolve
+ correctly against the anchor height reference */
+ top: -55px;
+ border-radius: 0 0 12px 12px;
+}
+.noVNC_bottom #noVNC_control_bar {
+ left: auto;
+ /* FIXME: We want to mirror the left and right modes here and use a
+ relative top offset (100%), but it doesn't resolve
+ correctly against the anchor height reference */
+ top: 55px;
+ border-radius: 12px 12px 0 0;
+}
#noVNC_control_bar.noVNC_open {
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
left: 0;
}
+:is(.noVNC_top, .noVNC_bottom) #noVNC_control_bar.noVNC_open {
+ left: auto;
+ top: 0;
+}
#noVNC_control_bar::before {
/* This extra element is to get a proper shadow */
content: "";
@@ -263,20 +316,23 @@ html {
left: -30px;
transition: box-shadow 0.5s ease-in-out;
}
-#noVNC_control_bar.noVNC_open::before {
- box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
-}
-.noVNC_right #noVNC_control_bar {
- left: 100%;
- border-radius: 12px 0 0 12px;
+.noVNC_right #noVNC_control_bar::before {
+ visibility: hidden;
}
-.noVNC_right #noVNC_control_bar.noVNC_open {
- left: 0;
+.noVNC_top #noVNC_control_bar::before {
+ height: 30px;
+ width: 100%;
+ top: -30px;
+ bottom: auto;
}
-.noVNC_right #noVNC_control_bar::before {
+.noVNC_bottom #noVNC_control_bar::before {
visibility: hidden;
}
+#noVNC_control_bar.noVNC_open::before {
+ box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
+}
+
#noVNC_control_bar_handle {
position: absolute;
left: -15px;
@@ -288,41 +344,96 @@ html {
cursor: pointer;
border-radius: 6px;
background-color: var(--novnc-darkblue);
- background-image: url("../images/handle_bg.svg");
- background-repeat: no-repeat;
- background-position: right;
box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.5);
}
+:root:not(.noVNC_connected) #noVNC_control_bar_handle {
+ display: none;
+}
+:is(.noVNC_top, .noVNC_bottom) #noVNC_control_bar_handle {
+ transform: translateX(35px);
+ top: -15px;
+ left: 0;
+ width: 50px;
+ height: calc(100% + 30px);
+}
+
+#noVNC_control_bar_handle::before {
+ content: "";
+ background: url("../images/handle_bg.svg");
+ background-repeat: no-repeat;
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 15px;
+ height: 50px;
+}
+.noVNC_right #noVNC_control_bar_handle::before {
+ left: 0;
+ right: auto;
+}
+.noVNC_top #noVNC_control_bar_handle::before {
+ left: 0;
+ right: auto;
+ transform-origin: bottom left;
+ transform: rotate(90deg) translateX(20px);
+}
+.noVNC_bottom #noVNC_control_bar_handle::before {
+ left: 0;
+ right: auto;
+ transform-origin: bottom left;
+ transform: rotate(90deg) translateX(-50px);
+}
+
#noVNC_control_bar_handle:after {
content: "";
transition: transform 0.5s ease-in-out;
- background: url("../images/handle.svg");
+ background: url("../images/handle.svg") no-repeat center;
+ background-size: 5px 6px;
position: absolute;
- top: 22px; /* (50px-6px)/2 */
- right: 5px;
- width: 5px;
- height: 6px;
+ top: 20px; /* (50px-10px)/2 */
+ right: 3px;
+ transform: none;
+ width: 10px;
+ height: 10px;
+ transform-origin: center;
}
-#noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
- transform: translateX(1px) rotate(180deg);
+.noVNC_right #noVNC_control_bar_handle:after {
+ left: 3px;
+ right: auto;
+ transform: rotate(180deg);
}
-:root:not(.noVNC_connected) #noVNC_control_bar_handle {
- display: none;
+.noVNC_top #noVNC_control_bar_handle:after {
+ left: 20px;
+ right: auto;
+ top: auto;
+ bottom: 3px;
+ transform: rotate(90deg);
}
-.noVNC_right #noVNC_control_bar_handle {
- background-position: left;
+.noVNC_bottom #noVNC_control_bar_handle:after {
+ left: 20px;
+ right: auto;
+ top: 3px;
+ bottom: auto;
+ transform: rotate(-90deg);
}
-.noVNC_right #noVNC_control_bar_handle:after {
- left: 5px;
- right: 0;
+
+#noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
transform: translateX(1px) rotate(180deg);
}
.noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
- transform: none;
+ transform: translateX(-1px);
+}
+.noVNC_top #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
+ transform: translateY(1px) rotate(-90deg);
}
+.noVNC_bottom #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after {
+ transform: translateY(-1px) rotate(90deg);
+}
+
/* Larger touch area for the handle, used when a touch screen is available */
#noVNC_control_bar_handle div {
position: absolute;
+ left: auto;
right: -35px;
top: 0;
width: 50px;
@@ -338,6 +449,22 @@ html {
left: -35px;
right: auto;
}
+.noVNC_top #noVNC_control_bar_handle div {
+ left: 0;
+ right: auto;
+ top: auto;
+ bottom: -35px;
+ width: 100%;
+ height: 50px;
+}
+.noVNC_bottom #noVNC_control_bar_handle div {
+ left: 0;
+ right: auto;
+ top: -35px;
+ bottom: auto;
+ width: 100%;
+ height: 50px;
+}
#noVNC_control_bar > .noVNC_scroll {
max-height: 100vh; /* Chrome is buggy with 100% */
@@ -350,23 +477,38 @@ html {
align-items: center;
gap: 10px 0;
}
+:is(.noVNC_top, .noVNC_bottom) > #noVNC_control_bar > .noVNC_scroll {
+ max-width: 100vw; /* Chrome is buggy with 100% */
+ overflow-x: auto;
+ overflow-y: hidden;
+ flex-direction: row;
+ gap: 0 10px;
+}
/* Control bar hint */
-#noVNC_hint_anchor {
+.noVNC_hint_anchor {
position: fixed;
+ left: -50px;
+}
+.noVNC_hint_anchor.noVNC_right {
+ left: auto;
right: -50px;
+}
+.noVNC_hint_anchor.noVNC_top {
left: auto;
+ top: -50px;
}
-#noVNC_control_bar_anchor.noVNC_right + #noVNC_hint_anchor {
- left: -50px;
- right: auto;
+.noVNC_hint_anchor.noVNC_bottom {
+ left: auto;
+ top: auto;
+ bottom: -50px;
}
-#noVNC_control_bar_hint {
- position: relative;
- transform: scale(0);
+.noVNC_control_bar_hint {
width: 100px;
height: 50%;
max-height: 600px;
+ position: relative;
+ transform: scale(0);
visibility: hidden;
opacity: 0;
@@ -376,13 +518,19 @@ html {
border-radius: 12px;
transition-delay: 0s;
}
-#noVNC_control_bar_hint.noVNC_active {
+:is(.noVNC_top, .noVNC_bottom) .noVNC_control_bar_hint {
+ width: 50%;
+ height: 100px;
+ max-width: 600px;
+ max-height: none;
+}
+.noVNC_control_bar_hint.noVNC_active {
visibility: visible;
opacity: 1;
transition-delay: 0.2s;
transform: scale(1);
}
-#noVNC_control_bar_hint.noVNC_notransition {
+.noVNC_control_bar_hint.noVNC_notransition {
transition: none !important;
}
@@ -390,7 +538,6 @@ html {
#noVNC_control_bar .noVNC_button {
min-width: unset;
padding: 4px 4px;
- vertical-align: middle;
border:1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
background-color: transparent;
@@ -411,7 +558,7 @@ html {
box-sizing: border-box; /* so max-width don't have to care about padding */
max-width: calc(100vw - 75px - 25px); /* minus left and right margins */
- max-height: 100vh; /* Chrome is buggy with 100% */
+ max-height: calc(100vh - 75px - 25px); /* minus top and bottom margins */
overflow-x: hidden;
overflow-y: auto;
@@ -431,16 +578,24 @@ html {
opacity: 1;
transform: translateX(75px);
}
-.noVNC_right .noVNC_vcenter {
- left: auto;
- right: 0;
-}
.noVNC_right .noVNC_panel {
transform: translateX(-25px);
}
.noVNC_right .noVNC_panel.noVNC_open {
transform: translateX(-75px);
}
+.noVNC_top .noVNC_panel {
+ transform: translateY(25px);
+}
+.noVNC_top .noVNC_panel.noVNC_open {
+ transform: translateY(75px);
+}
+.noVNC_bottom .noVNC_panel {
+ transform: translateY(-25px);
+}
+.noVNC_bottom .noVNC_panel.noVNC_open {
+ transform: translateY(-75px);
+}
.noVNC_panel > * {
display: block;
@@ -546,9 +701,17 @@ html {
/* Remove all but top border */
border: none;
border-top: 1px solid rgba(255, 255, 255, 0.2);
- width: 100%;
+ width: 35px;
+ height: 1px;
margin: 0;
}
+:is(.noVNC_top, .noVNC_bottom) .noVNC_logo + hr {
+ /* Remove all but left border */
+ border-left: 1px solid rgba(255, 255, 255, 0.2);
+ border-top: none;
+ width: 1px;
+ height: 35px;
+}
:root:not(.noVNC_connected) #noVNC_view_drag_button {
display: none;
@@ -587,6 +750,10 @@ html {
#noVNC_modifiers > * {
margin: 0;
}
+:is(.noVNC_top, .noVNC_bottom) #noVNC_modifiers {
+ flex-direction: row;
+ gap: 0 10px;
+}
/* Shutdown/Reboot */
:root:not(.noVNC_connected) #noVNC_power_button {
diff --git a/app/ui.js b/app/ui.js
index 24d32d5..2f9e602 100644
--- a/app/ui.js
+++ b/app/ui.js
@@ -37,6 +37,8 @@ const UI = {
controlbarGrabbed: false,
controlbarDrag: false,
+ controlbarMouseDownClientX: 0,
+ controlbarMouseDownOffsetX: 0,
controlbarMouseDownClientY: 0,
controlbarMouseDownOffsetY: 0,
@@ -110,8 +112,11 @@ const UI = {
}
// Restore control bar position
- if (WebUtil.readSetting('controlbar_pos') === 'right') {
- UI.toggleControlbarSide();
+ const pos = WebUtil.readSetting('controlbar_pos');
+ if (['left', 'right', 'top', 'bottom'].includes(pos)) {
+ UI.toggleControlbarSide(pos);
+ } else {
+ UI.toggleControlbarSide('left');
}
UI.initFullscreen();
@@ -575,7 +580,15 @@ const UI = {
}
},
- toggleControlbarSide() {
+ getControlbarPos() {
+ const anchor = document.getElementById('noVNC_control_bar_anchor');
+ if (anchor.classList.contains('noVNC_right')) return 'right';
+ if (anchor.classList.contains('noVNC_top')) return 'top';
+ if (anchor.classList.contains('noVNC_bottom')) return 'bottom';
+ return 'left';
+ },
+
+ toggleControlbarSide(pos) {
// Temporarily disable animation, if bar is displayed, to avoid weird
// movement. The transitionend-event will not fire when display=none.
const bar = document.getElementById('noVNC_control_bar');
@@ -586,13 +599,12 @@ const UI = {
}
const anchor = document.getElementById('noVNC_control_bar_anchor');
- if (anchor.classList.contains("noVNC_right")) {
- WebUtil.writeSetting('controlbar_pos', 'left');
- anchor.classList.remove("noVNC_right");
- } else {
- WebUtil.writeSetting('controlbar_pos', 'right');
- anchor.classList.add("noVNC_right");
+
+ anchor.classList.remove('noVNC_right', 'noVNC_top', 'noVNC_bottom');
+ if (['right', 'top', 'bottom'].includes(pos)) {
+ anchor.classList.add(`noVNC_${pos}`);
}
+ WebUtil.writeSetting('controlbar_pos', pos);
// Consider this a movement of the handle
UI.controlbarDrag = true;
@@ -602,19 +614,21 @@ const UI = {
},
showControlbarHint(show, animate=true) {
- const hint = document.getElementById('noVNC_control_bar_hint');
+ const getPos = element =>
+ ['right', 'top', 'bottom'].find(pos =>
+ element.classList.contains(`noVNC_${pos}`)
+ ) ?? 'left';
- if (animate) {
- hint.classList.remove("noVNC_notransition");
- } else {
- hint.classList.add("noVNC_notransition");
- }
+ const anchor = document.getElementById('noVNC_control_bar_anchor');
+ const anchorPos = getPos(anchor);
- if (show) {
- hint.classList.add("noVNC_active");
- } else {
- hint.classList.remove("noVNC_active");
- }
+ document.querySelectorAll('.noVNC_control_bar_hint').forEach((hint) => {
+ const hintPos = getPos(hint.parentElement);
+ const shouldShow = show && (hintPos !== anchorPos);
+
+ hint.classList.toggle('noVNC_active', shouldShow);
+ hint.classList.toggle('noVNC_notransition', !animate || !shouldShow);
+ });
},
dragControlbarHandle(e) {
@@ -622,28 +636,62 @@ const UI = {
const ptr = getPointerEvent(e);
- const anchor = document.getElementById('noVNC_control_bar_anchor');
- if (ptr.clientX < (window.innerWidth * 0.1)) {
- if (anchor.classList.contains("noVNC_right")) {
- UI.toggleControlbarSide();
+ let controlBarPos = UI.getControlbarPos();
+
+ if (ptr.clientX < (window.innerWidth * 0.1) &&
+ ptr.clientY > (window.innerHeight * 0.25) &&
+ ptr.clientY < (window.innerHeight * 0.75)) {
+ if (controlBarPos !== 'left') {
+ UI.toggleControlbarSide('left');
+ controlBarPos = 'left';
+ }
+
+ } else if (ptr.clientX > (window.innerWidth * 0.9) &&
+ ptr.clientY > (window.innerHeight * 0.25) &&
+ ptr.clientY < (window.innerHeight * 0.75)) {
+ if (controlBarPos !== 'right') {
+ UI.toggleControlbarSide('right');
+ controlBarPos = 'right';
}
- } else if (ptr.clientX > (window.innerWidth * 0.9)) {
- if (!anchor.classList.contains("noVNC_right")) {
- UI.toggleControlbarSide();
+
+ // Slightly increased height thresholds since 10% of the
+ // height proved small in practice
+ } else if (ptr.clientX > (window.innerWidth * 0.25) &&
+ ptr.clientX < (window.innerWidth * 0.75) &&
+ ptr.clientY < (window.innerHeight * 0.2)) {
+ if (controlBarPos !== 'top') {
+ UI.toggleControlbarSide('top');
+ controlBarPos = 'top';
+ }
+
+ } else if (ptr.clientX > (window.innerWidth * 0.25) &&
+ ptr.clientX < (window.innerWidth * 0.75) &&
+ ptr.clientY > (window.innerHeight * 0.8)) {
+ if (controlBarPos !== 'bottom') {
+ UI.toggleControlbarSide("bottom");
+ controlBarPos = 'bottom';
}
}
+ const isVertical = controlBarPos === 'left' || controlBarPos === 'right';
+
if (!UI.controlbarDrag) {
- const dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
+ const dragDistance = isVertical
+ ? Math.abs(ptr.clientY - UI.controlbarMouseDownClientY)
+ : Math.abs(ptr.clientX - UI.controlbarMouseDownClientX);
if (dragDistance < dragThreshold) return;
UI.controlbarDrag = true;
}
- const eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
-
- UI.moveControlbarHandle(eventY);
+ if (isVertical) {
+ const eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
+ UI.moveControlbarHandle(eventY, true);
+ } else {
+ const eventX = ptr.clientX - UI.controlbarMouseDownOffsetX;
+ UI.moveControlbarHandle(eventX, false);
+ }
e.preventDefault();
e.stopPropagation();
@@ -652,41 +700,56 @@ const UI = {
},
// Move the handle but don't allow any position outside the bounds
- moveControlbarHandle(viewportRelativeY) {
+ moveControlbarHandle(viewportRelativeCoord, isVertical) {
const handle = document.getElementById("noVNC_control_bar_handle");
- const handleHeight = handle.getBoundingClientRect().height;
+
+ const handleSpan = isVertical
+ ? handle.getBoundingClientRect().height
+ : handle.getBoundingClientRect().width;
+
const controlbarBounds = document.getElementById("noVNC_control_bar")
.getBoundingClientRect();
+ const controlbarBoundsStart = isVertical
+ ? controlbarBounds.top
+ : controlbarBounds.left;
+ const controlbarBoundsSpan = isVertical
+ ? controlbarBounds.height
+ : controlbarBounds.width;
+
const margin = 10;
// These heights need to be non-zero for the below logic to work
- if (handleHeight === 0 || controlbarBounds.height === 0) {
+ if (handleSpan === 0 || controlbarBoundsSpan === 0) {
return;
}
- let newY = viewportRelativeY;
+ let newCoord = viewportRelativeCoord;
// Check if the coordinates are outside the control bar
- if (newY < controlbarBounds.top + margin) {
- // Force coordinates to be below the top of the control bar
- newY = controlbarBounds.top + margin;
+ if (newCoord < controlbarBoundsStart + margin) {
+ // Force coordinates to be below the start of the control bar
+ newCoord = controlbarBoundsStart + margin;
- } else if (newY > controlbarBounds.top +
- controlbarBounds.height - handleHeight - margin) {
- // Force coordinates to be above the bottom of the control bar
- newY = controlbarBounds.top +
- controlbarBounds.height - handleHeight - margin;
+ } else if (newCoord > controlbarBoundsStart +
+ controlbarBoundsSpan - handleSpan - margin) {
+ // Force coordinates to be before the end of the control bar
+ newCoord = controlbarBoundsStart +
+ controlbarBoundsSpan - handleSpan - margin;
}
// Corner case: control bar too small for stable position
- if (controlbarBounds.height < (handleHeight + margin * 2)) {
- newY = controlbarBounds.top +
- (controlbarBounds.height - handleHeight) / 2;
+ if (controlbarBoundsSpan < (handleSpan + margin * 2)) {
+ newCoord = controlbarBoundsStart +
+ (controlbarBoundsSpan - handleSpan) / 2;
}
// The transform needs coordinates that are relative to the parent
- const parentRelativeY = newY - controlbarBounds.top;
- handle.style.transform = "translateY(" + parentRelativeY + "px)";
+ const parentRelativeCoord = newCoord - controlbarBoundsStart;
+ if (isVertical) {
+ handle.style.transform = "translateY(" + parentRelativeCoord + "px)";
+ } else {
+ handle.style.transform = "translateX(" + parentRelativeCoord + "px)";
+ }
},
updateControlbarHandle() {
@@ -694,7 +757,15 @@ const UI = {
// the move function expects coordinates relative the the viewport.
const handle = document.getElementById("noVNC_control_bar_handle");
const handleBounds = handle.getBoundingClientRect();
- UI.moveControlbarHandle(handleBounds.top);
+
+ const controlBarPos = UI.getControlbarPos();
+ const isVertical = controlBarPos === 'left' || controlBarPos === 'right';
+
+ if (isVertical) {
+ UI.moveControlbarHandle(handleBounds.top, true);
+ } else {
+ UI.moveControlbarHandle(handleBounds.left, false);
+ }
},
controlbarHandleMouseUp(e) {
@@ -732,6 +803,8 @@ const UI = {
UI.controlbarMouseDownClientY = ptr.clientY;
UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top;
+ UI.controlbarMouseDownClientX = ptr.clientX;
+ UI.controlbarMouseDownOffsetX = ptr.clientX - bounds.left;
e.preventDefault();
e.stopPropagation();
UI.keepControlbar();
diff --git a/vnc.html b/vnc.html
index 84470de..8f65427 100644
--- a/vnc.html
+++ b/vnc.html
@@ -110,8 +110,7 @@
</div>
<!-- noVNC control bar -->
- <div id="noVNC_control_bar_anchor" class="noVNC_vcenter">
-
+ <div id="noVNC_control_bar_anchor" class="noVNC_crosscenter">
<div id="noVNC_control_bar">
<div id="noVNC_control_bar_handle" title="Hide/Show the control bar"><div></div></div>
@@ -137,7 +136,7 @@
<input type="image" alt="Extra keys" src="app/images/toggleextrakeys.svg"
id="noVNC_toggle_extra_keys_button" class="noVNC_button"
title="Show extra keys">
- <div class="noVNC_vcenter">
+ <div class="noVNC_crosscenter">
<div id="noVNC_modifiers" class="noVNC_panel">
<input type="image" alt="Ctrl" src="app/images/ctrl.svg"
id="noVNC_toggle_ctrl_button" class="noVNC_button"
@@ -164,7 +163,7 @@
<input type="image" alt="Shutdown/Reboot" src="app/images/power.svg"
id="noVNC_power_button" class="noVNC_button"
title="Shutdown/Reboot...">
- <div class="noVNC_vcenter">
+ <div class="noVNC_crosscenter">
<div id="noVNC_power" class="noVNC_panel">
<div class="noVNC_heading">
<img alt="" src="app/images/power.svg"> Power
@@ -179,7 +178,7 @@
<input type="image" alt="Clipboard" src="app/images/clipboard.svg"
id="noVNC_clipboard_button" class="noVNC_button"
title="Clipboard">
- <div class="noVNC_vcenter">
+ <div class="noVNC_crosscenter">
<div id="noVNC_clipboard" class="noVNC_panel">
<div class="noVNC_heading">
<img alt="" src="app/images/clipboard.svg"> Clipboard
@@ -200,7 +199,7 @@
<input type="image" alt="Settings" src="app/images/settings.svg"
id="noVNC_settings_button" class="noVNC_button"
title="Settings">
- <div class="noVNC_vcenter">
+ <div class="noVNC_crosscenter">
<div id="noVNC_settings" class="noVNC_panel">
<div class="noVNC_heading">
<img alt="" src="app/images/settings.svg"> Settings
@@ -333,8 +332,20 @@
</div> <!-- End of noVNC_control_bar -->
- <div id="noVNC_hint_anchor" class="noVNC_vcenter">
- <div id="noVNC_control_bar_hint">
+ <div class="noVNC_hint_anchor noVNC_crosscenter">
+ <div class="noVNC_control_bar_hint">
+ </div>
+ </div>
+ <div class="noVNC_hint_anchor noVNC_right noVNC_crosscenter">
+ <div class="noVNC_control_bar_hint">
+ </div>
+ </div>
+ <div class="noVNC_hint_anchor noVNC_top noVNC_crosscenter">
+ <div class="noVNC_control_bar_hint">
+ </div>
+ </div>
+ <div class="noVNC_hint_anchor noVNC_bottom noVNC_crosscenter">
+ <div class="noVNC_control_bar_hint">
</div>
</div>