diff --git a/FluentTerminal.App/App.xaml b/FluentTerminal.App/App.xaml index 061c2b0..98d40d2 100644 --- a/FluentTerminal.App/App.xaml +++ b/FluentTerminal.App/App.xaml @@ -1,12 +1,23 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converters="using:FluentTerminal.App.Converters"> + + diff --git a/FluentTerminal.App/App.xaml.cs b/FluentTerminal.App/App.xaml.cs index f826d0b..892d48e 100644 --- a/FluentTerminal.App/App.xaml.cs +++ b/FluentTerminal.App/App.xaml.cs @@ -3,8 +3,12 @@ using FluentTerminal.App.Services; using FluentTerminal.App.Services.Implementation; using FluentTerminal.App.ViewModels; using FluentTerminal.App.Views; +using FluentTerminal.Models; +using FluentTerminal.Models.Enums; using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Threading.Tasks; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; @@ -26,11 +30,16 @@ namespace FluentTerminal.App public static App Instance; private bool _alreadyLaunched; private IContainer _container; - private int _openTerminalWindows; private int? _settingsWindowId; + private readonly ISettingsService _settingsService; + private ApplicationSettings _applicationSettings; + + private List _mainViewModels; public App() { + _mainViewModels = new List(); + InitializeComponent(); Instance = this; @@ -44,13 +53,21 @@ namespace FluentTerminal.App builder.RegisterType().InstancePerDependency(); _container = builder.Build(); + + _settingsService = _container.Resolve(); + _settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged; + + _applicationSettings = _settingsService.GetApplicationSettings(); + } + + private void OnApplicationSettingsChanged(object sender, EventArgs e) + { + _applicationSettings = _settingsService.GetApplicationSettings(); } public async Task CreateNewTerminalWindow(string startupDirectory) { - var viewModel = _container.Resolve(); - viewModel.AddTerminal(startupDirectory); - var id = await CreateSecondaryView(typeof(MainPage), viewModel, true); + var id = await CreateSecondaryView(typeof(MainPage), true, startupDirectory); await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id); } @@ -63,15 +80,14 @@ namespace FluentTerminal.App { if (_settingsWindowId == null) { - var viewModel = _container.Resolve(); - _settingsWindowId = await CreateSecondaryView(typeof(SettingsPage), viewModel, true); + _settingsWindowId = await CreateSecondaryView(typeof(SettingsPage), true, null); } await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value); } - public void TerminalWindowClosed() + public void TerminalWindowClosed(MainViewModel viewModel) { - _openTerminalWindows--; + _mainViewModels.Remove(viewModel); } protected override async void OnActivated(IActivatedEventArgs args) @@ -95,7 +111,15 @@ namespace FluentTerminal.App } else if (command == "new") { - await CreateNewTerminalWindow(parameter); + if (_applicationSettings.NewTerminalLocation == NewTerminalLocation.Tab && _mainViewModels.Any()) + { + await _mainViewModels.Last().AddTerminal(parameter); + } + else + { + await CreateNewTerminalWindow(parameter); + } + } } else @@ -107,9 +131,16 @@ namespace FluentTerminal.App } else if (command == "new") { - var viewModel = _container.Resolve(); - viewModel.AddTerminal(parameter); - await CreateMainView(typeof(MainPage), viewModel, true); + if (_applicationSettings.NewTerminalLocation == NewTerminalLocation.Tab && _mainViewModels.Any()) + { + await _mainViewModels.Last().AddTerminal(parameter); + } + else + { + var viewModel = _container.Resolve(); + await viewModel.AddTerminal(parameter); + await CreateMainView(typeof(MainPage), viewModel, true); + } } } } @@ -132,15 +163,13 @@ namespace FluentTerminal.App if (!_alreadyLaunched) { var viewModel = _container.Resolve(); - viewModel.AddTerminal(null); + await viewModel.AddTerminal(null); await CreateMainView(typeof(MainPage), viewModel, true); Window.Current.Activate(); } - else if (_openTerminalWindows == 0) + else if (_mainViewModels.Any() == false) { - var viewModel = _container.Resolve(); - viewModel.AddTerminal(null); - await CreateSecondaryView(typeof(MainPage), viewModel, true); + await CreateSecondaryView(typeof(MainPage), true, string.Empty); } } @@ -161,9 +190,9 @@ namespace FluentTerminal.App { CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar; - if (pageType == typeof(MainPage)) + if (viewModel is MainViewModel mainViewModel) { - _openTerminalWindows++; + _mainViewModels.Add(mainViewModel); } rootFrame.Navigate(pageType, viewModel); @@ -172,11 +201,14 @@ namespace FluentTerminal.App Window.Current.Activate(); } - private async Task CreateSecondaryView(Type pageType, INotifyPropertyChanged viewModel, bool ExtendViewIntoTitleBar) + private async Task CreateSecondaryView(Type pageType, bool ExtendViewIntoTitleBar, object parameter) { int windowId = 0; + TViewModel viewModel = default; await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { + viewModel = _container.Resolve(); + CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ExtendViewIntoTitleBar; var frame = new Frame(); frame.Navigate(pageType, viewModel); @@ -186,9 +218,10 @@ namespace FluentTerminal.App windowId = ApplicationView.GetForCurrentView().Id; }); - if (pageType == typeof(MainPage)) + if (viewModel is MainViewModel mainViewModel && parameter is string directory) { - _openTerminalWindows++; + _mainViewModels.Add(mainViewModel); + await mainViewModel.AddTerminal(directory); } return windowId; diff --git a/FluentTerminal.App/FluentTerminal.App.csproj b/FluentTerminal.App/FluentTerminal.App.csproj index ea5612f..a2c7239 100644 --- a/FluentTerminal.App/FluentTerminal.App.csproj +++ b/FluentTerminal.App/FluentTerminal.App.csproj @@ -79,6 +79,7 @@ false prompt true + 7.1 bin\x64\Release\ @@ -122,6 +123,7 @@ + diff --git a/FluentTerminal.App/Services/Implementation/DefaultValueProvider.cs b/FluentTerminal.App/Services/Implementation/DefaultValueProvider.cs index 4c23cf5..d9b1b57 100644 --- a/FluentTerminal.App/Services/Implementation/DefaultValueProvider.cs +++ b/FluentTerminal.App/Services/Implementation/DefaultValueProvider.cs @@ -13,7 +13,8 @@ namespace FluentTerminal.App.Services.Implementation return new ApplicationSettings { ConfirmClosingTabs = false, - ConfirmClosingWindows = false + ConfirmClosingWindows = false, + NewTerminalLocation = NewTerminalLocation.Tab }; } diff --git a/FluentTerminal.App/ViewModels/MainViewModel.cs b/FluentTerminal.App/ViewModels/MainViewModel.cs index 2165bda..a48bd4b 100644 --- a/FluentTerminal.App/ViewModels/MainViewModel.cs +++ b/FluentTerminal.App/ViewModels/MainViewModel.cs @@ -25,6 +25,7 @@ namespace FluentTerminal.App.ViewModels private CoreDispatcher _dispatcher; private int _nextTerminalId; private TerminalViewModel _selectedTerminal; + private string _title; public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService) { @@ -48,6 +49,10 @@ namespace FluentTerminal.App.ViewModels AddTerminalCommand = new RelayCommand(() => AddTerminal(null)); ShowSettingsCommand = new RelayCommand(async () => await ShowSettings()); + Terminals.CollectionChanged += OnTerminalsCollectionChanged; + + Title = "Fluent Terminal"; + _dispatcher = CoreApplication.GetCurrentView().Dispatcher; SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest; } @@ -82,13 +87,23 @@ namespace FluentTerminal.App.ViewModels public ObservableCollection Terminals { get; } = new ObservableCollection(); - public void AddTerminal(string startupDirectory) + public string Title { - var terminal = new TerminalViewModel(GetNextTerminalId(), _settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService, _applicationSettings, startupDirectory); - terminal.CloseRequested += OnTerminalCloseRequested; - Terminals.Add(terminal); + get => _title; + set => Set(ref _title, value); + } - SelectedTerminal = terminal; + public Task AddTerminal(string startupDirectory) + { + return _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + var terminal = new TerminalViewModel(GetNextTerminalId(), _settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService, _applicationSettings, startupDirectory); + terminal.CloseRequested += OnTerminalCloseRequested; + terminal.TitleChanged += OnTitleChanged; + Terminals.Add(terminal); + + SelectedTerminal = terminal; + }).AsTask(); } public void CloseAllTerminals() @@ -111,7 +126,7 @@ namespace FluentTerminal.App.ViewModels private Task NewWindow() { - return App.Instance.CreateNewTerminalWindow(null); + return App.Instance.CreateNewTerminalWindow(string.Empty); } private async void OnApplicationSettingsChanged(object sender, EventArgs e) @@ -133,7 +148,7 @@ namespace FluentTerminal.App.ViewModels if (result == DialogButton.OK) { CloseAllTerminals(); - App.Instance.TerminalWindowClosed(); + App.Instance.TerminalWindowClosed(this); } else { @@ -143,7 +158,7 @@ namespace FluentTerminal.App.ViewModels else { CloseAllTerminals(); - App.Instance.TerminalWindowClosed(); + App.Instance.TerminalWindowClosed(this); } deferral.Complete(); } @@ -176,6 +191,22 @@ namespace FluentTerminal.App.ViewModels } } + 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); diff --git a/FluentTerminal.App/ViewModels/Settings/GeneralPageViewModel.cs b/FluentTerminal.App/ViewModels/Settings/GeneralPageViewModel.cs new file mode 100644 index 0000000..17a13cd --- /dev/null +++ b/FluentTerminal.App/ViewModels/Settings/GeneralPageViewModel.cs @@ -0,0 +1,101 @@ +using FluentTerminal.App.Services; +using FluentTerminal.Models; +using FluentTerminal.Models.Enums; +using GalaSoft.MvvmLight; +using GalaSoft.MvvmLight.Command; +using System.Threading.Tasks; + +namespace FluentTerminal.App.ViewModels.Settings +{ + public class GeneralPageViewModel : ViewModelBase + { + private readonly IDefaultValueProvider _defaultValueProvider; + private readonly IDialogService _dialogService; + private readonly ISettingsService _settingsService; + private ApplicationSettings _applicationSettings; + private bool _editingNewTerminalLocation; + + public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider) + { + _settingsService = settingsService; + _dialogService = dialogService; + _defaultValueProvider = defaultValueProvider; + _applicationSettings = _settingsService.GetApplicationSettings(); + + RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults()); + } + + public bool ConfirmClosingTabs + { + get => _applicationSettings.ConfirmClosingTabs; + set + { + if (_applicationSettings.ConfirmClosingTabs != value) + { + _applicationSettings.ConfirmClosingTabs = value; + _settingsService.SaveApplicationSettings(_applicationSettings); + RaisePropertyChanged(); + } + } + } + + public bool ConfirmClosingWindows + { + get => _applicationSettings.ConfirmClosingWindows; + set + { + if (_applicationSettings.ConfirmClosingWindows != value) + { + _applicationSettings.ConfirmClosingWindows = value; + _settingsService.SaveApplicationSettings(_applicationSettings); + RaisePropertyChanged(); + } + } + } + + public NewTerminalLocation NewTerminalLocation + { + get => _applicationSettings.NewTerminalLocation; + set + { + if (_applicationSettings.NewTerminalLocation != value && !_editingNewTerminalLocation) + { + _editingNewTerminalLocation = true; + _applicationSettings.NewTerminalLocation = value; + _settingsService.SaveApplicationSettings(_applicationSettings); + RaisePropertyChanged(); + RaisePropertyChanged(nameof(WindowIsSelected)); + RaisePropertyChanged(nameof(TabIsSelected)); + _editingNewTerminalLocation = false; + } + } + } + + public RelayCommand RestoreDefaultsCommand { get; } + + public bool TabIsSelected + { + get => NewTerminalLocation == NewTerminalLocation.Tab; + set => NewTerminalLocation = NewTerminalLocation.Tab; + } + + public bool WindowIsSelected + { + get => NewTerminalLocation == NewTerminalLocation.Window; + set => NewTerminalLocation = NewTerminalLocation.Window; + } + + private async Task RestoreDefaults() + { + var result = await _dialogService.ShowDialogAsnyc("Please confirm", "Are you sure you want to restore the general settings?", DialogButton.OK, DialogButton.Cancel); + + if (result == DialogButton.OK) + { + var defaults = _defaultValueProvider.GetDefaultApplicationSettings(); + ConfirmClosingWindows = defaults.ConfirmClosingWindows; + ConfirmClosingTabs = defaults.ConfirmClosingTabs; + NewTerminalLocation = defaults.NewTerminalLocation; + } + } + } +} \ No newline at end of file diff --git a/FluentTerminal.App/ViewModels/SettingsViewModel.cs b/FluentTerminal.App/ViewModels/SettingsViewModel.cs index 976e585..73531d6 100644 --- a/FluentTerminal.App/ViewModels/SettingsViewModel.cs +++ b/FluentTerminal.App/ViewModels/SettingsViewModel.cs @@ -20,12 +20,12 @@ namespace FluentTerminal.App.ViewModels private readonly IDialogService _dialogService; private readonly ITrayProcessCommunicationService _trayProcessCommunicationService; private readonly ISettingsService _settingsService; - private ApplicationSettings _applicationSettings; private ThemeViewModel _selectedTheme; private ShellConfiguration _shellConfiguration; private TerminalOptions _terminalOptions; public KeyBindingsPageViewModel KeyBindings { get; } + public GeneralPageViewModel General { get; } public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService, ITrayProcessCommunicationService trayProcessCommunicationService) { @@ -40,10 +40,10 @@ namespace FluentTerminal.App.ViewModels _shellConfiguration = _settingsService.GetShellConfiguration(); _terminalOptions = _settingsService.GetTerminalOptions(); - _applicationSettings = _settingsService.GetApplicationSettings(); ShellType = _shellConfiguration.Shell; KeyBindings = new KeyBindingsPageViewModel(_settingsService, dialogService, _defaultValueProvider, _trayProcessCommunicationService); + General = new GeneralPageViewModel(_settingsService, dialogService, _defaultValueProvider); var activeThemeId = _settingsService.GetCurrentThemeId(); foreach (var theme in _settingsService.GetThemes()) @@ -102,34 +102,6 @@ namespace FluentTerminal.App.ViewModels set => ShellType = ShellType.CMD; } - public bool ConfirmClosingTabs - { - get => _applicationSettings.ConfirmClosingTabs; - set - { - if (_applicationSettings.ConfirmClosingTabs != value) - { - _applicationSettings.ConfirmClosingTabs = value; - _settingsService.SaveApplicationSettings(_applicationSettings); - RaisePropertyChanged(); - } - } - } - - public bool ConfirmClosingWindows - { - get => _applicationSettings.ConfirmClosingWindows; - set - { - if (_applicationSettings.ConfirmClosingWindows != value) - { - _applicationSettings.ConfirmClosingWindows = value; - _settingsService.SaveApplicationSettings(_applicationSettings); - RaisePropertyChanged(); - } - } - } - public RelayCommand CreateThemeCommand { get; set; } public bool CursorBlink @@ -367,12 +339,6 @@ namespace FluentTerminal.App.ViewModels FontFamily = defaults.FontFamily; FontSize = defaults.FontSize; } - else if (result == DialogButton.OK && area == "general") - { - var defaults = _defaultValueProvider.GetDefaultApplicationSettings(); - ConfirmClosingWindows = defaults.ConfirmClosingWindows; - ConfirmClosingTabs = defaults.ConfirmClosingTabs; - } } } } \ No newline at end of file diff --git a/FluentTerminal.App/Views/MainPage.xaml b/FluentTerminal.App/Views/MainPage.xaml index d327754..4b224b2 100644 --- a/FluentTerminal.App/Views/MainPage.xaml +++ b/FluentTerminal.App/Views/MainPage.xaml @@ -13,16 +13,6 @@ mc:Ignorable="d"> - - @@ -51,7 +41,7 @@ Grid.Column="0" IsHitTestVisible="False" Style="{StaticResource WindowTitleTextBlockStyle}" - Text="{x:Bind ViewModel.Terminals[0].Title, Mode=OneWay}" + Text="{x:Bind ViewModel.Title, Mode=OneWay}" Visibility="{x:Bind ViewModel.Terminals.Count, Mode=OneWay, Converter={StaticResource OneToVisibleConverter}}" /> @@ -30,14 +29,18 @@ Background="{ThemeResource SystemControlBackgroundAltHighBrush}" DefaultLabelPosition="Right"> - + + +