BASE Temp 1.2.2
This commit is contained in:
1 parent
195360dc47
commit
63be149caf
9 files changed
+430
-10
No files matched your search
@@ -8,6 +8,7 @@ public sealed class AppSettings
|
||||
{
|
||||
public string AppDisplayName { get; set; } = "FoxBrowser";
|
||||
public string? AppIconPath { get; set; }
|
||||
public Dictionary<string, string> Shortcuts { get; set; } = new();
|
||||
}
|
||||
|
||||
public static class AppSettingsStore
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FoxBrowser.Core;
|
||||
|
||||
public sealed class ShortcutManager
|
||||
{
|
||||
public const string CloseTab = "CloseTab";
|
||||
public const string NewTab = "NewTab";
|
||||
public const string ToggleTopPanel = "ToggleTopPanel";
|
||||
|
||||
private static readonly IReadOnlyDictionary<string, KeyGesture> Defaults =
|
||||
new Dictionary<string, KeyGesture>
|
||||
{
|
||||
[CloseTab] = new(Key.W, ModifierKeys.Control),
|
||||
[NewTab] = new(Key.N, ModifierKeys.Control),
|
||||
[ToggleTopPanel] = new(Key.F, ModifierKeys.Control | ModifierKeys.Shift),
|
||||
};
|
||||
|
||||
private static readonly Lazy<ShortcutManager> LazyInstance = new(() => new ShortcutManager());
|
||||
private readonly Dictionary<string, KeyGesture> _shortcuts = new();
|
||||
|
||||
private ShortcutManager()
|
||||
{
|
||||
ResetToDefaults(save: false);
|
||||
Load(AppSettingsStore.Load());
|
||||
}
|
||||
|
||||
public static ShortcutManager Instance => LazyInstance.Value;
|
||||
public IReadOnlyDictionary<string, KeyGesture> Shortcuts => _shortcuts;
|
||||
public event EventHandler? Changed;
|
||||
|
||||
public KeyGesture Get(string action) => _shortcuts[action];
|
||||
|
||||
public string? FindAction(Key key, ModifierKeys modifiers)
|
||||
{
|
||||
return _shortcuts.FirstOrDefault(pair => pair.Value.Key == key && pair.Value.Modifiers == modifiers).Key;
|
||||
}
|
||||
|
||||
public bool TrySet(string action, KeyGesture gesture, out string? conflictAction)
|
||||
{
|
||||
conflictAction = _shortcuts.FirstOrDefault(pair => pair.Key != action && GesturesEqual(pair.Value, gesture)).Key;
|
||||
if (!string.IsNullOrEmpty(conflictAction)) return false;
|
||||
_shortcuts[action] = gesture;
|
||||
Save();
|
||||
Changed?.Invoke(this, EventArgs.Empty);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetToDefaults()
|
||||
{
|
||||
ResetToDefaults(save: true);
|
||||
}
|
||||
|
||||
public static string GetActionLabel(string action) => action switch
|
||||
{
|
||||
CloseTab => "Закрити вкладку",
|
||||
NewTab => "Нова вкладка",
|
||||
ToggleTopPanel => "Показати/сховати верхню панель",
|
||||
_ => action,
|
||||
};
|
||||
|
||||
public static string Format(KeyGesture gesture)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
if (gesture.Modifiers.HasFlag(ModifierKeys.Control)) parts.Add("Ctrl");
|
||||
if (gesture.Modifiers.HasFlag(ModifierKeys.Shift)) parts.Add("Shift");
|
||||
if (gesture.Modifiers.HasFlag(ModifierKeys.Alt)) parts.Add("Alt");
|
||||
if (gesture.Modifiers.HasFlag(ModifierKeys.Windows)) parts.Add("Win");
|
||||
parts.Add(gesture.Key.ToString());
|
||||
return string.Join("+", parts);
|
||||
}
|
||||
|
||||
public static bool TryCreateGesture(Key key, ModifierKeys modifiers, out KeyGesture? gesture)
|
||||
{
|
||||
gesture = null;
|
||||
if (key is Key.LeftCtrl or Key.RightCtrl or Key.LeftShift or Key.RightShift or Key.LeftAlt or Key.RightAlt or Key.LWin or Key.RWin)
|
||||
return false;
|
||||
if (key == Key.None || modifiers == ModifierKeys.None)
|
||||
return false;
|
||||
gesture = new KeyGesture(key, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Load(AppSettings settings)
|
||||
{
|
||||
if (settings.Shortcuts is null) return;
|
||||
foreach (var pair in settings.Shortcuts)
|
||||
{
|
||||
if (!Defaults.ContainsKey(pair.Key)) continue;
|
||||
if (TryParse(pair.Value, out var gesture)) _shortcuts[pair.Key] = gesture!;
|
||||
}
|
||||
}
|
||||
|
||||
private void Save()
|
||||
{
|
||||
var settings = AppSettingsStore.Load();
|
||||
settings.Shortcuts = _shortcuts.ToDictionary(pair => pair.Key, pair => Format(pair.Value));
|
||||
AppSettingsStore.Save(settings);
|
||||
}
|
||||
|
||||
private void ResetToDefaults(bool save)
|
||||
{
|
||||
_shortcuts.Clear();
|
||||
foreach (var pair in Defaults) _shortcuts[pair.Key] = pair.Value;
|
||||
if (save) Save();
|
||||
}
|
||||
|
||||
private static bool GesturesEqual(KeyGesture left, KeyGesture right) => left.Key == right.Key && left.Modifiers == right.Modifiers;
|
||||
|
||||
private static bool TryParse(string? value, out KeyGesture? gesture)
|
||||
{
|
||||
gesture = null;
|
||||
if (string.IsNullOrWhiteSpace(value)) return false;
|
||||
var parts = value.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
if (parts.Length < 2 || !Enum.TryParse(parts[^1], true, out Key key)) return false;
|
||||
var modifiers = ModifierKeys.None;
|
||||
foreach (var part in parts[..^1])
|
||||
{
|
||||
if (part.Equals("Ctrl", StringComparison.OrdinalIgnoreCase) || part.Equals("Control", StringComparison.OrdinalIgnoreCase)) modifiers |= ModifierKeys.Control;
|
||||
else if (part.Equals("Shift", StringComparison.OrdinalIgnoreCase)) modifiers |= ModifierKeys.Shift;
|
||||
else if (part.Equals("Alt", StringComparison.OrdinalIgnoreCase)) modifiers |= ModifierKeys.Alt;
|
||||
else if (part.Equals("Win", StringComparison.OrdinalIgnoreCase) || part.Equals("Windows", StringComparison.OrdinalIgnoreCase)) modifiers |= ModifierKeys.Windows;
|
||||
else return false;
|
||||
}
|
||||
if (!TryCreateGesture(key, modifiers, out gesture)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+16
-2
@@ -27,6 +27,7 @@ public sealed class TabManager : IAsyncDisposable
|
||||
private bool _navigationInProgress;
|
||||
private bool _switchInProgress;
|
||||
private bool _restoringSession;
|
||||
private readonly List<BrowserTab> _sessionOrder = new();
|
||||
private readonly string _blockedElementsPath = Path.Combine(AppContext.BaseDirectory, "blocked-elements.json");
|
||||
private readonly Dictionary<string, List<string>> _blockedElements;
|
||||
|
||||
@@ -81,13 +82,16 @@ public sealed class TabManager : IAsyncDisposable
|
||||
_restoringSession = false;
|
||||
SessionReady = true;
|
||||
}
|
||||
_sessionOrder.AddRange(Tabs);
|
||||
await SaveSessionAsync();
|
||||
}
|
||||
|
||||
public async Task SaveSessionAsync()
|
||||
{
|
||||
var activeIndex = ActiveTab is not null ? Tabs.IndexOf(ActiveTab) : 0;
|
||||
await SessionStore.SaveAsync(Tabs, activeIndex);
|
||||
var orderedTabs = _sessionOrder.Where(Tabs.Contains).Concat(Tabs.Where(tab => !_sessionOrder.Contains(tab)));
|
||||
var snapshotOrder = orderedTabs.ToList();
|
||||
var activeIndex = ActiveTab is not null ? snapshotOrder.IndexOf(ActiveTab) : 0;
|
||||
await SessionStore.SaveAsync(snapshotOrder, activeIndex);
|
||||
}
|
||||
|
||||
private async Task RestoreSessionOrDefaultAsync()
|
||||
@@ -125,6 +129,7 @@ public sealed class TabManager : IAsyncDisposable
|
||||
{
|
||||
var tab = new BrowserTab(NormalizeAddress(address));
|
||||
Tabs.Add(tab);
|
||||
if (SessionReady) _sessionOrder.Add(tab);
|
||||
await SwitchToTabAsync(tab);
|
||||
return tab;
|
||||
}
|
||||
@@ -318,6 +323,15 @@ public sealed class TabManager : IAsyncDisposable
|
||||
await SaveSessionAsync();
|
||||
}
|
||||
|
||||
public void MoveTab(BrowserTab tab, int targetIndex)
|
||||
{
|
||||
var sourceIndex = Tabs.IndexOf(tab);
|
||||
if (sourceIndex < 0 || Tabs.Count < 2) return;
|
||||
targetIndex = Math.Clamp(targetIndex, 0, Tabs.Count - 1);
|
||||
if (sourceIndex == targetIndex) return;
|
||||
Tabs.Move(sourceIndex, targetIndex);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (ActiveTab is not null) await CaptureActiveStateAsync();
|
||||
|
||||
+10
-2
@@ -96,8 +96,9 @@
|
||||
</Window.Resources>
|
||||
|
||||
<DockPanel>
|
||||
<StackPanel x:Name="TopPanel" DockPanel.Dock="Top">
|
||||
<!-- Tab strip -->
|
||||
<Border DockPanel.Dock="Top" Background="{StaticResource HeaderBg}" Padding="12,10,12,8" BorderBrush="{StaticResource BorderDark}" BorderThickness="0,0,0,1">
|
||||
<Border Background="{StaticResource HeaderBg}" Padding="12,10,12,8" BorderBrush="{StaticResource BorderDark}" BorderThickness="0,0,0,1">
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="2,0,18,0">
|
||||
<Border Width="32" Height="32" CornerRadius="9" Background="{StaticResource AurBlueSoft}">
|
||||
@@ -109,6 +110,11 @@
|
||||
ItemsSource="{Binding Tabs}"
|
||||
ItemTemplate="{StaticResource TabTemplate}"
|
||||
SelectionChanged="TabStrip_SelectionChanged"
|
||||
AllowDrop="True"
|
||||
PreviewMouseLeftButtonDown="TabStrip_PreviewMouseLeftButtonDown"
|
||||
PreviewMouseMove="TabStrip_PreviewMouseMove"
|
||||
DragOver="TabStrip_DragOver"
|
||||
Drop="TabStrip_Drop"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Padding="0"
|
||||
@@ -121,7 +127,7 @@
|
||||
</Border>
|
||||
|
||||
<!-- Browser controls -->
|
||||
<Border DockPanel.Dock="Top" Background="{StaticResource HeaderBgElevated}" Padding="12,9" BorderBrush="{StaticResource BorderDark}" BorderThickness="0,0,0,1">
|
||||
<Border Background="{StaticResource HeaderBgElevated}" Padding="12,9" BorderBrush="{StaticResource BorderDark}" BorderThickness="0,0,0,1">
|
||||
<DockPanel LastChildFill="True">
|
||||
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
|
||||
<Button Content="←" ToolTip="Назад" Click="Back_Click" Style="{StaticResource IconButton}" />
|
||||
@@ -187,6 +193,7 @@
|
||||
</Popup>
|
||||
<Button Content="♫" ToolTip="Режим лише аудіо" Click="AudioOnly_Click" Height="32" Width="36" Padding="0" Margin="0,0,4,0" />
|
||||
<Button Content="Розширення" ToolTip="Керування unpacked extensions" Click="Extensions_Click" Height="32" Padding="11,0" />
|
||||
<Button Content="⌨" ToolTip="Клавіатурні скорочення" Click="Shortcuts_Click" Height="32" Width="36" Padding="0" Margin="4,0,0,0" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid VerticalAlignment="Center">
|
||||
@@ -199,6 +206,7 @@
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Web content -->
|
||||
<Grid x:Name="WebViewHost" Background="#0B0E12">
|
||||
|
||||
+96
-1
@@ -11,6 +11,11 @@ using Microsoft.Web.WebView2.Core;
|
||||
using FoxBrowser.Core;
|
||||
using FoxBrowser.Native;
|
||||
using FoxBrowser.UI;
|
||||
using WpfDragDropEffects = System.Windows.DragDropEffects;
|
||||
using WpfDragEventArgs = System.Windows.DragEventArgs;
|
||||
using WpfKeyEventArgs = System.Windows.Input.KeyEventArgs;
|
||||
using WpfMouseEventArgs = System.Windows.Input.MouseEventArgs;
|
||||
using WpfPoint = System.Windows.Point;
|
||||
|
||||
namespace FoxBrowser;
|
||||
|
||||
@@ -24,6 +29,9 @@ public partial class MainWindow : Window
|
||||
private AudioSessionLabeler? _audioLabeler;
|
||||
private bool _updatingDarkModeMenu;
|
||||
private AppSettings _appSettings;
|
||||
private WpfPoint _tabDragStartPoint;
|
||||
private BrowserTab? _tabDragItem;
|
||||
private bool _topPanelVisible = true;
|
||||
|
||||
private const int WmNcRButtonDown = 0x00A4;
|
||||
private const int WmNcRButtonUp = 0x00A5;
|
||||
@@ -34,6 +42,7 @@ public partial class MainWindow : Window
|
||||
InitializeComponent();
|
||||
_appSettings = AppSettingsStore.Load();
|
||||
ApplyAppIdentity(_appSettings);
|
||||
PreviewKeyDown += MainWindow_PreviewKeyDown;
|
||||
SourceInitialized += MainWindow_SourceInitialized;
|
||||
Loaded += MainWindow_Loaded;
|
||||
}
|
||||
@@ -75,6 +84,16 @@ public partial class MainWindow : Window
|
||||
popup.IsOpen = false;
|
||||
OpenAppSettings();
|
||||
};
|
||||
var shortcutsItem = new System.Windows.Controls.Button
|
||||
{
|
||||
Content = "Клавіатурні скорочення…",
|
||||
Style = (Style)FindResource("FoxMenuButton")
|
||||
};
|
||||
shortcutsItem.Click += (_, _) =>
|
||||
{
|
||||
popup.IsOpen = false;
|
||||
Shortcuts_Click(this, new RoutedEventArgs());
|
||||
};
|
||||
popup.Child = new Border
|
||||
{
|
||||
Background = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromRgb(23, 27, 34)),
|
||||
@@ -82,7 +101,10 @@ public partial class MainWindow : Window
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(8),
|
||||
Padding = new Thickness(8),
|
||||
Child = item
|
||||
Child = new StackPanel
|
||||
{
|
||||
Children = { item, shortcutsItem }
|
||||
}
|
||||
};
|
||||
popup.IsOpen = true;
|
||||
}
|
||||
@@ -302,6 +324,79 @@ public partial class MainWindow : Window
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
||||
private void Shortcuts_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = new ShortcutSettingsWindow { Owner = this };
|
||||
window.ShowDialog();
|
||||
}
|
||||
|
||||
private async void MainWindow_PreviewKeyDown(object sender, WpfKeyEventArgs e)
|
||||
{
|
||||
var key = e.Key == Key.System ? e.SystemKey : e.Key;
|
||||
var action = ShortcutManager.Instance.FindAction(key, Keyboard.Modifiers);
|
||||
if (action is null) return;
|
||||
e.Handled = true;
|
||||
switch (action)
|
||||
{
|
||||
case ShortcutManager.CloseTab when _tabManager?.ActiveTab is not null:
|
||||
await _tabManager.CloseTabAsync(_tabManager.ActiveTab);
|
||||
RefreshAddressBar();
|
||||
break;
|
||||
case ShortcutManager.NewTab when _tabManager is not null:
|
||||
await _tabManager.AddTabAsync();
|
||||
RefreshAddressBar();
|
||||
break;
|
||||
case ShortcutManager.ToggleTopPanel:
|
||||
_topPanelVisible = !_topPanelVisible;
|
||||
TopPanel.Visibility = _topPanelVisible ? Visibility.Visible : Visibility.Collapsed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void TabStrip_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_tabDragStartPoint = e.GetPosition(TabStrip);
|
||||
_tabDragItem = FindTabItem(e.OriginalSource as DependencyObject)?.DataContext as BrowserTab;
|
||||
}
|
||||
|
||||
private void TabStrip_PreviewMouseMove(object sender, WpfMouseEventArgs e)
|
||||
{
|
||||
if (e.LeftButton != MouseButtonState.Pressed || _tabDragItem is null) return;
|
||||
var position = e.GetPosition(TabStrip);
|
||||
if (Math.Abs(position.X - _tabDragStartPoint.X) < SystemParameters.MinimumHorizontalDragDistance &&
|
||||
Math.Abs(position.Y - _tabDragStartPoint.Y) < SystemParameters.MinimumVerticalDragDistance) return;
|
||||
var item = _tabDragItem;
|
||||
_tabDragItem = null;
|
||||
System.Windows.DragDrop.DoDragDrop(TabStrip, item, WpfDragDropEffects.Move);
|
||||
}
|
||||
|
||||
private void TabStrip_DragOver(object sender, WpfDragEventArgs e)
|
||||
{
|
||||
e.Effects = e.Data.GetDataPresent(typeof(BrowserTab)) ? WpfDragDropEffects.Move : WpfDragDropEffects.None;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void TabStrip_Drop(object sender, WpfDragEventArgs e)
|
||||
{
|
||||
if (_tabManager is null || !e.Data.GetDataPresent(typeof(BrowserTab))) return;
|
||||
var source = e.Data.GetData(typeof(BrowserTab)) as BrowserTab;
|
||||
var target = FindTabItem(e.OriginalSource as DependencyObject)?.DataContext as BrowserTab;
|
||||
if (source is null || target is null || ReferenceEquals(source, target)) return;
|
||||
var targetIndex = _tabManager.Tabs.IndexOf(target);
|
||||
if (targetIndex >= 0) _tabManager.MoveTab(source, targetIndex);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private static TabItem? FindTabItem(DependencyObject? element)
|
||||
{
|
||||
while (element is not null)
|
||||
{
|
||||
if (element is TabItem tabItem) return tabItem;
|
||||
element = System.Windows.Media.VisualTreeHelper.GetParent(element);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void PinnedExtension_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_tabManager?.ExtensionManager is null || sender is not System.Windows.Controls.Button button || button.DataContext is not PinnedExtensionItem item)
|
||||
|
||||
@@ -47,7 +47,12 @@ public partial class AppSettingsWindow : Window
|
||||
if (name.Length == 0) name = "FoxBrowser";
|
||||
try
|
||||
{
|
||||
var settings = new AppSettings { AppDisplayName = name, AppIconPath = _iconPath };
|
||||
var settings = new AppSettings
|
||||
{
|
||||
AppDisplayName = name,
|
||||
AppIconPath = _iconPath,
|
||||
Shortcuts = ShortcutManager.Instance.Shortcuts.ToDictionary(pair => pair.Key, pair => ShortcutManager.Format(pair.Value))
|
||||
};
|
||||
AppSettingsStore.Save(settings);
|
||||
_apply(settings);
|
||||
DialogResult = true;
|
||||
|
||||
@@ -11,6 +11,7 @@ public partial class ExtensionPopupWindow : Window
|
||||
private readonly CoreWebView2Environment _environment;
|
||||
private readonly string _extensionId;
|
||||
private readonly string _popupPage;
|
||||
private WebView2? _view;
|
||||
|
||||
public ExtensionPopupWindow(
|
||||
CoreWebView2Environment environment,
|
||||
@@ -47,14 +48,21 @@ public partial class ExtensionPopupWindow : Window
|
||||
{
|
||||
try
|
||||
{
|
||||
var view = new WebView2();
|
||||
PopupHost.Children.Add(view);
|
||||
await view.EnsureCoreWebView2Async(_environment);
|
||||
view.CoreWebView2.Navigate($"chrome-extension://{_extensionId}/{_popupPage}");
|
||||
_view = new WebView2();
|
||||
PopupHost.Children.Add(_view);
|
||||
await _view.EnsureCoreWebView2Async(_environment);
|
||||
_view.CoreWebView2.Navigate($"chrome-extension://{_extensionId}/{_popupPage}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_view?.Dispose();
|
||||
_view = null;
|
||||
base.OnClosed(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Window x:Class="FoxBrowser.UI.ShortcutSettingsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Клавіатурні скорочення" Width="580" Height="330"
|
||||
MinWidth="520" Background="#15171A" Foreground="#F2F4F7"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="NoResize">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="Клавіатурні скорочення" FontSize="18" FontWeight="SemiBold" Margin="0,0,0,8" />
|
||||
<TextBlock Grid.Row="0" Text="Натисніть «Змінити», потім потрібну комбінацію Ctrl/Shift/Alt + клавіша." Foreground="#AEB4BE" Margin="0,30,0,12" />
|
||||
<StackPanel x:Name="ShortcutList" Grid.Row="1" />
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,14,0,0">
|
||||
<Button Content="Скинути до стандартних" Click="Reset_Click" Padding="10,6" Margin="0,0,8,0" />
|
||||
<Button Content="Закрити" Click="Close_Click" Padding="14,6" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,138 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using FoxBrowser.Core;
|
||||
using WpfButton = System.Windows.Controls.Button;
|
||||
using WpfColor = System.Windows.Media.Color;
|
||||
using WpfKeyEventArgs = System.Windows.Input.KeyEventArgs;
|
||||
using WpfTextBox = System.Windows.Controls.TextBox;
|
||||
|
||||
namespace FoxBrowser.UI;
|
||||
|
||||
public partial class ShortcutSettingsWindow : Window
|
||||
{
|
||||
private readonly ShortcutManager _shortcuts = ShortcutManager.Instance;
|
||||
private readonly Dictionary<string, WpfTextBox> _boxes = new();
|
||||
private string? _capturingAction;
|
||||
private KeyGesture? _pendingGesture;
|
||||
|
||||
public ShortcutSettingsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_shortcuts.Changed += Shortcuts_Changed;
|
||||
BuildRows();
|
||||
}
|
||||
|
||||
private void BuildRows()
|
||||
{
|
||||
ShortcutList.Children.Clear();
|
||||
_boxes.Clear();
|
||||
foreach (var action in new[] { ShortcutManager.CloseTab, ShortcutManager.NewTab, ShortcutManager.ToggleTopPanel })
|
||||
{
|
||||
var box = new WpfTextBox
|
||||
{
|
||||
Text = ShortcutManager.Format(_shortcuts.Get(action)),
|
||||
IsReadOnly = true,
|
||||
IsTabStop = false,
|
||||
Width = 150,
|
||||
Padding = new Thickness(7, 4, 7, 4),
|
||||
Background = new SolidColorBrush(WpfColor.FromRgb(32, 35, 40)),
|
||||
Foreground = new SolidColorBrush(WpfColor.FromRgb(242, 244, 247)),
|
||||
BorderBrush = new SolidColorBrush(WpfColor.FromRgb(69, 76, 87)),
|
||||
HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center,
|
||||
Tag = action,
|
||||
};
|
||||
var change = new WpfButton { Content = "Змінити", Padding = new Thickness(10, 5, 10, 5), Tag = action };
|
||||
change.Click += Change_Click;
|
||||
var row = new Grid { Margin = new Thickness(0, 0, 0, 8) };
|
||||
row.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
row.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
row.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
row.Children.Add(new TextBlock { Text = ShortcutManager.GetActionLabel(action), VerticalAlignment = VerticalAlignment.Center });
|
||||
Grid.SetColumn(box, 1);
|
||||
Grid.SetColumn(change, 2);
|
||||
change.Margin = new Thickness(8, 0, 0, 0);
|
||||
row.Children.Add(box);
|
||||
row.Children.Add(change);
|
||||
ShortcutList.Children.Add(row);
|
||||
_boxes[action] = box;
|
||||
}
|
||||
}
|
||||
|
||||
private void Change_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not WpfButton button || button.Tag is not string action || !_boxes.TryGetValue(action, out var box)) return;
|
||||
_capturingAction = action;
|
||||
_pendingGesture = null;
|
||||
box.Text = "Натисніть комбінацію…";
|
||||
box.Focus();
|
||||
box.PreviewKeyDown -= CaptureBox_PreviewKeyDown;
|
||||
box.PreviewKeyDown += CaptureBox_PreviewKeyDown;
|
||||
box.PreviewKeyUp -= CaptureBox_PreviewKeyUp;
|
||||
box.PreviewKeyUp += CaptureBox_PreviewKeyUp;
|
||||
}
|
||||
|
||||
private void CaptureBox_PreviewKeyDown(object sender, WpfKeyEventArgs e)
|
||||
{
|
||||
if (_capturingAction is null || sender is not WpfTextBox box) return;
|
||||
var key = e.Key == Key.System ? e.SystemKey : e.Key;
|
||||
if (key == Key.Escape)
|
||||
{
|
||||
CancelCapture(box);
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
var modifiers = Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Alt);
|
||||
if (!ShortcutManager.TryCreateGesture(key, modifiers, out var gesture)) return;
|
||||
_pendingGesture = gesture;
|
||||
box.Text = ShortcutManager.Format(gesture);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void CaptureBox_PreviewKeyUp(object sender, WpfKeyEventArgs e)
|
||||
{
|
||||
if (_capturingAction is null || _pendingGesture is null || sender is not WpfTextBox box) return;
|
||||
var action = _capturingAction;
|
||||
if (!_shortcuts.TrySet(action, _pendingGesture, out var conflictAction))
|
||||
{
|
||||
System.Windows.MessageBox.Show(this, $"Комбінація вже призначена для «{ShortcutManager.GetActionLabel(conflictAction!)}». Оберіть іншу.", "Конфлікт шорткатів", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
box.Text = ShortcutManager.Format(_shortcuts.Get(action));
|
||||
}
|
||||
StopCapture(box);
|
||||
}
|
||||
|
||||
private void CancelCapture(WpfTextBox box)
|
||||
{
|
||||
box.Text = ShortcutManager.Format(_shortcuts.Get(_capturingAction!));
|
||||
StopCapture(box);
|
||||
}
|
||||
|
||||
private void StopCapture(WpfTextBox box)
|
||||
{
|
||||
box.PreviewKeyDown -= CaptureBox_PreviewKeyDown;
|
||||
box.PreviewKeyUp -= CaptureBox_PreviewKeyUp;
|
||||
_capturingAction = null;
|
||||
_pendingGesture = null;
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
|
||||
private void Shortcuts_Changed(object? sender, EventArgs e)
|
||||
{
|
||||
if (!Dispatcher.CheckAccess())
|
||||
{
|
||||
Dispatcher.BeginInvoke(BuildRows);
|
||||
return;
|
||||
}
|
||||
BuildRows();
|
||||
}
|
||||
|
||||
private void Reset_Click(object sender, RoutedEventArgs e) => _shortcuts.ResetToDefaults();
|
||||
private void Close_Click(object sender, RoutedEventArgs e) => Close();
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_shortcuts.Changed -= Shortcuts_Changed;
|
||||
base.OnClosed(e);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user