From ebe39b26dc0f78593a81ea3d8b04ff0bf970d3c6 Mon Sep 17 00:00:00 2001 From: itsjohncs Date: Wed, 6 Oct 2021 22:59:19 -0700 Subject: [PATCH 1/2] Two-finger swipe now switches windows (#3901) The Alt+Up and Alt+Down keybindings on Desktop did not have an equivalent for Mobile users. Now a two-finger swipe left on a touchscreen is equivalent to Alt+Up (similarly swipe right is equivalent to Alt+Down). --- client/js/helpers/listenForTwoFingerSwipes.js | 108 ++++++++++++++++++ client/js/keybinds.js | 20 +++- 2 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 client/js/helpers/listenForTwoFingerSwipes.js diff --git a/client/js/helpers/listenForTwoFingerSwipes.js b/client/js/helpers/listenForTwoFingerSwipes.js new file mode 100644 index 00000000..7be48e87 --- /dev/null +++ b/client/js/helpers/listenForTwoFingerSwipes.js @@ -0,0 +1,108 @@ +"use strict"; + +// onTwoFingerSwipe will be called with a cardinal direction ("n", "e", "s" or +// "w") as its only argument. +function listenForTwoFingerSwipes(onTwoFingerSwipe) { + let history = []; + + document.body.addEventListener( + "touchmove", + function (event) { + if (event.touches.length !== 2) { + return; + } + + const a = event.touches.item(0); + const b = event.touches.item(1); + + const timestamp = window.performance.now(); + const center = [(a.screenX + b.screenX) / 2, (a.screenY + b.screenY) / 2]; + + if (history.length > 0) { + const last = history[history.length - 1]; + const centersAreEqual = + last.center[0] === center[0] && last.center[1] === center[1]; + + if (last.timestamp === timestamp || centersAreEqual) { + // Touches with the same timestamps or center don't help us + // see the speed of movement. Ignore them. + return; + } + } + + history.push({timestamp, center}); + }, + {passive: true} + ); + + document.body.addEventListener( + "touchend", + function () { + if (event.touches.length >= 2) { + return; + } + + try { + const direction = getSwipe(history); + + if (direction) { + onTwoFingerSwipe(direction); + } + } finally { + history = []; + } + }, + {passive: true} + ); + + document.body.addEventListener( + "touchcancel", + function () { + history = []; + }, + {passive: true} + ); +} + +// Returns the cardinal direction of the swipe or null if there is no swipe. +function getSwipe(hist) { + // Speed is in pixels/millisecond. Must be maintained throughout swipe. + const MIN_SWIPE_SPEED = 0.2; + + if (hist.length < 2) { + return null; + } + + for (let i = 1; i < hist.length; ++i) { + const previous = hist[i - 1]; + const current = hist[i]; + + const speed = + distance(previous.center, current.center) / + Math.abs(previous.timestamp - current.timestamp); + + if (speed < MIN_SWIPE_SPEED) { + return null; + } + } + + return getCardinalDirection(hist[0].center, hist[hist.length - 1].center); +} + +function distance([x1, y1], [x2, y2]) { + return Math.hypot(x1 - x2, y1 - y2); +} + +function getCardinalDirection([x1, y1], [x2, y2]) { + // If θ is the angle of the vector then this is tan(θ) + const tangent = (y2 - y1) / (x2 - x1); + + // All values of |tan(-45° to 45°)| are less than 1, same for 145° to 225° + if (Math.abs(tangent) < 1) { + return x1 < x2 ? "e" : "w"; + } + + return y1 < y2 ? "s" : "n"; +} + +export default listenForTwoFingerSwipes; diff --git a/client/js/keybinds.js b/client/js/keybinds.js index daee796f..04e31514 100644 --- a/client/js/keybinds.js +++ b/client/js/keybinds.js @@ -6,6 +6,7 @@ import store from "./store"; import {switchToChannel} from "./router"; import isChannelCollapsed from "./helpers/isChannelCollapsed"; import isIgnoredKeybind from "./helpers/isIgnoredKeybind"; +import listenForTwoFingerSwipes from "./helpers/listenForTwoFingerSwipes"; // Switch to the next/previous window in the channel list. Mousetrap.bind(["alt+up", "alt+down"], function (e, keys) { @@ -13,11 +14,22 @@ Mousetrap.bind(["alt+up", "alt+down"], function (e, keys) { return true; } + navigateWindow(keys.split("+").pop() === "up" ? -1 : 1); + + return false; +}); + +listenForTwoFingerSwipes(function (cardinalDirection) { + if (cardinalDirection === "e" || cardinalDirection === "w") { + navigateWindow(cardinalDirection === "e" ? -1 : 1); + } +}); + +function navigateWindow(direction) { if (store.state.networks.length === 0) { - return false; + return; } - const direction = keys.split("+").pop() === "up" ? -1 : 1; const flatChannels = []; let index = -1; @@ -44,9 +56,7 @@ Mousetrap.bind(["alt+up", "alt+down"], function (e, keys) { index = (((index + direction) % length) + length) % length; jumpToChannel(flatChannels[index]); - - return false; -}); +} // Switch to the next/previous lobby in the channel list Mousetrap.bind(["alt+shift+up", "alt+shift+down"], function (e, keys) { From 91a0815bb5f8be8e34dd6e8c09d431ceb47f82e2 Mon Sep 17 00:00:00 2001 From: itsjohncs Date: Thu, 7 Oct 2021 13:36:56 -0700 Subject: [PATCH 2/2] Add Gestures section to help window. This documents the two gestures that The Lounge currently supports. The section is only visible if your device supports touch. --- client/components/Windows/Help.vue | 31 ++++++++++++++++++++++++++++++ client/css/style.css | 4 ++++ 2 files changed, 35 insertions(+) diff --git a/client/components/Windows/Help.vue b/client/components/Windows/Help.vue index 95067dff..fd1408d6 100644 --- a/client/components/Windows/Help.vue +++ b/client/components/Windows/Help.vue @@ -87,6 +87,36 @@

+

Gestures

+ +
+
Single-Finger Swipe Left
+
+

Hide sidebar.

+
+
+ +
+
Single-Finger Swipe Right
+
+

Show sidebar.

+
+
+ +
+
Two-Finger Swipe Left
+
+

Switch to the next window in the channel list.

+
+
+ +
+
Two-Finger Swipe Right
+
+

Switch to the previous window in the channel list.

+
+
+

Keyboard Shortcuts

@@ -764,6 +794,7 @@ export default { data() { return { isApple: navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) || false, + isTouch: navigator.maxTouchPoints > 0, }; }, }; diff --git a/client/css/style.css b/client/css/style.css index 30bceaaa..44c4d3dd 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -2024,6 +2024,10 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ padding-right: 15px; } +#help .help-item .subject.gesture { + font-weight: bold; +} + #help .help-item .description p { margin-bottom: 0; }