A browser counted as a touch screen only if it had no fine pointer at all. A Galaxy phone with an S Pen reports one, so it got the desktop layout, where nothing answered a tap or a swipe on the picture. Now its main pointer decides. Taps and swipes are also taken from a finger in any layout, as on a laptop's touch screen. On TV a swipe over the banner, or over the timeline, counts as one over the picture. The browser no longer takes a drag on the banner as a scroll, which ended the swipe.
- commit
- 6129c5cc215d1cf96465e8d95f51f0b14a43c950
- parent
- f6cf4475353c7a4ce39af19307372557698e221a
- author
- FelineStateMachine <226808055+FelineStateMachine@users.noreply.github.com>
- kind
- feat risk score 2.1 of 3, Jev's estimate
| M | README.md | 5 | +++-- |
| M | frontend/ | 17 | ++++++++++------- |
| M | frontend/ | 2 | +- |
| M | frontend/ | 6 | ++++-- |
@@ -189,12 +189,13 @@ classes in style.css):
189189 | Desktop | The app in a window on a Mac, Linux or Windows, or a browser | Mouse and keyboard; letters are shortcuts | Keys |190190 | Console | The Linux app under gamescope (SteamOS Game Mode) | A controller | Its buttons, from the start |191191 | TV | The Android TV app | A remote: arrows, OK, Back and media keys | The remote's keys; hold OK for more |192-| Touch | A phone or a tablet, at `/tv/` or installed from it | Taps, swipes and holds | None |192+| Touch | A phone or a tablet (a browser whose main pointer is a finger), at `/tv/` or installed from it | Taps, swipes and holds | None |193193 194194 The app says which it is (`AIRWAVES_PLATFORM=desktop`, `console` or `tv` overrides the195195 desktop app's guess; `?platform=` does the same at `/tv/`), and Settings, Display, Layout196196 can change it on one device. Console and TV start at 125%, sized for across a room. A197-controller or a keyboard used on any of them still brings its own hints.197+controller or a keyboard used on any of them still brings its own hints, and a finger its198+gestures.198199 199200 On touch, a tap on TV brings up the controls, and a tap anywhere off a button puts them200201 away, as a phone's video players do; a swipe left or right@@ -17,7 +17,9 @@ const TOUCH_SWIPE = 48; // CSS pixels a finger travels for a swipe
1717 const TOUCH_HOLD_MS = 550; // a press this long is a hold1818 const TOUCH_MOUSE_MS = 1000; // a finger's stand-in mouse events follow it within this1919 20-const touching = (e) => platform === 'touch' && e.pointerType !== 'mouse';20+// A finger's or a pen's pointer: its gestures are a finger's in any21+// layout, as on a laptop's touch screen.22+const touching = (e) => e.pointerType !== 'mouse';2123 2224 // A finger also sends a mouse's events after it, for pages made for one.2325 // Here they'd act twice: its move is a pointer brought to the screen, which@@ -25,9 +27,10 @@ const touching = (e) => platform === 'touch' && e.pointerType !== 'mouse';
2527 // double click, which plays what the second tap already played. What a2628 // finger does is handled here, from its pointer events.2729 const fromFinger = () => performance.now() - touch.at < TOUCH_MOUSE_MS;30+const fingerTap = () => platform === 'touch' || fromFinger();2831 2932 function wireTouch() {30- const lastFinger = (e) => { if (e.pointerType !== 'mouse') touch.at = performance.now(); };33+ const lastFinger = (e) => { if (touching(e)) touch.at = performance.now(); };3134 document.addEventListener('pointerdown', (e) => {3235 lastFinger(e);3336 if (!touching(e)) return;@@ -68,17 +71,17 @@ function wireTouch() {
6871 // click only to what listens for one, and first (capture), so a tap on6972 // the picture elsewhere, which goes back to TV (wireDock), isn't one.7073 const tapTV = (e) => {71- if (platform !== 'touch' || state.view !== 'tv' || document.body.classList.contains('booting')) return;74+ if (!fingerTap() || state.view !== 'tv' || document.body.classList.contains('booting')) return;7275 if (e.target.closest('button, input, a, .tl, [data-act]')) return;7376 if (osd.open) hideBanner(); else openOSD(null);7477 };7578 for (const el of [stage, $('#banner')]) el.addEventListener('click', tapTV, true);7679 }7780 78-// swipe acts on a swipe over el: on TV, the arrows; in the guide, the79-// hours shown.81+// swipe acts on a swipe over el: on TV, over the picture or the banner,82+// the arrows; in the guide, the hours shown.8083 function swipe(el, dir) {81- if (state.view === 'tv' && (stage.contains(el) || el === document.body)) {84+ if (state.view === 'tv' && !el.closest('#dock')) {8285 if (dir === 'left' || dir === 'right') {8386 skip(dir === 'right' ? 30 : -10);8487 return openOSD('timeline');@@ -105,4 +108,4 @@ function holdCell(cell) {
105108 106109 // tappedAgain reports whether a tap lands on what was already picked: the107110 // guide's, Recordings' and Podcasts' second tap does it.108-const tappedAgain = (was, now) => platform === 'touch' && was === now;111+const tappedAgain = (was, now) => fingerTap() && was === now;@@ -756,7 +756,7 @@ body.mouse-active .ob:hover { color: var(--paper); background: var(--ink-3); }
756756 enough to hit, and the notch and the home bar are left clear. */757757 body.pf-touch { -webkit-tap-highlight-color: transparent; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; touch-action: manipulation; overscroll-behavior: none; }758758 body.pf-touch input { -webkit-user-select: text; user-select: text; }759-body.pf-touch #stage { touch-action: none; }759+body.pf-touch #stage, body.pf-touch #banner { touch-action: none; }760760 body.pf-touch .g-rows { touch-action: pan-y; }761761 body.pf-touch .r-grid, body.pf-touch .s-list, body.pf-touch .a-tablewrap, body.pf-touch .p-menu, body.pf-touch .r-list { overflow-y: auto; touch-action: pan-y; }762762 body.pf-touch .wx-main { overflow-y: auto; touch-action: pan-y; }@@ -131,8 +131,10 @@
131131 132132 const query = new URLSearchParams(location.search);133133 134- // A phone or a tablet: a touch screen, and no mouse or trackpad.135- const touchScreen = !tvApp && matchMedia('(pointer: coarse)').matches && !matchMedia('(any-pointer: fine)').matches;134+ // A phone or a tablet: its main pointer is a finger. Not that it has no135+ // fine one besides: a Galaxy phone's S Pen is one, and a tablet's136+ // keyboard case may bring a trackpad.137+ const touchScreen = !tvApp && matchMedia('(pointer: coarse)').matches;136138 137139 // The Android TV app is the TV experience, a touch screen the touch138140 // experience, a browser a desktop's; ?platform=desktop, console, tv or