Files
FluentTerminal/FluentTerminal.App.ViewModels/MainViewModel.cs
T
Aleksandar Pesic 7ab42ec1de Menu item keyboard accelerators implemented (#589)
* Added icon to "Recent >" menu item.

* Refactored app menu implementation, and implemented keyboard accelerators. Still doesn't work though.

* Few things fixed, but still doesn't work.

* Clicking menu items works.

* Fixing an issue with resources.

* New menu binding implemented. Still doesn't work though.

* Menu displayed properly, except for "Settings".

* Menu works, but it doesn't refresh.

* Works. But needs to be polished.

* All known issues with the new app menu solved.

* Significantly simplifying app menu binding.

* Removing some obsolete classes.

* Menu binding mechanism additionally optimized for performances.

* Removing unused translations.

* Further refactoring app menu binding, in order to make it more general.

* New window mechanism simplified. Terminal-launching code refactored to be more readable. A bug with "SSH Info" fixed.

* Merging the latest changed.
2019-11-26 22:03:22 +01:00

786 lines
32 KiB
C#

using FluentTerminal.App.Services;
using FluentTerminal.App.Services.EventArgs;
using FluentTerminal.App.Services.Utilities;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using FluentTerminal.App.ViewModels.Menu;
using FluentTerminal.Models.Messages;
using GalaSoft.MvvmLight.Command;
namespace FluentTerminal.App.ViewModels
{
public class MainViewModel : ViewModelBase
{
private readonly IClipboardService _clipboardService;
private readonly IDialogService _dialogService;
private readonly IDispatcherTimer _dispatcherTimer;
private readonly IKeyboardCommandService _keyboardCommandService;
private readonly ISettingsService _settingsService;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
private readonly ICommandHistoryService _commandHistoryService;
private readonly IAcceleratorKeyValidator _acceleratorKeyValidator;
private ApplicationSettings _applicationSettings;
private string _background;
private double _backgroundOpacity;
private TerminalViewModel _selectedTerminal;
private TabsPosition _tabsPosition;
private string _windowTitle;
private IDictionary<string, ICollection<KeyBinding>> _keyBindings;
public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService,
IApplicationView applicationView, IDispatcherTimer dispatcherTimer, IClipboardService clipboardService, ICommandHistoryService commandHistoryService, IAcceleratorKeyValidator acceleratorKeyValidator)
{
MessengerInstance.Register<ApplicationSettingsChangedMessage>(this, OnApplicationSettingsChanged);
MessengerInstance.Register<CurrentThemeChangedMessage>(this, OnCurrentThemeChanged);
MessengerInstance.Register<ShellProfileAddedMessage>(this, OnShellProfileAdded);
MessengerInstance.Register<ShellProfileDeletedMessage>(this, OnShellProfileDeleted);
MessengerInstance.Register<SshProfileAddedMessage>(this, OnSshProfileAdded);
MessengerInstance.Register<SshProfileDeletedMessage>(this, OnSshProfileDeleted);
MessengerInstance.Register<TerminalOptionsChangedMessage>(this, OnTerminalOptionsChanged);
MessengerInstance.Register<CommandHistoryChangedMessage>(this, OnCommandHistoryChanged);
MessengerInstance.Register<KeyBindingsChangedMessage>(this, OnKeyBindingChanged);
_settingsService = settingsService;
_trayProcessCommunicationService = trayProcessCommunicationService;
_dialogService = dialogService;
ApplicationView = applicationView;
_dispatcherTimer = dispatcherTimer;
_clipboardService = clipboardService;
_keyboardCommandService = keyboardCommandService;
_commandHistoryService = commandHistoryService;
_acceleratorKeyValidator = acceleratorKeyValidator;
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewTab), async () => await AddDefaultProfileAsync(NewTerminalLocation.Tab));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewWindow), async () => await AddDefaultProfileAsync(NewTerminalLocation.Window));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewSshTab), async () => await AddSshProfileAsync(NewTerminalLocation.Tab));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewSshWindow), async () => await AddSshProfileAsync(NewTerminalLocation.Window));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewCustomCommandTab), async () => await AddQuickLaunchProfileAsync(NewTerminalLocation.Tab));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewCustomCommandWindow), async () => await AddQuickLaunchProfileAsync(NewTerminalLocation.Window));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ConfigurableNewTab), async () => await AddSelectedProfileAsync(NewTerminalLocation.Tab));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ConfigurableNewWindow), async () => await AddSelectedProfileAsync(NewTerminalLocation.Window));
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ChangeTabTitle), async () => await SelectedTerminal.EditTitle());
_keyboardCommandService.RegisterCommandHandler(nameof(Command.CloseTab), CloseCurrentTab);
_keyboardCommandService.RegisterCommandHandler(nameof(Command.DuplicateTab), async () => await AddTabAsync(SelectedTerminal.ShellProfile.Clone()));
// Add all of the commands for switching to a tab of a given ID, if there's one open there
for (int i = 0; i < 9; i++)
{
var switchCmd = Command.SwitchToTerm1 + i;
int tabNumber = i;
// ReSharper disable once InconsistentNaming
void handler() => SelectTabNumber(tabNumber);
_keyboardCommandService.RegisterCommandHandler(switchCmd.ToString(), handler);
}
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NextTab), SelectNextTab);
_keyboardCommandService.RegisterCommandHandler(nameof(Command.PreviousTab), SelectPreviousTab);
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ShowSettings), ShowSettings);
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ToggleFullScreen), ToggleFullScreen);
foreach (ShellProfile profile in _settingsService.GetShellProfiles())
{
_keyboardCommandService.RegisterCommandHandler(profile.Id.ToString(), async () => await AddProfileByGuidAsync(profile.Id));
}
foreach (SshProfile profile in _settingsService.GetSshProfiles())
{
_keyboardCommandService.RegisterCommandHandler(profile.Id.ToString(), async () => await AddProfileByGuidAsync(profile.Id));
}
var currentTheme = _settingsService.GetCurrentTheme();
var options = _settingsService.GetTerminalOptions();
Background = currentTheme.Colors.Background;
BackgroundOpacity = options.BackgroundOpacity;
_applicationSettings = _settingsService.GetApplicationSettings();
TabsPosition = _applicationSettings.TabsPosition;
AddLocalShellCommand = new RelayCommand(async () => await AddDefaultProfileAsync(NewTerminalLocation.Tab));
ApplicationView.CloseRequested += OnCloseRequest;
ApplicationView.Closed += OnClosed;
Terminals.CollectionChanged += OnTerminalsCollectionChanged;
LoadKeyBindings();
InitializeAppMenu();
}
private void LoadKeyBindings() => _keyBindings = _settingsService.GetCommandKeyBindings();
private void OnClosed(object sender, EventArgs e)
{
MessengerInstance.Unregister(this);
ApplicationView.CloseRequested -= OnCloseRequest;
ApplicationView.Closed -= OnClosed;
Terminals.CollectionChanged -= OnTerminalsCollectionChanged;
_keyboardCommandService.ClearCommandHandlers();
_applicationSettings = null;
AddLocalShellCommand = null;
Closed?.Invoke(this, e);
}
private void OnShellProfileDeleted(ShellProfileDeletedMessage message)
{
_keyboardCommandService.DeregisterCommandHandler(message.ProfileId.ToString());
}
private void OnShellProfileAdded(ShellProfileAddedMessage message)
{
_keyboardCommandService.RegisterCommandHandler(message.ShellProfile.Id.ToString(),
async () => await AddProfileByGuidAsync(message.ShellProfile.Id));
}
private void OnSshProfileAdded(SshProfileAddedMessage message)
{
_keyboardCommandService.RegisterCommandHandler(message.SshProfile.Id.ToString(),
async () => await AddProfileByGuidAsync(message.SshProfile.Id));
}
private void OnSshProfileDeleted(SshProfileDeletedMessage message)
{
_keyboardCommandService.DeregisterCommandHandler(message.ProfileId.ToString());
}
private void OnTerminalsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged(nameof(ShowTabsOnTop));
RaisePropertyChanged(nameof(ShowTabsOnBottom));
}
public event EventHandler Closed;
public event EventHandler<NewWindowRequestedEventArgs> NewWindowRequested;
public event EventHandler ShowSettingsRequested;
public event EventHandler ActivatedMv;
public event EventHandler<TerminalViewModel> TabTearedOff;
public void TearOffTab(TerminalViewModel model)
{
TabTearedOff?.Invoke(this, model);
}
public void FocusWindow()
{
ActivatedMv?.Invoke(this, EventArgs.Empty);
}
public RelayCommand AddLocalShellCommand { get; private set; }
public string WindowTitle
{
get => _windowTitle;
set
{
if (Set(ref _windowTitle, value))
{
ApplicationView.Title = value;
}
}
}
public bool ShowTabsOnTop
{
get => TabsPosition == TabsPosition.Top;
}
public bool ShowTabsOnBottom
{
get => TabsPosition == TabsPosition.Bottom;
}
public string Background
{
get => _background;
set => Set(ref _background, value);
}
public double BackgroundOpacity
{
get => _backgroundOpacity;
set => Set(ref _backgroundOpacity, value);
}
public TerminalViewModel SelectedTerminal
{
get => _selectedTerminal;
set
{
if (SelectedTerminal != null)
{
_selectedTerminal.IsSelected = false;
}
if (Set(ref _selectedTerminal, value))
{
SelectedTerminal?.FocusTerminal();
if (SelectedTerminal != null)
{
SelectedTerminal.IsSelected = true;
if (_applicationSettings.ShowCustomTitleInTitlebar)
{
WindowTitle = SelectedTerminal.TabTitle;
}
else
{
WindowTitle = SelectedTerminal.ShellTitle;
}
}
}
}
}
public TabsPosition TabsPosition
{
get => _tabsPosition;
set => Set(ref _tabsPosition, value);
}
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
public IApplicationView ApplicationView { get; }
#region Launching terminal sessions
#region Default profile
private Task AddDefaultProfileAsync() =>
AddDefaultProfileAsync(_settingsService.GetApplicationSettings().NewTerminalLocation);
public Task AddDefaultProfileAsync(NewTerminalLocation location)
{
var profile = _settingsService.GetDefaultShellProfile();
return AddProfileAsync(profile, location);
}
#endregion Default profile
#region User-selected profile
private Task AddSelectedProfileAsync() =>
AddSelectedProfileAsync(_settingsService.GetApplicationSettings().NewTerminalLocation);
private async Task AddSelectedProfileAsync(NewTerminalLocation location)
{
var profile = await _dialogService.ShowProfileSelectionDialogAsync();
await AddProfileAsync(profile, location);
}
#endregion User-selected profile
#region SSH profile
private Task AddSshProfileAsync() =>
AddSshProfileAsync(_settingsService.GetApplicationSettings().NewTerminalLocation);
private async Task AddSshProfileAsync(NewTerminalLocation location)
{
var profile = await _dialogService.ShowSshConnectionInfoDialogAsync();
await AddProfileAsync(profile, location);
}
#endregion SSH profile
#region Quick launch profile
private Task AddQuickLaunchProfileAsync() =>
AddQuickLaunchProfileAsync(_settingsService.GetApplicationSettings().NewTerminalLocation);
private async Task AddQuickLaunchProfileAsync(NewTerminalLocation location)
{
var profile = await _dialogService.ShowCustomCommandDialogAsync();
await AddProfileAsync(profile, location);
}
#endregion Quick launch profile
#region Profile by Guid
public Task AddProfileByGuidAsync(Guid profileId) =>
AddProfileByGuidAsync(profileId, _settingsService.GetApplicationSettings().NewTerminalLocation);
private async Task AddProfileByGuidAsync(Guid profileId, NewTerminalLocation location)
{
var profile = _settingsService.GetShellProfile(profileId) ?? _settingsService.GetSshProfile(profileId);
await AddProfileAsync(profile, location);
}
#endregion Profile by Guid
#region For serialization
public Task AddTabAsync(string terminalState, int position)
{
return AddTabAsync(new ShellProfile(), terminalState, position);
}
#endregion For serialization
private Task AddProfileAsync(ShellProfile profile) =>
AddProfileAsync(profile, _settingsService.GetApplicationSettings().NewTerminalLocation);
private Task AddProfileAsync(ShellProfile profile, NewTerminalLocation location)
{
if (profile == null)
{
return Task.CompletedTask;
}
if (location == NewTerminalLocation.Tab)
{
return AddTabAsync(profile);
}
NewWindowRequested?.Invoke(this, new NewWindowRequestedEventArgs(profile));
return Task.CompletedTask;
}
public Task AddTabAsync(ShellProfile profile) => AddTabAsync(profile, string.Empty, Terminals.Count);
private Task AddTabAsync(ShellProfile profile, string terminalState, int position)
{
profile.Tag = new DelayedHistorySaver(() => _commandHistoryService.MarkUsed(profile));
return ApplicationView.RunOnDispatcherThread(() =>
{
var terminal = new TerminalViewModel(_settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService,
_applicationSettings, profile, ApplicationView, _dispatcherTimer, _clipboardService, terminalState);
terminal.Closed += OnTerminalClosed;
terminal.ShellTitleChanged += Terminal_ShellTitleChanged;
terminal.CustomTitleChanged += Terminal_CustomTitleChanged;
terminal.CloseLeftTabsRequested += Terminal_CloseLeftTabsRequested;
terminal.CloseRightTabsRequested += Terminal_CloseRightTabsRequested;
terminal.CloseOtherTabsRequested += Terminal_CloseOtherTabsRequested;
terminal.DuplicateTabRequested += Terminal_DuplicateTabRequested;
Terminals.Insert(Math.Min(position, Terminals.Count), terminal);
SelectedTerminal = terminal;
});
}
#endregion Launching terminal sessions
private async void Terminal_DuplicateTabRequested(object sender, EventArgs e)
{
if (sender is TerminalViewModel terminal)
{
await AddTabAsync(terminal.ShellProfile.Clone());
}
}
private void Terminal_CloseOtherTabsRequested(object sender, EventArgs e)
{
if (sender is TerminalViewModel terminal)
{
Array.ForEach(Terminals.ToArray(),
async t => {
if (terminal != t)
{
Logger.Instance.Debug("Terminal with Id: {@id} closed.", t.Terminal.Id);
await t.CloseCommand.ExecuteAsync();
}
});
}
}
private async void Terminal_CloseRightTabsRequested(object sender, EventArgs e)
{
if (sender is TerminalViewModel terminal)
{
for (int i = Terminals.Count - 1; i > Terminals.IndexOf(terminal); --i)
{
var terminalToRemove = Terminals[i];
Logger.Instance.Debug("Terminal with Id: {@id} closed.", terminalToRemove.Terminal.Id);
await terminalToRemove.CloseCommand.ExecuteAsync();
}
}
}
private async void Terminal_CloseLeftTabsRequested(object sender, EventArgs e)
{
if (sender is TerminalViewModel terminal)
{
for(int i = Terminals.IndexOf(terminal) - 1; i >= 0; --i)
{
var terminalToRemove = Terminals[i];
Logger.Instance.Debug("Terminal with Id: {@id} closed.", terminalToRemove.Terminal.Id);
await terminalToRemove.CloseCommand.ExecuteAsync();
}
}
}
private void Terminal_CustomTitleChanged(object sender, string e)
{
if (sender is TerminalViewModel terminal)
{
if (terminal.IsSelected && _applicationSettings.ShowCustomTitleInTitlebar)
{
if (string.IsNullOrWhiteSpace(e))
{
WindowTitle = terminal.ShellTitle;
}
else
{
WindowTitle = e;
}
}
}
}
private void Terminal_ShellTitleChanged(object sender, string e)
{
if (sender is TerminalViewModel terminal && terminal.IsSelected && !_applicationSettings.ShowCustomTitleInTitlebar)
{
WindowTitle = e;
}
}
public async Task CloseAllTerminals()
{
foreach (var terminal in Terminals)
{
terminal.Closed -= OnTerminalClosed;
terminal.ShellTitleChanged -= Terminal_ShellTitleChanged;
terminal.CustomTitleChanged -= Terminal_CustomTitleChanged;
terminal.CloseLeftTabsRequested -= Terminal_CloseLeftTabsRequested;
terminal.CloseRightTabsRequested -= Terminal_CloseRightTabsRequested;
terminal.CloseOtherTabsRequested -= Terminal_CloseOtherTabsRequested;
terminal.DuplicateTabRequested -= Terminal_DuplicateTabRequested;
await terminal.Close();
}
}
private void CloseCurrentTab()
{
SelectedTerminal?.CloseCommand.ExecuteAsync();
}
private async void OnApplicationSettingsChanged(ApplicationSettingsChangedMessage message)
{
await ApplicationView.RunOnDispatcherThread(() =>
{
_applicationSettings = message.ApplicationSettings;
TabsPosition = message.ApplicationSettings.TabsPosition;
RaisePropertyChanged(nameof(ShowTabsOnTop));
RaisePropertyChanged(nameof(ShowTabsOnBottom));
if (message.ApplicationSettings.ShowCustomTitleInTitlebar)
{
WindowTitle = SelectedTerminal?.TabTitle;
}
else
{
WindowTitle = SelectedTerminal?.ShellTitle;
}
});
}
private async Task OnCloseRequest(object sender, CancelableEventArgs e)
{
if (_applicationSettings.ConfirmClosingWindows)
{
var result = await _dialogService.ShowMessageDialogAsnyc(I18N.Translate("PleaseConfirm"), I18N.Translate("ConfirmCloseWindow"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(false);
if (result == DialogButton.OK)
{
await CloseAllTerminals();
}
else
{
e.Cancelled = true;
}
}
else
{
await CloseAllTerminals();
}
}
private async void OnCurrentThemeChanged(CurrentThemeChangedMessage message)
{
await ApplicationView.RunOnDispatcherThread(() =>
{
var currentTheme = _settingsService.GetTheme(message.ThemeId);
Background = currentTheme.Colors.Background;
});
}
private async void OnTerminalClosed(object sender, EventArgs e)
{
if (sender is TerminalViewModel terminal)
{
if (SelectedTerminal == terminal)
{
SelectedTerminal = Terminals.LastOrDefault(t => t != terminal);
}
Logger.Instance.Debug("Terminal with Id: {@id} closed.", terminal.Terminal.Id);
terminal.Closed -= OnTerminalClosed;
terminal.ShellTitleChanged -= Terminal_ShellTitleChanged;
terminal.CustomTitleChanged -= Terminal_CustomTitleChanged;
terminal.CloseLeftTabsRequested -= Terminal_CloseLeftTabsRequested;
terminal.CloseRightTabsRequested -= Terminal_CloseRightTabsRequested;
terminal.CloseOtherTabsRequested -= Terminal_CloseOtherTabsRequested;
terminal.DuplicateTabRequested -= Terminal_DuplicateTabRequested;
Terminals.Remove(terminal);
if (Terminals.Count == 0)
{
await ApplicationView.TryClose();
}
}
}
private async void OnTerminalOptionsChanged(TerminalOptionsChangedMessage message)
{
await ApplicationView.RunOnDispatcherThread(() =>
{
BackgroundOpacity = message.TerminalOptions.BackgroundOpacity;
});
}
private void OnKeyBindingChanged(KeyBindingsChangedMessage message)
{
LoadKeyBindings();
// Should be scheduled no matter if we're in the UI thread.
ApplicationView.RunOnDispatcherThread(CreateMenuViewModel);
}
private void OnCommandHistoryChanged(CommandHistoryChangedMessage message)
{
// Should be scheduled no matter if we're in the UI thread.
ApplicationView.RunOnDispatcherThread(CreateMenuViewModel);
}
private void SelectTabNumber(int tabNumber)
{
if (tabNumber < Terminals.Count)
{
SelectedTerminal = Terminals[tabNumber];
}
}
private void SelectNextTab()
{
var currentIndex = Terminals.IndexOf(SelectedTerminal);
var nextIndex = (currentIndex + 1) % Terminals.Count;
SelectedTerminal = Terminals[nextIndex];
}
private void SelectPreviousTab()
{
var currentIndex = Terminals.IndexOf(SelectedTerminal);
var previousIndex = (currentIndex - 1 + Terminals.Count) % Terminals.Count;
SelectedTerminal = Terminals[previousIndex];
}
public void ShowSettings()
{
ShowSettingsRequested?.Invoke(this, EventArgs.Empty);
}
private void ToggleFullScreen()
{
ApplicationView.ToggleFullScreen();
}
public void OnWindowKeyDown(int key, bool control, bool alt, bool shift, bool meta)
{
var binding = _keyBindings.Values.SelectMany(bs => bs).FirstOrDefault(b =>
b.Key == key && b.Ctrl == control && b.Alt == alt && b.Shift == shift && b.Meta == meta);
if (binding != null)
{
_keyboardCommandService.SendCommand(binding.Command);
}
}
#region App menu
private const int RecentItemsMaxCount = 10;
private AppMenuViewModel _menuViewModel;
private RelayCommand _newRemoteTabCommand;
private RelayCommand _newQuickTabCommand;
private RelayCommand _settingsCommand;
private RelayCommand _aboutCommand;
public AppMenuViewModel MenuViewModel
{
get => _menuViewModel;
private set
{
// Changing app menu view model only if it's actually different than previous.
if (!value.EquivalentTo(_menuViewModel))
{
Set(ref _menuViewModel, value);
}
}
}
private void InitializeAppMenu()
{
_newRemoteTabCommand = new RelayCommand(async () => await AddSshProfileAsync(NewTerminalLocation.Tab));
_newQuickTabCommand = new RelayCommand(async () => await AddQuickLaunchProfileAsync(NewTerminalLocation.Tab));
_settingsCommand = new RelayCommand(ShowSettings);
_aboutCommand = new RelayCommand(async () => await _dialogService.ShowAboutDialogAsync());
CreateMenuViewModel();
}
private void CreateMenuViewModel()
{
var tabItem = new MenuItemViewModel(I18N.TranslateWithFallback("NewTab.Text", "New tab"),
AddLocalShellCommand,
I18N.TranslateWithFallback("NewTab_Description", "Opens default profile in a new tab."),
icon: 57609 /*(int) Symbol.Add*/);
if (_keyBindings.TryGetValue(nameof(Command.NewTab), out var keyBindings) &&
keyBindings.FirstOrDefault() is KeyBinding tabKeyBindings &&
_acceleratorKeyValidator.Valid(tabKeyBindings.Key))
{
LoadKeyBindingsFromModel(tabItem, tabKeyBindings);
}
else
{
tabItem.KeyBinding = null;
}
var remoteTabItem = new MenuItemViewModel(I18N.TranslateWithFallback("NewSshTab.Text", "New remote tab"),
_newRemoteTabCommand,
I18N.TranslateWithFallback("NewSshTab_Description", "Opens a new SSH or Mosh session in a new tab."),
icon: 57609 /*(int) Symbol.Add*/);
if (_keyBindings.TryGetValue(nameof(Command.NewSshTab), out keyBindings) &&
keyBindings.FirstOrDefault() is KeyBinding remoteTabKeyBinding &&
_acceleratorKeyValidator.Valid(remoteTabKeyBinding.Key))
{
LoadKeyBindingsFromModel(remoteTabItem, remoteTabKeyBinding);
}
else
{
remoteTabItem.KeyBinding = null;
}
var quickTab = new MenuItemViewModel(I18N.TranslateWithFallback("NewQuickTab.Text", "New quick tab"),
_newQuickTabCommand,
I18N.TranslateWithFallback("NewQuickTab_Description",
"Opens \"Quick Launch\" dialog and starts session in a new tab."),
icon: 57609 /*(int) Symbol.Add*/);
if (_keyBindings.TryGetValue(nameof(Command.NewCustomCommandTab), out keyBindings) &&
keyBindings.FirstOrDefault() is KeyBinding quickTabKeyBinding &&
_acceleratorKeyValidator.Valid(quickTabKeyBinding.Key))
{
LoadKeyBindingsFromModel(quickTab, quickTabKeyBinding);
}
else
{
quickTab.KeyBinding = null;
}
var settings = new MenuItemViewModel(I18N.TranslateWithFallback("Settings.Text", "Settings"),
_settingsCommand, I18N.TranslateWithFallback("Settings_Description", "Opens settings window."),
icon: 57621 /*(int) Symbol.Setting*/);
if (_keyBindings.TryGetValue(nameof(Command.ShowSettings), out keyBindings) &&
keyBindings.FirstOrDefault() is KeyBinding settingsKeyBinding &&
_acceleratorKeyValidator.Valid(settingsKeyBinding.Key))
{
LoadKeyBindingsFromModel(settings, settingsKeyBinding);
}
else
{
settings.KeyBinding = null;
}
var recent = new ExpandableMenuItemViewModel(I18N.TranslateWithFallback("Recent.Text", "Recent"),
GetRecentMenuItems(), I18N.TranslateWithFallback("Recent_Description", "Recently opened sessions."),
icon: "\uF738" /*Segoe MDL2 Assets Glyph property*/);
var about = new MenuItemViewModel(I18N.TranslateWithFallback("AboutDialog.Title", "About"), _aboutCommand,
I18N.TranslateWithFallback("About_Description", "Basic info about the app."),
icon: "\uE946" /*Segoe MDL2 Assets Glyph property*/);
var appMenuViewModel = new AppMenuViewModel(new MenuItemViewModelBase[]
{
tabItem,
remoteTabItem,
quickTab,
settings,
recent,
about,
});
MenuViewModel = appMenuViewModel;
}
private ObservableCollection<MenuItemViewModel> GetRecentMenuItems() =>
new ObservableCollection<MenuItemViewModel>(_commandHistoryService
.GetHistoryRecentFirst(top: RecentItemsMaxCount).Select(CommandToMenuItem));
private MenuItemViewModel CommandToMenuItem(ExecutedCommand command)
{
var itemCommand = new RelayCommand(async () => await AddProfileAsync(command.ShellProfile),
keepTargetAlive: true);
var keyBinding = command.ShellProfile.KeyBindings?.FirstOrDefault() is KeyBinding kb
? new MenuItemKeyBindingViewModel(kb)
: null;
return new MenuItemViewModel(command.Value, itemCommand, keyBinding: keyBinding);
}
private void LoadKeyBindingsFromModel(MenuItemViewModel menuItemViewModel, KeyBinding keyBinding)
{
if (!_acceleratorKeyValidator.Valid(keyBinding.Key))
{
menuItemViewModel.KeyBinding = null;
return;
}
if (menuItemViewModel.KeyBinding == null)
{
menuItemViewModel.KeyBinding = new MenuItemKeyBindingViewModel(keyBinding);
}
else
{
menuItemViewModel.KeyBinding.Key = keyBinding.Key;
menuItemViewModel.KeyBinding.Ctrl = keyBinding.Ctrl;
menuItemViewModel.KeyBinding.Alt = keyBinding.Alt;
menuItemViewModel.KeyBinding.Shift = keyBinding.Shift;
menuItemViewModel.KeyBinding.Windows = keyBinding.Meta;
}
}
#endregion App menu
}
}