슬래시 입력 UX 보강: 포커스/키 처리 통합 및 휠 fallback 추가
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- InputBox_PreviewKeyDown에서 슬래시 네비게이션 키를 우선 처리 - TryHandleSlashNavigationKey로 창/입력창 키 경로 통합(Up/Down/Pg/Home/End/Tab/Enter/Esc) - 가시 항목 0일 때 SlashScrollViewer 오프셋 fallback 추가 - README/DEVELOPMENT 이력(2026-04-04 16:18 KST) 동기화
This commit is contained in:
@@ -4923,6 +4923,9 @@ public partial class ChatWindow : Window
|
||||
|
||||
private async void InputBox_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (TryHandleSlashNavigationKey(e))
|
||||
return;
|
||||
|
||||
// Ctrl+V: 클립보드 이미지 붙여넣기
|
||||
if (e.Key == Key.V && Keyboard.Modifiers.HasFlag(ModifierKeys.Control))
|
||||
{
|
||||
@@ -5571,6 +5574,13 @@ public partial class ChatWindow : Window
|
||||
if (_slashPalette.Matches.Count == 0)
|
||||
return;
|
||||
|
||||
if (GetVisibleSlashOrderedIndices().Count == 0)
|
||||
{
|
||||
if (SlashScrollViewer != null)
|
||||
SlashScrollViewer.ScrollToVerticalOffset(Math.Max(0, SlashScrollViewer.VerticalOffset - delta / 3.0));
|
||||
return;
|
||||
}
|
||||
|
||||
var steps = Math.Max(1, Math.Abs(delta) / 120);
|
||||
var direction = delta > 0 ? -1 : 1;
|
||||
for (var i = 0; i < steps; i++)
|
||||
@@ -10902,61 +10912,69 @@ public partial class ChatWindow : Window
|
||||
}
|
||||
|
||||
// 슬래시 명령 팝업 키 처리
|
||||
if (SlashPopup.IsOpen)
|
||||
if (TryHandleSlashNavigationKey(e))
|
||||
return;
|
||||
|
||||
if (PermissionPopup.IsOpen && e.Key == Key.Escape)
|
||||
{
|
||||
if (e.Key == Key.Escape)
|
||||
{
|
||||
PermissionPopup.IsOpen = false;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryHandleSlashNavigationKey(KeyEventArgs e)
|
||||
{
|
||||
if (!SlashPopup.IsOpen)
|
||||
return false;
|
||||
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Escape:
|
||||
SlashPopup.IsOpen = false;
|
||||
_slashPalette.SelectedIndex = -1;
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Up)
|
||||
{
|
||||
SlashPopup_ScrollByDelta(120); // 위로 1칸
|
||||
return true;
|
||||
case Key.Up:
|
||||
SlashPopup_ScrollByDelta(120);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Down)
|
||||
{
|
||||
SlashPopup_ScrollByDelta(-120); // 아래로 1칸
|
||||
return true;
|
||||
case Key.Down:
|
||||
SlashPopup_ScrollByDelta(-120);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.PageUp)
|
||||
{
|
||||
return true;
|
||||
case Key.PageUp:
|
||||
SlashPopup_ScrollByDelta(600);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.PageDown)
|
||||
{
|
||||
return true;
|
||||
case Key.PageDown:
|
||||
SlashPopup_ScrollByDelta(-600);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Home)
|
||||
return true;
|
||||
case Key.Home:
|
||||
{
|
||||
var visible = GetVisibleSlashOrderedIndices();
|
||||
_slashPalette.SelectedIndex = visible.Count > 0 ? visible[0] : GetFirstVisibleSlashIndex(_slashPalette.Matches);
|
||||
UpdateSlashSelectionVisualState();
|
||||
EnsureSlashSelectionVisible();
|
||||
e.Handled = true;
|
||||
return true;
|
||||
}
|
||||
else if (e.Key == Key.End)
|
||||
case Key.End:
|
||||
{
|
||||
var visible = GetVisibleSlashOrderedIndices();
|
||||
_slashPalette.SelectedIndex = visible.Count > 0 ? visible[^1] : GetFirstVisibleSlashIndex(_slashPalette.Matches);
|
||||
UpdateSlashSelectionVisualState();
|
||||
EnsureSlashSelectionVisible();
|
||||
e.Handled = true;
|
||||
return true;
|
||||
}
|
||||
else if (e.Key == Key.Enter && _slashPalette.SelectedIndex >= 0)
|
||||
{
|
||||
e.Handled = true;
|
||||
case Key.Tab when _slashPalette.SelectedIndex >= 0:
|
||||
case Key.Enter when _slashPalette.SelectedIndex >= 0:
|
||||
ExecuteSlashSelectedItem();
|
||||
}
|
||||
}
|
||||
|
||||
if (PermissionPopup.IsOpen && e.Key == Key.Escape)
|
||||
{
|
||||
PermissionPopup.IsOpen = false;
|
||||
e.Handled = true;
|
||||
e.Handled = true;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user