# 패치 03 — 하단 탭 모양 정리 + 선택 인디케이터 슬라이드 애니메이션 첨부하신 첫 번째 사진(아이콘 위 / 라벨 아래 / 라벨 아래 밑줄만) 기준으로 맞추고, 탭을 전환하면 그 밑줄이 선택한 탭으로 **미끄러져 이동**합니다. 화면 컨텐츠는 건드리지 않고 하단 탭 바에만 애니메이션이 들어갑니다. 이 패치는 패치 01·02를 포함해 대체합니다 — `ShellStyles.xaml`의 하단 탭 관련 두 스타일을 통째로 교체합니다. ## 1. 모양 (사진 1 기준) - 칸 사이 세로 구분선 **제거** - 선택 시 상단 3px 바 + 옅은 accent 판 → **바닥 3px 밑줄만** - 선택 배경 틴트 **제거**, 선택된 탭은 글자·아이콘 색만 accent - 마우스 오버는 선택되지 않은 탭에만 옅은 틴트 - 탭은 가운데 모임(균등 분할 아님), 아이템 패딩 `30,13,30,14` ## 2. `src/UbisamBase.Core/Themes/ShellStyles.xaml` `Ubisam.Style.SidebarItem.Horizontal`과 `Ubisam.Style.SidebarBar.Horizontal`을 아래 두 스타일로 교체하세요. `SidebarBar.Horizontal`에는 `ControlTemplate`이 새로 들어갑니다 — 그 안의 `SelectionIndicator` / `SelectionIndicatorShift` 이름을 코드비하인드가 찾습니다. **이름을 바꾸지 마세요.** ```xml ``` ## 3. `src/UbisamBase.Core/Shell/ShellWindow.xaml.cs` — 애니메이션 using 추가: ```csharp using System.Windows.Media.Animation; ``` `ShellWindow` 생성자 마지막에 이벤트 두 개를 붙이세요. ```csharp NavList.SelectionChanged += (_, _) => MoveTabIndicator(animate: true); NavList.Loaded += (_, _) => MoveTabIndicator(animate: false); NavList.SizeChanged += (_, _) => MoveTabIndicator(animate: false); ``` 그리고 메서드를 추가하세요. ```csharp /// 하단 탭의 선택 인디케이터를 선택된 탭 아래로 옮긴다. 좌/우 네비게이션일 때는 인디케이터가 없어 무시된다. private void MoveTabIndicator(bool animate) { if (NavList.Template?.FindName("SelectionIndicator", NavList) is not Border indicator || NavList.Template.FindName("SelectionIndicatorShift", NavList) is not TranslateTransform shift) { return; } if (NavList.SelectedItem == null || NavList.ItemContainerGenerator.ContainerFromItem(NavList.SelectedItem) is not ListBoxItem container || !container.IsArrangeValid) { indicator.Width = 0; return; } var origin = container.TranslatePoint(new Point(0, 0), NavList); var targetX = origin.X; var targetWidth = container.ActualWidth; if (!animate) { shift.BeginAnimation(TranslateTransform.XProperty, null); indicator.BeginAnimation(FrameworkElement.WidthProperty, null); shift.X = targetX; indicator.Width = targetWidth; return; } var duration = new Duration(TimeSpan.FromMilliseconds(260)); var ease = new CubicEase { EasingMode = EasingMode.EaseOut }; shift.BeginAnimation(TranslateTransform.XProperty, new DoubleAnimation(targetX, duration) { EasingFunction = ease, FillBehavior = FillBehavior.HoldEnd }); indicator.BeginAnimation(FrameworkElement.WidthProperty, new DoubleAnimation(targetWidth, duration) { EasingFunction = ease, FillBehavior = FillBehavior.HoldEnd }); } ``` `ApplyNavPosition`에서 위치를 바꾼 뒤에도 한 번 호출해 주세요 (좌/우 → 하단 전환 시 위치 재계산). ```csharp NavList.Width = isHorizontal ? double.NaN : 140; NavList.Height = double.NaN; if (isHorizontal) { Dispatcher.BeginInvoke(new Action(() => MoveTabIndicator(animate: false))); } ``` ## 4. 확인 1. 탭을 누르면 밑줄이 260ms에 걸쳐 미끄러져 오고, 너비도 새 탭 폭에 맞게 함께 변하는지 2. 앱을 처음 켰을 때는 애니메이션 없이 첫 탭 아래에 바로 놓이는지 3. 창 크기를 바꾸거나 최대화할 때 밑줄이 어긋나지 않는지 4. 설정에서 네비게이션 위치를 좌/우로 바꿨다가 하단으로 되돌렸을 때 밑줄이 정상 위치인지 5. 권한에 따라 탭이 필터링(`navView.Refresh()`)된 뒤에도 밑줄이 맞는지 — 어긋나면 `OnAuthChanged` 끝에 `MoveTabIndicator(animate: false)`를 한 줄 추가하세요 ## 참고 - 260ms가 느리면 `TimeSpan.FromMilliseconds(160)`으로 줄이세요. - 밑줄을 라벨 폭만큼만 짧게 하고 싶으면 `targetWidth`를 `container.ActualWidth - 40` 정도로 줄이고 `targetX`에 `20`을 더하면 됩니다.