/
githubmirror
/
Files
Обзор
Документация
Войти
/
githubmirror
/
Files
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Files.App.Controls/Sidebar/SidebarView.xaml.cs
284 строки
8 KB
yair100
Feature: Added Tree View sidebar (#18537)
14 июн 2026, 17:35
Не верифицирован
14 июн 2026, 17:35
d11c28b
Код
Авторство
О чём код?
// Copyright (c) Files Community // Licensed under the MIT License. using Microsoft.UI.Input; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Markup; using Windows.Foundation; using Windows.System; using Windows.UI.Core; namespace Files.App.Controls { [ContentProperty(Name = "InnerContent")] public sealed partial class SidebarView : UserControl { private const double COMPACT_MAX_WIDTH = 200; public event EventHandler<ItemInvokedEventArgs>? ItemInvoked; public event EventHandler<ItemContextInvokedArgs>? ItemContextInvoked; public event EventHandler<ItemDragOverEventArgs>? ItemDragOver; public event EventHandler<ItemDroppedEventArgs>? ItemDropped; internal SidebarItem? SelectedItemContainer = null; private bool draggingSidebarResizer; private double preManipulationSidebarWidth = 0; public SidebarView() { InitializeComponent(); } internal void UpdateSelectedItemContainer(SidebarItem container) { SelectedItemContainer = container; } internal void RaiseItemInvoked(SidebarItem item, PointerUpdateKind pointerUpdateKind) { // Only true group headers (e.g. Pinned, Drives) suppress selection; leaves-with-children (tree-view folder rows) navigate AND get selected. if (item.Item is null || (item.IsGroupHeader && item.Item.IsLeafWithChildren != true)) return; SelectedItem = item.Item; ItemInvoked?.Invoke(item, new(pointerUpdateKind)); } internal void RaiseContextRequested(SidebarItem item, Point e) { ItemContextInvoked?.Invoke(item, new(item.Item, e)); } internal void RaiseItemDropped(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent) { if (sideBarItem.Item is null) return; ItemDropped?.Invoke(this, new(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent)); } internal void RaiseItemDragOver(SidebarItem sideBarItem, SidebarItemDropPosition dropPosition, DragEventArgs rawEvent) { if (sideBarItem.Item is null) return; ItemDragOver?.Invoke(this, new(sideBarItem.Item, rawEvent.DataView, dropPosition, rawEvent)); } private void UpdateMinimalMode() { if (DisplayMode != SidebarDisplayMode.Minimal) return; if (IsPaneOpen) { VisualStateManager.GoToState(this, "MinimalExpanded", true); } else { VisualStateManager.GoToState(this, "MinimalCollapsed", true); } } private void UpdateDisplayMode() { switch (DisplayMode) { case SidebarDisplayMode.Compact: VisualStateManager.GoToState(this, "Compact", true); break; case SidebarDisplayMode.Expanded: UpdateOpenPaneLengthColumn(); VisualStateManager.GoToState(this, "Expanded", true); break; case SidebarDisplayMode.Minimal: IsPaneOpen = false; UpdateMinimalMode(); break; } UpdateResizerAvailability(); } private void UpdateDisplayModeForPaneWidth(double newPaneWidth) { if (newPaneWidth < COMPACT_MAX_WIDTH) { DisplayMode = SidebarDisplayMode.Compact; } else if (newPaneWidth > COMPACT_MAX_WIDTH) { DisplayMode = SidebarDisplayMode.Expanded; OpenPaneLength = newPaneWidth; } } private void UpdateOpenPaneLengthColumn() { if (DisplayMode != SidebarDisplayMode.Expanded) return; PaneColumnDefinition.Width = new GridLength(OpenPaneLength); } private void UpdateResizerAvailability() { if (!CanResizePane) { SidebarResizer.Visibility = Visibility.Collapsed; SidebarResizer.IsHitTestVisible = false; return; } SidebarResizer.IsHitTestVisible = true; if (DisplayMode != SidebarDisplayMode.Minimal) SidebarResizer.Visibility = Visibility.Visible; } private void SidebarView_Loaded(object sender, RoutedEventArgs e) { UpdateDisplayMode(); UpdateResizerAvailability(); PaneColumnGrid.Translation = new System.Numerics.Vector3(0, 0, 32); } public double VerticalScrollOffset => MenuItemHostScrollViewer?.VerticalOffset ?? 0; public void ScrollToVerticalOffset(double offset) { MenuItemHostScrollViewer?.ChangeView(null, offset, null, true); } private void SidebarResizer_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) { if (!CanResizePane) return; draggingSidebarResizer = true; preManipulationSidebarWidth = PaneColumnGrid.ActualWidth; VisualStateManager.GoToState(this, "ResizerPressed", true); e.Handled = true; } private void SidebarResizer_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) { var newWidth = preManipulationSidebarWidth + e.Cumulative.Translation.X; UpdateDisplayModeForPaneWidth(newWidth); e.Handled = true; } private void SidebarResizerControl_KeyDown(object sender, KeyRoutedEventArgs e) { if (!CanResizePane) return; if ( e.Key != VirtualKey.Space && e.Key != VirtualKey.Enter && e.Key != VirtualKey.Left && e.Key != VirtualKey.Right && e.Key != VirtualKey.Control ) return; var primaryInvocation = e.Key == VirtualKey.Space || e.Key == VirtualKey.Enter; if (DisplayMode == SidebarDisplayMode.Expanded) { if (primaryInvocation) { DisplayMode = SidebarDisplayMode.Compact; return; } var ctrl = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control); var increment = ctrl.HasFlag(CoreVirtualKeyStates.Down) ? 5 : 1; // Left makes the pane smaller so we invert the increment if (e.Key == VirtualKey.Left) increment = -increment; var newWidth = OpenPaneLength + increment; UpdateDisplayModeForPaneWidth(newWidth); e.Handled = true; return; } else if (DisplayMode == SidebarDisplayMode.Compact) { if (primaryInvocation || e.Key == VirtualKey.Right) { DisplayMode = SidebarDisplayMode.Expanded; e.Handled = true; } } } private void PaneLightDismissLayer_PointerPressed(object sender, PointerRoutedEventArgs e) { IsPaneOpen = false; e.Handled = true; } private void PaneLightDismissLayer_Tapped(object sender, TappedRoutedEventArgs e) { IsPaneOpen = false; e.Handled = true; } private void SidebarResizer_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) { if (!CanResizePane) return; if (DisplayMode == SidebarDisplayMode.Expanded) { DisplayMode = SidebarDisplayMode.Compact; e.Handled = true; } else { DisplayMode = SidebarDisplayMode.Expanded; e.Handled = true; } } private void SidebarResizer_PointerEntered(object sender, PointerRoutedEventArgs e) { if (!CanResizePane) return; var sidebarResizer = (FrameworkElement)sender; sidebarResizer.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast)); VisualStateManager.GoToState(this, "ResizerPointerOver", true); e.Handled = true; } private void SidebarResizer_PointerExited(object sender, PointerRoutedEventArgs e) { if (!CanResizePane || draggingSidebarResizer) return; var sidebarResizer = (FrameworkElement)sender; sidebarResizer.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.Arrow)); VisualStateManager.GoToState(this, "ResizerNormal", true); e.Handled = true; } private void SidebarResizer_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) { draggingSidebarResizer = false; VisualStateManager.GoToState(this, "ResizerNormal", true); e.Handled = true; } private void MenuItemHostScrollViewer_ContextRequested(UIElement sender, ContextRequestedEventArgs e) { ItemContextInvoked?.Invoke(this, new(null, e.TryGetPosition(this, out var point) ? point : default)); e.Handled = true; } private void MenuItemsHost_ElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPreparedEventArgs args) { if (args.Element is SidebarItem sidebarItem) { // Assign Owner from the hosting SidebarView rather than letting the row's HookupOwners FindAscendant walk resolve it: that walk picks up the WRONG SidebarView when this view is the nested SettingsSidebar inside SidebarControl.InnerContent (and a stale Owner from a prior realization can persist across container recycling). Owner drives both the click-routing target and the chevron-column visual state. sidebarItem.Owner = this; sidebarItem.HandleItemChange(); } } } }