using FluentTerminal.App.Services; using FluentTerminal.Models; using FluentTerminal.Models.Enums; using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using Windows.ApplicationModel.Core; using Windows.UI.Core; using Windows.UI.Core.Preview; using Windows.UI.ViewManagement; namespace FluentTerminal.App.ViewModels { public class MainViewModel : ViewModelBase { private readonly IDialogService _dialogService; private readonly IKeyboardCommandService _keyboardCommandService; private readonly ISettingsService _settingsService; private readonly ITrayProcessCommunicationService _trayProcessCommunicationService; private ApplicationSettings _applicationSettings; private string _background; private double _backgroundOpacity; private readonly CoreDispatcher _dispatcher; private int _nextTerminalId; private TerminalViewModel _selectedTerminal; private string _title; public event EventHandler Closed; public event EventHandler NewWindowRequested; public event EventHandler ShowSettingsRequested; public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService) { _settingsService = settingsService; _settingsService.CurrentThemeChanged += OnCurrentThemeChanged; _settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged; _settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged; _trayProcessCommunicationService = trayProcessCommunicationService; _dialogService = dialogService; _keyboardCommandService = keyboardCommandService; _keyboardCommandService.RegisterCommandHandler(Command.NewTab, () => AddTerminal(null, false)); _keyboardCommandService.RegisterCommandHandler(Command.ConfigurableNewTab, () => AddTerminal(null, true)); _keyboardCommandService.RegisterCommandHandler(Command.CloseTab, CloseCurrentTab); _keyboardCommandService.RegisterCommandHandler(Command.NextTab, SelectNextTab); _keyboardCommandService.RegisterCommandHandler(Command.PreviousTab, SelectPreviousTab); _keyboardCommandService.RegisterCommandHandler(Command.NewWindow, NewWindow); _keyboardCommandService.RegisterCommandHandler(Command.ShowSettings, ShowSettings); _keyboardCommandService.RegisterCommandHandler(Command.ToggleFullScreen, ToggleFullScreen); var currentTheme = _settingsService.GetCurrentTheme(); var options = _settingsService.GetTerminalOptions(); Background = currentTheme.Colors.Background; BackgroundOpacity = options.BackgroundOpacity; _applicationSettings = _settingsService.GetApplicationSettings(); AddTerminalCommand = new RelayCommand(() => AddTerminal(null, false)); ShowSettingsCommand = new RelayCommand(ShowSettings); Terminals.CollectionChanged += OnTerminalsCollectionChanged; Title = "Fluent Terminal"; _dispatcher = CoreApplication.GetCurrentView().Dispatcher; SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest; } private async void OnTerminalOptionsChanged(object sender, TerminalOptions e) { await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { BackgroundOpacity = e.BackgroundOpacity; }); } public RelayCommand AddTerminalCommand { get; } 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 (Set(ref _selectedTerminal, value)) { SelectedTerminal?.FocusTerminal(); } } } public RelayCommand ShowSettingsCommand { get; } public ObservableCollection Terminals { get; } = new ObservableCollection(); public string Title { get => _title; set => Set(ref _title, value); } public Task AddTerminal(string startupDirectory, bool showProfileSelection) { return _dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => { ShellProfile profile = null; if (showProfileSelection) { profile = await _dialogService.ShowProfileSelectionDialogAsync(); if (profile == null) { return; } } else { profile = _settingsService.GetDefaultShellProfile(); } var terminal = new TerminalViewModel(GetNextTerminalId(), _settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService, _applicationSettings, startupDirectory, profile); terminal.CloseRequested += OnTerminalCloseRequested; terminal.TitleChanged += OnTitleChanged; Terminals.Add(terminal); SelectedTerminal = terminal; }).AsTask(); } public void CloseAllTerminals() { foreach (var terminal in Terminals) { terminal.CloseView(); } } private void CloseCurrentTab() { SelectedTerminal?.CloseCommand.Execute(null); } private int GetNextTerminalId() { return _nextTerminalId++; } private void NewWindow() { NewWindowRequested?.Invoke(this, EventArgs.Empty); } private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e) { await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => _applicationSettings = e); } private async void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e) { var deferral = e.GetDeferral(); if (_applicationSettings.ConfirmClosingWindows) { var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to close this window?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(false); if (result == DialogButton.OK) { CloseAllTerminals(); Closed?.Invoke(this, EventArgs.Empty); } else { e.Handled = true; } } else { CloseAllTerminals(); Closed?.Invoke(this, EventArgs.Empty); } deferral.Complete(); } private async void OnCurrentThemeChanged(object sender, Guid e) { await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { var currentTheme = _settingsService.GetTheme(e); Background = currentTheme.Colors.Background; }); } private void OnTerminalCloseRequested(object sender, EventArgs e) { if (sender is TerminalViewModel terminal) { terminal.CloseView(); if (SelectedTerminal == terminal) { SelectedTerminal = Terminals.LastOrDefault(t => t != terminal); } Terminals.Remove(terminal); if (Terminals.Count == 0) { Closed?.Invoke(this, EventArgs.Empty); ApplicationView.GetForCurrentView().TryConsolidateAsync(); } } } private void OnTerminalsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (Terminals.Any()) { Title = Terminals.First().Title; } } private void OnTitleChanged(object sender, string e) { if (sender is TerminalViewModel terminal && Terminals.First() == terminal) { Title = e; } } 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]; } private void ShowSettings() { ShowSettingsRequested?.Invoke(this, EventArgs.Empty); } private void ToggleFullScreen() { var applicationView = ApplicationView.GetForCurrentView(); if (applicationView.IsFullScreenMode) { applicationView.ExitFullScreenMode(); } else { applicationView.TryEnterFullScreenMode(); } } } }