diff --git a/MainPage.xaml b/MainPage.xaml index 3707378..bf2e7ef 100644 --- a/MainPage.xaml +++ b/MainPage.xaml @@ -30,7 +30,7 @@ - + diff --git a/MainPage.xaml.cs b/MainPage.xaml.cs index e579eb0..0f1d4e3 100644 --- a/MainPage.xaml.cs +++ b/MainPage.xaml.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.ComponentModel; using System.Runtime.CompilerServices; using CommanderApp.Services; @@ -141,6 +141,9 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged OnHomeClicked(this, EventArgs.Empty); break; } + + // Всегда скроллим после любого действия с клавиатуры + ScrollToSelectedItem(); }); } @@ -159,6 +162,9 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged var button = RightPanel.Children[e.RightSelectedIndex] as Button; button?.Focus(); } + + // Скроллим при изменении состояния + ScrollToSelectedItem(); } private void SetInitialFocus() @@ -178,6 +184,7 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged if (focused) { _panelManager.SetSelection(0, true); + ScrollToSelectedItem(); } } } @@ -188,6 +195,55 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged } } + // Простой и надежный метод скролла + private async void ScrollToSelectedItem() + { + try + { + await Task.Delay(50); // Небольшая задержка для стабильности + + if (_panelManager.IsLeftPanelActive) + { + if (_panelManager.LeftSelectedIndex >= 0 && _panelManager.LeftSelectedIndex < LeftPanel.Children.Count) + { + // Вычисляем позицию для скролла + var itemHeight = 40; // Высота одного элемента + var scrollViewHeight = LeftScrollView.Height; // Высота видимой области + var visibleItems = (int)(scrollViewHeight / itemHeight); // Сколько элементов видно + + // Вычисляем целевой скролл, чтобы элемент был в середине видимой области + var targetScrollY = Math.Max(0, (_panelManager.LeftSelectedIndex - visibleItems / 2) * itemHeight); + + await LeftScrollView.ScrollToAsync(0, targetScrollY, true); + + System.Diagnostics.Debug.WriteLine($"Left scroll: index={_panelManager.LeftSelectedIndex}, targetY={targetScrollY}"); + } + } + else + { + if (_panelManager.RightSelectedIndex >= 0 && _panelManager.RightSelectedIndex < RightPanel.Children.Count) + { + // Вычисляем позицию для скролла + var itemHeight = 40; // Высота одного элемента + var scrollViewHeight = RightScrollView.Height; // Высота видимой области + var visibleItems = (int)(scrollViewHeight / itemHeight); // Сколько элементов видно + + // Вычисляем целевой скролл, чтобы элемент был в середине видимой области + var targetScrollY = Math.Max(0, (_panelManager.RightSelectedIndex - visibleItems / 2) * itemHeight); + + await RightScrollView.ScrollToAsync(0, targetScrollY, true); + + System.Diagnostics.Debug.WriteLine($"Right scroll: index={_panelManager.RightSelectedIndex}, targetY={targetScrollY}"); + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Scroll error: {ex.Message}"); + } + } + + // Остальные методы без изменений... private void OpenSelectedItem() { var item = _panelManager.SelectedItem; @@ -302,6 +358,7 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged { var firstButton = panel.Children[0] as Button; firstButton?.Focus(); + ScrollToSelectedItem(); }); } } @@ -327,29 +384,6 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged CornerRadius = 0 }; - // Устанавливаем тематический цвет текста - // button.SetAppThemeColor(Button.TextColorProperty, Colors.Black, Colors.White); - - // Визуальные состояния для фокуса - var visualStateGroups = new VisualStateGroupList(); - var commonStates = new VisualStateGroup { Name = "CommonStates" }; - - var normalState = new VisualState { Name = "Normal" }; - normalState.Setters.Add(new Setter { Property = Button.BackgroundColorProperty, Value = NormalButtonColor }); - - var focusedState = new VisualState { Name = "Focused" }; - focusedState.Setters.Add(new Setter { Property = Button.BackgroundColorProperty, Value = FocusedButtonColor }); - - var pressedState = new VisualState { Name = "Pressed" }; - pressedState.Setters.Add(new Setter { Property = Button.BackgroundColorProperty, Value = NormalButtonColor }); - - commonStates.States.Add(normalState); - commonStates.States.Add(focusedState); - commonStates.States.Add(pressedState); - visualStateGroups.Add(commonStates); - - VisualStateManager.SetVisualStateGroups(button, visualStateGroups); - // Обработчик клика - выделение при клике, открытие при двойном клике button.Clicked += (s, e) => HandleItemClick(button, item, isLeft); @@ -387,6 +421,7 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged // Устанавливаем фокус на кнопку button.Focus(); + ScrollToSelectedItem(); } // Если это двойной клик - открываем/запускаем @@ -403,7 +438,7 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged } } - // Команды тулбара + // Команды тулбара (без изменений) private async void OnCopyClicked(object sender, EventArgs e) { System.Diagnostics.Debug.WriteLine("=== BUTTON: F5 Copy ==="); diff --git a/MockFileOperations.cs b/MockFileOperations.cs deleted file mode 100644 index 59a4ab0..0000000 --- a/MockFileOperations.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace CommanderApp.Services; - -public class MockFileOperations : IFileOperations -{ - public List CopiedFiles { get; } = new(); - public List MovedFiles { get; } = new(); - public List DeletedItems { get; } = new(); - public List CreatedDirectories { get; } = new(); - public List OpenedFiles { get; } = new(); - - public Task CopyAsync(string sourcePath, string targetPath, bool overwrite = true) - { - CopiedFiles.Add($"{sourcePath} -> {targetPath}"); - return Task.FromResult(true); - } - - public Task MoveAsync(string sourcePath, string targetPath, bool overwrite = true) - { - MovedFiles.Add($"{sourcePath} -> {targetPath}"); - return Task.FromResult(true); - } - - public Task DeleteAsync(string path) - { - DeletedItems.Add(path); - return Task.FromResult(true); - } - - public Task CreateDirectoryAsync(string path) - { - CreatedDirectories.Add(path); - return Task.FromResult(true); - } - - public Task OpenFileAsync(string filePath) - { - OpenedFiles.Add(filePath); - return Task.FromResult(true); - } - - public Task CopyDirectoryAsync(string sourceDir, string targetDir) - { - CopiedFiles.Add($"DIR: {sourceDir} -> {targetDir}"); - return Task.FromResult(true); - } -} \ No newline at end of file diff --git a/MockFileSystemItem.cs b/MockFileSystemItem.cs deleted file mode 100644 index 6192aa7..0000000 --- a/MockFileSystemItem.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace CommanderApp; - -public static class MockFileSystemItem -{ - public static FileSystemItem CreateDirectory(string name, string path) - { - return new FileSystemItem - { - Name = name, - FullName = path, - IsDirectory = true - }; - } - - public static FileSystemItem CreateFile(string name, string path) - { - return new FileSystemItem - { - Name = name, - FullName = path, - IsDirectory = false - }; - } - - public static FileSystemItem CreateParentDirectory(string path) - { - return new FileSystemItem - { - Name = "..", - FullName = path, - IsDirectory = true - }; - } -} \ No newline at end of file diff --git a/MockFileSystemService.cs b/MockFileSystemService.cs deleted file mode 100644 index 4b5a225..0000000 --- a/MockFileSystemService.cs +++ /dev/null @@ -1,81 +0,0 @@ -namespace CommanderApp; - -public class MockFileSystemService : IFileSystemService -{ - public string MockRootPath { get; set; } = "/mock/root"; - - private readonly Dictionary> _mockDirectories = new(); - - public MockFileSystemService() - { - // Инициализируем mock данными - SetupMockData(); - } - - private void SetupMockData() - { - _mockDirectories["/mock/root"] = new List - { - new FileSystemItem { Name = "Documents", FullName = "/mock/root/Documents", IsDirectory = true }, - new FileSystemItem { Name = "Pictures", FullName = "/mock/root/Pictures", IsDirectory = true }, - new FileSystemItem { Name = "readme.txt", FullName = "/mock/root/readme.txt", IsDirectory = false }, - new FileSystemItem { Name = "..", FullName = "/mock", IsDirectory = true } - }; - - _mockDirectories["/mock/root/Documents"] = new List - { - new FileSystemItem { Name = "Project1", FullName = "/mock/root/Documents/Project1", IsDirectory = true }, - new FileSystemItem { Name = "Project2", FullName = "/mock/root/Documents/Project2", IsDirectory = true }, - new FileSystemItem { Name = "notes.txt", FullName = "/mock/root/Documents/notes.txt", IsDirectory = false }, - new FileSystemItem { Name = "..", FullName = "/mock/root", IsDirectory = true } - }; - - _mockDirectories["/mock/root/Pictures"] = new List - { - new FileSystemItem { Name = "vacation.jpg", FullName = "/mock/root/Pictures/vacation.jpg", IsDirectory = false }, - new FileSystemItem { Name = "..", FullName = "/mock/root", IsDirectory = true } - }; - } - - public string GetRootPath() - { - return MockRootPath; - } - - public IEnumerable GetDirectoryContents(string path) - { - if (_mockDirectories.ContainsKey(path)) - { - return _mockDirectories[path]; - } - - // Если директории нет в mock данных, возвращаем пустой список с ".." - var parent = GetParentPath(path); - if (parent != null) - { - return new List - { - new FileSystemItem { Name = "..", FullName = parent, IsDirectory = true } - }; - } - - return Enumerable.Empty(); - } - - // Метод для добавления mock данных в тестах - public void AddMockDirectory(string path, List items) - { - _mockDirectories[path] = items; - } - - private string GetParentPath(string path) - { - var separator = '/'; - var lastSeparator = path.LastIndexOf(separator); - if (lastSeparator > 0) - { - return path.Substring(0, lastSeparator); - } - return null; - } -} \ No newline at end of file diff --git a/MockKeyboardService.cs b/MockKeyboardService.cs deleted file mode 100644 index a960d78..0000000 --- a/MockKeyboardService.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace CommanderApp.Services; - -public class MockKeyboardService : IKeyboardService -{ - public event EventHandler KeyPressed; - - public void SetupKeyboardHandling(ContentPage page) - { - // Mock implementation - does nothing - } - - // Method to simulate key presses in tests - public void SimulateKeyPress(string key) - { - KeyPressed?.Invoke(this, new KeyPressedEventArgs { Key = key, Platform = "Test" }); - } -} \ No newline at end of file diff --git a/MockPanelManager.cs b/MockPanelManager.cs deleted file mode 100644 index 59a1ff3..0000000 --- a/MockPanelManager.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections.ObjectModel; - -namespace CommanderApp.Services; - -public class MockPanelManager : IPanelManager -{ - public event EventHandler StateChanged; - - public bool IsLeftPanelActive { get; set; } = true; - public FileSystemItem SelectedItem { get; set; } - public string ActivePanelPath { get; set; } = "/mock/path"; - public string LeftPanelPath { get; set; } = "/left/path"; - public string RightPanelPath { get; set; } = "/right/path"; - public int LeftSelectedIndex { get; set; } = 0; - public int RightSelectedIndex { get; set; } = -1; - - public ObservableCollection LeftItems { get; } = new(); - public ObservableCollection RightItems { get; } = new(); - - public void SwitchToLeftPanel() => IsLeftPanelActive = true; - public void SwitchToRightPanel() => IsLeftPanelActive = false; - - public void MoveSelection(int direction) - { - if (IsLeftPanelActive) - LeftSelectedIndex = Math.Max(0, LeftSelectedIndex + direction); - else - RightSelectedIndex = Math.Max(0, RightSelectedIndex + direction); - } - - public void SetSelection(int index, bool isLeftPanel) - { - if (isLeftPanel) - LeftSelectedIndex = index; - else - RightSelectedIndex = index; - } - - public void UpdatePanelPaths(string leftPath, string rightPath) - { - LeftPanelPath = leftPath; - RightPanelPath = rightPath; - } - - public void ClearSelection() - { - LeftSelectedIndex = -1; - RightSelectedIndex = -1; - } -} \ No newline at end of file diff --git a/MockPathHelper.cs b/MockPathHelper.cs deleted file mode 100644 index e4417cc..0000000 --- a/MockPathHelper.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace CommanderApp.Services; - -public class MockPathHelper : IPathHelper -{ - public string MockHomePath { get; set; } = "/mock/home"; - public string MockRootPath { get; set; } = "/mock/root"; - - public string GetUserHomePath() => MockHomePath; - public string GetRootPath() => MockRootPath; - public string CombinePaths(params string[] paths) => string.Join("/", paths); - public string GetFileName(string path) => System.IO.Path.GetFileName(path); - public string GetDirectoryName(string path) => System.IO.Path.GetDirectoryName(path) ?? string.Empty; -} \ No newline at end of file