Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: ______
49 changes: 49 additions & 0 deletions src/ConsoleMode/SessionMenuWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -476,6 +483,48 @@ private bool FocusFirstWindowCard()
return false;
}

/// <summary>
/// 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.
/// </summary>
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;
}

/// <summary>True when the focus is on a window card with no card to its left (the first column of the grid).</summary>
private bool IsOnLeftmostCard()
{
Expand Down
Loading