Compare commits

...
8 Commits
33 changed files with 382 additions and 143 deletions

No files matched your search

@@ -592,14 +592,14 @@ namespace FluentTerminal.App.Services.Test
public void GetDefaultShellProfileId_ValueExistsInRoamingSettings_ReturnsDefaultShellProfileId()
{
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var localSettings = new Mock<IApplicationDataContainer>();
var defaultShellProfileId = (object)_fixture.Create<Guid>();
roamingSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId)).Returns(true);
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId)).Returns(true);
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
LocalSettings = localSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
@@ -608,7 +608,7 @@ namespace FluentTerminal.App.Services.Test
var result = settingsService.GetDefaultShellProfileId();
result.Should().Be((Guid)defaultShellProfileId);
roamingSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId), Times.Once);
localSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId), Times.Once);
}
[Fact]
@@ -617,14 +617,14 @@ namespace FluentTerminal.App.Services.Test
var defaultValueProvider = new Mock<IDefaultValueProvider>();
var defaultShellProfileId = _fixture.Create<Guid>();
defaultValueProvider.Setup(x => x.GetDefaultShellProfileId()).Returns(defaultShellProfileId);
var roamingSettings = new Mock<IApplicationDataContainer>();
var localSettings = new Mock<IApplicationDataContainer>();
var currentDefaultShellProfileId = (object)_fixture.Create<Guid>();
roamingSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId)).Returns(false);
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId)).Returns(false);
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
LocalSettings = localSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
@@ -634,7 +634,7 @@ namespace FluentTerminal.App.Services.Test
result.Should().Be(defaultShellProfileId);
defaultValueProvider.Verify(x => x.GetDefaultShellProfileId(), Times.Once);
roamingSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId), Times.Once);
localSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId), Times.Once);
}
[Fact]
@@ -710,24 +710,24 @@ namespace FluentTerminal.App.Services.Test
var defaultValueProvider = new Mock<IDefaultValueProvider>();
defaultValueProvider.Setup(x => x.GetDefaultShellProfileId()).Returns(defaultShellProfile.Id);
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var localSettings = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
ShellProfiles = shellProfilesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = roamingSettings.Object,
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = localSettings.Object,
Themes = Mock.Of<IApplicationDataContainer>()
};
shellProfilesContainer.Setup(x => x.ReadValueFromJson(currentShellProfileId.ToString(), default(ShellProfile))).Returns(value: null);
shellProfilesContainer.Setup(x => x.ReadValueFromJson(defaultShellProfile.Id.ToString(), default(ShellProfile))).Returns(defaultShellProfile);
roamingSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentShellProfileId)).Returns(true);
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentShellProfileId)).Returns(true);
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
var result = settingsService.GetDefaultShellProfile();
result.Should().Be(defaultShellProfile);
roamingSettings.Verify(x => x.SetValue(SettingsService.DefaultShellProfileKey, defaultShellProfile.Id), Times.Once);
localSettings.Verify(x => x.SetValue(SettingsService.DefaultShellProfileKey, defaultShellProfile.Id), Times.Once);
}
[Fact]
@@ -43,30 +43,13 @@ namespace FluentTerminal.App.Services.Test
var terminalSize = _fixture.Create<TerminalSize>();
var shellProfile = _fixture.Create<ShellProfile>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
await trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
var result = await trayProcessCommunicationService.CreateTerminal(terminalSize, shellProfile);
result.Should().BeEquivalentTo(response);
}
[Fact]
public async Task Initialize_Default_SendsSetToggleWindowKeyBindingsRequest()
{
var settingsService = new Mock<ISettingsService>();
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
{
[Command.ToggleWindow] = keyBindings.ToList()
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
await trayProcessCommunicationService.Initialize(appServiceConnection.Object);
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(SetToggleWindowKeyBindingsRequest))), Times.Once);
}
[Fact]
public async Task ResizeTerminal_Default_SendsResizeTerminalRequest()
{
@@ -80,7 +63,7 @@ namespace FluentTerminal.App.Services.Test
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
await trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
await trayProcessCommunicationService.ResizeTerminal(terminalId, terminalSize);
@@ -100,7 +83,7 @@ namespace FluentTerminal.App.Services.Test
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
await trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
await trayProcessCommunicationService.WriteText(terminalId, text);
@@ -119,7 +102,7 @@ namespace FluentTerminal.App.Services.Test
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
await trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
await trayProcessCommunicationService.CloseTerminal(terminalId);
@@ -127,7 +110,7 @@ namespace FluentTerminal.App.Services.Test
}
[Fact]
public async Task OnMessageReceived_TerminalExitedRequest_InvokesTerminalExitedEvent()
public void OnMessageReceived_TerminalExitedRequest_InvokesTerminalExitedEvent()
{
var terminalId = _fixture.Create<int>();
var receivedTerminalId = 0;
@@ -149,7 +132,7 @@ namespace FluentTerminal.App.Services.Test
});
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
var appServiceConnection = new Mock<IAppServiceConnection>();
await trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.TerminalExited += (s, e) =>
{
terminalExitedEventCalled = true;
@@ -163,7 +146,7 @@ namespace FluentTerminal.App.Services.Test
}
[Fact]
public async Task OnMessageReceived_DisplayTerminalOutputRequest_InvokesCorrectOutputHandler()
public void OnMessageReceived_DisplayTerminalOutputRequest_InvokesCorrectOutputHandler()
{
var terminalId = _fixture.Create<int>();
var output = _fixture.Create<string>();
@@ -186,7 +169,7 @@ namespace FluentTerminal.App.Services.Test
});
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
var appServiceConnection = new Mock<IAppServiceConnection>();
await trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.SubscribeForTerminalOutput(terminalId, o =>
{
receivedOutput = o;
@@ -1,11 +1,10 @@
using FluentTerminal.App.Services;
using Newtonsoft.Json;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Storage;
namespace FluentTerminal.App.Adapters
namespace FluentTerminal.App.Services.Adapters
{
public class ApplicationDataContainerAdapter : IApplicationDataContainer
{
@@ -55,4 +54,4 @@ namespace FluentTerminal.App.Adapters
_applicationDataContainer.Values[name] = JsonConvert.SerializeObject(value);
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace FluentTerminal.App.Services
{
public static class Constants
{
public const string ThemesContainerName = "Themes";
public const string KeyBindingsContainerName = "KeyBindings";
public const string ShellProfilesContainerName = "ShellProfiles";
}
}
@@ -1,14 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<LangVersion>7.1</LangVersion>
</PropertyGroup>
@@ -21,4 +14,15 @@
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Windows.Foundation.FoundationContract">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.16299.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Windows.Foundation.UniversalApiContract">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.16299.0\Windows.Foundation.UniversalApiContract\5.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
</Project>
@@ -0,0 +1,12 @@
using FluentTerminal.Models.Enums;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface IStartupTaskService
{
Task DisableStartupTask();
Task<StartupTaskStatus> EnableStartupTask();
Task<StartupTaskStatus> GetStatus();
}
}
@@ -9,7 +9,7 @@ namespace FluentTerminal.App.Services
{
event EventHandler<int> TerminalExited;
Task Initialize(IAppServiceConnection appServiceConnection);
void Initialize(IAppServiceConnection appServiceConnection);
Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellProfile shellProfile);
@@ -18,7 +18,8 @@ namespace FluentTerminal.App.Services.Implementation
TabsPosition = TabsPosition.Top,
CopyOnSelect = false,
MouseMiddleClickAction = MouseAction.None,
MouseRightClickAction = MouseAction.ContextMenu
MouseRightClickAction = MouseAction.ContextMenu,
AlwaysShowTabs = false
};
}
@@ -154,7 +154,7 @@ namespace FluentTerminal.App.Services.Implementation
public Guid GetDefaultShellProfileId()
{
if (_roamingSettings.TryGetValue(DefaultShellProfileKey, out object value))
if (_localSettings.TryGetValue(DefaultShellProfileKey, out object value))
{
return (Guid)value;
}
@@ -176,7 +176,7 @@ namespace FluentTerminal.App.Services.Implementation
public void SaveDefaultShellProfileId(Guid id)
{
_roamingSettings.SetValue(DefaultShellProfileKey, id);
_localSettings.SetValue(DefaultShellProfileKey, id);
}
public IEnumerable<ShellProfile> GetShellProfiles()
@@ -37,11 +37,10 @@ namespace FluentTerminal.App.Services.Implementation
return JsonConvert.DeserializeObject<CreateTerminalResponse>(responseMessage[MessageKeys.Content]);
}
public Task Initialize(IAppServiceConnection appServiceConnection)
public void Initialize(IAppServiceConnection appServiceConnection)
{
_appServiceConnection = appServiceConnection;
_appServiceConnection.MessageReceived += OnMessageReceived;
return UpdateToggleWindowKeyBindings();
}
private void OnMessageReceived(object sender, IDictionary<string, string> e)
@@ -6,6 +6,7 @@ using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
@@ -58,6 +59,13 @@ namespace FluentTerminal.App.ViewModels
ShowSettingsCommand = new RelayCommand(ShowSettings);
_applicationView.CloseRequested += OnCloseRequest;
Terminals.CollectionChanged += OnTerminalsCollectionChanged;
}
private void OnTerminalsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged(nameof(ShowTabsOnTop));
RaisePropertyChanged(nameof(ShowTabsOnBottom));
}
public event EventHandler Closed;
@@ -68,6 +76,16 @@ namespace FluentTerminal.App.ViewModels
public RelayCommand AddTerminalCommand { get; }
public bool ShowTabsOnTop
{
get => TabsPosition == TabsPosition.Top && (Terminals.Count > 1 || _applicationSettings.AlwaysShowTabs);
}
public bool ShowTabsOnBottom
{
get => TabsPosition == TabsPosition.Bottom && (Terminals.Count > 1 || _applicationSettings.AlwaysShowTabs);
}
public string Background
{
get => _background;
@@ -162,6 +180,8 @@ namespace FluentTerminal.App.ViewModels
{
_applicationSettings = e;
TabsPosition = e.TabsPosition;
RaisePropertyChanged(nameof(ShowTabsOnTop));
RaisePropertyChanged(nameof(ShowTabsOnBottom));
});
}
@@ -9,23 +9,61 @@ namespace FluentTerminal.App.ViewModels.Settings
{
public class GeneralPageViewModel : ViewModelBase
{
private readonly ApplicationSettings _applicationSettings;
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private readonly ApplicationSettings _applicationSettings;
private readonly IStartupTaskService _startupTaskService;
private bool _canEnableStartupTask;
private bool _editingNewTerminalLocation;
private bool _editingTabsPosition;
private bool _startupTaskEnabled;
private string _startupTaskErrorMessage;
public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IStartupTaskService startupTaskService)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
_startupTaskService = startupTaskService;
_applicationSettings = _settingsService.GetApplicationSettings();
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
}
public async Task OnNavigatedTo()
{
var startupTaskStatus = await _startupTaskService.GetStatus();
SetStartupTaskPropertiesForStatus(startupTaskStatus);
}
public bool AlwaysShowTabs
{
get => _applicationSettings.AlwaysShowTabs;
set
{
if (_applicationSettings.AlwaysShowTabs != value)
{
_applicationSettings.AlwaysShowTabs = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public bool BottomIsSelected
{
get => TabsPosition == TabsPosition.Bottom;
set => TabsPosition = TabsPosition.Bottom;
}
public bool CanEnableStartupTask
{
get => _canEnableStartupTask;
set => Set(ref _canEnableStartupTask, value);
}
public bool ConfirmClosingTabs
{
get => _applicationSettings.ConfirmClosingTabs;
@@ -54,20 +92,6 @@ namespace FluentTerminal.App.ViewModels.Settings
}
}
public bool UnderlineSelectedTab
{
get => _applicationSettings.UnderlineSelectedTab;
set
{
if (_applicationSettings.UnderlineSelectedTab != value)
{
_applicationSettings.UnderlineSelectedTab = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public NewTerminalLocation NewTerminalLocation
{
get => _applicationSettings.NewTerminalLocation;
@@ -88,18 +112,32 @@ namespace FluentTerminal.App.ViewModels.Settings
public RelayCommand RestoreDefaultsCommand { get; }
public bool StartupTaskEnabled
{
get => _startupTaskEnabled;
set
{
if (_startupTaskEnabled != value)
{
_startupTaskEnabled = value;
RaisePropertyChanged(nameof(StartupTaskEnabled));
SetStartupTaskState(value);
}
}
}
public string StartupTaskErrorMessage
{
get => _startupTaskErrorMessage;
set => Set(ref _startupTaskErrorMessage, value);
}
public bool TabIsSelected
{
get => NewTerminalLocation == NewTerminalLocation.Tab;
set => NewTerminalLocation = NewTerminalLocation.Tab;
}
public bool WindowIsSelected
{
get => NewTerminalLocation == NewTerminalLocation.Window;
set => NewTerminalLocation = NewTerminalLocation.Window;
}
public TabsPosition TabsPosition
{
get => _applicationSettings.TabsPosition;
@@ -124,10 +162,24 @@ namespace FluentTerminal.App.ViewModels.Settings
set => TabsPosition = TabsPosition.Top;
}
public bool BottomIsSelected
public bool UnderlineSelectedTab
{
get => TabsPosition == TabsPosition.Bottom;
set => TabsPosition = TabsPosition.Bottom;
get => _applicationSettings.UnderlineSelectedTab;
set
{
if (_applicationSettings.UnderlineSelectedTab != value)
{
_applicationSettings.UnderlineSelectedTab = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public bool WindowIsSelected
{
get => NewTerminalLocation == NewTerminalLocation.Window;
set => NewTerminalLocation = NewTerminalLocation.Window;
}
private async Task RestoreDefaults()
@@ -141,7 +193,53 @@ namespace FluentTerminal.App.ViewModels.Settings
ConfirmClosingTabs = defaults.ConfirmClosingTabs;
UnderlineSelectedTab = defaults.UnderlineSelectedTab;
NewTerminalLocation = defaults.NewTerminalLocation;
AlwaysShowTabs = defaults.AlwaysShowTabs;
}
}
private void SetStartupTaskPropertiesForStatus(StartupTaskStatus startupTaskStatus)
{
switch (startupTaskStatus)
{
case StartupTaskStatus.Enabled:
StartupTaskEnabled = true;
StartupTaskErrorMessage = string.Empty;
CanEnableStartupTask = true;
break;
case StartupTaskStatus.Disabled:
StartupTaskEnabled = false;
StartupTaskErrorMessage = string.Empty;
CanEnableStartupTask = true;
break;
case StartupTaskStatus.DisabledByUser:
StartupTaskEnabled = false;
StartupTaskErrorMessage = "Disabled by user. Please reactivate it in the Startup tab of the Task Manager.";
CanEnableStartupTask = false;
break;
case StartupTaskStatus.DisabledByPolicy:
StartupTaskEnabled = false;
StartupTaskErrorMessage = "Disabled by policy.";
CanEnableStartupTask = false;
break;
}
}
private async Task SetStartupTaskState(bool enabled)
{
StartupTaskStatus status;
if (enabled)
{
status = await _startupTaskService.EnableStartupTask();
}
else
{
_startupTaskService.DisableStartupTask();
status = await _startupTaskService.GetStatus();
}
SetStartupTaskPropertiesForStatus(status);
}
}
}
@@ -8,10 +8,10 @@ namespace FluentTerminal.App.ViewModels
public class SettingsViewModel : ViewModelBase
{
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService,
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService, IFileSystemService fileSystemService)
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService, IFileSystemService fileSystemService, IStartupTaskService startupTaskService)
{
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService);
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider);
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService);
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService);
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService);
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService);
@@ -216,6 +216,11 @@ namespace FluentTerminal.App.ViewModels
await FocusTerminal().ConfigureAwait(true);
}
public void CopyText(string text)
{
_clipboardService.SetText(text);
}
private void CloseSearchPanel()
{
SearchText = string.Empty;
+7 -9
View File
@@ -2,6 +2,7 @@
using FluentTerminal.App.Adapters;
using FluentTerminal.App.Dialogs;
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Adapters;
using FluentTerminal.App.Services.Dialogs;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.App.ViewModels;
@@ -29,10 +30,6 @@ namespace FluentTerminal.App
{
public sealed partial class App : Application
{
public const string ThemesContainerName = "Themes";
public const string KeyBindingsContainerName = "KeyBindings";
public const string ShellProfilesContainerName = "ShellProfiles";
public TaskCompletionSource<int> _trayReady = new TaskCompletionSource<int>();
private readonly ISettingsService _settingsService;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
@@ -55,9 +52,9 @@ namespace FluentTerminal.App
{
LocalSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings),
RoamingSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings),
KeyBindings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(KeyBindingsContainerName, ApplicationDataCreateDisposition.Always)),
ShellProfiles = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings.CreateContainer(ShellProfilesContainerName, ApplicationDataCreateDisposition.Always)),
Themes = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(ThemesContainerName, ApplicationDataCreateDisposition.Always))
KeyBindings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.KeyBindingsContainerName, ApplicationDataCreateDisposition.Always)),
ShellProfiles = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings.CreateContainer(Constants.ShellProfilesContainerName, ApplicationDataCreateDisposition.Always)),
Themes = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.ThemesContainerName, ApplicationDataCreateDisposition.Always))
};
var builder = new ContainerBuilder();
@@ -79,6 +76,7 @@ namespace FluentTerminal.App
builder.RegisterType<MessageDialogAdapter>().As<IMessageDialog>().InstancePerDependency();
builder.RegisterType<ApplicationViewAdapter>().As<IApplicationView>().InstancePerDependency();
builder.RegisterType<DispatcherTimerAdapter>().As<IDispatcherTimer>().InstancePerDependency();
builder.RegisterType<StartupTaskService>().As<IStartupTaskService>().SingleInstance();
builder.RegisterInstance(applicationDataContainers);
_container = builder.Build();
@@ -296,10 +294,10 @@ namespace FluentTerminal.App
private async Task StartSystemTray()
{
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("AppLaunchedParameterGroup").AsTask();
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
await Task.WhenAll(launch, clearCache, _trayReady.Task).ConfigureAwait(true);
await _trayProcessCommunicationService.Initialize(_appServiceConnection).ConfigureAwait(true);
_trayProcessCommunicationService.Initialize(_appServiceConnection);
}
}
}
+3 -1
View File
@@ -104,7 +104,6 @@
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="Adapters\ApplicationDataContainerAdapter.cs" />
<Compile Include="Adapters\ApplicationViewAdapter.cs" />
<Compile Include="Adapters\AppServiceConnectionAdapter.cs" />
<Compile Include="Adapters\DispatcherTimerAdapter.cs" />
@@ -127,6 +126,7 @@
</Compile>
<Compile Include="Services\ClipboardService.cs" />
<Compile Include="Services\FileSystemService.cs" />
<Compile Include="Services\StartupTaskService.cs" />
<Compile Include="Services\SystemFontService.cs" />
<Compile Include="Utilities\ColorExtensions.cs" />
<Compile Include="Utilities\ContrastHelper.cs" />
@@ -177,6 +177,8 @@
</AppxManifest>
<Content Include="Assets\settingsicons.ttf" />
<Content Include="Win32\Autofac.dll" />
<Content Include="Win32\FluentTerminal.App.Services.dll" />
<Content Include="Win32\FluentTerminal.App.Services.pdb" />
<Content Include="Win32\FluentTerminal.Models.dll" />
<Content Include="Win32\FluentTerminal.Models.pdb" />
<Content Include="Win32\FluentTerminal.SystemTray.exe" />
+9 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.14.0" />
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.2.0.0" />
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>Fluent Terminal</DisplayName>
@@ -26,12 +26,19 @@
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" uap5:Optional="true" />
</uap:VisualElements>
<Extensions>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe" />
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe">
<desktop:FullTrustProcess>
<desktop:ParameterGroup GroupId="AppLaunchedParameterGroup" Parameters="appLaunched" />
</desktop:FullTrustProcess>
</desktop:Extension>
<uap5:Extension Category="windows.appExecutionAlias" Executable="FluentTerminal.App.exe" EntryPoint="FluentTerminal.App.App">
<uap5:AppExecutionAlias>
<uap5:ExecutionAlias Alias="flute.exe" />
</uap5:AppExecutionAlias>
</uap5:Extension>
<desktop:Extension Category="windows.startupTask" Executable="Win32\FluentTerminal.SystemTray.exe" EntryPoint="Windows.FullTrustApplication">
<desktop:StartupTask TaskId="FluentTerminalStartupTask" Enabled="false" DisplayName="Fluent Terminal" />
</desktop:Extension>
<uap:Extension Category="windows.appService">
<uap:AppService Name="FluentTerminalAppService" />
</uap:Extension>
@@ -0,0 +1,47 @@
using FluentTerminal.Models.Enums;
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel;
namespace FluentTerminal.App.Services
{
public class StartupTaskService : IStartupTaskService
{
public const string StartupTaskname = "FluentTerminalStartupTask";
public async Task<StartupTaskStatus> GetStatus()
{
var startupTask = await StartupTask.GetAsync(StartupTaskname);
return ConvertState(startupTask.State);
}
public async Task<StartupTaskStatus> EnableStartupTask()
{
var startupTask = await StartupTask.GetAsync(StartupTaskname);
var newState = await startupTask.RequestEnableAsync();
return ConvertState(newState);
}
public async Task DisableStartupTask()
{
var startupTask = await StartupTask.GetAsync(StartupTaskname);
startupTask.Disable();
}
private StartupTaskStatus ConvertState(StartupTaskState state)
{
switch (state)
{
case StartupTaskState.Disabled:
return StartupTaskStatus.Disabled;
case StartupTaskState.DisabledByUser:
return StartupTaskStatus.DisabledByUser;
case StartupTaskState.Enabled:
return StartupTaskStatus.Enabled;
case StartupTaskState.DisabledByPolicy:
return StartupTaskStatus.DisabledByPolicy;
}
throw new NotImplementedException();
}
}
}
+19 -23
View File
@@ -44,7 +44,18 @@
</Button.Content>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem Command="{x:Bind ViewModel.ShowSettingsCommand}" Text="Settings">
<MenuFlyoutItem
Width="160"
Command="{x:Bind ViewModel.AddTerminalCommand}"
Text="New tab">
<MenuFlyoutItem.Icon>
<SymbolIcon Symbol="Add" />
</MenuFlyoutItem.Icon>
</MenuFlyoutItem>
<MenuFlyoutItem
Width="160"
Command="{x:Bind ViewModel.ShowSettingsCommand}"
Text="Settings">
<MenuFlyoutItem.Icon>
<SymbolIcon Symbol="Setting" />
</MenuFlyoutItem.Icon>
@@ -52,34 +63,19 @@
</MenuFlyout>
</Button.Flyout>
</Button>
<StackPanel
<TextBlock
Grid.Column="1"
Orientation="Horizontal"
Visibility="{x:Bind ViewModel.Terminals.Count, Mode=OneWay, Converter={StaticResource OneToVisibleConverter}}">
<TextBlock
IsHitTestVisible="False"
Style="{StaticResource WindowTitleTextBlockStyle}"
Text="{x:Bind ViewModel.SelectedTerminal.Title, Mode=OneWay}" />
<Button
x:Name="Add"
Command="{x:Bind ViewModel.AddTerminalCommand}"
Style="{StaticResource TitleBarButtonStyle}"
ToolTipService.ToolTip="New tab">
<Button.Content>
<Viewbox Width="12" Height="12">
<SymbolIcon Symbol="Add" />
</Viewbox>
</Button.Content>
</Button>
</StackPanel>
IsHitTestVisible="False"
Style="{StaticResource WindowTitleTextBlockStyle}"
Text="{x:Bind ViewModel.SelectedTerminal.Title, Mode=OneWay}"
Visibility="{x:Bind ViewModel.ShowTabsOnTop, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}" />
<views:TabBar
Grid.Column="1"
HorizontalAlignment="Left"
AddCommand="{x:Bind ViewModel.AddTerminalCommand}"
ItemsSource="{x:Bind ViewModel.Terminals, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedTerminal, Mode=TwoWay}"
Visibility="{x:Bind ViewModel.TabsPosition, Mode=OneWay, Converter={StaticResource EnumValueToVisibiltyConverter}, ConverterParameter=Top}" />
Visibility="{x:Bind ViewModel.ShowTabsOnTop, Mode=OneWay}" />
</Grid>
<ContentControl
x:Name="TerminalContainer"
@@ -93,6 +89,6 @@
AddCommand="{x:Bind ViewModel.AddTerminalCommand}"
ItemsSource="{x:Bind ViewModel.Terminals, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedTerminal, Mode=TwoWay}"
Visibility="{x:Bind ViewModel.TabsPosition, Mode=OneWay, Converter={StaticResource EnumValueToVisibiltyConverter}, ConverterParameter=Bottom}" />
Visibility="{x:Bind ViewModel.ShowTabsOnBottom, Mode=OneWay}" />
</Grid>
</Page>
@@ -35,7 +35,15 @@
</CommandBar>
</Grid>
<StackPanel Grid.Row="2" Margin="24,0,0,0">
<TextBlock Margin="0,0,0,8" Text="Open new terminals as" />
<ToggleSwitch
Header="Run on startup"
IsEnabled="{x:Bind ViewModel.CanEnableStartupTask, Mode=OneWay}"
IsOn="{x:Bind ViewModel.StartupTaskEnabled, Mode=TwoWay}" />
<TextBlock
Foreground="Red"
Text="{x:Bind ViewModel.StartupTaskErrorMessage, Mode=OneWay}"
Visibility="{x:Bind ViewModel.CanEnableStartupTask, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}" />
<TextBlock Margin="0,24,0,8" Text="Open new terminals as" />
<RadioButton
Content="Tabs"
GroupName="NewTerminalLocation"
@@ -55,6 +63,10 @@
Content="Bottom"
GroupName="TabsPosition"
IsChecked="{x:Bind ViewModel.BottomIsSelected, Mode=TwoWay}" />
<ToggleSwitch
Margin="0,0,0,24"
Header="Always show tabs"
IsOn="{x:Bind ViewModel.AlwaysShowTabs, Mode=TwoWay}" />
<ToggleSwitch
Margin="0,0,0,24"
Header="Underline selected tab"
@@ -18,6 +18,7 @@ namespace FluentTerminal.App.Views.SettingsPages
if (e.Parameter is GeneralPageViewModel viewModel)
{
ViewModel = viewModel;
ViewModel.OnNavigatedTo();
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@
d:DesignWidth="200"
mc:Ignorable="d">
<Grid x:Name="Tabs" Visibility="{x:Bind ItemsSource.Count, Mode=OneWay, Converter={StaticResource OneToCollapsedConverter}}">
<Grid x:Name="Tabs">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
@@ -281,11 +281,15 @@ namespace FluentTerminal.App.Views
});
}
public void OnSelectionChanged(string selection)
public async void OnSelectionChanged(string selection)
{
if (ViewModel.ApplicationSettings.CopyOnSelect)
if (ViewModel.ApplicationSettings.CopyOnSelect && !ViewModel.ShowSearchPanel)
{
_dispatcherJobs.Add(() => OnKeyboardCommand(nameof(Command.Copy)));
_dispatcherJobs.Add(async () =>
{
ViewModel.CopyText(selection);
await ExecuteScriptAsync("term.clearSelection()");
});
}
}
@@ -12,5 +12,6 @@ namespace FluentTerminal.Models
public bool CopyOnSelect { get; set; }
public MouseAction MouseMiddleClickAction { get; set; }
public MouseAction MouseRightClickAction { get; set; }
public bool AlwaysShowTabs { get; set; }
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FluentTerminal.Models.Enums
{
public enum StartupTaskStatus
{
Enabled,
Disabled,
DisabledByUser,
DisabledByPolicy
}
}
@@ -175,6 +175,10 @@
<Content Include="BinaryDependencies\x86\winpty.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj">
<Project>{A1AD7385-F92E-46AC-A349-43DC99B1E72D}</Project>
<Name>FluentTerminal.App.Services</Name>
</ProjectReference>
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
<Name>FluentTerminal.Models</Name>
+23 -3
View File
@@ -1,4 +1,7 @@
using Autofac;
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Adapters;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.SystemTray.Services;
using GlobalHotKey;
using System;
@@ -6,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;
using Windows.Storage;
namespace FluentTerminal.SystemTray
{
@@ -14,7 +18,7 @@ namespace FluentTerminal.SystemTray
private const string MutexName = "FluentTerminalMutex";
[STAThread]
public static void Main()
public static void Main(string[] args)
{
if (!Mutex.TryOpenExisting(MutexName, out Mutex mutex))
{
@@ -23,8 +27,18 @@ namespace FluentTerminal.SystemTray
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var applicationDataContainers = new ApplicationDataContainers
{
LocalSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings),
RoamingSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings),
KeyBindings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.KeyBindingsContainerName, ApplicationDataCreateDisposition.Always)),
ShellProfiles = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings.CreateContainer(Constants.ShellProfilesContainerName, ApplicationDataCreateDisposition.Always)),
Themes = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.ThemesContainerName, ApplicationDataCreateDisposition.Always))
};
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterInstance(applicationDataContainers);
containerBuilder.RegisterType<UpdateService>().SingleInstance();
containerBuilder.RegisterType<NotificationService>().SingleInstance();
containerBuilder.RegisterType<TerminalsManager>().SingleInstance();
@@ -32,14 +46,20 @@ namespace FluentTerminal.SystemTray
containerBuilder.RegisterType<HotKeyManager>().SingleInstance();
containerBuilder.RegisterType<SystemTrayApplicationContext>().SingleInstance();
containerBuilder.RegisterType<AppCommunicationService>().SingleInstance();
containerBuilder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>();
containerBuilder.RegisterType<SettingsService>().As<ISettingsService>();
containerBuilder.RegisterInstance(Dispatcher.CurrentDispatcher).SingleInstance();
var container = containerBuilder.Build();
var appCommunicationService = container.Resolve<AppCommunicationService>();
appCommunicationService.StartAppServiceConnection();
Task.Run(() => CheckForNewVersion(container.Resolve<UpdateService>(), container.Resolve<NotificationService>()));
if (args.Length > 0 && args[2] == "appLaunched")
{
appCommunicationService.StartAppServiceConnection();
}
Task.Run(() => CheckForNewVersion(container.Resolve<UpdateService>(), container.Resolve<NotificationService>()));
Application.Run(container.Resolve<SystemTrayApplicationContext>());
mutex.Close();
@@ -1,4 +1,5 @@
using FluentTerminal.Models;
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GlobalHotKey;
using System;
@@ -15,21 +16,23 @@ namespace FluentTerminal.SystemTray.Services
{
public class ToggleWindowService : IDisposable
{
private const int SW_MINIMIZE = 6;
private readonly HotKeyManager _hotKeyManager;
private readonly NotificationService _notificationService;
private Dispatcher _dispatcher;
private readonly Dispatcher _dispatcher;
private bool _disposedValue;
private List<HotKey> _hotKeys;
private readonly List<HotKey> _hotKeys;
private const int SW_MINIMIZE = 6;
public ToggleWindowService(Dispatcher dispatcher, HotKeyManager hotKeyManager, NotificationService notificationService)
public ToggleWindowService(Dispatcher dispatcher, HotKeyManager hotKeyManager, NotificationService notificationService, ISettingsService settingsService)
{
_dispatcher = dispatcher;
_notificationService = notificationService;
_hotKeyManager = hotKeyManager;
_hotKeys = new List<HotKey>();
_hotKeyManager.KeyPressed += OnKeyPressed;
var keyBindings = settingsService.GetKeyBindings()[Command.ToggleWindow];
SetHotKeys(keyBindings);
}
public void Dispose()
@@ -104,9 +107,6 @@ namespace FluentTerminal.SystemTray.Services
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private static string GetKeyBindingRepresentation(KeyBinding keyBinding)
{
var keys = new List<string>();
@@ -126,10 +126,13 @@ namespace FluentTerminal.SystemTray.Services
keys.Add(((ExtendedVirtualKey)keyBinding.Key).ToString());
return string.Join(" + ", keys);
}
}
[DllImport("user32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private string GetActiveProcessFileName()
{
@@ -148,7 +151,7 @@ namespace FluentTerminal.SystemTray.Services
private async void OnKeyPressed(object sender, KeyPressedEventArgs e)
{
if (GetActiveProcessFileName() == "Fluent Terminal")
if (GetActiveProcessFileName().EndsWith("Fluent Terminal"))
{
var hwnd = GetForegroundWindow();
ShowWindow(hwnd, SW_MINIMIZE);
+2 -2
View File
@@ -19,8 +19,8 @@ A Terminal Emulator based on UWP and web technologies.
- Explorer context menu integration (Installation script can be found [here](https://github.com/felixse/FluentTerminal/tree/master/Explorer%20Context%20Menu%20Integration))
## Screenshots:
![Terminal window](Screenshots/Terminal.png)
![Settings window](Screenshots/Settings.png)
![Terminal window](Screenshots/terminal.jpg)
![Settings window](Screenshots/settings.jpg)
## Up Next:
- Launch shell profile with a custom defined keybinding
Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 843 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB