Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bbed15874 | ||
|
|
997cb9814e | ||
|
|
6524d14687 | ||
|
|
7876984327 |
No files matched your search
@@ -1,3 +0,0 @@
|
|||||||
# Git will handle the files in whatever way it thinks is best.
|
|
||||||
# Overwrites global core.autocrlf git setting
|
|
||||||
* text=auto
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: windows-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@master
|
|
||||||
|
|
||||||
- name: Build client
|
|
||||||
working-directory: ./FluentTerminal.Client
|
|
||||||
run: |
|
|
||||||
yarn
|
|
||||||
yarn build
|
|
||||||
|
|
||||||
- name: Setup MSBuild.exe
|
|
||||||
uses: microsoft/setup-msbuild@v1.0.2
|
|
||||||
|
|
||||||
- name: MSBuild
|
|
||||||
run: msbuild FluentTerminal.sln -t:build -p:Configuration=Debug -p:Platform=x64 -m -restore
|
|
||||||
@@ -292,5 +292,3 @@ __pycache__/
|
|||||||
*.btm.cs
|
*.btm.cs
|
||||||
*.odx.cs
|
*.odx.cs
|
||||||
*.xsd.cs
|
*.xsd.cs
|
||||||
|
|
||||||
!FluentTerminal.App/MoshExecutables/*
|
|
||||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,4 +1,3 @@
|
|||||||
reg add "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
reg add "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%1\"" /f
|
||||||
reg add "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
reg add "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
||||||
reg add "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
|
||||||
reg add "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
reg add "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
copy /y FluentTerminal.ico "%LOCALAPPDATA%\Microsoft\WindowsApps"
|
|
||||||
reg add "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
|
||||||
reg add "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f
|
|
||||||
reg add "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
|
||||||
reg add "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f
|
|
||||||
reg add "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
|
||||||
reg add "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f
|
|
||||||
reg add "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new \"%%V\"" /f
|
|
||||||
reg add "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here" /v icon /t REG_SZ /d "%LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico" /f
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
If you want a an icon for the context menu entries, download the .ico file and put in the same directory as the Install_with_icon.bat file then run it.
|
|
||||||
The same Uninstall.bat works for both Install.bat files.
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
reg delete "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here" /f
|
reg delete "HKCU\Software\Classes\Directory\shell\Open Fluent Terminal here" /f
|
||||||
reg delete "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here" /f
|
reg delete "HKCU\Software\Classes\Directory\Background\shell\Open Fluent Terminal here" /f
|
||||||
reg delete "HKCU\Software\Classes\Drive\shell\Open Fluent Terminal here" /f
|
|
||||||
reg delete "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here" /f
|
reg delete "HKCU\Software\Classes\LibraryFolder\Background\shell\Open Fluent Terminal here" /f
|
||||||
del %LOCALAPPDATA%\Microsoft\WindowsApps\FluentTerminal.ico
|
|
||||||
@@ -23,7 +23,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
public void ShowCreateKeyBindingDialog_Default_UsesCreateKeyBindingDialog()
|
public void ShowCreateKeyBindingDialog_Default_UsesCreateKeyBindingDialog()
|
||||||
{
|
{
|
||||||
var createKeyBindingDialog = new Mock<ICreateKeyBindingDialog>();
|
var createKeyBindingDialog = new Mock<ICreateKeyBindingDialog>();
|
||||||
var dialogService = new DialogService(Mock.Of<IMessageDialog>, () => createKeyBindingDialog.Object, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>, Mock.Of<ICustomCommandDialog>, Mock.Of<IAboutDialog>);
|
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, () => createKeyBindingDialog.Object, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>);
|
||||||
|
|
||||||
dialogService.ShowCreateKeyBindingDialog();
|
dialogService.ShowCreateKeyBindingDialog();
|
||||||
|
|
||||||
@@ -36,9 +36,9 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var title = string.Empty;
|
var title = string.Empty;
|
||||||
var content = _fixture.Create<string>();
|
var content = _fixture.Create<string>();
|
||||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||||
var dialogService = new DialogService(Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>, Mock.Of<ICustomCommandDialog>, Mock.Of<IAboutDialog>);
|
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>);
|
||||||
|
|
||||||
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsync(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||||
|
|
||||||
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("title");
|
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("title");
|
||||||
}
|
}
|
||||||
@@ -49,9 +49,9 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var title = _fixture.Create<string>();
|
var title = _fixture.Create<string>();
|
||||||
var content = string.Empty;
|
var content = string.Empty;
|
||||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||||
var dialogService = new DialogService(Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>, Mock.Of<ICustomCommandDialog>, Mock.Of<IAboutDialog>);
|
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>);
|
||||||
|
|
||||||
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsync(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||||
|
|
||||||
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("content");
|
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("content");
|
||||||
}
|
}
|
||||||
@@ -61,9 +61,9 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
{
|
{
|
||||||
var title = _fixture.Create<string>();
|
var title = _fixture.Create<string>();
|
||||||
var content = _fixture.Create<string>();
|
var content = _fixture.Create<string>();
|
||||||
var dialogService = new DialogService(Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>, Mock.Of<ICustomCommandDialog>, Mock.Of<IAboutDialog>);
|
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>);
|
||||||
|
|
||||||
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsync(title, content);
|
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content);
|
||||||
|
|
||||||
showMessageDialogAsnyc.Should().Throw<ArgumentException>().And.ParamName.Should().Be("buttons");
|
showMessageDialogAsnyc.Should().Throw<ArgumentException>().And.ParamName.Should().Be("buttons");
|
||||||
}
|
}
|
||||||
@@ -75,19 +75,30 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var content = _fixture.Create<string>();
|
var content = _fixture.Create<string>();
|
||||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||||
var messageDialog = new Mock<IMessageDialog>();
|
var messageDialog = new Mock<IMessageDialog>();
|
||||||
var dialogService = new DialogService(() => messageDialog.Object, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>, Mock.Of<ICustomCommandDialog>, Mock.Of<IAboutDialog>);
|
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, () => messageDialog.Object, Mock.Of<ICreateKeyBindingDialog>, Mock.Of<IInputDialog>, Mock.Of<ISshConnectionInfoDialog>);
|
||||||
|
|
||||||
dialogService.ShowMessageDialogAsync(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||||
|
|
||||||
messageDialog.Verify(x => x.ShowAsync(), Times.Once);
|
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>, Mock.Of<ISshConnectionInfoDialog>);
|
||||||
|
|
||||||
|
dialogService.ShowProfileSelectionDialogAsync();
|
||||||
|
|
||||||
|
shellProfileSelectionDialog.Verify(x => x.SelectProfile(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShowInputDialogAsync_UsesIInputDialogSetTitle()
|
public void ShowInputDialogAsync_UsesIInputDialogSetTitle()
|
||||||
{
|
{
|
||||||
var title = "title";
|
var title = "title";
|
||||||
var inputDialog = new Mock<IInputDialog>();
|
var inputDialog = new Mock<IInputDialog>();
|
||||||
var dialogService = new DialogService(Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, () => inputDialog.Object, Mock.Of<ISshConnectionInfoDialog>, Mock.Of<ICustomCommandDialog>, Mock.Of<IAboutDialog>);
|
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>, () => inputDialog.Object, Mock.Of<ISshConnectionInfoDialog>);
|
||||||
|
|
||||||
dialogService.ShowInputDialogAsync(title);
|
dialogService.ShowInputDialogAsync(title);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -11,13 +11,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoFixture" Version="4.17.0" />
|
<PackageReference Include="AutoFixture" Version="4.8.0" />
|
||||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
<PackageReference Include="FluentAssertions" Version="5.6.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
|
||||||
<PackageReference Include="Moq" Version="4.16.1" />
|
<PackageReference Include="Moq" Version="4.10.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -34,11 +34,4 @@
|
|||||||
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
|
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
|
||||||
</ItemGroup>
|
</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>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -41,41 +41,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
|
|
||||||
invoke.Should().Throw<InvalidOperationException>();
|
invoke.Should().Throw<InvalidOperationException>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DeregisterCommandHandler_CommandIsNull_ThrowsArgumentNullException()
|
|
||||||
{
|
|
||||||
var keyboardCommandService = new KeyboardCommandService();
|
|
||||||
|
|
||||||
Action invoke = () => keyboardCommandService.DeregisterCommandHandler(null);
|
|
||||||
|
|
||||||
invoke.Should().Throw<ArgumentNullException>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DeregisterCommandHandler_CommandRegistered_HandlerGetsRemoved()
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
Action invoke = () => keyboardCommandService.DeregisterCommandHandler(command);
|
|
||||||
|
|
||||||
invoke.Should().NotThrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DeregisterCommandHandler_CommandNotRegistered_ShouldNotThrow()
|
|
||||||
{
|
|
||||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
|
||||||
var keyboardCommandService = new KeyboardCommandService();
|
|
||||||
|
|
||||||
Action invoke = () => keyboardCommandService.DeregisterCommandHandler(command);
|
|
||||||
|
|
||||||
invoke.Should().NotThrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SendCommand_CommandIsRegistered_HandlerGetsInvoked()
|
public void SendCommand_CommandIsRegistered_HandlerGetsInvoked()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using AutoFixture;
|
using AutoFixture;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using FluentTerminal.App.Services.Implementation;
|
using FluentTerminal.App.Services.Implementation;
|
||||||
|
using FluentTerminal.App.Services.Utilities;
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using Moq;
|
using Moq;
|
||||||
@@ -9,9 +10,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentTerminal.Models.Messages;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Microsoft.Toolkit.Mvvm.Messaging;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Test
|
namespace FluentTerminal.App.Services.Test
|
||||||
{
|
{
|
||||||
@@ -37,8 +36,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||||
@@ -60,7 +58,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var applicationDataContainers = new ApplicationDataContainers
|
var applicationDataContainers = new ApplicationDataContainers
|
||||||
{
|
{
|
||||||
ShellProfiles = shellProfilesContainer.Object,
|
ShellProfiles = shellProfilesContainer.Object,
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
@@ -89,7 +86,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
Themes = Mock.Of<IApplicationDataContainer>()
|
Themes = Mock.Of<IApplicationDataContainer>()
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
@@ -115,7 +111,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
Themes = Mock.Of<IApplicationDataContainer>()
|
Themes = Mock.Of<IApplicationDataContainer>()
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||||
@@ -139,7 +134,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
Themes = Mock.Of<IApplicationDataContainer>()
|
Themes = Mock.Of<IApplicationDataContainer>()
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
@@ -161,13 +155,11 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
Themes = Mock.Of<IApplicationDataContainer>()
|
Themes = Mock.Of<IApplicationDataContainer>()
|
||||||
};
|
};
|
||||||
var currentThemeChangedEventInvoked = false;
|
var currentThemeChangedEventInvoked = false;
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
WeakReferenceMessenger.Default.Register<CurrentThemeChangedMessage>(this,
|
settingsService.CurrentThemeChanged += (s, e) => currentThemeChangedEventInvoked = true;
|
||||||
(r, m) => currentThemeChangedEventInvoked = true);
|
|
||||||
|
|
||||||
settingsService.SaveCurrentThemeId(currentThemeId);
|
settingsService.SaveCurrentThemeId(currentThemeId);
|
||||||
|
|
||||||
@@ -186,8 +178,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -212,12 +203,10 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = roamingSettings.Object,
|
RoamingSettings = roamingSettings.Object,
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
WeakReferenceMessenger.Default.Register<CurrentThemeChangedMessage>(this,
|
settingsService.CurrentThemeChanged += (s, e) => currentThemeChangedEventInvoked = true;
|
||||||
(r, m) => currentThemeChangedEventInvoked = true);
|
|
||||||
|
|
||||||
settingsService.SaveTheme(theme);
|
settingsService.SaveTheme(theme);
|
||||||
|
|
||||||
@@ -236,8 +225,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -259,8 +247,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -283,8 +270,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -307,8 +293,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -331,8 +316,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -354,8 +338,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -377,12 +360,10 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
WeakReferenceMessenger.Default.Register<TerminalOptionsChangedMessage>(this,
|
settingsService.TerminalOptionsChanged += (s, e) => terminalOptionsChangedEventInvoked = true;
|
||||||
(r, m) => terminalOptionsChangedEventInvoked = true);
|
|
||||||
|
|
||||||
settingsService.SaveTerminalOptions(terminalOptions);
|
settingsService.SaveTerminalOptions(terminalOptions);
|
||||||
|
|
||||||
@@ -402,8 +383,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -425,8 +405,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -448,11 +427,10 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
WeakReferenceMessenger.Default.Register<ApplicationSettingsChangedMessage>(this, (r, m) => applicationSettingsChangedEventInvoked = true);
|
settingsService.ApplicationSettingsChanged += (s, e) => applicationSettingsChangedEventInvoked = true;
|
||||||
|
|
||||||
settingsService.SaveApplicationSettings(applicationSettings);
|
settingsService.SaveApplicationSettings(applicationSettings);
|
||||||
|
|
||||||
@@ -475,8 +453,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -494,14 +471,17 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var commands = _fixture.CreateMany<Command>(5);
|
var commands = _fixture.CreateMany<Command>(5);
|
||||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
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
|
var applicationDataContainers = new ApplicationDataContainers
|
||||||
{
|
{
|
||||||
KeyBindings = keyBindingsContainer.Object,
|
KeyBindings = keyBindingsContainer.Object,
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||||
|
|
||||||
@@ -526,8 +506,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
|
|
||||||
@@ -550,12 +529,10 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
WeakReferenceMessenger.Default.Register<KeyBindingsChangedMessage>(this,
|
settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true;
|
||||||
(r, m) => keyBindingsChangedEventInvoked = true);
|
|
||||||
|
|
||||||
settingsService.SaveKeyBindings(command.ToString(), keyBindings);
|
settingsService.SaveKeyBindings(command.ToString(), keyBindings);
|
||||||
|
|
||||||
@@ -573,8 +550,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||||
|
|
||||||
@@ -599,12 +575,10 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
Themes = Mock.Of<IApplicationDataContainer>(),
|
Themes = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>()
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>()
|
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
WeakReferenceMessenger.Default.Register<KeyBindingsChangedMessage>(this,
|
settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true;
|
||||||
(r, m) => keyBindingsChangedEventInvoked = true);
|
|
||||||
|
|
||||||
settingsService.ResetKeyBindings();
|
settingsService.ResetKeyBindings();
|
||||||
|
|
||||||
@@ -624,7 +598,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
Themes = Mock.Of<IApplicationDataContainer>()
|
Themes = Mock.Of<IApplicationDataContainer>()
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||||
@@ -650,7 +623,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
Themes = Mock.Of<IApplicationDataContainer>()
|
Themes = Mock.Of<IApplicationDataContainer>()
|
||||||
};
|
};
|
||||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||||
@@ -672,7 +644,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var applicationDataContainers = new ApplicationDataContainers
|
var applicationDataContainers = new ApplicationDataContainers
|
||||||
{
|
{
|
||||||
ShellProfiles = shellProfilesContainer.Object,
|
ShellProfiles = shellProfilesContainer.Object,
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
@@ -682,7 +653,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
|
|
||||||
var result = settingsService.GetShellProfiles();
|
var result = settingsService.GetShellProfiles();
|
||||||
|
|
||||||
result.Select(p => p.EqualTo(shellProfiles.FirstOrDefault(x => x.Id == p.Id)).Should().BeTrue());
|
result.Should().BeEquivalentTo(shellProfiles);
|
||||||
shellProfilesContainer.Verify(x => x.GetAll(), Times.Once);
|
shellProfilesContainer.Verify(x => x.GetAll(), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,7 +666,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var applicationDataContainers = new ApplicationDataContainers
|
var applicationDataContainers = new ApplicationDataContainers
|
||||||
{
|
{
|
||||||
ShellProfiles = shellProfilesContainer.Object,
|
ShellProfiles = shellProfilesContainer.Object,
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
@@ -717,7 +687,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var applicationDataContainers = new ApplicationDataContainers
|
var applicationDataContainers = new ApplicationDataContainers
|
||||||
{
|
{
|
||||||
ShellProfiles = shellProfilesContainer.Object,
|
ShellProfiles = shellProfilesContainer.Object,
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
@@ -742,7 +711,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var applicationDataContainers = new ApplicationDataContainers
|
var applicationDataContainers = new ApplicationDataContainers
|
||||||
{
|
{
|
||||||
ShellProfiles = shellProfilesContainer.Object,
|
ShellProfiles = shellProfilesContainer.Object,
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = localSettings.Object,
|
LocalSettings = localSettings.Object,
|
||||||
@@ -771,7 +739,6 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var applicationDataContainers = new ApplicationDataContainers
|
var applicationDataContainers = new ApplicationDataContainers
|
||||||
{
|
{
|
||||||
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
|
||||||
SshProfiles = Mock.Of<IApplicationDataContainer>(),
|
|
||||||
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
KeyBindings = Mock.Of<IApplicationDataContainer>(),
|
||||||
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
LocalSettings = Mock.Of<IApplicationDataContainer>(),
|
||||||
RoamingSettings = roamingSettings.Object,
|
RoamingSettings = roamingSettings.Object,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Newtonsoft.Json;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.Foundation.Collections;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Test
|
namespace FluentTerminal.App.Services.Test
|
||||||
@@ -35,20 +34,20 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
});
|
});
|
||||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||||
var response = _fixture.Create<CreateTerminalResponse>();
|
var response = _fixture.Create<CreateTerminalResponse>();
|
||||||
var responseMessage = new ValueSet
|
var responseMessage = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
[MessageKeys.Type] = _fixture.Create<string>(),
|
[MessageKeys.Type] = _fixture.Create<string>(),
|
||||||
[MessageKeys.Content] = JsonConvert.SerializeObject(response)
|
[MessageKeys.Content] = JsonConvert.SerializeObject(response)
|
||||||
};
|
};
|
||||||
appServiceConnection.Setup(x => x.SendMessageAsync(It.IsAny<ValueSet>())).Returns(Task.FromResult(responseMessage));
|
appServiceConnection.Setup(x => x.SendMessageAsync(It.IsAny<IDictionary<string, string>>())).Returns(Task.FromResult((IDictionary<string, string>)responseMessage));
|
||||||
var terminalSize = _fixture.Create<TerminalSize>();
|
var terminalSize = _fixture.Create<TerminalSize>();
|
||||||
var shellProfile = _fixture.Create<ShellProfile>();
|
var shellProfile = _fixture.Create<ShellProfile>();
|
||||||
var sessionType = _fixture.Create<SessionType>();
|
var sessionType = _fixture.Create<SessionType>();
|
||||||
var id = _fixture.Create<byte>();
|
var id = _fixture.Create<int>();
|
||||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||||
|
|
||||||
var result = await trayProcessCommunicationService.CreateTerminalAsync(id, terminalSize, shellProfile, sessionType);
|
var result = await trayProcessCommunicationService.CreateTerminal(id, terminalSize, shellProfile, sessionType);
|
||||||
|
|
||||||
result.Should().BeEquivalentTo(response);
|
result.Should().BeEquivalentTo(response);
|
||||||
}
|
}
|
||||||
@@ -56,7 +55,7 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ResizeTerminal_Default_SendsResizeTerminalRequest()
|
public async Task ResizeTerminal_Default_SendsResizeTerminalRequest()
|
||||||
{
|
{
|
||||||
var terminalId = _fixture.Create<byte>();
|
var terminalId = _fixture.Create<int>();
|
||||||
var terminalSize = _fixture.Create<TerminalSize>();
|
var terminalSize = _fixture.Create<TerminalSize>();
|
||||||
var settingsService = new Mock<ISettingsService>();
|
var settingsService = new Mock<ISettingsService>();
|
||||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||||
@@ -68,15 +67,15 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||||
|
|
||||||
await trayProcessCommunicationService.ResizeTerminalAsync(terminalId, terminalSize);
|
await trayProcessCommunicationService.ResizeTerminal(terminalId, terminalSize);
|
||||||
|
|
||||||
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<ValueSet>(d => (byte)d[MessageKeys.Type] == (byte)MessageIdentifiers.ResizeTerminalRequest)), Times.Once);
|
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(ResizeTerminalRequest))), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Write_Default_SendsWriteDataRequest()
|
public async Task Write_Default_SendsWriteDataRequest()
|
||||||
{
|
{
|
||||||
var terminalId = _fixture.Create<byte>();
|
var terminalId = _fixture.Create<int>();
|
||||||
var data = _fixture.Create<byte[]>();
|
var data = _fixture.Create<byte[]>();
|
||||||
var settingsService = new Mock<ISettingsService>();
|
var settingsService = new Mock<ISettingsService>();
|
||||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||||
@@ -88,15 +87,15 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||||
|
|
||||||
await trayProcessCommunicationService.WriteAsync(terminalId, data);
|
await trayProcessCommunicationService.Write(terminalId, data);
|
||||||
|
|
||||||
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<ValueSet>(d => (byte)d[MessageKeys.Type] == Constants.TerminalBufferRequestIdentifier)), Times.Once);
|
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(WriteDataRequest))), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CloseTerminal_Default_SendsTerminalExitedRequest()
|
public async Task CloseTerminal_Default_SendsTerminalExitedRequest()
|
||||||
{
|
{
|
||||||
var terminalId = _fixture.Create<byte>();
|
var terminalId = _fixture.Create<int>();
|
||||||
var settingsService = new Mock<ISettingsService>();
|
var settingsService = new Mock<ISettingsService>();
|
||||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||||
@@ -107,21 +106,24 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||||
|
|
||||||
await trayProcessCommunicationService.CloseTerminalAsync(terminalId);
|
await trayProcessCommunicationService.CloseTerminal(terminalId);
|
||||||
|
|
||||||
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<ValueSet>(d => (byte)d[MessageKeys.Type] == (byte)MessageIdentifiers.TerminalExitedRequest)), Times.Once);
|
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(TerminalExitedRequest))), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnMessageReceived_TerminalExitedRequest_InvokesTerminalExitedEvent()
|
public void OnMessageReceived_TerminalExitedRequest_InvokesTerminalExitedEvent()
|
||||||
{
|
{
|
||||||
var request = new TerminalExitedRequest(_fixture.Create<byte>(), _fixture.Create<int>());
|
var terminalId = _fixture.Create<int>();
|
||||||
|
var receivedTerminalId = 0;
|
||||||
var receivedExitStatus = (TerminalExitStatus) null;
|
var terminalExitedEventCalled = false;
|
||||||
|
var request = new TerminalExitedRequest
|
||||||
var message = new ValueSet
|
|
||||||
{
|
{
|
||||||
[MessageKeys.Type] = (byte) MessageIdentifiers.TerminalExitedRequest,
|
TerminalId = terminalId
|
||||||
|
};
|
||||||
|
var message = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
[MessageKeys.Type] = nameof(TerminalExitedRequest),
|
||||||
[MessageKeys.Content] = JsonConvert.SerializeObject(request)
|
[MessageKeys.Content] = JsonConvert.SerializeObject(request)
|
||||||
};
|
};
|
||||||
var settingsService = new Mock<ISettingsService>();
|
var settingsService = new Mock<ISettingsService>();
|
||||||
@@ -133,29 +135,33 @@ namespace FluentTerminal.App.Services.Test
|
|||||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||||
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
|
||||||
trayProcessCommunicationService.TerminalExited += (s, status) =>
|
trayProcessCommunicationService.TerminalExited += (s, e) =>
|
||||||
{
|
{
|
||||||
receivedExitStatus = status;
|
terminalExitedEventCalled = true;
|
||||||
|
receivedTerminalId = e;
|
||||||
};
|
};
|
||||||
|
|
||||||
appServiceConnection.Raise(x => x.MessageReceived += null, null, message);
|
appServiceConnection.Raise(x => x.MessageReceived += null, null, message);
|
||||||
|
|
||||||
receivedExitStatus.Should().NotBeNull();
|
terminalExitedEventCalled.Should().BeTrue();
|
||||||
receivedExitStatus.TerminalId.Should().Be(request.TerminalId);
|
receivedTerminalId.Should().Be(terminalId);
|
||||||
receivedExitStatus.ExitCode.Should().Be(request.ExitCode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnMessageReceived_DisplayTerminalOutputRequest_InvokesCorrectOutputHandler()
|
public void OnMessageReceived_DisplayTerminalOutputRequest_InvokesCorrectOutputHandler()
|
||||||
{
|
{
|
||||||
var terminalId = _fixture.Create<byte>();
|
var terminalId = _fixture.Create<int>();
|
||||||
var output = _fixture.Create<byte[]>();
|
var output = _fixture.Create<byte[]>();
|
||||||
var receivedOutput = default(byte[]);
|
var receivedOutput = default(byte[]);
|
||||||
var message = new ValueSet
|
var request = new DisplayTerminalOutputRequest
|
||||||
{
|
{
|
||||||
[MessageKeys.Type] = Constants.TerminalBufferRequestIdentifier,
|
TerminalId = terminalId,
|
||||||
[MessageKeys.TerminalId] = terminalId,
|
Output = output
|
||||||
[MessageKeys.Content] = output
|
};
|
||||||
|
var message = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
[MessageKeys.Type] = nameof(DisplayTerminalOutputRequest),
|
||||||
|
[MessageKeys.Content] = JsonConvert.SerializeObject(request)
|
||||||
};
|
};
|
||||||
var settingsService = new Mock<ISettingsService>();
|
var settingsService = new Mock<ISettingsService>();
|
||||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Adapters
|
namespace FluentTerminal.App.Services.Adapters
|
||||||
{
|
{
|
||||||
@@ -21,11 +20,6 @@ namespace FluentTerminal.App.Services.Adapters
|
|||||||
_applicationDataContainer.Values.Remove(key);
|
_applicationDataContainer.Values.Remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_applicationDataContainer.Values.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<object> GetAll()
|
public IEnumerable<object> GetAll()
|
||||||
{
|
{
|
||||||
return _applicationDataContainer.Values.Select(x => x.Value);
|
return _applicationDataContainer.Values.Select(x => x.Value);
|
||||||
@@ -57,8 +51,7 @@ namespace FluentTerminal.App.Services.Adapters
|
|||||||
|
|
||||||
public void WriteValueAsJson<T>(string name, T value)
|
public void WriteValueAsJson<T>(string name, T value)
|
||||||
{
|
{
|
||||||
_applicationDataContainer.Values[name] = JsonConvert.SerializeObject(value,
|
_applicationDataContainer.Values[name] = JsonConvert.SerializeObject(value);
|
||||||
PreserveDictionaryKeyCaseContractResolver.SerializerSettings);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,5 @@
|
|||||||
public IApplicationDataContainer Themes { get; set; }
|
public IApplicationDataContainer Themes { get; set; }
|
||||||
public IApplicationDataContainer KeyBindings { get; set; }
|
public IApplicationDataContainer KeyBindings { get; set; }
|
||||||
public IApplicationDataContainer ShellProfiles { get; set; }
|
public IApplicationDataContainer ShellProfiles { get; set; }
|
||||||
public IApplicationDataContainer SshProfiles { get; set; }
|
|
||||||
public IApplicationDataContainer HistoryContainer { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,5 @@
|
|||||||
public const string ThemesContainerName = "Themes";
|
public const string ThemesContainerName = "Themes";
|
||||||
public const string KeyBindingsContainerName = "KeyBindings";
|
public const string KeyBindingsContainerName = "KeyBindings";
|
||||||
public const string ShellProfilesContainerName = "ShellProfiles";
|
public const string ShellProfilesContainerName = "ShellProfiles";
|
||||||
public const string SshProfilesContainerName = "SshProfiles";
|
|
||||||
public const byte TerminalBufferRequestIdentifier = 0;
|
|
||||||
public const string SshCommandName = "ssh";
|
|
||||||
public const string MoshCommandName = "mosh";
|
|
||||||
public const string ExecutedCommandsContainerName = "HistoryContainer";
|
|
||||||
public const string TerminalViewModelStateId = "TerminalViewModelState";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Dialogs
|
|
||||||
{
|
|
||||||
public interface IAboutDialog
|
|
||||||
{
|
|
||||||
Task ShowAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Dialogs
|
|
||||||
{
|
|
||||||
public interface ICustomCommandDialog
|
|
||||||
{
|
|
||||||
Task<ShellProfile> GetCustomCommandAsync(ShellProfile input = null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,11 @@
|
|||||||
|
namespace FluentTerminal.App.Services.Dialogs
|
||||||
|
{
|
||||||
|
public interface ISshConnectionInfo
|
||||||
|
{
|
||||||
|
string Host { get; set; }
|
||||||
|
|
||||||
|
ushort Port { get; set; }
|
||||||
|
|
||||||
|
string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
using FluentTerminal.Models;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Dialogs
|
namespace FluentTerminal.App.Services.Dialogs
|
||||||
{
|
{
|
||||||
public interface ISshConnectionInfoDialog
|
public interface ISshConnectionInfoDialog
|
||||||
{
|
{
|
||||||
Task<SshProfile> GetSshConnectionInfoAsync(SshProfile input = null);
|
Task<ISshConnectionInfo> GetSshConnectionInfoAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using Windows.UI.Xaml;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.EventArgs
|
|
||||||
{
|
|
||||||
public class NewTabRequestedEventArgs : System.EventArgs
|
|
||||||
{
|
|
||||||
public DragEventArgs DragEventArgs { get; set; }
|
|
||||||
public int Position { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
using System;
|
namespace FluentTerminal.App.Services.EventArgs
|
||||||
using FluentTerminal.Models;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.EventArgs
|
|
||||||
{
|
{
|
||||||
public class NewWindowRequestedEventArgs : System.EventArgs
|
public class NewWindowRequestedEventArgs : System.EventArgs
|
||||||
{
|
{
|
||||||
public ShellProfile Profile { get; }
|
public bool ShowProfileSelection { get; set; }
|
||||||
|
|
||||||
public NewWindowRequestedEventArgs(ShellProfile profile) =>
|
|
||||||
Profile = profile ?? throw new ArgumentNullException(nameof(profile));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Exceptions
|
|
||||||
{
|
|
||||||
public class ReadTextFileException : Exception
|
|
||||||
{
|
|
||||||
public ReadTextFileException() : base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadTextFileException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadTextFileException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ReadTextFileException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Exceptions
|
|
||||||
{
|
|
||||||
public class SaveTextFileException : Exception
|
|
||||||
{
|
|
||||||
public SaveTextFileException() : base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SaveTextFileException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SaveTextFileException(string message, Exception innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SaveTextFileException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,13 +6,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||||
<PackageReference Include="Microsoft.Toolkit.Mvvm" Version="7.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="PListNet" Version="2.0.3" />
|
||||||
<PackageReference Include="PListNet" Version="3.2.0" />
|
<PackageReference Include="RestSharp" Version="106.6.9" />
|
||||||
<PackageReference Include="RestSharp" Version="106.12.0" />
|
<PackageReference Include="Serilog.Sinks.Async" Version="1.3.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
|
||||||
<PackageReference Include="System.Runtime.InteropServices.WindowsRuntime" Version="4.3.0" />
|
<PackageReference Include="System.Runtime.InteropServices.WindowsRuntime" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.Foundation.Collections;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services
|
namespace FluentTerminal.App.Services
|
||||||
{
|
{
|
||||||
public interface IAppServiceConnection
|
public interface IAppServiceConnection
|
||||||
{
|
{
|
||||||
event EventHandler<ValueSet> MessageReceived;
|
event EventHandler<IDictionary<string, string>> MessageReceived;
|
||||||
|
|
||||||
Task<ValueSet> SendMessageAsync(ValueSet message);
|
Task<IDictionary<string, string>> SendMessageAsync(IDictionary<string, string> message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,8 +14,6 @@ namespace FluentTerminal.App.Services
|
|||||||
|
|
||||||
void Delete(string key);
|
void Delete(string key);
|
||||||
|
|
||||||
void Clear();
|
|
||||||
|
|
||||||
IEnumerable<object> GetAll();
|
IEnumerable<object> GetAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services
|
|
||||||
{
|
|
||||||
public interface IApplicationLanguageService
|
|
||||||
{
|
|
||||||
IEnumerable<string> Languages { get; }
|
|
||||||
|
|
||||||
void SetLanguage(string language);
|
|
||||||
|
|
||||||
string GetCurrentLanguage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using FluentTerminal.App.Services.EventArgs;
|
using FluentTerminal.App.Services.EventArgs;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.UI.Core;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services
|
namespace FluentTerminal.App.Services
|
||||||
{
|
{
|
||||||
@@ -15,13 +14,9 @@ namespace FluentTerminal.App.Services
|
|||||||
int Id { get; }
|
int Id { get; }
|
||||||
string Title { get; set; }
|
string Title { get; set; }
|
||||||
|
|
||||||
Task ExecuteOnUiThreadAsync(Action action, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal,
|
Task RunOnDispatcherThread(Action action);
|
||||||
bool enforceNewSchedule = false);
|
|
||||||
|
|
||||||
Task<T> ExecuteOnUiThreadAsync<T>(Func<T> func, CoreDispatcherPriority priority = CoreDispatcherPriority.Normal,
|
Task<bool> TryClose();
|
||||||
bool enforceNewSchedule = false);
|
|
||||||
|
|
||||||
Task<bool> TryCloseAsync();
|
|
||||||
|
|
||||||
bool ToggleFullScreen();
|
bool ToggleFullScreen();
|
||||||
bool IsApiContractPresent(string api, ushort version);
|
bool IsApiContractPresent(string api, ushort version);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace FluentTerminal.App.Services
|
|||||||
{
|
{
|
||||||
public interface IClipboardService
|
public interface IClipboardService
|
||||||
{
|
{
|
||||||
Task<string> GetTextAsync();
|
Task<string> GetText();
|
||||||
|
|
||||||
void SetText(string text);
|
void SetText(string text);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services
|
|
||||||
{
|
|
||||||
public interface ICommandHistoryService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the most recent <paramref name="top"/> commands from history.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="includeProfiles">Indicates if never used saved profiles should also be returned.</param>
|
|
||||||
/// <param name="top">Maximal number of items to return.</param>
|
|
||||||
/// <param name="profilesProvider">List of all saved profiles. If this argument isn't specified, the method will
|
|
||||||
/// get the list from <see cref="ISettingsService"/>. The purpose of this argument is to avoid loading the
|
|
||||||
/// profiles from settings again (since it includes file system access and JSON deserialization), if we
|
|
||||||
/// already have them loaded.</param>
|
|
||||||
List<ExecutedCommand> GetHistoryRecentFirst(bool includeProfiles = false, int top = int.MaxValue,
|
|
||||||
Func<List<ShellProfile>> profilesProvider = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the most used <paramref name="top"/> commands from history.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="includeProfiles">Indicates if never used saved profiles should also be returned.</param>
|
|
||||||
/// <param name="top">Maximal number of items to return.</param>
|
|
||||||
/// <param name="profilesProvider">List of all saved profiles. If this argument isn't specified, the method will
|
|
||||||
/// get the list from <see cref="ISettingsService"/>. The purpose of this argument is to avoid loading the
|
|
||||||
/// profiles from settings again (since it includes file system access and JSON deserialization), if we
|
|
||||||
/// already have them loaded.</param>
|
|
||||||
List<ExecutedCommand> GetHistoryMostUsedFirst(bool includeProfiles = false, int top = int.MaxValue,
|
|
||||||
Func<List<ShellProfile>> profilesProvider = null);
|
|
||||||
|
|
||||||
void MarkUsed(ShellProfile profile);
|
|
||||||
|
|
||||||
void Delete(ExecutedCommand executedCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,24 @@
|
|||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using FluentTerminal.App.Services.Dialogs;
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services
|
namespace FluentTerminal.App.Services
|
||||||
{
|
{
|
||||||
public enum DialogButton
|
public enum DialogButton
|
||||||
{
|
{
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
OK,
|
OK,
|
||||||
Cancel
|
Cancel
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IDialogService
|
public interface IDialogService
|
||||||
{
|
{
|
||||||
Task<DialogButton> ShowMessageDialogAsync(string title, string content, params DialogButton[] buttons);
|
Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons);
|
||||||
|
|
||||||
|
Task<ShellProfile> ShowProfileSelectionDialogAsync();
|
||||||
|
|
||||||
Task<KeyBinding> ShowCreateKeyBindingDialog();
|
Task<KeyBinding> ShowCreateKeyBindingDialog();
|
||||||
|
|
||||||
Task<string> ShowInputDialogAsync(string title);
|
Task<string> ShowInputDialogAsync(string title);
|
||||||
|
|
||||||
Task<SshProfile> ShowSshConnectionInfoDialogAsync(SshProfile input = null);
|
Task<ISshConnectionInfo> ShowSshConnectionInfoDialogAsync();
|
||||||
|
|
||||||
Task<ShellProfile> ShowCustomCommandDialogAsync(ShellProfile input = null);
|
|
||||||
|
|
||||||
Task ShowAboutDialogAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,10 @@ namespace FluentTerminal.App.Services
|
|||||||
{
|
{
|
||||||
public interface IFileSystemService
|
public interface IFileSystemService
|
||||||
{
|
{
|
||||||
Task<File> OpenFileAsync(IEnumerable<string> fileTypes);
|
Task<File> OpenFile(IEnumerable<string> fileTypes);
|
||||||
|
|
||||||
Task<string> BrowseForDirectoryAsync();
|
Task<string> BrowseForDirectory();
|
||||||
|
|
||||||
Task SaveTextFileAsync(string name, string fileTypeDescription, string fileType, string content);
|
Task SaveTextFile(string name, string fileTypeDescription, string fileType, string content);
|
||||||
|
|
||||||
Task<ImageFile> SaveImageInRoamingAsync(ImageFile imageFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using FluentTerminal.Models;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services
|
|
||||||
{
|
|
||||||
public interface IImageFileSystemService
|
|
||||||
{
|
|
||||||
Task<ImageFile> ImportTemporaryImageFileAsync(IEnumerable<string> fileTypes);
|
|
||||||
|
|
||||||
Task RemoveTemporaryBackgroundThemeImageAsync();
|
|
||||||
|
|
||||||
Task RemoveImportedImageAsync(string fileName);
|
|
||||||
|
|
||||||
string EncodeImage(ImageFile imageFile);
|
|
||||||
|
|
||||||
Task<ImageFile> ImportThemeImageAsync(ImageFile backgroundImage, string encodedImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,5 @@ namespace FluentTerminal.App.Services
|
|||||||
|
|
||||||
void SendCommand(string command);
|
void SendCommand(string command);
|
||||||
void DeregisterCommandHandler(string command);
|
void DeregisterCommandHandler(string command);
|
||||||
void ClearCommandHandlers();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,16 @@ namespace FluentTerminal.App.Services
|
|||||||
{
|
{
|
||||||
public interface ISettingsService
|
public interface ISettingsService
|
||||||
{
|
{
|
||||||
|
event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||||
|
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;
|
||||||
|
|
||||||
void DeleteShellProfile(Guid id);
|
void DeleteShellProfile(Guid id);
|
||||||
void DeleteSshProfile(Guid id);
|
|
||||||
void DeleteTheme(Guid id);
|
void DeleteTheme(Guid id);
|
||||||
ApplicationSettings GetApplicationSettings();
|
ApplicationSettings GetApplicationSettings();
|
||||||
TerminalTheme GetCurrentTheme();
|
TerminalTheme GetCurrentTheme();
|
||||||
@@ -16,28 +24,18 @@ namespace FluentTerminal.App.Services
|
|||||||
Guid GetDefaultShellProfileId();
|
Guid GetDefaultShellProfileId();
|
||||||
IDictionary<string, ICollection<KeyBinding>> GetCommandKeyBindings();
|
IDictionary<string, ICollection<KeyBinding>> GetCommandKeyBindings();
|
||||||
IEnumerable<ShellProfile> GetShellProfiles();
|
IEnumerable<ShellProfile> GetShellProfiles();
|
||||||
IEnumerable<SshProfile> GetSshProfiles();
|
|
||||||
/// <summary>
|
|
||||||
/// Returns union of <see cref="GetShellProfiles"/> and <see cref="GetSshProfiles"/>.
|
|
||||||
/// </summary>
|
|
||||||
IEnumerable<ShellProfile> GetAllProfiles();
|
|
||||||
IEnumerable<TabTheme> GetTabThemes();
|
IEnumerable<TabTheme> GetTabThemes();
|
||||||
TerminalOptions GetTerminalOptions();
|
TerminalOptions GetTerminalOptions();
|
||||||
TerminalTheme GetTheme(Guid id);
|
TerminalTheme GetTheme(Guid id);
|
||||||
IEnumerable<TerminalTheme> GetThemes();
|
IEnumerable<TerminalTheme> GetThemes();
|
||||||
void ResetKeyBindings();
|
void ResetKeyBindings();
|
||||||
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
||||||
void NotifyApplicationSettingsChanged(ApplicationSettings applicationSettings);
|
|
||||||
void SaveCurrentThemeId(Guid id);
|
void SaveCurrentThemeId(Guid id);
|
||||||
void SaveDefaultShellProfileId(Guid id);
|
void SaveDefaultShellProfileId(Guid id);
|
||||||
void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings);
|
void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings);
|
||||||
void SaveShellProfile(ShellProfile shellProfile, bool newShell = false);
|
void SaveShellProfile(ShellProfile shellProfile, bool newShell = false);
|
||||||
void SaveSshProfile(SshProfile sshProfile, bool newShell = false);
|
|
||||||
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
||||||
void SaveTheme(TerminalTheme theme, bool newTheme = false);
|
void SaveTheme(TerminalTheme theme, bool newTheme = false);
|
||||||
ShellProfile GetShellProfile(Guid id);
|
ShellProfile GetShellProfile(Guid id);
|
||||||
SshProfile GetSshProfile(Guid id);
|
|
||||||
string ExportSettings();
|
|
||||||
void ImportSettings(string serializedSettings);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using FluentTerminal.Models;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services
|
|
||||||
{
|
|
||||||
public interface IShellProfileMigrationService
|
|
||||||
{
|
|
||||||
void Migrate(ShellProfile profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,8 +5,8 @@ namespace FluentTerminal.App.Services
|
|||||||
{
|
{
|
||||||
public interface IStartupTaskService
|
public interface IStartupTaskService
|
||||||
{
|
{
|
||||||
Task DisableStartupTaskAsync();
|
Task DisableStartupTask();
|
||||||
Task<StartupTaskStatus> EnableStartupTaskAsync();
|
Task<StartupTaskStatus> EnableStartupTask();
|
||||||
Task<StartupTaskStatus> GetStatusAsync();
|
Task<StartupTaskStatus> GetStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,5 @@ namespace FluentTerminal.App.Services
|
|||||||
IEnumerable<string> SupportedFileTypes { get; }
|
IEnumerable<string> SupportedFileTypes { get; }
|
||||||
|
|
||||||
Task<TerminalTheme> Parse(string fileName, Stream fileContent);
|
Task<TerminalTheme> Parse(string fileName, Stream fileContent);
|
||||||
|
|
||||||
Task<ExportedTerminalTheme> Import(string fileName, Stream fileContent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,47 +8,22 @@ namespace FluentTerminal.App.Services
|
|||||||
{
|
{
|
||||||
public interface ITrayProcessCommunicationService
|
public interface ITrayProcessCommunicationService
|
||||||
{
|
{
|
||||||
event EventHandler<TerminalExitStatus> TerminalExited;
|
event EventHandler<int> TerminalExited;
|
||||||
|
|
||||||
void Initialize(IAppServiceConnection appServiceConnection);
|
void Initialize(IAppServiceConnection appServiceConnection);
|
||||||
|
|
||||||
Task<CreateTerminalResponse> CreateTerminalAsync(byte id, TerminalSize size, ShellProfile shellProfile,
|
Task<CreateTerminalResponse> CreateTerminal(int id, TerminalSize size, ShellProfile shellProfile, SessionType sessionType);
|
||||||
SessionType sessionType);
|
|
||||||
|
|
||||||
Task<PauseTerminalOutputResponse> PauseTerminalOutputAsync(byte id, bool pause);
|
Task ResizeTerminal(int id, TerminalSize size);
|
||||||
|
|
||||||
Task ResizeTerminalAsync(byte id, TerminalSize size);
|
Task UpdateToggleWindowKeyBindings();
|
||||||
|
|
||||||
Task UpdateToggleWindowKeyBindingsAsync();
|
Task Write(int terminalId, byte[] data);
|
||||||
|
|
||||||
Task WriteAsync(byte terminalId, byte[] data);
|
void SubscribeForTerminalOutput(int terminalId, Action<byte[]> callback);
|
||||||
|
|
||||||
void SubscribeForTerminalOutput(byte terminalId, Action<byte[]> callback);
|
Task CloseTerminal(int terminalId);
|
||||||
|
Task<GetAvailablePortResponse> GetAvailablePort();
|
||||||
void UnsubscribeFromTerminalOutput(byte terminalId);
|
int GetNextTerminalId();
|
||||||
|
|
||||||
Task CloseTerminalAsync(byte terminalId);
|
|
||||||
|
|
||||||
byte GetNextTerminalId();
|
|
||||||
|
|
||||||
Task<string> GetUserNameAsync();
|
|
||||||
|
|
||||||
Task SaveTextFileAsync(string path, string content);
|
|
||||||
|
|
||||||
Task<string> ReadTextFileAsync(string path);
|
|
||||||
|
|
||||||
Task<string> GetSshConfigDirAsync();
|
|
||||||
|
|
||||||
Task<string[]> GetFilesFromSshConfigDirAsync();
|
|
||||||
|
|
||||||
Task<bool> CheckFileExistsAsync(string path);
|
|
||||||
|
|
||||||
Task MuteTerminalAsync(bool mute);
|
|
||||||
|
|
||||||
void UpdateSettings(ApplicationSettings settings);
|
|
||||||
|
|
||||||
Task<string> GetCommandPathAsync(string command);
|
|
||||||
|
|
||||||
Task QuitApplicationAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ namespace FluentTerminal.App.Services
|
|||||||
{
|
{
|
||||||
public interface IUpdateService
|
public interface IUpdateService
|
||||||
{
|
{
|
||||||
Task CheckForUpdateAsync(bool notifyNoUpdate = false);
|
Task CheckForUpdate(bool notifyNoUpdate = false);
|
||||||
Version GetCurrentVersion();
|
Version GetCurrentVersion();
|
||||||
Task<Version> GetLatestVersionAsync();
|
Task<Version> GetLatestVersionAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Windows.Foundation.Metadata;
|
|
||||||
using Windows.Storage;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Implementation
|
namespace FluentTerminal.App.Services.Implementation
|
||||||
{
|
{
|
||||||
@@ -11,14 +9,6 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
public ApplicationSettings GetDefaultApplicationSettings()
|
public ApplicationSettings GetDefaultApplicationSettings()
|
||||||
{
|
{
|
||||||
var logDirectoryPath = string.Empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Accessing ApplicationData.Current during run of unit tests causes
|
|
||||||
// "System.InvalidOperationException : The process has no package identity" exception.
|
|
||||||
logDirectoryPath = ApplicationData.Current.LocalCacheFolder.Path;
|
|
||||||
} catch (InvalidOperationException) { }
|
|
||||||
|
|
||||||
return new ApplicationSettings
|
return new ApplicationSettings
|
||||||
{
|
{
|
||||||
ConfirmClosingTabs = false,
|
ConfirmClosingTabs = false,
|
||||||
@@ -26,23 +16,15 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
UnderlineSelectedTab = false,
|
UnderlineSelectedTab = false,
|
||||||
InactiveTabColorMode = InactiveTabColorMode.Background,
|
InactiveTabColorMode = InactiveTabColorMode.Background,
|
||||||
NewTerminalLocation = NewTerminalLocation.Tab,
|
NewTerminalLocation = NewTerminalLocation.Tab,
|
||||||
TabWindowCascadingAppMenu = true,
|
|
||||||
TabsPosition = TabsPosition.Top,
|
TabsPosition = TabsPosition.Top,
|
||||||
CopyOnSelect = false,
|
CopyOnSelect = false,
|
||||||
MouseMiddleClickAction = MouseAction.None,
|
MouseMiddleClickAction = MouseAction.None,
|
||||||
MouseRightClickAction = MouseAction.ContextMenu,
|
MouseRightClickAction = MouseAction.ContextMenu,
|
||||||
|
AlwaysShowTabs = false,
|
||||||
|
AlwaysUseWinPty = true,
|
||||||
ShowNewOutputIndicator = false,
|
ShowNewOutputIndicator = false,
|
||||||
EnableTrayIcon = true,
|
EnableTrayIcon = true,
|
||||||
ShowCustomTitleInTitlebar = true,
|
ShowCustomTitleInTitlebar = true
|
||||||
UseMoshByDefault = true,
|
|
||||||
AutoFallbackToWindowsUsernameInLinks = true,
|
|
||||||
RTrimCopiedLines = true,
|
|
||||||
MuteTerminalBeeps = true,
|
|
||||||
EnableLogging = false,
|
|
||||||
PrintableOutputOnly = true,
|
|
||||||
LogDirectoryPath = logDirectoryPath,
|
|
||||||
UseConPty = true,
|
|
||||||
ShowTextCopied = false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +38,9 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
new KeyBinding
|
new KeyBinding
|
||||||
{
|
{
|
||||||
Command = nameof(Command.ToggleWindow),
|
Command = nameof(Command.ToggleWindow),
|
||||||
|
Ctrl = false,
|
||||||
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.Scroll
|
Key = (int)ExtendedVirtualKey.Scroll
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -67,6 +52,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.NextTab),
|
Command = nameof(Command.NextTab),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.Tab
|
Key = (int)ExtendedVirtualKey.Tab
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -78,6 +65,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.PreviousTab),
|
Command = nameof(Command.PreviousTab),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
|
Alt = false,
|
||||||
Shift = true,
|
Shift = true,
|
||||||
Key = (int)ExtendedVirtualKey.Tab
|
Key = (int)ExtendedVirtualKey.Tab
|
||||||
}
|
}
|
||||||
@@ -97,35 +85,13 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
new KeyBinding
|
new KeyBinding
|
||||||
{
|
{
|
||||||
Command = command.ToString(),
|
Command = command.ToString(),
|
||||||
Alt = true,
|
Ctrl = true,
|
||||||
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.Number1 + (command - Command.SwitchToTerm1)
|
Key = (int)ExtendedVirtualKey.Number1 + (command - Command.SwitchToTerm1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
case Command.DuplicateTab:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.DuplicateTab),
|
|
||||||
Ctrl = true,
|
|
||||||
Shift = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.D
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case Command.ReconnectTab:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.ReconnectTab),
|
|
||||||
Ctrl = true,
|
|
||||||
Alt = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.R
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case Command.NewTab:
|
case Command.NewTab:
|
||||||
return new List<KeyBinding>
|
return new List<KeyBinding>
|
||||||
{
|
{
|
||||||
@@ -133,59 +99,38 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.NewTab),
|
Command = nameof(Command.NewTab),
|
||||||
Ctrl = true,
|
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,
|
Shift = true,
|
||||||
Key = (int)ExtendedVirtualKey.T
|
Key = (int)ExtendedVirtualKey.T
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
case Command.NewSshTab:
|
case Command.NewRemoteTab:
|
||||||
return new List<KeyBinding>
|
return new List<KeyBinding>
|
||||||
{
|
{
|
||||||
new KeyBinding
|
new KeyBinding
|
||||||
{
|
{
|
||||||
Command = nameof(Command.NewSshTab),
|
Command = nameof(Command.NewRemoteTab),
|
||||||
Ctrl = true,
|
Ctrl = false,
|
||||||
Shift = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.Y
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case Command.NewSshWindow:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.NewSshWindow),
|
|
||||||
Ctrl = true,
|
|
||||||
Alt = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.Y
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case Command.NewCustomCommandTab:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.NewCustomCommandTab),
|
|
||||||
Ctrl = true,
|
|
||||||
Alt = true,
|
Alt = true,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.T
|
Key = (int)ExtendedVirtualKey.T
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
case Command.NewCustomCommandWindow:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.NewCustomCommandWindow),
|
|
||||||
Ctrl = true,
|
|
||||||
Alt = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.N
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case Command.ChangeTabTitle:
|
case Command.ChangeTabTitle:
|
||||||
return new List<KeyBinding>
|
return new List<KeyBinding>
|
||||||
{
|
{
|
||||||
@@ -193,6 +138,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.ChangeTabTitle),
|
Command = nameof(Command.ChangeTabTitle),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
|
Alt = false,
|
||||||
Shift = true,
|
Shift = true,
|
||||||
Key = (int)ExtendedVirtualKey.R
|
Key = (int)ExtendedVirtualKey.R
|
||||||
}
|
}
|
||||||
@@ -205,7 +151,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.CloseTab),
|
Command = nameof(Command.CloseTab),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Shift = true,
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.W
|
Key = (int)ExtendedVirtualKey.W
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -217,6 +164,20 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.NewWindow),
|
Command = nameof(Command.NewWindow),
|
||||||
Ctrl = true,
|
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,
|
Shift = true,
|
||||||
Key = (int)ExtendedVirtualKey.N
|
Key = (int)ExtendedVirtualKey.N
|
||||||
}
|
}
|
||||||
@@ -229,7 +190,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.ShowSettings),
|
Command = nameof(Command.ShowSettings),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Shift = true,
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.Comma
|
Key = (int)ExtendedVirtualKey.Comma
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -241,7 +203,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.Copy),
|
Command = nameof(Command.Copy),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Shift = true,
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.C
|
Key = (int)ExtendedVirtualKey.C
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -253,7 +216,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.Paste),
|
Command = nameof(Command.Paste),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Shift = true,
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.V
|
Key = (int)ExtendedVirtualKey.V
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -266,7 +230,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.PasteWithoutNewlines),
|
Command = nameof(Command.PasteWithoutNewlines),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Alt = true,
|
Alt = false,
|
||||||
|
Shift = true,
|
||||||
Key = (int)ExtendedVirtualKey.V
|
Key = (int)ExtendedVirtualKey.V
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -278,34 +243,22 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.Search),
|
Command = nameof(Command.Search),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Shift = true,
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.F
|
Key = (int)ExtendedVirtualKey.F
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
case Command.CloseSearch:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.CloseSearch),
|
|
||||||
Key = (int)ExtendedVirtualKey.Escape
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
case Command.ToggleFullScreen:
|
case Command.ToggleFullScreen:
|
||||||
return new List<KeyBinding>
|
return new List<KeyBinding>
|
||||||
{
|
{
|
||||||
new KeyBinding
|
new KeyBinding
|
||||||
{
|
{
|
||||||
Command = nameof(Command.ToggleFullScreen),
|
Command = nameof(Command.ToggleFullScreen),
|
||||||
|
Ctrl = false,
|
||||||
Alt = true,
|
Alt = true,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.Enter
|
Key = (int)ExtendedVirtualKey.Enter
|
||||||
},
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.ToggleFullScreen),
|
|
||||||
Key = (int)ExtendedVirtualKey.F11
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -316,7 +269,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.SelectAll),
|
Command = nameof(Command.SelectAll),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Shift = true,
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.A
|
Key = (int)ExtendedVirtualKey.A
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -328,43 +282,14 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Command = nameof(Command.Clear),
|
Command = nameof(Command.Clear),
|
||||||
Ctrl = true,
|
Ctrl = true,
|
||||||
Alt = true,
|
Alt = false,
|
||||||
|
Shift = false,
|
||||||
Key = (int)ExtendedVirtualKey.L
|
Key = (int)ExtendedVirtualKey.L
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
case Command.IncreaseFontSize:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.IncreaseFontSize),
|
|
||||||
Ctrl = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.Plus
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case Command.DecreaseFontSize:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.DecreaseFontSize),
|
|
||||||
Ctrl = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.Minus
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case Command.ResetFontSize:
|
|
||||||
return new List<KeyBinding>
|
|
||||||
{
|
|
||||||
new KeyBinding
|
|
||||||
{
|
|
||||||
Command = nameof(Command.ResetFontSize),
|
|
||||||
Ctrl = true,
|
|
||||||
Key = (int)ExtendedVirtualKey.Number0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
throw new InvalidOperationException($"Default keybindings for Command '{command}' are missing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid GetDefaultShellProfileId()
|
public Guid GetDefaultShellProfileId()
|
||||||
@@ -379,7 +304,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
new TabTheme
|
new TabTheme
|
||||||
{
|
{
|
||||||
Id = 0,
|
Id = 0,
|
||||||
Name = "TabTheme.None",
|
Name = "None",
|
||||||
BackgroundOpacity = double.NaN,
|
BackgroundOpacity = double.NaN,
|
||||||
BackgroundPointerOverOpacity = double.NaN,
|
BackgroundPointerOverOpacity = double.NaN,
|
||||||
BackgroundPressedOpacity = double.NaN,
|
BackgroundPressedOpacity = double.NaN,
|
||||||
@@ -390,42 +315,41 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
new TabTheme
|
new TabTheme
|
||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
Name = "TabTheme.Red",
|
Name = "Red",
|
||||||
Color = "#E81123"
|
Color = "#E81123"
|
||||||
},
|
},
|
||||||
new TabTheme
|
new TabTheme
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
Name = "TabTheme.Green",
|
Name = "Green",
|
||||||
Color = "#10893E"
|
Color = "#10893E"
|
||||||
},
|
},
|
||||||
new TabTheme
|
new TabTheme
|
||||||
{
|
{
|
||||||
Id = 3,
|
Id = 3,
|
||||||
Name = "TabTheme.Blue",
|
Name = "Blue",
|
||||||
Color = "#0078D7"
|
Color = "#0078D7"
|
||||||
},
|
},
|
||||||
new TabTheme
|
new TabTheme
|
||||||
{
|
{
|
||||||
Id = 4,
|
Id = 4,
|
||||||
Name = "TabTheme.Purple",
|
Name = "Purple",
|
||||||
Color = "#881798"
|
Color = "#881798"
|
||||||
},
|
},
|
||||||
new TabTheme
|
new TabTheme
|
||||||
{
|
{
|
||||||
Id = 5,
|
Id = 5,
|
||||||
Name = "TabTheme.Orange",
|
Name = "Orange",
|
||||||
Color = "#FF8C00"
|
Color = "#FF8C00"
|
||||||
},
|
},
|
||||||
new TabTheme
|
new TabTheme
|
||||||
{
|
{
|
||||||
Id = 6,
|
Id = 6,
|
||||||
Name = "TabTheme.Teal",
|
Name = "Teal",
|
||||||
Color = "#00B7C3"
|
Color = "#00B7C3"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public TerminalOptions GetDefaultTerminalOptions()
|
public TerminalOptions GetDefaultTerminalOptions()
|
||||||
{
|
{
|
||||||
return new TerminalOptions
|
return new TerminalOptions
|
||||||
@@ -436,12 +360,10 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
ScrollBarStyle = ScrollBarStyle.Hidden,
|
ScrollBarStyle = ScrollBarStyle.Hidden,
|
||||||
FontFamily = "Consolas",
|
FontFamily = "Consolas",
|
||||||
FontSize = 13,
|
FontSize = 13,
|
||||||
FontWeight = 400,
|
BoldText = false,
|
||||||
BackgroundOpacity = 0.8,
|
BackgroundOpacity = 0.8,
|
||||||
Padding = 12,
|
Padding = 12,
|
||||||
ScrollBackLimit = 1000,
|
ScrollBackLimit = 1000
|
||||||
WordSeparator = " ()[]{}:;|│!&*<>@"&squo;",
|
|
||||||
UseAcrylicBackground = true
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,17 +380,11 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Id = GetDefaultShellProfileId(),
|
Id = GetDefaultShellProfileId(),
|
||||||
Name = "Powershell",
|
Name = "Powershell",
|
||||||
MigrationVersion = ShellProfile.CurrentMigrationVersion,
|
|
||||||
Arguments = string.Empty,
|
Arguments = string.Empty,
|
||||||
Location = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe",
|
Location = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe",
|
||||||
PreInstalled = true,
|
PreInstalled = true,
|
||||||
WorkingDirectory = string.Empty,
|
WorkingDirectory = string.Empty,
|
||||||
UseConPty = ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8), // Windows 10 1903+
|
LineEndingTranslation = LineEndingStyle.DoNotModify,
|
||||||
UseBuffer = false,
|
|
||||||
EnvironmentVariables = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["TERM"] = "xterm-256color"
|
|
||||||
},
|
|
||||||
KeyBindings = new []
|
KeyBindings = new []
|
||||||
{
|
{
|
||||||
new KeyBinding
|
new KeyBinding
|
||||||
@@ -486,17 +402,11 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Id = Guid.Parse("ab942a61-7673-4755-9bd8-765aff91d9a3"),
|
Id = Guid.Parse("ab942a61-7673-4755-9bd8-765aff91d9a3"),
|
||||||
Name = "CMD",
|
Name = "CMD",
|
||||||
MigrationVersion = ShellProfile.CurrentMigrationVersion,
|
|
||||||
Arguments = string.Empty,
|
Arguments = string.Empty,
|
||||||
Location = @"C:\Windows\System32\cmd.exe",
|
Location = @"C:\Windows\System32\cmd.exe",
|
||||||
PreInstalled = true,
|
PreInstalled = true,
|
||||||
WorkingDirectory = string.Empty,
|
WorkingDirectory = string.Empty,
|
||||||
UseConPty = ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7), // Windows 10 1809+
|
LineEndingTranslation = LineEndingStyle.DoNotModify,
|
||||||
UseBuffer = true,
|
|
||||||
EnvironmentVariables = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["TERM"] = "xterm-256color"
|
|
||||||
},
|
|
||||||
KeyBindings = new []
|
KeyBindings = new []
|
||||||
{
|
{
|
||||||
new KeyBinding
|
new KeyBinding
|
||||||
@@ -514,17 +424,11 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
Id= Guid.Parse("e5785ad6-584f-40cb-bdcd-d5b3b3953e7f"),
|
Id= Guid.Parse("e5785ad6-584f-40cb-bdcd-d5b3b3953e7f"),
|
||||||
Name = "WSL",
|
Name = "WSL",
|
||||||
MigrationVersion = ShellProfile.CurrentMigrationVersion,
|
|
||||||
Arguments = string.Empty,
|
Arguments = string.Empty,
|
||||||
Location = @"C:\windows\system32\wsl.exe",
|
Location = @"C:\windows\system32\wsl.exe",
|
||||||
PreInstalled = true,
|
PreInstalled = true,
|
||||||
WorkingDirectory = string.Empty,
|
WorkingDirectory = string.Empty,
|
||||||
UseConPty = ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7), // Windows 10 1809+
|
LineEndingTranslation = LineEndingStyle.DoNotModify,
|
||||||
UseBuffer = true, //TODO: Set to false if the buffer causes issues with WSL.
|
|
||||||
EnvironmentVariables = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
["TERM"] = "xterm-256color"
|
|
||||||
},
|
|
||||||
KeyBindings = new []
|
KeyBindings = new []
|
||||||
{
|
{
|
||||||
new KeyBinding
|
new KeyBinding
|
||||||
|
|||||||
@@ -7,24 +7,21 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
public class DialogService : IDialogService
|
public class DialogService : IDialogService
|
||||||
{
|
{
|
||||||
|
private readonly Func<IShellProfileSelectionDialog> _shellProfileSelectionDialogFactory;
|
||||||
private readonly Func<IMessageDialog> _messageDialogFactory;
|
private readonly Func<IMessageDialog> _messageDialogFactory;
|
||||||
private readonly Func<ICreateKeyBindingDialog> _createKeyBindingDialogFactory;
|
private readonly Func<ICreateKeyBindingDialog> _createKeyBindingDialogFactory;
|
||||||
private readonly Func<IInputDialog> _inputDialogFactory;
|
private readonly Func<IInputDialog> _inputDialogFactory;
|
||||||
private readonly Func<ISshConnectionInfoDialog> _sshConnectionInfoDialogFactory;
|
private readonly Func<ISshConnectionInfoDialog> _sshConnectionInfoDialogFactory;
|
||||||
private readonly Func<ICustomCommandDialog> _customCommandDialogFactory;
|
|
||||||
private readonly Func<IAboutDialog> _aboutDialogFactory;
|
|
||||||
|
|
||||||
public DialogService(Func<IMessageDialog> messageDialogFactory, Func<ICreateKeyBindingDialog> createKeyBindingDialogFactory,
|
public DialogService(Func<IShellProfileSelectionDialog> shellProfileSelectionDialogFactory,
|
||||||
Func<IInputDialog> inputDialogFactory, Func<ISshConnectionInfoDialog> sshConnectionInfoDialogFactory,
|
Func<IMessageDialog> messageDialogFactory, Func<ICreateKeyBindingDialog> createKeyBindingDialogFactory,
|
||||||
Func<ICustomCommandDialog> customCommandDialogFactory,
|
Func<IInputDialog> inputDialogFactory, Func<ISshConnectionInfoDialog> sshConnectionInfoDialogFactory)
|
||||||
Func<IAboutDialog> aboutDialogFactory)
|
|
||||||
{
|
{
|
||||||
|
_shellProfileSelectionDialogFactory = shellProfileSelectionDialogFactory;
|
||||||
_messageDialogFactory = messageDialogFactory;
|
_messageDialogFactory = messageDialogFactory;
|
||||||
_createKeyBindingDialogFactory = createKeyBindingDialogFactory;
|
_createKeyBindingDialogFactory = createKeyBindingDialogFactory;
|
||||||
_inputDialogFactory = inputDialogFactory;
|
_inputDialogFactory = inputDialogFactory;
|
||||||
_sshConnectionInfoDialogFactory = sshConnectionInfoDialogFactory;
|
_sshConnectionInfoDialogFactory = sshConnectionInfoDialogFactory;
|
||||||
_customCommandDialogFactory = customCommandDialogFactory;
|
|
||||||
_aboutDialogFactory = aboutDialogFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<KeyBinding> ShowCreateKeyBindingDialog()
|
public Task<KeyBinding> ShowCreateKeyBindingDialog()
|
||||||
@@ -34,7 +31,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
return dialog.CreateKeyBinding();
|
return dialog.CreateKeyBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<DialogButton> ShowMessageDialogAsync(string title, string content, params DialogButton[] buttons)
|
public Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(title))
|
if (string.IsNullOrWhiteSpace(title))
|
||||||
{
|
{
|
||||||
@@ -71,17 +68,14 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
return dialog.GetInput();
|
return dialog.GetInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<SshProfile> ShowSshConnectionInfoDialogAsync(SshProfile input = null) =>
|
public Task<ShellProfile> ShowProfileSelectionDialogAsync()
|
||||||
_sshConnectionInfoDialogFactory().GetSshConnectionInfoAsync(input);
|
|
||||||
|
|
||||||
public Task<ShellProfile> ShowCustomCommandDialogAsync(ShellProfile input = null) =>
|
|
||||||
_customCommandDialogFactory().GetCustomCommandAsync(input);
|
|
||||||
|
|
||||||
public Task ShowAboutDialogAsync()
|
|
||||||
{
|
{
|
||||||
var dialog = _aboutDialogFactory();
|
var dialog = _shellProfileSelectionDialogFactory();
|
||||||
|
|
||||||
return dialog.ShowAsync();
|
return dialog.SelectProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<ISshConnectionInfo> ShowSshConnectionInfoDialogAsync() =>
|
||||||
|
_sshConnectionInfoDialogFactory().GetSshConnectionInfoAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,20 +9,9 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
public class FluentTerminalThemeParser : IThemeParser
|
public class FluentTerminalThemeParser : IThemeParser
|
||||||
{
|
{
|
||||||
public IEnumerable<string> SupportedFileTypes { get; } = new[] { ".flutecolors" };
|
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".flutecolors" };
|
||||||
|
|
||||||
public Task<ExportedTerminalTheme> Import(string fileName, Stream fileContent)
|
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
||||||
{
|
|
||||||
return DeserializeThemeAsync<ExportedTerminalTheme>(fileName, fileContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
|
||||||
{
|
|
||||||
return DeserializeThemeAsync<TerminalTheme>(fileName, fileContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<T> DeserializeThemeAsync<T>(string fileName, Stream fileContent)
|
|
||||||
where T : TerminalTheme
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(fileName))
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
{
|
{
|
||||||
@@ -36,8 +25,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
|
|
||||||
using (var streamReader = new StreamReader(fileContent))
|
using (var streamReader = new StreamReader(fileContent))
|
||||||
{
|
{
|
||||||
var content = await streamReader.ReadToEndAsync().ConfigureAwait(false);
|
var content = await streamReader.ReadToEndAsync();
|
||||||
var theme = JsonConvert.DeserializeObject<T>(content);
|
var theme = JsonConvert.DeserializeObject<TerminalTheme>(content);
|
||||||
|
|
||||||
theme.PreInstalled = false;
|
theme.PreInstalled = false;
|
||||||
theme.Id = Guid.NewGuid();
|
theme.Id = Guid.NewGuid();
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Implementation
|
namespace FluentTerminal.App.Services.Implementation
|
||||||
{
|
{
|
||||||
@@ -15,7 +14,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
private const byte Opacity30Percent = 77;
|
private const byte Opacity30Percent = 77;
|
||||||
|
|
||||||
public IEnumerable<string> SupportedFileTypes { get; } = new[] { ".itermcolors" };
|
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".itermcolors" };
|
||||||
|
|
||||||
private static class ITermThemeKeys
|
private static class ITermThemeKeys
|
||||||
{
|
{
|
||||||
@@ -37,7 +36,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
public const string Ansi15Color = "Ansi 15 Color";
|
public const string Ansi15Color = "Ansi 15 Color";
|
||||||
|
|
||||||
public const string BackgroundColor = "Background Color";
|
public const string BackgroundColor = "Background Color";
|
||||||
//public const string BoldColor = "Bold Color";
|
public const string BoldColor = "Bold Color";
|
||||||
public const string CursorColor = "Cursor Color";
|
public const string CursorColor = "Cursor Color";
|
||||||
public const string CursorTextColor = "Cursor Text Color";
|
public const string CursorTextColor = "Cursor Text Color";
|
||||||
public const string ForegroundColor = "Foreground Color";
|
public const string ForegroundColor = "Foreground Color";
|
||||||
@@ -52,7 +51,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
public const string RedComponent = "Red Component";
|
public const string RedComponent = "Red Component";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(fileName))
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
{
|
{
|
||||||
@@ -66,13 +65,13 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
|
|
||||||
var node = PList.Load(fileContent) as DictionaryNode ?? throw new ParseThemeException("Root node was not a dictionary.");
|
var node = PList.Load(fileContent) as DictionaryNode ?? throw new ParseThemeException("Root node was not a dictionary.");
|
||||||
|
|
||||||
return Task.FromResult(new TerminalTheme
|
return new TerminalTheme
|
||||||
{
|
{
|
||||||
Name = Path.GetFileNameWithoutExtension(fileName),
|
Name = Path.GetFileNameWithoutExtension(fileName),
|
||||||
Colors = GetColors(node),
|
Colors = GetColors(node),
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
PreInstalled = false
|
PreInstalled = false
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private TerminalColors GetColors(DictionaryNode themeDictionary)
|
private TerminalColors GetColors(DictionaryNode themeDictionary)
|
||||||
@@ -84,8 +83,6 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
Cursor = GetColorString(themeDictionary[ITermThemeKeys.CursorColor]),
|
Cursor = GetColorString(themeDictionary[ITermThemeKeys.CursorColor]),
|
||||||
CursorAccent = GetColorString(themeDictionary[ITermThemeKeys.CursorTextColor]),
|
CursorAccent = GetColorString(themeDictionary[ITermThemeKeys.CursorTextColor]),
|
||||||
Selection = GetColorString(themeDictionary[ITermThemeKeys.SelectionColor], Opacity30Percent),
|
Selection = GetColorString(themeDictionary[ITermThemeKeys.SelectionColor], Opacity30Percent),
|
||||||
SelectionForeground = GetColorString(themeDictionary[ITermThemeKeys.SelectedTextColor]),
|
|
||||||
SelectionBackground = GetColorString(themeDictionary[ITermThemeKeys.SelectionColor]),
|
|
||||||
Black = GetColorString(themeDictionary[ITermThemeKeys.Ansi0Color]),
|
Black = GetColorString(themeDictionary[ITermThemeKeys.Ansi0Color]),
|
||||||
Red = GetColorString(themeDictionary[ITermThemeKeys.Ansi1Color]),
|
Red = GetColorString(themeDictionary[ITermThemeKeys.Ansi1Color]),
|
||||||
Green = GetColorString(themeDictionary[ITermThemeKeys.Ansi2Color]),
|
Green = GetColorString(themeDictionary[ITermThemeKeys.Ansi2Color]),
|
||||||
@@ -113,6 +110,8 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
var green = dictionaryNode[ITermThemeColorKeys.GreenComponent] as RealNode ?? throw new ParseThemeException("Green 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 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)
|
if (alpha == byte.MaxValue)
|
||||||
{
|
{
|
||||||
return $"#{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
|
return $"#{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
|
||||||
@@ -134,30 +133,5 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
return (alpha / 256.0).ToString(CultureInfo.InvariantCulture);
|
return (alpha / 256.0).ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ExportedTerminalTheme> Import(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.");
|
|
||||||
|
|
||||||
var terminalTheme = new TerminalTheme
|
|
||||||
{
|
|
||||||
Name = Path.GetFileNameWithoutExtension(fileName),
|
|
||||||
Colors = GetColors(node),
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
PreInstalled = false
|
|
||||||
};
|
|
||||||
|
|
||||||
return Task.FromResult(new ExportedTerminalTheme(terminalTheme, string.Empty));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,10 +42,5 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
|
|
||||||
throw new KeyNotFoundException($"No handler registered for command: {command}");
|
throw new KeyNotFoundException($"No handler registered for command: {command}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearCommandHandlers()
|
|
||||||
{
|
|
||||||
_commandHandlers.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Implementation
|
|
||||||
{
|
|
||||||
// The purpose of this class is to ensure backward compatibility of mosh profiles and command history
|
|
||||||
// TODO: This class should be probably deleted in few months or so
|
|
||||||
public static class MoshBackwardCompatibility
|
|
||||||
{
|
|
||||||
private static readonly string MoshCommandExe = $"{Constants.MoshCommandName}.exe";
|
|
||||||
|
|
||||||
private static readonly Regex OldFormatRx = new Regex(@".+\s(?<ports>\d{1,5}:\d{1,5})$", RegexOptions.Compiled);
|
|
||||||
|
|
||||||
private static readonly Regex TargetRx =
|
|
||||||
new Regex(@"(^|\s+)(?<user>[^\s@;]+)@(?<host>[^\s@:]+)(:(?<port>\d{1,5}))?\s+", RegexOptions.Compiled);
|
|
||||||
|
|
||||||
private static bool IsMoshProfile(ShellProfile profile) =>
|
|
||||||
Constants.MoshCommandName.Equals(profile.Location, StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
MoshCommandExe.Equals(profile.Location, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the profile contains obsolete mosh syntax, and changes it if it does.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="profile">The profile. Note that the method doesn't change the input profile at all, but creates and
|
|
||||||
/// returns its clone if there's something to change.</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// The method actually checks if <see cref="ShellProfile.Arguments"/> of the input <paramref name="profile"/> is in
|
|
||||||
/// obsolete <c>mosh</c> format <c>[ssh_arguments] [target] [mosh_port_range]</c>, and if it is changes it to the new
|
|
||||||
/// format <c>--ssh="[ssh_arguments]" -p [mosh_ports] [target]</c>.
|
|
||||||
/// </remarks>
|
|
||||||
/// <returns>
|
|
||||||
/// If the method detected that the input profile is obsolete mosh profile, it will create its clone with the updated
|
|
||||||
/// command line arguments, and return it. Otherwise it will simply return unchanged input profile (the same reference
|
|
||||||
/// as the input profile). It allows us to simply check if anything is changed by comparing the input and output
|
|
||||||
/// profiles for reference equality.
|
|
||||||
/// </returns>
|
|
||||||
public static ShellProfile FixProfile(ShellProfile profile)
|
|
||||||
{
|
|
||||||
// If there aren't any arguments, or if the profile isn't a mosh profile, there's nothing to do. Just return.
|
|
||||||
if (string.IsNullOrWhiteSpace(profile.Arguments) || !IsMoshProfile(profile)) return profile;
|
|
||||||
|
|
||||||
var arguments = profile.Arguments.Trim();
|
|
||||||
|
|
||||||
var moshPortsMatch = OldFormatRx.Match(arguments);
|
|
||||||
|
|
||||||
if (!moshPortsMatch.Success) return profile;// Arguments don't end with mosh ports (i.e. 60000:61000), so it isn't an obsolete format.
|
|
||||||
|
|
||||||
var targetMatches = TargetRx.Matches(arguments);
|
|
||||||
|
|
||||||
if (targetMatches.Count != 1) return profile;// Target part (user@host:port) not found or found multiple times. Cannot reliably determine the target, so don't change.
|
|
||||||
|
|
||||||
var targetMatch = targetMatches[0];
|
|
||||||
|
|
||||||
// Removing [mosh_ports] from the original arguments
|
|
||||||
arguments = arguments.Substring(0, arguments.Length - moshPortsMatch.Groups["ports"].Length).Trim();
|
|
||||||
|
|
||||||
var newProfile = profile.Clone();
|
|
||||||
|
|
||||||
// Removing [target] from the original arguments, thus getting "pure" ssh arguments
|
|
||||||
var sshArguments = targetMatch.Index > 0 ? arguments.Substring(0, targetMatch.Index).Trim() : string.Empty;
|
|
||||||
|
|
||||||
if (targetMatch.Index + targetMatch.Length < arguments.Length)
|
|
||||||
{
|
|
||||||
sshArguments += $" {arguments.Substring(targetMatch.Index + targetMatch.Length).Trim()}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the ssh port is defined in the original target (i.e. user@host:port), which was allowed in the obsolete mosh, extracting the port part into a separate ssh argument
|
|
||||||
if (targetMatch.Groups["port"].Success && uint.TryParse(targetMatch.Groups["port"].Value, out var port) &&
|
|
||||||
port != SshProfile.DefaultSshPort)
|
|
||||||
{
|
|
||||||
sshArguments += $" -p {targetMatch.Groups["port"].Value}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replacing any double quotes in ssh arguments with single quotes, since ssh arguments will be double-quoted as a whole
|
|
||||||
sshArguments = sshArguments.Replace("\"", "'").Trim();
|
|
||||||
|
|
||||||
arguments = $"-p {moshPortsMatch.Groups["ports"].Value}";
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(sshArguments))
|
|
||||||
{
|
|
||||||
// If there were some ssh arguments, add them as defined in the new format
|
|
||||||
arguments += $" --ssh=\"ssh {sshArguments}\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally append the target to the arguments
|
|
||||||
arguments += $" {targetMatch.Groups["user"]}@{targetMatch.Groups["host"]}";
|
|
||||||
|
|
||||||
newProfile.Arguments = arguments;
|
|
||||||
|
|
||||||
return newProfile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.Models.Messages;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Messaging;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Implementation
|
namespace FluentTerminal.App.Services.Implementation
|
||||||
{
|
{
|
||||||
@@ -15,14 +12,12 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
public const string CurrentThemeKey = "CurrentTheme";
|
public const string CurrentThemeKey = "CurrentTheme";
|
||||||
public const string DefaultShellProfileKey = "DefaultShellProfile";
|
public const string DefaultShellProfileKey = "DefaultShellProfile";
|
||||||
public const string DefaultSshProfileKey = "DefaultSshProfile";
|
|
||||||
|
|
||||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
private readonly IApplicationDataContainer _keyBindings;
|
private readonly IApplicationDataContainer _keyBindings;
|
||||||
private readonly IApplicationDataContainer _localSettings;
|
private readonly IApplicationDataContainer _localSettings;
|
||||||
private readonly IApplicationDataContainer _roamingSettings;
|
private readonly IApplicationDataContainer _roamingSettings;
|
||||||
private readonly IApplicationDataContainer _shellProfiles;
|
private readonly IApplicationDataContainer _shellProfiles;
|
||||||
private readonly IApplicationDataContainer _sshProfiles;
|
|
||||||
private readonly IApplicationDataContainer _themes;
|
private readonly IApplicationDataContainer _themes;
|
||||||
|
|
||||||
public SettingsService(IDefaultValueProvider defaultValueProvider, ApplicationDataContainers containers)
|
public SettingsService(IDefaultValueProvider defaultValueProvider, ApplicationDataContainers containers)
|
||||||
@@ -34,8 +29,6 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
_themes = containers.Themes;
|
_themes = containers.Themes;
|
||||||
_keyBindings = containers.KeyBindings;
|
_keyBindings = containers.KeyBindings;
|
||||||
_shellProfiles = containers.ShellProfiles;
|
_shellProfiles = containers.ShellProfiles;
|
||||||
_sshProfiles = containers.SshProfiles;
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var theme in _defaultValueProvider.GetPreInstalledThemes())
|
foreach (var theme in _defaultValueProvider.GetPreInstalledThemes())
|
||||||
{
|
{
|
||||||
@@ -54,132 +47,25 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ExportSettings()
|
public event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||||
{
|
|
||||||
var config = new
|
|
||||||
{
|
|
||||||
App = GetApplicationSettings(),
|
|
||||||
KeyBindings = GetCommandKeyBindings(),
|
|
||||||
TerminalOptions = GetTerminalOptions(),
|
|
||||||
Themes = new List<TerminalTheme>(),
|
|
||||||
Profiles = new List<ShellProfile>(),
|
|
||||||
SshProfiles = new List<SshProfile>(),
|
|
||||||
DefaultSettings = GetDefaultSettings(),
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var theme in GetThemes().Where(x => !x.PreInstalled))
|
public event EventHandler<TerminalTheme> ThemeAdded;
|
||||||
{
|
public event EventHandler<Guid> CurrentThemeChanged;
|
||||||
config.Themes.Add(theme);
|
public event EventHandler<Guid> ThemeDeleted;
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var profile in GetShellProfiles())
|
public event EventHandler KeyBindingsChanged;
|
||||||
{
|
|
||||||
config.Profiles.Add(profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var profile in GetSshProfiles())
|
public event EventHandler<ShellProfile> ShellProfileAdded;
|
||||||
{
|
public event EventHandler<Guid> ShellProfileDeleted;
|
||||||
config.SshProfiles.Add(profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonConvert.SerializeObject(config, PreserveDictionaryKeyCaseContractResolver.SerializerSettings);
|
public event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, string> GetDefaultSettings()
|
|
||||||
{
|
|
||||||
return new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
[DefaultShellProfileKey] = GetDefaultShellProfileId().ToString(),
|
|
||||||
[CurrentThemeKey] = GetCurrentThemeId().ToString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ImportSettings(string serializedSettings)
|
|
||||||
{
|
|
||||||
var config = new
|
|
||||||
{
|
|
||||||
App = GetApplicationSettings(),
|
|
||||||
KeyBindings = new Dictionary<string, ICollection<KeyBinding>>(),
|
|
||||||
Themes = new List<TerminalTheme>(),
|
|
||||||
Profiles = new List<ShellProfile>(),
|
|
||||||
SshProfiles = new List<SshProfile>(),
|
|
||||||
TerminalOptions = GetTerminalOptions(),
|
|
||||||
DefaultSettings = new Dictionary<string, string>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
JsonConvert.PopulateObject(serializedSettings, config);
|
|
||||||
|
|
||||||
SaveApplicationSettings(config.App);
|
|
||||||
|
|
||||||
// Since we set each command sepaartely, we don't need all existing settings
|
|
||||||
foreach (var pair in config.KeyBindings)
|
|
||||||
{
|
|
||||||
SaveKeyBindings(pair.Key, pair.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't create/modify pre-installed themes
|
|
||||||
foreach (var theme in config.Themes.Where(x => !x.PreInstalled))
|
|
||||||
{
|
|
||||||
var existingTheme = GetTheme(theme.Id);
|
|
||||||
if (existingTheme?.PreInstalled == true)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SaveTheme(theme, existingTheme != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var profile in config.Profiles)
|
|
||||||
{
|
|
||||||
var existingProfile = GetShellProfile(profile.Id);
|
|
||||||
var isNew = existingProfile == default;
|
|
||||||
|
|
||||||
if (!isNew && existingProfile.PreInstalled)
|
|
||||||
{
|
|
||||||
// those cannot be edited
|
|
||||||
profile.Name = existingProfile.Name;
|
|
||||||
profile.Location = existingProfile.Location;
|
|
||||||
|
|
||||||
SaveShellProfile(profile, false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SaveShellProfile(profile, isNew);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var profile in config.SshProfiles)
|
|
||||||
{
|
|
||||||
var existingProfile = GetSshProfile(profile.Id);
|
|
||||||
var isNew = existingProfile == default;
|
|
||||||
|
|
||||||
SaveSshProfile(profile, isNew);
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveTerminalOptions(config.TerminalOptions);
|
|
||||||
|
|
||||||
if (config.DefaultSettings.ContainsKey(DefaultShellProfileKey))
|
|
||||||
{
|
|
||||||
SaveDefaultShellProfileId(new Guid(config.DefaultSettings[DefaultShellProfileKey]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.DefaultSettings.ContainsKey(CurrentThemeKey))
|
|
||||||
{
|
|
||||||
SaveCurrentThemeId(new Guid(config.DefaultSettings[CurrentThemeKey]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteShellProfile(Guid id)
|
public void DeleteShellProfile(Guid id)
|
||||||
{
|
{
|
||||||
_shellProfiles.Delete(id.ToString());
|
_shellProfiles.Delete(id.ToString());
|
||||||
WeakReferenceMessenger.Default.Send(new ShellProfileDeletedMessage(id));
|
ShellProfileDeleted?.Invoke(this, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteSshProfile(Guid id)
|
|
||||||
{
|
|
||||||
_sshProfiles.Delete(id.ToString());
|
|
||||||
WeakReferenceMessenger.Default.Send(new ShellProfileDeletedMessage(id));
|
|
||||||
WeakReferenceMessenger.Default.Send(new KeyBindingsChangedMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void DeleteTheme(Guid id)
|
public void DeleteTheme(Guid id)
|
||||||
{
|
{
|
||||||
_themes.Delete(id.ToString());
|
_themes.Delete(id.ToString());
|
||||||
@@ -193,7 +79,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Send(new ThemeDeletedMessage(id));
|
ThemeDeleted?.Invoke(this, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApplicationSettings GetApplicationSettings()
|
public ApplicationSettings GetApplicationSettings()
|
||||||
@@ -240,10 +126,6 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
return _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
|
return _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
|
||||||
}
|
}
|
||||||
public SshProfile GetSshProfile(Guid id)
|
|
||||||
{
|
|
||||||
return _sshProfiles.ReadValueFromJson(id.ToString(), default(SshProfile));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid GetDefaultShellProfileId()
|
public Guid GetDefaultShellProfileId()
|
||||||
{
|
{
|
||||||
@@ -267,43 +149,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
|
|
||||||
public IEnumerable<ShellProfile> GetShellProfiles()
|
public IEnumerable<ShellProfile> GetShellProfiles()
|
||||||
{
|
{
|
||||||
return _shellProfiles.GetAll().Select(x => JsonConvert.DeserializeObject<ShellProfile>((string) x))
|
return _shellProfiles.GetAll().Select(x => JsonConvert.DeserializeObject<ShellProfile>((string)x)).ToList();
|
||||||
.Select(MoshBackwardCompatibilityFixProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<SshProfile> GetSshProfiles()
|
|
||||||
{
|
|
||||||
return _sshProfiles.GetAll().Select(x => JsonConvert.DeserializeObject<SshProfile>((string) x))
|
|
||||||
.Select(MoshBackwardCompatibilityFixProfile).Cast<SshProfile>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<ShellProfile> GetAllProfiles()
|
|
||||||
{
|
|
||||||
return GetShellProfiles().Union(GetSshProfiles());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ShellProfile MoshBackwardCompatibilityFixProfile(ShellProfile profile)
|
|
||||||
{
|
|
||||||
var fixedProfile = MoshBackwardCompatibility.FixProfile(profile);
|
|
||||||
|
|
||||||
if (ReferenceEquals(fixedProfile, profile))
|
|
||||||
{
|
|
||||||
// Nothing changed
|
|
||||||
return fixedProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fixedProfile is SshProfile sshProfile)
|
|
||||||
{
|
|
||||||
DeleteSshProfile(profile.Id);
|
|
||||||
SaveSshProfile(sshProfile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DeleteShellProfile(profile.Id);
|
|
||||||
SaveShellProfile(fixedProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixedProfile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<TabTheme> GetTabThemes()
|
public IEnumerable<TabTheme> GetTabThemes()
|
||||||
@@ -333,43 +179,36 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
_keyBindings.WriteValueAsJson(command.ToString(), _defaultValueProvider.GetDefaultKeyBindings(command));
|
_keyBindings.WriteValueAsJson(command.ToString(), _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Send(new KeyBindingsChangedMessage());
|
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
|
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
|
||||||
{
|
{
|
||||||
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
|
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
|
||||||
WeakReferenceMessenger.Default.Send(new ApplicationSettingsChangedMessage(applicationSettings.Clone()));
|
ApplicationSettingsChanged?.Invoke(this, applicationSettings);
|
||||||
}
|
|
||||||
|
|
||||||
public void NotifyApplicationSettingsChanged(ApplicationSettings applicationSettings)
|
|
||||||
{
|
|
||||||
WeakReferenceMessenger.Default.Send(new ApplicationSettingsChangedMessage(applicationSettings.Clone()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveCurrentThemeId(Guid id)
|
public void SaveCurrentThemeId(Guid id)
|
||||||
{
|
{
|
||||||
_roamingSettings.SetValue(CurrentThemeKey, id);
|
_roamingSettings.SetValue(CurrentThemeKey, id);
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Send(new CurrentThemeChangedMessage(id));
|
CurrentThemeChanged?.Invoke(this, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveDefaultShellProfileId(Guid id)
|
public void SaveDefaultShellProfileId(Guid id)
|
||||||
{
|
{
|
||||||
_localSettings.SetValue(DefaultShellProfileKey, id);
|
_localSettings.SetValue(DefaultShellProfileKey, id);
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Send(new DefaultShellProfileChangedMessage(id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings)
|
public void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings)
|
||||||
{
|
{
|
||||||
if (!Enum.TryParse<Command>(command, true, out var enumValue))
|
if (!Enum.IsDefined(typeof(Command), command))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
_keyBindings.WriteValueAsJson(enumValue.ToString(), keyBindings);
|
_keyBindings.WriteValueAsJson(command.ToString(), keyBindings);
|
||||||
WeakReferenceMessenger.Default.Send(new KeyBindingsChangedMessage());
|
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveShellProfile(ShellProfile shellProfile, bool newShell = false)
|
public void SaveShellProfile(ShellProfile shellProfile, bool newShell = false)
|
||||||
@@ -377,39 +216,18 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||||
|
|
||||||
// When saving the shell profile, we also need to update keybindings for everywhere.
|
// When saving the shell profile, we also need to update keybindings for everywhere.
|
||||||
WeakReferenceMessenger.Default.Send(new KeyBindingsChangedMessage());
|
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||||
|
|
||||||
if (newShell)
|
if (newShell)
|
||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.Send(new ShellProfileAddedMessage(shellProfile));
|
ShellProfileAdded?.Invoke(this, shellProfile);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WeakReferenceMessenger.Default.Send(new ShellProfileChangedMessage(shellProfile));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveSshProfile(SshProfile sshProfile, bool newShell = false)
|
|
||||||
{
|
|
||||||
_sshProfiles.WriteValueAsJson(sshProfile.Id.ToString(), sshProfile);
|
|
||||||
|
|
||||||
// When saving the shell profile, we also need to update keybindings for everywhere.
|
|
||||||
WeakReferenceMessenger.Default.Send(new KeyBindingsChangedMessage());
|
|
||||||
|
|
||||||
if (newShell)
|
|
||||||
{
|
|
||||||
WeakReferenceMessenger.Default.Send(new ShellProfileAddedMessage(sshProfile));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WeakReferenceMessenger.Default.Send(new ShellProfileChangedMessage(sshProfile));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveTerminalOptions(TerminalOptions terminalOptions)
|
public void SaveTerminalOptions(TerminalOptions terminalOptions)
|
||||||
{
|
{
|
||||||
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
||||||
WeakReferenceMessenger.Default.Send(new TerminalOptionsChangedMessage(terminalOptions));
|
TerminalOptionsChanged?.Invoke(this, terminalOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveTheme(TerminalTheme theme, bool newTheme = false)
|
public void SaveTheme(TerminalTheme theme, bool newTheme = false)
|
||||||
@@ -418,12 +236,12 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
|
|
||||||
if (theme.Id == GetCurrentThemeId())
|
if (theme.Id == GetCurrentThemeId())
|
||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.Send(new CurrentThemeChangedMessage(theme.Id));
|
CurrentThemeChanged?.Invoke(this, theme.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newTheme)
|
if (newTheme)
|
||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.Send(new ThemeAddedMessage(theme));
|
ThemeAdded?.Invoke(this, theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
using FluentTerminal.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Implementation
|
|
||||||
{
|
|
||||||
public class ShellProfileMigrationService : IShellProfileMigrationService
|
|
||||||
{
|
|
||||||
private readonly Dictionary<int, Action<ShellProfile>> _migrationSteps = new Dictionary<int, Action<ShellProfile>>
|
|
||||||
{
|
|
||||||
[1] = new Action<ShellProfile>(profile =>
|
|
||||||
{
|
|
||||||
profile.EnvironmentVariables["TERM"] = "xterm-256color";
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public void Migrate(ShellProfile profile)
|
|
||||||
{
|
|
||||||
while(profile.MigrationVersion < ShellProfile.CurrentMigrationVersion)
|
|
||||||
{
|
|
||||||
ApplyMigrationStep(profile, profile.MigrationVersion + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyMigrationStep(ShellProfile profile, int targetVersion)
|
|
||||||
{
|
|
||||||
_migrationSteps[targetVersion](profile);
|
|
||||||
profile.MigrationVersion = targetVersion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
{
|
{
|
||||||
public class ThemeParserFactory : IThemeParserFactory
|
public class ThemeParserFactory : IThemeParserFactory
|
||||||
{
|
{
|
||||||
private readonly IEnumerable<IThemeParser> _parsers;
|
private IEnumerable<IThemeParser> _parsers;
|
||||||
|
|
||||||
public IEnumerable<string> SupportedFileTypes { get; }
|
public IEnumerable<string> SupportedFileTypes { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using FluentTerminal.App.Services.Exceptions;
|
using FluentTerminal.Models;
|
||||||
using FluentTerminal.Models;
|
|
||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using FluentTerminal.Models.Requests;
|
using FluentTerminal.Models.Requests;
|
||||||
using FluentTerminal.Models.Responses;
|
using FluentTerminal.Models.Responses;
|
||||||
@@ -7,150 +6,42 @@ using Newtonsoft.Json;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Windows.Foundation.Collections;
|
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Implementation
|
namespace FluentTerminal.App.Services.Implementation
|
||||||
{
|
{
|
||||||
public class TrayProcessCommunicationService : ITrayProcessCommunicationService
|
public class TrayProcessCommunicationService : ITrayProcessCommunicationService
|
||||||
{
|
{
|
||||||
#region Static
|
|
||||||
|
|
||||||
private static string _userName;
|
|
||||||
private static string _sshConfigDir;
|
|
||||||
private static readonly Dictionary<string, string> CommandPaths = new Dictionary<string, string>();
|
|
||||||
private static readonly Dictionary<string, string> CommandErrors = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
private static ValueSet CreateMessage(IMessage content)
|
|
||||||
{
|
|
||||||
return new ValueSet
|
|
||||||
{
|
|
||||||
[MessageKeys.Type] = content.Identifier,
|
|
||||||
[MessageKeys.Content] = JsonConvert.SerializeObject(content,
|
|
||||||
PreserveDictionaryKeyCaseContractResolver.SerializerSettings)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Static
|
|
||||||
|
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private IAppServiceConnection _appServiceConnection;
|
private IAppServiceConnection _appServiceConnection;
|
||||||
private readonly Dictionary<byte, Action<byte[]>> _terminalOutputHandlers;
|
private readonly Dictionary<int, Action<byte[]>> _terminalOutputHandlers;
|
||||||
// ReSharper disable once RedundantDefaultMemberInitializer
|
private int _nextTerminalId = 0;
|
||||||
private byte _nextTerminalId = 0;
|
|
||||||
|
|
||||||
public event EventHandler<TerminalExitStatus> TerminalExited;
|
public event EventHandler<int> TerminalExited;
|
||||||
|
|
||||||
public TrayProcessCommunicationService(ISettingsService settingsService)
|
public TrayProcessCommunicationService(ISettingsService settingsService)
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_terminalOutputHandlers = new Dictionary<byte, Action<byte[]>>();
|
_terminalOutputHandlers = new Dictionary<int, Action<byte[]>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte GetNextTerminalId()
|
public int GetNextTerminalId()
|
||||||
{
|
{
|
||||||
return _nextTerminalId++;
|
return _nextTerminalId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetUserNameAsync()
|
public async Task<GetAvailablePortResponse> GetAvailablePort()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(_userName))
|
var request = new GetAvailablePortRequest();
|
||||||
{
|
|
||||||
// Returning the username from cache
|
|
||||||
return _userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await GetResponseAsync<StringValueResponse>(new GetUserNameRequest()).ConfigureAwait(false);
|
var responseMessage = await _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||||
|
var response = JsonConvert.DeserializeObject<GetAvailablePortResponse>(responseMessage[MessageKeys.Content]);
|
||||||
|
|
||||||
if (response.Success)
|
Logger.Instance.Debug("Received GetAvailablePortResponse: {@response}", response);
|
||||||
{
|
|
||||||
_userName = response.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _userName;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveTextFileAsync(string path, string content)
|
public async Task<CreateTerminalResponse> CreateTerminal(int id, TerminalSize size, ShellProfile shellProfile, SessionType sessionType)
|
||||||
{
|
|
||||||
var response =
|
|
||||||
await GetResponseAsync<CommonResponse>(new SaveTextFileRequest {Path = path, Content = content})
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!response.Success)
|
|
||||||
{
|
|
||||||
throw new SaveTextFileException(string.IsNullOrEmpty(response.Error)
|
|
||||||
? "Failed to save the file."
|
|
||||||
: response.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> ReadTextFileAsync(string path)
|
|
||||||
{
|
|
||||||
var response = await GetResponseAsync<StringValueResponse>(new ReadTextFileRequest { Path = path }).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!response.Success)
|
|
||||||
{
|
|
||||||
throw new ReadTextFileException(string.IsNullOrWhiteSpace(response.Error) ? "Failed to read the file." : response.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetSshConfigDirAsync()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(_sshConfigDir))
|
|
||||||
{
|
|
||||||
return _sshConfigDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
var response =
|
|
||||||
await GetResponseAsync<GetSshConfigFolderResponse>(new GetSshConfigFolderRequest
|
|
||||||
{ IncludeContent = false }).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (response.Success)
|
|
||||||
{
|
|
||||||
_sshConfigDir = response.Path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _sshConfigDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string[]> GetFilesFromSshConfigDirAsync()
|
|
||||||
{
|
|
||||||
var response =
|
|
||||||
await GetResponseAsync<GetSshConfigFolderResponse>(new GetSshConfigFolderRequest
|
|
||||||
{IncludeContent = true}).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (response.Success)
|
|
||||||
{
|
|
||||||
_sshConfigDir = response.Path;
|
|
||||||
|
|
||||||
return response.Files;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> CheckFileExistsAsync(string path)
|
|
||||||
{
|
|
||||||
var response = await GetResponseAsync<CommonResponse>(new CheckFileExistsRequest {Path = path})
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
return response.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task MuteTerminalAsync(bool mute)
|
|
||||||
{
|
|
||||||
return _appServiceConnection.SendMessageAsync(CreateMessage(new MuteTerminalRequest {Mute = mute}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateSettings(ApplicationSettings settings)
|
|
||||||
{
|
|
||||||
_appServiceConnection.SendMessageAsync(CreateMessage(new UpdateSettingsRequest { Settings = settings }));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CreateTerminalResponse> CreateTerminalAsync(byte id, TerminalSize size, ShellProfile shellProfile,
|
|
||||||
SessionType sessionType)
|
|
||||||
{
|
{
|
||||||
var request = new CreateTerminalRequest
|
var request = new CreateTerminalRequest
|
||||||
{
|
{
|
||||||
@@ -162,151 +53,106 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
|
|
||||||
Logger.Instance.Debug("Sending CreateTerminalRequest: {@request}", request);
|
Logger.Instance.Debug("Sending CreateTerminalRequest: {@request}", request);
|
||||||
|
|
||||||
var response = await GetResponseAsync<CreateTerminalResponse>(request).ConfigureAwait(false);
|
var responseMessage = await _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||||
|
var response = JsonConvert.DeserializeObject<CreateTerminalResponse>(responseMessage[MessageKeys.Content]);
|
||||||
|
|
||||||
Logger.Instance.Debug("Received CreateTerminalResponse: {@response}", response);
|
Logger.Instance.Debug("Received CreateTerminalResponse: {@response}", response);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<PauseTerminalOutputResponse> PauseTerminalOutputAsync(byte id, bool pause)
|
|
||||||
{
|
|
||||||
return GetResponseAsync<PauseTerminalOutputResponse>(
|
|
||||||
new PauseTerminalOutputRequest {Id = id, Pause = pause});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize(IAppServiceConnection appServiceConnection)
|
public void Initialize(IAppServiceConnection appServiceConnection)
|
||||||
{
|
{
|
||||||
_appServiceConnection = appServiceConnection;
|
_appServiceConnection = appServiceConnection;
|
||||||
_appServiceConnection.MessageReceived += OnMessageReceived;
|
_appServiceConnection.MessageReceived += OnMessageReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMessageReceived(object sender, IDictionary<string, object> e)
|
private void OnMessageReceived(object sender, IDictionary<string, string> e)
|
||||||
{
|
{
|
||||||
var messageType = (byte)e[MessageKeys.Type];
|
var messageType = e[MessageKeys.Type];
|
||||||
var messageContent = e[MessageKeys.Content];
|
var messageContent = e[MessageKeys.Content];
|
||||||
|
|
||||||
switch (messageType)
|
if (messageType == nameof(DisplayTerminalOutputRequest))
|
||||||
{
|
{
|
||||||
case Constants.TerminalBufferRequestIdentifier:
|
var request = JsonConvert.DeserializeObject<DisplayTerminalOutputRequest>(messageContent);
|
||||||
var terminalId = (byte)e[MessageKeys.TerminalId];
|
|
||||||
|
|
||||||
if (_terminalOutputHandlers.ContainsKey(terminalId))
|
if (_terminalOutputHandlers.ContainsKey(request.TerminalId))
|
||||||
{
|
{
|
||||||
_terminalOutputHandlers[terminalId].Invoke((byte[])messageContent);
|
_terminalOutputHandlers[request.TerminalId].Invoke(request.Output);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Instance.Error("Received output for unknown terminal Id {id}", terminalId);
|
Logger.Instance.Error("Received output for unknown terminal Id {id}", request.TerminalId);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case (byte) MessageIdentifiers.TerminalExitedRequest:
|
else if (messageType == nameof(TerminalExitedRequest))
|
||||||
var request = JsonConvert.DeserializeObject<TerminalExitedRequest>((string)messageContent);
|
{
|
||||||
Logger.Instance.Debug("Received TerminalExitedRequest: {@request}", request);
|
var request = JsonConvert.DeserializeObject<TerminalExitedRequest>(messageContent);
|
||||||
|
|
||||||
TerminalExited?.Invoke(this, request.ToStatus());
|
Logger.Instance.Debug("Received TerminalExitedRequest: {@request}", request);
|
||||||
break;
|
|
||||||
default:
|
TerminalExited?.Invoke(this, request.TerminalId);
|
||||||
Logger.Instance.Error("Received unknown message type: {messageType}", messageType);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ResizeTerminalAsync(byte id, TerminalSize size)
|
public Task ResizeTerminal(int id, TerminalSize size)
|
||||||
{
|
{
|
||||||
return _appServiceConnection.SendMessageAsync(CreateMessage(new ResizeTerminalRequest
|
var request = new ResizeTerminalRequest
|
||||||
{TerminalId = id, NewSize = size}));
|
{
|
||||||
|
TerminalId = id,
|
||||||
|
NewSize = size
|
||||||
|
};
|
||||||
|
|
||||||
|
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SubscribeForTerminalOutput(byte terminalId, Action<byte[]> callback)
|
public void SubscribeForTerminalOutput(int terminalId, Action<byte[]> callback)
|
||||||
{
|
{
|
||||||
_terminalOutputHandlers[terminalId] = callback;
|
_terminalOutputHandlers[terminalId] = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnsubscribeFromTerminalOutput(byte id)
|
public Task UpdateToggleWindowKeyBindings()
|
||||||
{
|
|
||||||
if (_terminalOutputHandlers.ContainsKey(id))
|
|
||||||
{
|
|
||||||
_terminalOutputHandlers.Remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task UpdateToggleWindowKeyBindingsAsync()
|
|
||||||
{
|
{
|
||||||
var keyBindings = _settingsService.GetCommandKeyBindings()[nameof(Command.ToggleWindow)];
|
var keyBindings = _settingsService.GetCommandKeyBindings()[nameof(Command.ToggleWindow)];
|
||||||
|
|
||||||
return _appServiceConnection.SendMessageAsync(CreateMessage(new SetToggleWindowKeyBindingsRequest
|
var request = new SetToggleWindowKeyBindingsRequest
|
||||||
{KeyBindings = keyBindings}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task WriteAsync(byte id, byte[] data)
|
|
||||||
{
|
|
||||||
var message = new ValueSet
|
|
||||||
{
|
{
|
||||||
[MessageKeys.Type] = Constants.TerminalBufferRequestIdentifier,
|
KeyBindings = keyBindings
|
||||||
[MessageKeys.TerminalId] = id,
|
|
||||||
[MessageKeys.Content] = data
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return _appServiceConnection.SendMessageAsync(message);
|
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task CloseTerminalAsync(byte terminalId)
|
public Task Write(int id, byte[] data)
|
||||||
{
|
{
|
||||||
var request = new TerminalExitedRequest(terminalId, -1);
|
var request = new WriteDataRequest
|
||||||
|
{
|
||||||
|
TerminalId = id,
|
||||||
|
Data = data
|
||||||
|
};
|
||||||
|
|
||||||
|
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CloseTerminal(int terminalId)
|
||||||
|
{
|
||||||
|
var request = new TerminalExitedRequest
|
||||||
|
{
|
||||||
|
TerminalId = terminalId
|
||||||
|
};
|
||||||
|
|
||||||
Logger.Instance.Debug("Sending TerminalExitedRequest: {@request}", request);
|
Logger.Instance.Debug("Sending TerminalExitedRequest: {@request}", request);
|
||||||
|
|
||||||
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetCommandPathAsync(string command)
|
private IDictionary<string, string> CreateMessage(object content)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(command))
|
return new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Input value is null or empty.", nameof(command));
|
[MessageKeys.Type] = content.GetType().Name,
|
||||||
}
|
[MessageKeys.Content] = JsonConvert.SerializeObject(content)
|
||||||
|
};
|
||||||
command = command.Trim();
|
|
||||||
|
|
||||||
var commandLower = command.ToLowerInvariant();
|
|
||||||
|
|
||||||
if (CommandPaths.TryGetValue(commandLower, out var path))
|
|
||||||
{
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CommandErrors.TryGetValue(commandLower, out var error))
|
|
||||||
{
|
|
||||||
throw new Exception(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await GetResponseAsync<StringValueResponse>(new GetCommandPathRequest {Command = command})
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (response.Success)
|
|
||||||
{
|
|
||||||
CommandPaths[commandLower] = response.Value;
|
|
||||||
|
|
||||||
return response.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandErrors[commandLower] = response.Error;
|
|
||||||
|
|
||||||
throw new Exception(response.Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task QuitApplicationAsync()
|
|
||||||
{
|
|
||||||
return _appServiceConnection.SendMessageAsync(CreateMessage(new QuitApplicationRequest()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<T> GetResponseAsync<T>(IMessage request)
|
|
||||||
{
|
|
||||||
var messageResponse =
|
|
||||||
await _appServiceConnection.SendMessageAsync(CreateMessage(request)).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<T>((string)messageResponse[MessageKeys.Content]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,9 +17,9 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
_notificationService = notificationService;
|
_notificationService = notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CheckForUpdateAsync(bool notifyNoUpdate = false)
|
public async Task CheckForUpdate(bool notifyNoUpdate = false)
|
||||||
{
|
{
|
||||||
var latest = await GetLatestVersionAsync().ConfigureAwait(false);
|
var latest = await GetLatestVersionAsync();
|
||||||
if (latest > GetCurrentVersion())
|
if (latest > GetCurrentVersion())
|
||||||
{
|
{
|
||||||
_notificationService.ShowNotification("Update available",
|
_notificationService.ShowNotification("Update available",
|
||||||
@@ -42,7 +42,7 @@ namespace FluentTerminal.App.Services.Implementation
|
|||||||
var restClient = new RestClient(apiEndpoint);
|
var restClient = new RestClient(apiEndpoint);
|
||||||
var restRequest = new RestRequest("/repos/felixse/fluentterminal/releases", Method.GET);
|
var restRequest = new RestRequest("/repos/felixse/fluentterminal/releases", Method.GET);
|
||||||
|
|
||||||
var restResponse = await restClient.ExecuteAsync(restRequest).ConfigureAwait(false);
|
var restResponse = await restClient.ExecuteTaskAsync(restRequest);
|
||||||
if (restResponse.IsSuccessful)
|
if (restResponse.IsSuccessful)
|
||||||
{
|
{
|
||||||
dynamic restResponseData = JsonConvert.DeserializeObject(restResponse.Content);
|
dynamic restResponseData = JsonConvert.DeserializeObject(restResponse.Content);
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ namespace FluentTerminal.App.Services
|
|||||||
.MinimumLevel.Is((LogEventLevel)configuration.LogLevel)
|
.MinimumLevel.Is((LogEventLevel)configuration.LogLevel)
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
|
|
||||||
// Log file is not created during unhandled exception processing.
|
Log.Information("Initialized");
|
||||||
// Force log file creation with the highest severity initialization record.
|
|
||||||
Log.Fatal("Initialized");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Debug(string text, params object[] propertyValues)
|
public void Debug(string text, params object[] propertyValues)
|
||||||
|
|||||||
@@ -1,188 +1,144 @@
|
|||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using FluentTerminal.Models.Responses;
|
using FluentTerminal.Models.Responses;
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
namespace FluentTerminal.App.Services
|
||||||
namespace FluentTerminal.App.Services
|
{
|
||||||
{
|
public class Terminal
|
||||||
public class Terminal
|
{
|
||||||
{
|
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
private Func<Task<string>> _selectedTextCallback;
|
||||||
private Func<Task<string>> _selectedTextCallback;
|
|
||||||
private bool _closingFromUi;
|
public Terminal(ITrayProcessCommunicationService trayProcessCommunicationService)
|
||||||
private bool _exited;
|
{
|
||||||
private readonly bool _requireShellProcessStart;
|
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||||
private string _fallbackTitle;
|
_trayProcessCommunicationService.TerminalExited += OnTerminalExited;
|
||||||
|
Id = _trayProcessCommunicationService.GetNextTerminalId();
|
||||||
public Terminal(ITrayProcessCommunicationService trayProcessCommunicationService, byte? terminalId = null)
|
}
|
||||||
{
|
|
||||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
private void OnTerminalExited(object sender, int e)
|
||||||
_trayProcessCommunicationService.TerminalExited += OnTerminalExited;
|
{
|
||||||
Id = terminalId ?? trayProcessCommunicationService.GetNextTerminalId();
|
if (e == Id)
|
||||||
_requireShellProcessStart = !terminalId.HasValue;
|
{
|
||||||
}
|
Closed?.Invoke(this, System.EventArgs.Empty);
|
||||||
|
}
|
||||||
public void Reconnect()
|
}
|
||||||
{
|
|
||||||
_exited = false;
|
/// <summary>
|
||||||
_trayProcessCommunicationService.TerminalExited += OnTerminalExited;
|
/// To be observed by both view and viewmodel
|
||||||
}
|
/// </summary>
|
||||||
|
public event EventHandler Closed;
|
||||||
private void OnTerminalExited(object sender, TerminalExitStatus status)
|
|
||||||
{
|
/// <summary>
|
||||||
if (status.TerminalId != Id)
|
/// to be observed by viewmodel
|
||||||
{
|
/// </summary>
|
||||||
return;
|
public event EventHandler<string> KeyboardCommandReceived;
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
_exited = true;
|
/// to be observed by view
|
||||||
Exited?.Invoke(this, status.ExitCode);
|
/// </summary>
|
||||||
|
public event EventHandler<byte[]> OutputReceived;
|
||||||
if (_closingFromUi || status.ExitCode <= 0)
|
|
||||||
{
|
/// <summary>
|
||||||
Closed?.Invoke(this, System.EventArgs.Empty);
|
/// to be observed by viewmodel
|
||||||
}
|
/// </summary>
|
||||||
|
public event EventHandler<TerminalSize> SizeChanged;
|
||||||
_trayProcessCommunicationService.TerminalExited -= OnTerminalExited;
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// to be observed by viewmodel
|
||||||
public event EventHandler<int> Exited;
|
/// </summary>
|
||||||
|
public event EventHandler<string> TitleChanged;
|
||||||
/// <summary>
|
|
||||||
/// To be observed by both view and viewmodel
|
public string FallbackTitle { get; private set; }
|
||||||
/// </summary>
|
|
||||||
public event EventHandler Closed;
|
public int Id { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// to be observed by viewmodel
|
/// To be called by either view or viewmodel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<string> KeyboardCommandReceived;
|
public async Task Close()
|
||||||
|
{
|
||||||
/// <summary>
|
await _trayProcessCommunicationService.CloseTerminal(Id).ConfigureAwait(true);
|
||||||
/// to be observed by view
|
}
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<byte[]> OutputReceived;
|
/// <summary>
|
||||||
|
/// to be called by viewmodel
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// to be observed by viewmodel
|
/// <returns></returns>
|
||||||
/// </summary>
|
public Task<string> GetSelectedText()
|
||||||
public event EventHandler<TerminalSize> SizeChanged;
|
{
|
||||||
|
return _selectedTextCallback();
|
||||||
/// <summary>
|
}
|
||||||
/// to be observed by viewmodel
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public event EventHandler<string> TitleChanged;
|
/// to be called by view
|
||||||
|
/// </summary>
|
||||||
public byte Id { get; }
|
public void ProcessKeyboardCommand(string command)
|
||||||
|
{
|
||||||
public ShellProfile Profile { get; private set; }
|
KeyboardCommandReceived?.Invoke(this, command);
|
||||||
|
}
|
||||||
/// <summary>
|
|
||||||
/// To be called by either view or viewmodel
|
/// <summary>
|
||||||
/// </summary>
|
/// To be set by view
|
||||||
public Task CloseAsync()
|
/// </summary>
|
||||||
{
|
public void RegisterSelectedTextCallback(Func<Task<string>> selectedTextCallback)
|
||||||
if (_exited)
|
{
|
||||||
{
|
_selectedTextCallback = selectedTextCallback;
|
||||||
Closed?.Invoke(this, System.EventArgs.Empty);
|
}
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
/// <summary>
|
||||||
_closingFromUi = true;
|
/// To be called by view
|
||||||
_trayProcessCommunicationService.UnsubscribeFromTerminalOutput(Id);
|
/// </summary>
|
||||||
return _trayProcessCommunicationService.CloseTerminalAsync(Id);
|
public async Task SetSize(TerminalSize size)
|
||||||
}
|
{
|
||||||
|
await _trayProcessCommunicationService.ResizeTerminal(Id, size);
|
||||||
/// <summary>
|
SizeChanged?.Invoke(this, size);
|
||||||
/// to be called by viewmodel
|
}
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
/// <summary>
|
||||||
public Task<string> GetSelectedText()
|
/// to be called by view
|
||||||
{
|
/// </summary>
|
||||||
return _selectedTextCallback?.Invoke();
|
public void SetTitle(string title)
|
||||||
}
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(title))
|
||||||
/// <summary>
|
{
|
||||||
/// to be called by view
|
title = FallbackTitle;
|
||||||
/// </summary>
|
}
|
||||||
public void ProcessKeyboardCommand(string command)
|
TitleChanged?.Invoke(this, title);
|
||||||
{
|
}
|
||||||
KeyboardCommandReceived?.Invoke(this, command);
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// to be called by view when ready
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// To be set by view
|
/// <param name="shellProfile"></param>
|
||||||
/// </summary>
|
/// <param name="size"></param>
|
||||||
public void RegisterSelectedTextCallback(Func<Task<string>> selectedTextCallback)
|
/// <param name="sessionType"></param>
|
||||||
{
|
public async Task<CreateTerminalResponse> StartShellProcess(ShellProfile shellProfile, TerminalSize size, SessionType sessionType)
|
||||||
_selectedTextCallback = selectedTextCallback;
|
{
|
||||||
}
|
_trayProcessCommunicationService.SubscribeForTerminalOutput(Id, t => OutputReceived?.Invoke(this, t));
|
||||||
|
var response = await _trayProcessCommunicationService.CreateTerminal(Id, size, shellProfile, sessionType).ConfigureAwait(true);
|
||||||
/// <summary>
|
|
||||||
/// To be called by view
|
if (response.Success)
|
||||||
/// </summary>
|
{
|
||||||
public async Task SetSizeAsync(TerminalSize size)
|
FallbackTitle = response.ShellExecutableName;
|
||||||
{
|
SetTitle(FallbackTitle);
|
||||||
await _trayProcessCommunicationService.ResizeTerminalAsync(Id, size).ConfigureAwait(false);
|
}
|
||||||
SizeChanged?.Invoke(this, size);
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Task Write(byte[] data)
|
||||||
/// to be called by view
|
{
|
||||||
/// </summary>
|
return _trayProcessCommunicationService.Write(Id, data);
|
||||||
public void SetTitle(string title)
|
}
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(title))
|
/// <summary>
|
||||||
{
|
/// Tells the ViewModel that this view failed to launch and can be closed.
|
||||||
title = _fallbackTitle;
|
/// </summary>
|
||||||
}
|
public void ReportLauchFailed()
|
||||||
TitleChanged?.Invoke(this, title);
|
{
|
||||||
}
|
Closed?.Invoke(this, System.EventArgs.Empty);
|
||||||
|
}
|
||||||
/// <summary>
|
}
|
||||||
/// To be called by view when ready
|
|
||||||
/// </summary>
|
|
||||||
public async Task<TerminalResponse> StartShellProcessAsync(ShellProfile shellProfile, TerminalSize size, SessionType sessionType, string termState)
|
|
||||||
{
|
|
||||||
Profile = shellProfile;
|
|
||||||
|
|
||||||
if (!_requireShellProcessStart && !string.IsNullOrEmpty(termState))
|
|
||||||
{
|
|
||||||
OutputReceived?.Invoke(this, Encoding.UTF8.GetBytes(termState));
|
|
||||||
}
|
|
||||||
|
|
||||||
_trayProcessCommunicationService.SubscribeForTerminalOutput(Id, t => OutputReceived?.Invoke(this, t));
|
|
||||||
|
|
||||||
if (_requireShellProcessStart)
|
|
||||||
{
|
|
||||||
var response = await _trayProcessCommunicationService
|
|
||||||
.CreateTerminalAsync(Id, size, shellProfile, sessionType).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (response.Success)
|
|
||||||
{
|
|
||||||
_fallbackTitle = response.Name;
|
|
||||||
SetTitle(_fallbackTitle);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return await _trayProcessCommunicationService.PauseTerminalOutputAsync(Id, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Write(byte[] data)
|
|
||||||
{
|
|
||||||
return _trayProcessCommunicationService.WriteAsync(Id, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tells the ViewModel that this view failed to launch and can be closed.
|
|
||||||
/// </summary>
|
|
||||||
public void ReportLauchFailed()
|
|
||||||
{
|
|
||||||
Closed?.Invoke(this, System.EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using Windows.ApplicationModel.Resources;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Utilities
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Get strings in different languages.
|
|
||||||
/// Author: Zuozishi
|
|
||||||
/// </summary>
|
|
||||||
public static class I18N
|
|
||||||
{
|
|
||||||
private static readonly ResourceLoader ResourceLoader = ResourceLoader.GetForViewIndependentUse();
|
|
||||||
|
|
||||||
public static string Translate(string resource)
|
|
||||||
{
|
|
||||||
return ResourceLoader.GetString(resource.Replace('.', '/'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string TranslateWithFallback(string resource, string fallback)
|
|
||||||
{
|
|
||||||
var result = Translate(resource);
|
|
||||||
|
|
||||||
return string.IsNullOrEmpty(result) ? fallback : result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.Services.Utilities
|
|
||||||
{
|
|
||||||
public class PreserveDictionaryKeyCaseContractResolver : DefaultContractResolver
|
|
||||||
{
|
|
||||||
public static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
|
|
||||||
{ ContractResolver = new PreserveDictionaryKeyCaseContractResolver() };
|
|
||||||
|
|
||||||
protected override JsonDictionaryContract CreateDictionaryContract(Type objectType)
|
|
||||||
{
|
|
||||||
var contract = base.CreateDictionaryContract(objectType);
|
|
||||||
|
|
||||||
contract.DictionaryKeyResolver = key => key;
|
|
||||||
|
|
||||||
return contract;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace FluentTerminal.App.Services.Utilities
|
namespace FluentTerminal.App.Services.Utilities
|
||||||
{
|
{
|
||||||
public class TerminalThemeContractResolver : PreserveDictionaryKeyCaseContractResolver
|
public class TerminalThemeContractResolver : DefaultContractResolver
|
||||||
{
|
{
|
||||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||||
{
|
{
|
||||||
@@ -24,4 +24,4 @@ namespace FluentTerminal.App.Services.Utilities
|
|||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
|
||||||
{
|
|
||||||
internal class DelayedHistorySaver : ISessionSuccessTracker
|
|
||||||
{
|
|
||||||
private const int DelayMilliseconds = 5000;
|
|
||||||
|
|
||||||
private readonly Action _saveAction;
|
|
||||||
|
|
||||||
private readonly object _lock = new object();
|
|
||||||
|
|
||||||
private CancellationTokenSource _cts;
|
|
||||||
|
|
||||||
private bool _sessionStarted;
|
|
||||||
private bool _outputReceived;
|
|
||||||
|
|
||||||
private bool _toSave = true;
|
|
||||||
|
|
||||||
internal DelayedHistorySaver(Action saveAction)
|
|
||||||
{
|
|
||||||
_saveAction = saveAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSuccessfulSessionStart()
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (!_toSave || _sessionStarted)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_sessionStarted = true;
|
|
||||||
|
|
||||||
if (_outputReceived)
|
|
||||||
{
|
|
||||||
InitiateSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetOutputReceived()
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (!_toSave || _outputReceived)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_outputReceived = true;
|
|
||||||
|
|
||||||
if (_sessionStarted)
|
|
||||||
{
|
|
||||||
InitiateSave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetInvalid()
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (!_toSave || !(_cts is CancellationTokenSource cts))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_toSave = false;
|
|
||||||
|
|
||||||
cts.Cancel();
|
|
||||||
cts.Dispose();
|
|
||||||
_cts = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitiateSave()
|
|
||||||
{
|
|
||||||
_cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
Task.Delay(DelayMilliseconds, _cts.Token).ContinueWith(t =>
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_toSave)
|
|
||||||
{
|
|
||||||
_saveAction.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
_toSave = false;
|
|
||||||
|
|
||||||
_cts?.Dispose();
|
|
||||||
_cts = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
|
||||||
{
|
|
||||||
public class EnvironmentVariableViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
private string _name;
|
|
||||||
private string _value;
|
|
||||||
|
|
||||||
public Guid Id { get; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get => _name;
|
|
||||||
set => SetProperty(ref _name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Value
|
|
||||||
{
|
|
||||||
get => _value;
|
|
||||||
set => SetProperty(ref _value, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Toolkit.Mvvm" Version="7.0.2" />
|
<PackageReference Include="Fleck" Version="1.0.3" />
|
||||||
|
<PackageReference Include="MvvmLightLibs" Version="5.4.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -13,11 +14,4 @@
|
|||||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
|
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<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>
|
</Project>
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace FluentTerminal.App.ViewModels
|
|
||||||
{
|
|
||||||
public interface ISessionSuccessTracker
|
|
||||||
{
|
|
||||||
void SetSuccessfulSessionStart();
|
|
||||||
|
|
||||||
void SetOutputReceived();
|
|
||||||
|
|
||||||
void SetInvalid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
|
||||||
{
|
|
||||||
public interface ITerminalView : IDisposable
|
|
||||||
{
|
|
||||||
Task ChangeFontSize(int fontSize);
|
|
||||||
Task ChangeThemeAsync(TerminalTheme theme);
|
|
||||||
Task ChangeKeyBindingsAsync();
|
|
||||||
Task ChangeOptionsAsync(TerminalOptions options);
|
|
||||||
Task InitializeAsync(TerminalViewModel viewModel);
|
|
||||||
Task ReconnectAsync();
|
|
||||||
void DisposalPrepare();
|
|
||||||
Task FindNextAsync(SearchRequest request);
|
|
||||||
Task FindPreviousAsync(SearchRequest request);
|
|
||||||
Task FocusTerminalAsync();
|
|
||||||
Task<string> SerializeXtermStateAsync();
|
|
||||||
void Paste(string text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -1,41 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class ExpandableMenuItemViewModel : MenuItemViewModelBase
|
|
||||||
{
|
|
||||||
public ObservableCollection<MenuItemViewModelBase> SubItems { get; }
|
|
||||||
|
|
||||||
public ExpandableMenuItemViewModel(string text, IEnumerable<MenuItemViewModelBase> subItems = null,
|
|
||||||
string description = null, Mdl2Icon icon = null) : base(text, description, icon)
|
|
||||||
{
|
|
||||||
if (subItems == null)
|
|
||||||
{
|
|
||||||
SubItems = new ObservableCollection<MenuItemViewModelBase>();
|
|
||||||
}
|
|
||||||
else if (subItems is ObservableCollection<MenuItemViewModelBase> observableSubItems)
|
|
||||||
{
|
|
||||||
SubItems = observableSubItems;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SubItems = new ObservableCollection<MenuItemViewModelBase>(subItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool EquivalentTo(MenuItemViewModelBase other)
|
|
||||||
{
|
|
||||||
if (!base.EquivalentTo(other)) return false;
|
|
||||||
|
|
||||||
if (!(other is ExpandableMenuItemViewModel otherExpandable) ||
|
|
||||||
SubItems.Count != otherExpandable.SubItems.Count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !SubItems.Where((t, i) => !t.EquivalentTo(otherExpandable.SubItems[i])).Any();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public sealed class Mdl2Icon
|
|
||||||
{
|
|
||||||
public string Color { get; }
|
|
||||||
public string Glyph { get; }
|
|
||||||
|
|
||||||
private Mdl2Icon(string glyph, string color = null)
|
|
||||||
{
|
|
||||||
Glyph = glyph;
|
|
||||||
Color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Mdl2Icon Link(string color = null) => new Mdl2Icon("\uE71B", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Copy(string color = null) => new Mdl2Icon("\uE8C8", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Paste(string color = null) => new Mdl2Icon("\uE77F", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Edit(string color = null) => new Mdl2Icon("\uE70F", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Search(string color = null) => new Mdl2Icon("\uE721", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Add(string color = null) => new Mdl2Icon("\uE710", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Cancel(string color = null) => new Mdl2Icon("\uE711", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon PaginationDotOutline10(string color = null) => new Mdl2Icon("\uF126", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon PaginationDotSolid10(string color = null) => new Mdl2Icon("\uF127", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon NewWindow(string color = null) => new Mdl2Icon("\uE78B", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon History(string color = null) => new Mdl2Icon("\uE81C", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Settings(string color = null) => new Mdl2Icon("\uE713", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Info(string color = null) => new Mdl2Icon("\uE946", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon FavoriteStar(string color = null) => new Mdl2Icon("\uE734", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Globe(string color = null) => new Mdl2Icon("\uE774", color);
|
|
||||||
|
|
||||||
public static Mdl2Icon Play(string color = null) => new Mdl2Icon("\uE768", color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
using FluentTerminal.Models.Enums;
|
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Windows.System;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class MenuItemKeyBindingViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
private int _key;
|
|
||||||
|
|
||||||
public int Key
|
|
||||||
{
|
|
||||||
get => _key;
|
|
||||||
set => SetProperty(ref _key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _keyModifiers;
|
|
||||||
|
|
||||||
// Corresponds to https://docs.microsoft.com/en-us/uwp/api/windows.system.virtualkeymodifiers
|
|
||||||
public int KeyModifiers
|
|
||||||
{
|
|
||||||
get => _keyModifiers;
|
|
||||||
private set => SetProperty(ref _keyModifiers, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _ctrl;
|
|
||||||
|
|
||||||
public bool Ctrl
|
|
||||||
{
|
|
||||||
get => _ctrl;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _ctrl, value))
|
|
||||||
{
|
|
||||||
SetKeyModifiers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _alt;
|
|
||||||
|
|
||||||
public bool Alt
|
|
||||||
{
|
|
||||||
get => _alt;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _alt, value))
|
|
||||||
{
|
|
||||||
SetKeyModifiers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _shift;
|
|
||||||
|
|
||||||
public bool Shift
|
|
||||||
{
|
|
||||||
get => _shift;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _shift, value))
|
|
||||||
{
|
|
||||||
SetKeyModifiers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _windows;
|
|
||||||
|
|
||||||
public bool Windows
|
|
||||||
{
|
|
||||||
get => _windows;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _windows, value))
|
|
||||||
{
|
|
||||||
SetKeyModifiers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsExtendedVirtualKey => !Enum.IsDefined(typeof(VirtualKey), Key);
|
|
||||||
|
|
||||||
public string GetOverrideText()
|
|
||||||
{
|
|
||||||
var segments = new List<string>();
|
|
||||||
|
|
||||||
if (Ctrl) segments.Add("Ctrl");
|
|
||||||
if (Alt) segments.Add("Alt");
|
|
||||||
if (Windows) segments.Add("Windows");
|
|
||||||
if (Shift) segments.Add("Shift");
|
|
||||||
|
|
||||||
segments.Add(EnumHelper.GetEnumDescription((ExtendedVirtualKey)Key));
|
|
||||||
|
|
||||||
return string.Join("+", segments);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItemKeyBindingViewModel(int key = 0, bool ctrl = false, bool alt = false, bool shift = false,
|
|
||||||
bool windows = false)
|
|
||||||
{
|
|
||||||
_key = key;
|
|
||||||
_ctrl = ctrl;
|
|
||||||
_alt = alt;
|
|
||||||
_shift = shift;
|
|
||||||
_windows = windows;
|
|
||||||
|
|
||||||
SetKeyModifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItemKeyBindingViewModel(KeyBinding keyBinding) : this(keyBinding.Key, keyBinding.Ctrl,
|
|
||||||
keyBinding.Alt, keyBinding.Shift, keyBinding.Meta)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void SetKeyModifiers()
|
|
||||||
{
|
|
||||||
// Based on VirtualKeyModifiers enum values (https://docs.microsoft.com/en-us/uwp/api/windows.system.virtualkeymodifiers)
|
|
||||||
var result = 0;
|
|
||||||
|
|
||||||
if (_ctrl)
|
|
||||||
{
|
|
||||||
result |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_alt)
|
|
||||||
{
|
|
||||||
result |= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_shift)
|
|
||||||
{
|
|
||||||
result |= 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_windows)
|
|
||||||
{
|
|
||||||
result |= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyModifiers = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool EquivalentTo(MenuItemKeyBindingViewModel other)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(this, other)) return true;
|
|
||||||
|
|
||||||
if (other == null) return false;
|
|
||||||
|
|
||||||
return _key == other._key && _keyModifiers == other._keyModifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class MenuItemViewModel : MenuItemViewModelBase
|
|
||||||
{
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
private MenuItemKeyBindingViewModel _keyBinding;
|
|
||||||
|
|
||||||
public MenuItemKeyBindingViewModel KeyBinding
|
|
||||||
{
|
|
||||||
get => _keyBinding;
|
|
||||||
set => SetProperty(ref _keyBinding, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICommand Command { get; }
|
|
||||||
|
|
||||||
#endregion Properties
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public MenuItemViewModel(string text, ICommand command, string description = null, Mdl2Icon icon = null,
|
|
||||||
MenuItemKeyBindingViewModel keyBinding = null) : base(text, description, icon)
|
|
||||||
{
|
|
||||||
_keyBinding = keyBinding;
|
|
||||||
Command = command ?? throw new ArgumentNullException(nameof(command));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Constructors
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public override bool EquivalentTo(MenuItemViewModelBase other)
|
|
||||||
{
|
|
||||||
if (!base.EquivalentTo(other)) return false;
|
|
||||||
|
|
||||||
if (!(other is MenuItemViewModel menuItem)) return false;
|
|
||||||
|
|
||||||
return _keyBinding?.EquivalentTo(menuItem._keyBinding) ?? menuItem._keyBinding == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using FluentTerminal.Models;
|
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class MenuItemViewModelBase : ObservableObject
|
|
||||||
{
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
private string _text;
|
|
||||||
|
|
||||||
public string Text
|
|
||||||
{
|
|
||||||
get => _text;
|
|
||||||
set => SetProperty(ref _text, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _description;
|
|
||||||
|
|
||||||
public string Description
|
|
||||||
{
|
|
||||||
get => _description;
|
|
||||||
set => SetProperty(ref _description, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mdl2Icon _icon;
|
|
||||||
|
|
||||||
public Mdl2Icon Icon
|
|
||||||
{
|
|
||||||
get => _icon;
|
|
||||||
set => SetProperty(ref _icon, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Properties
|
|
||||||
|
|
||||||
#region Constructor
|
|
||||||
|
|
||||||
protected MenuItemViewModelBase(string text, string description, Mdl2Icon icon)
|
|
||||||
{
|
|
||||||
_text = text;
|
|
||||||
_description = description;
|
|
||||||
_icon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Constructor
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public virtual bool EquivalentTo(MenuItemViewModelBase other)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(this, other)) return true;
|
|
||||||
|
|
||||||
if (other == null) return false;
|
|
||||||
|
|
||||||
return _text.NullableEqualTo(other._text) && _description.NullableEqualTo(other._description) &&
|
|
||||||
(_icon?.Equals(other._icon) ?? other._icon == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class MenuViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
public ObservableCollection<MenuItemViewModelBase> Items { get; }
|
|
||||||
|
|
||||||
public MenuViewModel(IEnumerable<MenuItemViewModelBase> items = null)
|
|
||||||
{
|
|
||||||
if (items == null)
|
|
||||||
{
|
|
||||||
Items = new ObservableCollection<MenuItemViewModelBase>();
|
|
||||||
}
|
|
||||||
else if (items is ObservableCollection<MenuItemViewModelBase> observableItems)
|
|
||||||
{
|
|
||||||
Items = observableItems;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Items = new ObservableCollection<MenuItemViewModelBase>(items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool EquivalentTo(MenuViewModel other)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(this, other)) return true;
|
|
||||||
|
|
||||||
if (other == null) return false;
|
|
||||||
|
|
||||||
if (Items.Count != other.Items.Count) return false;
|
|
||||||
|
|
||||||
return !Items.Where((t, i) => !t.EquivalentTo(other.Items[i])).Any();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class RadioMenuItemViewModel : MenuItemViewModelBase
|
|
||||||
{
|
|
||||||
public object BindingSource { get; }
|
|
||||||
public string BindingPath { get; }
|
|
||||||
public string GroupName { get; }
|
|
||||||
|
|
||||||
public RadioMenuItemViewModel(string text, string groupName, object bindingSource, string bindingPath = null, string description = null, Mdl2Icon icon = null)
|
|
||||||
: base(text, description, icon)
|
|
||||||
{
|
|
||||||
GroupName = groupName ?? throw new ArgumentNullException(nameof(groupName));
|
|
||||||
BindingSource = bindingSource ?? throw new ArgumentNullException(nameof(bindingSource));
|
|
||||||
BindingPath = bindingPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool EquivalentTo(MenuItemViewModelBase other)
|
|
||||||
{
|
|
||||||
if (!base.EquivalentTo(other)) return false;
|
|
||||||
|
|
||||||
if (!(other is RadioMenuItemViewModel radioMenuItem)) return false;
|
|
||||||
|
|
||||||
return GroupName == radioMenuItem.GroupName && BindingSource == radioMenuItem.BindingSource && BindingPath == radioMenuItem.BindingPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class SeparatorMenuItemViewModel : MenuItemViewModelBase
|
|
||||||
{
|
|
||||||
public SeparatorMenuItemViewModel()
|
|
||||||
: base(string.Empty, string.Empty, null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool EquivalentTo(MenuItemViewModelBase other)
|
|
||||||
{
|
|
||||||
return other is SeparatorMenuItemViewModel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Menu
|
|
||||||
{
|
|
||||||
public class ToggleMenuItemViewModel : MenuItemViewModelBase
|
|
||||||
{
|
|
||||||
public object BindingSource { get; }
|
|
||||||
public string BindingPath { get; }
|
|
||||||
|
|
||||||
public ToggleMenuItemViewModel(string text, object bindingSource, string bindingPath = null, string description = null, Mdl2Icon icon = null)
|
|
||||||
: base(text, description, icon)
|
|
||||||
{
|
|
||||||
BindingSource = bindingSource ?? throw new ArgumentNullException(nameof(bindingSource));
|
|
||||||
BindingPath = bindingPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool EquivalentTo(MenuItemViewModelBase other)
|
|
||||||
{
|
|
||||||
if (!base.EquivalentTo(other)) return false;
|
|
||||||
|
|
||||||
if (!(other is ToggleMenuItemViewModel toggleMenuItem)) return false;
|
|
||||||
|
|
||||||
return BindingSource == toggleMenuItem.BindingSource && BindingPath == toggleMenuItem.BindingPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using System;
|
|
||||||
using Windows.UI.Xaml;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
|
||||||
{
|
|
||||||
public class OverlayViewModel : ObservableObject
|
|
||||||
{
|
|
||||||
private readonly DispatcherTimer _overlayTimer;
|
|
||||||
private bool _showOverlay;
|
|
||||||
private string _overlayContent;
|
|
||||||
|
|
||||||
// Important! The constructor has to be called from the UI thread.
|
|
||||||
public OverlayViewModel()
|
|
||||||
{
|
|
||||||
_overlayTimer = new DispatcherTimer {Interval = new TimeSpan(0, 0, 2)};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ShowOverlay
|
|
||||||
{
|
|
||||||
get => _showOverlay;
|
|
||||||
set => SetProperty(ref _showOverlay, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OverlayContent
|
|
||||||
{
|
|
||||||
get => _overlayContent;
|
|
||||||
set => SetProperty(ref _overlayContent, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Show(string message)
|
|
||||||
{
|
|
||||||
OverlayContent = message;
|
|
||||||
ShowOverlay = true;
|
|
||||||
|
|
||||||
if (_overlayTimer.IsEnabled)
|
|
||||||
{
|
|
||||||
_overlayTimer.Stop();
|
|
||||||
_overlayTimer.Tick -= OnResizeOverlayTimerFinished;
|
|
||||||
}
|
|
||||||
_overlayTimer.Start();
|
|
||||||
_overlayTimer.Tick += OnResizeOverlayTimerFinished;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnResizeOverlayTimerFinished(object sender, object e)
|
|
||||||
{
|
|
||||||
_overlayTimer.Stop();
|
|
||||||
_overlayTimer.Tick -= OnResizeOverlayTimerFinished;
|
|
||||||
ShowOverlay = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,258 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using FluentTerminal.App.Services;
|
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.App.ViewModels.Profiles;
|
|
||||||
using FluentTerminal.App.ViewModels.Settings;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for all profile view models. Implements logic for saving, editing, resetting, etc.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ProfileViewModelBase<T> : ObservableObject where T : ProfileProviderViewModelBase
|
|
||||||
{
|
|
||||||
#region Fields
|
|
||||||
|
|
||||||
protected readonly IDialogService DialogService;
|
|
||||||
protected readonly ISettingsService SettingsService;
|
|
||||||
|
|
||||||
private List<KeyBinding> _bindingsBeforeEdit;
|
|
||||||
private Dictionary<string, string> _environmentVariablesBeforeEdit;
|
|
||||||
private string _nameBeforeEdit;
|
|
||||||
|
|
||||||
#endregion Fields
|
|
||||||
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
protected bool IsNew { get; private set; }
|
|
||||||
|
|
||||||
public Guid Id { get; private set; }
|
|
||||||
|
|
||||||
private bool _inEditMode;
|
|
||||||
|
|
||||||
public bool InEditMode
|
|
||||||
{
|
|
||||||
get => _inEditMode;
|
|
||||||
set => SetProperty(ref _inEditMode, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _name;
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get => _name;
|
|
||||||
set => SetProperty(ref _name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyBindingsViewModel KeyBindings { get; }
|
|
||||||
|
|
||||||
public ObservableCollection<EnvironmentVariableViewModel> EnvironmentVariables { get; } = new ObservableCollection<EnvironmentVariableViewModel>();
|
|
||||||
|
|
||||||
public T ProfileVm { get; protected set; }
|
|
||||||
|
|
||||||
#endregion Properties
|
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
// Needs to be triggered from UI thread
|
|
||||||
public event EventHandler Deleted;
|
|
||||||
|
|
||||||
#endregion Events
|
|
||||||
|
|
||||||
#region Commands
|
|
||||||
|
|
||||||
public ICommand CancelEditCommand { get; }
|
|
||||||
|
|
||||||
public ICommand DeleteCommand { get; }
|
|
||||||
|
|
||||||
public ICommand EditCommand { get; }
|
|
||||||
|
|
||||||
public ICommand SaveChangesCommand { get; }
|
|
||||||
|
|
||||||
public ICommand AddKeyboardShortcutCommand { get; }
|
|
||||||
|
|
||||||
#endregion Commands
|
|
||||||
|
|
||||||
#region Constructor
|
|
||||||
|
|
||||||
protected ProfileViewModelBase(ShellProfile shellProfile, ISettingsService settingsService, IDialogService dialogService, bool isNew)
|
|
||||||
{
|
|
||||||
SettingsService = settingsService;
|
|
||||||
DialogService = dialogService;
|
|
||||||
IsNew = isNew;
|
|
||||||
|
|
||||||
KeyBindings = new KeyBindingsViewModel(shellProfile.Id.ToString(), dialogService, string.Empty, false);
|
|
||||||
|
|
||||||
foreach (var environmentVariable in shellProfile.EnvironmentVariables)
|
|
||||||
{
|
|
||||||
EnvironmentVariables.Add(new EnvironmentVariableViewModel
|
|
||||||
{
|
|
||||||
Name = environmentVariable.Key,
|
|
||||||
Value = environmentVariable.Value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Initialize(shellProfile);
|
|
||||||
|
|
||||||
DeleteCommand = new AsyncRelayCommand(DeleteAsync, CanDelete);
|
|
||||||
EditCommand = new RelayCommand(Edit);
|
|
||||||
CancelEditCommand = new AsyncRelayCommand(CancelEditAsync);
|
|
||||||
SaveChangesCommand = new AsyncRelayCommand(SaveChangesAsync);
|
|
||||||
AddKeyboardShortcutCommand = new AsyncRelayCommand(AddKeyboardShortcutAsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Constructor
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
// Loads view model properties from the input shellProfile
|
|
||||||
protected void Initialize(ShellProfile shellProfile)
|
|
||||||
{
|
|
||||||
Id = shellProfile.Id;
|
|
||||||
Name = shellProfile.Name;
|
|
||||||
|
|
||||||
KeyBindings.Clear();
|
|
||||||
foreach (var keyBinding in shellProfile.KeyBindings.Select(x => new KeyBinding(x)).ToList())
|
|
||||||
{
|
|
||||||
KeyBindings.Add(keyBinding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool HasChanges()
|
|
||||||
{
|
|
||||||
return ProfileVm.HasChanges()
|
|
||||||
|| !_nameBeforeEdit.NullableEqualTo(_name)
|
|
||||||
|| !(_bindingsBeforeEdit?.SequenceEqual(KeyBindings.KeyBindings.Select(x => x.Model).ToList()) ?? false)
|
|
||||||
|| !(_environmentVariablesBeforeEdit?.SequenceEqual(EnvironmentVariables.ToDictionary(x => x.Name, x => x.Value)) ?? false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task DeleteAsync()
|
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we need to trigger Deleted event from the calling (UI) thread.
|
|
||||||
var result = await DialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmDeleteProfile"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (result == DialogButton.OK)
|
|
||||||
{
|
|
||||||
Deleted?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract bool CanDelete();
|
|
||||||
|
|
||||||
private void Edit()
|
|
||||||
{
|
|
||||||
if (InEditMode)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_bindingsBeforeEdit = KeyBindings.KeyBindings.Select(x => x.Model).ToList();
|
|
||||||
_environmentVariablesBeforeEdit = EnvironmentVariables.ToDictionary(x => x.Name, x => x.Value);
|
|
||||||
_nameBeforeEdit = _name;
|
|
||||||
|
|
||||||
KeyBindings.Editable = true;
|
|
||||||
InEditMode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task CancelEditAsync()
|
|
||||||
{
|
|
||||||
if (IsNew)
|
|
||||||
{
|
|
||||||
await DeleteAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HasChanges())
|
|
||||||
{
|
|
||||||
KeyBindings.Editable = false;
|
|
||||||
InEditMode = false;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards
|
|
||||||
if (await DialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmDiscardChanges"), DialogButton.OK, DialogButton.Cancel)
|
|
||||||
.ConfigureAwait(true) == DialogButton.OK)
|
|
||||||
{
|
|
||||||
// Cancelled, so rollback
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards
|
|
||||||
await ProfileVm.RejectChangesAsync().ConfigureAwait(true);
|
|
||||||
|
|
||||||
KeyBindings.Editable = false;
|
|
||||||
InEditMode = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task SaveChangesAsync()
|
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
var error = await ProfileVm.AcceptChangesAsync().ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(error))
|
|
||||||
{
|
|
||||||
await DialogService.ShowMessageDialogAsync(I18N.Translate("InvalidInput"), error, DialogButton.OK)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EnvironmentVariables.Select(x => x.Name).Any(string.IsNullOrWhiteSpace))
|
|
||||||
{
|
|
||||||
await DialogService.ShowMessageDialogAsync(I18N.Translate("InvalidInput"),
|
|
||||||
I18N.Translate("EmptyEnvironmentVariableName"), DialogButton.OK).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EnvironmentVariables.Select(x => x.Name).Distinct().Count() != EnvironmentVariables.Count)
|
|
||||||
{
|
|
||||||
await DialogService.ShowMessageDialogAsync(I18N.Translate("InvalidInput"),
|
|
||||||
I18N.Translate("DuplicateEnvironmentVariable"), DialogButton.OK).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var profile = ProfileVm.Model;
|
|
||||||
|
|
||||||
profile.Id = Id;
|
|
||||||
profile.Name = _name;
|
|
||||||
profile.KeyBindings = KeyBindings.KeyBindings.Select(x => x.Model).ToList();
|
|
||||||
profile.EnvironmentVariables = EnvironmentVariables.ToDictionary(x => x.Name, x => x.Value);
|
|
||||||
|
|
||||||
if (profile is SshProfile sshProfile)
|
|
||||||
{
|
|
||||||
SettingsService.SaveSshProfile(sshProfile, IsNew);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SettingsService.SaveShellProfile(profile, IsNew);
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyBindings.Editable = false;
|
|
||||||
InEditMode = false;
|
|
||||||
IsNew = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task AddKeyboardShortcutAsync()
|
|
||||||
{
|
|
||||||
return KeyBindings.ShowAddKeyBindingDialogAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using FluentTerminal.App.Services;
|
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Profiles
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Used for common profiles (PowerShell, CMD, WSL).
|
|
||||||
/// </summary>
|
|
||||||
public class CommonProfileProviderViewModel : ProfileProviderViewModelBase
|
|
||||||
{
|
|
||||||
#region Fields
|
|
||||||
|
|
||||||
private readonly IFileSystemService _fileSystemService;
|
|
||||||
|
|
||||||
#endregion Fields
|
|
||||||
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
public bool PreInstalled { get; private set; }
|
|
||||||
|
|
||||||
private string _location;
|
|
||||||
|
|
||||||
public string Location
|
|
||||||
{
|
|
||||||
get => _location;
|
|
||||||
set => SetProperty(ref _location, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _workingDirectory;
|
|
||||||
|
|
||||||
public string WorkingDirectory
|
|
||||||
{
|
|
||||||
get => _workingDirectory;
|
|
||||||
set => SetProperty(ref _workingDirectory, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _arguments;
|
|
||||||
|
|
||||||
public string Arguments
|
|
||||||
{
|
|
||||||
get => _arguments;
|
|
||||||
set => SetProperty(ref _arguments, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Properties
|
|
||||||
|
|
||||||
#region Commands
|
|
||||||
|
|
||||||
public ICommand BrowseForCustomShellCommand { get; }
|
|
||||||
|
|
||||||
public ICommand BrowseForWorkingDirectoryCommand { get; }
|
|
||||||
|
|
||||||
#endregion Commands
|
|
||||||
|
|
||||||
#region Constructor
|
|
||||||
|
|
||||||
public CommonProfileProviderViewModel(ISettingsService settingsService, IApplicationView applicationView,
|
|
||||||
IFileSystemService fileSystemService, ShellProfile original = null) : base(settingsService, applicationView,
|
|
||||||
true, original)
|
|
||||||
{
|
|
||||||
_fileSystemService = fileSystemService;
|
|
||||||
|
|
||||||
BrowseForCustomShellCommand = new AsyncRelayCommand(BrowseForCustomShell);
|
|
||||||
BrowseForWorkingDirectoryCommand = new AsyncRelayCommand(BrowseForWorkingDirectory);
|
|
||||||
|
|
||||||
Initialize(Model);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Constructor
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
private void Initialize(ShellProfile profile)
|
|
||||||
{
|
|
||||||
PreInstalled = profile.PreInstalled;
|
|
||||||
Location = profile.Location;
|
|
||||||
Arguments = profile.Arguments;
|
|
||||||
WorkingDirectory = profile.WorkingDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadFromProfile(ShellProfile profile)
|
|
||||||
{
|
|
||||||
base.LoadFromProfile(profile);
|
|
||||||
|
|
||||||
Initialize(profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task CopyToProfileAsync(ShellProfile profile)
|
|
||||||
{
|
|
||||||
await base.CopyToProfileAsync(profile).ConfigureAwait(false);
|
|
||||||
|
|
||||||
profile.Location = _location;
|
|
||||||
profile.Arguments = _arguments;
|
|
||||||
profile.WorkingDirectory = _workingDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<string> ValidateAsync()
|
|
||||||
{
|
|
||||||
var error = await base.ValidateAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(error))
|
|
||||||
{
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_location))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = I18N.Translate("LocationCantBeEmpty");
|
|
||||||
|
|
||||||
return string.IsNullOrEmpty(error) ? "Location cannot be empty." : error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool HasChanges()
|
|
||||||
{
|
|
||||||
return base.HasChanges() || !Model.Location.NullableEqualTo(_location) ||
|
|
||||||
!Model.WorkingDirectory.NullableEqualTo(_workingDirectory) ||
|
|
||||||
!Model.Arguments.NullableEqualTo(_arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task BrowseForCustomShell()
|
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
var file = await _fileSystemService.OpenFileAsync(new[] { ".exe" }).ConfigureAwait(true);
|
|
||||||
if (file != null)
|
|
||||||
{
|
|
||||||
Location = file.Path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task BrowseForWorkingDirectory()
|
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
var directory = await _fileSystemService.BrowseForDirectoryAsync().ConfigureAwait(true);
|
|
||||||
if (directory != null)
|
|
||||||
{
|
|
||||||
WorkingDirectory = directory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,386 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Web;
|
|
||||||
using Windows.UI.Core;
|
|
||||||
using FluentTerminal.App.Services;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
using FluentTerminal.Models.Messages;
|
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Messaging;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Profiles
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for all profile view model classes. It contains properties shared by all other view models
|
|
||||||
/// (theme-related properties, line-ending translation, and WinPTY/ConPTY selection).
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ProfileProviderViewModelBase : ObservableObject,
|
|
||||||
IRecipient<ThemeAddedMessage>,
|
|
||||||
IRecipient<ThemeDeletedMessage>
|
|
||||||
{
|
|
||||||
#region Fields
|
|
||||||
|
|
||||||
protected readonly ISettingsService SettingsService;
|
|
||||||
protected readonly IApplicationView ApplicationView;
|
|
||||||
private readonly bool _strictProfileType;
|
|
||||||
|
|
||||||
#endregion Fields
|
|
||||||
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
private ShellProfile _model;
|
|
||||||
|
|
||||||
public ShellProfile Model
|
|
||||||
{
|
|
||||||
get => _model;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReferenceEquals(_model, value))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_strictProfileType && _model != null && _model.GetType() != value.GetType())
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"The value has to be of type {_model.GetType().Name}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_model = value;
|
|
||||||
|
|
||||||
LoadFromProfile(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableCollection<TabTheme> TabThemes { get; }
|
|
||||||
|
|
||||||
public ObservableCollection<TerminalTheme> TerminalThemes { get; }
|
|
||||||
|
|
||||||
private TabTheme _selectedTabTheme;
|
|
||||||
|
|
||||||
public TabTheme SelectedTabTheme
|
|
||||||
{
|
|
||||||
get => _selectedTabTheme ?? (_selectedTabTheme = TabThemes.FirstOrDefault(t => t.Id.Equals(_tabThemeId)));
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
// Ignore attempt setting null.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that it's from the list
|
|
||||||
var theme = TabThemes.Contains(value)
|
|
||||||
? value
|
|
||||||
: TabThemes.FirstOrDefault(t => t.Id == value.Id) ?? TabThemes.First();
|
|
||||||
|
|
||||||
if (SetProperty(ref _selectedTabTheme, theme))
|
|
||||||
{
|
|
||||||
_tabThemeId = theme.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _tabThemeId;
|
|
||||||
|
|
||||||
public int TabThemeId
|
|
||||||
{
|
|
||||||
get => _tabThemeId;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _tabThemeId, value))
|
|
||||||
{
|
|
||||||
SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id.Equals(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private TerminalTheme _selectedTerminalTheme;
|
|
||||||
|
|
||||||
public TerminalTheme SelectedTerminalTheme
|
|
||||||
{
|
|
||||||
get => _selectedTerminalTheme ??
|
|
||||||
(_selectedTerminalTheme = TerminalThemes.FirstOrDefault(t => t.Id.Equals(_terminalThemeId)));
|
|
||||||
set
|
|
||||||
{
|
|
||||||
TerminalTheme theme = value;
|
|
||||||
|
|
||||||
if (theme == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (!TerminalThemes.Contains(theme))
|
|
||||||
{
|
|
||||||
// Ensure that it's from the list
|
|
||||||
theme = TerminalThemes.FirstOrDefault(t => t.Id.Equals(theme.Id)) ?? TerminalThemes.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SetProperty(ref _selectedTerminalTheme, theme))
|
|
||||||
{
|
|
||||||
_terminalThemeId = theme.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Guid _terminalThemeId;
|
|
||||||
|
|
||||||
public Guid TerminalThemeId
|
|
||||||
{
|
|
||||||
get => _terminalThemeId;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (SetProperty(ref _terminalThemeId, value))
|
|
||||||
{
|
|
||||||
SelectedTerminalTheme = TerminalThemes.FirstOrDefault(t => t.Id.Equals(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _useConPty;
|
|
||||||
|
|
||||||
public bool UseConPty
|
|
||||||
{
|
|
||||||
get => _useConPty;
|
|
||||||
set => SetProperty(ref _useConPty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _useBuffer;
|
|
||||||
|
|
||||||
public bool UseBuffer
|
|
||||||
{
|
|
||||||
get => _useBuffer;
|
|
||||||
set => SetProperty(ref _useBuffer, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Properties
|
|
||||||
|
|
||||||
#region Constructor
|
|
||||||
|
|
||||||
protected ProfileProviderViewModelBase(ISettingsService settingsService, IApplicationView applicationView,
|
|
||||||
bool strictProfileType, ShellProfile original = null)
|
|
||||||
{
|
|
||||||
SettingsService = settingsService;
|
|
||||||
ApplicationView = applicationView;
|
|
||||||
_strictProfileType = strictProfileType;
|
|
||||||
|
|
||||||
_model = original ?? new ShellProfile();
|
|
||||||
|
|
||||||
TabThemes = new ObservableCollection<TabTheme>(settingsService.GetTabThemes());
|
|
||||||
|
|
||||||
SelectedTabTheme = TabThemes.First();
|
|
||||||
|
|
||||||
TerminalThemes = new ObservableCollection<TerminalTheme>(settingsService.GetThemes());
|
|
||||||
|
|
||||||
TerminalThemes.Insert(0, new TerminalTheme {Id = Guid.Empty, Name = "Default"});
|
|
||||||
|
|
||||||
SelectedTerminalTheme = TerminalThemes.First();
|
|
||||||
|
|
||||||
Initialize(Model);
|
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Constructor
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public void Receive(ThemeDeletedMessage message)
|
|
||||||
{
|
|
||||||
var theme = TerminalThemes.FirstOrDefault(t => t.Id.Equals(message.ThemeId));
|
|
||||||
|
|
||||||
if (theme == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplicationView.ExecuteOnUiThreadAsync(() =>
|
|
||||||
{
|
|
||||||
TerminalThemes.Remove(theme);
|
|
||||||
|
|
||||||
if (_terminalThemeId.Equals(message.ThemeId))
|
|
||||||
{
|
|
||||||
SelectedTerminalTheme = TerminalThemes.First();
|
|
||||||
}
|
|
||||||
}, CoreDispatcherPriority.Low, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Receive(ThemeAddedMessage message)
|
|
||||||
{
|
|
||||||
ApplicationView.ExecuteOnUiThreadAsync(() => TerminalThemes.Add(message.Theme), CoreDispatcherPriority.Low);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize(ShellProfile profile)
|
|
||||||
{
|
|
||||||
UseConPty = profile.UseConPty;
|
|
||||||
UseBuffer = profile.UseBuffer;
|
|
||||||
TerminalThemeId = profile.TerminalThemeId;
|
|
||||||
TabThemeId = profile.TabThemeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void LoadFromProfile(ShellProfile profile)
|
|
||||||
{
|
|
||||||
Initialize(profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual Task CopyToProfileAsync(ShellProfile profile)
|
|
||||||
{
|
|
||||||
profile.UseConPty = _useConPty;
|
|
||||||
profile.UseBuffer = _useBuffer;
|
|
||||||
profile.TerminalThemeId = _terminalThemeId;
|
|
||||||
profile.TabThemeId = _tabThemeId;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Task<string> ValidateAsync()
|
|
||||||
{
|
|
||||||
return Task.FromResult<string>(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns <c>true</c> if view model values aren't equal to the corresponding values of the
|
|
||||||
/// underlying <see cref="ShellProfile"/>.
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool HasChanges()
|
|
||||||
{
|
|
||||||
return Model.UseConPty != _useConPty ||
|
|
||||||
Model.UseBuffer != _useBuffer ||
|
|
||||||
!Model.TerminalThemeId.Equals(_terminalThemeId) ||
|
|
||||||
Model.TabThemeId != _tabThemeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Copies values from the view model to the underlying <see cref="ShellProfile"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="acceptIfInvalid">Defines what will happen if the view model data is invalid (if
|
|
||||||
/// <see cref="ValidateAsync"/> returns non-empty string). If this parameter is set to <c>true</c>, the
|
|
||||||
/// data will be copied to the underlying <see cref="ShellProfile"/> anyway. If it's set to <c>false</c>,
|
|
||||||
/// (default) the underlying <see cref="ShellProfile"/> won't be changed at all.</param>
|
|
||||||
/// <returns><c>null</c> or empty string if the operation was successful (the data is valid), or an error
|
|
||||||
/// message returned by <see cref="ValidateAsync"/> method.</returns>
|
|
||||||
public async Task<string> AcceptChangesAsync(bool acceptIfInvalid = false)
|
|
||||||
{
|
|
||||||
var error = await ValidateAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (acceptIfInvalid || string.IsNullOrEmpty(error))
|
|
||||||
{
|
|
||||||
await CopyToProfileAsync(Model).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RejectChangesAsync()
|
|
||||||
{
|
|
||||||
return ApplicationView.ExecuteOnUiThreadAsync(() => LoadFromProfile(Model));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
|
|
||||||
#region Links/shortcuts related
|
|
||||||
|
|
||||||
private const string UseConPtyQueryStringName = "conpty";
|
|
||||||
private const string UseBufferQueryStringName = "buffer";
|
|
||||||
private const string TerminalThemeIdQueryStringName = "theme";
|
|
||||||
private const string TabThemeIdQueryStringName = "tab";
|
|
||||||
|
|
||||||
private const string ShortcutFileFormat = @"[{{000214A0-0000-0000-C000-000000000046}}]
|
|
||||||
Prop3=19,0
|
|
||||||
[InternetShortcut]
|
|
||||||
IDList=
|
|
||||||
URL={0}
|
|
||||||
";
|
|
||||||
|
|
||||||
public static string GetShortcutFileContent(string url) => string.Format(ShortcutFileFormat, url);
|
|
||||||
|
|
||||||
public static IEnumerable<Tuple<string, string>> ParseParams(string uriOpts, char separator) =>
|
|
||||||
uriOpts.Split(separator).Select(ParseSshOptionFromUri).Where(p => p != null);
|
|
||||||
|
|
||||||
private static Tuple<string, string> ParseSshOptionFromUri(string option)
|
|
||||||
{
|
|
||||||
string[] nv = option.Split('=');
|
|
||||||
|
|
||||||
if (nv.Length != 2 || string.IsNullOrEmpty(nv[0]))
|
|
||||||
{
|
|
||||||
// For now simply ignore invalid options
|
|
||||||
return null;
|
|
||||||
//throw new FormatException($"Invalid SSH option '{option}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Tuple.Create(HttpUtility.UrlDecode(nv[0]), HttpUtility.UrlDecode(nv[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates URL.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><see cref="Tuple"/> whose first item represents success. If the first item is <c>true</c>,
|
|
||||||
/// the second contains the URL. If the first item is <c>false</c>, the second contains error message.</returns>
|
|
||||||
public virtual Task<Tuple<bool, string>> GetUrlAsync() =>
|
|
||||||
Task.FromResult(Tuple.Create(false, "Not supported."));
|
|
||||||
|
|
||||||
public string GetBaseQueryString()
|
|
||||||
{
|
|
||||||
var queryString = $"{UseConPtyQueryStringName}={_useConPty}&{UseBufferQueryStringName}={_useBuffer}";
|
|
||||||
|
|
||||||
if (_tabThemeId != TabThemes.First().Id)
|
|
||||||
{
|
|
||||||
queryString += $"&{TabThemeIdQueryStringName}={_tabThemeId:##########}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_terminalThemeId.Equals(TerminalThemes.First().Id))
|
|
||||||
{
|
|
||||||
queryString += $"&{TerminalThemeIdQueryStringName}={_terminalThemeId}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return queryString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadBaseFromQueryString(IList<Tuple<string, string>> queryStringParams)
|
|
||||||
{
|
|
||||||
if (queryStringParams == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyValue = queryStringParams.FirstOrDefault(t =>
|
|
||||||
UseConPtyQueryStringName.Equals(t.Item1, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(keyValue?.Item2) && bool.TryParse(keyValue.Item2?.ToLower(), out bool useConPty))
|
|
||||||
{
|
|
||||||
UseConPty = useConPty;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyValue = queryStringParams.FirstOrDefault(t =>
|
|
||||||
UseBufferQueryStringName.Equals(t.Item1, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(keyValue?.Item2) && bool.TryParse(keyValue.Item2?.ToLower(), out bool useBuffer))
|
|
||||||
{
|
|
||||||
UseConPty = useBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyValue = queryStringParams.FirstOrDefault(t =>
|
|
||||||
TabThemeIdQueryStringName.Equals(t.Item1, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(keyValue?.Item2) && int.TryParse(keyValue.Item2, out int tabThemeId))
|
|
||||||
{
|
|
||||||
TabThemeId = tabThemeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
keyValue = queryStringParams.FirstOrDefault(t =>
|
|
||||||
TerminalThemeIdQueryStringName.Equals(t.Item1, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(keyValue?.Item2) && Guid.TryParse(keyValue.Item2, out Guid terminalThemeId))
|
|
||||||
{
|
|
||||||
TerminalThemeId = terminalThemeId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Links/shortcuts related
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,537 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Web;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using FluentTerminal.App.Services;
|
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Profiles
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// View-model for rich-UI based SSH profiles.
|
|
||||||
/// </summary>
|
|
||||||
public class SshConnectViewModel : ProfileProviderViewModelBase
|
|
||||||
{
|
|
||||||
#region Static
|
|
||||||
|
|
||||||
public static string GetErrorString(SshConnectionInfoValidationResult result, string separator = "; ") =>
|
|
||||||
string.Join(separator, GetErrors(result));
|
|
||||||
|
|
||||||
public static IEnumerable<string> GetErrors(SshConnectionInfoValidationResult result)
|
|
||||||
{
|
|
||||||
if (result == SshConnectionInfoValidationResult.Valid)
|
|
||||||
{
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var value in Enum.GetValues(typeof(SshConnectionInfoValidationResult))
|
|
||||||
.Cast<SshConnectionInfoValidationResult>().Where(r => r != SshConnectionInfoValidationResult.Valid))
|
|
||||||
{
|
|
||||||
if ((value & result) == value)
|
|
||||||
{
|
|
||||||
yield return I18N.Translate($"{nameof(SshConnectionInfoValidationResult)}.{value}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Static
|
|
||||||
|
|
||||||
#region Fields
|
|
||||||
|
|
||||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
|
||||||
private readonly IFileSystemService _fileSystemService;
|
|
||||||
|
|
||||||
// To prevent validating the existence of the same file multiple times because it's kinda expensive
|
|
||||||
private string _validatedIdentityFile;
|
|
||||||
|
|
||||||
#endregion Fields
|
|
||||||
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
private string _host;
|
|
||||||
|
|
||||||
public string Host
|
|
||||||
{
|
|
||||||
get => _host;
|
|
||||||
set => SetProperty(ref _host, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ushort _sshPort;
|
|
||||||
|
|
||||||
public ushort SshPort
|
|
||||||
{
|
|
||||||
get => _sshPort;
|
|
||||||
set => SetProperty(ref _sshPort, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _username;
|
|
||||||
|
|
||||||
public string Username
|
|
||||||
{
|
|
||||||
get => _username;
|
|
||||||
set => SetProperty(ref _username, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _identityFile;
|
|
||||||
|
|
||||||
public string IdentityFile
|
|
||||||
{
|
|
||||||
get => _identityFile;
|
|
||||||
set => SetProperty(ref _identityFile, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _useMosh;
|
|
||||||
|
|
||||||
public bool UseMosh
|
|
||||||
{
|
|
||||||
get => _useMosh;
|
|
||||||
set => SetProperty(ref _useMosh, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ushort _moshPortFrom;
|
|
||||||
|
|
||||||
public ushort MoshPortFrom
|
|
||||||
{
|
|
||||||
get => _moshPortFrom;
|
|
||||||
set => SetProperty(ref _moshPortFrom, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ushort _moshPortTo;
|
|
||||||
|
|
||||||
public ushort MoshPortTo
|
|
||||||
{
|
|
||||||
get => _moshPortTo;
|
|
||||||
set => SetProperty(ref _moshPortTo, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Properties
|
|
||||||
|
|
||||||
#region Commands
|
|
||||||
|
|
||||||
public ICommand BrowseForIdentityFileCommand { get; }
|
|
||||||
|
|
||||||
#endregion Commands
|
|
||||||
|
|
||||||
#region Constructor
|
|
||||||
|
|
||||||
public SshConnectViewModel(ISettingsService settingsService, IApplicationView applicationView,
|
|
||||||
ITrayProcessCommunicationService trayProcessCommunicationService, IFileSystemService fileSystemService,
|
|
||||||
SshProfile original = null) : base(settingsService, applicationView, true,
|
|
||||||
original ?? new SshProfile { UseMosh = settingsService.GetApplicationSettings().UseMoshByDefault,
|
|
||||||
RequestConPty = settingsService.GetApplicationSettings().UseConPty
|
|
||||||
})
|
|
||||||
{
|
|
||||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
|
||||||
_fileSystemService = fileSystemService;
|
|
||||||
|
|
||||||
Initialize((SshProfile)Model);
|
|
||||||
|
|
||||||
BrowseForIdentityFileCommand = new AsyncRelayCommand(BrowseForIdentityFileAsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Constructor
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
// Fills the view model properties from the input sshProfile
|
|
||||||
private void Initialize(SshProfile sshProfile)
|
|
||||||
{
|
|
||||||
Host = sshProfile.Host;
|
|
||||||
SshPort = sshProfile.SshPort;
|
|
||||||
|
|
||||||
Username = sshProfile.Username;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Username))
|
|
||||||
{
|
|
||||||
_trayProcessCommunicationService.GetUserNameAsync().ContinueWith(t =>
|
|
||||||
{
|
|
||||||
var username = t.Result;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(username))
|
|
||||||
{
|
|
||||||
ApplicationView.ExecuteOnUiThreadAsync(() => Username = username);
|
|
||||||
}
|
|
||||||
}, TaskContinuationOptions.OnlyOnRanToCompletion);
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityFile = sshProfile.IdentityFile;
|
|
||||||
UseMosh = sshProfile.UseMosh;
|
|
||||||
MoshPortFrom = sshProfile.MoshPortFrom;
|
|
||||||
MoshPortTo = sshProfile.MoshPortTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task BrowseForIdentityFileAsync()
|
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
var file = await _fileSystemService.OpenFileAsync(new[] { "*" }).ConfigureAwait(true);
|
|
||||||
if (file != null)
|
|
||||||
{
|
|
||||||
IdentityFile = file.Path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetArgumentsString()
|
|
||||||
{
|
|
||||||
var sshArguments = GetSshArguments();
|
|
||||||
|
|
||||||
if (!_useMosh)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(sshArguments) ? $"{_username}@{_host}" : $"{sshArguments} {_username}@{_host}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.IsNullOrEmpty(sshArguments)
|
|
||||||
? $"-p {_moshPortFrom}:{_moshPortTo} {_username}@{_host}"
|
|
||||||
: $"-p {_moshPortFrom}:{_moshPortTo} --ssh=\"ssh {sshArguments}\" {_username}@{_host}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetSshArguments()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_identityFile))
|
|
||||||
{
|
|
||||||
return _sshPort == SshProfile.DefaultSshPort ? null : $"-p {_sshPort:#####}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return _sshPort == SshProfile.DefaultSshPort
|
|
||||||
? $"-i \"{_identityFile}\""
|
|
||||||
: $"-p {_sshPort:#####} -i \"{_identityFile}\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadFromProfile(ShellProfile profile)
|
|
||||||
{
|
|
||||||
base.LoadFromProfile(profile);
|
|
||||||
|
|
||||||
Initialize((SshProfile)profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task CopyToProfileAsync(ShellProfile profile)
|
|
||||||
{
|
|
||||||
await base.CopyToProfileAsync(profile).ConfigureAwait(false);
|
|
||||||
|
|
||||||
var sshProfile = (SshProfile) profile;
|
|
||||||
|
|
||||||
sshProfile.Location = _useMosh ? Constants.MoshCommandName : Constants.SshCommandName;
|
|
||||||
sshProfile.Arguments = GetArgumentsString();
|
|
||||||
|
|
||||||
sshProfile.WorkingDirectory = null;
|
|
||||||
|
|
||||||
sshProfile.Host = _host;
|
|
||||||
sshProfile.SshPort = _sshPort;
|
|
||||||
sshProfile.Username = _username;
|
|
||||||
sshProfile.IdentityFile = _identityFile;
|
|
||||||
sshProfile.UseMosh = _useMosh;
|
|
||||||
sshProfile.MoshPortFrom = _moshPortFrom;
|
|
||||||
sshProfile.MoshPortTo = _moshPortTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<string> ValidateAsync()
|
|
||||||
{
|
|
||||||
var error = await base.ValidateAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(error))
|
|
||||||
{
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await GetSshInfoValidationResultAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (result == SshConnectionInfoValidationResult.Valid)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = GetErrorString(result, Environment.NewLine);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(error))
|
|
||||||
{
|
|
||||||
error = "Invalid input.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool HasChanges()
|
|
||||||
{
|
|
||||||
var original = (SshProfile)Model;
|
|
||||||
|
|
||||||
return base.HasChanges() || !original.Host.NullableEqualTo(_host) || original.SshPort != _sshPort ||
|
|
||||||
!original.Username.NullableEqualTo(_username) ||
|
|
||||||
!original.IdentityFile.NullableEqualTo(_identityFile) || original.UseMosh != _useMosh ||
|
|
||||||
original.MoshPortFrom != _moshPortFrom || original.MoshPortTo != _moshPortTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<SshConnectionInfoValidationResult> GetSshInfoValidationResultAsync()
|
|
||||||
{
|
|
||||||
var result = SshConnectionInfoValidationResult.Valid;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_username))
|
|
||||||
{
|
|
||||||
result |= SshConnectionInfoValidationResult.UsernameEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_host))
|
|
||||||
{
|
|
||||||
result |= SshConnectionInfoValidationResult.HostEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sshPort < 1)
|
|
||||||
{
|
|
||||||
result |= SshConnectionInfoValidationResult.SshPortZeroOrNegative;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!await CheckIdentityFileExistsAsync().ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
result |= SshConnectionInfoValidationResult.IdentityFileDoesNotExist;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_useMosh)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_moshPortFrom < 1)
|
|
||||||
{
|
|
||||||
result |= SshConnectionInfoValidationResult.MoshPortZeroOrNegative;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_moshPortFrom > _moshPortTo)
|
|
||||||
{
|
|
||||||
result |= SshConnectionInfoValidationResult.MoshPortRangeInvalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task<bool> CheckIdentityFileExistsAsync()
|
|
||||||
{
|
|
||||||
var identityFile = _identityFile;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(identityFile) ||
|
|
||||||
identityFile.Equals(_validatedIdentityFile, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we need to take into account that files from ssh config dir can be provided by name, without full path.
|
|
||||||
string fullPath;
|
|
||||||
|
|
||||||
if (Path.IsPathRooted(identityFile))
|
|
||||||
{
|
|
||||||
fullPath = identityFile;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
var sshConfigDir = await _trayProcessCommunicationService.GetSshConfigDirAsync().ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(sshConfigDir))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fullPath = Path.Combine(sshConfigDir, identityFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
if (await _trayProcessCommunicationService.CheckFileExistsAsync(fullPath).ConfigureAwait(true))
|
|
||||||
{
|
|
||||||
_validatedIdentityFile = identityFile;
|
|
||||||
|
|
||||||
IdentityFile = fullPath;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetValidatedIdentityFile(string identityFile)
|
|
||||||
{
|
|
||||||
_validatedIdentityFile = identityFile;
|
|
||||||
IdentityFile = identityFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
|
|
||||||
#region Links/shortcuts related
|
|
||||||
|
|
||||||
// Resources:
|
|
||||||
// https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04#section-3
|
|
||||||
// https://man.openbsd.org/ssh
|
|
||||||
|
|
||||||
private const string SshUriScheme = "ssh";
|
|
||||||
private const string MoshUriScheme = "mosh";
|
|
||||||
|
|
||||||
// Constant derived from https://man.openbsd.org/ssh
|
|
||||||
private const string IdentityFileOptionName = "IdentityFile";
|
|
||||||
|
|
||||||
private static readonly string[] ValidMoshPortsNames = { "mosh_ports", "mosh-ports" };
|
|
||||||
|
|
||||||
private static readonly Regex MoshRangeRx =
|
|
||||||
new Regex(@"^(?<from>\d{1,5})[:-](?<to>\d{1,5})$", RegexOptions.Compiled);
|
|
||||||
|
|
||||||
public static bool CheckScheme(Uri uri) =>
|
|
||||||
SshUriScheme.Equals(uri?.Scheme, StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
MoshUriScheme.Equals(uri?.Scheme, StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
public static SshConnectViewModel ParseUri(Uri uri, ISettingsService settingsService,
|
|
||||||
IApplicationView applicationView, ITrayProcessCommunicationService trayProcessCommunicationService,
|
|
||||||
IFileSystemService fileSystemService, IApplicationDataContainer historyContainer)
|
|
||||||
{
|
|
||||||
var vm = new SshConnectViewModel(settingsService, applicationView, trayProcessCommunicationService,
|
|
||||||
fileSystemService)
|
|
||||||
{
|
|
||||||
Host = uri.Host,
|
|
||||||
UseMosh = MoshUriScheme.Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (uri.Port >= 0)
|
|
||||||
vm.SshPort = (ushort)uri.Port;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(uri.UserInfo))
|
|
||||||
{
|
|
||||||
string[] parts = uri.UserInfo.Split(';');
|
|
||||||
|
|
||||||
if (parts.Length > 2)
|
|
||||||
throw new FormatException($"UserInfo part contains {parts.Length} elements.");
|
|
||||||
|
|
||||||
vm.Username = HttpUtility.UrlDecode(parts[0]);
|
|
||||||
|
|
||||||
if (parts.Length > 1)
|
|
||||||
{
|
|
||||||
// For now we are only interested in IdentityFile option
|
|
||||||
Tuple<string, string> identityFileOption = ParseParams(parts[1], ',').FirstOrDefault(p =>
|
|
||||||
string.Equals(p.Item1, IdentityFileOptionName, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
vm.IdentityFile = identityFileOption?.Item2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
|
||||||
string queryString = uri.Query?.Trim();
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(queryString))
|
|
||||||
{
|
|
||||||
return vm;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryString.StartsWith("?", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
queryString = queryString.Substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var queryStringParams = ParseParams(queryString, '&').ToList();
|
|
||||||
|
|
||||||
var moshPorts = queryStringParams.FirstOrDefault(p =>
|
|
||||||
ValidMoshPortsNames.Any(n => string.Equals(p.Item1, n, StringComparison.OrdinalIgnoreCase)));
|
|
||||||
|
|
||||||
if (moshPorts != null)
|
|
||||||
{
|
|
||||||
Match match = MoshRangeRx.Match(moshPorts.Item2);
|
|
||||||
|
|
||||||
if (match.Success)
|
|
||||||
{
|
|
||||||
vm.MoshPortFrom = ushort.Parse(match.Groups["from"].Value);
|
|
||||||
vm.MoshPortTo = ushort.Parse(match.Groups["to"].Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.LoadBaseFromQueryString(queryStringParams);
|
|
||||||
|
|
||||||
return vm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<Tuple<bool, string>> GetUrlAsync()
|
|
||||||
{
|
|
||||||
var error = await base.ValidateAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(error))
|
|
||||||
{
|
|
||||||
return Tuple.Create(false, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await GetSshInfoValidationResultAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (result != SshConnectionInfoValidationResult.Valid &&
|
|
||||||
// For links we can ignore missing username
|
|
||||||
result != SshConnectionInfoValidationResult.UsernameEmpty)
|
|
||||||
{
|
|
||||||
|
|
||||||
error = GetErrorString(result, Environment.NewLine);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(error))
|
|
||||||
{
|
|
||||||
error = "Invalid input.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Tuple.Create(false, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
var sb = new StringBuilder(_useMosh ? MoshUriScheme : SshUriScheme);
|
|
||||||
|
|
||||||
sb.Append("://");
|
|
||||||
|
|
||||||
bool containsUserInfo = false;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_username))
|
|
||||||
{
|
|
||||||
sb.Append(HttpUtility.UrlEncode(_username));
|
|
||||||
|
|
||||||
containsUserInfo = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_identityFile))
|
|
||||||
{
|
|
||||||
sb.Append(";");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_identityFile))
|
|
||||||
{
|
|
||||||
sb.Append($"{IdentityFileOptionName}={HttpUtility.UrlEncode(_identityFile)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
containsUserInfo = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (containsUserInfo)
|
|
||||||
sb.Append("@");
|
|
||||||
|
|
||||||
sb.Append(_host);
|
|
||||||
|
|
||||||
if (_sshPort != SshProfile.DefaultSshPort)
|
|
||||||
{
|
|
||||||
sb.Append($":{_sshPort:#####}");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append("/");
|
|
||||||
|
|
||||||
bool queryStringAdded = false;
|
|
||||||
|
|
||||||
if (_useMosh)
|
|
||||||
{
|
|
||||||
sb.Append(
|
|
||||||
$"?{ValidMoshPortsNames[0]}={_moshPortFrom:#####}-{_moshPortTo:#####}");
|
|
||||||
|
|
||||||
queryStringAdded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var terminalInfoQueryString = GetBaseQueryString();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(terminalInfoQueryString))
|
|
||||||
{
|
|
||||||
sb.Append($"{(queryStringAdded ? "&" : "?")}{terminalInfoQueryString}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Tuple.Create(true, sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Links/shortcuts related
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Profiles
|
|
||||||
{
|
|
||||||
[Flags]
|
|
||||||
public enum SshConnectionInfoValidationResult
|
|
||||||
{
|
|
||||||
Valid = 0,
|
|
||||||
UsernameEmpty = 1,
|
|
||||||
HostEmpty = 2,
|
|
||||||
SshPortZeroOrNegative = 4,
|
|
||||||
MoshPortZeroOrNegative = 8,
|
|
||||||
MoshPortRangeInvalid = 16,
|
|
||||||
IdentityFileDoesNotExist = 32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +1,36 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
using GalaSoft.MvvmLight;
|
||||||
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class AboutPageViewModel : ObservableObject
|
public class AboutPageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private const string BaseUrl = "https://github.com/felixse/FluentTerminal/releases/tag/";
|
private const string BaseUrl = "https://github.com/felixse/FluentTerminal/releases/tag/";
|
||||||
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IUpdateService _updateService;
|
private readonly IUpdateService _updateService;
|
||||||
private string _latestVersion;
|
private string _latestVersion;
|
||||||
private readonly IApplicationView _applicationView;
|
private readonly IApplicationView _applicationView;
|
||||||
|
|
||||||
public AboutPageViewModel(IUpdateService updateService, IApplicationView applicationView)
|
public AboutPageViewModel(ISettingsService settingsService, IUpdateService updateService, IApplicationView applicationView)
|
||||||
{
|
{
|
||||||
|
_settingsService = settingsService;
|
||||||
_updateService = updateService;
|
_updateService = updateService;
|
||||||
_applicationView = applicationView;
|
_applicationView = applicationView;
|
||||||
|
|
||||||
CheckForUpdatesCommand = new AsyncRelayCommand(() => CheckForUpdateAsync(true));
|
CheckForUpdatesCommand = new AsyncCommand(() => CheckForUpdate(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommand CheckForUpdatesCommand { get; }
|
public IAsyncCommand CheckForUpdatesCommand { get; }
|
||||||
|
|
||||||
public string CurrentVersion
|
public string CurrentVersion
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var version = _updateService.GetCurrentVersion();
|
var version = _updateService.GetCurrentVersion();
|
||||||
return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
|
return ConvertVersionToString(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,12 +41,12 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _latestVersion;
|
get => _latestVersion;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _latestVersion, value))
|
if (Set(ref _latestVersion, value))
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(LatestVersionFound));
|
RaisePropertyChanged(nameof(LatestVersionFound));
|
||||||
OnPropertyChanged(nameof(LatestVersionLoading));
|
RaisePropertyChanged(nameof(LatestVersionLoading));
|
||||||
OnPropertyChanged(nameof(LatestVersionNotFound));
|
RaisePropertyChanged(nameof(LatestVersionNotFound));
|
||||||
OnPropertyChanged(nameof(LatestVersionReleaseNotesURL));
|
RaisePropertyChanged(nameof(LatestVersionReleaseNotesURL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,16 +61,19 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
|
|
||||||
public Task OnNavigatedTo()
|
public Task OnNavigatedTo()
|
||||||
{
|
{
|
||||||
return CheckForUpdateAsync(false);
|
return CheckForUpdate(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckForUpdateAsync(bool notifyNoUpdate)
|
public async Task CheckForUpdate(bool notifyNoUpdate)
|
||||||
{
|
{
|
||||||
var version = await _updateService.GetLatestVersionAsync().ConfigureAwait(false);
|
var version = ConvertVersionToString(await _updateService.GetLatestVersionAsync());
|
||||||
await _applicationView.ExecuteOnUiThreadAsync(() =>
|
await _applicationView.RunOnDispatcherThread(() => LatestVersion = version);
|
||||||
LatestVersion = $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}")
|
await _updateService.CheckForUpdate(notifyNoUpdate);
|
||||||
.ConfigureAwait(false);
|
}
|
||||||
await _updateService.CheckForUpdateAsync(notifyNoUpdate).ConfigureAwait(false);
|
|
||||||
|
private string ConvertVersionToString(Version version)
|
||||||
|
{
|
||||||
|
return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using GalaSoft.MvvmLight;
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
using GalaSoft.MvvmLight.Command;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class GeneralPageViewModel : ObservableObject
|
public class GeneralPageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly ApplicationSettings _applicationSettings;
|
private readonly ApplicationSettings _applicationSettings;
|
||||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
@@ -18,56 +15,57 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IStartupTaskService _startupTaskService;
|
private readonly IStartupTaskService _startupTaskService;
|
||||||
private bool _canEnableStartupTask;
|
private bool _canEnableStartupTask;
|
||||||
|
private bool _editingNewTerminalLocation;
|
||||||
|
private bool _editingTabsPosition;
|
||||||
|
private bool _editingInactiveTabColorMode;
|
||||||
private bool _startupTaskEnabled;
|
private bool _startupTaskEnabled;
|
||||||
private bool _shouldRestartForTrayMessage;
|
private bool _shouldRestartForTrayMessage;
|
||||||
private string _startupTaskErrorMessage;
|
private string _startupTaskErrorMessage;
|
||||||
private bool _needsToRestart;
|
|
||||||
private readonly IApplicationLanguageService _applicationLanguageService;
|
|
||||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
|
||||||
private readonly IFileSystemService _fileSystemService;
|
|
||||||
|
|
||||||
public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider,
|
public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IStartupTaskService startupTaskService)
|
||||||
IStartupTaskService startupTaskService, IApplicationLanguageService applicationLanguageService,
|
|
||||||
ITrayProcessCommunicationService trayProcessCommunicationService, IFileSystemService fileSystemService)
|
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
_defaultValueProvider = defaultValueProvider;
|
_defaultValueProvider = defaultValueProvider;
|
||||||
_startupTaskService = startupTaskService;
|
_startupTaskService = startupTaskService;
|
||||||
_applicationLanguageService = applicationLanguageService;
|
|
||||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
|
||||||
_fileSystemService = fileSystemService;
|
|
||||||
|
|
||||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||||
|
|
||||||
RestoreDefaultsCommand = new AsyncRelayCommand(RestoreDefaultsAsync);
|
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||||
BrowseLogDirectoryCommand = new AsyncRelayCommand(BrowseLogDirectoryAsync);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> Languages => _applicationLanguageService.Languages;
|
public async Task OnNavigatedTo()
|
||||||
|
|
||||||
public bool NeedsToRestart
|
|
||||||
{
|
{
|
||||||
get => _needsToRestart;
|
var startupTaskStatus = await _startupTaskService.GetStatus();
|
||||||
set => SetProperty(ref _needsToRestart, value);
|
SetStartupTaskPropertiesForStatus(startupTaskStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SelectedLanguage
|
public bool AlwaysShowTabs
|
||||||
{
|
{
|
||||||
get => _applicationLanguageService.GetCurrentLanguage();
|
get => _applicationSettings.AlwaysShowTabs;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_applicationLanguageService.SetLanguage(value);
|
if (_applicationSettings.AlwaysShowTabs != value)
|
||||||
NeedsToRestart = true;
|
{
|
||||||
|
_applicationSettings.AlwaysShowTabs = value;
|
||||||
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
|
RaisePropertyChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
public bool AlwaysUseWinPty
|
||||||
public async Task OnNavigatedToAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we want to execute SetStartupTaskPropertiesForStatus from the calling (UI) thread.
|
get => _applicationSettings.AlwaysUseWinPty;
|
||||||
var startupTaskStatus = await _startupTaskService.GetStatusAsync().ConfigureAwait(true);
|
set
|
||||||
SetStartupTaskPropertiesForStatus(startupTaskStatus);
|
{
|
||||||
|
if (_applicationSettings.AlwaysUseWinPty != value)
|
||||||
|
{
|
||||||
|
_applicationSettings.AlwaysUseWinPty = value;
|
||||||
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
|
RaisePropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowCustomTitleInTitlebar
|
public bool ShowCustomTitleInTitlebar
|
||||||
@@ -79,7 +77,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_applicationSettings.ShowCustomTitleInTitlebar = value;
|
_applicationSettings.ShowCustomTitleInTitlebar = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +91,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_applicationSettings.EnableTrayIcon = value;
|
_applicationSettings.EnableTrayIcon = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
|
|
||||||
// Toggle message telling user to restart the program
|
// Toggle message telling user to restart the program
|
||||||
ShouldRestartForTrayMessage = !ShouldRestartForTrayMessage;
|
ShouldRestartForTrayMessage = !ShouldRestartForTrayMessage;
|
||||||
@@ -104,7 +102,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public bool ShouldRestartForTrayMessage
|
public bool ShouldRestartForTrayMessage
|
||||||
{
|
{
|
||||||
get => _shouldRestartForTrayMessage;
|
get => _shouldRestartForTrayMessage;
|
||||||
set => SetProperty(ref _shouldRestartForTrayMessage, value);
|
set => Set(ref _shouldRestartForTrayMessage, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowNewOutputIndicator
|
public bool ShowNewOutputIndicator
|
||||||
@@ -116,120 +114,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_applicationSettings.ShowNewOutputIndicator = value;
|
_applicationSettings.ShowNewOutputIndicator = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UseMoshByDefault
|
|
||||||
{
|
|
||||||
get => _applicationSettings.UseMoshByDefault;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.UseMoshByDefault != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.UseMoshByDefault = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AutoFallbackToWindowsUsernameInLinks
|
|
||||||
{
|
|
||||||
get => _applicationSettings.AutoFallbackToWindowsUsernameInLinks;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.AutoFallbackToWindowsUsernameInLinks != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.AutoFallbackToWindowsUsernameInLinks = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RTrimCopiedLines
|
|
||||||
{
|
|
||||||
get => _applicationSettings.RTrimCopiedLines;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.RTrimCopiedLines != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.RTrimCopiedLines = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MuteTerminalBeeps
|
|
||||||
{
|
|
||||||
get => _applicationSettings.MuteTerminalBeeps;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.MuteTerminalBeeps != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.MuteTerminalBeeps = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
|
|
||||||
_trayProcessCommunicationService.MuteTerminalAsync(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool EnableLogging
|
|
||||||
{
|
|
||||||
get => _applicationSettings.EnableLogging;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.EnableLogging != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.EnableLogging = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool PrintableOutputOnly
|
|
||||||
{
|
|
||||||
get => _applicationSettings.PrintableOutputOnly;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.PrintableOutputOnly != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.PrintableOutputOnly = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LogDirectoryPath
|
|
||||||
{
|
|
||||||
get => _applicationSettings.LogDirectoryPath;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.LogDirectoryPath != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.LogDirectoryPath = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UseConPty
|
|
||||||
{
|
|
||||||
get => _applicationSettings.UseConPty;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.UseConPty != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.UseConPty = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,13 +122,13 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public bool BottomIsSelected
|
public bool BottomIsSelected
|
||||||
{
|
{
|
||||||
get => TabsPosition == TabsPosition.Bottom;
|
get => TabsPosition == TabsPosition.Bottom;
|
||||||
set { if (value) TabsPosition = TabsPosition.Bottom; }
|
set => TabsPosition = TabsPosition.Bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanEnableStartupTask
|
public bool CanEnableStartupTask
|
||||||
{
|
{
|
||||||
get => _canEnableStartupTask;
|
get => _canEnableStartupTask;
|
||||||
set => SetProperty(ref _canEnableStartupTask, value);
|
set => Set(ref _canEnableStartupTask, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ConfirmClosingTabs
|
public bool ConfirmClosingTabs
|
||||||
@@ -255,7 +140,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_applicationSettings.ConfirmClosingTabs = value;
|
_applicationSettings.ConfirmClosingTabs = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,7 +154,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_applicationSettings.ConfirmClosingWindows = value;
|
_applicationSettings.ConfirmClosingWindows = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,43 +164,31 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _applicationSettings.NewTerminalLocation;
|
get => _applicationSettings.NewTerminalLocation;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_applicationSettings.NewTerminalLocation != value)
|
if (_applicationSettings.NewTerminalLocation != value && !_editingNewTerminalLocation)
|
||||||
{
|
{
|
||||||
|
_editingNewTerminalLocation = true;
|
||||||
_applicationSettings.NewTerminalLocation = value;
|
_applicationSettings.NewTerminalLocation = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
OnPropertyChanged(nameof(TabIsSelected));
|
RaisePropertyChanged(nameof(WindowIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(TabIsSelected));
|
||||||
|
_editingNewTerminalLocation = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TabWindowCascadingAppMenu
|
public RelayCommand RestoreDefaultsCommand { get; }
|
||||||
{
|
|
||||||
get => _applicationSettings.TabWindowCascadingAppMenu;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_applicationSettings.TabWindowCascadingAppMenu != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.TabWindowCascadingAppMenu = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICommand RestoreDefaultsCommand { get; }
|
|
||||||
|
|
||||||
public ICommand BrowseLogDirectoryCommand { get; }
|
|
||||||
|
|
||||||
public bool StartupTaskEnabled
|
public bool StartupTaskEnabled
|
||||||
{
|
{
|
||||||
get => _startupTaskEnabled;
|
get => _startupTaskEnabled;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (SetProperty(ref _startupTaskEnabled, value))
|
if (_startupTaskEnabled != value)
|
||||||
{
|
{
|
||||||
// ReSharper disable once AssignmentIsFullyDiscarded
|
_startupTaskEnabled = value;
|
||||||
_ = SetStartupTaskStateAsync(value);
|
RaisePropertyChanged(nameof(StartupTaskEnabled));
|
||||||
|
SetStartupTaskState(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,13 +196,13 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public string StartupTaskErrorMessage
|
public string StartupTaskErrorMessage
|
||||||
{
|
{
|
||||||
get => _startupTaskErrorMessage;
|
get => _startupTaskErrorMessage;
|
||||||
set => SetProperty(ref _startupTaskErrorMessage, value);
|
set => Set(ref _startupTaskErrorMessage, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TabIsSelected
|
public bool TabIsSelected
|
||||||
{
|
{
|
||||||
get => NewTerminalLocation == NewTerminalLocation.Tab;
|
get => NewTerminalLocation == NewTerminalLocation.Tab;
|
||||||
set => NewTerminalLocation = value ? NewTerminalLocation.Tab : NewTerminalLocation.Window;
|
set => NewTerminalLocation = NewTerminalLocation.Tab;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TabsPosition TabsPosition
|
public TabsPosition TabsPosition
|
||||||
@@ -337,11 +210,15 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _applicationSettings.TabsPosition;
|
get => _applicationSettings.TabsPosition;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_applicationSettings.TabsPosition != value)
|
if (_applicationSettings.TabsPosition != value && !_editingTabsPosition)
|
||||||
{
|
{
|
||||||
|
_editingTabsPosition = true;
|
||||||
_applicationSettings.TabsPosition = value;
|
_applicationSettings.TabsPosition = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
|
RaisePropertyChanged(nameof(TopIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(BottomIsSelected));
|
||||||
|
_editingTabsPosition = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,7 +226,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public bool TopIsSelected
|
public bool TopIsSelected
|
||||||
{
|
{
|
||||||
get => TabsPosition == TabsPosition.Top;
|
get => TabsPosition == TabsPosition.Top;
|
||||||
set { if (value) TabsPosition = TabsPosition.Top; }
|
set => TabsPosition = TabsPosition.Top;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UnderlineSelectedTab
|
public bool UnderlineSelectedTab
|
||||||
@@ -361,7 +238,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_applicationSettings.UnderlineSelectedTab = value;
|
_applicationSettings.UnderlineSelectedTab = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,13 +246,13 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public bool BackgroundIsSelected
|
public bool BackgroundIsSelected
|
||||||
{
|
{
|
||||||
get => InactiveTabColorMode == InactiveTabColorMode.Background;
|
get => InactiveTabColorMode == InactiveTabColorMode.Background;
|
||||||
set { if (value) InactiveTabColorMode = InactiveTabColorMode.Background; }
|
set => InactiveTabColorMode = InactiveTabColorMode.Background;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UnderlinedIsSelected
|
public bool UnderlinedIsSelected
|
||||||
{
|
{
|
||||||
get => InactiveTabColorMode == InactiveTabColorMode.Underlined;
|
get => InactiveTabColorMode == InactiveTabColorMode.Underlined;
|
||||||
set { if (value) InactiveTabColorMode = InactiveTabColorMode.Underlined; }
|
set => InactiveTabColorMode = InactiveTabColorMode.Underlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InactiveTabColorMode InactiveTabColorMode
|
public InactiveTabColorMode InactiveTabColorMode
|
||||||
@@ -383,36 +260,28 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _applicationSettings.InactiveTabColorMode;
|
get => _applicationSettings.InactiveTabColorMode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_applicationSettings.InactiveTabColorMode != value)
|
if (_applicationSettings.InactiveTabColorMode != value && !_editingInactiveTabColorMode)
|
||||||
{
|
{
|
||||||
|
_editingInactiveTabColorMode = true;
|
||||||
_applicationSettings.InactiveTabColorMode = value;
|
_applicationSettings.InactiveTabColorMode = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
|
RaisePropertyChanged(nameof(BackgroundIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(UnderlinedIsSelected));
|
||||||
|
_editingInactiveTabColorMode = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowTextCopied
|
public bool WindowIsSelected
|
||||||
{
|
{
|
||||||
get => _applicationSettings.ShowTextCopied;
|
get => NewTerminalLocation == NewTerminalLocation.Window;
|
||||||
set
|
set => NewTerminalLocation = NewTerminalLocation.Window;
|
||||||
{
|
|
||||||
if (_applicationSettings.ShowTextCopied != value)
|
|
||||||
{
|
|
||||||
_applicationSettings.ShowTextCopied = value;
|
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
private async Task RestoreDefaults()
|
||||||
private async Task RestoreDefaultsAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards
|
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the general settings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||||
var result = await _dialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmRestoreGeneralSettings"), DialogButton.OK, DialogButton.Cancel)
|
|
||||||
.ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (result == DialogButton.OK)
|
if (result == DialogButton.OK)
|
||||||
{
|
{
|
||||||
@@ -422,22 +291,14 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
UnderlineSelectedTab = defaults.UnderlineSelectedTab;
|
UnderlineSelectedTab = defaults.UnderlineSelectedTab;
|
||||||
InactiveTabColorMode = defaults.InactiveTabColorMode;
|
InactiveTabColorMode = defaults.InactiveTabColorMode;
|
||||||
NewTerminalLocation = defaults.NewTerminalLocation;
|
NewTerminalLocation = defaults.NewTerminalLocation;
|
||||||
|
AlwaysShowTabs = defaults.AlwaysShowTabs;
|
||||||
|
AlwaysUseWinPty = defaults.AlwaysUseWinPty;
|
||||||
ShowNewOutputIndicator = defaults.ShowNewOutputIndicator;
|
ShowNewOutputIndicator = defaults.ShowNewOutputIndicator;
|
||||||
EnableTrayIcon = defaults.EnableTrayIcon;
|
EnableTrayIcon = defaults.EnableTrayIcon;
|
||||||
ShowCustomTitleInTitlebar = defaults.ShowCustomTitleInTitlebar;
|
ShowCustomTitleInTitlebar = defaults.ShowCustomTitleInTitlebar;
|
||||||
UseMoshByDefault = defaults.UseMoshByDefault;
|
|
||||||
AutoFallbackToWindowsUsernameInLinks = defaults.AutoFallbackToWindowsUsernameInLinks;
|
|
||||||
RTrimCopiedLines = defaults.RTrimCopiedLines;
|
|
||||||
MuteTerminalBeeps = defaults.MuteTerminalBeeps;
|
|
||||||
EnableLogging = defaults.EnableLogging;
|
|
||||||
PrintableOutputOnly = defaults.PrintableOutputOnly;
|
|
||||||
LogDirectoryPath = defaults.LogDirectoryPath;
|
|
||||||
UseConPty = defaults.UseConPty;
|
|
||||||
ShowTextCopied = defaults.ShowTextCopied;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private void SetStartupTaskPropertiesForStatus(StartupTaskStatus startupTaskStatus)
|
private void SetStartupTaskPropertiesForStatus(StartupTaskStatus startupTaskStatus)
|
||||||
{
|
{
|
||||||
switch (startupTaskStatus)
|
switch (startupTaskStatus)
|
||||||
@@ -456,53 +317,31 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
|
|
||||||
case StartupTaskStatus.DisabledByUser:
|
case StartupTaskStatus.DisabledByUser:
|
||||||
StartupTaskEnabled = false;
|
StartupTaskEnabled = false;
|
||||||
StartupTaskErrorMessage = I18N.Translate("DisabledByUser");
|
StartupTaskErrorMessage = "Disabled by user. Please reactivate it in the Startup tab of the Task Manager.";
|
||||||
CanEnableStartupTask = false;
|
CanEnableStartupTask = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StartupTaskStatus.DisabledByPolicy:
|
case StartupTaskStatus.DisabledByPolicy:
|
||||||
StartupTaskEnabled = false;
|
StartupTaskEnabled = false;
|
||||||
StartupTaskErrorMessage = I18N.Translate("DisabledByPolicy");
|
StartupTaskErrorMessage = "Disabled by policy.";
|
||||||
CanEnableStartupTask = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StartupTaskStatus.EnabledByPolicy:
|
|
||||||
StartupTaskEnabled = true;
|
|
||||||
StartupTaskErrorMessage = I18N.TranslateWithFallback("EnabledByPolicy", "Enabled by policy.");
|
|
||||||
CanEnableStartupTask = false;
|
CanEnableStartupTask = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
private async Task SetStartupTaskState(bool enabled)
|
||||||
private async Task SetStartupTaskStateAsync(bool enabled)
|
|
||||||
{
|
{
|
||||||
StartupTaskStatus status;
|
StartupTaskStatus status;
|
||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we need to execute SetStartupTaskPropertiesForStatus in the calling (UI) thread.
|
status = await _startupTaskService.EnableStartupTask();
|
||||||
status = await _startupTaskService.EnableStartupTaskAsync().ConfigureAwait(true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we need to execute SetStartupTaskPropertiesForStatus in the calling (UI) thread.
|
_startupTaskService.DisableStartupTask();
|
||||||
await _startupTaskService.DisableStartupTaskAsync().ConfigureAwait(true);
|
status = await _startupTaskService.GetStatus();
|
||||||
// ConfigureAwait(true) because we need to execute SetStartupTaskPropertiesForStatus in the calling (UI) thread.
|
|
||||||
status = await _startupTaskService.GetStatusAsync().ConfigureAwait(true);
|
|
||||||
}
|
}
|
||||||
SetStartupTaskPropertiesForStatus(status);
|
SetStartupTaskPropertiesForStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task BrowseLogDirectoryAsync()
|
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
var folder = await _fileSystemService.BrowseForDirectoryAsync().ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (folder != null)
|
|
||||||
{
|
|
||||||
LogDirectoryPath = folder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using GalaSoft.MvvmLight;
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class KeyBindingViewModel : ObservableObject
|
public class KeyBindingViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
|
|
||||||
@@ -18,11 +16,10 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
Model = keyBinding;
|
Model = keyBinding;
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
EditCommand = new AsyncRelayCommand(EditAsync);
|
EditCommand = new AsyncCommand(Edit);
|
||||||
DeleteCommand = new AsyncRelayCommand(DeleteAsync);
|
DeleteCommand = new AsyncCommand(Delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs to be triggered from the UI thread
|
|
||||||
public event EventHandler Deleted;
|
public event EventHandler Deleted;
|
||||||
|
|
||||||
public event EventHandler Edited;
|
public event EventHandler Edited;
|
||||||
@@ -37,7 +34,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
if (Model.Meta != value)
|
if (Model.Meta != value)
|
||||||
{
|
{
|
||||||
Model.Meta = value;
|
Model.Meta = value;
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +47,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
if (Model.Alt != value)
|
if (Model.Alt != value)
|
||||||
{
|
{
|
||||||
Model.Alt = value;
|
Model.Alt = value;
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,13 +60,13 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
if (Model.Ctrl != value)
|
if (Model.Ctrl != value)
|
||||||
{
|
{
|
||||||
Model.Ctrl = value;
|
Model.Ctrl = value;
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommand DeleteCommand { get; }
|
public IAsyncCommand DeleteCommand { get; }
|
||||||
public ICommand EditCommand { get; }
|
public IAsyncCommand EditCommand { get; }
|
||||||
|
|
||||||
public int Key
|
public int Key
|
||||||
{
|
{
|
||||||
@@ -79,7 +76,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
if (Model.Key != value)
|
if (Model.Key != value)
|
||||||
{
|
{
|
||||||
Model.Key = value;
|
Model.Key = value;
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,15 +91,13 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
if (Model.Shift != value)
|
if (Model.Shift != value)
|
||||||
{
|
{
|
||||||
Model.Shift = value;
|
Model.Shift = value;
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
public async Task<bool> Edit()
|
||||||
public async Task<bool> EditAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
|
||||||
var keyBinding = await _dialogService.ShowCreateKeyBindingDialog().ConfigureAwait(true);
|
var keyBinding = await _dialogService.ShowCreateKeyBindingDialog().ConfigureAwait(true);
|
||||||
|
|
||||||
if (keyBinding != null)
|
if (keyBinding != null)
|
||||||
@@ -121,12 +116,9 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
private async Task Delete()
|
||||||
private async Task DeleteAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we need to trigger Deleted event in the calling (UI) thread.
|
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this keybinding?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||||
var result = await _dialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmDeleteKeybindings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (result == DialogButton.OK)
|
if (result == DialogButton.OK)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,44 +2,46 @@
|
|||||||
using FluentTerminal.App.Services.Utilities;
|
using FluentTerminal.App.Services.Utilities;
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using GalaSoft.MvvmLight;
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
using GalaSoft.MvvmLight.Command;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class KeyBindingsPageViewModel : ObservableObject
|
public class KeyBindingsPageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
|
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||||
|
private IDictionary<string, ICollection<KeyBinding>> _keyBindings;
|
||||||
|
|
||||||
public KeyBindingsPageViewModel(ISettingsService settingsService, IDialogService dialogService, ITrayProcessCommunicationService trayProcessCommunicationService)
|
public KeyBindingsPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ITrayProcessCommunicationService trayProcessCommunicationService)
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
|
_defaultValueProvider = defaultValueProvider;
|
||||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||||
RestoreDefaultsCommand = new AsyncRelayCommand(RestoreDefaultsAsync);
|
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||||
AddCommand = new AsyncRelayCommand<string>(AddAsync);
|
AddCommand = new RelayCommand<string>(async command => await Add(command).ConfigureAwait(false));
|
||||||
|
|
||||||
Initialize(_settingsService.GetCommandKeyBindings());
|
Initialize(_settingsService.GetCommandKeyBindings());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommand AddCommand { get; }
|
public RelayCommand<string> AddCommand { get; }
|
||||||
public ObservableCollection<KeyBindingsViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingsViewModel>();
|
public ObservableCollection<KeyBindingsViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingsViewModel>();
|
||||||
public ICommand RestoreDefaultsCommand { get; }
|
public RelayCommand RestoreDefaultsCommand { get; }
|
||||||
|
|
||||||
private Task AddAsync(string command)
|
private async Task Add(string command)
|
||||||
{
|
{
|
||||||
return KeyBindings.First(k => k.Command == command).ShowAddKeyBindingDialogAsync();
|
var keyBinding = KeyBindings.FirstOrDefault(k => k.Command == command);
|
||||||
|
await keyBinding?.ShowAddKeyBindingDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private void ClearKeyBindings()
|
private void ClearKeyBindings()
|
||||||
{
|
{
|
||||||
foreach (var keyBinding in KeyBindings)
|
foreach (var keyBinding in KeyBindings)
|
||||||
@@ -50,16 +52,17 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
KeyBindings.Clear();
|
KeyBindings.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private void Initialize(IDictionary<string, ICollection<KeyBinding>> keyBindings)
|
private void Initialize(IDictionary<string, ICollection<KeyBinding>> keyBindings)
|
||||||
{
|
{
|
||||||
|
_keyBindings = keyBindings;
|
||||||
|
|
||||||
ClearKeyBindings();
|
ClearKeyBindings();
|
||||||
|
|
||||||
foreach (var value in Enum.GetValues(typeof(Command)))
|
foreach (var value in Enum.GetValues(typeof(Command)))
|
||||||
{
|
{
|
||||||
var command = (Command)value;
|
var command = (Command)value;
|
||||||
var viewModel = new KeyBindingsViewModel(command.ToString(), _dialogService, I18N.Translate($"{nameof(Command)}.{command}"), true);
|
var viewModel = new KeyBindingsViewModel(command.ToString(), _dialogService, EnumHelper.GetEnumDescription(command), true);
|
||||||
foreach (var keyBinding in keyBindings[command.ToString()])
|
foreach (var keyBinding in _keyBindings[command.ToString()])
|
||||||
{
|
{
|
||||||
viewModel.Add(keyBinding);
|
viewModel.Add(keyBinding);
|
||||||
}
|
}
|
||||||
@@ -71,15 +74,12 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
private void OnEdited(string command, ICollection<KeyBinding> keyBindings)
|
private void OnEdited(string command, ICollection<KeyBinding> keyBindings)
|
||||||
{
|
{
|
||||||
_settingsService.SaveKeyBindings(command, keyBindings);
|
_settingsService.SaveKeyBindings(command, keyBindings);
|
||||||
_trayProcessCommunicationService.UpdateToggleWindowKeyBindingsAsync();
|
_trayProcessCommunicationService.UpdateToggleWindowKeyBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
private async Task RestoreDefaults()
|
||||||
private async Task RestoreDefaultsAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we need to execute Initialize in the calling (UI) thread.
|
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the default keybindings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||||
var result = await _dialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmRestoreKeybindings"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (result == DialogButton.OK)
|
if (result == DialogButton.OK)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using GalaSoft.MvvmLight;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class KeyBindingsViewModel : ObservableObject
|
public class KeyBindingsViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private bool _editable;
|
private bool _editable;
|
||||||
@@ -37,30 +37,26 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public bool Editable
|
public bool Editable
|
||||||
{
|
{
|
||||||
get => _editable;
|
get => _editable;
|
||||||
set => SetProperty(ref _editable, value);
|
set => Set(ref _editable, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<KeyBindingViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingViewModel>();
|
public ObservableCollection<KeyBindingViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingViewModel>();
|
||||||
|
|
||||||
// Requires UI thread
|
public async Task ShowAddKeyBindingDialog()
|
||||||
public async Task ShowAddKeyBindingDialogAsync()
|
|
||||||
{
|
{
|
||||||
var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService, this);
|
var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService, this);
|
||||||
|
|
||||||
// ConfigureAwait(true) because we need to execute Add method in the calling (UI) thread.
|
if (await newKeyBinding.Edit().ConfigureAwait(true))
|
||||||
if (await newKeyBinding.EditAsync().ConfigureAwait(true))
|
|
||||||
{
|
{
|
||||||
Add(newKeyBinding.Model);
|
Add(newKeyBinding.Model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
KeyBindings.Clear();
|
KeyBindings.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
public void Add(KeyBinding keyBinding)
|
public void Add(KeyBinding keyBinding)
|
||||||
{
|
{
|
||||||
var viewModel = new KeyBindingViewModel(keyBinding, _dialogService, this);
|
var viewModel = new KeyBindingViewModel(keyBinding, _dialogService, this);
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using GalaSoft.MvvmLight;
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
using GalaSoft.MvvmLight.Command;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class MousePageViewModel : ObservableObject
|
public class MousePageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
private readonly ApplicationSettings _applicationSettings;
|
private readonly ApplicationSettings _applicationSettings;
|
||||||
|
private bool _editingMouseRightClickAction;
|
||||||
|
private bool _editingMouseMiddleClickAction;
|
||||||
|
|
||||||
public MousePageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
|
public MousePageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
|
||||||
{
|
{
|
||||||
@@ -23,7 +26,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
_defaultValueProvider = defaultValueProvider;
|
_defaultValueProvider = defaultValueProvider;
|
||||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||||
|
|
||||||
RestoreDefaultsCommand = new AsyncRelayCommand(RestoreDefaultsAsync);
|
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CopyOnSelect
|
public bool CopyOnSelect
|
||||||
@@ -35,7 +38,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_applicationSettings.CopyOnSelect = value;
|
_applicationSettings.CopyOnSelect = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,11 +48,16 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _applicationSettings.MouseRightClickAction;
|
get => _applicationSettings.MouseRightClickAction;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_applicationSettings.MouseRightClickAction != value)
|
if (_applicationSettings.MouseRightClickAction != value && !_editingMouseRightClickAction)
|
||||||
{
|
{
|
||||||
|
_editingMouseRightClickAction = true;
|
||||||
_applicationSettings.MouseRightClickAction = value;
|
_applicationSettings.MouseRightClickAction = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
|
RaisePropertyChanged(nameof(MouseRightClickNoneIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(MouseRightClickContextMenuIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(MouseRightClickPasteIsSelected));
|
||||||
|
_editingMouseRightClickAction = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,25 +65,19 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public bool MouseRightClickNoneIsSelected
|
public bool MouseRightClickNoneIsSelected
|
||||||
{
|
{
|
||||||
get => MouseRightClickAction == MouseAction.None;
|
get => MouseRightClickAction == MouseAction.None;
|
||||||
set { if (value) MouseRightClickAction = MouseAction.None; }
|
set => MouseRightClickAction = MouseAction.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MouseRightClickContextMenuIsSelected
|
public bool MouseRightClickContextMenuIsSelected
|
||||||
{
|
{
|
||||||
get => MouseRightClickAction == MouseAction.ContextMenu;
|
get => MouseRightClickAction == MouseAction.ContextMenu;
|
||||||
set { if (value) MouseRightClickAction = MouseAction.ContextMenu; }
|
set => MouseRightClickAction = MouseAction.ContextMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MouseRightClickPasteIsSelected
|
public bool MouseRightClickPasteIsSelected
|
||||||
{
|
{
|
||||||
get => MouseRightClickAction == MouseAction.Paste;
|
get => MouseRightClickAction == MouseAction.Paste;
|
||||||
set { if (value) MouseRightClickAction = MouseAction.Paste; }
|
set => MouseRightClickAction = MouseAction.Paste;
|
||||||
}
|
|
||||||
|
|
||||||
public bool MouseRightClickCopySelectionOrPasteIsSelected
|
|
||||||
{
|
|
||||||
get => MouseRightClickAction == MouseAction.CopySelectionOrPaste;
|
|
||||||
set { if (value) MouseRightClickAction = MouseAction.CopySelectionOrPaste; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MouseAction MouseMiddleClickAction
|
public MouseAction MouseMiddleClickAction
|
||||||
@@ -83,11 +85,16 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _applicationSettings.MouseMiddleClickAction;
|
get => _applicationSettings.MouseMiddleClickAction;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_applicationSettings.MouseMiddleClickAction != value)
|
if (_applicationSettings.MouseMiddleClickAction != value && !_editingMouseMiddleClickAction)
|
||||||
{
|
{
|
||||||
|
_editingMouseMiddleClickAction = true;
|
||||||
_applicationSettings.MouseMiddleClickAction = value;
|
_applicationSettings.MouseMiddleClickAction = value;
|
||||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
|
RaisePropertyChanged(nameof(MouseMiddleClickNoneIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(MouseMiddleClickContextMenuIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(MouseMiddleClickPasteIsSelected));
|
||||||
|
_editingMouseMiddleClickAction = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,36 +102,26 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public bool MouseMiddleClickNoneIsSelected
|
public bool MouseMiddleClickNoneIsSelected
|
||||||
{
|
{
|
||||||
get => MouseMiddleClickAction == MouseAction.None;
|
get => MouseMiddleClickAction == MouseAction.None;
|
||||||
set { if (value) MouseMiddleClickAction = MouseAction.None; }
|
set => MouseMiddleClickAction = MouseAction.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MouseMiddleClickContextMenuIsSelected
|
public bool MouseMiddleClickContextMenuIsSelected
|
||||||
{
|
{
|
||||||
get => MouseMiddleClickAction == MouseAction.ContextMenu;
|
get => MouseMiddleClickAction == MouseAction.ContextMenu;
|
||||||
set { if (value) MouseMiddleClickAction = MouseAction.ContextMenu; }
|
set => MouseMiddleClickAction = MouseAction.ContextMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MouseMiddleClickPasteIsSelected
|
public bool MouseMiddleClickPasteIsSelected
|
||||||
{
|
{
|
||||||
get => MouseMiddleClickAction == MouseAction.Paste;
|
get => MouseMiddleClickAction == MouseAction.Paste;
|
||||||
set { if (value) MouseMiddleClickAction = MouseAction.Paste; }
|
set => MouseMiddleClickAction = MouseAction.Paste;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MouseMiddleClickCopySelectionOrPasteIsSelected
|
public RelayCommand RestoreDefaultsCommand { get; }
|
||||||
{
|
|
||||||
get => MouseMiddleClickAction == MouseAction.CopySelectionOrPaste;
|
|
||||||
set { if (value) MouseMiddleClickAction = MouseAction.CopySelectionOrPaste; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICommand RestoreDefaultsCommand { get; }
|
private async Task RestoreDefaults()
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task RestoreDefaultsAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the mouse settings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||||
var result = await _dialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmRestoreMouseSettings"), DialogButton.OK, DialogButton.Cancel)
|
|
||||||
.ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (result == DialogButton.OK)
|
if (result == DialogButton.OK)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
|
using GalaSoft.MvvmLight;
|
||||||
|
using GalaSoft.MvvmLight.Command;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class ProfilesPageViewModel : ObservableObject
|
public class ProfilesPageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
@@ -80,9 +79,9 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommand CreateShellProfileCommand { get; }
|
public RelayCommand CreateShellProfileCommand { get; }
|
||||||
|
|
||||||
public ICommand CloneCommand { get; }
|
public RelayCommand<ShellProfileViewModel> CloneCommand { get; }
|
||||||
|
|
||||||
private void CreateShellProfile()
|
private void CreateShellProfile()
|
||||||
{
|
{
|
||||||
@@ -91,8 +90,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
PreInstalled = false,
|
PreInstalled = false,
|
||||||
Name = "New profile",
|
Name = "New profile",
|
||||||
KeyBindings = new List<KeyBinding>(),
|
KeyBindings = new List<KeyBinding>()
|
||||||
UseConPty = _settingsService.GetApplicationSettings().UseConPty
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AddShellProfile(shellProfile);
|
AddShellProfile(shellProfile);
|
||||||
@@ -100,18 +98,20 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
|
|
||||||
private void Clone(ShellProfileViewModel shellProfile)
|
private void Clone(ShellProfileViewModel shellProfile)
|
||||||
{
|
{
|
||||||
var cloned = shellProfile.ProfileVm.Model.Clone();
|
var cloned = new ShellProfile(shellProfile.Model)
|
||||||
|
{
|
||||||
cloned.Id = Guid.NewGuid();
|
Id = Guid.NewGuid(),
|
||||||
cloned.PreInstalled = false;
|
PreInstalled = false,
|
||||||
cloned.Name = $"Copy of {shellProfile.Name}";
|
Name = $"Copy of {shellProfile.Name}"
|
||||||
cloned.KeyBindings = new List<KeyBinding>();
|
};
|
||||||
|
|
||||||
AddShellProfile(cloned);
|
AddShellProfile(cloned);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddShellProfile(ShellProfile shellProfile)
|
private void AddShellProfile(ShellProfile shellProfile)
|
||||||
{
|
{
|
||||||
|
_settingsService.SaveShellProfile(shellProfile, true);
|
||||||
|
|
||||||
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService, _applicationView, _defaultValueProvider, true);
|
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService, _applicationView, _defaultValueProvider, true);
|
||||||
viewModel.EditCommand.Execute(null);
|
viewModel.EditCommand.Execute(null);
|
||||||
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
||||||
@@ -123,7 +123,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
public ShellProfileViewModel SelectedShellProfile
|
public ShellProfileViewModel SelectedShellProfile
|
||||||
{
|
{
|
||||||
get => _selectedShellProfile;
|
get => _selectedShellProfile;
|
||||||
set => SetProperty(ref _selectedShellProfile, value);
|
set => Set(ref _selectedShellProfile, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<ShellProfileViewModel> ShellProfiles { get; } = new ObservableCollection<ShellProfileViewModel>();
|
public ObservableCollection<ShellProfileViewModel> ShellProfiles { get; } = new ObservableCollection<ShellProfileViewModel>();
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
using FluentTerminal.App.Services;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
|
||||||
{
|
|
||||||
public class SshProfilesPageViewModel: ObservableObject
|
|
||||||
{
|
|
||||||
private readonly IDialogService _dialogService;
|
|
||||||
private readonly ISettingsService _settingsService;
|
|
||||||
private readonly IFileSystemService _fileSystemService;
|
|
||||||
private SshProfileViewModel _selectedShellProfile;
|
|
||||||
private readonly IApplicationView _applicationView;
|
|
||||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
|
||||||
private readonly IApplicationDataContainer _historyContainer;
|
|
||||||
|
|
||||||
public SshProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService,
|
|
||||||
IFileSystemService fileSystemService, IApplicationView applicationView,
|
|
||||||
ITrayProcessCommunicationService trayProcessCommunicationService,
|
|
||||||
IApplicationDataContainer historyContainer)
|
|
||||||
{
|
|
||||||
_settingsService = settingsService;
|
|
||||||
_dialogService = dialogService;
|
|
||||||
_fileSystemService = fileSystemService;
|
|
||||||
_applicationView = applicationView;
|
|
||||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
|
||||||
_historyContainer = historyContainer;
|
|
||||||
|
|
||||||
CreateSshProfileCommand = new RelayCommand(CreateSshProfile);
|
|
||||||
CloneCommand = new RelayCommand<SshProfileViewModel>(Clone);
|
|
||||||
|
|
||||||
foreach (var sshProfile in _settingsService.GetSshProfiles())
|
|
||||||
{
|
|
||||||
var viewModel = new SshProfileViewModel(sshProfile, settingsService, dialogService, fileSystemService,
|
|
||||||
applicationView, _trayProcessCommunicationService, historyContainer, false);
|
|
||||||
viewModel.Deleted += OnSshProfileDeleted;
|
|
||||||
SshProfiles.Add(viewModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SshProfiles.Count == 0)
|
|
||||||
{
|
|
||||||
CreateSshProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectedSshProfile = SshProfiles.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSshProfileDeleted(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is SshProfileViewModel sshProfile)
|
|
||||||
{
|
|
||||||
if (SelectedSshProfile == sshProfile)
|
|
||||||
{
|
|
||||||
SelectedSshProfile = SshProfiles.First();
|
|
||||||
}
|
|
||||||
SshProfiles.Remove(sshProfile);
|
|
||||||
_settingsService.DeleteSshProfile(sshProfile.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICommand CreateSshProfileCommand { get; }
|
|
||||||
|
|
||||||
public ICommand CloneCommand { get; }
|
|
||||||
|
|
||||||
public void CreateSshProfile()
|
|
||||||
{
|
|
||||||
var shellProfile = new SshProfile
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
PreInstalled = false,
|
|
||||||
Name = "New SSH profile",
|
|
||||||
KeyBindings = new List<KeyBinding>(),
|
|
||||||
UseMosh = _settingsService.GetApplicationSettings().UseMoshByDefault,
|
|
||||||
RequestConPty = _settingsService.GetApplicationSettings().UseConPty
|
|
||||||
};
|
|
||||||
|
|
||||||
AddSshProfile(shellProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Clone(SshProfileViewModel shellProfile)
|
|
||||||
{
|
|
||||||
var cloned = (SshProfile) shellProfile.ProfileVm.Model.Clone();
|
|
||||||
|
|
||||||
cloned.Id = Guid.NewGuid();
|
|
||||||
cloned.PreInstalled = false;
|
|
||||||
cloned.Name = $"Copy of {shellProfile.Name}";
|
|
||||||
cloned.KeyBindings = new List<KeyBinding>();
|
|
||||||
|
|
||||||
foreach (KeyBinding keyBinding in cloned.KeyBindings)
|
|
||||||
{
|
|
||||||
keyBinding.Command = cloned.Id.ToString();
|
|
||||||
}
|
|
||||||
AddSshProfile(cloned);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddSshProfile(SshProfile sshProfile)
|
|
||||||
{
|
|
||||||
var viewModel = new SshProfileViewModel(sshProfile, _settingsService, _dialogService, _fileSystemService,
|
|
||||||
_applicationView, _trayProcessCommunicationService, _historyContainer, true);
|
|
||||||
|
|
||||||
viewModel.EditCommand.Execute(null);
|
|
||||||
viewModel.Deleted += OnSshProfileDeleted;
|
|
||||||
SshProfiles.Add(viewModel);
|
|
||||||
SelectedSshProfile = viewModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SshProfileViewModel SelectedSshProfile
|
|
||||||
{
|
|
||||||
get => _selectedShellProfile;
|
|
||||||
set => SetProperty(ref _selectedShellProfile, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableCollection<SshProfileViewModel> SshProfiles { get; } = new ObservableCollection<SshProfileViewModel>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +1,57 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.App.ViewModels.Utilities;
|
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
using FluentTerminal.Models.Enums;
|
using FluentTerminal.Models.Enums;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using GalaSoft.MvvmLight;
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
using GalaSoft.MvvmLight.Command;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class TerminalPageViewModel : ObservableObject
|
public class TerminalPageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly TerminalOptions _terminalOptions;
|
private readonly TerminalOptions _terminalOptions;
|
||||||
private readonly ISettingsService _settingsService;
|
private readonly ISettingsService _settingsService;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
|
private bool _isEditingCursorStyle;
|
||||||
|
private bool _isEditingScrollBarStyle;
|
||||||
|
|
||||||
public bool BarIsSelected
|
public bool BarIsSelected
|
||||||
{
|
{
|
||||||
get => CursorStyle == CursorStyle.Bar;
|
get => CursorStyle == CursorStyle.Bar;
|
||||||
set { if (value) CursorStyle = CursorStyle.Bar; }
|
set => CursorStyle = CursorStyle.Bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool BlockIsSelected
|
public bool BlockIsSelected
|
||||||
{
|
{
|
||||||
get => CursorStyle == CursorStyle.Block;
|
get => CursorStyle == CursorStyle.Block;
|
||||||
set { if (value) CursorStyle = CursorStyle.Block; }
|
set => CursorStyle = CursorStyle.Block;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool UnderlineIsSelected
|
public bool UnderlineIsSelected
|
||||||
{
|
{
|
||||||
get => CursorStyle == CursorStyle.Underline;
|
get => CursorStyle == CursorStyle.Underline;
|
||||||
set { if (value) CursorStyle = CursorStyle.Underline; }
|
set => CursorStyle = CursorStyle.Underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HiddenIsSelected
|
public bool HiddenIsSelected
|
||||||
{
|
{
|
||||||
get => ScrollBarStyle == ScrollBarStyle.Hidden;
|
get => ScrollBarStyle == ScrollBarStyle.Hidden;
|
||||||
set { if (value) ScrollBarStyle = ScrollBarStyle.Hidden; }
|
set => ScrollBarStyle = ScrollBarStyle.Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AutoHidingIsSelected
|
public bool AutoHidingIsSelected
|
||||||
{
|
{
|
||||||
get => ScrollBarStyle == ScrollBarStyle.AutoHiding;
|
get => ScrollBarStyle == ScrollBarStyle.AutoHiding;
|
||||||
set { if (value) ScrollBarStyle = ScrollBarStyle.AutoHiding; }
|
set => ScrollBarStyle = ScrollBarStyle.AutoHiding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool VisibleIsSelected
|
public bool VisibleIsSelected
|
||||||
{
|
{
|
||||||
get => ScrollBarStyle == ScrollBarStyle.Visible;
|
get => ScrollBarStyle == ScrollBarStyle.Visible;
|
||||||
set { if (value) ScrollBarStyle = ScrollBarStyle.Visible; }
|
set => ScrollBarStyle = ScrollBarStyle.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CursorBlink
|
public bool CursorBlink
|
||||||
@@ -64,21 +63,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_terminalOptions.CursorBlink = value;
|
_terminalOptions.CursorBlink = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UseAcrylicBackground
|
|
||||||
{
|
|
||||||
get => _terminalOptions.UseAcrylicBackground;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_terminalOptions.UseAcrylicBackground != value)
|
|
||||||
{
|
|
||||||
_terminalOptions.UseAcrylicBackground = value;
|
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,21 +77,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_terminalOptions.FontFamily = value;
|
_terminalOptions.FontFamily = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int FontWeight
|
|
||||||
{
|
|
||||||
get => _terminalOptions.FontWeight;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_terminalOptions.FontWeight != value)
|
|
||||||
{
|
|
||||||
_terminalOptions.FontWeight = value;
|
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +91,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_terminalOptions.BackgroundOpacity = value;
|
_terminalOptions.BackgroundOpacity = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +105,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_terminalOptions.Padding = value;
|
_terminalOptions.Padding = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,6 +115,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _terminalOptions.ScrollBackLimit.ToString();
|
get => _terminalOptions.ScrollBackLimit.ToString();
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
var oldValue = _terminalOptions.ScrollBackLimit;
|
||||||
if (uint.TryParse(value, out uint intValue))
|
if (uint.TryParse(value, out uint intValue))
|
||||||
{
|
{
|
||||||
if (intValue == uint.MinValue)
|
if (intValue == uint.MinValue)
|
||||||
@@ -154,27 +126,26 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_terminalOptions.ScrollBackLimit = intValue;
|
_terminalOptions.ScrollBackLimit = intValue;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string WordSeparator
|
public bool BoldText
|
||||||
{
|
{
|
||||||
get => WebViewSpecialCharEncoder.Decode(_terminalOptions.WordSeparator);
|
get => _terminalOptions.BoldText;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
value = WebViewSpecialCharEncoder.Encode(value);
|
if (_terminalOptions.BoldText != value)
|
||||||
if (_terminalOptions.WordSeparator != value)
|
|
||||||
{
|
{
|
||||||
_terminalOptions.WordSeparator = value;
|
_terminalOptions.BoldText = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,25 +161,30 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_terminalOptions.FontSize = value;
|
_terminalOptions.FontSize = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<int> Sizes { get; }
|
public IEnumerable<int> Sizes { get; }
|
||||||
|
|
||||||
public ICommand RestoreDefaultsCommand { get; }
|
public RelayCommand RestoreDefaultsCommand { get; }
|
||||||
|
|
||||||
private CursorStyle CursorStyle
|
private CursorStyle CursorStyle
|
||||||
{
|
{
|
||||||
get => _terminalOptions.CursorStyle;
|
get => _terminalOptions.CursorStyle;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_terminalOptions.CursorStyle != value)
|
if (_terminalOptions.CursorStyle != value && !_isEditingCursorStyle)
|
||||||
{
|
{
|
||||||
|
_isEditingCursorStyle = true;
|
||||||
_terminalOptions.CursorStyle = value;
|
_terminalOptions.CursorStyle = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
|
RaisePropertyChanged(nameof(BlockIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(BarIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||||
|
_isEditingCursorStyle = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,22 +194,23 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
get => _terminalOptions.ScrollBarStyle;
|
get => _terminalOptions.ScrollBarStyle;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_terminalOptions.ScrollBarStyle != value)
|
if (_terminalOptions.ScrollBarStyle != value && !_isEditingScrollBarStyle)
|
||||||
{
|
{
|
||||||
|
_isEditingScrollBarStyle = true;
|
||||||
_terminalOptions.ScrollBarStyle = value;
|
_terminalOptions.ScrollBarStyle = value;
|
||||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||||
OnPropertyChanged();
|
RaisePropertyChanged();
|
||||||
|
RaisePropertyChanged(nameof(HiddenIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(AutoHidingIsSelected));
|
||||||
|
RaisePropertyChanged(nameof(VisibleIsSelected));
|
||||||
|
_isEditingScrollBarStyle = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
private async Task RestoreDefaults()
|
||||||
private async Task RestoreDefaultsAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we're setting some view-model properties afterwards.
|
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the default terminal options?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||||
var result = await _dialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmRestoreTerminalOptions"), DialogButton.OK, DialogButton.Cancel)
|
|
||||||
.ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (result == DialogButton.OK)
|
if (result == DialogButton.OK)
|
||||||
{
|
{
|
||||||
@@ -243,10 +220,9 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
ScrollBarStyle = defaults.ScrollBarStyle;
|
ScrollBarStyle = defaults.ScrollBarStyle;
|
||||||
FontFamily = defaults.FontFamily;
|
FontFamily = defaults.FontFamily;
|
||||||
FontSize = defaults.FontSize;
|
FontSize = defaults.FontSize;
|
||||||
FontWeight = defaults.FontWeight;
|
BoldText = defaults.BoldText;
|
||||||
BackgroundOpacity = defaults.BackgroundOpacity;
|
BackgroundOpacity = defaults.BackgroundOpacity;
|
||||||
ScrollBackLimit = defaults.ScrollBackLimit.ToString();
|
ScrollBackLimit = defaults.ScrollBackLimit.ToString();
|
||||||
WordSeparator = defaults.WordSeparator;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,10 +232,10 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
_defaultValueProvider = defaultValueProvider;
|
_defaultValueProvider = defaultValueProvider;
|
||||||
|
|
||||||
RestoreDefaultsCommand = new AsyncRelayCommand(RestoreDefaultsAsync);
|
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||||
|
|
||||||
Fonts = systemFontService.GetSystemFontFamilies().OrderBy(s => s.Name);
|
Fonts = systemFontService.GetSystemFontFamilies().OrderBy(s => s.Name);
|
||||||
Sizes = Enumerable.Range(2, 72);
|
Sizes = Enumerable.Range(1, 72);
|
||||||
|
|
||||||
_terminalOptions = _settingsService.GetTerminalOptions();
|
_terminalOptions = _settingsService.GetTerminalOptions();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
|
using GalaSoft.MvvmLight;
|
||||||
|
using GalaSoft.MvvmLight.Command;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using FluentTerminal.Models.Messages;
|
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Messaging;
|
|
||||||
using System.Windows.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels.Settings
|
namespace FluentTerminal.App.ViewModels.Settings
|
||||||
{
|
{
|
||||||
public class ThemesPageViewModel : ObservableObject,
|
public class ThemesPageViewModel : ViewModelBase
|
||||||
IRecipient<TerminalOptionsChangedMessage>
|
|
||||||
{
|
{
|
||||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
private readonly IDialogService _dialogService;
|
private readonly IDialogService _dialogService;
|
||||||
@@ -23,36 +17,30 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
private double _backgroundOpacity;
|
private double _backgroundOpacity;
|
||||||
private readonly IThemeParserFactory _themeParserFactory;
|
private readonly IThemeParserFactory _themeParserFactory;
|
||||||
private readonly IFileSystemService _fileSystemService;
|
private readonly IFileSystemService _fileSystemService;
|
||||||
private readonly IImageFileSystemService _imageFileSystemService;
|
|
||||||
|
|
||||||
public event EventHandler<string> SelectedThemeBackgroundColorChanged;
|
public event EventHandler<string> SelectedThemeBackgroundColorChanged;
|
||||||
public event EventHandler<ImageFile> SelectedThemeBackgroundImageChanged;
|
|
||||||
public event EventHandler<ThemeViewModel> SelectedThemeChanged;
|
|
||||||
|
|
||||||
public ThemesPageViewModel(ISettingsService settingsService,
|
public ThemesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider,
|
||||||
IDialogService dialogService,
|
IThemeParserFactory themeParserFactory, IFileSystemService fileSystemService)
|
||||||
IDefaultValueProvider defaultValueProvider,
|
|
||||||
IThemeParserFactory themeParserFactory,
|
|
||||||
IFileSystemService fileSystemService,
|
|
||||||
IImageFileSystemService imageFileSystemService)
|
|
||||||
{
|
{
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
_dialogService = dialogService;
|
_dialogService = dialogService;
|
||||||
_defaultValueProvider = defaultValueProvider;
|
_defaultValueProvider = defaultValueProvider;
|
||||||
_themeParserFactory = themeParserFactory;
|
_themeParserFactory = themeParserFactory;
|
||||||
_fileSystemService = fileSystemService;
|
_fileSystemService = fileSystemService;
|
||||||
_imageFileSystemService = imageFileSystemService;
|
|
||||||
|
|
||||||
CreateThemeCommand = new RelayCommand(CreateTheme);
|
CreateThemeCommand = new RelayCommand(CreateTheme);
|
||||||
ImportThemeCommand = new AsyncRelayCommand(ImportThemeAsync);
|
ImportThemeCommand = new RelayCommand(ImportTheme);
|
||||||
CloneCommand = new RelayCommand<ThemeViewModel>(CloneTheme);
|
CloneCommand = new RelayCommand<ThemeViewModel>(CloneTheme);
|
||||||
|
|
||||||
|
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||||
|
|
||||||
BackgroundOpacity = _settingsService.GetTerminalOptions().BackgroundOpacity;
|
BackgroundOpacity = _settingsService.GetTerminalOptions().BackgroundOpacity;
|
||||||
|
|
||||||
var activeThemeId = _settingsService.GetCurrentThemeId();
|
var activeThemeId = _settingsService.GetCurrentThemeId();
|
||||||
foreach (var theme in _settingsService.GetThemes())
|
foreach (var theme in _settingsService.GetThemes())
|
||||||
{
|
{
|
||||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService, _imageFileSystemService, false);
|
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, fileSystemService, false);
|
||||||
viewModel.Activated += OnThemeActivated;
|
viewModel.Activated += OnThemeActivated;
|
||||||
viewModel.Deleted += OnThemeDeleted;
|
viewModel.Deleted += OnThemeDeleted;
|
||||||
|
|
||||||
@@ -64,18 +52,16 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
}
|
}
|
||||||
|
|
||||||
SelectedTheme = Themes.First(t => t.IsActive);
|
SelectedTheme = Themes.First(t => t.IsActive);
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommand CreateThemeCommand { get; }
|
public RelayCommand CreateThemeCommand { get; }
|
||||||
public ICommand ImportThemeCommand { get; }
|
public RelayCommand ImportThemeCommand { get; }
|
||||||
public ICommand CloneCommand { get; set; }
|
public RelayCommand<ThemeViewModel> CloneCommand { get; set; }
|
||||||
|
|
||||||
public double BackgroundOpacity
|
public double BackgroundOpacity
|
||||||
{
|
{
|
||||||
get => _backgroundOpacity;
|
get => _backgroundOpacity;
|
||||||
set => SetProperty(ref _backgroundOpacity, value);
|
set => Set(ref _backgroundOpacity, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThemeViewModel SelectedTheme
|
public ThemeViewModel SelectedTheme
|
||||||
@@ -86,17 +72,11 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
if (_selectedTheme != null)
|
if (_selectedTheme != null)
|
||||||
{
|
{
|
||||||
_selectedTheme.BackgroundChanged -= OnSelectedThemeBackgroundChanged;
|
_selectedTheme.BackgroundChanged -= OnSelectedThemeBackgroundChanged;
|
||||||
_selectedTheme.BackgroundImageChanged -= OnSelectedThemeBackgroundImageChanged;
|
|
||||||
}
|
}
|
||||||
|
Set(ref _selectedTheme, value);
|
||||||
SetProperty(ref _selectedTheme, value);
|
|
||||||
SelectedThemeChanged?.Invoke(this, _selectedTheme);
|
|
||||||
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
_selectedTheme.BackgroundOpacity = BackgroundOpacity;
|
|
||||||
value.BackgroundChanged += OnSelectedThemeBackgroundChanged;
|
value.BackgroundChanged += OnSelectedThemeBackgroundChanged;
|
||||||
value.BackgroundImageChanged += OnSelectedThemeBackgroundImageChanged;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,49 +109,28 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
AddTheme(theme);
|
AddTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requires UI thread
|
private async void ImportTheme()
|
||||||
private async Task ImportThemeAsync()
|
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we need to execute AddTheme method in the calling (UI) thread.
|
var file = await _fileSystemService.OpenFile(_themeParserFactory.SupportedFileTypes).ConfigureAwait(true);
|
||||||
var file = await _fileSystemService.OpenFileAsync(_themeParserFactory.SupportedFileTypes)
|
|
||||||
.ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
var parser = _themeParserFactory.GetParser(file.FileType);
|
var parser = _themeParserFactory.GetParser(file.FileType);
|
||||||
|
|
||||||
if (parser == null)
|
if (parser == null)
|
||||||
{
|
{
|
||||||
await _dialogService.ShowMessageDialogAsync(I18N.Translate("ImportThemeFailed"),
|
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", "No suitable parser found", DialogButton.OK).ConfigureAwait(false);
|
||||||
I18N.Translate("NoSuitableParserFound"), DialogButton.OK).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// ConfigureAwait(true) because we need to execute AddTheme method in the calling (UI) thread.
|
var theme = await parser.Parse(file.Name, file.Content).ConfigureAwait(true);
|
||||||
var exportedTheme = await parser.Import(file.Name, file.Content).ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(exportedTheme.EncodedImage))
|
AddTheme(theme);
|
||||||
{
|
|
||||||
// ConfigureAwait(true) because we need to execute AddTheme method in the calling (UI) thread.
|
|
||||||
var importedImage = await _imageFileSystemService
|
|
||||||
.ImportThemeImageAsync(exportedTheme.BackgroundImage, exportedTheme.EncodedImage)
|
|
||||||
.ConfigureAwait(true);
|
|
||||||
|
|
||||||
exportedTheme.BackgroundImage = importedImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
var terminalTheme = new TerminalTheme(exportedTheme);
|
|
||||||
|
|
||||||
AddTheme(terminalTheme);
|
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
await _dialogService
|
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", exception.Message, DialogButton.OK).ConfigureAwait(false);
|
||||||
.ShowMessageDialogAsync(I18N.Translate("ImportThemeFailed"), exception.Message, DialogButton.OK)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +139,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
{
|
{
|
||||||
_settingsService.SaveTheme(theme, true);
|
_settingsService.SaveTheme(theme, true);
|
||||||
|
|
||||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService, _imageFileSystemService, true);
|
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService, true);
|
||||||
viewModel.EditCommand.Execute(null);
|
viewModel.EditCommand.Execute(null);
|
||||||
viewModel.Activated += OnThemeActivated;
|
viewModel.Activated += OnThemeActivated;
|
||||||
viewModel.Deleted += OnThemeDeleted;
|
viewModel.Deleted += OnThemeDeleted;
|
||||||
@@ -220,19 +179,14 @@ namespace FluentTerminal.App.ViewModels.Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(TerminalOptionsChangedMessage message)
|
private void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||||
{
|
{
|
||||||
BackgroundOpacity = message.TerminalOptions.BackgroundOpacity;
|
BackgroundOpacity = e.BackgroundOpacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectedThemeBackgroundChanged(object sender, string e)
|
private void OnSelectedThemeBackgroundChanged(object sender, string e)
|
||||||
{
|
{
|
||||||
SelectedThemeBackgroundColorChanged?.Invoke(this, e);
|
SelectedThemeBackgroundColorChanged?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectedThemeBackgroundImageChanged(object sender, ImageFile e)
|
|
||||||
{
|
|
||||||
SelectedThemeBackgroundImageChanged?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,40 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.App.ViewModels.Settings;
|
using FluentTerminal.App.ViewModels.Settings;
|
||||||
using Microsoft.Toolkit.Mvvm.ComponentModel;
|
using GalaSoft.MvvmLight;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
namespace FluentTerminal.App.ViewModels
|
||||||
{
|
{
|
||||||
public class SettingsViewModel : ObservableObject
|
public class SettingsViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService,
|
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService,
|
||||||
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService,
|
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService,
|
||||||
IFileSystemService fileSystemService, IStartupTaskService startupTaskService, IApplicationView applicationView,
|
IFileSystemService fileSystemService, IStartupTaskService startupTaskService, IUpdateService updateService, IApplicationView applicationView)
|
||||||
IApplicationLanguageService applicationLanguageService, ApplicationDataContainers containers,
|
|
||||||
IImageFileSystemService imageFileSystemService)
|
|
||||||
{
|
{
|
||||||
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, trayProcessCommunicationService);
|
About = new AboutPageViewModel(settingsService, updateService, applicationView);
|
||||||
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService, applicationLanguageService, trayProcessCommunicationService, fileSystemService);
|
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService);
|
||||||
Profiles = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService, applicationView);
|
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService);
|
||||||
|
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService, applicationView);
|
||||||
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService);
|
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService);
|
||||||
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService, imageFileSystemService);
|
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService);
|
||||||
Mouse = new MousePageViewModel(settingsService, dialogService, defaultValueProvider);
|
Mouse = new MousePageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||||
SshProfiles = new SshProfilesPageViewModel(settingsService, dialogService, fileSystemService,
|
|
||||||
applicationView, trayProcessCommunicationService, containers.HistoryContainer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler Closed;
|
public event EventHandler Closed;
|
||||||
|
public event EventHandler AboutPageRequested;
|
||||||
|
|
||||||
public GeneralPageViewModel General { get; }
|
public GeneralPageViewModel General { get; }
|
||||||
public KeyBindingsPageViewModel KeyBindings { get; }
|
public KeyBindingsPageViewModel KeyBindings { get; }
|
||||||
public ProfilesPageViewModel Profiles { get; }
|
public ProfilesPageViewModel Shell { get; }
|
||||||
public TerminalPageViewModel Terminal { get; }
|
public TerminalPageViewModel Terminal { get; }
|
||||||
public ThemesPageViewModel Themes { get; }
|
public ThemesPageViewModel Themes { get; }
|
||||||
public MousePageViewModel Mouse { get; }
|
public MousePageViewModel Mouse { get; }
|
||||||
public SshProfilesPageViewModel SshProfiles { get; }
|
public AboutPageViewModel About { get; }
|
||||||
|
|
||||||
|
public void NavigateToAboutPage()
|
||||||
|
{
|
||||||
|
AboutPageRequested?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,111 +1,376 @@
|
|||||||
using FluentTerminal.App.Services;
|
using FluentTerminal.App.Services;
|
||||||
using FluentTerminal.App.Services.Utilities;
|
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||||
|
using FluentTerminal.App.ViewModels.Settings;
|
||||||
using FluentTerminal.Models;
|
using FluentTerminal.Models;
|
||||||
|
using FluentTerminal.Models.Enums;
|
||||||
|
using GalaSoft.MvvmLight;
|
||||||
|
using GalaSoft.MvvmLight.Command;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentTerminal.App.ViewModels.Profiles;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using Microsoft.Toolkit.Mvvm.Input;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
namespace FluentTerminal.App.ViewModels
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class ShellProfileViewModel : ViewModelBase
|
||||||
/// Extends <see cref="ProfileViewModelBase{T}"/>, and implements only "Set as default" logic.
|
|
||||||
/// </summary>
|
|
||||||
public class ShellProfileViewModel : ProfileViewModelBase<CommonProfileProviderViewModel>
|
|
||||||
{
|
{
|
||||||
#region Fields
|
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 readonly IDefaultValueProvider _defaultValueProvider;
|
||||||
|
|
||||||
#endregion Fields
|
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;
|
||||||
|
|
||||||
#region Properties
|
_settingsService.ThemeAdded += OnThemeAdded;
|
||||||
|
_settingsService.ThemeDeleted += OnThemeDeleted;
|
||||||
|
|
||||||
private bool _isDefault;
|
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
|
public bool IsDefault
|
||||||
{
|
{
|
||||||
get => _isDefault;
|
get => _isDefault;
|
||||||
set => SetProperty(ref _isDefault, value);
|
set => Set(ref _isDefault, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Properties
|
public string Location
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
public event EventHandler SetAsDefault;
|
|
||||||
|
|
||||||
#endregion Events
|
|
||||||
|
|
||||||
#region Commands
|
|
||||||
|
|
||||||
public ICommand SetDefaultCommand { get; }
|
|
||||||
|
|
||||||
public ICommand RestoreDefaultsCommand { get; }
|
|
||||||
|
|
||||||
#endregion Commands
|
|
||||||
|
|
||||||
#region Constrcutor
|
|
||||||
|
|
||||||
public ShellProfileViewModel(ShellProfile shellProfile, ISettingsService settingsService,
|
|
||||||
IDialogService dialogService, IFileSystemService fileSystemService, IApplicationView applicationView,
|
|
||||||
IDefaultValueProvider defaultValueProvider, bool isNew) : base(shellProfile, settingsService, dialogService,
|
|
||||||
isNew)
|
|
||||||
{
|
{
|
||||||
_defaultValueProvider = defaultValueProvider;
|
get => _location;
|
||||||
|
set => Set(ref _location, value);
|
||||||
ProfileVm = new CommonProfileProviderViewModel(settingsService, applicationView, fileSystemService,
|
|
||||||
shellProfile);
|
|
||||||
|
|
||||||
SetDefaultCommand = new RelayCommand(SetDefault);
|
|
||||||
RestoreDefaultsCommand = new AsyncRelayCommand(RestoreDefaultsAsync);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Constrcutor
|
public string Name
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
// Requires UI thread
|
|
||||||
private async Task RestoreDefaultsAsync()
|
|
||||||
{
|
{
|
||||||
if (InEditMode || !ProfileVm.PreInstalled)
|
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 => Set(ref _lineEndingStyle, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DoNotModifyIsSelected
|
||||||
|
{
|
||||||
|
get => LineEndingStyle == LineEndingStyle.DoNotModify;
|
||||||
|
set { if (value) LineEndingStyle = LineEndingStyle.DoNotModify; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToCRLFIsSelected
|
||||||
|
{
|
||||||
|
get => LineEndingStyle == LineEndingStyle.ToCRLF;
|
||||||
|
set { if (value) LineEndingStyle = LineEndingStyle.ToCRLF; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToLFIsSelected
|
||||||
|
{
|
||||||
|
get => LineEndingStyle == LineEndingStyle.ToLF;
|
||||||
|
set { if (value) LineEndingStyle = LineEndingStyle.ToLF; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ToCRIsSelected
|
||||||
|
{
|
||||||
|
get => LineEndingStyle == LineEndingStyle.ToCR;
|
||||||
|
set { if (value) LineEndingStyle = LineEndingStyle.ToCR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureAwait(true) because we need to execute Initialize method in the calling (UI) thread.
|
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore this profile?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||||
var result = await DialogService.ShowMessageDialogAsync(I18N.Translate("PleaseConfirm"),
|
|
||||||
I18N.Translate("ConfirmRestoreProfile"), DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
|
||||||
|
|
||||||
if (result == DialogButton.OK)
|
if (result == DialogButton.OK)
|
||||||
{
|
{
|
||||||
var profile = _defaultValueProvider.GetPreinstalledShellProfiles().FirstOrDefault(x => x.Id.Equals(Id));
|
Model = _defaultValueProvider.GetPreinstalledShellProfiles().FirstOrDefault(x => x.Id == Model.Id);
|
||||||
|
InitializeViewModelProperties(Model);
|
||||||
if (profile == null)
|
_settingsService.SaveShellProfile(Model);
|
||||||
{
|
|
||||||
// Should not happen ever, but...
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfileVm.Model = profile;
|
|
||||||
Initialize(profile);
|
|
||||||
|
|
||||||
SettingsService.SaveShellProfile(profile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CanDelete()
|
public Task AddKeyboardShortcut()
|
||||||
{
|
{
|
||||||
return !ProfileVm.PreInstalled;
|
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,
|
||||||
|
LineEndingTranslation = Model.LineEndingTranslation,
|
||||||
|
Location = Model.Location,
|
||||||
|
Name = Model.Name,
|
||||||
|
WorkingDirectory = Model.WorkingDirectory,
|
||||||
|
TabThemeId = Model.TabThemeId,
|
||||||
|
TerminalThemeId = Model.TerminalThemeId
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyBindings.Editable = true;
|
||||||
|
InEditMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetDefault()
|
private void SetDefault()
|
||||||
{
|
{
|
||||||
SetAsDefault?.Invoke(this, EventArgs.Empty);
|
SetAsDefault?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using FluentTerminal.App.Services.Dialogs;
|
||||||
|
using GalaSoft.MvvmLight;
|
||||||
|
|
||||||
|
namespace FluentTerminal.App.ViewModels
|
||||||
|
{
|
||||||
|
public class SshConnectionInfoViewModel : ViewModelBase, ISshConnectionInfo
|
||||||
|
{
|
||||||
|
private string _host = string.Empty;
|
||||||
|
|
||||||
|
public string Host
|
||||||
|
{
|
||||||
|
get => _host;
|
||||||
|
set => Set(ref _host, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort _port = 22;
|
||||||
|
|
||||||
|
public ushort Port
|
||||||
|
{
|
||||||
|
get => _port;
|
||||||
|
set => Set(ref _port, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _username = string.Empty;
|
||||||
|
|
||||||
|
public string Username
|
||||||
|
{
|
||||||
|
get => _username;
|
||||||
|
set => Set(ref _username, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using FluentTerminal.App.Services;
|
|
||||||
using FluentTerminal.App.ViewModels.Profiles;
|
|
||||||
using FluentTerminal.Models;
|
|
||||||
|
|
||||||
namespace FluentTerminal.App.ViewModels
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Extends <see cref="ProfileViewModelBase{T}"/>, and doesn't implement any additional logic because in
|
|
||||||
/// case of SSH profiles no additional logic is needed.
|
|
||||||
/// </summary>
|
|
||||||
public class SshProfileViewModel : ProfileViewModelBase<SshConnectViewModel>
|
|
||||||
{
|
|
||||||
#region Constructor
|
|
||||||
|
|
||||||
public SshProfileViewModel(SshProfile sshProfile, ISettingsService settingsService,
|
|
||||||
IDialogService dialogService, IFileSystemService fileSystemService, IApplicationView applicationView,
|
|
||||||
ITrayProcessCommunicationService trayProcessCommunicationService,
|
|
||||||
IApplicationDataContainer historyContainer, bool isNew) : base(sshProfile, settingsService, dialogService,
|
|
||||||
isNew)
|
|
||||||
{
|
|
||||||
ProfileVm = new SshConnectViewModel(settingsService, applicationView, trayProcessCommunicationService,
|
|
||||||
fileSystemService, sshProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Constructor
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
protected override bool CanDelete()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loaded 100 of 474 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user