From aa113a43db884f923929ef6ae7018e5c86d9d902 Mon Sep 17 00:00:00 2001 From: nextestudios Date: Wed, 30 Sep 2026 23:19:39 -0300 Subject: [PATCH 1/2] fix: move sideways across the window cards of the session menu Left/right between the open-window cards now picks the neighbour on the same row by position instead of relying on the XY focus search, which could miss it (cards scaled while focused, clipped by the scrolling panel). The end of a row is an end, and Left on the first column still goes back to the side panel. Co-Authored-By: Claude Sonnet 5.5 --- docs/TESTING.md | 1 + src/ConsoleMode/SessionMenuWindow.xaml.cs | 49 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/docs/TESTING.md b/docs/TESTING.md index 6918d61..fc0e594 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -331,6 +331,7 @@ Pré-condições: 1.6 instalada por cima da 1.5, com a configuração da 1.5 (te - [ ] Durante o jogo, Share + △ (Select + Y) abre o overlay; → em uma opção lateral leva ao primeiro cartão de janela e ✕ ativa a janela escolhida. Resultado: ______ - [ ] No primeiro cartão de uma coluna, ← retorna à opção lateral de origem; ↑/↓ e ←/→ navegam pelos demais cartões, inclusive com rolagem. Resultado: ______ - [ ] Com seletor de sessão ou confirmação de fechamento aberto, as direções ficam dentro do diálogo. Sem janelas abertas, → não perde o foco. Resultado: ______ +- [ ] Com **3 ou mais janelas** abertas (uma linha de cartões): → e ← percorrem a linha cartão a cartão; → no último cartão não faz nada (não pula para outro lugar); ← no primeiro volta à opção lateral de origem. Com várias linhas: → e ← ficam na linha do cartão. Resultado: ______ - [ ] Os cartões não exibem o botão X no canto. Quadrado/Delete continuam pedindo confirmação para fechar a janela selecionada. Resultado: ______ - [ ] A logo fornecida do ControlFS aparece no painel fixo do overlay, tanto instalado quanto ausente, inclusive no aplicativo publicado. Resultado: ______ - [ ] Com overlay aberto ou outro aplicativo em primeiro plano, a tela Console ao fundo não reage ao DualShock. Resultado: ______ diff --git a/src/ConsoleMode/SessionMenuWindow.xaml.cs b/src/ConsoleMode/SessionMenuWindow.xaml.cs index be3ca35..d5439dd 100644 --- a/src/ConsoleMode/SessionMenuWindow.xaml.cs +++ b/src/ConsoleMode/SessionMenuWindow.xaml.cs @@ -93,6 +93,13 @@ public SessionMenuWindow(MainViewModel viewModel, ScreenRect? target) UiSounds.Play(UiSound.Move); return true; } + // Along a row of window cards: pick the neighbour by position, not by the spatial search. + if (!_editingVolume && direction is (FocusNavigationDirection.Left or FocusNavigationDirection.Right) + && !ViewModel.IsSessionPickerOpen && MoveAcrossWindowCards(direction)) + { + UiSounds.Play(UiSound.Move); + return true; + } // Cross into the grid explicitly; spatial focus may prefer another sidebar row. if (!_editingVolume && direction == FocusNavigationDirection.Right && !ViewModel.IsSessionPickerOpen && Root.XamlRoot is { } root @@ -476,6 +483,48 @@ private bool FocusFirstWindowCard() return false; } + /// + /// Left/right between window cards by position: the card on the same row with the next X to that side. + /// The XY focus search can miss the neighbour (cards scaled while focused, clipped or scrolled in the + /// panel), which left the grid impossible to cross sideways. The end of a row is an end: Right on the last + /// card is consumed so the search can't jump somewhere else; Left on the first column is the caller's + /// (back to the side panel). False when the focus is not on a window card. + /// + private bool MoveAcrossWindowCards(FocusNavigationDirection direction) + { + if (Root.XamlRoot is not { } root + || FocusManager.GetFocusedElement(root) is not FrameworkElement { DataContext: SwitchWindowItem } current) + return false; + + Windows.Foundation.Point Origin(FrameworkElement e) => e.TransformToVisual(SwitcherList).TransformPoint(new Windows.Foundation.Point(0, 0)); + var from = Origin(current); + var rowTolerance = Math.Max(current.ActualHeight / 2, 1); + + Control? best = null; + var bestX = 0d; + for (var i = 0; i < ViewModel.SwitcherWindows.Count; i++) + { + if (SwitcherList.ContainerFromIndex(i) is not { } container + || FocusManager.FindFirstFocusableElement(container) is not Control card + || ReferenceEquals(card, current)) + continue; + var at = Origin(card); + if (Math.Abs(at.Y - from.Y) > rowTolerance) continue; + var dx = at.X - from.X; + if (direction == FocusNavigationDirection.Right ? dx <= 1 : dx >= -1) continue; + if (best is null || (direction == FocusNavigationDirection.Right ? at.X < bestX : at.X > bestX)) + { + best = card; + bestX = at.X; + } + } + + if (best is null) return direction == FocusNavigationDirection.Right; + best.Focus(FocusState.Keyboard); + best.StartBringIntoView(); + return true; + } + /// True when the focus is on a window card with no card to its left (the first column of the grid). private bool IsOnLeftmostCard() { From 4971ab15e7a6cf13e33167d550ca468973ef7f6e Mon Sep 17 00:00:00 2001 From: nextestudios Date: Thu, 1 Oct 2026 00:08:47 -0300 Subject: [PATCH 2/2] docs(changelog): reference #133 Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.en-US.md | 3 +++ CHANGELOG.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 4a7cc71..2ca86a7 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -4,6 +4,9 @@ English (US) release notes, mirroring CHANGELOG.md (Brazilian Portuguese). Befor ## [Unreleased] +### Fixes +- Session menu: ← and → move across the open-window cards by position without failing; nothing moves past the last card of a row. (#133) + ## [1.6.0-alpha.5] ### Fixes diff --git a/CHANGELOG.md b/CHANGELOG.md index 5074c8d..00dace5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Notas em português do Brasil; a versão em inglês (Estados Unidos) fica em `CH ## [Unreleased] +### Correções +- Menu da sessão: ← e → percorrem os cartões das janelas abertas pela posição, sem falhar; no último cartão da linha nada se move. (#133) + ## [1.6.0-alpha.5] ### Correções