diff --git a/FluentTerminal.App.ViewModels/MainViewModel.cs b/FluentTerminal.App.ViewModels/MainViewModel.cs index fe5a339..f2f1ee6 100644 --- a/FluentTerminal.App.ViewModels/MainViewModel.cs +++ b/FluentTerminal.App.ViewModels/MainViewModel.cs @@ -628,7 +628,7 @@ namespace FluentTerminal.App.ViewModels private const int RecentItemsMaxCount = 10; - private AppMenuViewModel _menuViewModel; + private MenuViewModel _menuViewModel; private readonly RelayCommand _newDefaultTabCommand; private readonly RelayCommand _newDefaultWindowCommand; @@ -640,7 +640,7 @@ namespace FluentTerminal.App.ViewModels private readonly RelayCommand _aboutCommand; private readonly ICommand _quitCommand; - public AppMenuViewModel MenuViewModel + public MenuViewModel AppMenuViewModel { get => _menuViewModel; private set => Set(ref _menuViewModel, value); @@ -648,7 +648,7 @@ namespace FluentTerminal.App.ViewModels private void CreateMenuViewModel() { - var appMenuViewModel = new AppMenuViewModel(); + var appMenuViewModel = new MenuViewModel(); if (_applicationSettings.TabWindowCascadingAppMenu) { @@ -711,7 +711,7 @@ namespace FluentTerminal.App.ViewModels if (!appMenuViewModel.EquivalentTo(_menuViewModel)) { - MenuViewModel = appMenuViewModel; + AppMenuViewModel = appMenuViewModel; } } diff --git a/FluentTerminal.App.ViewModels/Menu/AppMenuViewModel.cs b/FluentTerminal.App.ViewModels/Menu/MenuViewModel.cs similarity index 83% rename from FluentTerminal.App.ViewModels/Menu/AppMenuViewModel.cs rename to FluentTerminal.App.ViewModels/Menu/MenuViewModel.cs index 60a84c5..c9c8348 100644 --- a/FluentTerminal.App.ViewModels/Menu/AppMenuViewModel.cs +++ b/FluentTerminal.App.ViewModels/Menu/MenuViewModel.cs @@ -5,11 +5,11 @@ using GalaSoft.MvvmLight; namespace FluentTerminal.App.ViewModels.Menu { - public class AppMenuViewModel : ViewModelBase + public class MenuViewModel : ViewModelBase { public ObservableCollection Items { get; } - public AppMenuViewModel(IEnumerable items = null) + public MenuViewModel(IEnumerable items = null) { if (items == null) { @@ -25,7 +25,7 @@ namespace FluentTerminal.App.ViewModels.Menu } } - public bool EquivalentTo(AppMenuViewModel other) + public bool EquivalentTo(MenuViewModel other) { if (ReferenceEquals(this, other)) return true; diff --git a/FluentTerminal.App.ViewModels/TerminalViewModel.cs b/FluentTerminal.App.ViewModels/TerminalViewModel.cs index 666b0db..be992df 100644 --- a/FluentTerminal.App.ViewModels/TerminalViewModel.cs +++ b/FluentTerminal.App.ViewModels/TerminalViewModel.cs @@ -13,6 +13,8 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Windows.UI.Core; using FluentTerminal.Models.Messages; +using System.Windows.Input; +using FluentTerminal.App.ViewModels.Menu; namespace FluentTerminal.App.ViewModels { @@ -105,6 +107,9 @@ namespace FluentTerminal.App.ViewModels private string _shellTitle; private bool _hasCustomTitle; private byte? _terminalId; + private bool _hasSelection; + private string _hoveredUri; + private MenuViewModel _contextMenu; public TerminalViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService, ApplicationSettings applicationSettings, ShellProfile shellProfile, @@ -113,6 +118,7 @@ namespace FluentTerminal.App.ViewModels MessengerInstance.Register(this, OnApplicationSettingsChanged); MessengerInstance.Register(this, OnCurrentThemeChanged); MessengerInstance.Register(this, OnTerminalOptionsChanged); + MessengerInstance.Register(this, OnKeyBindingsChanged); SettingsService = settingsService; @@ -142,7 +148,10 @@ namespace FluentTerminal.App.ViewModels SelectTabThemeCommand = new RelayCommand(SelectTabTheme, CanExecuteCommand); EditTitleCommand = new AsyncCommand(EditTitleAsync, CanExecuteCommand); DuplicateTabCommand = new RelayCommand(DuplicateTab, CanExecuteCommand); - + CopyCommand = new AsyncCommand(Copy, () => HasSelection); + PasteCommand = new AsyncCommand(Paste); + CopyLinkCommand = new AsyncCommand(() => CopyTextAsync(HoveredUri), () => !string.IsNullOrWhiteSpace(HoveredUri)); + ShowSearchPanelCommand = new RelayCommand(() => ShowSearchPanel = true, () => !ShowSearchPanel); if (!string.IsNullOrEmpty(terminalState)) { Restore(terminalState); @@ -155,6 +164,14 @@ namespace FluentTerminal.App.ViewModels Terminal.TitleChanged += Terminal_TitleChanged; Terminal.Exited += Terminal_Exited; Terminal.Closed += Terminal_Closed; + + ContextMenu = BuidContextMenu(); + } + + public MenuViewModel ContextMenu + { + get => _contextMenu; + set => Set(ref _contextMenu, value); } private bool _disposalRequested; @@ -217,6 +234,14 @@ namespace FluentTerminal.App.ViewModels public RelayCommand DuplicateTabCommand { get; } + public ICommand CopyLinkCommand { get; private set; } + + public ICommand CopyCommand { get; private set; } + + public ICommand PasteCommand { get; private set; } + + public ICommand ShowSearchPanelCommand { get; private set; } + public double BackgroundOpacity => _terminalOptions?.BackgroundOpacity ?? 1.0; public bool IsSelected @@ -242,6 +267,24 @@ namespace FluentTerminal.App.ViewModels } } + public bool HasSelection + { + get => _hasSelection; + set => Set(ref _hasSelection, value); + } + + public string HoveredUri + { + get => _hoveredUri; + set + { + if (Set(ref _hoveredUri, value)) + { + ContextMenu = BuidContextMenu(); + } + } + } + public bool IsHovered { get => _isHovered; @@ -517,6 +560,11 @@ namespace FluentTerminal.App.ViewModels ApplicationView.ExecuteOnUiThreadAsync(() => RaisePropertyChanged(nameof(BackgroundOpacity)), CoreDispatcherPriority.Low); } + private void OnKeyBindingsChanged(KeyBindingsChangedMessage message) + { + ContextMenu = BuidContextMenu(); + } + private void SelectTabTheme(string id) { TabTheme = TabThemes.FirstOrDefault(t => t.Id == int.Parse(id)); @@ -549,18 +597,12 @@ namespace FluentTerminal.App.ViewModels { case nameof(Command.Copy): { - var selection = await Terminal.GetSelectedText().ConfigureAwait(false); - await CopyTextAsync(selection).ConfigureAwait(false); + await Copy().ConfigureAwait(false); return; } case nameof(Command.Paste): { - string content = await ClipboardService.GetTextAsync().ConfigureAwait(false); - if (content != null) - { - content = ShellProfile.TranslateLineEndings(content); - await TerminalView.PasteAsync(content).ConfigureAwait(false); - } + await Paste().ConfigureAwait(false); return; } case nameof(Command.PasteWithoutNewlines): @@ -632,5 +674,75 @@ namespace FluentTerminal.App.ViewModels await CloseAsync().ConfigureAwait(false); } } + + private async Task Copy() + { + var selection = await Terminal.GetSelectedText().ConfigureAwait(false); + await CopyTextAsync(selection).ConfigureAwait(false); + } + + private async Task Paste() + { + var content = await ClipboardService.GetTextAsync().ConfigureAwait(false); + if (content != null) + { + content = ShellProfile.TranslateLineEndings(content); + await TerminalView.PasteAsync(content).ConfigureAwait(false); + } + } + + private MenuViewModel BuidContextMenu() + { + var commandKeyBindings = SettingsService.GetCommandKeyBindings(); + + void addKeyBindings(MenuItemViewModel menuItem, Command command) + { + var keyBindings = commandKeyBindings[command.ToString()]; + if (keyBindings?.Any() == true) + { + menuItem.KeyBinding = new MenuItemKeyBindingViewModel(keyBindings.First()); + } + } + + var contextMenu = new MenuViewModel(); + if (!string.IsNullOrWhiteSpace(HoveredUri)) + { + var copyLink = new MenuItemViewModel(I18N.Translate("CopyLink"), CopyLinkCommand, icon: "\uE71B"); + contextMenu.Items.Add(copyLink); + contextMenu.Items.Add(new SeparatorMenuItemViewModel()); + } + + var copy = new MenuItemViewModel(I18N.Translate("Command.Copy"), CopyCommand, icon: "\uE8C8"); + addKeyBindings(copy, Command.Copy); + contextMenu.Items.Add(copy); + + var paste = new MenuItemViewModel(I18N.Translate("Command.Paste"), PasteCommand, icon: "\uE77F"); + addKeyBindings(paste, Command.Paste); + contextMenu.Items.Add(paste); + + contextMenu.Items.Add(new SeparatorMenuItemViewModel()); + + var editTitle = new MenuItemViewModel(I18N.Translate("EditTitle.Text"), EditTitleCommand, icon: "\uE70F"); + addKeyBindings(editTitle, Command.ChangeTabTitle); + contextMenu.Items.Add(editTitle); + + var search = new MenuItemViewModel(I18N.Translate("Search.Text"), ShowSearchPanelCommand, icon: "\uE721"); + addKeyBindings(search, Command.Search); + contextMenu.Items.Add(search); + + contextMenu.Items.Add(new SeparatorMenuItemViewModel()); + + var duplicate = new MenuItemViewModel(I18N.Translate("Command.DuplicateTab"), DuplicateTabCommand); + addKeyBindings(duplicate, Command.DuplicateTab); + contextMenu.Items.Add(duplicate); + + contextMenu.Items.Add(new SeparatorMenuItemViewModel()); + + var close = new MenuItemViewModel(I18N.Translate("Close"), CloseCommand, icon: "\uE711"); + addKeyBindings(close, Command.CloseTab); + contextMenu.Items.Add(close); + + return contextMenu; + } } } \ No newline at end of file diff --git a/FluentTerminal.App/App.xaml b/FluentTerminal.App/App.xaml index e55b37c..75b4f38 100644 --- a/FluentTerminal.App/App.xaml +++ b/FluentTerminal.App/App.xaml @@ -42,6 +42,7 @@ + \ No newline at end of file diff --git a/FluentTerminal.App/Converters/AppMenuViewModelToFlyoutMenuConverter.cs b/FluentTerminal.App/Converters/MenuViewModelToFlyoutMenuConverter.cs similarity index 81% rename from FluentTerminal.App/Converters/AppMenuViewModelToFlyoutMenuConverter.cs rename to FluentTerminal.App/Converters/MenuViewModelToFlyoutMenuConverter.cs index 34b1a6a..6fe8120 100644 --- a/FluentTerminal.App/Converters/AppMenuViewModelToFlyoutMenuConverter.cs +++ b/FluentTerminal.App/Converters/MenuViewModelToFlyoutMenuConverter.cs @@ -6,7 +6,7 @@ using Windows.UI.Xaml.Controls.Primitives; namespace FluentTerminal.App.Converters { - public class AppMenuViewModelToFlyoutMenuConverter : IValueConverter + public class MenuViewModelToFlyoutMenuConverter : IValueConverter { private static readonly MenuItemViewModelBaseToMenuFlayoutItemBaseConverter ItemConverter = new MenuItemViewModelBaseToMenuFlayoutItemBaseConverter(); @@ -18,10 +18,10 @@ namespace FluentTerminal.App.Converters return null; } - if (!(value is AppMenuViewModel appMenuViewModel)) + if (!(value is MenuViewModel menuViewModel)) { throw new ArgumentException( - $"Invalid {nameof(value)} argument type: {value.GetType()}. {typeof(AppMenuViewModel)} expected."); + $"Invalid {nameof(value)} argument type: {value.GetType()}. {typeof(MenuViewModel)} expected."); } var menuFlyout = new MenuFlyout @@ -29,7 +29,7 @@ namespace FluentTerminal.App.Converters Placement = FlyoutPlacementMode.BottomEdgeAlignedLeft }; - foreach (var menuItemViewModelBase in appMenuViewModel.Items) + foreach (var menuItemViewModelBase in menuViewModel.Items) { menuFlyout.Items?.Add((MenuFlyoutItemBase) ItemConverter.Convert(menuItemViewModelBase)); } diff --git a/FluentTerminal.App/FluentTerminal.App.csproj b/FluentTerminal.App/FluentTerminal.App.csproj index d0beb9b..d20234a 100644 --- a/FluentTerminal.App/FluentTerminal.App.csproj +++ b/FluentTerminal.App/FluentTerminal.App.csproj @@ -107,7 +107,7 @@ - + diff --git a/FluentTerminal.App/Strings/ar-iq/Resources.resw b/FluentTerminal.App/Strings/ar-iq/Resources.resw index 6ce9f95..8ad7348 100644 --- a/FluentTerminal.App/Strings/ar-iq/Resources.resw +++ b/FluentTerminal.App/Strings/ar-iq/Resources.resw @@ -1044,4 +1044,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/az/Resources.resw b/FluentTerminal.App/Strings/az/Resources.resw index 850540a..389f7f8 100644 --- a/FluentTerminal.App/Strings/az/Resources.resw +++ b/FluentTerminal.App/Strings/az/Resources.resw @@ -1044,4 +1044,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/de/Resources.resw b/FluentTerminal.App/Strings/de/Resources.resw index 3625402..932f1d8 100644 --- a/FluentTerminal.App/Strings/de/Resources.resw +++ b/FluentTerminal.App/Strings/de/Resources.resw @@ -1077,4 +1077,7 @@ Fuzzy Helle Farben + + Link kopieren + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/en/Resources.resw b/FluentTerminal.App/Strings/en/Resources.resw index a62c9fc..9f94223 100644 --- a/FluentTerminal.App/Strings/en/Resources.resw +++ b/FluentTerminal.App/Strings/en/Resources.resw @@ -1111,4 +1111,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/es/Resources.resw b/FluentTerminal.App/Strings/es/Resources.resw index c54ecd2..a19347d 100644 --- a/FluentTerminal.App/Strings/es/Resources.resw +++ b/FluentTerminal.App/Strings/es/Resources.resw @@ -1065,4 +1065,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/fr/Resources.resw b/FluentTerminal.App/Strings/fr/Resources.resw index 22e2c5c..3ee04f0 100644 --- a/FluentTerminal.App/Strings/fr/Resources.resw +++ b/FluentTerminal.App/Strings/fr/Resources.resw @@ -1108,4 +1108,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/he/Resources.resw b/FluentTerminal.App/Strings/he/Resources.resw index f104031..51b8671 100644 --- a/FluentTerminal.App/Strings/he/Resources.resw +++ b/FluentTerminal.App/Strings/he/Resources.resw @@ -1053,4 +1053,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/hi/Resources.resw b/FluentTerminal.App/Strings/hi/Resources.resw index 0be055e..0e418a8 100644 --- a/FluentTerminal.App/Strings/hi/Resources.resw +++ b/FluentTerminal.App/Strings/hi/Resources.resw @@ -1044,4 +1044,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/it/Resources.resw b/FluentTerminal.App/Strings/it/Resources.resw index 2ad2102..94ead63 100644 --- a/FluentTerminal.App/Strings/it/Resources.resw +++ b/FluentTerminal.App/Strings/it/Resources.resw @@ -1056,4 +1056,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/ja/Resources.resw b/FluentTerminal.App/Strings/ja/Resources.resw index 9fd386c..9a1353d 100644 --- a/FluentTerminal.App/Strings/ja/Resources.resw +++ b/FluentTerminal.App/Strings/ja/Resources.resw @@ -1044,4 +1044,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/ko/Resources.resw b/FluentTerminal.App/Strings/ko/Resources.resw index bce8f4d..5fde1e0 100644 --- a/FluentTerminal.App/Strings/ko/Resources.resw +++ b/FluentTerminal.App/Strings/ko/Resources.resw @@ -1053,4 +1053,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/nl/Resources.resw b/FluentTerminal.App/Strings/nl/Resources.resw index 40094e7..2724e36 100644 --- a/FluentTerminal.App/Strings/nl/Resources.resw +++ b/FluentTerminal.App/Strings/nl/Resources.resw @@ -1110,4 +1110,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/pl/Resources.resw b/FluentTerminal.App/Strings/pl/Resources.resw index af34e91..80fb14c 100644 --- a/FluentTerminal.App/Strings/pl/Resources.resw +++ b/FluentTerminal.App/Strings/pl/Resources.resw @@ -1045,4 +1045,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/pt-BR/Resources.resw b/FluentTerminal.App/Strings/pt-BR/Resources.resw index bd73f73..45304a6 100644 --- a/FluentTerminal.App/Strings/pt-BR/Resources.resw +++ b/FluentTerminal.App/Strings/pt-BR/Resources.resw @@ -1045,4 +1045,7 @@ Fuzzy Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/ro/Resources.resw b/FluentTerminal.App/Strings/ro/Resources.resw index fe5602b..112ae27 100644 --- a/FluentTerminal.App/Strings/ro/Resources.resw +++ b/FluentTerminal.App/Strings/ro/Resources.resw @@ -1044,4 +1044,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/ru/Resources.resw b/FluentTerminal.App/Strings/ru/Resources.resw index 6c26a73..446ed95 100644 --- a/FluentTerminal.App/Strings/ru/Resources.resw +++ b/FluentTerminal.App/Strings/ru/Resources.resw @@ -1045,4 +1045,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/uk/Resources.resw b/FluentTerminal.App/Strings/uk/Resources.resw index 8afbaa8..0776528 100644 --- a/FluentTerminal.App/Strings/uk/Resources.resw +++ b/FluentTerminal.App/Strings/uk/Resources.resw @@ -1044,4 +1044,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Strings/zh-CN/Resources.resw b/FluentTerminal.App/Strings/zh-CN/Resources.resw index cd3a7e6..de7461a 100644 --- a/FluentTerminal.App/Strings/zh-CN/Resources.resw +++ b/FluentTerminal.App/Strings/zh-CN/Resources.resw @@ -1044,4 +1044,7 @@ Bright colors + + Copy link + \ No newline at end of file diff --git a/FluentTerminal.App/Views/MainPage.xaml b/FluentTerminal.App/Views/MainPage.xaml index da5c64e..51c8314 100644 --- a/FluentTerminal.App/Views/MainPage.xaml +++ b/FluentTerminal.App/Views/MainPage.xaml @@ -14,7 +14,6 @@ - @@ -40,7 +39,7 @@ -