Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0733b1ab02 | ||
|
|
4e54668092 | ||
|
|
74abd4f448 | ||
|
|
b85be454ce | ||
|
|
1f3dd72214 | ||
|
|
c4fcd986c0 | ||
|
|
ea21d37089 | ||
|
|
2f133720cc | ||
|
|
250c90fa02 | ||
|
|
7e2c5d2f45 | ||
|
|
9fed3fb192 | ||
|
|
0d85f933c7 | ||
|
|
f235ddf0f1 | ||
|
|
887cd45b4c | ||
|
|
aac2f220d6 | ||
|
|
90f15cc1e3 | ||
|
|
292b66af4d | ||
|
|
b6d835984c | ||
|
|
369b5ddacf | ||
|
|
6be29f49d7 | ||
|
|
8131140cb0 | ||
|
|
304314230c | ||
|
|
0ce605f122 | ||
|
|
d4aadd7dd4 | ||
|
|
3cf3e0a330 | ||
|
|
ff2cfc36b6 | ||
|
|
b118c66959 | ||
|
|
131e3536f1 | ||
|
|
b47b544065 | ||
|
|
b13c40e7e8 | ||
|
|
4e47fcef72 | ||
|
|
8acf618fd4 | ||
|
|
49e09f3f8b | ||
|
|
9bf8284d9f | ||
|
|
0f8aac81de | ||
|
|
abec73b868 | ||
|
|
86486c99f2 | ||
|
|
d9b7052e87 | ||
|
|
0efe105321 | ||
|
|
ed0dad960d | ||
|
|
2f58ed63de | ||
|
|
cc7e069f11 | ||
|
|
94a0eac58f | ||
|
|
9af0556d6c | ||
|
|
b14ca2c4ed | ||
|
|
89c1a5a614 | ||
|
|
3e32f611e8 | ||
|
|
07e65ce2bf | ||
|
|
f2a48487f1 | ||
|
|
c9d0cc7368 | ||
|
|
eecc2e8243 | ||
|
|
5c1896cd3b | ||
|
|
e0cde7fe0f | ||
|
|
2d4d0bc23a | ||
|
|
9a5bff3d37 | ||
|
|
28f212b7d6 | ||
|
|
bfad59fa7a | ||
|
|
f0e8f01d30 | ||
|
|
3ffd94e99d | ||
|
|
1be49d27ab | ||
|
|
4f43f64783 | ||
|
|
e7997a5272 | ||
|
|
507138364e | ||
|
|
0a4a1baeba | ||
|
|
14d111c4de | ||
|
|
db73d7e1cb | ||
|
|
825e9883d7 | ||
|
|
b2866c34df | ||
|
|
58e3ab9115 | ||
|
|
2b66b3c87f | ||
|
|
4505504732 | ||
|
|
567da9b0a3 | ||
|
|
7ccc36b121 | ||
|
|
559bf9d394 | ||
|
|
cee34dfb63 | ||
|
|
55995bd7a9 | ||
|
|
36edfddf74 | ||
|
|
188046e60a | ||
|
|
5338f3cae8 | ||
|
|
624146ecd7 | ||
|
|
8f711d1a9e | ||
|
|
b50090060b | ||
|
|
7db2321a82 | ||
|
|
def3aa3683 | ||
|
|
77d9a42c36 | ||
|
|
d395085064 | ||
|
|
56e5d60dae | ||
|
|
780cbf64b1 | ||
|
|
6b25d78ce6 | ||
|
|
53726e0e68 | ||
|
|
5e11e5d58b | ||
|
|
3e0417ce47 | ||
|
|
417bc843b0 | ||
|
|
6d6bdcca3e | ||
|
|
b4bfe39586 | ||
|
|
940cb6f38b | ||
|
|
59c6a39458 | ||
|
|
05ce19ec8c | ||
|
|
7b2bd59365 | ||
|
|
6a67883777 | ||
|
|
847e1c045a | ||
|
|
e918c5492e | ||
|
|
1aba3d4926 | ||
|
|
5ad51b6bf7 | ||
|
|
72cfe1168f | ||
|
|
5a96a0387b | ||
|
|
00a5b6377d | ||
|
|
f610aa8a0f | ||
|
|
c32342bca8 | ||
|
|
84af36f6bc | ||
|
|
f56465f54a |
No files matched your search
@@ -2,6 +2,7 @@
|
||||
FluentTerminal.App/Win32
|
||||
FluentTerminal.App/Client/
|
||||
FluentTerminal.Client/dist
|
||||
/.sonarqube
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class DefaultValueProviderTests
|
||||
{
|
||||
public static IEnumerable<object[]> Commands => System.Enum.GetValues(typeof(Command)).Cast<Command>().Select(x => new object[] { x });
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultApplicationSettings_Default_DoesNotReturnNull()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var applicationSettings = defaultValueProvider.GetDefaultApplicationSettings();
|
||||
|
||||
applicationSettings.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Commands))]
|
||||
public void GetDefaultKeyBindings_AllCommands_ReturnsANonEmptyList(Command command)
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var keyBindings = defaultValueProvider.GetDefaultKeyBindings(command);
|
||||
|
||||
keyBindings.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultShellProfileId_Default_DoesNotReturnEmptyGuid()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var defaultShellProfileId = defaultValueProvider.GetDefaultShellProfileId();
|
||||
|
||||
defaultShellProfileId.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultShellProfileId_Default_IdIsOneOfThePreInstalledShellProfiles()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
|
||||
|
||||
var defaultShellProfileId = defaultValueProvider.GetDefaultShellProfileId();
|
||||
|
||||
shellProfiles.Select(x => x.Id).Should().Contain(defaultShellProfileId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultTerminalOptions_Default_DoesNotReturnNull()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var defaultTerminalOptions = defaultValueProvider.GetDefaultTerminalOptions();
|
||||
|
||||
defaultTerminalOptions.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultThemeId_Default_DoesNotReturnEmptyGuid()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var defaultThemeId = defaultValueProvider.GetDefaultThemeId();
|
||||
|
||||
defaultThemeId.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultThemeId_Default_IdIsOneOfThePreInstalledThemes()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
var themes = defaultValueProvider.GetPreInstalledThemes();
|
||||
|
||||
var defaultThemeId = defaultValueProvider.GetDefaultThemeId();
|
||||
|
||||
themes.Select(x => x.Id).Should().Contain(defaultThemeId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreinstalledShellProfiles_Default_ReturnsANonEmptyList()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
|
||||
|
||||
shellProfiles.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreinstalledShellProfiles_Default_HaveUniqueIds()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
|
||||
|
||||
shellProfiles.Select(x => x.Id).Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreinstalledShellProfiles_Default_HavePreInstalledSetToTrue()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
|
||||
|
||||
shellProfiles.Select(x => x.PreInstalled).Should().AllBeEquivalentTo(true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreinstalledShellProfiles_Default_HaveNonEmptyLocations()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
|
||||
|
||||
shellProfiles.Select(x => x.Location).Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreInstalledThemes_Default_ReturnsANonEmptyList()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var themes = defaultValueProvider.GetPreInstalledThemes();
|
||||
|
||||
themes.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreinstalledThemes_Default_HaveUniqueIds()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var themes = defaultValueProvider.GetPreInstalledThemes();
|
||||
|
||||
themes.Select(x => x.Id).Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreinstalledThemes_Default_HavePreInstalledSetToTrue()
|
||||
{
|
||||
var defaultValueProvider = new DefaultValueProvider();
|
||||
|
||||
var themes = defaultValueProvider.GetPreInstalledThemes();
|
||||
|
||||
themes.Select(x => x.PreInstalled).Should().AllBeEquivalentTo(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class DialogServiceTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public DialogServiceTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowCreateKeyBindingDialog_Default_UsesCreateKeyBindingDialog()
|
||||
{
|
||||
var createKeyBindingDialog = new Mock<ICreateKeyBindingDialog>();
|
||||
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, () => createKeyBindingDialog.Object, Mock.Of<IInputDialog>);
|
||||
|
||||
dialogService.ShowCreateKeyBindingDialog();
|
||||
|
||||
createKeyBindingDialog.Verify(x => x.CreateKeyBinding(), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowMessageDialogAsnyc_TitleIsEmpty_ThrowsArgumentNullException()
|
||||
{
|
||||
var title = string.Empty;
|
||||
var content = _fixture.Create<string>();
|
||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>);
|
||||
|
||||
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||
|
||||
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("title");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowMessageDialogAsnyc_ContentIsEmpty_ThrowsArgumentNullException()
|
||||
{
|
||||
var title = _fixture.Create<string>();
|
||||
var content = string.Empty;
|
||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>);
|
||||
|
||||
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||
|
||||
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("content");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowMessageDialogAsnyc_NoButtonsPassed_ThrowsArgumentException()
|
||||
{
|
||||
var title = _fixture.Create<string>();
|
||||
var content = _fixture.Create<string>();
|
||||
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>);
|
||||
|
||||
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content);
|
||||
|
||||
showMessageDialogAsnyc.Should().Throw<ArgumentException>().And.ParamName.Should().Be("buttons");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowMessageDialogAsnyc_Default_UsesMessageDialog()
|
||||
{
|
||||
var title = _fixture.Create<string>();
|
||||
var content = _fixture.Create<string>();
|
||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||
var messageDialog = new Mock<IMessageDialog>();
|
||||
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, () => messageDialog.Object, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>);
|
||||
|
||||
dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||
|
||||
messageDialog.Verify(x => x.ShowAsync(), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowProfileSelectionDialogAsync_Default_UsesShellProfileSelectionDialog()
|
||||
{
|
||||
var shellProfileSelectionDialog = new Mock<IShellProfileSelectionDialog>();
|
||||
var dialogService = new DialogService(() => shellProfileSelectionDialog.Object, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>);
|
||||
|
||||
dialogService.ShowProfileSelectionDialogAsync();
|
||||
|
||||
shellProfileSelectionDialog.Verify(x => x.SelectProfile(), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="TestData\AdventureTime.itermcolors" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.4.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="5.4.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="Moq" Version="4.8.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="TestData\AdventureTime.itermcolors">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,80 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class FluentTerminalThemeParserTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public FluentTerminalThemeParserTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_ValidFileStream_ReturnsTheme()
|
||||
{
|
||||
var theme = _fixture.Create<TerminalTheme>();
|
||||
var serialized = JsonConvert.SerializeObject(theme);
|
||||
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
|
||||
var parser = new FluentTerminalThemeParser();
|
||||
|
||||
var parsedTheme = await parser.Parse(theme.Name, stream);
|
||||
|
||||
parsedTheme.Name.Should().Be(theme.Name);
|
||||
parsedTheme.Author.Should().Be(theme.Author);
|
||||
parsedTheme.PreInstalled.Should().Be(false);
|
||||
parsedTheme.Colors.Should().BeEquivalentTo(theme.Colors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_InvalidFileStream_ThrowsException()
|
||||
{
|
||||
var fileName = _fixture.Create<string>();
|
||||
var serialized = _fixture.Create<string>();
|
||||
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
|
||||
var parser = new FluentTerminalThemeParser();
|
||||
|
||||
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
|
||||
|
||||
parse.Should().Throw<Exception>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void Parse_InvalidFileName_ThrowsArgumentNullException(string fileName)
|
||||
{
|
||||
var theme = _fixture.Create<TerminalTheme>();
|
||||
var serialized = JsonConvert.SerializeObject(theme);
|
||||
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
|
||||
var parser = new FluentTerminalThemeParser();
|
||||
|
||||
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
|
||||
|
||||
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileName");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_FileStreamIsNull_ThrowsArgumentNullException()
|
||||
{
|
||||
var fileName = _fixture.Create<string>();
|
||||
Stream stream = null;
|
||||
var parser = new FluentTerminalThemeParser();
|
||||
|
||||
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
|
||||
|
||||
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileContent");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class ITermThemeParserTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public ITermThemeParserTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void Parse_InvalidFileName_ThrowsArgumentNullException(string fileName)
|
||||
{
|
||||
var stream = typeof(ITermThemeParserTests).Assembly.GetManifestResourceStream("FluentTerminal.App.Services.Test.TestData.AdventureTime.itermcolors");
|
||||
var parser = new ITermThemeParser();
|
||||
|
||||
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
|
||||
|
||||
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileName");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_FileStreamIsNull_ThrowsArgumentNullException()
|
||||
{
|
||||
var fileName = _fixture.Create<string>();
|
||||
Stream stream = null;
|
||||
var parser = new ITermThemeParser();
|
||||
|
||||
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
|
||||
|
||||
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileContent");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parse_InvalidFileStream_ThrowsException()
|
||||
{
|
||||
var fileName = _fixture.Create<string>();
|
||||
var serialized = _fixture.Create<string>();
|
||||
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
|
||||
var parser = new ITermThemeParser();
|
||||
|
||||
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
|
||||
|
||||
parse.Should().Throw<Exception>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parse_ValidThemeFile_ParsesCorrectly()
|
||||
{
|
||||
var fileName = _fixture.Create<string>();
|
||||
var stream = typeof(ITermThemeParserTests).Assembly.GetManifestResourceStream("FluentTerminal.App.Services.Test.TestData.AdventureTime.itermcolors");
|
||||
var parser = new ITermThemeParser();
|
||||
|
||||
var theme = await parser.Parse(fileName, stream);
|
||||
|
||||
theme.Colors.Background.ToLower().Should().Be("#1f1d45");
|
||||
theme.Colors.Cursor.ToLower().Should().Be("#efbf38");
|
||||
theme.Colors.Foreground.ToLower().Should().Be("#f8dcc0");
|
||||
theme.Colors.Black.ToLower().Should().Be("#050404");
|
||||
theme.Colors.Red.ToLower().Should().Be("#bd0013");
|
||||
theme.Colors.Green.ToLower().Should().Be("#4ab118");
|
||||
theme.Colors.Yellow.ToLower().Should().Be("#e7741e");
|
||||
theme.Colors.Blue.ToLower().Should().Be("#0f4ac6");
|
||||
theme.Colors.Magenta.ToLower().Should().Be("#665993");
|
||||
theme.Colors.Cyan.ToLower().Should().Be("#70a598");
|
||||
theme.Colors.White.ToLower().Should().Be("#f8dcc0");
|
||||
theme.Colors.BrightBlack.ToLower().Should().Be("#4e7cbf");
|
||||
theme.Colors.BrightRed.ToLower().Should().Be("#fc5f5a");
|
||||
theme.Colors.BrightGreen.ToLower().Should().Be("#9eff6e");
|
||||
theme.Colors.BrightYellow.ToLower().Should().Be("#efc11a");
|
||||
theme.Colors.BrightBlue.ToLower().Should().Be("#1997c6");
|
||||
theme.Colors.BrightMagenta.ToLower().Should().Be("#9b5953");
|
||||
theme.Colors.BrightCyan.ToLower().Should().Be("#c8faf4");
|
||||
theme.Colors.BrightWhite.ToLower().Should().Be("#f6f5fb");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,27 +2,25 @@
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Test.Services
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
[TestClass]
|
||||
public class KeyboardCommandServiceTests
|
||||
{
|
||||
private Fixture _fixture;
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
public KeyboardCommandServiceTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void RegisterCommandHandler_HandlerIsNull_ThrowsArgumentNullException()
|
||||
{
|
||||
var command = _fixture.Create<Command>();
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
Action handler = null;
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
@@ -31,10 +29,10 @@ namespace FluentTerminal.App.Test.Services
|
||||
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("handler");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void RegisterCommandHandler_CommandAlreadyRegisted_ThrowsInvalidOperationException()
|
||||
{
|
||||
var command = _fixture.Create<Command>();
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
var handler = _fixture.Create<Action>();
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
keyboardCommandService.RegisterCommandHandler(command, handler);
|
||||
@@ -44,10 +42,10 @@ namespace FluentTerminal.App.Test.Services
|
||||
invoke.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsRegistered_HandlerGetsInvoked()
|
||||
{
|
||||
var command = _fixture.Create<Command>();
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
var handlerCalled = false;
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
keyboardCommandService.RegisterCommandHandler(command, () => handlerCalled = true);
|
||||
@@ -57,15 +55,26 @@ namespace FluentTerminal.App.Test.Services
|
||||
handlerCalled.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsNotRegisted_KeyNotFoundExceptionIsThrown()
|
||||
{
|
||||
var command = _fixture.Create<Command>();
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
Action invoke = () => keyboardCommandService.SendCommand(command);
|
||||
|
||||
invoke.Should().Throw<KeyNotFoundException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsToggleWindow_ShouldNotThrow()
|
||||
{
|
||||
var command = nameof(Command.ToggleWindow);
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
Action invoke = () => keyboardCommandService.SendCommand(command);
|
||||
|
||||
invoke.Should().NotThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,758 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class SettingsServiceTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public SettingsServiceTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_Default_WritesAllPreinstalledThemesInThemeContainer()
|
||||
{
|
||||
var themes = _fixture.CreateMany<TerminalTheme>(3);
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
defaultValueProvider.Setup(x => x.GetPreInstalledThemes()).Returns(themes);
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
Themes = themesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
|
||||
new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
defaultValueProvider.Verify(x => x.GetPreInstalledThemes(), Times.Once);
|
||||
foreach (var theme in themes)
|
||||
{
|
||||
themesContainer.Verify(x => x.WriteValueAsJson(theme.Id.ToString(), theme), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_Default_WritesAllPreinstalledShellProfilesInShellProfilesContainer()
|
||||
{
|
||||
var shellProfiles = _fixture.CreateMany<ShellProfile>(3);
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
defaultValueProvider.Setup(x => x.GetPreinstalledShellProfiles()).Returns(shellProfiles);
|
||||
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
ShellProfiles = shellProfilesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
|
||||
new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
defaultValueProvider.Verify(x => x.GetPreinstalledShellProfiles(), Times.Once);
|
||||
foreach (var shellProfile in shellProfiles)
|
||||
{
|
||||
shellProfilesContainer.Verify(x => x.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCurrentThemeId_ValueExistsInRoamingSettings_ReturnsCurrentThemeId()
|
||||
{
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var currentThemeId = (object)_fixture.Create<Guid>();
|
||||
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(true);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetCurrentThemeId();
|
||||
|
||||
result.Should().Be((Guid)currentThemeId);
|
||||
roamingSettings.Verify(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCurrentThemeId_ValueDoesNotExistInRoamingSettings_ReturnsDefaultThemeIdFromDefaultValueProvider()
|
||||
{
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
var defaultThemeId = _fixture.Create<Guid>();
|
||||
defaultValueProvider.Setup(x => x.GetDefaultThemeId()).Returns(defaultThemeId);
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var currentThemeId = (object)_fixture.Create<Guid>();
|
||||
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(false);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetCurrentThemeId();
|
||||
|
||||
result.Should().Be(defaultThemeId);
|
||||
defaultValueProvider.Verify(x => x.GetDefaultThemeId(), Times.Once);
|
||||
roamingSettings.Verify(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveCurrentThemeId_Default_SetsValueOnRoamingSettings()
|
||||
{
|
||||
var currentThemeId = _fixture.Create<Guid>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.SaveCurrentThemeId(currentThemeId);
|
||||
|
||||
roamingSettings.Verify(x => x.SetValue(SettingsService.CurrentThemeKey, currentThemeId), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveCurrentThemeId_Default_InvokesCurrentThemeChangedEvent()
|
||||
{
|
||||
var currentThemeId = _fixture.Create<Guid>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var currentThemeChangedEventInvoked = false;
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
settingsService.CurrentThemeChanged += (s, e) => currentThemeChangedEventInvoked = true;
|
||||
|
||||
settingsService.SaveCurrentThemeId(currentThemeId);
|
||||
|
||||
currentThemeChangedEventInvoked.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveTheme_Default_WritesToThemesContainer()
|
||||
{
|
||||
var theme = _fixture.Create<TerminalTheme>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
Themes = themesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.SaveTheme(theme);
|
||||
|
||||
themesContainer.Verify(x => x.WriteValueAsJson(theme.Id.ToString(), theme), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveTheme_ThemeIsCurrentTheme_InvokesCurrentThemeChangedEvent()
|
||||
{
|
||||
var theme = _fixture.Create<TerminalTheme>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var currentThemeId = (object)theme.Id;
|
||||
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(true);
|
||||
var currentThemeChangedEventInvoked = false;
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
Themes = themesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
settingsService.CurrentThemeChanged += (s, e) => currentThemeChangedEventInvoked = true;
|
||||
|
||||
settingsService.SaveTheme(theme);
|
||||
|
||||
currentThemeChangedEventInvoked.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteTheme_Default_CallsDeleteOnThemesContainer()
|
||||
{
|
||||
var themeId = _fixture.Create<Guid>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
Themes = themesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.DeleteTheme(themeId);
|
||||
|
||||
themesContainer.Verify(x => x.Delete(themeId.ToString()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetThemes_Default_CallsGetAllOnThemesContainer()
|
||||
{
|
||||
var themes = _fixture.CreateMany<TerminalTheme>(3);
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
themesContainer.Setup(x => x.GetAll()).Returns(themes.Select(JsonConvert.SerializeObject));
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
Themes = themesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetThemes();
|
||||
|
||||
result.Should().BeEquivalentTo(themes);
|
||||
themesContainer.Verify(x => x.GetAll(), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTheme_ValidThemeId_ReturnsThemeFromThemesContainer()
|
||||
{
|
||||
var theme = _fixture.Create<TerminalTheme>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
themesContainer.Setup(x => x.ReadValueFromJson(theme.Id.ToString(), default(TerminalTheme))).Returns(theme);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
Themes = themesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetTheme(theme.Id);
|
||||
|
||||
result.Should().BeEquivalentTo(theme);
|
||||
themesContainer.Verify(x => x.ReadValueFromJson(theme.Id.ToString(), default(TerminalTheme)), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTheme_UnknownThemeId_ReturnsNull()
|
||||
{
|
||||
var themeId = _fixture.Create<Guid>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
themesContainer.Setup(x => x.ReadValueFromJson(themeId.ToString(), default(TerminalTheme))).Returns(value: null);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
Themes = themesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetTheme(themeId);
|
||||
|
||||
result.Should().BeNull();
|
||||
themesContainer.Verify(x => x.ReadValueFromJson(themeId.ToString(), default(TerminalTheme)), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTerminalOptions_Default_ReturnsTerminalOptionsFromRoamingSettings()
|
||||
{
|
||||
var terminalOptions = _fixture.Create<TerminalOptions>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
roamingSettings.Setup(x => x.ReadValueFromJson(nameof(TerminalOptions), It.IsAny<TerminalOptions>())).Returns(terminalOptions);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetTerminalOptions();
|
||||
|
||||
result.Should().BeEquivalentTo(terminalOptions);
|
||||
roamingSettings.Verify(x => x.ReadValueFromJson(nameof(TerminalOptions), It.IsAny<TerminalOptions>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveTerminalOptions_Default_WritesToRoamingSettings()
|
||||
{
|
||||
var terminalOptions = _fixture.Create<TerminalOptions>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.SaveTerminalOptions(terminalOptions);
|
||||
|
||||
roamingSettings.Verify(x => x.WriteValueAsJson(nameof(TerminalOptions), terminalOptions), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveTerminalOptions_Default_InvokesTerminalOptionsChangedEvent()
|
||||
{
|
||||
var terminalOptions = _fixture.Create<TerminalOptions>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var terminalOptionsChangedEventInvoked = false;
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
settingsService.TerminalOptionsChanged += (s, e) => terminalOptionsChangedEventInvoked = true;
|
||||
|
||||
settingsService.SaveTerminalOptions(terminalOptions);
|
||||
|
||||
terminalOptionsChangedEventInvoked.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetApplicationSettings_Default_ReturnsApplicationSettingsFromRoamingSettings()
|
||||
{
|
||||
var applicationSettings = _fixture.Create<ApplicationSettings>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
roamingSettings.Setup(x => x.ReadValueFromJson(nameof(ApplicationSettings), It.IsAny<ApplicationSettings>())).Returns(applicationSettings);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetApplicationSettings();
|
||||
|
||||
result.Should().BeEquivalentTo(applicationSettings);
|
||||
roamingSettings.Verify(x => x.ReadValueFromJson(nameof(ApplicationSettings), It.IsAny<ApplicationSettings>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveApplicationSettings_Default_WritesToRoamingSettings()
|
||||
{
|
||||
var applicationSettings = _fixture.Create<ApplicationSettings>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.SaveApplicationSettings(applicationSettings);
|
||||
|
||||
roamingSettings.Verify(x => x.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveApplicationSettings_Default_InvokesApplicationSettingsChangedEvent()
|
||||
{
|
||||
var applicationSettings = _fixture.Create<ApplicationSettings>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var applicationSettingsChangedEventInvoked = false;
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
settingsService.ApplicationSettingsChanged += (s, e) => applicationSettingsChangedEventInvoked = true;
|
||||
|
||||
settingsService.SaveApplicationSettings(applicationSettings);
|
||||
|
||||
applicationSettingsChangedEventInvoked.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetKeyBindings_KeyBindingsInKeyBindingsContainer_ReturnsKeyBindingsFromKeyBindingsContainer()
|
||||
{
|
||||
var commands = _fixture.CreateMany<Command>(5);
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
foreach (var command in commands)
|
||||
{
|
||||
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null)).Returns(new Collection<KeyBinding> { });
|
||||
}
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
KeyBindings = keyBindingsContainer.Object,
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetCommandKeyBindings();
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
keyBindingsContainer.Verify(x => x.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetKeyBindings_KeyBindingsNotInKeyBindingsContainer_ReturnsKeyBindingsFromDefaultValueProvider()
|
||||
{
|
||||
var commands = _fixture.CreateMany<Command>(5);
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
foreach (var command in commands)
|
||||
{
|
||||
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(EnumHelper.GetEnumDescription(command), null)).Returns(new Collection<KeyBinding> { });
|
||||
}
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
KeyBindings = keyBindingsContainer.Object,
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetCommandKeyBindings();
|
||||
|
||||
foreach (Command command in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
defaultValueProvider.Verify(x => x.GetDefaultKeyBindings(command), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveKeyBindings_Default_WritesToKeyBindingsContainer()
|
||||
{
|
||||
var command = _fixture.Create<Command>();
|
||||
var keyBindings = _fixture.Create<ICollection<KeyBinding>>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
KeyBindings = keyBindingsContainer.Object,
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.SaveKeyBindings(command.ToString(), keyBindings);
|
||||
|
||||
keyBindingsContainer.Verify(x => x.WriteValueAsJson(command.ToString(), keyBindings), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveKeyBindings_Default_InvokesKeyBindingsChangedEvent()
|
||||
{
|
||||
var command = _fixture.Create<Command>();
|
||||
var keyBindings = _fixture.Create<Collection<KeyBinding>>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
var keyBindingsChangedEventInvoked = false;
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
KeyBindings = keyBindingsContainer.Object,
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true;
|
||||
|
||||
settingsService.SaveKeyBindings(command.ToString(), keyBindings);
|
||||
|
||||
keyBindingsChangedEventInvoked.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetKeyBindings_Default_WritesToKeyBindingsContainerFromDefaultValueProvider()
|
||||
{
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
KeyBindings = keyBindingsContainer.Object,
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
settingsService.ResetKeyBindings();
|
||||
|
||||
foreach (Command command in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
keyBindingsContainer.Verify(x => x.WriteValueAsJson(command.ToString(), It.IsAny<ICollection<KeyBinding>>()), Times.Once);
|
||||
defaultValueProvider.Verify(x => x.GetDefaultKeyBindings(command), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResetKeyBindings_Default_InvokesKeyBindingsChangedEvent()
|
||||
{
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
var keyBindingsChangedEventInvoked = false;
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
KeyBindings = keyBindingsContainer.Object,
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true;
|
||||
|
||||
settingsService.ResetKeyBindings();
|
||||
|
||||
keyBindingsChangedEventInvoked.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultShellProfileId_ValueExistsInRoamingSettings_ReturnsDefaultShellProfileId()
|
||||
{
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var localSettings = new Mock<IApplicationDataContainer>();
|
||||
var defaultShellProfileId = (object)_fixture.Create<Guid>();
|
||||
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId)).Returns(true);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
LocalSettings = localSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetDefaultShellProfileId();
|
||||
|
||||
result.Should().Be((Guid)defaultShellProfileId);
|
||||
localSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultShellProfileId_ValueDoesNotExistInRoamingSettings_ReturnsDefaultShellProfileIdFromDefaultValueProvider()
|
||||
{
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
var defaultShellProfileId = _fixture.Create<Guid>();
|
||||
defaultValueProvider.Setup(x => x.GetDefaultShellProfileId()).Returns(defaultShellProfileId);
|
||||
var localSettings = new Mock<IApplicationDataContainer>();
|
||||
var currentDefaultShellProfileId = (object)_fixture.Create<Guid>();
|
||||
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId)).Returns(false);
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
LocalSettings = localSettings.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetDefaultShellProfileId();
|
||||
|
||||
result.Should().Be(defaultShellProfileId);
|
||||
defaultValueProvider.Verify(x => x.GetDefaultShellProfileId(), Times.Once);
|
||||
localSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetShellProfiles_Default_CallsGetAllOnShellProfilesContainer()
|
||||
{
|
||||
var shellProfiles = _fixture.CreateMany<ShellProfile>(3);
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
|
||||
shellProfilesContainer.Setup(x => x.GetAll()).Returns(shellProfiles.Select(JsonConvert.SerializeObject));
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
ShellProfiles = shellProfilesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetShellProfiles();
|
||||
|
||||
result.Should().BeEquivalentTo(shellProfiles);
|
||||
shellProfilesContainer.Verify(x => x.GetAll(), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveShellProfile_Default_WritesToShellProfilesContainer()
|
||||
{
|
||||
var shellProfile = _fixture.Create<ShellProfile>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
ShellProfiles = shellProfilesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.SaveShellProfile(shellProfile);
|
||||
|
||||
shellProfilesContainer.Verify(x => x.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeleteShellProfile_Default_CallsDeleteOnShellProfilesContainer()
|
||||
{
|
||||
var shellProfileId = _fixture.Create<Guid>();
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
ShellProfiles = shellProfilesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
Themes = Mock.Of<IApplicationDataContainer>()
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.DeleteShellProfile(shellProfileId);
|
||||
|
||||
shellProfilesContainer.Verify(x => x.Delete(shellProfileId.ToString()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultShellProfile_ProfileNotFound_DefaultShellProfileIdGetsResetToDefault()
|
||||
{
|
||||
var currentShellProfileId = (object)_fixture.Create<Guid>();
|
||||
var defaultShellProfile = _fixture.Create<ShellProfile>();
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
defaultValueProvider.Setup(x => x.GetDefaultShellProfileId()).Returns(defaultShellProfile.Id);
|
||||
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
|
||||
var localSettings = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
ShellProfiles = shellProfilesContainer.Object,
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
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);
|
||||
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);
|
||||
localSettings.Verify(x => x.SetValue(SettingsService.DefaultShellProfileKey, defaultShellProfile.Id), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCurrentTheme_ThemeNotFound_CurrentThemeIdGetsResetToDefault()
|
||||
{
|
||||
var currentThemeId = (object)_fixture.Create<Guid>();
|
||||
var defaultTheme = _fixture.Create<TerminalTheme>();
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
defaultValueProvider.Setup(x => x.GetDefaultThemeId()).Returns(defaultTheme.Id);
|
||||
var themesContainer = new Mock<IApplicationDataContainer>();
|
||||
var roamingSettings = new Mock<IApplicationDataContainer>();
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||
RoamingSettings = roamingSettings.Object,
|
||||
Themes = themesContainer.Object
|
||||
};
|
||||
themesContainer.Setup(x => x.ReadValueFromJson(currentThemeId.ToString(), default(TerminalTheme))).Returns(value: null);
|
||||
themesContainer.Setup(x => x.ReadValueFromJson(defaultTheme.Id.ToString(), default(TerminalTheme))).Returns(defaultTheme);
|
||||
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(true);
|
||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetCurrentTheme();
|
||||
|
||||
result.Should().Be(defaultTheme);
|
||||
roamingSettings.Verify(x => x.SetValue(SettingsService.CurrentThemeKey, defaultTheme.Id), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Ansi 0 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.016337040811777115</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.016232240945100784</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.020433688536286354</real>
|
||||
</dict>
|
||||
<key>Ansi 1 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.075083047151565552</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.0</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.73976802825927734</real>
|
||||
</dict>
|
||||
<key>Ansi 10 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.43171143531799316</real>
|
||||
<key>Green Component</key>
|
||||
<real>1</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.61819148063659668</real>
|
||||
</dict>
|
||||
<key>Ansi 11 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.10242157429456711</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.7578997015953064</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.93761706352233887</real>
|
||||
</dict>
|
||||
<key>Ansi 12 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.77801346778869629</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.59102588891983032</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.096501544117927551</real>
|
||||
</dict>
|
||||
<key>Ansi 13 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.32394066452980042</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.34950375556945801</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.60686224699020386</real>
|
||||
</dict>
|
||||
<key>Ansi 14 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.95550096035003662</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.97915208339691162</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.78500419855117798</real>
|
||||
</dict>
|
||||
<key>Ansi 15 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.98438382148742676</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.96043074131011963</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.96449893712997437</real>
|
||||
</dict>
|
||||
<key>Ansi 2 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.092498354613780975</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.6950421929359436</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.28945007920265198</real>
|
||||
</dict>
|
||||
<key>Ansi 3 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.1164485365152359</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.45553147792816162</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.90543299913406372</real>
|
||||
</dict>
|
||||
<key>Ansi 4 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.77829605340957642</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.28880593180656433</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.059818677604198456</real>
|
||||
</dict>
|
||||
<key>Ansi 5 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.57602518796920776</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.35004982352256775</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.40191537141799927</real>
|
||||
</dict>
|
||||
<key>Ansi 6 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.59465551376342773</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.64633101224899292</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.43781715631484985</real>
|
||||
</dict>
|
||||
<key>Ansi 7 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.75428962707519531</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.86175870895385742</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.97308963537216187</real>
|
||||
</dict>
|
||||
<key>Ansi 8 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.7491685152053833</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.48549851775169373</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.30726605653762817</real>
|
||||
</dict>
|
||||
<key>Ansi 9 Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.35247993469238281</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.37095117568969727</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.98876720666885376</real>
|
||||
</dict>
|
||||
<key>Background Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.27054747939109802</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.11274909973144531</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.12003477662801743</real>
|
||||
</dict>
|
||||
<key>Bold Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.075083047151565552</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.0</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.73976802825927734</real>
|
||||
</dict>
|
||||
<key>Cursor Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.21945112943649292</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.74987590312957764</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.93704277276992798</real>
|
||||
</dict>
|
||||
<key>Cursor Text Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.037519864737987518</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.032785907387733459</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.02988031692802906</real>
|
||||
</dict>
|
||||
<key>Foreground Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.75428962707519531</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.86175870895385742</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.97308963537216187</real>
|
||||
</dict>
|
||||
<key>Selected Text Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.767447829246521</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.85237473249435425</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.95287787914276123</real>
|
||||
</dict>
|
||||
<key>Selection Color</key>
|
||||
<dict>
|
||||
<key>Blue Component</key>
|
||||
<real>0.30699285864830017</real>
|
||||
<key>Green Component</key>
|
||||
<real>0.41871801018714905</real>
|
||||
<key>Red Component</key>
|
||||
<real>0.43789717555046082</real>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,45 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class ThemeParserFactoryTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public ThemeParserFactoryTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetParser_SupportedFileType_ReturnsParser()
|
||||
{
|
||||
var supportedFileType = _fixture.Create<string>();
|
||||
var parser = new Mock<IThemeParser>();
|
||||
parser.Setup(x => x.SupportedFileTypes).Returns(new string[] { supportedFileType });
|
||||
var themeParserFactory = new ThemeParserFactory(new IThemeParser[] { parser.Object });
|
||||
|
||||
var result = themeParserFactory.GetParser(supportedFileType);
|
||||
|
||||
parser.Should().Be(parser);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetParser_UnsupportedFileType_ReturnsNull()
|
||||
{
|
||||
var unSupportedFileType = _fixture.Create<string>();
|
||||
var supportedFileType = _fixture.Create<string>();
|
||||
var parser = new Mock<IThemeParser>();
|
||||
parser.Setup(x => x.SupportedFileTypes).Returns(new string[] { supportedFileType });
|
||||
var themeParserFactory = new ThemeParserFactory(new IThemeParser[] { parser.Object });
|
||||
|
||||
var result = themeParserFactory.GetParser(unSupportedFileType);
|
||||
|
||||
result.Should().BeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using FluentTerminal.Models.Requests;
|
||||
using FluentTerminal.Models.Responses;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class TrayProcessCommunicationServiceTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public TrayProcessCommunicationServiceTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateTerminal_Default_ReturnsResponseFromAppServiceConnection()
|
||||
{
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var response = _fixture.Create<CreateTerminalResponse>();
|
||||
var responseMessage = new Dictionary<string, string>
|
||||
{
|
||||
[MessageKeys.Type] = _fixture.Create<string>(),
|
||||
[MessageKeys.Content] = JsonConvert.SerializeObject(response)
|
||||
};
|
||||
appServiceConnection.Setup(x => x.SendMessageAsync(It.IsAny<IDictionary<string, string>>())).Returns(Task.FromResult((IDictionary<string, string>)responseMessage));
|
||||
var terminalSize = _fixture.Create<TerminalSize>();
|
||||
var shellProfile = _fixture.Create<ShellProfile>();
|
||||
var sessionType = _fixture.Create<SessionType>();
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||
|
||||
var result = await trayProcessCommunicationService.CreateTerminal(terminalSize, shellProfile, sessionType);
|
||||
|
||||
result.Should().BeEquivalentTo(response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResizeTerminal_Default_SendsResizeTerminalRequest()
|
||||
{
|
||||
var terminalId = _fixture.Create<int>();
|
||||
var terminalSize = _fixture.Create<TerminalSize>();
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||
|
||||
await trayProcessCommunicationService.ResizeTerminal(terminalId, terminalSize);
|
||||
|
||||
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(ResizeTerminalRequest))), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteText_Default_SendsWriteTextRequest()
|
||||
{
|
||||
var terminalId = _fixture.Create<int>();
|
||||
var text = _fixture.Create<string>();
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||
|
||||
await trayProcessCommunicationService.WriteText(terminalId, text);
|
||||
|
||||
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(WriteTextRequest))), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CloseTerminal_Default_SendsTerminalExitedRequest()
|
||||
{
|
||||
var terminalId = _fixture.Create<int>();
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||
|
||||
await trayProcessCommunicationService.CloseTerminal(terminalId);
|
||||
|
||||
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(TerminalExitedRequest))), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnMessageReceived_TerminalExitedRequest_InvokesTerminalExitedEvent()
|
||||
{
|
||||
var terminalId = _fixture.Create<int>();
|
||||
var receivedTerminalId = 0;
|
||||
var terminalExitedEventCalled = false;
|
||||
var request = new TerminalExitedRequest
|
||||
{
|
||||
TerminalId = terminalId
|
||||
};
|
||||
var message = new Dictionary<string, string>
|
||||
{
|
||||
[MessageKeys.Type] = nameof(TerminalExitedRequest),
|
||||
[MessageKeys.Content] = JsonConvert.SerializeObject(request)
|
||||
};
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||
trayProcessCommunicationService.TerminalExited += (s, e) =>
|
||||
{
|
||||
terminalExitedEventCalled = true;
|
||||
receivedTerminalId = e;
|
||||
};
|
||||
|
||||
appServiceConnection.Raise(x => x.MessageReceived += null, null, message);
|
||||
|
||||
terminalExitedEventCalled.Should().BeTrue();
|
||||
receivedTerminalId.Should().Be(terminalId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnMessageReceived_DisplayTerminalOutputRequest_InvokesCorrectOutputHandler()
|
||||
{
|
||||
var terminalId = _fixture.Create<int>();
|
||||
var output = _fixture.Create<string>();
|
||||
var receivedOutput = string.Empty;
|
||||
var request = new DisplayTerminalOutputRequest
|
||||
{
|
||||
TerminalId = terminalId,
|
||||
Output = output
|
||||
};
|
||||
var message = new Dictionary<string, string>
|
||||
{
|
||||
[MessageKeys.Type] = nameof(DisplayTerminalOutputRequest),
|
||||
[MessageKeys.Content] = JsonConvert.SerializeObject(request)
|
||||
};
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||
trayProcessCommunicationService.SubscribeForTerminalOutput(terminalId, o =>
|
||||
{
|
||||
receivedOutput = o;
|
||||
});
|
||||
|
||||
appServiceConnection.Raise(x => x.MessageReceived += null, null, message);
|
||||
|
||||
receivedOutput.Should().Be(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace FluentTerminal.App.Services.Adapters
|
||||
{
|
||||
public class ApplicationDataContainerAdapter : IApplicationDataContainer
|
||||
{
|
||||
private readonly ApplicationDataContainer _applicationDataContainer;
|
||||
|
||||
public ApplicationDataContainerAdapter(ApplicationDataContainer applicationDataContainer)
|
||||
{
|
||||
_applicationDataContainer = applicationDataContainer;
|
||||
}
|
||||
|
||||
public void Delete(string key)
|
||||
{
|
||||
_applicationDataContainer.Values.Remove(key);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetAll()
|
||||
{
|
||||
return _applicationDataContainer.Values.Select(x => x.Value);
|
||||
}
|
||||
|
||||
public T ReadValueFromJson<T>(string name, T fallbackValue)
|
||||
{
|
||||
if (_applicationDataContainer.Values.TryGetValue(name, out object value))
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(fallbackValue, default))
|
||||
{
|
||||
fallbackValue = Activator.CreateInstance<T>();
|
||||
}
|
||||
|
||||
JsonConvert.PopulateObject((string)value, fallbackValue);
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
public void SetValue(string key, object value)
|
||||
{
|
||||
_applicationDataContainer.Values[key] = value;
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out object value)
|
||||
{
|
||||
return _applicationDataContainer.Values.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public void WriteValueAsJson<T>(string name, T value)
|
||||
{
|
||||
_applicationDataContainer.Values[name] = JsonConvert.SerializeObject(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public class ApplicationDataContainers
|
||||
{
|
||||
public IApplicationDataContainer LocalSettings { get; set; }
|
||||
public IApplicationDataContainer RoamingSettings { get; set; }
|
||||
public IApplicationDataContainer Themes { get; set; }
|
||||
public IApplicationDataContainer KeyBindings { get; set; }
|
||||
public IApplicationDataContainer ShellProfiles { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Dialogs
|
||||
{
|
||||
public interface ICreateKeyBindingDialog
|
||||
{
|
||||
Task<KeyBinding> CreateKeyBinding();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Dialogs
|
||||
{
|
||||
public interface IInputDialog
|
||||
{
|
||||
void SetTitle(string title);
|
||||
Task<string> GetInput();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Dialogs
|
||||
{
|
||||
public interface IMessageDialog
|
||||
{
|
||||
string Title { get; set; }
|
||||
string Content { get; set; }
|
||||
|
||||
void AddButton(DialogButton button);
|
||||
|
||||
Task<DialogButton> ShowAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Dialogs
|
||||
{
|
||||
public interface IShellProfileSelectionDialog
|
||||
{
|
||||
ShellProfile SelectedProfile { get; }
|
||||
|
||||
Task<ShellProfile> SelectProfile();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FluentTerminal.App.Services.EventArgs
|
||||
{
|
||||
public class CancelableEventArgs : System.EventArgs
|
||||
{
|
||||
public bool Cancelled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FluentTerminal.App.Services.EventArgs
|
||||
{
|
||||
public class NewWindowRequestedEventArgs : System.EventArgs
|
||||
{
|
||||
public bool ShowProfileSelection { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services.Exceptions
|
||||
{
|
||||
public class ParseThemeException : Exception
|
||||
{
|
||||
public ParseThemeException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public ParseThemeException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ParseThemeException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected ParseThemeException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="PListNet" Version="2.0.3" />
|
||||
<PackageReference Include="RestSharp" Version="106.5.4" />
|
||||
<PackageReference Include="System.Runtime.InteropServices.WindowsRuntime" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IAppServiceConnection
|
||||
{
|
||||
event EventHandler<IDictionary<string, string>> MessageReceived;
|
||||
|
||||
Task<IDictionary<string, string>> SendMessageAsync(IDictionary<string, string> message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IApplicationDataContainer
|
||||
{
|
||||
T ReadValueFromJson<T>(string name, T fallbackValue);
|
||||
|
||||
void WriteValueAsJson<T>(string name, T value);
|
||||
|
||||
bool TryGetValue(string key, out object value);
|
||||
|
||||
void SetValue(string key, object value);
|
||||
|
||||
void Delete(string key);
|
||||
|
||||
IEnumerable<object> GetAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using FluentTerminal.App.Services.EventArgs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public delegate Task CloseRequestedHandler(object sender, CancelableEventArgs args);
|
||||
|
||||
public interface IApplicationView
|
||||
{
|
||||
event CloseRequestedHandler CloseRequested;
|
||||
|
||||
string Title { get; set; }
|
||||
|
||||
Task RunOnDispatcherThread(Action action);
|
||||
|
||||
Task<bool> TryClose();
|
||||
|
||||
bool ToggleFullScreen();
|
||||
bool IsApiContractPresent(string api, ushort version);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IClipboardService
|
||||
{
|
||||
Task<string> GetText();
|
||||
|
||||
void SetText(string text);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -6,12 +7,20 @@ namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IDefaultValueProvider
|
||||
{
|
||||
IEnumerable<ShellProfile> GetPreinstalledShellProfiles();
|
||||
Guid GetDefaultShellProfileId();
|
||||
TerminalOptions GetDefaultTerminalOptions();
|
||||
KeyBindings GetDefaultKeyBindings();
|
||||
ApplicationSettings GetDefaultApplicationSettings();
|
||||
|
||||
ICollection<KeyBinding> GetDefaultKeyBindings(Command command);
|
||||
|
||||
Guid GetDefaultShellProfileId();
|
||||
|
||||
IEnumerable<TabTheme> GetDefaultTabThemes();
|
||||
|
||||
TerminalOptions GetDefaultTerminalOptions();
|
||||
|
||||
Guid GetDefaultThemeId();
|
||||
|
||||
IEnumerable<ShellProfile> GetPreinstalledShellProfiles();
|
||||
|
||||
IEnumerable<TerminalTheme> GetPreInstalledThemes();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,5 +14,8 @@ namespace FluentTerminal.App.Services
|
||||
Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons);
|
||||
|
||||
Task<ShellProfile> ShowProfileSelectionDialogAsync();
|
||||
|
||||
Task<KeyBinding> ShowCreateKeyBindingDialog();
|
||||
Task<string> ShowInputDialogAsync(string title);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IDispatcherTimer
|
||||
{
|
||||
void Start();
|
||||
|
||||
void Stop();
|
||||
|
||||
TimeSpan Interval { get; set; }
|
||||
bool IsEnabled { get; }
|
||||
|
||||
event EventHandler<object> Tick;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IFileSystemService
|
||||
{
|
||||
Task<File> OpenFile(IEnumerable<string> fileTypes);
|
||||
|
||||
Task<string> BrowseForDirectory();
|
||||
|
||||
Task SaveTextFile(string name, string fileTypeDescription, string fileType, string content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IKeyboardCommandService
|
||||
{
|
||||
void RegisterCommandHandler(string command, Action handler);
|
||||
|
||||
void SendCommand(string command);
|
||||
void DeregisterCommandHandler(string command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface INotificationService
|
||||
{
|
||||
void ShowNotification(string title, string content, string url = null);
|
||||
}
|
||||
}
|
||||
@@ -6,35 +6,36 @@ namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ISettingsService
|
||||
{
|
||||
event EventHandler<Guid> CurrentThemeChanged;
|
||||
event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||
event EventHandler<KeyBindings> KeyBindingsChanged;
|
||||
event EventHandler<Guid> CurrentThemeChanged;
|
||||
event EventHandler KeyBindingsChanged;
|
||||
event EventHandler<ShellProfile> ShellProfileAdded;
|
||||
event EventHandler<Guid> ShellProfileDeleted;
|
||||
event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
event EventHandler<Guid> ThemeDeleted;
|
||||
event EventHandler<TerminalTheme> ThemeAdded;
|
||||
|
||||
Guid GetDefaultShellProfileId();
|
||||
ShellProfile GetDefaultShellProfile();
|
||||
void SaveDefaultShellProfileId(Guid id);
|
||||
|
||||
IEnumerable<ShellProfile> GetShellProfiles();
|
||||
void SaveShellProfile(ShellProfile shellProfile);
|
||||
void DeleteShellProfile(Guid id);
|
||||
|
||||
TerminalOptions GetTerminalOptions();
|
||||
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
||||
|
||||
void DeleteTheme(Guid id);
|
||||
ApplicationSettings GetApplicationSettings();
|
||||
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
||||
|
||||
KeyBindings GetKeyBindings();
|
||||
void SaveKeyBindings(KeyBindings keyBindings);
|
||||
|
||||
TerminalTheme GetCurrentTheme();
|
||||
Guid GetCurrentThemeId();
|
||||
ShellProfile GetDefaultShellProfile();
|
||||
Guid GetDefaultShellProfileId();
|
||||
IDictionary<string, ICollection<KeyBinding>> GetCommandKeyBindings();
|
||||
IEnumerable<ShellProfile> GetShellProfiles();
|
||||
IEnumerable<TabTheme> GetTabThemes();
|
||||
TerminalOptions GetTerminalOptions();
|
||||
TerminalTheme GetTheme(Guid id);
|
||||
void SaveCurrentThemeId(Guid id);
|
||||
|
||||
IEnumerable<TerminalTheme> GetThemes();
|
||||
void SaveTheme(TerminalTheme theme);
|
||||
void DeleteTheme(Guid id);
|
||||
void ResetKeyBindings();
|
||||
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
||||
void SaveCurrentThemeId(Guid id);
|
||||
void SaveDefaultShellProfileId(Guid id);
|
||||
void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings);
|
||||
void SaveShellProfile(ShellProfile shellProfile, bool newShell = false);
|
||||
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
||||
void SaveTheme(TerminalTheme theme, bool newTheme = false);
|
||||
ShellProfile GetShellProfile(Guid id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ISystemFontService
|
||||
{
|
||||
IEnumerable<string> GetSystemFontFamilies();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IThemeParser
|
||||
{
|
||||
IEnumerable<string> SupportedFileTypes { get; }
|
||||
|
||||
Task<TerminalTheme> Parse(string fileName, Stream fileContent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IThemeParserFactory
|
||||
{
|
||||
IEnumerable<string> SupportedFileTypes { get; }
|
||||
|
||||
IThemeParser GetParser(string fileType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using FluentTerminal.Models.Responses;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ITrayProcessCommunicationService
|
||||
{
|
||||
event EventHandler<int> TerminalExited;
|
||||
|
||||
void Initialize(IAppServiceConnection appServiceConnection);
|
||||
|
||||
Task<CreateTerminalResponse> CreateTerminal(int id, TerminalSize size, ShellProfile shellProfile, SessionType sessionType);
|
||||
|
||||
Task ResizeTerminal(int id, TerminalSize size);
|
||||
|
||||
Task UpdateToggleWindowKeyBindings();
|
||||
|
||||
Task WriteText(int terminalId, string text);
|
||||
|
||||
void SubscribeForTerminalOutput(int terminalId, Action<string> callback);
|
||||
|
||||
Task CloseTerminal(int terminalId);
|
||||
Task<GetAvailablePortResponse> GetAvailablePort();
|
||||
int GetNextTerminalId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IUpdateService
|
||||
{
|
||||
Task CheckForUpdate(bool notifyNoUpdate = false);
|
||||
Version GetCurrentVersion();
|
||||
Task<Version> GetLatestVersionAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,599 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class DefaultValueProvider : IDefaultValueProvider
|
||||
{
|
||||
public ApplicationSettings GetDefaultApplicationSettings()
|
||||
{
|
||||
return new ApplicationSettings
|
||||
{
|
||||
ConfirmClosingTabs = false,
|
||||
ConfirmClosingWindows = false,
|
||||
UnderlineSelectedTab = false,
|
||||
InactiveTabColorMode = InactiveTabColorMode.Background,
|
||||
NewTerminalLocation = NewTerminalLocation.Tab,
|
||||
TabsPosition = TabsPosition.Top,
|
||||
CopyOnSelect = false,
|
||||
MouseMiddleClickAction = MouseAction.None,
|
||||
MouseRightClickAction = MouseAction.ContextMenu,
|
||||
AlwaysShowTabs = false,
|
||||
AlwaysUseWinPty = true,
|
||||
ShowNewOutputIndicator = false,
|
||||
EnableTrayIcon = true
|
||||
};
|
||||
}
|
||||
|
||||
public ICollection<KeyBinding> GetDefaultKeyBindings(Command command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case Command.ToggleWindow:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.ToggleWindow),
|
||||
Ctrl = false,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Scroll
|
||||
}
|
||||
};
|
||||
|
||||
case Command.NextTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.NextTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Tab
|
||||
}
|
||||
};
|
||||
|
||||
case Command.PreviousTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.PreviousTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
Key = (int)ExtendedVirtualKey.Tab
|
||||
}
|
||||
};
|
||||
|
||||
case Command.SwitchToTerm1:
|
||||
case Command.SwitchToTerm2:
|
||||
case Command.SwitchToTerm3:
|
||||
case Command.SwitchToTerm4:
|
||||
case Command.SwitchToTerm5:
|
||||
case Command.SwitchToTerm6:
|
||||
case Command.SwitchToTerm7:
|
||||
case Command.SwitchToTerm8:
|
||||
case Command.SwitchToTerm9:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = command.ToString(),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Number1 + (command - Command.SwitchToTerm1)
|
||||
}
|
||||
};
|
||||
|
||||
case Command.NewTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.NewTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.T
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ConfigurableNewTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.ConfigurableNewTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
Key = (int)ExtendedVirtualKey.T
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ChangeTabTitle:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.ChangeTabTitle),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
Key = (int)ExtendedVirtualKey.R
|
||||
}
|
||||
};
|
||||
|
||||
case Command.CloseTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.CloseTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.W
|
||||
}
|
||||
};
|
||||
|
||||
case Command.NewWindow:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.NewWindow),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.N
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ConfigurableNewWindow:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.ConfigurableNewWindow),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
Key = (int)ExtendedVirtualKey.N
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ShowSettings:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.ShowSettings),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Comma
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Copy:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.Copy),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.C
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Paste:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.Paste),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.V
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
case Command.PasteWithoutNewlines:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.PasteWithoutNewlines),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
Key = (int)ExtendedVirtualKey.V
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Search:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.Search),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.F
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ToggleFullScreen:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.ToggleFullScreen),
|
||||
Ctrl = false,
|
||||
Alt = true,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Enter
|
||||
}
|
||||
};
|
||||
|
||||
case Command.SelectAll:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.SelectAll),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.A
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Clear:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = nameof(Command.Clear),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.L
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Guid GetDefaultShellProfileId()
|
||||
{
|
||||
return Guid.Parse("813f2298-210a-481a-bdbf-c17bc637a3e2");
|
||||
}
|
||||
|
||||
public IEnumerable<TabTheme> GetDefaultTabThemes()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new TabTheme
|
||||
{
|
||||
Id = 0,
|
||||
Name = "None",
|
||||
BackgroundOpacity = double.NaN,
|
||||
BackgroundPointerOverOpacity = double.NaN,
|
||||
BackgroundPressedOpacity = double.NaN,
|
||||
BackgroundSelectedOpacity = double.NaN,
|
||||
BackgroundSelectedPointerOverOpacity = double.NaN,
|
||||
BackgroundSelectedPressedOpacity = double.NaN
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Red",
|
||||
Color = "#E81123"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Green",
|
||||
Color = "#10893E"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Blue",
|
||||
Color = "#0078D7"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Purple",
|
||||
Color = "#881798"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Orange",
|
||||
Color = "#FF8C00"
|
||||
},
|
||||
new TabTheme
|
||||
{
|
||||
Id = 6,
|
||||
Name = "Teal",
|
||||
Color = "#00B7C3"
|
||||
}
|
||||
};
|
||||
}
|
||||
public TerminalOptions GetDefaultTerminalOptions()
|
||||
{
|
||||
return new TerminalOptions
|
||||
{
|
||||
BellStyle = BellStyle.None,
|
||||
CursorBlink = true,
|
||||
CursorStyle = CursorStyle.Block,
|
||||
ScrollBarStyle = ScrollBarStyle.Hidden,
|
||||
FontFamily = "Consolas",
|
||||
FontSize = 13,
|
||||
BoldText = false,
|
||||
BackgroundOpacity = 0.8,
|
||||
Padding = 12,
|
||||
ScrollBackLimit = 1000
|
||||
};
|
||||
}
|
||||
|
||||
public Guid GetDefaultThemeId()
|
||||
{
|
||||
return Guid.Parse("281e4352-bb50-47b7-a691-2b13830df95e");
|
||||
}
|
||||
|
||||
public IEnumerable<ShellProfile> GetPreinstalledShellProfiles()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ShellProfile
|
||||
{
|
||||
Id = GetDefaultShellProfileId(),
|
||||
Name = "Powershell",
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty,
|
||||
LineEndingTranslation = LineEndingStyle.DoNotModify,
|
||||
KeyBindings = new []
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = GetDefaultShellProfileId().ToString(),
|
||||
Ctrl=true,
|
||||
Alt=true,
|
||||
Shift=false,
|
||||
Meta=false,
|
||||
Key=(int)ExtendedVirtualKey.Number1
|
||||
}
|
||||
}
|
||||
},
|
||||
new ShellProfile
|
||||
{
|
||||
Id = Guid.Parse("ab942a61-7673-4755-9bd8-765aff91d9a3"),
|
||||
Name = "CMD",
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\Windows\System32\cmd.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty,
|
||||
LineEndingTranslation = LineEndingStyle.DoNotModify,
|
||||
KeyBindings = new []
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = "ab942a61-7673-4755-9bd8-765aff91d9a3",
|
||||
Ctrl=true,
|
||||
Alt=true,
|
||||
Shift=false,
|
||||
Meta=false,
|
||||
Key=(int)ExtendedVirtualKey.Number2
|
||||
}
|
||||
}
|
||||
},
|
||||
new ShellProfile
|
||||
{
|
||||
Id= Guid.Parse("e5785ad6-584f-40cb-bdcd-d5b3b3953e7f"),
|
||||
Name = "WSL",
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\windows\system32\wsl.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty,
|
||||
LineEndingTranslation = LineEndingStyle.DoNotModify,
|
||||
KeyBindings = new []
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = "e5785ad6-584f-40cb-bdcd-d5b3b3953e7f",
|
||||
Ctrl=true,
|
||||
Alt=true,
|
||||
Shift=false,
|
||||
Meta=false,
|
||||
Key=(int)ExtendedVirtualKey.Number3
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<TerminalTheme> GetPreInstalledThemes()
|
||||
{
|
||||
var defaultXterm = new TerminalTheme
|
||||
{
|
||||
Id = GetDefaultThemeId(),
|
||||
Author = "xterm.js",
|
||||
Name = "Xterm.js Default",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Black = "#2e3436",
|
||||
Red = "#cc0000",
|
||||
Green = "#4e9a06",
|
||||
Yellow = "#c4a000",
|
||||
Blue = "#3465a4",
|
||||
Magenta = "#75507b",
|
||||
Cyan = "#06989a",
|
||||
White = "#d3d7cf",
|
||||
BrightBlack = "#555753",
|
||||
BrightRed = "#ef2929",
|
||||
BrightGreen = "#8ae234",
|
||||
BrightYellow = "#fce94f",
|
||||
BrightBlue = "#729fcf",
|
||||
BrightMagenta = "#ad7fa8",
|
||||
BrightCyan = "#34e2e2",
|
||||
BrightWhite = "#eeeeec",
|
||||
Foreground = "#ffffff",
|
||||
Background = "#000000",
|
||||
Cursor = "#ffffff",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "rgba(255, 255, 255, 0.3)"
|
||||
}
|
||||
};
|
||||
|
||||
var powerShell = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("3571ce1b-31ce-4cf7-ae15-e0bff70c3eea"),
|
||||
Author = "Microsoft",
|
||||
Name = "PowerShell",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Black = "#000000",
|
||||
Red = "#800000",
|
||||
Green = "#008000",
|
||||
Yellow = "#EEEDF0",
|
||||
Blue = "#000080",
|
||||
Magenta = "#012456",
|
||||
Cyan = "#008080",
|
||||
White = "#c0c0c0",
|
||||
BrightBlack = "#808080",
|
||||
BrightRed = "#ff0000",
|
||||
BrightGreen = "#00ff00",
|
||||
BrightYellow = "#ffff00",
|
||||
BrightBlue = "#0000ff",
|
||||
BrightMagenta = "#ff00ff",
|
||||
BrightCyan = "#00ffff",
|
||||
BrightWhite = "#ffffff",
|
||||
Foreground = "#ffffff",
|
||||
Background = "#012456",
|
||||
Cursor = "#fedba9",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "rgba(254, 219, 169, 0.3)"
|
||||
}
|
||||
};
|
||||
|
||||
var homebrew = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("5f034210-067b-40e2-b9c9-6a25eb6fb8e6"),
|
||||
Author = "Hans Kokx",
|
||||
Name = "Homebrew",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Black = "#000000",
|
||||
Red = "#A0160B",
|
||||
Green = "#00AF21",
|
||||
Yellow = "#A1A222",
|
||||
Blue = "#192AB7",
|
||||
Magenta = "#AA2FAE",
|
||||
Cyan = "#12B1BC",
|
||||
White = "#BBB5AF",
|
||||
BrightBlack = "#747876",
|
||||
BrightRed = "#E52213",
|
||||
BrightGreen = "#00D92B",
|
||||
BrightYellow = "#E6E435",
|
||||
BrightBlue = "#283EF9",
|
||||
BrightMagenta = "#EB43E6",
|
||||
BrightCyan = "#15E7E8",
|
||||
BrightWhite = "#E9E9E9",
|
||||
Foreground = "#00D92B",
|
||||
Background = "#000000",
|
||||
Cursor = "#00D92B",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "#4CFFFFFF"
|
||||
}
|
||||
};
|
||||
|
||||
var tomorrow = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("0cbbf805-fa86-4be5-ac51-75f3054dcc3a"),
|
||||
Author = "Chris Kempson",
|
||||
Name = "Tomorrow",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Foreground = "#4D4D4C",
|
||||
Background = "#FFFFFF",
|
||||
Cursor = "#4D4D4C",
|
||||
CursorAccent = "#FFFFFF",
|
||||
Selection = "rgba(214, 214, 214, 0.5)",
|
||||
Black = "#000000",
|
||||
Red = "#C82829",
|
||||
Green = "#718C00",
|
||||
Yellow = "#EAB700",
|
||||
Blue = "#4271AE",
|
||||
Magenta = "#8959A8",
|
||||
Cyan = "#3E999F",
|
||||
White = "#FFFFFF",
|
||||
BrightBlack = "#000000",
|
||||
BrightRed = "#C82829",
|
||||
BrightGreen = "#718C00",
|
||||
BrightYellow = "#EAB700",
|
||||
BrightBlue = "#4271AE",
|
||||
BrightMagenta = "#8959A8",
|
||||
BrightCyan = "#3E999F",
|
||||
BrightWhite = "#FFFFFF"
|
||||
}
|
||||
};
|
||||
|
||||
var ubuntu = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("32519543-bf94-4db6-92a1-7bcec3966c82"),
|
||||
Author = "Canonical",
|
||||
Name = "Ubuntu Bash",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Foreground = "#EEEEEE",
|
||||
Background = "#300A24",
|
||||
Cursor = "#CFF5DB",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "#4DABEDC1",
|
||||
Black = "#300A24",
|
||||
Red = "#CC0000",
|
||||
Green = "#4E9A06",
|
||||
Yellow = "#06989A",
|
||||
Blue = "#3465A4",
|
||||
Magenta = "#75507B",
|
||||
Cyan = "#C4A000",
|
||||
White = "#D3D7CF",
|
||||
BrightBlack = "#554E53",
|
||||
BrightRed = "#EF2929",
|
||||
BrightGreen = "#8AE234",
|
||||
BrightYellow = "#34E2E2",
|
||||
BrightBlue = "#729FCF",
|
||||
BrightMagenta = "#AD7FA8",
|
||||
BrightCyan = "#FCE94F",
|
||||
BrightWhite = "#EEEEEE"
|
||||
}
|
||||
};
|
||||
|
||||
return new[] { defaultXterm, powerShell, homebrew, tomorrow, ubuntu };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class DialogService : IDialogService
|
||||
{
|
||||
private readonly Func<IShellProfileSelectionDialog> _shellProfileSelectionDialogFactory;
|
||||
private readonly Func<IMessageDialog> _messageDialogFactory;
|
||||
private readonly Func<ICreateKeyBindingDialog> _createKeyBindingDialogFactory;
|
||||
private readonly Func<IInputDialog> _inputDialogFactory;
|
||||
|
||||
public DialogService(Func<IShellProfileSelectionDialog> shellProfileSelectionDialogFactory, Func<IMessageDialog> messageDialogFactory, Func<ICreateKeyBindingDialog> createKeyBindingDialogFactory, Func<IInputDialog> inputDialogFactory)
|
||||
{
|
||||
_shellProfileSelectionDialogFactory = shellProfileSelectionDialogFactory;
|
||||
_messageDialogFactory = messageDialogFactory;
|
||||
_createKeyBindingDialogFactory = createKeyBindingDialogFactory;
|
||||
_inputDialogFactory = inputDialogFactory;
|
||||
}
|
||||
|
||||
public Task<KeyBinding> ShowCreateKeyBindingDialog()
|
||||
{
|
||||
var dialog = _createKeyBindingDialogFactory();
|
||||
|
||||
return dialog.CreateKeyBinding();
|
||||
}
|
||||
|
||||
public Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(title));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(content));
|
||||
}
|
||||
|
||||
if (buttons.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Must not be empty", nameof(buttons));
|
||||
}
|
||||
|
||||
var dialog = _messageDialogFactory();
|
||||
dialog.Content = content;
|
||||
dialog.Title = title;
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
dialog.AddButton(button);
|
||||
}
|
||||
|
||||
return dialog.ShowAsync();
|
||||
}
|
||||
|
||||
public Task<string> ShowInputDialogAsync(string title)
|
||||
{
|
||||
var dialog = _inputDialogFactory.Invoke();
|
||||
dialog.SetTitle(title);
|
||||
|
||||
return dialog.GetInput();
|
||||
}
|
||||
|
||||
public Task<ShellProfile> ShowProfileSelectionDialogAsync()
|
||||
{
|
||||
var dialog = _shellProfileSelectionDialogFactory();
|
||||
|
||||
return dialog.SelectProfile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using FluentTerminal.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class FluentTerminalThemeParser : IThemeParser
|
||||
{
|
||||
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".flutecolors" };
|
||||
|
||||
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileName));
|
||||
}
|
||||
|
||||
if (fileContent == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileContent));
|
||||
}
|
||||
|
||||
using (var streamReader = new StreamReader(fileContent))
|
||||
{
|
||||
var content = await streamReader.ReadToEndAsync();
|
||||
var theme = JsonConvert.DeserializeObject<TerminalTheme>(content);
|
||||
|
||||
theme.PreInstalled = false;
|
||||
theme.Id = Guid.NewGuid();
|
||||
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using FluentTerminal.App.Services.Exceptions;
|
||||
using FluentTerminal.Models;
|
||||
using PListNet;
|
||||
using PListNet.Nodes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class ITermThemeParser : IThemeParser
|
||||
{
|
||||
private const byte Opacity30Percent = 77;
|
||||
|
||||
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".itermcolors" };
|
||||
|
||||
private static class ITermThemeKeys
|
||||
{
|
||||
public const string Ansi0Color = "Ansi 0 Color";
|
||||
public const string Ansi1Color = "Ansi 1 Color";
|
||||
public const string Ansi2Color = "Ansi 2 Color";
|
||||
public const string Ansi3Color = "Ansi 3 Color";
|
||||
public const string Ansi4Color = "Ansi 4 Color";
|
||||
public const string Ansi5Color = "Ansi 5 Color";
|
||||
public const string Ansi6Color = "Ansi 6 Color";
|
||||
public const string Ansi7Color = "Ansi 7 Color";
|
||||
public const string Ansi8Color = "Ansi 8 Color";
|
||||
public const string Ansi9Color = "Ansi 9 Color";
|
||||
public const string Ansi10Color = "Ansi 10 Color";
|
||||
public const string Ansi11Color = "Ansi 11 Color";
|
||||
public const string Ansi12Color = "Ansi 12 Color";
|
||||
public const string Ansi13Color = "Ansi 13 Color";
|
||||
public const string Ansi14Color = "Ansi 14 Color";
|
||||
public const string Ansi15Color = "Ansi 15 Color";
|
||||
|
||||
public const string BackgroundColor = "Background Color";
|
||||
public const string BoldColor = "Bold Color";
|
||||
public const string CursorColor = "Cursor Color";
|
||||
public const string CursorTextColor = "Cursor Text Color";
|
||||
public const string ForegroundColor = "Foreground Color";
|
||||
public const string SelectedTextColor = "Selected Text Color";
|
||||
public const string SelectionColor = "Selection Color";
|
||||
}
|
||||
|
||||
private static class ITermThemeColorKeys
|
||||
{
|
||||
public const string BlueComponent = "Blue Component";
|
||||
public const string GreenComponent = "Green Component";
|
||||
public const string RedComponent = "Red Component";
|
||||
}
|
||||
|
||||
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileName));
|
||||
}
|
||||
|
||||
if (fileContent == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileContent));
|
||||
}
|
||||
|
||||
var node = PList.Load(fileContent) as DictionaryNode ?? throw new ParseThemeException("Root node was not a dictionary.");
|
||||
|
||||
return new TerminalTheme
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(fileName),
|
||||
Colors = GetColors(node),
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false
|
||||
};
|
||||
}
|
||||
|
||||
private TerminalColors GetColors(DictionaryNode themeDictionary)
|
||||
{
|
||||
return new TerminalColors
|
||||
{
|
||||
Background = GetColorString(themeDictionary[ITermThemeKeys.BackgroundColor]),
|
||||
Foreground = GetColorString(themeDictionary[ITermThemeKeys.ForegroundColor]),
|
||||
Cursor = GetColorString(themeDictionary[ITermThemeKeys.CursorColor]),
|
||||
CursorAccent = GetColorString(themeDictionary[ITermThemeKeys.CursorTextColor]),
|
||||
Selection = GetColorString(themeDictionary[ITermThemeKeys.SelectionColor], Opacity30Percent),
|
||||
Black = GetColorString(themeDictionary[ITermThemeKeys.Ansi0Color]),
|
||||
Red = GetColorString(themeDictionary[ITermThemeKeys.Ansi1Color]),
|
||||
Green = GetColorString(themeDictionary[ITermThemeKeys.Ansi2Color]),
|
||||
Yellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi3Color]),
|
||||
Blue = GetColorString(themeDictionary[ITermThemeKeys.Ansi4Color]),
|
||||
Magenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi5Color]),
|
||||
Cyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi6Color]),
|
||||
White = GetColorString(themeDictionary[ITermThemeKeys.Ansi7Color]),
|
||||
BrightBlack = GetColorString(themeDictionary[ITermThemeKeys.Ansi8Color]),
|
||||
BrightRed = GetColorString(themeDictionary[ITermThemeKeys.Ansi9Color]),
|
||||
BrightGreen = GetColorString(themeDictionary[ITermThemeKeys.Ansi10Color]),
|
||||
BrightYellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi11Color]),
|
||||
BrightBlue = GetColorString(themeDictionary[ITermThemeKeys.Ansi12Color]),
|
||||
BrightMagenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi13Color]),
|
||||
BrightCyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi14Color]),
|
||||
BrightWhite = GetColorString(themeDictionary[ITermThemeKeys.Ansi15Color]),
|
||||
};
|
||||
}
|
||||
|
||||
private string GetColorString(PNode colorNode, byte alpha = Byte.MaxValue)
|
||||
{
|
||||
var dictionaryNode = colorNode as DictionaryNode ?? throw new ParseThemeException("Color node was not a dictionary");
|
||||
|
||||
var red = dictionaryNode[ITermThemeColorKeys.RedComponent] as RealNode ?? throw new ParseThemeException("Red node value was not a real number");
|
||||
var green = dictionaryNode[ITermThemeColorKeys.GreenComponent] as RealNode ?? throw new ParseThemeException("Green node value was not a real number");
|
||||
var blue = dictionaryNode[ITermThemeColorKeys.BlueComponent] as RealNode ?? throw new ParseThemeException("Blue node value was not a real number");
|
||||
|
||||
var color = $"#{alpha:X2}{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
|
||||
|
||||
if (alpha == byte.MaxValue)
|
||||
{
|
||||
return $"#{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"rgba({GetByteValue(red):G}, {GetByteValue(green):G}, {GetByteValue(blue):G}, {ToDoubleString(alpha)})";
|
||||
}
|
||||
}
|
||||
|
||||
private byte GetByteValue(RealNode node)
|
||||
{
|
||||
var doubleValue = node.Value * Byte.MaxValue;
|
||||
|
||||
return Convert.ToByte(doubleValue);
|
||||
}
|
||||
|
||||
private static string ToDoubleString(byte alpha)
|
||||
{
|
||||
return (alpha / 256.0).ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class KeyboardCommandService : IKeyboardCommandService
|
||||
{
|
||||
private readonly Dictionary<Command, Action> _commandHandlers = new Dictionary<Command, Action>();
|
||||
|
||||
public void RegisterCommandHandler(Command command, Action handler)
|
||||
{
|
||||
if (_commandHandlers.ContainsKey(command))
|
||||
{
|
||||
throw new InvalidOperationException("command already registered");
|
||||
}
|
||||
|
||||
_commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public void SendCommand(Command command)
|
||||
{
|
||||
if (_commandHandlers.TryGetValue(command, out Action handler))
|
||||
{
|
||||
handler.Invoke();
|
||||
return;
|
||||
}
|
||||
throw new KeyNotFoundException($"No handler registered for command: {command}");
|
||||
}
|
||||
}
|
||||
}
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class KeyboardCommandService : IKeyboardCommandService
|
||||
{
|
||||
private readonly Dictionary<string, Action> _commandHandlers = new Dictionary<string, Action>();
|
||||
|
||||
public void RegisterCommandHandler(string command, Action handler)
|
||||
{
|
||||
if (_commandHandlers.ContainsKey(command))
|
||||
{
|
||||
throw new InvalidOperationException("command already registered");
|
||||
}
|
||||
|
||||
_commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public void DeregisterCommandHandler(string command)
|
||||
{
|
||||
if (_commandHandlers.ContainsKey(command))
|
||||
{
|
||||
_commandHandlers.Remove(command);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendCommand(string command)
|
||||
{
|
||||
if (_commandHandlers.TryGetValue(command, out Action handler))
|
||||
{
|
||||
handler.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// already registered by SystemTray's ToggleWindowService
|
||||
if (command == nameof(Command.ToggleWindow))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"No handler registered for command: {command}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using Windows.Data.Xml.Dom;
|
||||
using System.Diagnostics;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Services
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class NotificationService
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
public void ShowNotification(string title, string content)
|
||||
public void ShowNotification(string title, string content, string url = null)
|
||||
{
|
||||
string xml = $@"<toast>
|
||||
<visual>
|
||||
@@ -20,7 +21,11 @@ namespace FluentTerminal.SystemTray.Services
|
||||
doc.LoadXml(xml);
|
||||
|
||||
var toast = new ToastNotification(doc);
|
||||
if (url != null)
|
||||
{
|
||||
toast.Activated += (n, o) => Process.Start(url);
|
||||
}
|
||||
ToastNotificationManager.CreateToastNotifier().Show(toast);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class SettingsService : ISettingsService
|
||||
{
|
||||
public const string CurrentThemeKey = "CurrentTheme";
|
||||
public const string DefaultShellProfileKey = "DefaultShellProfile";
|
||||
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IApplicationDataContainer _keyBindings;
|
||||
private readonly IApplicationDataContainer _localSettings;
|
||||
private readonly IApplicationDataContainer _roamingSettings;
|
||||
private readonly IApplicationDataContainer _shellProfiles;
|
||||
private readonly IApplicationDataContainer _themes;
|
||||
|
||||
public SettingsService(IDefaultValueProvider defaultValueProvider, ApplicationDataContainers containers)
|
||||
{
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_localSettings = containers.LocalSettings;
|
||||
_roamingSettings = containers.RoamingSettings;
|
||||
|
||||
_themes = containers.Themes;
|
||||
_keyBindings = containers.KeyBindings;
|
||||
_shellProfiles = containers.ShellProfiles;
|
||||
|
||||
foreach (var theme in _defaultValueProvider.GetPreInstalledThemes())
|
||||
{
|
||||
if (GetTheme(theme.Id) == null)
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var shellProfile in _defaultValueProvider.GetPreinstalledShellProfiles())
|
||||
{
|
||||
if (GetShellProfile(shellProfile.Id) == null)
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||
|
||||
public event EventHandler<TerminalTheme> ThemeAdded;
|
||||
public event EventHandler<Guid> CurrentThemeChanged;
|
||||
public event EventHandler<Guid> ThemeDeleted;
|
||||
|
||||
public event EventHandler KeyBindingsChanged;
|
||||
|
||||
public event EventHandler<ShellProfile> ShellProfileAdded;
|
||||
public event EventHandler<Guid> ShellProfileDeleted;
|
||||
|
||||
public event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
|
||||
public void DeleteShellProfile(Guid id)
|
||||
{
|
||||
_shellProfiles.Delete(id.ToString());
|
||||
ShellProfileDeleted?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public void DeleteTheme(Guid id)
|
||||
{
|
||||
_themes.Delete(id.ToString());
|
||||
|
||||
foreach (var profile in GetShellProfiles())
|
||||
{
|
||||
if (profile.TerminalThemeId == id)
|
||||
{
|
||||
profile.TerminalThemeId = Guid.Empty;
|
||||
SaveShellProfile(profile);
|
||||
}
|
||||
}
|
||||
|
||||
ThemeDeleted?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public ApplicationSettings GetApplicationSettings()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(ApplicationSettings), _defaultValueProvider.GetDefaultApplicationSettings());
|
||||
}
|
||||
|
||||
public TerminalTheme GetCurrentTheme()
|
||||
{
|
||||
var id = GetCurrentThemeId();
|
||||
var theme = GetTheme(id);
|
||||
if (theme == null)
|
||||
{
|
||||
id = _defaultValueProvider.GetDefaultThemeId();
|
||||
SaveCurrentThemeId(id);
|
||||
theme = GetTheme(id);
|
||||
}
|
||||
return theme;
|
||||
}
|
||||
|
||||
public Guid GetCurrentThemeId()
|
||||
{
|
||||
if (_roamingSettings.TryGetValue(CurrentThemeKey, out object value))
|
||||
{
|
||||
return (Guid)value;
|
||||
}
|
||||
return _defaultValueProvider.GetDefaultThemeId();
|
||||
}
|
||||
|
||||
public ShellProfile GetDefaultShellProfile()
|
||||
{
|
||||
var id = GetDefaultShellProfileId();
|
||||
var profile = _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
|
||||
if (profile == null)
|
||||
{
|
||||
id = _defaultValueProvider.GetDefaultShellProfileId();
|
||||
SaveDefaultShellProfileId(id);
|
||||
profile = _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
public ShellProfile GetShellProfile(Guid id)
|
||||
{
|
||||
return _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
|
||||
}
|
||||
|
||||
public Guid GetDefaultShellProfileId()
|
||||
{
|
||||
if (_localSettings.TryGetValue(DefaultShellProfileKey, out object value))
|
||||
{
|
||||
return (Guid)value;
|
||||
}
|
||||
return _defaultValueProvider.GetDefaultShellProfileId();
|
||||
}
|
||||
|
||||
public IDictionary<string, ICollection<KeyBinding>> GetCommandKeyBindings()
|
||||
{
|
||||
var keyBindings = new Dictionary<string, ICollection<KeyBinding>>();
|
||||
|
||||
foreach (Command command in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
keyBindings.Add(command.ToString(), _keyBindings.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null) ?? _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||
}
|
||||
return keyBindings;
|
||||
}
|
||||
|
||||
public IEnumerable<ShellProfile> GetShellProfiles()
|
||||
{
|
||||
return _shellProfiles.GetAll().Select(x => JsonConvert.DeserializeObject<ShellProfile>((string)x)).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<TabTheme> GetTabThemes()
|
||||
{
|
||||
return _defaultValueProvider.GetDefaultTabThemes();
|
||||
}
|
||||
|
||||
public TerminalOptions GetTerminalOptions()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(TerminalOptions), _defaultValueProvider.GetDefaultTerminalOptions());
|
||||
}
|
||||
|
||||
public TerminalTheme GetTheme(Guid id)
|
||||
{
|
||||
return _themes.ReadValueFromJson(id.ToString(), default(TerminalTheme));
|
||||
}
|
||||
|
||||
public IEnumerable<TerminalTheme> GetThemes()
|
||||
{
|
||||
return _themes.GetAll().Select(x => JsonConvert.DeserializeObject<TerminalTheme>((string)x)).ToList();
|
||||
}
|
||||
|
||||
public void ResetKeyBindings()
|
||||
{
|
||||
foreach (Command command in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
_keyBindings.WriteValueAsJson(command.ToString(), _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||
}
|
||||
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
|
||||
ApplicationSettingsChanged?.Invoke(this, applicationSettings);
|
||||
}
|
||||
|
||||
public void SaveCurrentThemeId(Guid id)
|
||||
{
|
||||
_roamingSettings.SetValue(CurrentThemeKey, id);
|
||||
|
||||
CurrentThemeChanged?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public void SaveDefaultShellProfileId(Guid id)
|
||||
{
|
||||
_localSettings.SetValue(DefaultShellProfileKey, id);
|
||||
}
|
||||
|
||||
public void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(Command), command))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
_keyBindings.WriteValueAsJson(command.ToString(), keyBindings);
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void SaveShellProfile(ShellProfile shellProfile, bool newShell = false)
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
|
||||
// When saving the shell profile, we also need to update keybindings for everywhere.
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
if (newShell)
|
||||
{
|
||||
ShellProfileAdded?.Invoke(this, shellProfile);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveTerminalOptions(TerminalOptions terminalOptions)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
||||
TerminalOptionsChanged?.Invoke(this, terminalOptions);
|
||||
}
|
||||
|
||||
public void SaveTheme(TerminalTheme theme, bool newTheme = false)
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
|
||||
if (theme.Id == GetCurrentThemeId())
|
||||
{
|
||||
CurrentThemeChanged?.Invoke(this, theme.Id);
|
||||
}
|
||||
|
||||
if (newTheme)
|
||||
{
|
||||
ThemeAdded?.Invoke(this, theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class ThemeParserFactory : IThemeParserFactory
|
||||
{
|
||||
private IEnumerable<IThemeParser> _parsers;
|
||||
|
||||
public IEnumerable<string> SupportedFileTypes { get; }
|
||||
|
||||
public ThemeParserFactory(IEnumerable<IThemeParser> parsers)
|
||||
{
|
||||
_parsers = parsers;
|
||||
SupportedFileTypes = _parsers.SelectMany(p => p.SupportedFileTypes);
|
||||
}
|
||||
|
||||
public IThemeParser GetParser(string fileType)
|
||||
{
|
||||
return _parsers.FirstOrDefault(p => p.SupportedFileTypes.Contains(fileType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using FluentTerminal.Models.Requests;
|
||||
using FluentTerminal.Models.Responses;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class TrayProcessCommunicationService : ITrayProcessCommunicationService
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private IAppServiceConnection _appServiceConnection;
|
||||
private readonly Dictionary<int, Action<string>> _terminalOutputHandlers;
|
||||
private int _nextTerminalId = 0;
|
||||
|
||||
public event EventHandler<int> TerminalExited;
|
||||
|
||||
public TrayProcessCommunicationService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_terminalOutputHandlers = new Dictionary<int, Action<string>>();
|
||||
}
|
||||
|
||||
public int GetNextTerminalId()
|
||||
{
|
||||
return _nextTerminalId++;
|
||||
}
|
||||
|
||||
public async Task<GetAvailablePortResponse> GetAvailablePort()
|
||||
{
|
||||
var request = new GetAvailablePortRequest();
|
||||
|
||||
var responseMessage = await _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||
|
||||
return JsonConvert.DeserializeObject<GetAvailablePortResponse>(responseMessage[MessageKeys.Content]);
|
||||
}
|
||||
|
||||
public async Task<CreateTerminalResponse> CreateTerminal(int id, TerminalSize size, ShellProfile shellProfile, SessionType sessionType)
|
||||
{
|
||||
var request = new CreateTerminalRequest
|
||||
{
|
||||
Id = id,
|
||||
Size = size,
|
||||
Profile = shellProfile,
|
||||
SessionType = sessionType
|
||||
};
|
||||
|
||||
var responseMessage = await _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||
|
||||
return JsonConvert.DeserializeObject<CreateTerminalResponse>(responseMessage[MessageKeys.Content]);
|
||||
}
|
||||
|
||||
public void Initialize(IAppServiceConnection appServiceConnection)
|
||||
{
|
||||
_appServiceConnection = appServiceConnection;
|
||||
_appServiceConnection.MessageReceived += OnMessageReceived;
|
||||
}
|
||||
|
||||
private void OnMessageReceived(object sender, IDictionary<string, string> e)
|
||||
{
|
||||
var messageType = e[MessageKeys.Type];
|
||||
var messageContent = e[MessageKeys.Content];
|
||||
|
||||
if (messageType == nameof(DisplayTerminalOutputRequest))
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<DisplayTerminalOutputRequest>(messageContent);
|
||||
|
||||
if (_terminalOutputHandlers.ContainsKey(request.TerminalId))
|
||||
{
|
||||
_terminalOutputHandlers[request.TerminalId].Invoke(request.Output);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("output was not handled: " + request.Output);
|
||||
}
|
||||
}
|
||||
else if (messageType == nameof(TerminalExitedRequest))
|
||||
{
|
||||
var request = JsonConvert.DeserializeObject<TerminalExitedRequest>(messageContent);
|
||||
|
||||
TerminalExited?.Invoke(this, request.TerminalId);
|
||||
}
|
||||
}
|
||||
|
||||
public Task ResizeTerminal(int id, TerminalSize size)
|
||||
{
|
||||
var request = new ResizeTerminalRequest
|
||||
{
|
||||
TerminalId = id,
|
||||
NewSize = size
|
||||
};
|
||||
|
||||
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||
}
|
||||
|
||||
public void SubscribeForTerminalOutput(int terminalId, Action<string> callback)
|
||||
{
|
||||
_terminalOutputHandlers[terminalId] = callback;
|
||||
}
|
||||
|
||||
public Task UpdateToggleWindowKeyBindings()
|
||||
{
|
||||
var keyBindings = _settingsService.GetCommandKeyBindings()[nameof(Command.ToggleWindow)];
|
||||
|
||||
var request = new SetToggleWindowKeyBindingsRequest
|
||||
{
|
||||
KeyBindings = keyBindings
|
||||
};
|
||||
|
||||
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||
}
|
||||
|
||||
public Task WriteText(int id, string text)
|
||||
{
|
||||
var request = new WriteTextRequest
|
||||
{
|
||||
TerminalId = id,
|
||||
Text = text
|
||||
};
|
||||
|
||||
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||
}
|
||||
|
||||
public Task CloseTerminal(int terminalId)
|
||||
{
|
||||
var request = new TerminalExitedRequest
|
||||
{
|
||||
TerminalId = terminalId
|
||||
};
|
||||
|
||||
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||
}
|
||||
|
||||
private IDictionary<string, string> CreateMessage(object content)
|
||||
{
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
[MessageKeys.Type] = content.GetType().Name,
|
||||
[MessageKeys.Content] = JsonConvert.SerializeObject(content)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using Windows.ApplicationModel;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class UpdateService : IUpdateService
|
||||
{
|
||||
private const string apiEndpoint = "https://api.github.com";
|
||||
|
||||
private readonly INotificationService _notificationService;
|
||||
|
||||
public UpdateService(INotificationService notificationService)
|
||||
{
|
||||
_notificationService = notificationService;
|
||||
}
|
||||
|
||||
public async Task CheckForUpdate(bool notifyNoUpdate = false)
|
||||
{
|
||||
var latest = await GetLatestVersionAsync();
|
||||
if (latest > GetCurrentVersion())
|
||||
{
|
||||
_notificationService.ShowNotification("Update available",
|
||||
"Click to open the releases page.", "https://github.com/felixse/FluentTerminal/releases");
|
||||
}
|
||||
else if (notifyNoUpdate)
|
||||
{
|
||||
_notificationService.ShowNotification("No update available", "You're up to date!");
|
||||
}
|
||||
}
|
||||
|
||||
public Version GetCurrentVersion()
|
||||
{
|
||||
var currentVersion = Package.Current.Id.Version;
|
||||
return new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build, currentVersion.Revision);
|
||||
}
|
||||
|
||||
public async Task<Version> GetLatestVersionAsync()
|
||||
{
|
||||
var restClient = new RestClient(apiEndpoint);
|
||||
var restRequest = new RestRequest("/repos/felixse/fluentterminal/releases", Method.GET);
|
||||
|
||||
var restResponse = await restClient.ExecuteTaskAsync(restRequest);
|
||||
if (restResponse.IsSuccessful)
|
||||
{
|
||||
dynamic restResponseData = JsonConvert.DeserializeObject(restResponse.Content);
|
||||
string tag = restResponseData[0].tag_name;
|
||||
var latestVersion = new Version(tag);
|
||||
return new Version(latestVersion.Major, latestVersion.Minor, latestVersion.Build, latestVersion.Revision);
|
||||
}
|
||||
return new Version(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public class Terminal
|
||||
{
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private Func<Task<string>> _selectedTextCallback;
|
||||
|
||||
public Terminal(ITrayProcessCommunicationService trayProcessCommunicationService)
|
||||
{
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
Id = _trayProcessCommunicationService.GetNextTerminalId();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To be observed by both view and viewmodel
|
||||
/// </summary>
|
||||
public event EventHandler Closed;
|
||||
|
||||
/// <summary>
|
||||
/// to be observed by viewmodel
|
||||
/// </summary>
|
||||
public event EventHandler<string> KeyboardCommandReceived;
|
||||
|
||||
/// <summary>
|
||||
/// to be observed by view
|
||||
/// </summary>
|
||||
public event EventHandler<string> OutputReceived;
|
||||
|
||||
/// <summary>
|
||||
/// to be observed by viewmodel
|
||||
/// </summary>
|
||||
public event EventHandler<TerminalSize> SizeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// to be observed by viewmodel
|
||||
/// </summary>
|
||||
public event EventHandler<string> TitleChanged;
|
||||
|
||||
public string FallbackTitle { get; private set; }
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// To be called by either view or viewmodel
|
||||
/// </summary>
|
||||
public async Task Close()
|
||||
{
|
||||
await _trayProcessCommunicationService.CloseTerminal(Id).ConfigureAwait(true);
|
||||
Closed?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// to be called by viewmodel
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task<string> GetSelectedText()
|
||||
{
|
||||
return _selectedTextCallback();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// to be called by view
|
||||
/// </summary>
|
||||
public void ProcessKeyboardCommand(string command)
|
||||
{
|
||||
KeyboardCommandReceived?.Invoke(this, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To be set by view
|
||||
/// </summary>
|
||||
public void RegisterSelectedTextCallback(Func<Task<string>> selectedTextCallback)
|
||||
{
|
||||
_selectedTextCallback = selectedTextCallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To be called by view
|
||||
/// </summary>
|
||||
public async Task SetSize(TerminalSize size)
|
||||
{
|
||||
await _trayProcessCommunicationService.ResizeTerminal(Id, size);
|
||||
SizeChanged?.Invoke(this, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// to be called by view
|
||||
/// </summary>
|
||||
public void SetTitle(string title)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
title = FallbackTitle;
|
||||
}
|
||||
TitleChanged?.Invoke(this, title);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// to be called by view when ready
|
||||
/// </summary>
|
||||
/// <param name="shellProfile"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="sessionType"></param>
|
||||
public async Task StartShellProcess(ShellProfile shellProfile, TerminalSize size, SessionType sessionType)
|
||||
{
|
||||
_trayProcessCommunicationService.SubscribeForTerminalOutput(Id, t => OutputReceived?.Invoke(this, t));
|
||||
var response = await _trayProcessCommunicationService.CreateTerminal(Id, size, shellProfile, sessionType).ConfigureAwait(true);
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
FallbackTitle = response.ShellExecutableName;
|
||||
SetTitle(FallbackTitle);
|
||||
}
|
||||
}
|
||||
|
||||
public Task Write(string text)
|
||||
{
|
||||
return _trayProcessCommunicationService.WriteText(Id, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FluentTerminal.App.Services.Utilities
|
||||
{
|
||||
public static class EnumHelper
|
||||
{
|
||||
public static string GetEnumDescription(Enum value)
|
||||
{
|
||||
var fieldInfo = value.GetType().GetField(value.ToString());
|
||||
|
||||
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
if (attributes?.Length > 0)
|
||||
{
|
||||
return attributes[0].Description;
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using FluentTerminal.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FluentTerminal.App.Services.Utilities
|
||||
{
|
||||
public class TerminalThemeContractResolver : DefaultContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
if (property.DeclaringType == typeof(TerminalTheme) && property.PropertyName == nameof(TerminalTheme.PreInstalled))
|
||||
{
|
||||
property.ShouldSerialize = (_) => false;
|
||||
}
|
||||
|
||||
if (property.DeclaringType == typeof(TerminalTheme) && property.PropertyName == nameof(TerminalTheme.Id))
|
||||
{
|
||||
property.ShouldSerialize = (_) => false;
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,171 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{812A09E4-3A05-41AB-8E9A-DC4389B5D73E}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>FluentTerminal.App.Test</RootNamespace>
|
||||
<AssemblyName>FluentTerminal.App.Test</AssemblyName>
|
||||
<DefaultLanguage>de-DE</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<PackageCertificateKeyFile>FluentTerminal.App.Test_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
<UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">$(VisualStudioVersion)</UnitTestPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<SDKReference Include="TestPlatform.Universal, Version=$(UnitTestPlatformVersion)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\DialogServiceTests.cs" />
|
||||
<Compile Include="Services\KeyboardCommandServiceTests.cs" />
|
||||
<Compile Include="UnitTestApp.xaml.cs">
|
||||
<DependentUpon>UnitTestApp.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="UnitTestApp.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
<None Include="FluentTerminal.App.Test_TemporaryKey.pfx" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Properties\UnitTestApp.rd.xml" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture">
|
||||
<Version>4.2.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions">
|
||||
<Version>5.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.1.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Moq">
|
||||
<Version>4.8.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestAdapter">
|
||||
<Version>1.2.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestFramework">
|
||||
<Version>1.2.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.App\FluentTerminal.App.csproj">
|
||||
<Project>{41020100-3414-4e0a-9a8e-92d3c4179df8}</Project>
|
||||
<Name>FluentTerminal.App</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
|
||||
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
|
||||
<Name>FluentTerminal.Models</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,45 +0,0 @@
|
||||
<?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"
|
||||
IgnorableNamespaces="uap mp">
|
||||
|
||||
<Identity Name="63c2a56f-7075-408a-a37d-24bb7fb21665"
|
||||
Publisher="CN=Felix"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="63c2a56f-7075-408a-a37d-24bb7fb21665" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>FluentTerminal.App.Test</DisplayName>
|
||||
<PublisherDisplayName>Felix</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="vstest.executionengine.universal.App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="FluentTerminal.App.Test.App">
|
||||
<uap:VisualElements
|
||||
DisplayName="FluentTerminal.App.Test"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
Square44x44Logo="Assets\Square44x44Logo.png"
|
||||
Description="FluentTerminal.App.Test"
|
||||
BackgroundColor="transparent">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
<Capability Name="internetClientServer" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
@@ -1,18 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("FluentTerminal.App.Test")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("FluentTerminal.App.Test")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: AssemblyMetadata("TargetPlatform","UAP")]
|
||||
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
||||
@@ -1,29 +0,0 @@
|
||||
<!--
|
||||
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
|
||||
developers. However, you can modify these parameters to modify the behavior of the .NET Native
|
||||
optimizer.
|
||||
|
||||
Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919
|
||||
|
||||
To fully enable reflection for App1.MyClass and all of its public/private members
|
||||
<Type Name="App1.MyClass" Dynamic="Required All"/>
|
||||
|
||||
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
|
||||
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
|
||||
|
||||
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
|
||||
<Namespace Name="DataClasses.ViewModels" Seralize="All" />
|
||||
-->
|
||||
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Application>
|
||||
<!--
|
||||
An Assembly element with Name="*Application*" applies to all assemblies in
|
||||
the application package. The asterisks are not wildcards.
|
||||
-->
|
||||
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||
<!-- Add your application specific runtime directives here. -->
|
||||
|
||||
|
||||
</Application>
|
||||
</Directives>
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
namespace FluentTerminal.App.Test.Services
|
||||
{
|
||||
[TestClass]
|
||||
public class DialogServiceTests
|
||||
{
|
||||
private Fixture _fixture;
|
||||
|
||||
[TestInitialize]
|
||||
public void TestInitialize()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShowDialogAsync_TitleIsEmpty_ThrowsArgumentNullException()
|
||||
{
|
||||
var title = string.Empty;
|
||||
var content = _fixture.Create<string>();
|
||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||
var dialogService = new DialogService(Mock.Of<ISettingsService>());
|
||||
|
||||
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||
|
||||
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("title");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShowDialogAsync_ContentIsEmpty_ThrowsArgumentNullException()
|
||||
{
|
||||
var title = _fixture.Create<string>();
|
||||
var content = string.Empty;
|
||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||
var dialogService = new DialogService(Mock.Of<ISettingsService>());
|
||||
|
||||
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||
|
||||
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("content");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ShowDialogAsync_NoButtonsPassed_ThrowsArgumentException()
|
||||
{
|
||||
var title = _fixture.Create<string>();
|
||||
var content = _fixture.Create<string>();
|
||||
var dialogService = new DialogService(Mock.Of<ISettingsService>());
|
||||
|
||||
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content);
|
||||
|
||||
invoke.Should().Throw<ArgumentException>().And.ParamName.Should().Be("buttons");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<Application
|
||||
x:Class="FluentTerminal.App.Test.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:FluentTerminal.App.Test"
|
||||
RequestedTheme="Light">
|
||||
|
||||
</Application>
|
||||
@@ -1,102 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App.Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
sealed partial class App : Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.Suspending += OnSuspending;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used such as when the application is launched to open a specific file.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs e)
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
if (System.Diagnostics.Debugger.IsAttached)
|
||||
{
|
||||
this.DebugSettings.EnableFrameRateCounter = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Frame rootFrame = Window.Current.Content as Frame;
|
||||
|
||||
// Do not repeat app initialization when the Window already has content,
|
||||
// just ensure that the window is active
|
||||
if (rootFrame == null)
|
||||
{
|
||||
// Create a Frame to act as the navigation context and navigate to the first page
|
||||
rootFrame = new Frame();
|
||||
|
||||
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||
|
||||
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
|
||||
{
|
||||
//TODO: Load state from previously suspended application
|
||||
}
|
||||
|
||||
// Place the frame in the current Window
|
||||
Window.Current.Content = rootFrame;
|
||||
}
|
||||
|
||||
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI();
|
||||
|
||||
// Ensure the current window is active
|
||||
Window.Current.Activate();
|
||||
|
||||
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when Navigation to a certain page fails
|
||||
/// </summary>
|
||||
/// <param name="sender">The Frame which failed navigation</param>
|
||||
/// <param name="e">Details about the navigation failure</param>
|
||||
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when application execution is being suspended. Application state is saved
|
||||
/// without knowing whether the application will be terminated or resumed with the contents
|
||||
/// of memory still intact.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the suspend request.</param>
|
||||
/// <param name="e">Details about the suspend request.</param>
|
||||
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
//TODO: Save application state and stop any background activity
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Fleck" Version="1.0.3" />
|
||||
<PackageReference Include="MvvmLightLibs" Version="5.4.1" />
|
||||
<PackageReference Include="ReactiveUI" Version="9.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
|
||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,124 @@
|
||||
using FluentTerminal.App.ViewModels.Utilities;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Infrastructure
|
||||
{
|
||||
public class AsyncCommand : IAsyncCommand
|
||||
{
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
private bool _isExecuting;
|
||||
private readonly Func<Task> _execute;
|
||||
private readonly Func<bool> _canExecute;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public AsyncCommand(
|
||||
Func<Task> execute,
|
||||
Func<bool> canExecute = null,
|
||||
IErrorHandler errorHandler = null)
|
||||
{
|
||||
_execute = execute;
|
||||
_canExecute = canExecute;
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
public bool CanExecute()
|
||||
{
|
||||
return !_isExecuting && (_canExecute?.Invoke() ?? true);
|
||||
}
|
||||
|
||||
public async Task ExecuteAsync()
|
||||
{
|
||||
if (CanExecute())
|
||||
{
|
||||
try
|
||||
{
|
||||
_isExecuting = true;
|
||||
await _execute();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isExecuting = false;
|
||||
}
|
||||
}
|
||||
|
||||
RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#region Explicit implementations
|
||||
bool ICommand.CanExecute(object parameter)
|
||||
{
|
||||
return CanExecute();
|
||||
}
|
||||
|
||||
void ICommand.Execute(object parameter)
|
||||
{
|
||||
ExecuteAsync().FireAndForgetSafeAsync(_errorHandler);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class AsyncCommand<T> : IAsyncCommand<T>
|
||||
{
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
private bool _isExecuting;
|
||||
private readonly Func<T, Task> _execute;
|
||||
private readonly Func<T, bool> _canExecute;
|
||||
private readonly IErrorHandler _errorHandler;
|
||||
|
||||
public AsyncCommand(Func<T, Task> execute, Func<T, bool> canExecute = null, IErrorHandler errorHandler = null)
|
||||
{
|
||||
_execute = execute;
|
||||
_canExecute = canExecute;
|
||||
_errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
public bool CanExecute(T parameter)
|
||||
{
|
||||
return !_isExecuting && (_canExecute?.Invoke(parameter) ?? true);
|
||||
}
|
||||
|
||||
public async Task ExecuteAsync(T parameter)
|
||||
{
|
||||
if (CanExecute(parameter))
|
||||
{
|
||||
try
|
||||
{
|
||||
_isExecuting = true;
|
||||
await _execute(parameter);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isExecuting = false;
|
||||
}
|
||||
}
|
||||
|
||||
RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#region Explicit implementations
|
||||
bool ICommand.CanExecute(object parameter)
|
||||
{
|
||||
return CanExecute((T)parameter);
|
||||
}
|
||||
|
||||
void ICommand.Execute(object parameter)
|
||||
{
|
||||
ExecuteAsync((T)parameter).FireAndForgetSafeAsync(_errorHandler);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// based on: https://github.com/johnthiriet/AsyncVoid
|
||||
/// </summary>
|
||||
public interface IAsyncCommand : ICommand
|
||||
{
|
||||
Task ExecuteAsync();
|
||||
bool CanExecute();
|
||||
}
|
||||
|
||||
public interface IAsyncCommand<T> : ICommand
|
||||
{
|
||||
Task ExecuteAsync(T parameter);
|
||||
bool CanExecute(T parameter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Infrastructure
|
||||
{
|
||||
public interface IErrorHandler
|
||||
{
|
||||
void HandleError(Exception ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.EventArgs;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IApplicationView _applicationView;
|
||||
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 ApplicationSettings _applicationSettings;
|
||||
private string _background;
|
||||
private double _backgroundOpacity;
|
||||
private TerminalViewModel _selectedTerminal;
|
||||
private TabsPosition _tabsPosition;
|
||||
|
||||
public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService,
|
||||
IApplicationView applicationView, IDispatcherTimer dispatcherTimer, IClipboardService clipboardService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
|
||||
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
_settingsService.ShellProfileAdded += OnShellProfileAdded;
|
||||
_settingsService.ShellProfileDeleted += OnShellProfileDeleted;
|
||||
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
_dialogService = dialogService;
|
||||
_applicationView = applicationView;
|
||||
_dispatcherTimer = dispatcherTimer;
|
||||
_clipboardService = clipboardService;
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewTab), () => AddTerminal(null, false, Guid.Empty));
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ConfigurableNewTab), () => AddTerminal(null, true, Guid.Empty));
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ChangeTabTitle), () => SelectedTerminal.EditTitle());
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.CloseTab), CloseCurrentTab);
|
||||
|
||||
// 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;
|
||||
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.NewWindow), () => NewWindow(false));
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ConfigurableNewWindow), () => NewWindow(true));
|
||||
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ShowSettings), ShowSettings);
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ToggleFullScreen), ToggleFullScreen);
|
||||
|
||||
foreach (ShellProfile profile in _settingsService.GetShellProfiles())
|
||||
{
|
||||
_keyboardCommandService.RegisterCommandHandler(profile.Id.ToString(), () => AddTerminal(profile.WorkingDirectory, false, profile.Id));
|
||||
}
|
||||
|
||||
var currentTheme = _settingsService.GetCurrentTheme();
|
||||
var options = _settingsService.GetTerminalOptions();
|
||||
Background = currentTheme.Colors.Background;
|
||||
BackgroundOpacity = options.BackgroundOpacity;
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
TabsPosition = _applicationSettings.TabsPosition;
|
||||
|
||||
AddTerminalCommand = new RelayCommand(() => AddTerminal(null, false, Guid.Empty));
|
||||
ShowAboutCommand = new RelayCommand(ShowAbout);
|
||||
ShowSettingsCommand = new RelayCommand(ShowSettings);
|
||||
|
||||
_applicationView.CloseRequested += OnCloseRequest;
|
||||
Terminals.CollectionChanged += OnTerminalsCollectionChanged;
|
||||
}
|
||||
|
||||
private void OnShellProfileDeleted(object sender, Guid e)
|
||||
{
|
||||
_keyboardCommandService.DeregisterCommandHandler(e.ToString());
|
||||
}
|
||||
|
||||
private void OnShellProfileAdded(object sender, ShellProfile e)
|
||||
{
|
||||
_keyboardCommandService.RegisterCommandHandler(e.Id.ToString(), () => AddTerminal(e.WorkingDirectory, false, e.Id));
|
||||
}
|
||||
|
||||
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 ShowAboutRequested;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand ShowAboutCommand { get; }
|
||||
|
||||
public RelayCommand ShowSettingsCommand { get; }
|
||||
|
||||
public TabsPosition TabsPosition
|
||||
{
|
||||
get => _tabsPosition;
|
||||
set => Set(ref _tabsPosition, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
|
||||
|
||||
/// <summary>
|
||||
/// Add a new terminal window, either with a specified terminal profile, with the default, or by showing the profile selection dialog.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task AddTerminal(string startupDirectory, bool showProfileSelection, Guid profileId)
|
||||
{
|
||||
return _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
ShellProfile profile = null;
|
||||
|
||||
if (showProfileSelection)
|
||||
{
|
||||
profile = await _dialogService.ShowProfileSelectionDialogAsync();
|
||||
|
||||
if (profile == null)
|
||||
{
|
||||
if (Terminals.Count == 0)
|
||||
{
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
_applicationView.TryClose();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (profileId == Guid.Empty)
|
||||
{
|
||||
profile = _settingsService.GetDefaultShellProfile();
|
||||
}
|
||||
else
|
||||
{
|
||||
profile = _settingsService.GetShellProfile(profileId);
|
||||
}
|
||||
|
||||
var terminal = new TerminalViewModel(_settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService,
|
||||
_applicationSettings, startupDirectory, profile, _applicationView, _dispatcherTimer, _clipboardService);
|
||||
terminal.Closed += OnTerminalCloseRequested;
|
||||
Terminals.Add(terminal);
|
||||
|
||||
SelectedTerminal = terminal;
|
||||
});
|
||||
}
|
||||
|
||||
public void CloseAllTerminals()
|
||||
{
|
||||
foreach (var terminal in Terminals)
|
||||
{
|
||||
terminal.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseCurrentTab()
|
||||
{
|
||||
SelectedTerminal?.CloseCommand.Execute(null);
|
||||
}
|
||||
|
||||
private void NewWindow(bool showProfileSelection)
|
||||
{
|
||||
var args = new NewWindowRequestedEventArgs
|
||||
{
|
||||
ShowProfileSelection = showProfileSelection
|
||||
};
|
||||
|
||||
NewWindowRequested?.Invoke(this, args);
|
||||
}
|
||||
|
||||
private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
_applicationSettings = e;
|
||||
TabsPosition = e.TabsPosition;
|
||||
RaisePropertyChanged(nameof(ShowTabsOnTop));
|
||||
RaisePropertyChanged(nameof(ShowTabsOnBottom));
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnCloseRequest(object sender, CancelableEventArgs e)
|
||||
{
|
||||
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.Cancelled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseAllTerminals();
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnCurrentThemeChanged(object sender, Guid e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
var currentTheme = _settingsService.GetTheme(e);
|
||||
Background = currentTheme.Colors.Background;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnTerminalCloseRequested(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is TerminalViewModel terminal)
|
||||
{
|
||||
terminal.Close();
|
||||
if (SelectedTerminal == terminal)
|
||||
{
|
||||
SelectedTerminal = Terminals.LastOrDefault(t => t != terminal);
|
||||
}
|
||||
Terminals.Remove(terminal);
|
||||
|
||||
if (Terminals.Count == 0)
|
||||
{
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
_applicationView.TryClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
BackgroundOpacity = e.BackgroundOpacity;
|
||||
});
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
private void ShowAbout()
|
||||
{
|
||||
ShowAboutRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void ShowSettings()
|
||||
{
|
||||
ShowSettingsRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void ToggleFullScreen()
|
||||
{
|
||||
_applicationView.ToggleFullScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class AboutPageViewModel : ViewModelBase
|
||||
{
|
||||
private const string BaseUrl = "https://github.com/felixse/FluentTerminal/releases/tag/";
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IUpdateService _updateService;
|
||||
private string _latestVersion;
|
||||
private readonly IApplicationView _applicationView;
|
||||
|
||||
public AboutPageViewModel(ISettingsService settingsService, IUpdateService updateService, IApplicationView applicationView)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_updateService = updateService;
|
||||
_applicationView = applicationView;
|
||||
|
||||
CheckForUpdatesCommand = new AsyncCommand(() => CheckForUpdate(true));
|
||||
}
|
||||
|
||||
public IAsyncCommand CheckForUpdatesCommand { get; }
|
||||
|
||||
public string CurrentVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
var version = _updateService.GetCurrentVersion();
|
||||
return ConvertVersionToString(version);
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentVersionReleaseNotesURL => BaseUrl + CurrentVersion;
|
||||
|
||||
public string LatestVersion
|
||||
{
|
||||
get => _latestVersion;
|
||||
set
|
||||
{
|
||||
if (Set(ref _latestVersion, value))
|
||||
{
|
||||
RaisePropertyChanged(nameof(LatestVersionFound));
|
||||
RaisePropertyChanged(nameof(LatestVersionLoading));
|
||||
RaisePropertyChanged(nameof(LatestVersionNotFound));
|
||||
RaisePropertyChanged(nameof(LatestVersionReleaseNotesURL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool LatestVersionFound => !LatestVersionNotFound && !LatestVersionLoading;
|
||||
|
||||
public bool LatestVersionLoading => LatestVersion == null;
|
||||
|
||||
public bool LatestVersionNotFound => LatestVersion == "0.0.0.0";
|
||||
|
||||
public string LatestVersionReleaseNotesURL => BaseUrl + LatestVersion;
|
||||
|
||||
public Task OnNavigatedTo()
|
||||
{
|
||||
return CheckForUpdate(false);
|
||||
}
|
||||
|
||||
public async Task CheckForUpdate(bool notifyNoUpdate)
|
||||
{
|
||||
var version = ConvertVersionToString(await _updateService.GetLatestVersionAsync());
|
||||
await _applicationView.RunOnDispatcherThread(() => LatestVersion = version);
|
||||
await _updateService.CheckForUpdate(notifyNoUpdate);
|
||||
}
|
||||
|
||||
private string ConvertVersionToString(Version version)
|
||||
{
|
||||
return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
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 ApplicationSettings _applicationSettings;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IStartupTaskService _startupTaskService;
|
||||
private bool _canEnableStartupTask;
|
||||
private bool _editingNewTerminalLocation;
|
||||
private bool _editingTabsPosition;
|
||||
private bool _editingInactiveTabColorMode;
|
||||
private bool _startupTaskEnabled;
|
||||
private bool _shouldRestartForTrayMessage;
|
||||
private string _startupTaskErrorMessage;
|
||||
|
||||
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 AlwaysUseWinPty
|
||||
{
|
||||
get => _applicationSettings.AlwaysUseWinPty;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.AlwaysUseWinPty != value)
|
||||
{
|
||||
_applicationSettings.AlwaysUseWinPty = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableTrayIcon
|
||||
{
|
||||
get => _applicationSettings.EnableTrayIcon;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.EnableTrayIcon != value)
|
||||
{
|
||||
_applicationSettings.EnableTrayIcon = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
|
||||
// Toggle message telling user to restart the program
|
||||
ShouldRestartForTrayMessage = !ShouldRestartForTrayMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldRestartForTrayMessage
|
||||
{
|
||||
get => _shouldRestartForTrayMessage;
|
||||
set => Set(ref _shouldRestartForTrayMessage, value);
|
||||
}
|
||||
|
||||
public bool ShowNewOutputIndicator
|
||||
{
|
||||
get => _applicationSettings.ShowNewOutputIndicator;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.ShowNewOutputIndicator != value)
|
||||
{
|
||||
_applicationSettings.ShowNewOutputIndicator = 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;
|
||||
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 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 TabsPosition TabsPosition
|
||||
{
|
||||
get => _applicationSettings.TabsPosition;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.TabsPosition != value && !_editingTabsPosition)
|
||||
{
|
||||
_editingTabsPosition = true;
|
||||
_applicationSettings.TabsPosition = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(TopIsSelected));
|
||||
RaisePropertyChanged(nameof(BottomIsSelected));
|
||||
_editingTabsPosition = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TopIsSelected
|
||||
{
|
||||
get => TabsPosition == TabsPosition.Top;
|
||||
set => TabsPosition = TabsPosition.Top;
|
||||
}
|
||||
|
||||
public bool UnderlineSelectedTab
|
||||
{
|
||||
get => _applicationSettings.UnderlineSelectedTab;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.UnderlineSelectedTab != value)
|
||||
{
|
||||
_applicationSettings.UnderlineSelectedTab = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool BackgroundIsSelected
|
||||
{
|
||||
get => InactiveTabColorMode == InactiveTabColorMode.Background;
|
||||
set => InactiveTabColorMode = InactiveTabColorMode.Background;
|
||||
}
|
||||
|
||||
public bool UnderlinedIsSelected
|
||||
{
|
||||
get => InactiveTabColorMode == InactiveTabColorMode.Underlined;
|
||||
set => InactiveTabColorMode = InactiveTabColorMode.Underlined;
|
||||
}
|
||||
|
||||
public InactiveTabColorMode InactiveTabColorMode
|
||||
{
|
||||
get => _applicationSettings.InactiveTabColorMode;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.InactiveTabColorMode != value && !_editingInactiveTabColorMode)
|
||||
{
|
||||
_editingInactiveTabColorMode = true;
|
||||
_applicationSettings.InactiveTabColorMode = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(BackgroundIsSelected));
|
||||
RaisePropertyChanged(nameof(UnderlinedIsSelected));
|
||||
_editingInactiveTabColorMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool WindowIsSelected
|
||||
{
|
||||
get => NewTerminalLocation == NewTerminalLocation.Window;
|
||||
set => NewTerminalLocation = NewTerminalLocation.Window;
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the general settings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultApplicationSettings();
|
||||
ConfirmClosingWindows = defaults.ConfirmClosingWindows;
|
||||
ConfirmClosingTabs = defaults.ConfirmClosingTabs;
|
||||
UnderlineSelectedTab = defaults.UnderlineSelectedTab;
|
||||
InactiveTabColorMode = defaults.InactiveTabColorMode;
|
||||
NewTerminalLocation = defaults.NewTerminalLocation;
|
||||
AlwaysShowTabs = defaults.AlwaysShowTabs;
|
||||
AlwaysUseWinPty = defaults.AlwaysUseWinPty;
|
||||
ShowNewOutputIndicator = defaults.ShowNewOutputIndicator;
|
||||
EnableTrayIcon = defaults.EnableTrayIcon;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Views;
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
@@ -13,18 +11,21 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
|
||||
public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService)
|
||||
public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService, KeyBindingsViewModel parent)
|
||||
{
|
||||
Model = keyBinding;
|
||||
Parent = parent;
|
||||
_dialogService = dialogService;
|
||||
EditCommand = new RelayCommand(async () => await Edit().ConfigureAwait(false));
|
||||
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false));
|
||||
EditCommand = new AsyncCommand(Edit);
|
||||
DeleteCommand = new AsyncCommand(Delete);
|
||||
}
|
||||
|
||||
public event EventHandler Deleted;
|
||||
|
||||
public event EventHandler Edited;
|
||||
|
||||
public KeyBindingsViewModel Parent { get; }
|
||||
|
||||
public bool Meta
|
||||
{
|
||||
get => Model.Meta;
|
||||
@@ -64,8 +65,8 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand DeleteCommand { get; }
|
||||
public RelayCommand EditCommand { get; }
|
||||
public IAsyncCommand DeleteCommand { get; }
|
||||
public IAsyncCommand EditCommand { get; }
|
||||
|
||||
public int Key
|
||||
{
|
||||
@@ -97,16 +98,15 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
|
||||
public async Task<bool> Edit()
|
||||
{
|
||||
var dialog = new CreateKeyBindingDialog(_dialogService);
|
||||
var result = await dialog.ShowAsync();
|
||||
var keyBinding = await _dialogService.ShowCreateKeyBindingDialog().ConfigureAwait(true);
|
||||
|
||||
if (result == ContentDialogResult.Primary)
|
||||
if (keyBinding != null)
|
||||
{
|
||||
Alt = dialog.KeyBinding.Alt;
|
||||
Ctrl = dialog.KeyBinding.Ctrl;
|
||||
Shift = dialog.KeyBinding.Shift;
|
||||
Meta = dialog.KeyBinding.Meta;
|
||||
Key = dialog.KeyBinding.Key;
|
||||
Alt = keyBinding.Alt;
|
||||
Ctrl = keyBinding.Ctrl;
|
||||
Shift = keyBinding.Shift;
|
||||
Meta = keyBinding.Meta;
|
||||
Key = keyBinding.Key;
|
||||
|
||||
Edited?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class KeyBindingsPageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private IDictionary<string, ICollection<KeyBinding>> _keyBindings;
|
||||
|
||||
public KeyBindingsPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ITrayProcessCommunicationService trayProcessCommunicationService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||
AddCommand = new RelayCommand<string>(async command => await Add(command).ConfigureAwait(false));
|
||||
|
||||
Initialize(_settingsService.GetCommandKeyBindings());
|
||||
}
|
||||
|
||||
public RelayCommand<string> AddCommand { get; }
|
||||
public ObservableCollection<KeyBindingsViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingsViewModel>();
|
||||
public RelayCommand RestoreDefaultsCommand { get; }
|
||||
|
||||
private async Task Add(string command)
|
||||
{
|
||||
var keyBinding = KeyBindings.FirstOrDefault(k => k.Command == command);
|
||||
await keyBinding?.ShowAddKeyBindingDialog();
|
||||
}
|
||||
|
||||
private void ClearKeyBindings()
|
||||
{
|
||||
foreach (var keyBinding in KeyBindings)
|
||||
{
|
||||
keyBinding.Edited -= OnEdited;
|
||||
}
|
||||
|
||||
KeyBindings.Clear();
|
||||
}
|
||||
|
||||
private void Initialize(IDictionary<string, ICollection<KeyBinding>> keyBindings)
|
||||
{
|
||||
_keyBindings = keyBindings;
|
||||
|
||||
ClearKeyBindings();
|
||||
|
||||
foreach (var value in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
var command = (Command)value;
|
||||
var viewModel = new KeyBindingsViewModel(command.ToString(), _dialogService, EnumHelper.GetEnumDescription(command), true);
|
||||
foreach (var keyBinding in _keyBindings[command.ToString()])
|
||||
{
|
||||
viewModel.Add(keyBinding);
|
||||
}
|
||||
viewModel.Edited += OnEdited;
|
||||
KeyBindings.Add(viewModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEdited(string command, ICollection<KeyBinding> keyBindings)
|
||||
{
|
||||
_settingsService.SaveKeyBindings(command, keyBindings);
|
||||
_trayProcessCommunicationService.UpdateToggleWindowKeyBindings();
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the default keybindings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
_settingsService.ResetKeyBindings();
|
||||
Initialize(_settingsService.GetCommandKeyBindings());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class KeyBindingsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private bool _editable;
|
||||
|
||||
public KeyBindingsViewModel(string command, IDialogService dialogService, string commandName, bool editable)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
|
||||
Command = command;
|
||||
CommandName = commandName;
|
||||
Editable = editable;
|
||||
}
|
||||
|
||||
public delegate void EditedEvent(string command, ICollection<KeyBinding> keyBindings);
|
||||
|
||||
public event EditedEvent Edited;
|
||||
|
||||
public string Command { get; }
|
||||
|
||||
public string CommandName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the "Edit" and "Delete" links appear next to the key binding list.
|
||||
/// </summary>
|
||||
public bool Editable
|
||||
{
|
||||
get => _editable;
|
||||
set => Set(ref _editable, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<KeyBindingViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingViewModel>();
|
||||
|
||||
public async Task ShowAddKeyBindingDialog()
|
||||
{
|
||||
var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService, this);
|
||||
|
||||
if (await newKeyBinding.Edit().ConfigureAwait(true))
|
||||
{
|
||||
Add(newKeyBinding.Model);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
KeyBindings.Clear();
|
||||
}
|
||||
|
||||
public void Add(KeyBinding keyBinding)
|
||||
{
|
||||
var viewModel = new KeyBindingViewModel(keyBinding, _dialogService, this);
|
||||
|
||||
viewModel.Deleted += ViewModel_Deleted;
|
||||
viewModel.Edited += ViewModel_Edited;
|
||||
KeyBindings.Add(viewModel);
|
||||
Edited?.Invoke(Command, KeyBindings.Select(x => x.Model).ToList());
|
||||
}
|
||||
|
||||
private void ViewModel_Deleted(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is KeyBindingViewModel keyBinding)
|
||||
{
|
||||
KeyBindings.Remove(keyBinding);
|
||||
Edited?.Invoke(Command, KeyBindings.Select(x => x.Model).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewModel_Edited(object sender, EventArgs e)
|
||||
{
|
||||
Edited?.Invoke(Command, KeyBindings.Select(x => x.Model).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class MousePageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly ApplicationSettings _applicationSettings;
|
||||
private bool _editingMouseRightClickAction;
|
||||
private bool _editingMouseMiddleClickAction;
|
||||
|
||||
public MousePageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
|
||||
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
public bool CopyOnSelect
|
||||
{
|
||||
get => _applicationSettings.CopyOnSelect;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.CopyOnSelect != value)
|
||||
{
|
||||
_applicationSettings.CopyOnSelect = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MouseAction MouseRightClickAction
|
||||
{
|
||||
get => _applicationSettings.MouseRightClickAction;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.MouseRightClickAction != value && !_editingMouseRightClickAction)
|
||||
{
|
||||
_editingMouseRightClickAction = true;
|
||||
_applicationSettings.MouseRightClickAction = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(MouseRightClickNoneIsSelected));
|
||||
RaisePropertyChanged(nameof(MouseRightClickContextMenuIsSelected));
|
||||
RaisePropertyChanged(nameof(MouseRightClickPasteIsSelected));
|
||||
_editingMouseRightClickAction = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MouseRightClickNoneIsSelected
|
||||
{
|
||||
get => MouseRightClickAction == MouseAction.None;
|
||||
set => MouseRightClickAction = MouseAction.None;
|
||||
}
|
||||
|
||||
public bool MouseRightClickContextMenuIsSelected
|
||||
{
|
||||
get => MouseRightClickAction == MouseAction.ContextMenu;
|
||||
set => MouseRightClickAction = MouseAction.ContextMenu;
|
||||
}
|
||||
|
||||
public bool MouseRightClickPasteIsSelected
|
||||
{
|
||||
get => MouseRightClickAction == MouseAction.Paste;
|
||||
set => MouseRightClickAction = MouseAction.Paste;
|
||||
}
|
||||
|
||||
public MouseAction MouseMiddleClickAction
|
||||
{
|
||||
get => _applicationSettings.MouseMiddleClickAction;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.MouseMiddleClickAction != value && !_editingMouseMiddleClickAction)
|
||||
{
|
||||
_editingMouseMiddleClickAction = true;
|
||||
_applicationSettings.MouseMiddleClickAction = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(MouseMiddleClickNoneIsSelected));
|
||||
RaisePropertyChanged(nameof(MouseMiddleClickContextMenuIsSelected));
|
||||
RaisePropertyChanged(nameof(MouseMiddleClickPasteIsSelected));
|
||||
_editingMouseMiddleClickAction = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MouseMiddleClickNoneIsSelected
|
||||
{
|
||||
get => MouseMiddleClickAction == MouseAction.None;
|
||||
set => MouseMiddleClickAction = MouseAction.None;
|
||||
}
|
||||
|
||||
public bool MouseMiddleClickContextMenuIsSelected
|
||||
{
|
||||
get => MouseMiddleClickAction == MouseAction.ContextMenu;
|
||||
set => MouseMiddleClickAction = MouseAction.ContextMenu;
|
||||
}
|
||||
|
||||
public bool MouseMiddleClickPasteIsSelected
|
||||
{
|
||||
get => MouseMiddleClickAction == MouseAction.Paste;
|
||||
set => MouseMiddleClickAction = MouseAction.Paste;
|
||||
}
|
||||
|
||||
public RelayCommand RestoreDefaultsCommand { get; }
|
||||
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the mouse settings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultApplicationSettings();
|
||||
CopyOnSelect = defaults.CopyOnSelect;
|
||||
MouseMiddleClickAction = defaults.MouseMiddleClickAction;
|
||||
MouseRightClickAction = defaults.MouseRightClickAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
@@ -13,20 +14,25 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
private ShellProfileViewModel _selectedShellProfile;
|
||||
private readonly IApplicationView _applicationView;
|
||||
|
||||
public ProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
|
||||
public ProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IFileSystemService fileSystemService, IApplicationView applicationView)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_fileSystemService = fileSystemService;
|
||||
_applicationView = applicationView;
|
||||
|
||||
CreateShellProfileCommand = new RelayCommand(CreateShellProfile);
|
||||
CloneCommand = new RelayCommand<ShellProfileViewModel>(Clone);
|
||||
|
||||
var defaultShellProfileId = _settingsService.GetDefaultShellProfileId();
|
||||
foreach (var shellProfile in _settingsService.GetShellProfiles())
|
||||
{
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, settingsService, dialogService);
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, settingsService, dialogService, fileSystemService, applicationView, defaultValueProvider, false);
|
||||
viewModel.Deleted += OnShellProfileDeleted;
|
||||
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
||||
|
||||
@@ -38,6 +44,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
}
|
||||
|
||||
SelectedShellProfile = ShellProfiles.First(p => p.IsDefault);
|
||||
|
||||
}
|
||||
|
||||
private void OnShellProfileSetAsDefault(object sender, EventArgs e)
|
||||
@@ -74,18 +81,38 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
|
||||
public RelayCommand CreateShellProfileCommand { get; }
|
||||
|
||||
public RelayCommand<ShellProfileViewModel> CloneCommand { get; }
|
||||
|
||||
private void CreateShellProfile()
|
||||
{
|
||||
var shellProfile = new ShellProfile
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = "New profile"
|
||||
Name = "New profile",
|
||||
KeyBindings = new List<KeyBinding>()
|
||||
};
|
||||
|
||||
_settingsService.SaveShellProfile(shellProfile);
|
||||
AddShellProfile(shellProfile);
|
||||
}
|
||||
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService);
|
||||
private void Clone(ShellProfileViewModel shellProfile)
|
||||
{
|
||||
var cloned = new ShellProfile(shellProfile.Model)
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = $"Copy of {shellProfile.Name}"
|
||||
};
|
||||
|
||||
AddShellProfile(cloned);
|
||||
}
|
||||
|
||||
private void AddShellProfile(ShellProfile shellProfile)
|
||||
{
|
||||
_settingsService.SaveShellProfile(shellProfile, true);
|
||||
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService, _applicationView, _defaultValueProvider, true);
|
||||
viewModel.EditCommand.Execute(null);
|
||||
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
||||
viewModel.Deleted += OnShellProfileDeleted;
|
||||
@@ -1,16 +1,17 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class TerminalPageViewModel : ViewModelBase
|
||||
public class TerminalPageViewModel : ReactiveObject
|
||||
{
|
||||
private readonly TerminalOptions _terminalOptions;
|
||||
private readonly ISettingsService _settingsService;
|
||||
@@ -64,7 +65,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
_terminalOptions.CursorBlink = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,7 +79,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
_terminalOptions.FontFamily = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,8 +92,60 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
if (_terminalOptions.BackgroundOpacity != value)
|
||||
{
|
||||
_terminalOptions.BackgroundOpacity = value;
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Padding
|
||||
{
|
||||
get => _terminalOptions.Padding;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.Padding != value)
|
||||
{
|
||||
_terminalOptions.Padding = value;
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ScrollBackLimit
|
||||
{
|
||||
get => _terminalOptions.ScrollBackLimit.ToString();
|
||||
set
|
||||
{
|
||||
var oldValue = _terminalOptions.ScrollBackLimit;
|
||||
if (uint.TryParse(value, out uint intValue))
|
||||
{
|
||||
if (intValue == uint.MinValue)
|
||||
{
|
||||
intValue = uint.MaxValue;
|
||||
}
|
||||
if (_terminalOptions.ScrollBackLimit != intValue)
|
||||
{
|
||||
_terminalOptions.ScrollBackLimit = intValue;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool BoldText
|
||||
{
|
||||
get => _terminalOptions.BoldText;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.BoldText != value)
|
||||
{
|
||||
_terminalOptions.BoldText = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +161,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
_terminalOptions.FontSize = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,10 +180,10 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
_isEditingCursorStyle = true;
|
||||
_terminalOptions.CursorStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(BlockIsSelected));
|
||||
RaisePropertyChanged(nameof(BarIsSelected));
|
||||
RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||
this.RaisePropertyChanged();
|
||||
this.RaisePropertyChanged(nameof(BlockIsSelected));
|
||||
this.RaisePropertyChanged(nameof(BarIsSelected));
|
||||
this.RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||
_isEditingCursorStyle = false;
|
||||
}
|
||||
}
|
||||
@@ -146,10 +199,10 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
_isEditingScrollBarStyle = true;
|
||||
_terminalOptions.ScrollBarStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(HiddenIsSelected));
|
||||
RaisePropertyChanged(nameof(AutoHidingIsSelected));
|
||||
RaisePropertyChanged(nameof(VisibleIsSelected));
|
||||
this.RaisePropertyChanged();
|
||||
this.RaisePropertyChanged(nameof(HiddenIsSelected));
|
||||
this.RaisePropertyChanged(nameof(AutoHidingIsSelected));
|
||||
this.RaisePropertyChanged(nameof(VisibleIsSelected));
|
||||
_isEditingScrollBarStyle = false;
|
||||
}
|
||||
}
|
||||
@@ -167,11 +220,13 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
ScrollBarStyle = defaults.ScrollBarStyle;
|
||||
FontFamily = defaults.FontFamily;
|
||||
FontSize = defaults.FontSize;
|
||||
BoldText = defaults.BoldText;
|
||||
BackgroundOpacity = defaults.BackgroundOpacity;
|
||||
ScrollBackLimit = defaults.ScrollBackLimit.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
|
||||
public TerminalPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ISystemFontService systemFontService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
@@ -179,10 +234,19 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
|
||||
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||
|
||||
Fonts = CanvasTextFormat.GetSystemFontFamilies().OrderBy(s => s);
|
||||
Fonts = systemFontService.GetSystemFontFamilies().OrderBy(s => s);
|
||||
Sizes = Enumerable.Range(1, 72);
|
||||
|
||||
_terminalOptions = _settingsService.GetTerminalOptions();
|
||||
|
||||
this.ObservableForProperty(x => x.BackgroundOpacity).Throttle(TimeSpan.FromMilliseconds(800)).ObserveOn(RxApp.MainThreadScheduler).Subscribe(x =>
|
||||
{
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
});
|
||||
this.ObservableForProperty(x => x.Padding).Throttle(TimeSpan.FromMilliseconds(800)).ObserveOn(RxApp.MainThreadScheduler).Subscribe(x =>
|
||||
{
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,23 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
private readonly ISettingsService _settingsService;
|
||||
private ThemeViewModel _selectedTheme;
|
||||
private double _backgroundOpacity;
|
||||
private readonly IThemeParserFactory _themeParserFactory;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
|
||||
public ThemesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
|
||||
public event EventHandler<string> SelectedThemeBackgroundColorChanged;
|
||||
|
||||
public ThemesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider,
|
||||
IThemeParserFactory themeParserFactory, IFileSystemService fileSystemService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_themeParserFactory = themeParserFactory;
|
||||
_fileSystemService = fileSystemService;
|
||||
|
||||
CreateThemeCommand = new RelayCommand(CreateTheme);
|
||||
ImportThemeCommand = new RelayCommand(ImportTheme);
|
||||
CloneCommand = new RelayCommand<ThemeViewModel>(CloneTheme);
|
||||
|
||||
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
|
||||
@@ -31,7 +40,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
var activeThemeId = _settingsService.GetCurrentThemeId();
|
||||
foreach (var theme in _settingsService.GetThemes())
|
||||
{
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService);
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, fileSystemService, false);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
viewModel.Deleted += OnThemeDeleted;
|
||||
|
||||
@@ -45,12 +54,9 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
SelectedTheme = Themes.First(t => t.IsActive);
|
||||
}
|
||||
|
||||
private void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||
{
|
||||
BackgroundOpacity = e.BackgroundOpacity;
|
||||
}
|
||||
|
||||
public RelayCommand CreateThemeCommand { get; }
|
||||
public RelayCommand ImportThemeCommand { get; }
|
||||
public RelayCommand<ThemeViewModel> CloneCommand { get; set; }
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
@@ -61,11 +67,34 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
public ThemeViewModel SelectedTheme
|
||||
{
|
||||
get => _selectedTheme;
|
||||
set => Set(ref _selectedTheme, value);
|
||||
set
|
||||
{
|
||||
if (_selectedTheme != null)
|
||||
{
|
||||
_selectedTheme.BackgroundChanged -= OnSelectedThemeBackgroundChanged;
|
||||
}
|
||||
Set(ref _selectedTheme, value);
|
||||
if (value != null)
|
||||
{
|
||||
value.BackgroundChanged += OnSelectedThemeBackgroundChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ThemeViewModel> Themes { get; } = new ObservableCollection<ThemeViewModel>();
|
||||
|
||||
private void CloneTheme(ThemeViewModel theme)
|
||||
{
|
||||
var cloned = new TerminalTheme(theme.Model)
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = $"Copy of {theme.Name}"
|
||||
};
|
||||
|
||||
AddTheme(cloned);
|
||||
}
|
||||
|
||||
private void CreateTheme()
|
||||
{
|
||||
var defaultTheme = _settingsService.GetTheme(_defaultValueProvider.GetDefaultThemeId());
|
||||
@@ -77,9 +106,41 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
Colors = new TerminalColors(defaultTheme.Colors)
|
||||
};
|
||||
|
||||
_settingsService.SaveTheme(theme);
|
||||
AddTheme(theme);
|
||||
}
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService);
|
||||
private async void ImportTheme()
|
||||
{
|
||||
var file = await _fileSystemService.OpenFile(_themeParserFactory.SupportedFileTypes).ConfigureAwait(true);
|
||||
if (file != null)
|
||||
{
|
||||
var parser = _themeParserFactory.GetParser(file.FileType);
|
||||
|
||||
if (parser == null)
|
||||
{
|
||||
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", "No suitable parser found", DialogButton.OK).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var theme = await parser.Parse(file.Name, file.Content).ConfigureAwait(true);
|
||||
|
||||
AddTheme(theme);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", exception.Message, DialogButton.OK).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTheme(TerminalTheme theme)
|
||||
{
|
||||
_settingsService.SaveTheme(theme, true);
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService, true);
|
||||
viewModel.EditCommand.Execute(null);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
viewModel.Deleted += OnThemeDeleted;
|
||||
Themes.Add(viewModel);
|
||||
@@ -117,5 +178,15 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
_settingsService.DeleteTheme(theme.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||
{
|
||||
BackgroundOpacity = e.BackgroundOpacity;
|
||||
}
|
||||
|
||||
private void OnSelectedThemeBackgroundChanged(object sender, string e)
|
||||
{
|
||||
SelectedThemeBackgroundColorChanged?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,22 +7,34 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService, ITrayProcessCommunicationService trayProcessCommunicationService)
|
||||
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService,
|
||||
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService,
|
||||
IFileSystemService fileSystemService, IStartupTaskService startupTaskService, IUpdateService updateService, IApplicationView applicationView)
|
||||
{
|
||||
About = new AboutPageViewModel(settingsService, updateService, applicationView);
|
||||
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService);
|
||||
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService);
|
||||
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService, applicationView);
|
||||
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService);
|
||||
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService);
|
||||
Mouse = new MousePageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||
}
|
||||
|
||||
public event EventHandler Closed;
|
||||
public event EventHandler AboutPageRequested;
|
||||
|
||||
public GeneralPageViewModel General { get; }
|
||||
public KeyBindingsPageViewModel KeyBindings { get; }
|
||||
public ProfilesPageViewModel Shell { get; }
|
||||
public TerminalPageViewModel Terminal { get; }
|
||||
public ThemesPageViewModel Themes { get; }
|
||||
public MousePageViewModel Mouse { get; }
|
||||
public AboutPageViewModel About { get; }
|
||||
|
||||
public void NavigateToAboutPage()
|
||||
{
|
||||
AboutPageRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
@@ -0,0 +1,382 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
using FluentTerminal.App.ViewModels.Settings;
|
||||
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;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class ShellProfileViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private string _arguments;
|
||||
private ShellProfile _fallbackProfile;
|
||||
private bool _inEditMode;
|
||||
private bool _isDefault;
|
||||
private string _location;
|
||||
private LineEndingStyle _lineEndingStyle;
|
||||
private string _name;
|
||||
private bool _isNew;
|
||||
private TabTheme _selectedTabTheme;
|
||||
private TerminalTheme _selectedTerminalTheme;
|
||||
private string _workingDirectory;
|
||||
private readonly IApplicationView _applicationView;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private bool _editingLineEndingStyle;
|
||||
|
||||
public ShellProfileViewModel(ShellProfile shellProfile, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService, IApplicationView applicationView, IDefaultValueProvider defaultValueProvider, Boolean isNew)
|
||||
{
|
||||
Model = shellProfile;
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_fileSystemService = fileSystemService;
|
||||
_applicationView = applicationView;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_isNew = isNew;
|
||||
|
||||
_settingsService.ThemeAdded += OnThemeAdded;
|
||||
_settingsService.ThemeDeleted += OnThemeDeleted;
|
||||
|
||||
TabThemes = new ObservableCollection<TabTheme>(settingsService.GetTabThemes());
|
||||
|
||||
TerminalThemes = new ObservableCollection<TerminalTheme>();
|
||||
TerminalThemes.Add(new TerminalTheme
|
||||
{
|
||||
Id = Guid.Empty,
|
||||
Name = "Default"
|
||||
});
|
||||
foreach (var theme in _settingsService.GetThemes())
|
||||
{
|
||||
TerminalThemes.Add(theme);
|
||||
}
|
||||
|
||||
KeyBindings = new KeyBindingsViewModel(shellProfile.Id.ToString(), _dialogService, string.Empty, false);
|
||||
|
||||
InitializeViewModelProperties(shellProfile);
|
||||
|
||||
SetDefaultCommand = new RelayCommand(SetDefault);
|
||||
DeleteCommand = new AsyncCommand(Delete, CanDelete);
|
||||
EditCommand = new RelayCommand(Edit);
|
||||
CancelEditCommand = new AsyncCommand(CancelEdit);
|
||||
SaveChangesCommand = new RelayCommand(SaveChanges);
|
||||
AddKeyboardShortcutCommand = new AsyncCommand(AddKeyboardShortcut);
|
||||
BrowseForCustomShellCommand = new AsyncCommand(BrowseForCustomShell);
|
||||
BrowseForWorkingDirectoryCommand = new AsyncCommand(BrowseForWorkingDirectory);
|
||||
RestoreDefaultsCommand = new AsyncCommand(RestoreDefaults);
|
||||
}
|
||||
|
||||
private void InitializeViewModelProperties(ShellProfile shellProfile)
|
||||
{
|
||||
SelectedTerminalTheme = TerminalThemes.FirstOrDefault(t => t.Id == shellProfile.TerminalThemeId);
|
||||
Id = shellProfile.Id;
|
||||
Name = shellProfile.Name;
|
||||
Arguments = shellProfile.Arguments;
|
||||
Location = shellProfile.Location;
|
||||
WorkingDirectory = shellProfile.WorkingDirectory;
|
||||
SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == shellProfile.TabThemeId);
|
||||
PreInstalled = shellProfile.PreInstalled;
|
||||
LineEndingStyle = shellProfile.LineEndingTranslation;
|
||||
|
||||
KeyBindings.Clear();
|
||||
foreach (var keyBinding in shellProfile.KeyBindings.Select(x => new KeyBinding(x)).ToList())
|
||||
{
|
||||
KeyBindings.Add(keyBinding);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThemeAdded(object sender, TerminalTheme e)
|
||||
{
|
||||
_applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
TerminalThemes.Add(e);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnThemeDeleted(object sender, Guid e)
|
||||
{
|
||||
if (SelectedTerminalTheme.Id == e)
|
||||
{
|
||||
_applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
SelectedTerminalTheme = TerminalThemes.FirstOrDefault(x => x.Id == Guid.Empty);
|
||||
Model.TerminalThemeId = Guid.Empty;
|
||||
if (_fallbackProfile != null)
|
||||
{
|
||||
_fallbackProfile.TerminalThemeId = Guid.Empty;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler Deleted;
|
||||
public event EventHandler SetAsDefault;
|
||||
|
||||
public IAsyncCommand BrowseForCustomShellCommand { get; }
|
||||
public IAsyncCommand BrowseForWorkingDirectoryCommand { get; }
|
||||
public IAsyncCommand CancelEditCommand { get; }
|
||||
public IAsyncCommand DeleteCommand { get; }
|
||||
public RelayCommand EditCommand { get; }
|
||||
public RelayCommand SaveChangesCommand { get; }
|
||||
public RelayCommand SetDefaultCommand { get; }
|
||||
public IAsyncCommand RestoreDefaultsCommand { get; }
|
||||
public IAsyncCommand AddKeyboardShortcutCommand { get; }
|
||||
public ObservableCollection<TabTheme> TabThemes { get; }
|
||||
public KeyBindingsViewModel KeyBindings { get; }
|
||||
|
||||
public ObservableCollection<TerminalTheme> TerminalThemes { get; }
|
||||
public ShellProfile Model { get; private set; }
|
||||
public bool PreInstalled { get; private set; }
|
||||
|
||||
public string Arguments
|
||||
{
|
||||
get => _arguments;
|
||||
set => Set(ref _arguments, value);
|
||||
}
|
||||
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
public bool InEditMode
|
||||
{
|
||||
get => _inEditMode;
|
||||
set => Set(ref _inEditMode, value);
|
||||
}
|
||||
|
||||
public bool IsDefault
|
||||
{
|
||||
get => _isDefault;
|
||||
set => Set(ref _isDefault, value);
|
||||
}
|
||||
|
||||
public string Location
|
||||
{
|
||||
get => _location;
|
||||
set => Set(ref _location, value);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => Set(ref _name, value);
|
||||
}
|
||||
|
||||
public TabTheme SelectedTabTheme
|
||||
{
|
||||
get => _selectedTabTheme;
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
Set(ref _selectedTabTheme, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalTheme SelectedTerminalTheme
|
||||
{
|
||||
get => _selectedTerminalTheme;
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
Set(ref _selectedTerminalTheme, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get => _workingDirectory;
|
||||
set => Set(ref _workingDirectory, value);
|
||||
}
|
||||
|
||||
public LineEndingStyle LineEndingStyle
|
||||
{
|
||||
get => _lineEndingStyle;
|
||||
set
|
||||
{
|
||||
if (_lineEndingStyle != value && !_editingLineEndingStyle)
|
||||
{
|
||||
_editingLineEndingStyle = true;
|
||||
_lineEndingStyle = value;
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(DoNotModifyIsSelected));
|
||||
RaisePropertyChanged(nameof(ToCRLFIsSelected));
|
||||
RaisePropertyChanged(nameof(ToLFIsSelected));
|
||||
_editingLineEndingStyle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool DoNotModifyIsSelected
|
||||
{
|
||||
get => LineEndingStyle == LineEndingStyle.DoNotModify;
|
||||
set => LineEndingStyle = LineEndingStyle.DoNotModify;
|
||||
}
|
||||
|
||||
public bool ToCRLFIsSelected
|
||||
{
|
||||
get => LineEndingStyle == LineEndingStyle.ToCRLF;
|
||||
set => LineEndingStyle = LineEndingStyle.ToCRLF;
|
||||
}
|
||||
|
||||
public bool ToLFIsSelected
|
||||
{
|
||||
get => LineEndingStyle == LineEndingStyle.ToLF;
|
||||
set => LineEndingStyle = LineEndingStyle.ToLF;
|
||||
}
|
||||
|
||||
public void SaveChanges()
|
||||
{
|
||||
Model.Arguments = Arguments;
|
||||
Model.Location = Location;
|
||||
Model.Name = Name;
|
||||
Model.WorkingDirectory = WorkingDirectory;
|
||||
Model.TabThemeId = SelectedTabTheme.Id;
|
||||
Model.TerminalThemeId = SelectedTerminalTheme.Id;
|
||||
Model.LineEndingTranslation = _lineEndingStyle;
|
||||
Model.KeyBindings = KeyBindings.KeyBindings.Select(x => x.Model).ToList();
|
||||
_settingsService.SaveShellProfile(Model);
|
||||
|
||||
KeyBindings.Editable = false;
|
||||
InEditMode = false;
|
||||
_isNew = false;
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
if (InEditMode || !PreInstalled)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore this profile?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Model = _defaultValueProvider.GetPreinstalledShellProfiles().FirstOrDefault(x => x.Id == Model.Id);
|
||||
InitializeViewModelProperties(Model);
|
||||
_settingsService.SaveShellProfile(Model);
|
||||
}
|
||||
}
|
||||
|
||||
public Task AddKeyboardShortcut()
|
||||
{
|
||||
return KeyBindings.ShowAddKeyBindingDialog();
|
||||
}
|
||||
|
||||
private async Task BrowseForCustomShell()
|
||||
{
|
||||
var file = await _fileSystemService.OpenFile(new[] { ".exe" }).ConfigureAwait(true);
|
||||
if (file != null)
|
||||
{
|
||||
Location = file.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseForWorkingDirectory()
|
||||
{
|
||||
var directory = await _fileSystemService.BrowseForDirectory().ConfigureAwait(true);
|
||||
if (directory != null)
|
||||
{
|
||||
WorkingDirectory = directory;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CancelEdit()
|
||||
{
|
||||
if (_isNew)
|
||||
{
|
||||
await Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShellProfile changedProfile = new ShellProfile
|
||||
{
|
||||
Arguments = Arguments,
|
||||
Location = Location,
|
||||
Name = Name,
|
||||
WorkingDirectory = WorkingDirectory,
|
||||
TabThemeId = SelectedTabTheme.Id,
|
||||
TerminalThemeId = SelectedTerminalTheme.Id,
|
||||
LineEndingTranslation = _lineEndingStyle,
|
||||
KeyBindings = KeyBindings.KeyBindings.Select(x => x.Model).ToList()
|
||||
};
|
||||
|
||||
if (!_fallbackProfile.Equals(changedProfile))
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to discard all changes?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Arguments = _fallbackProfile.Arguments;
|
||||
Location = _fallbackProfile.Location;
|
||||
Name = _fallbackProfile.Name;
|
||||
WorkingDirectory = _fallbackProfile.WorkingDirectory;
|
||||
LineEndingStyle = _fallbackProfile.LineEndingTranslation;
|
||||
SelectedTerminalTheme = TerminalThemes.FirstOrDefault(t => t.Id == _fallbackProfile.TerminalThemeId);
|
||||
SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == _fallbackProfile.TabThemeId);
|
||||
|
||||
KeyBindings.KeyBindings.Clear();
|
||||
foreach (var keyBinding in Model.KeyBindings.Select(x => new KeyBinding(x)).ToList())
|
||||
{
|
||||
KeyBindings.Add(keyBinding);
|
||||
}
|
||||
|
||||
KeyBindings.Editable = false;
|
||||
InEditMode = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyBindings.Editable = false;
|
||||
InEditMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanDelete()
|
||||
{
|
||||
return !Model.PreInstalled;
|
||||
}
|
||||
|
||||
private async Task Delete()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this theme?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Deleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void Edit()
|
||||
{
|
||||
// todo write copy ctor
|
||||
_fallbackProfile = new ShellProfile
|
||||
{
|
||||
Arguments = Model.Arguments,
|
||||
Location = Model.Location,
|
||||
Name = Model.Name,
|
||||
WorkingDirectory = Model.WorkingDirectory,
|
||||
TabThemeId = Model.TabThemeId,
|
||||
TerminalThemeId = Model.TerminalThemeId
|
||||
};
|
||||
|
||||
KeyBindings.Editable = true;
|
||||
InEditMode = true;
|
||||
}
|
||||
|
||||
private void SetDefault()
|
||||
{
|
||||
SetAsDefault?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
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;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class TerminalViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IKeyboardCommandService _keyboardCommandService;
|
||||
private readonly IDispatcherTimer _resizeOverlayTimer;
|
||||
private bool _isSelected;
|
||||
private bool _newOutput;
|
||||
private string _resizeOverlayContent;
|
||||
private string _searchText;
|
||||
private bool _showResizeOverlay;
|
||||
private bool _showSearchPanel;
|
||||
private TabTheme _tabTheme;
|
||||
private TerminalTheme _terminalTheme;
|
||||
private string _title;
|
||||
|
||||
public TerminalViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService,
|
||||
IKeyboardCommandService keyboardCommandService, ApplicationSettings applicationSettings, string startupDirectory, ShellProfile shellProfile,
|
||||
IApplicationView applicationView, IDispatcherTimer dispatcherTimer, IClipboardService clipboardService)
|
||||
{
|
||||
SettingsService = settingsService;
|
||||
SettingsService.CurrentThemeChanged += OnCurrentThemeChanged;
|
||||
SettingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
SettingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
SettingsService.KeyBindingsChanged += OnKeyBindingsChanged;
|
||||
|
||||
TrayProcessCommunicationService = trayProcessCommunicationService;
|
||||
|
||||
DialogService = dialogService;
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
ApplicationSettings = applicationSettings;
|
||||
StartupDirectory = startupDirectory;
|
||||
ApplicationView = applicationView;
|
||||
ClipboardService = clipboardService;
|
||||
|
||||
ShellProfile = shellProfile;
|
||||
TerminalTheme = shellProfile.TerminalThemeId == Guid.Empty ? SettingsService.GetCurrentTheme() : SettingsService.GetTheme(shellProfile.TerminalThemeId);
|
||||
|
||||
TabThemes = new ObservableCollection<TabTheme>(SettingsService.GetTabThemes());
|
||||
TabTheme = TabThemes.FirstOrDefault(t => t.Id == ShellProfile.TabThemeId);
|
||||
|
||||
_resizeOverlayTimer = dispatcherTimer;
|
||||
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
|
||||
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
|
||||
|
||||
CloseCommand = new RelayCommand(async () => await TryClose().ConfigureAwait(false));
|
||||
FindNextCommand = new RelayCommand(FindNext);
|
||||
FindPreviousCommand = new RelayCommand(FindPrevious);
|
||||
CloseSearchPanelCommand = new RelayCommand(CloseSearchPanel);
|
||||
SelectTabThemeCommand = new RelayCommand<string>(SelectTabTheme);
|
||||
EditTitleCommand = new AsyncCommand(EditTitle);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(StartupDirectory))
|
||||
{
|
||||
ShellProfile.WorkingDirectory = StartupDirectory;
|
||||
}
|
||||
|
||||
Terminal = new Terminal(TrayProcessCommunicationService);
|
||||
Terminal.KeyboardCommandReceived += Terminal_KeyboardCommandReceived;
|
||||
Terminal.OutputReceived += Terminal_OutputReceived;
|
||||
Terminal.SizeChanged += Terminal_SizeChanged;
|
||||
Terminal.TitleChanged += Terminal_TitleChanged;
|
||||
Terminal.Closed += Terminal_Closed;
|
||||
}
|
||||
|
||||
public event EventHandler Activated;
|
||||
public event EventHandler Closed;
|
||||
public event EventHandler<string> FindNextRequested;
|
||||
public event EventHandler<string> FindPreviousRequested;
|
||||
public event EventHandler KeyBindingsChanged;
|
||||
public event EventHandler<TerminalOptions> OptionsChanged;
|
||||
public event EventHandler SearchStarted;
|
||||
public event EventHandler<TerminalTheme> ThemeChanged;
|
||||
|
||||
public ApplicationSettings ApplicationSettings { get; private set; }
|
||||
|
||||
public IApplicationView ApplicationView { get; }
|
||||
|
||||
public TabTheme BackgroundTabTheme
|
||||
{
|
||||
// The effective background theme depends on whether it is selected (use the theme), or if it is inactive
|
||||
// (if we're set to underline inactive tabs, use the null theme).
|
||||
get => IsSelected || (!IsSelected && ApplicationSettings.InactiveTabColorMode == InactiveTabColorMode.Background) ?
|
||||
_tabTheme :
|
||||
SettingsService.GetTabThemes().FirstOrDefault(t => t.Color == null);
|
||||
}
|
||||
|
||||
public IClipboardService ClipboardService { get; }
|
||||
|
||||
public RelayCommand CloseCommand { get; }
|
||||
|
||||
public RelayCommand CloseSearchPanelCommand { get; }
|
||||
|
||||
public IDialogService DialogService { get; }
|
||||
|
||||
public IAsyncCommand EditTitleCommand { get; }
|
||||
|
||||
public RelayCommand FindNextCommand { get; }
|
||||
|
||||
public RelayCommand FindPreviousCommand { get; }
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set
|
||||
{
|
||||
if (Set(ref _isSelected, value))
|
||||
{
|
||||
if (IsSelected)
|
||||
{
|
||||
ApplicationView.Title = Title ?? string.Empty;
|
||||
NewOutput = false;
|
||||
}
|
||||
RaisePropertyChanged(nameof(IsUnderlined));
|
||||
RaisePropertyChanged(nameof(BackgroundTabTheme));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUnderlined => (IsSelected && ApplicationSettings.UnderlineSelectedTab) ||
|
||||
(!IsSelected && ApplicationSettings.InactiveTabColorMode == InactiveTabColorMode.Underlined && TabTheme.Color != null);
|
||||
|
||||
public bool NewOutput
|
||||
{
|
||||
get => _newOutput;
|
||||
set => Set(ref _newOutput, value);
|
||||
}
|
||||
|
||||
public string ResizeOverlayContent
|
||||
{
|
||||
get => _resizeOverlayContent;
|
||||
set => Set(ref _resizeOverlayContent, value);
|
||||
}
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set => Set(ref _searchText, value);
|
||||
}
|
||||
|
||||
public RelayCommand<string> SelectTabThemeCommand { get; }
|
||||
|
||||
public ISettingsService SettingsService { get; }
|
||||
|
||||
public ShellProfile ShellProfile { get; }
|
||||
|
||||
public bool ShowResizeOverlay
|
||||
{
|
||||
get => _showResizeOverlay;
|
||||
set
|
||||
{
|
||||
Set(ref _showResizeOverlay, value);
|
||||
if (value)
|
||||
{
|
||||
if (_resizeOverlayTimer.IsEnabled)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
}
|
||||
_resizeOverlayTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowSearchPanel
|
||||
{
|
||||
get => _showSearchPanel;
|
||||
set => Set(ref _showSearchPanel, value);
|
||||
}
|
||||
|
||||
public string StartupDirectory { get; }
|
||||
|
||||
public TabTheme TabTheme
|
||||
{
|
||||
get => _tabTheme;
|
||||
set
|
||||
{
|
||||
Set(ref _tabTheme, value);
|
||||
RaisePropertyChanged(nameof(IsUnderlined));
|
||||
RaisePropertyChanged(nameof(BackgroundTabTheme));
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<TabTheme> TabThemes { get; }
|
||||
|
||||
public Terminal Terminal { get; private set; }
|
||||
|
||||
public TerminalTheme TerminalTheme
|
||||
{
|
||||
get => _terminalTheme;
|
||||
set => Set(ref _terminalTheme, value);
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
if (Set(ref _title, value) && IsSelected)
|
||||
{
|
||||
ApplicationView.Title = Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ITrayProcessCommunicationService TrayProcessCommunicationService { get; }
|
||||
|
||||
public Task Close()
|
||||
{
|
||||
return Terminal.Close();
|
||||
}
|
||||
|
||||
public void CopyText(string text)
|
||||
{
|
||||
ClipboardService.SetText(text);
|
||||
}
|
||||
|
||||
public async Task EditTitle()
|
||||
{
|
||||
var result = await DialogService.ShowInputDialogAsync("Edit Title");
|
||||
if (result != null)
|
||||
{
|
||||
Title = result;
|
||||
}
|
||||
}
|
||||
|
||||
public void FocusTerminal()
|
||||
{
|
||||
Activated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void CloseSearchPanel()
|
||||
{
|
||||
SearchText = string.Empty;
|
||||
ShowSearchPanel = false;
|
||||
FocusTerminal();
|
||||
}
|
||||
|
||||
private void FindNext()
|
||||
{
|
||||
FindNextRequested?.Invoke(this, SearchText);
|
||||
}
|
||||
|
||||
private void FindPrevious()
|
||||
{
|
||||
FindPreviousRequested?.Invoke(this, SearchText);
|
||||
}
|
||||
|
||||
private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
|
||||
{
|
||||
await ApplicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
ApplicationSettings = e;
|
||||
RaisePropertyChanged(nameof(IsUnderlined));
|
||||
RaisePropertyChanged(nameof(BackgroundTabTheme));
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnCurrentThemeChanged(object sender, Guid e)
|
||||
{
|
||||
await ApplicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
// only change theme if not overwritten by profile
|
||||
if (ShellProfile.TerminalThemeId == Guid.Empty)
|
||||
{
|
||||
var currentTheme = SettingsService.GetTheme(e);
|
||||
TerminalTheme = currentTheme;
|
||||
ThemeChanged?.Invoke(this, currentTheme);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void OnKeyBindingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
KeyBindingsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnResizeOverlayTimerFinished(object sender, object e)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
ShowResizeOverlay = false;
|
||||
}
|
||||
|
||||
private void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||
{
|
||||
OptionsChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void SelectTabTheme(string name)
|
||||
{
|
||||
TabTheme = TabThemes.FirstOrDefault(t => t.Name == name);
|
||||
}
|
||||
|
||||
private void Terminal_Closed(object sender, EventArgs e)
|
||||
{
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async void Terminal_KeyboardCommandReceived(object sender, string e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case nameof(Command.Copy):
|
||||
{
|
||||
var selection = await Terminal.GetSelectedText().ConfigureAwait(true);
|
||||
ClipboardService.SetText(selection);
|
||||
break;
|
||||
}
|
||||
case nameof(Command.Paste):
|
||||
{
|
||||
string content = await ClipboardService.GetText().ConfigureAwait(true);
|
||||
if (content != null)
|
||||
{
|
||||
content = ShellProfile.TranslateLineEndings(content);
|
||||
await Terminal.Write(content).ConfigureAwait(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nameof(Command.PasteWithoutNewlines):
|
||||
{
|
||||
string content = await ClipboardService.GetText().ConfigureAwait(true);
|
||||
if (content != null)
|
||||
{
|
||||
content = ShellProfile.NewlinePattern.Replace(content, string.Empty);
|
||||
await Terminal.Write(content).ConfigureAwait(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nameof(Command.Search):
|
||||
{
|
||||
ShowSearchPanel = !ShowSearchPanel;
|
||||
if (ShowSearchPanel)
|
||||
{
|
||||
SearchStarted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
_keyboardCommandService.SendCommand(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Terminal_OutputReceived(object sender, string e)
|
||||
{
|
||||
if (!IsSelected && ApplicationSettings.ShowNewOutputIndicator)
|
||||
{
|
||||
NewOutput = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Terminal_SizeChanged(object sender, TerminalSize e)
|
||||
{
|
||||
ResizeOverlayContent = $"{e.Columns} x {e.Rows}";
|
||||
ShowResizeOverlay = true;
|
||||
}
|
||||
|
||||
private void Terminal_TitleChanged(object sender, string e)
|
||||
{
|
||||
Title = e;
|
||||
}
|
||||
|
||||
private async Task TryClose()
|
||||
{
|
||||
if (ApplicationSettings.ConfirmClosingTabs)
|
||||
{
|
||||
var result = await DialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to close this tab?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Close().ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class ThemeViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private string _author;
|
||||
private string _background;
|
||||
private double _backgroundOpacity;
|
||||
private string _black;
|
||||
private string _blue;
|
||||
private string _brightBlack;
|
||||
private string _brightBlue;
|
||||
private string _brightCyan;
|
||||
private string _brightGreen;
|
||||
private string _brightMagenta;
|
||||
private string _brightRed;
|
||||
private string _brightWhite;
|
||||
private string _brightYellow;
|
||||
private string _cursor;
|
||||
private string _cursorAccent;
|
||||
private string _cyan;
|
||||
private TerminalTheme _fallbackTheme;
|
||||
private string _foreground;
|
||||
private string _green;
|
||||
private bool _inEditMode;
|
||||
private bool _isActive;
|
||||
private string _magenta;
|
||||
private string _name;
|
||||
private bool _isNew;
|
||||
private string _red;
|
||||
private string _selection;
|
||||
private string _white;
|
||||
private string _yellow;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
|
||||
public event EventHandler<string> BackgroundChanged;
|
||||
|
||||
public ThemeViewModel(TerminalTheme theme, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService, bool isNew)
|
||||
{
|
||||
Model = theme;
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_fileSystemService = fileSystemService;
|
||||
_isNew = isNew;
|
||||
|
||||
Name = Model.Name;
|
||||
Author = Model.Author;
|
||||
Id = Model.Id;
|
||||
|
||||
Black = Model.Colors.Black;
|
||||
Red = Model.Colors.Red;
|
||||
Green = Model.Colors.Green;
|
||||
Yellow = Model.Colors.Yellow;
|
||||
Blue = Model.Colors.Blue;
|
||||
Magenta = Model.Colors.Magenta;
|
||||
Cyan = Model.Colors.Cyan;
|
||||
White = Model.Colors.White;
|
||||
|
||||
BrightBlack = Model.Colors.BrightBlack;
|
||||
BrightRed = Model.Colors.BrightRed;
|
||||
BrightGreen = Model.Colors.BrightGreen;
|
||||
BrightYellow = Model.Colors.BrightYellow;
|
||||
BrightBlue = Model.Colors.BrightBlue;
|
||||
BrightMagenta = Model.Colors.BrightMagenta;
|
||||
BrightCyan = Model.Colors.BrightCyan;
|
||||
BrightWhite = Model.Colors.BrightWhite;
|
||||
|
||||
Background = Model.Colors.Background;
|
||||
Foreground = Model.Colors.Foreground;
|
||||
Cursor = Model.Colors.Cursor;
|
||||
CursorAccent = Model.Colors.CursorAccent;
|
||||
Selection = Model.Colors.Selection;
|
||||
|
||||
SetActiveCommand = new RelayCommand(SetActive);
|
||||
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false), NotPreInstalled);
|
||||
EditCommand = new RelayCommand(Edit, NotPreInstalled);
|
||||
CancelEditCommand = new RelayCommand(async () => await CancelEdit().ConfigureAwait(false));
|
||||
SaveChangesCommand = new RelayCommand(SaveChanges);
|
||||
ExportCommand = new RelayCommand(async () => await Export().ConfigureAwait(false), NotPreInstalled);
|
||||
}
|
||||
|
||||
public event EventHandler Activated;
|
||||
|
||||
public event EventHandler Deleted;
|
||||
|
||||
public TerminalTheme Model { get; }
|
||||
|
||||
public string Author
|
||||
{
|
||||
get => _author;
|
||||
set => Set(ref _author, value);
|
||||
}
|
||||
|
||||
public string Background
|
||||
{
|
||||
get => _background;
|
||||
set
|
||||
{
|
||||
Set(ref _background, value);
|
||||
BackgroundChanged?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _backgroundOpacity;
|
||||
set => Set(ref _backgroundOpacity, value);
|
||||
}
|
||||
|
||||
public string Black
|
||||
{
|
||||
get => _black;
|
||||
set => Set(ref _black, value);
|
||||
}
|
||||
|
||||
public string Blue
|
||||
{
|
||||
get => _blue;
|
||||
set => Set(ref _blue, value);
|
||||
}
|
||||
|
||||
public string BrightBlack
|
||||
{
|
||||
get => _brightBlack;
|
||||
set => Set(ref _brightBlack, value);
|
||||
}
|
||||
|
||||
public string BrightBlue
|
||||
{
|
||||
get => _brightBlue;
|
||||
set => Set(ref _brightBlue, value);
|
||||
}
|
||||
|
||||
public string BrightCyan
|
||||
{
|
||||
get => _brightCyan;
|
||||
set => Set(ref _brightCyan, value);
|
||||
}
|
||||
|
||||
public string BrightGreen
|
||||
{
|
||||
get => _brightGreen;
|
||||
set => Set(ref _brightGreen, value);
|
||||
}
|
||||
|
||||
public string BrightMagenta
|
||||
{
|
||||
get => _brightMagenta;
|
||||
set => Set(ref _brightMagenta, value);
|
||||
}
|
||||
|
||||
public string BrightRed
|
||||
{
|
||||
get => _brightRed;
|
||||
set => Set(ref _brightRed, value);
|
||||
}
|
||||
|
||||
public string BrightWhite
|
||||
{
|
||||
get => _brightWhite;
|
||||
set => Set(ref _brightWhite, value);
|
||||
}
|
||||
|
||||
public string BrightYellow
|
||||
{
|
||||
get => _brightYellow;
|
||||
set => Set(ref _brightYellow, value);
|
||||
}
|
||||
|
||||
public RelayCommand CancelEditCommand { get; }
|
||||
|
||||
public string Cursor
|
||||
{
|
||||
get => _cursor;
|
||||
set => Set(ref _cursor, value);
|
||||
}
|
||||
|
||||
public string CursorAccent
|
||||
{
|
||||
get => _cursorAccent;
|
||||
set => Set(ref _cursorAccent, value);
|
||||
}
|
||||
|
||||
public string Cyan
|
||||
{
|
||||
get => _cyan;
|
||||
set => Set(ref _cyan, value);
|
||||
}
|
||||
|
||||
public RelayCommand DeleteCommand { get; }
|
||||
public RelayCommand EditCommand { get; }
|
||||
public RelayCommand ExportCommand { get; }
|
||||
|
||||
public string Foreground
|
||||
{
|
||||
get => _foreground;
|
||||
set => Set(ref _foreground, value);
|
||||
}
|
||||
|
||||
public string Green
|
||||
{
|
||||
get => _green;
|
||||
set => Set(ref _green, value);
|
||||
}
|
||||
|
||||
public Guid Id { get; }
|
||||
|
||||
public bool InEditMode
|
||||
{
|
||||
get => _inEditMode;
|
||||
set => Set(ref _inEditMode, value);
|
||||
}
|
||||
|
||||
public bool IsActive
|
||||
{
|
||||
get => _isActive;
|
||||
set => Set(ref _isActive, value);
|
||||
}
|
||||
|
||||
public string Magenta
|
||||
{
|
||||
get => _magenta;
|
||||
set => Set(ref _magenta, value);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => Set(ref _name, value);
|
||||
}
|
||||
|
||||
public string Red
|
||||
{
|
||||
get => _red;
|
||||
set => Set(ref _red, value);
|
||||
}
|
||||
|
||||
public RelayCommand SaveChangesCommand { get; }
|
||||
|
||||
public string Selection
|
||||
{
|
||||
get => _selection;
|
||||
set => Set(ref _selection, value);
|
||||
}
|
||||
|
||||
public RelayCommand SetActiveCommand { get; }
|
||||
|
||||
public string White
|
||||
{
|
||||
get => _white;
|
||||
set => Set(ref _white, value);
|
||||
}
|
||||
|
||||
public string Yellow
|
||||
{
|
||||
get => _yellow;
|
||||
set => Set(ref _yellow, value);
|
||||
}
|
||||
|
||||
public void SaveChanges()
|
||||
{
|
||||
Model.Name = Name;
|
||||
Model.Author = Author;
|
||||
|
||||
Model.Colors.Black = Black;
|
||||
Model.Colors.Red = Red;
|
||||
Model.Colors.Green = Green;
|
||||
Model.Colors.Yellow = Yellow;
|
||||
Model.Colors.Blue = Blue;
|
||||
Model.Colors.Magenta = Magenta;
|
||||
Model.Colors.Cyan = Cyan;
|
||||
Model.Colors.White = White;
|
||||
|
||||
Model.Colors.BrightBlack = BrightBlack;
|
||||
Model.Colors.BrightRed = BrightRed;
|
||||
Model.Colors.BrightGreen = BrightGreen;
|
||||
Model.Colors.BrightYellow = BrightYellow;
|
||||
Model.Colors.BrightBlue = BrightBlue;
|
||||
Model.Colors.BrightMagenta = BrightMagenta;
|
||||
Model.Colors.BrightCyan = BrightCyan;
|
||||
Model.Colors.BrightWhite = BrightWhite;
|
||||
|
||||
Model.Colors.Background = Background;
|
||||
Model.Colors.Foreground = Foreground;
|
||||
Model.Colors.Cursor = Cursor;
|
||||
Model.Colors.CursorAccent = CursorAccent;
|
||||
Model.Colors.Selection = Selection;
|
||||
|
||||
_settingsService.SaveTheme(Model);
|
||||
|
||||
InEditMode = false;
|
||||
_isNew = false;
|
||||
}
|
||||
|
||||
private async Task CancelEdit()
|
||||
{
|
||||
if (_isNew)
|
||||
{
|
||||
await Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
TerminalTheme changedTheme = new TerminalTheme()
|
||||
{
|
||||
Name = Name,
|
||||
Author = Author,
|
||||
Colors = new TerminalColors()
|
||||
{
|
||||
Black = Black,
|
||||
Red = Red,
|
||||
Green = Green,
|
||||
Yellow = Yellow,
|
||||
Blue = Blue,
|
||||
Magenta = Magenta,
|
||||
Cyan = Cyan,
|
||||
White = White,
|
||||
|
||||
BrightBlack = BrightBlack,
|
||||
BrightRed = BrightRed,
|
||||
BrightGreen = BrightGreen,
|
||||
BrightYellow = BrightYellow,
|
||||
BrightBlue = BrightBlue,
|
||||
BrightMagenta = BrightMagenta,
|
||||
BrightCyan = BrightCyan,
|
||||
BrightWhite = BrightWhite,
|
||||
|
||||
Background = Background,
|
||||
Foreground = Foreground,
|
||||
Cursor = Cursor,
|
||||
CursorAccent = CursorAccent,
|
||||
Selection = Selection
|
||||
}
|
||||
};
|
||||
|
||||
if (!_fallbackTheme.Equals(changedTheme))
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to discard all changes?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Black = _fallbackTheme.Colors.Black;
|
||||
Red = _fallbackTheme.Colors.Red;
|
||||
Green = _fallbackTheme.Colors.Green;
|
||||
Yellow = _fallbackTheme.Colors.Yellow;
|
||||
Blue = _fallbackTheme.Colors.Blue;
|
||||
Magenta = _fallbackTheme.Colors.Magenta;
|
||||
Cyan = _fallbackTheme.Colors.Cyan;
|
||||
White = _fallbackTheme.Colors.White;
|
||||
|
||||
BrightBlack = _fallbackTheme.Colors.BrightBlack;
|
||||
BrightRed = _fallbackTheme.Colors.BrightRed;
|
||||
BrightGreen = _fallbackTheme.Colors.BrightGreen;
|
||||
BrightYellow = _fallbackTheme.Colors.BrightYellow;
|
||||
BrightBlue = _fallbackTheme.Colors.BrightBlue;
|
||||
BrightMagenta = _fallbackTheme.Colors.BrightMagenta;
|
||||
BrightCyan = _fallbackTheme.Colors.BrightCyan;
|
||||
BrightWhite = _fallbackTheme.Colors.BrightWhite;
|
||||
|
||||
Background = _fallbackTheme.Colors.Background;
|
||||
Foreground = _fallbackTheme.Colors.Foreground;
|
||||
Cursor = _fallbackTheme.Colors.Cursor;
|
||||
CursorAccent = _fallbackTheme.Colors.CursorAccent;
|
||||
Selection = _fallbackTheme.Colors.Selection;
|
||||
|
||||
Name = _fallbackTheme.Name;
|
||||
Author = _fallbackTheme.Author;
|
||||
|
||||
InEditMode = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InEditMode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool NotPreInstalled()
|
||||
{
|
||||
return !Model.PreInstalled;
|
||||
}
|
||||
|
||||
private async Task Delete()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this theme?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Deleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void Edit()
|
||||
{
|
||||
_fallbackTheme = new TerminalTheme(Model);
|
||||
InEditMode = true;
|
||||
}
|
||||
|
||||
private void SetActive()
|
||||
{
|
||||
Activated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private Task Export()
|
||||
{
|
||||
var content = JsonConvert.SerializeObject(Model, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new TerminalThemeContractResolver() });
|
||||
return _fileSystemService.SaveTextFile(Name, "Fluent Terminal Theme", ".flutecolors", content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Utilities
|
||||
{
|
||||
public class InputBuffer
|
||||
{
|
||||
private readonly Timer _timer = new Timer();
|
||||
private readonly Func<string, Task> _writeCallback;
|
||||
private readonly List<string> _buffer = new List<string>();
|
||||
private readonly object _lock = new object();
|
||||
|
||||
public InputBuffer(Func<string, Task> writeCallback)
|
||||
{
|
||||
_timer.AutoReset = true;
|
||||
_timer.Elapsed += _timer_Elapsed;
|
||||
_writeCallback = writeCallback;
|
||||
_timer.Interval = 5;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var data = string.Concat(_buffer);
|
||||
_buffer.Clear();
|
||||
_writeCallback.Invoke(data);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(string data)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_buffer.Add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Utilities
|
||||
{
|
||||
public static class TaskUtilities
|
||||
{
|
||||
#pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void
|
||||
public static async void FireAndForgetSafeAsync(this Task task, IErrorHandler handler = null)
|
||||
#pragma warning restore RECS0165 // Asynchronous methods should return a Task instead of void
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
handler?.HandleError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class AppServiceConnectionAdapter : IAppServiceConnection
|
||||
{
|
||||
private readonly AppServiceConnection _appServiceConnection;
|
||||
|
||||
public event EventHandler<IDictionary<string, string>> MessageReceived;
|
||||
|
||||
public AppServiceConnectionAdapter(AppServiceConnection appServiceConnection)
|
||||
{
|
||||
_appServiceConnection = appServiceConnection;
|
||||
_appServiceConnection.RequestReceived += OnRequestReceived;
|
||||
}
|
||||
|
||||
private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||
{
|
||||
var messageType = (string)args.Request.Message[MessageKeys.Type];
|
||||
var messageContent = (string)args.Request.Message[MessageKeys.Content];
|
||||
|
||||
MessageReceived?.Invoke(this, new Dictionary<string, string>
|
||||
{
|
||||
[MessageKeys.Type] = messageType,
|
||||
[MessageKeys.Content] = messageContent
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IDictionary<string, string>> SendMessageAsync(IDictionary<string, string> message)
|
||||
{
|
||||
var valueSet = new ValueSet();
|
||||
|
||||
foreach (var pair in message)
|
||||
{
|
||||
valueSet.Add(pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
var response = await _appServiceConnection.SendMessageAsync(valueSet).AsTask();
|
||||
|
||||
if (response.Status == AppServiceResponseStatus.Success)
|
||||
{
|
||||
var responseDict = new Dictionary<string, string>();
|
||||
foreach (var pair in response.Message)
|
||||
{
|
||||
responseDict.Add(pair.Key, (string)pair.Value);
|
||||
}
|
||||
return responseDict;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.EventArgs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.ViewManagement;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class ApplicationViewAdapter : IApplicationView
|
||||
{
|
||||
private readonly ApplicationView _applicationView;
|
||||
private readonly CoreDispatcher _dispatcher;
|
||||
|
||||
public event CloseRequestedHandler CloseRequested;
|
||||
|
||||
public ApplicationViewAdapter()
|
||||
{
|
||||
_applicationView = ApplicationView.GetForCurrentView();
|
||||
_dispatcher = CoreApplication.GetCurrentView().Dispatcher;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _applicationView.Title;
|
||||
set => _applicationView.Title = value;
|
||||
}
|
||||
|
||||
public bool IsApiContractPresent(string api, ushort version)
|
||||
{
|
||||
return ApiInformation.IsApiContractPresent(api, version);
|
||||
}
|
||||
|
||||
public Task RunOnDispatcherThread(Action action)
|
||||
{
|
||||
return _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask();
|
||||
}
|
||||
|
||||
public Task<bool> TryClose()
|
||||
{
|
||||
return _applicationView.TryConsolidateAsync().AsTask();
|
||||
}
|
||||
|
||||
public bool ToggleFullScreen()
|
||||
{
|
||||
if (_applicationView.IsFullScreenMode)
|
||||
{
|
||||
_applicationView.ExitFullScreenMode();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _applicationView.TryEnterFullScreenMode();
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
var deferral = e.GetDeferral();
|
||||
|
||||
var args = new CancelableEventArgs();
|
||||
if (CloseRequested != null)
|
||||
{
|
||||
await CloseRequested.Invoke(this, args);
|
||||
}
|
||||
|
||||
e.Handled = args.Cancelled;
|
||||
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class DispatcherTimerAdapter : IDispatcherTimer
|
||||
{
|
||||
private readonly DispatcherTimer _dispatcherTimer;
|
||||
|
||||
public DispatcherTimerAdapter()
|
||||
{
|
||||
_dispatcherTimer = new DispatcherTimer();
|
||||
_dispatcherTimer.Tick += OnDispatcherTimerTick;
|
||||
}
|
||||
|
||||
private void OnDispatcherTimerTick(object sender, object e)
|
||||
{
|
||||
Tick?.Invoke(this, null);
|
||||
}
|
||||
|
||||
public TimeSpan Interval
|
||||
{
|
||||
get => _dispatcherTimer.Interval;
|
||||
set => _dispatcherTimer.Interval = value;
|
||||
}
|
||||
|
||||
public bool IsEnabled => _dispatcherTimer.IsEnabled;
|
||||
|
||||
public event EventHandler<object> Tick;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_dispatcherTimer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_dispatcherTimer.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Popups;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class MessageDialogAdapter : IMessageDialog
|
||||
{
|
||||
private readonly MessageDialog _messageDialog;
|
||||
|
||||
public MessageDialogAdapter()
|
||||
{
|
||||
_messageDialog = new MessageDialog(string.Empty);
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _messageDialog.Title;
|
||||
set => _messageDialog.Title = value;
|
||||
}
|
||||
|
||||
public string Content
|
||||
{
|
||||
get => _messageDialog.Content;
|
||||
set => _messageDialog.Content = value;
|
||||
}
|
||||
|
||||
public void AddButton(DialogButton button)
|
||||
{
|
||||
_messageDialog.Commands.Add(new UICommand(button.ToString()));
|
||||
}
|
||||
|
||||
public async Task<DialogButton> ShowAsync()
|
||||
{
|
||||
var result = await _messageDialog.ShowAsync();
|
||||
|
||||
return Enum.Parse<DialogButton>(result.Label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,10 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Styles\Custom.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<LinearGradientBrush x:Key="MissingColorSelectionBrush" StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Offset="0" Color="Black" />
|
||||
<GradientStop Offset="1.0" Color="White" />
|
||||
</LinearGradientBrush>
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="OneToVisibleConverter"
|
||||
Else="Collapsed"
|
||||
@@ -21,12 +25,19 @@
|
||||
<converters:NegateConverter x:Key="NegateConverter" />
|
||||
<converters:FalseToVisibleConverter x:Key="FalseToVisibleConverter" />
|
||||
<converters:TrueToVisibleConverter x:Key="TrueToVisibleConverter" />
|
||||
<converters:TabColorFallbackConverter x:Key="TabColorFallbackConverter" />
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="ZeroToCollapsedConverter"
|
||||
Else="Visible"
|
||||
If="0"
|
||||
Then="Collapsed" />
|
||||
<converters:IntToExtendedVirtualKeyConverter x:Key="IntToExtendedVirtualKeyConverter" />
|
||||
<converters:BackgroundToApplicationThemeConverter x:Key="BackgroundToApplicationThemeConverter" />
|
||||
<converters:StringToColorConverter x:Key="StringToColorConverter" />
|
||||
<converters:EnumValueToVisibiltyConverter x:Key="EnumValueToVisibiltyConverter" />
|
||||
<converters:TabThemeSelectedConverter x:Key="TabThemeSelectedConverter" />
|
||||
<converters:ColorResourceKeyFallbackConverter x:Key="ColorResourceKeyFallbackConverter" />
|
||||
<converters:EnumToDescriptionConverter x:Key="EnumToDescriptionConverter" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
</Application>
|
||||
@@ -1,5 +1,10 @@
|
||||
using Autofac;
|
||||
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.EventArgs;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Views;
|
||||
@@ -15,6 +20,7 @@ using Windows.ApplicationModel.Activation;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
@@ -34,7 +40,7 @@ namespace FluentTerminal.App
|
||||
private readonly List<MainViewModel> _mainViewModels;
|
||||
private SettingsViewModel _settingsViewModel;
|
||||
private int? _settingsWindowId;
|
||||
private AppServiceConnection _appServiceConnection;
|
||||
private IAppServiceConnection _appServiceConnection;
|
||||
private BackgroundTaskDeferral _appServiceDeferral;
|
||||
|
||||
public App()
|
||||
@@ -43,14 +49,41 @@ namespace FluentTerminal.App
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
UnhandledException += OnUnhandledException;
|
||||
|
||||
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 builder = new ContainerBuilder();
|
||||
builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
|
||||
builder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>().SingleInstance();
|
||||
builder.RegisterType<TrayProcessCommunicationService>().As<ITrayProcessCommunicationService>().SingleInstance();
|
||||
builder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
|
||||
builder.RegisterType<KeyboardCommandService>().As<IKeyboardCommandService>().InstancePerDependency();
|
||||
builder.RegisterType<NotificationService>().As<INotificationService>().InstancePerDependency();
|
||||
builder.RegisterType<UpdateService>().As<IUpdateService>().InstancePerDependency();
|
||||
builder.RegisterType<MainViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<ThemeParserFactory>().As<IThemeParserFactory>().SingleInstance();
|
||||
builder.RegisterType<ITermThemeParser>().As<IThemeParser>().SingleInstance();
|
||||
builder.RegisterType<FluentTerminalThemeParser>().As<IThemeParser>().SingleInstance();
|
||||
builder.RegisterType<ClipboardService>().As<IClipboardService>().SingleInstance();
|
||||
builder.RegisterType<FileSystemService>().As<IFileSystemService>().SingleInstance();
|
||||
builder.RegisterType<SystemFontService>().As<ISystemFontService>().SingleInstance();
|
||||
builder.RegisterType<ShellProfileSelectionDialog>().As<IShellProfileSelectionDialog>().InstancePerDependency();
|
||||
builder.RegisterType<CreateKeyBindingDialog>().As<ICreateKeyBindingDialog>().InstancePerDependency();
|
||||
builder.RegisterType<InputDialog>().As<IInputDialog>().InstancePerDependency();
|
||||
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();
|
||||
|
||||
@@ -62,6 +95,11 @@ namespace FluentTerminal.App
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
}
|
||||
|
||||
private void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(e.Exception.ToString());
|
||||
}
|
||||
|
||||
protected override async void OnActivated(IActivatedEventArgs args)
|
||||
{
|
||||
if (args is CommandLineActivatedEventArgs commandLineActivated)
|
||||
@@ -85,11 +123,11 @@ namespace FluentTerminal.App
|
||||
{
|
||||
if (_applicationSettings.NewTerminalLocation == NewTerminalLocation.Tab && _mainViewModels.Count > 0)
|
||||
{
|
||||
await _mainViewModels.Last().AddTerminal(parameter, false).ConfigureAwait(true);
|
||||
await _mainViewModels.Last().AddTerminal(parameter, false, Guid.Empty).ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await CreateNewTerminalWindow(parameter).ConfigureAwait(true);
|
||||
await CreateNewTerminalWindow(parameter, false).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +141,7 @@ namespace FluentTerminal.App
|
||||
else if (command == "new")
|
||||
{
|
||||
var viewModel = _container.Resolve<MainViewModel>();
|
||||
await viewModel.AddTerminal(parameter, false).ConfigureAwait(true);
|
||||
await viewModel.AddTerminal(parameter, false, Guid.Empty).ConfigureAwait(true);
|
||||
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
@@ -115,13 +153,13 @@ namespace FluentTerminal.App
|
||||
if (!_alreadyLaunched)
|
||||
{
|
||||
var viewModel = _container.Resolve<MainViewModel>();
|
||||
await viewModel.AddTerminal(null, false).ConfigureAwait(true);
|
||||
await viewModel.AddTerminal(null, false, Guid.Empty).ConfigureAwait(true);
|
||||
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
|
||||
Window.Current.Activate();
|
||||
}
|
||||
else if (_mainViewModels.Count == 0)
|
||||
{
|
||||
await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, string.Empty).ConfigureAwait(true);
|
||||
await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, false, string.Empty).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +174,7 @@ namespace FluentTerminal.App
|
||||
_appServiceDeferral = args.TaskInstance.GetDeferral();
|
||||
args.TaskInstance.Canceled += OnTaskCanceled;
|
||||
|
||||
_appServiceConnection = details.AppServiceConnection;
|
||||
_appServiceConnection = new AppServiceConnectionAdapter(details.AppServiceConnection);
|
||||
|
||||
_trayReady.SetResult(0);
|
||||
}
|
||||
@@ -172,6 +210,7 @@ namespace FluentTerminal.App
|
||||
mainViewModel.Closed += OnMainViewModelClosed;
|
||||
mainViewModel.NewWindowRequested += OnNewWindowRequested;
|
||||
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
|
||||
mainViewModel.ShowAboutRequested += OnShowAboutRequested;
|
||||
_mainViewModels.Add(mainViewModel);
|
||||
}
|
||||
|
||||
@@ -181,13 +220,13 @@ namespace FluentTerminal.App
|
||||
Window.Current.Activate();
|
||||
}
|
||||
|
||||
private async Task CreateNewTerminalWindow(string startupDirectory)
|
||||
private async Task CreateNewTerminalWindow(string startupDirectory, bool showProfileSelection)
|
||||
{
|
||||
var id = await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, startupDirectory).ConfigureAwait(true);
|
||||
var id = await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, showProfileSelection, startupDirectory).ConfigureAwait(true);
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
|
||||
}
|
||||
|
||||
private async Task<int> CreateSecondaryView<TViewModel>(Type pageType, bool ExtendViewIntoTitleBar, object parameter)
|
||||
private async Task<int> CreateSecondaryView<TViewModel>(Type pageType, bool ExtendViewIntoTitleBar, bool showProfileSelection, object parameter)
|
||||
{
|
||||
int windowId = 0;
|
||||
TViewModel viewModel = default;
|
||||
@@ -209,8 +248,9 @@ namespace FluentTerminal.App
|
||||
mainViewModel.Closed += OnMainViewModelClosed;
|
||||
mainViewModel.NewWindowRequested += OnNewWindowRequested;
|
||||
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
|
||||
mainViewModel.ShowAboutRequested += OnShowAboutRequested;
|
||||
_mainViewModels.Add(mainViewModel);
|
||||
await mainViewModel.AddTerminal(directory, false).ConfigureAwait(true);
|
||||
await mainViewModel.AddTerminal(directory, showProfileSelection, Guid.Empty).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
if (viewModel is SettingsViewModel settingsViewModel)
|
||||
@@ -239,9 +279,9 @@ namespace FluentTerminal.App
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnNewWindowRequested(object sender, EventArgs e)
|
||||
private async void OnNewWindowRequested(object sender, NewWindowRequestedEventArgs e)
|
||||
{
|
||||
await CreateNewTerminalWindow(string.Empty).ConfigureAwait(true);
|
||||
await CreateNewTerminalWindow(string.Empty, e.ShowProfileSelection).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private void OnSettingsClosed(object sender, EventArgs e)
|
||||
@@ -251,26 +291,37 @@ namespace FluentTerminal.App
|
||||
_settingsWindowId = null;
|
||||
}
|
||||
|
||||
private void OnShowAboutRequested(object sender, EventArgs e)
|
||||
{
|
||||
ShowAbout().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async void OnShowSettingsRequested(object sender, EventArgs e)
|
||||
{
|
||||
await ShowSettings().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async Task ShowAbout()
|
||||
{
|
||||
await ShowSettings().ConfigureAwait(true);
|
||||
_settingsViewModel.NavigateToAboutPage();
|
||||
}
|
||||
|
||||
private async Task ShowSettings()
|
||||
{
|
||||
if (_settingsViewModel == null)
|
||||
{
|
||||
_settingsWindowId = await CreateSecondaryView<SettingsViewModel>(typeof(SettingsPage), true, null).ConfigureAwait(true);
|
||||
_settingsWindowId = await CreateSecondaryView<SettingsViewModel>(typeof(SettingsPage), true, false, null).ConfigureAwait(true);
|
||||
}
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,36 @@
|
||||
using FluentTerminal.App.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class BackgroundToApplicationThemeConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
Color color;
|
||||
if (value is string colorString)
|
||||
{
|
||||
color = colorString.ToColor();
|
||||
}
|
||||
else if (value is Color)
|
||||
{
|
||||
color = (Color)value;
|
||||
}
|
||||
|
||||
if (color != null)
|
||||
{
|
||||
return ContrastHelper.GetIdealThemeForBackgroundColor(color);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class ColorResourceKeyFallbackConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return Application.Current.Resources[(string)parameter];
|
||||
}
|
||||
else if (value is string hex)
|
||||
{
|
||||
var color = hex.ToColor();
|
||||
return new SolidColorBrush(color);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class EnumToDescriptionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
// value is the Enum, parameter is the string.
|
||||
if (value is Enum enumValue)
|
||||
{
|
||||
return EnumHelper.GetEnumDescription(enumValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class EnumValueToVisibiltyConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value.GetType().IsEnum && Enum.TryParse(value.GetType(), parameter.ToString(), true, out object result))
|
||||
{
|
||||
return Equals(value, result) ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,4 +24,4 @@ namespace FluentTerminal.App.Converters
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
@@ -11,7 +11,7 @@ namespace FluentTerminal.App.Converters
|
||||
{
|
||||
if (value is int intValue)
|
||||
{
|
||||
return GetEnumDescription((ExtendedVirtualKey)intValue);
|
||||
return EnumHelper.GetEnumDescription((ExtendedVirtualKey)intValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -20,24 +20,5 @@ namespace FluentTerminal.App.Converters
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static string GetEnumDescription(Enum value)
|
||||
{
|
||||
var fieldInfo = value.GetType().GetField(value.ToString());
|
||||
|
||||
if (fieldInfo == null)
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
if (attributes?.Length > 0)
|
||||
{
|
||||
return attributes[0].Description;
|
||||
}
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||