Compare commits
120
Commits
0.1.4.0
...
Dev/NamedPipe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76cb528eb4 | ||
|
|
def3aa3683 | ||
|
|
77d9a42c36 | ||
|
|
d395085064 | ||
|
|
56e5d60dae | ||
|
|
780cbf64b1 | ||
|
|
6b25d78ce6 | ||
|
|
53726e0e68 | ||
|
|
5e11e5d58b | ||
|
|
3e0417ce47 | ||
|
|
417bc843b0 | ||
|
|
6d6bdcca3e | ||
|
|
b4bfe39586 | ||
|
|
940cb6f38b | ||
|
|
59c6a39458 | ||
|
|
05ce19ec8c | ||
|
|
7b2bd59365 | ||
|
|
6a67883777 | ||
|
|
847e1c045a | ||
|
|
e918c5492e | ||
|
|
1aba3d4926 | ||
|
|
5ad51b6bf7 | ||
|
|
72cfe1168f | ||
|
|
5a96a0387b | ||
|
|
00a5b6377d | ||
|
|
f610aa8a0f | ||
|
|
c32342bca8 | ||
|
|
84af36f6bc | ||
|
|
f56465f54a | ||
|
|
2527657261 | ||
|
|
5414987ab6 | ||
|
|
327f0d5dd5 | ||
|
|
bd64aacbab | ||
|
|
c79b513470 | ||
|
|
bd3ca90514 | ||
|
|
88f88698a2 | ||
|
|
dcdf8d1854 | ||
|
|
fb717b3750 | ||
|
|
aff7b636f1 | ||
|
|
99a7de25d3 | ||
|
|
28d950ed9a | ||
|
|
1490470e91 | ||
|
|
236e0ff2e1 | ||
|
|
5cb9158077 | ||
|
|
941c7c1948 | ||
|
|
45e096c033 | ||
|
|
d37da0ef31 | ||
|
|
49abf56ec6 | ||
|
|
618a2485e1 | ||
|
|
cdeeeb6fa8 | ||
|
|
be833cc174 | ||
|
|
39955bcb3e | ||
|
|
e9b76a6867 | ||
|
|
89bce37dbd | ||
|
|
82a195c2f2 | ||
|
|
c48889cbbc | ||
|
|
dd9d5d2e72 | ||
|
|
7f7f15ea03 | ||
|
|
f502303426 | ||
|
|
2f2ed06d58 | ||
|
|
809167a872 | ||
|
|
5d3fd25098 | ||
|
|
6674fd0ad9 | ||
|
|
f874cb070d | ||
|
|
40230bfce8 | ||
|
|
3afa2b5f25 | ||
|
|
94ba1b8324 | ||
|
|
6dfacdfebd | ||
|
|
1ca7371103 | ||
|
|
1cb388d4af | ||
|
|
3bdd96c388 | ||
|
|
5bc5bf224f | ||
|
|
01815db1fe | ||
|
|
80cf43d3bf | ||
|
|
050f162c08 | ||
|
|
f5e03c9856 | ||
|
|
f763798683 | ||
|
|
46120e3431 | ||
|
|
829077883b | ||
|
|
8c928faa09 | ||
|
|
1654e9f1f5 | ||
|
|
f542331192 | ||
|
|
6830adb788 | ||
|
|
02e015371c | ||
|
|
80bdfa012c | ||
|
|
70326e9585 | ||
|
|
c0e1415b6e | ||
|
|
13a3402b58 | ||
|
|
73703412f8 | ||
|
|
ab226d95d4 | ||
|
|
de7a76bb3b | ||
|
|
379f58b4b8 | ||
|
|
3a04cd3db2 | ||
|
|
df0d3f080b | ||
|
|
04131d271a | ||
|
|
d66fe26634 | ||
|
|
8870f0621c | ||
|
|
e5250a7ae8 | ||
|
|
b2b6cedf2c | ||
|
|
e777c2c5ac | ||
|
|
7dcd8e1fbb | ||
|
|
37518cf88b | ||
|
|
ca0e22b8d0 | ||
|
|
074f5f9c7e | ||
|
|
0b40963c82 | ||
|
|
0b6b8a9968 | ||
|
|
47613454c7 | ||
|
|
96d063b4fd | ||
|
|
fec44fb0b5 | ||
|
|
58e7de5271 | ||
|
|
466b7a7503 | ||
|
|
50f9c84420 | ||
|
|
f07dae98ee | ||
|
|
1cc74f79b4 | ||
|
|
60c4f0518d | ||
|
|
f9fb05b194 | ||
|
|
d933f60b8b | ||
|
|
45bba6e2eb | ||
|
|
210aeedc25 | ||
|
|
acfb8e8883 |
No files matched your search
@@ -1,5 +1,7 @@
|
||||
# Project specific
|
||||
FluentTerminal.App/Win32
|
||||
FluentTerminal.App/Client/
|
||||
FluentTerminal.Client/dist
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
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\LibraryFolder\Background\shell\Open Fluent Terminal here\command" /d "\"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe\" new %%V" /f
|
||||
@@ -0,0 +1,3 @@
|
||||
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\LibraryFolder\Background\shell\Open Fluent Terminal here" /f
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class DialogServiceTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public DialogServiceTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowDialogAsync_TitleIsEmpty_ThrowsArgumentNullException()
|
||||
{
|
||||
var title = string.Empty;
|
||||
var content = _fixture.Create<string>();
|
||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||
var dialogService = new DialogService(() => Mock.Of<IShellProfileSelectionDialog>(), () => Mock.Of<IMessageDialog>(), () => Mock.Of<ICreateKeyBindingDialog>());
|
||||
|
||||
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||
|
||||
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("title");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowDialogAsync_ContentIsEmpty_ThrowsArgumentNullException()
|
||||
{
|
||||
var title = _fixture.Create<string>();
|
||||
var content = string.Empty;
|
||||
var buttons = _fixture.CreateMany<DialogButton>(2);
|
||||
var dialogService = new DialogService(() => Mock.Of<IShellProfileSelectionDialog>(), () => Mock.Of<IMessageDialog>(), () => Mock.Of<ICreateKeyBindingDialog>());
|
||||
|
||||
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
|
||||
|
||||
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("content");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShowDialogAsync_NoButtonsPassed_ThrowsArgumentException()
|
||||
{
|
||||
var title = _fixture.Create<string>();
|
||||
var content = _fixture.Create<string>();
|
||||
var dialogService = new DialogService(() => Mock.Of<IShellProfileSelectionDialog>(), () => Mock.Of<IMessageDialog>(), () => Mock.Of<ICreateKeyBindingDialog>());
|
||||
|
||||
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content);
|
||||
|
||||
invoke.Should().Throw<ArgumentException>().And.ParamName.Should().Be("buttons");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.4.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="5.4.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="Moq" Version="4.8.3" />
|
||||
<PackageReference Include="xunit" Version="2.3.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
|
||||
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,69 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
public class KeyboardCommandServiceTests
|
||||
{
|
||||
private readonly Fixture _fixture;
|
||||
|
||||
public KeyboardCommandServiceTests()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisterCommandHandler_HandlerIsNull_ThrowsArgumentNullException()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
Action handler = null;
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
Action invoke = () => keyboardCommandService.RegisterCommandHandler(command, handler);
|
||||
|
||||
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("handler");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RegisterCommandHandler_CommandAlreadyRegisted_ThrowsInvalidOperationException()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
var handler = _fixture.Create<Action>();
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
keyboardCommandService.RegisterCommandHandler(command, handler);
|
||||
|
||||
Action invoke = () => keyboardCommandService.RegisterCommandHandler(command, handler);
|
||||
|
||||
invoke.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsRegistered_HandlerGetsInvoked()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
var handlerCalled = false;
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
keyboardCommandService.RegisterCommandHandler(command, () => handlerCalled = true);
|
||||
|
||||
keyboardCommandService.SendCommand(command);
|
||||
|
||||
handlerCalled.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsNotRegisted_KeyNotFoundExceptionIsThrown()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
Action invoke = () => keyboardCommandService.SendCommand(command);
|
||||
|
||||
invoke.Should().Throw<KeyNotFoundException>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public class ApplicationDataContainers
|
||||
{
|
||||
public IApplicationDataContainer LocalSettings { get; set; }
|
||||
public IApplicationDataContainer RoamingSettings { get; set; }
|
||||
public IApplicationDataContainer Themes { get; set; }
|
||||
public IApplicationDataContainer KeyBindings { get; set; }
|
||||
public IApplicationDataContainer ShellProfiles { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Dialogs
|
||||
{
|
||||
public interface ICreateKeyBindingDialog
|
||||
{
|
||||
Task<KeyBinding> CreateKeyBinding();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Dialogs
|
||||
{
|
||||
public interface IMessageDialog
|
||||
{
|
||||
string Title { get; set; }
|
||||
string Content { get; set; }
|
||||
|
||||
void AddButton(DialogButton button);
|
||||
|
||||
Task<DialogButton> ShowAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Dialogs
|
||||
{
|
||||
public interface IShellProfileSelectionDialog
|
||||
{
|
||||
ShellProfile SelectedProfile { get; }
|
||||
|
||||
Task<ShellProfile> SelectProfile();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FluentTerminal.App.Services.EventArgs
|
||||
{
|
||||
public class CancelableEventArgs : System.EventArgs
|
||||
{
|
||||
public bool Cancelled { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services.Exceptions
|
||||
{
|
||||
public class ParseThemeException : Exception
|
||||
{
|
||||
public ParseThemeException() : base()
|
||||
{
|
||||
}
|
||||
|
||||
public ParseThemeException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public ParseThemeException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected ParseThemeException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="PListNet" Version="2.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IAppServiceConnection
|
||||
{
|
||||
Task<IDictionary<string, string>> SendMessageAsync(IDictionary<string, string> message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IApplicationDataContainer
|
||||
{
|
||||
T ReadValueFromJson<T>(string name, T fallbackValue);
|
||||
|
||||
void WriteValueAsJson<T>(string name, T value);
|
||||
|
||||
bool TryGetValue(string key, out object value);
|
||||
|
||||
void SetValue(string key, object value);
|
||||
|
||||
void Delete(string key);
|
||||
|
||||
IEnumerable<object> GetAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using FluentTerminal.App.Services.EventArgs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public delegate Task CloseRequestedHandler(object sender, CancelableEventArgs args);
|
||||
|
||||
public interface IApplicationView
|
||||
{
|
||||
event CloseRequestedHandler CloseRequested;
|
||||
|
||||
string Title { get; set; }
|
||||
|
||||
Task RunOnDispatcherThread(Action action);
|
||||
Task<bool> TryClose();
|
||||
bool ToggleFullScreen();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IClipboardService
|
||||
{
|
||||
Task<string> GetText();
|
||||
void SetText(string text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IDefaultValueProvider
|
||||
{
|
||||
IEnumerable<ShellProfile> GetPreinstalledShellProfiles();
|
||||
Guid GetDefaultShellProfileId();
|
||||
TerminalOptions GetDefaultTerminalOptions();
|
||||
ICollection<KeyBinding> GetDefaultKeyBindings(Command command);
|
||||
ApplicationSettings GetDefaultApplicationSettings();
|
||||
Guid GetDefaultThemeId();
|
||||
IEnumerable<TerminalTheme> GetPreInstalledThemes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public enum DialogButton
|
||||
{
|
||||
OK,
|
||||
Cancel
|
||||
}
|
||||
|
||||
public interface IDialogService
|
||||
{
|
||||
Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons);
|
||||
|
||||
Task<ShellProfile> ShowProfileSelectionDialogAsync();
|
||||
|
||||
Task<KeyBinding> ShowCreateKeyBindingDialog();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IDispatcherTimer
|
||||
{
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
TimeSpan Interval { get; set; }
|
||||
bool IsEnabled { get; }
|
||||
|
||||
event EventHandler<object> Tick;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IFileSystemService
|
||||
{
|
||||
Task<File> OpenFile(IEnumerable<string> fileTypes);
|
||||
|
||||
Task<string> BrowseForDirectory();
|
||||
|
||||
Task SaveTextFile(string name, string fileTypeDescription, string fileType, string content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IKeyboardCommandService
|
||||
{
|
||||
void RegisterCommandHandler(Command command, Action handler);
|
||||
void SendCommand(Command command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ISettingsService
|
||||
{
|
||||
event EventHandler<Guid> CurrentThemeChanged;
|
||||
event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||
event EventHandler KeyBindingsChanged;
|
||||
|
||||
Guid GetDefaultShellProfileId();
|
||||
ShellProfile GetDefaultShellProfile();
|
||||
void SaveDefaultShellProfileId(Guid id);
|
||||
|
||||
IEnumerable<ShellProfile> GetShellProfiles();
|
||||
void SaveShellProfile(ShellProfile shellProfile);
|
||||
void DeleteShellProfile(Guid id);
|
||||
|
||||
TerminalOptions GetTerminalOptions();
|
||||
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
||||
|
||||
ApplicationSettings GetApplicationSettings();
|
||||
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
||||
|
||||
IDictionary<Command, ICollection<KeyBinding>> GetKeyBindings();
|
||||
void SaveKeyBindings(Command command, ICollection<KeyBinding> keyBindings);
|
||||
void ResetKeyBindings();
|
||||
|
||||
TerminalTheme GetCurrentTheme();
|
||||
Guid GetCurrentThemeId();
|
||||
TerminalTheme GetTheme(Guid id);
|
||||
void SaveCurrentThemeId(Guid id);
|
||||
|
||||
IEnumerable<TerminalTheme> GetThemes();
|
||||
void SaveTheme(TerminalTheme theme);
|
||||
void DeleteTheme(Guid id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ISystemFontService
|
||||
{
|
||||
IEnumerable<string> GetSystemFontFamilies();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ITerminalView
|
||||
{
|
||||
event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
event EventHandler<string> TerminalTitleChanged;
|
||||
event EventHandler<Command> KeyboardCommandReceived;
|
||||
|
||||
Task ChangeTheme(TerminalColors theme);
|
||||
Task ChangeOptions(TerminalOptions options);
|
||||
Task ChangeKeyBindings(IEnumerable<KeyBinding> keyBindings);
|
||||
Task<TerminalSize> CreateTerminal(TerminalOptions options, TerminalColors theme, IEnumerable<KeyBinding> keyBindings);
|
||||
Task ConnectToSocket(string url);
|
||||
void Close();
|
||||
Task FocusTerminal();
|
||||
Task<string> GetSelection();
|
||||
Task Write(string text);
|
||||
Task FindNext(string searchText);
|
||||
Task FindPrevious(string searchText);
|
||||
void FocusSearchTextBox();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IThemeParser
|
||||
{
|
||||
IEnumerable<string> SupportedFileTypes { get; }
|
||||
|
||||
Task<TerminalTheme> Parse(string fileName, Stream fileContent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IThemeParserFactory
|
||||
{
|
||||
IEnumerable<string> SupportedFileTypes { get; }
|
||||
|
||||
IThemeParser GetParser(string fileType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Responses;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ITrayProcessCommunicationService
|
||||
{
|
||||
Task Initialize(IAppServiceConnection appServiceConnection);
|
||||
Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellProfile shellProfile);
|
||||
Task ResizeTerminal(int id, TerminalSize size);
|
||||
Task UpdateToggleWindowKeyBindings();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class DefaultValueProvider : IDefaultValueProvider
|
||||
{
|
||||
public ApplicationSettings GetDefaultApplicationSettings()
|
||||
{
|
||||
return new ApplicationSettings
|
||||
{
|
||||
ConfirmClosingTabs = false,
|
||||
ConfirmClosingWindows = false,
|
||||
UnderlineSelectedTab = false,
|
||||
NewTerminalLocation = NewTerminalLocation.Tab
|
||||
};
|
||||
}
|
||||
|
||||
public ICollection<KeyBinding> GetDefaultKeyBindings(Command command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case Command.ToggleWindow:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ToggleWindow,
|
||||
Ctrl = false,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Scroll
|
||||
}
|
||||
};
|
||||
|
||||
case Command.NextTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NextTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Tab
|
||||
}
|
||||
};
|
||||
|
||||
case Command.PreviousTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.PreviousTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
Key = (int)ExtendedVirtualKey.Tab
|
||||
}
|
||||
};
|
||||
|
||||
case Command.NewTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NewTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.T
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ConfigurableNewTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ConfigurableNewTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
Key = (int)ExtendedVirtualKey.T
|
||||
}
|
||||
};
|
||||
|
||||
case Command.CloseTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.CloseTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.W
|
||||
}
|
||||
};
|
||||
|
||||
case Command.NewWindow:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NewWindow,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.N
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ShowSettings:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ShowSettings,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Comma
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Copy:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Copy,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.C
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Paste:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Paste,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.V
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Search:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Search,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.F
|
||||
}
|
||||
};
|
||||
|
||||
case Command.ToggleFullScreen:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ToggleFullScreen,
|
||||
Ctrl = false,
|
||||
Alt = true,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Enter
|
||||
}
|
||||
};
|
||||
|
||||
case Command.SelectAll:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.SelectAll,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.A
|
||||
}
|
||||
};
|
||||
|
||||
case Command.Clear:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Clear,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.L
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Guid GetDefaultShellProfileId()
|
||||
{
|
||||
return Guid.Parse("813f2298-210a-481a-bdbf-c17bc637a3e2");
|
||||
}
|
||||
|
||||
public TerminalOptions GetDefaultTerminalOptions()
|
||||
{
|
||||
return new TerminalOptions
|
||||
{
|
||||
BellStyle = BellStyle.None,
|
||||
CursorBlink = true,
|
||||
CursorStyle = CursorStyle.Block,
|
||||
ScrollBarStyle = ScrollBarStyle.Hidden,
|
||||
FontFamily = "Consolas",
|
||||
FontSize = 13,
|
||||
BackgroundOpacity = 0.8,
|
||||
ScrollBackLimit = 1000
|
||||
};
|
||||
}
|
||||
|
||||
public Guid GetDefaultThemeId()
|
||||
{
|
||||
return Guid.Parse("281e4352-bb50-47b7-a691-2b13830df95e");
|
||||
}
|
||||
|
||||
public IEnumerable<ShellProfile> GetPreinstalledShellProfiles()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new ShellProfile
|
||||
{
|
||||
Id = GetDefaultShellProfileId(),
|
||||
Name = "Powershell",
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty
|
||||
},
|
||||
new ShellProfile
|
||||
{
|
||||
Id = Guid.Parse("ab942a61-7673-4755-9bd8-765aff91d9a3"),
|
||||
Name = "CMD",
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\Windows\System32\cmd.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty
|
||||
},
|
||||
new ShellProfile
|
||||
{
|
||||
Id= Guid.Parse("e5785ad6-584f-40cb-bdcd-d5b3b3953e7f"),
|
||||
Name = "WSL",
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\windows\system32\wsl.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<TerminalTheme> GetPreInstalledThemes()
|
||||
{
|
||||
var defaultXterm = new TerminalTheme
|
||||
{
|
||||
Id = GetDefaultThemeId(),
|
||||
Author = "xterm.js",
|
||||
Name = "Xterm.js Default",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Black = "#2e3436",
|
||||
Red = "#cc0000",
|
||||
Green = "#4e9a06",
|
||||
Yellow = "#c4a000",
|
||||
Blue = "#3465a4",
|
||||
Magenta = "#75507b",
|
||||
Cyan = "#06989a",
|
||||
White = "#d3d7cf",
|
||||
BrightBlack = "#555753",
|
||||
BrightRed = "#ef2929",
|
||||
BrightGreen = "#8ae234",
|
||||
BrightYellow = "#fce94f",
|
||||
BrightBlue = "#729fcf",
|
||||
BrightMagenta = "#ad7fa8",
|
||||
BrightCyan = "#34e2e2",
|
||||
BrightWhite = "#eeeeec",
|
||||
Foreground = "#ffffff",
|
||||
Background = "#000000",
|
||||
Cursor = "#ffffff",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "rgba(255, 255, 255, 0.3)"
|
||||
}
|
||||
};
|
||||
|
||||
var powerShell = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("3571ce1b-31ce-4cf7-ae15-e0bff70c3eea"),
|
||||
Author = "Microsoft",
|
||||
Name = "PowerShell",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Black = "#000000",
|
||||
Red = "#800000",
|
||||
Green = "#008000",
|
||||
Yellow = "#EEEDF0",
|
||||
Blue = "#000080",
|
||||
Magenta = "#012456",
|
||||
Cyan = "#008080",
|
||||
White = "#c0c0c0",
|
||||
BrightBlack = "#808080",
|
||||
BrightRed = "#ff0000",
|
||||
BrightGreen = "#00ff00",
|
||||
BrightYellow = "#ffff00",
|
||||
BrightBlue = "#0000ff",
|
||||
BrightMagenta = "#ff00ff",
|
||||
BrightCyan = "#00ffff",
|
||||
BrightWhite = "#ffffff",
|
||||
Foreground = "#ffffff",
|
||||
Background = "#012456",
|
||||
Cursor = "#fedba9",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "rgba(254, 219, 169, 0.3)"
|
||||
}
|
||||
};
|
||||
|
||||
var homebrew = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("5f034210-067b-40e2-b9c9-6a25eb6fb8e6"),
|
||||
Author = "Hans Kokx",
|
||||
Name = "Homebrew",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Black = "#000000",
|
||||
Red = "#A0160B",
|
||||
Green = "#00AF21",
|
||||
Yellow = "#A1A222",
|
||||
Blue = "#192AB7",
|
||||
Magenta = "#AA2FAE",
|
||||
Cyan = "#12B1BC",
|
||||
White = "#BBB5AF",
|
||||
BrightBlack = "#747876",
|
||||
BrightRed = "#E52213",
|
||||
BrightGreen = "#00D92B",
|
||||
BrightYellow = "#E6E435",
|
||||
BrightBlue = "#283EF9",
|
||||
BrightMagenta = "#EB43E6",
|
||||
BrightCyan = "#15E7E8",
|
||||
BrightWhite = "#E9E9E9",
|
||||
Foreground = "#00D92B",
|
||||
Background = "#000000",
|
||||
Cursor = "#00D92B",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "#4CFFFFFF"
|
||||
}
|
||||
};
|
||||
|
||||
var tomorrow = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("0cbbf805-fa86-4be5-ac51-75f3054dcc3a"),
|
||||
Author = "Chris Kempson",
|
||||
Name = "Tomorrow",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Foreground = "#4D4D4C",
|
||||
Background = "#FFFFFF",
|
||||
Cursor = "#4D4D4C",
|
||||
CursorAccent = "#FFFFFF",
|
||||
Selection = "rgba(214, 214, 214, 0.5)",
|
||||
Black = "#000000",
|
||||
Red = "#C82829",
|
||||
Green = "#718C00",
|
||||
Yellow = "#EAB700",
|
||||
Blue = "#4271AE",
|
||||
Magenta = "#8959A8",
|
||||
Cyan = "#3E999F",
|
||||
White = "#FFFFFF",
|
||||
BrightBlack = "#000000",
|
||||
BrightRed = "#C82829",
|
||||
BrightGreen = "#718C00",
|
||||
BrightYellow = "#EAB700",
|
||||
BrightBlue = "#4271AE",
|
||||
BrightMagenta = "#8959A8",
|
||||
BrightCyan = "#3E999F",
|
||||
BrightWhite = "#FFFFFF"
|
||||
}
|
||||
};
|
||||
|
||||
var ubuntu = new TerminalTheme
|
||||
{
|
||||
Id = Guid.Parse("32519543-bf94-4db6-92a1-7bcec3966c82"),
|
||||
Author = "Canonical",
|
||||
Name = "Ubuntu Bash",
|
||||
PreInstalled = true,
|
||||
Colors = new TerminalColors
|
||||
{
|
||||
Foreground = "#EEEEEE",
|
||||
Background = "#300A24",
|
||||
Cursor = "#CFF5DB",
|
||||
CursorAccent = "#000000",
|
||||
Selection = "#4DABEDC1",
|
||||
Black = "#300A24",
|
||||
Red = "#3465A4",
|
||||
Green = "#4E9A06",
|
||||
Yellow = "#06989A",
|
||||
Blue = "#CC0000",
|
||||
Magenta = "#75507B",
|
||||
Cyan = "#C4A000",
|
||||
White = "#D3D7CF",
|
||||
BrightBlack = "#554E53",
|
||||
BrightRed = "#729FCF",
|
||||
BrightGreen = "#8AE234",
|
||||
BrightYellow = "#34E2E2",
|
||||
BrightBlue = "#EF2929",
|
||||
BrightMagenta = "#AD7FA8",
|
||||
BrightCyan = "#FCE94F",
|
||||
BrightWhite = "#EEEEEE"
|
||||
}
|
||||
};
|
||||
|
||||
return new[] { defaultXterm, powerShell, homebrew, tomorrow, ubuntu };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using FluentTerminal.Models;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class DialogService : IDialogService
|
||||
{
|
||||
private readonly Func<IShellProfileSelectionDialog> _shellProfileSelectionDialogFactory;
|
||||
private readonly Func<IMessageDialog> _messageDialogFactory;
|
||||
private readonly Func<ICreateKeyBindingDialog> _createKeyBindingDialogFactory;
|
||||
|
||||
public DialogService(Func<IShellProfileSelectionDialog> shellProfileSelectionDialogFactory, Func<IMessageDialog> messageDialogFactory, Func<ICreateKeyBindingDialog> createKeyBindingDialogFactory)
|
||||
{
|
||||
_shellProfileSelectionDialogFactory = shellProfileSelectionDialogFactory;
|
||||
_messageDialogFactory = messageDialogFactory;
|
||||
_createKeyBindingDialogFactory = createKeyBindingDialogFactory;
|
||||
}
|
||||
|
||||
public Task<KeyBinding> ShowCreateKeyBindingDialog()
|
||||
{
|
||||
var dialog = _createKeyBindingDialogFactory();
|
||||
|
||||
return dialog.CreateKeyBinding();
|
||||
}
|
||||
|
||||
public Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(title));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(content));
|
||||
}
|
||||
|
||||
if (buttons.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Must not be empty", nameof(buttons));
|
||||
}
|
||||
|
||||
var dialog = _messageDialogFactory();
|
||||
dialog.Content = content;
|
||||
dialog.Title = title;
|
||||
|
||||
foreach (var button in buttons)
|
||||
{
|
||||
dialog.AddButton(button);
|
||||
}
|
||||
|
||||
return dialog.ShowAsync();
|
||||
}
|
||||
|
||||
public Task<ShellProfile> ShowProfileSelectionDialogAsync()
|
||||
{
|
||||
var dialog = _shellProfileSelectionDialogFactory();
|
||||
|
||||
return dialog.SelectProfile();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using FluentTerminal.Models;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class FluentTerminalThemeParser : IThemeParser
|
||||
{
|
||||
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".flutecolors" };
|
||||
|
||||
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
||||
{
|
||||
using (var streamReader = new StreamReader(fileContent))
|
||||
{
|
||||
var content = await streamReader.ReadToEndAsync();
|
||||
var theme = JsonConvert.DeserializeObject<TerminalTheme>(content);
|
||||
|
||||
theme.PreInstalled = false;
|
||||
theme.Id = Guid.NewGuid();
|
||||
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
using FluentTerminal.App.Services.Exceptions;
|
||||
using FluentTerminal.Models;
|
||||
using PListNet;
|
||||
using PListNet.Nodes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class ITermThemeParser : IThemeParser
|
||||
{
|
||||
private const byte Opacity30Percent = 77;
|
||||
|
||||
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".itermcolors" };
|
||||
|
||||
private static class ITermThemeKeys
|
||||
{
|
||||
public const string Ansi0Color = "Ansi 0 Color";
|
||||
public const string Ansi1Color = "Ansi 1 Color";
|
||||
public const string Ansi2Color = "Ansi 2 Color";
|
||||
public const string Ansi3Color = "Ansi 3 Color";
|
||||
public const string Ansi4Color = "Ansi 4 Color";
|
||||
public const string Ansi5Color = "Ansi 5 Color";
|
||||
public const string Ansi6Color = "Ansi 6 Color";
|
||||
public const string Ansi7Color = "Ansi 7 Color";
|
||||
public const string Ansi8Color = "Ansi 8 Color";
|
||||
public const string Ansi9Color = "Ansi 9 Color";
|
||||
public const string Ansi10Color = "Ansi 10 Color";
|
||||
public const string Ansi11Color = "Ansi 11 Color";
|
||||
public const string Ansi12Color = "Ansi 12 Color";
|
||||
public const string Ansi13Color = "Ansi 13 Color";
|
||||
public const string Ansi14Color = "Ansi 14 Color";
|
||||
public const string Ansi15Color = "Ansi 15 Color";
|
||||
|
||||
public const string BackgroundColor = "Background Color";
|
||||
public const string BoldColor = "Bold Color";
|
||||
public const string CursorColor = "Cursor Color";
|
||||
public const string CursorTextColor = "Cursor Text Color";
|
||||
public const string ForegroundColor = "Foreground Color";
|
||||
public const string SelectedTextColor = "Selected Text Color";
|
||||
public const string SelectionColor = "Selection Color";
|
||||
}
|
||||
|
||||
private static class ITermThemeColorKeys
|
||||
{
|
||||
public const string BlueComponent = "Blue Component";
|
||||
public const string GreenComponent = "Green Component";
|
||||
public const string RedComponent = "Red Component";
|
||||
}
|
||||
|
||||
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
|
||||
{
|
||||
var node = PList.Load(fileContent) as DictionaryNode ?? throw new ParseThemeException("Root node was not a dictionary.");
|
||||
|
||||
return new TerminalTheme
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(fileName),
|
||||
Colors = GetColors(node),
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false
|
||||
};
|
||||
}
|
||||
|
||||
private TerminalColors GetColors(DictionaryNode themeDictionary)
|
||||
{
|
||||
return new TerminalColors
|
||||
{
|
||||
Background = GetColorString(themeDictionary[ITermThemeKeys.BackgroundColor]),
|
||||
Foreground = GetColorString(themeDictionary[ITermThemeKeys.ForegroundColor]),
|
||||
Cursor = GetColorString(themeDictionary[ITermThemeKeys.CursorColor]),
|
||||
CursorAccent = GetColorString(themeDictionary[ITermThemeKeys.CursorTextColor]),
|
||||
Selection = GetColorString(themeDictionary[ITermThemeKeys.SelectionColor], Opacity30Percent),
|
||||
Black = GetColorString(themeDictionary[ITermThemeKeys.Ansi0Color]),
|
||||
Red = GetColorString(themeDictionary[ITermThemeKeys.Ansi1Color]),
|
||||
Green = GetColorString(themeDictionary[ITermThemeKeys.Ansi2Color]),
|
||||
Yellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi3Color]),
|
||||
Blue = GetColorString(themeDictionary[ITermThemeKeys.Ansi4Color]),
|
||||
Magenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi5Color]),
|
||||
Cyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi6Color]),
|
||||
White = GetColorString(themeDictionary[ITermThemeKeys.Ansi7Color]),
|
||||
BrightBlack = GetColorString(themeDictionary[ITermThemeKeys.Ansi8Color]),
|
||||
BrightRed = GetColorString(themeDictionary[ITermThemeKeys.Ansi9Color]),
|
||||
BrightGreen = GetColorString(themeDictionary[ITermThemeKeys.Ansi10Color]),
|
||||
BrightYellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi11Color]),
|
||||
BrightBlue = GetColorString(themeDictionary[ITermThemeKeys.Ansi12Color]),
|
||||
BrightMagenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi13Color]),
|
||||
BrightCyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi14Color]),
|
||||
BrightWhite = GetColorString(themeDictionary[ITermThemeKeys.Ansi15Color]),
|
||||
};
|
||||
}
|
||||
|
||||
private string GetColorString(PNode colorNode, byte alpha = Byte.MaxValue)
|
||||
{
|
||||
var dictionaryNode = colorNode as DictionaryNode ?? throw new ParseThemeException("Color node was not a dictionary");
|
||||
|
||||
var red = dictionaryNode[ITermThemeColorKeys.RedComponent] as RealNode ?? throw new ParseThemeException("Red node value was not a real number");
|
||||
var green = dictionaryNode[ITermThemeColorKeys.GreenComponent] as RealNode ?? throw new ParseThemeException("Green node value was not a real number");
|
||||
var blue = dictionaryNode[ITermThemeColorKeys.BlueComponent] as RealNode ?? throw new ParseThemeException("Blue node value was not a real number");
|
||||
|
||||
var color = $"#{alpha:X2}{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
|
||||
|
||||
if (alpha == byte.MaxValue)
|
||||
{
|
||||
return $"#{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"rgba({GetByteValue(red):G}, {GetByteValue(green):G}, {GetByteValue(blue):G}, {ToDoubleString(alpha)})";
|
||||
}
|
||||
}
|
||||
|
||||
private byte GetByteValue(RealNode node)
|
||||
{
|
||||
var doubleValue = node.Value * Byte.MaxValue;
|
||||
|
||||
return Convert.ToByte(doubleValue);
|
||||
}
|
||||
|
||||
private static string ToDoubleString(byte alpha)
|
||||
{
|
||||
return (alpha / 256.0).ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class KeyboardCommandService : IKeyboardCommandService
|
||||
{
|
||||
private readonly Dictionary<Command, Action> _commandHandlers = new Dictionary<Command, Action>();
|
||||
|
||||
public void RegisterCommandHandler(Command command, Action handler)
|
||||
{
|
||||
if (_commandHandlers.ContainsKey(command))
|
||||
{
|
||||
throw new InvalidOperationException("command already registered");
|
||||
}
|
||||
|
||||
_commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public void SendCommand(Command command)
|
||||
{
|
||||
if (_commandHandlers.TryGetValue(command, out Action handler))
|
||||
{
|
||||
handler.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// already registered by SystemTray's ToggleWindowService
|
||||
if (command == Command.ToggleWindow)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"No handler registered for command: {command}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class SettingsService : ISettingsService
|
||||
{
|
||||
public const string CurrentThemeKey = "CurrentTheme";
|
||||
public const string DefaultShellProfileKey = "DefaultShellProfile";
|
||||
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IApplicationDataContainer _localSettings;
|
||||
private readonly IApplicationDataContainer _themes;
|
||||
private readonly IApplicationDataContainer _keyBindings;
|
||||
private readonly IApplicationDataContainer _shellProfiles;
|
||||
private readonly IApplicationDataContainer _roamingSettings;
|
||||
|
||||
public event EventHandler<Guid> CurrentThemeChanged;
|
||||
public event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
public event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||
public event EventHandler KeyBindingsChanged;
|
||||
|
||||
public SettingsService(IDefaultValueProvider defaultValueProvider, ApplicationDataContainers containers)
|
||||
{
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_localSettings = containers.LocalSettings;
|
||||
_roamingSettings = containers.RoamingSettings;
|
||||
|
||||
_themes = containers.Themes;
|
||||
_keyBindings = containers.KeyBindings;
|
||||
_shellProfiles = containers.ShellProfiles;
|
||||
|
||||
foreach (var theme in _defaultValueProvider.GetPreInstalledThemes())
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
}
|
||||
|
||||
foreach (var shellProfile in _defaultValueProvider.GetPreinstalledShellProfiles())
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalTheme GetCurrentTheme()
|
||||
{
|
||||
var id = GetCurrentThemeId();
|
||||
return GetTheme(id);
|
||||
}
|
||||
|
||||
public Guid GetCurrentThemeId()
|
||||
{
|
||||
if (_roamingSettings.TryGetValue(CurrentThemeKey, out object value))
|
||||
{
|
||||
return (Guid)value;
|
||||
}
|
||||
return _defaultValueProvider.GetDefaultThemeId();
|
||||
}
|
||||
|
||||
public void SaveCurrentThemeId(Guid id)
|
||||
{
|
||||
_roamingSettings.SetValue(CurrentThemeKey, id);
|
||||
|
||||
CurrentThemeChanged?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public void SaveTheme(TerminalTheme theme)
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
|
||||
if (theme.Id == GetCurrentThemeId())
|
||||
{
|
||||
CurrentThemeChanged?.Invoke(this, theme.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteTheme(Guid id)
|
||||
{
|
||||
_themes.Delete(id.ToString());
|
||||
}
|
||||
|
||||
public IEnumerable<TerminalTheme> GetThemes()
|
||||
{
|
||||
return _themes.GetAll().Select(x => JsonConvert.DeserializeObject<TerminalTheme>((string)x)).ToList();
|
||||
}
|
||||
|
||||
public TerminalTheme GetTheme(Guid id)
|
||||
{
|
||||
return _themes.ReadValueFromJson(id.ToString(), default(TerminalTheme));
|
||||
}
|
||||
|
||||
public TerminalOptions GetTerminalOptions()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(TerminalOptions), _defaultValueProvider.GetDefaultTerminalOptions());
|
||||
}
|
||||
|
||||
public void SaveTerminalOptions(TerminalOptions terminalOptions)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
||||
TerminalOptionsChanged?.Invoke(this, terminalOptions);
|
||||
}
|
||||
|
||||
public ApplicationSettings GetApplicationSettings()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(ApplicationSettings), _defaultValueProvider.GetDefaultApplicationSettings());
|
||||
}
|
||||
|
||||
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
|
||||
ApplicationSettingsChanged?.Invoke(this, applicationSettings);
|
||||
}
|
||||
|
||||
public IDictionary<Command, ICollection<KeyBinding>> GetKeyBindings()
|
||||
{
|
||||
var keyBindings = new Dictionary<Command, ICollection<KeyBinding>>();
|
||||
foreach (var value in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
var command = (Command)value;
|
||||
keyBindings.Add(command, _keyBindings.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null) ?? _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||
}
|
||||
|
||||
return keyBindings;
|
||||
}
|
||||
|
||||
public void SaveKeyBindings(Command command, ICollection<KeyBinding> keyBindings)
|
||||
{
|
||||
_keyBindings.WriteValueAsJson(command.ToString(), keyBindings);
|
||||
_roamingSettings.WriteValueAsJson(nameof(KeyBindings), keyBindings);
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void ResetKeyBindings()
|
||||
{
|
||||
foreach (var value in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
var command = (Command)value;
|
||||
_keyBindings.WriteValueAsJson(command.ToString(), _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||
}
|
||||
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
public Guid GetDefaultShellProfileId()
|
||||
{
|
||||
if (_roamingSettings.TryGetValue(DefaultShellProfileKey, out object value))
|
||||
{
|
||||
return (Guid)value;
|
||||
}
|
||||
return _defaultValueProvider.GetDefaultShellProfileId();
|
||||
}
|
||||
|
||||
public ShellProfile GetDefaultShellProfile()
|
||||
{
|
||||
var id = GetDefaultShellProfileId();
|
||||
return _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
|
||||
}
|
||||
|
||||
public void SaveDefaultShellProfileId(Guid id)
|
||||
{
|
||||
_roamingSettings.SetValue(DefaultShellProfileKey, id);
|
||||
}
|
||||
|
||||
public IEnumerable<ShellProfile> GetShellProfiles()
|
||||
{
|
||||
return _shellProfiles.GetAll().Select(x => JsonConvert.DeserializeObject<ShellProfile>((string)x)).ToList();
|
||||
}
|
||||
|
||||
public void SaveShellProfile(ShellProfile shellProfile)
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
}
|
||||
|
||||
public void DeleteShellProfile(Guid id)
|
||||
{
|
||||
_shellProfiles.Delete(id.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class ThemeParserFactory : IThemeParserFactory
|
||||
{
|
||||
private IEnumerable<IThemeParser> _parsers;
|
||||
|
||||
public IEnumerable<string> SupportedFileTypes { get; }
|
||||
|
||||
public ThemeParserFactory(IEnumerable<IThemeParser> parsers)
|
||||
{
|
||||
_parsers = parsers;
|
||||
SupportedFileTypes = _parsers.SelectMany(p => p.SupportedFileTypes);
|
||||
}
|
||||
|
||||
public IThemeParser GetParser(string fileType)
|
||||
{
|
||||
return _parsers.FirstOrDefault(p => p.SupportedFileTypes.Contains(fileType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using FluentTerminal.Models.Requests;
|
||||
using FluentTerminal.Models.Responses;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class TrayProcessCommunicationService : ITrayProcessCommunicationService
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private IAppServiceConnection _appServiceConnection;
|
||||
|
||||
public TrayProcessCommunicationService(ISettingsService settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
|
||||
public async Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellProfile shellProfile)
|
||||
{
|
||||
var request = new CreateTerminalRequest
|
||||
{
|
||||
Size = size,
|
||||
Profile = shellProfile
|
||||
};
|
||||
|
||||
var message = new Dictionary<string, string>
|
||||
{
|
||||
{ MessageKeys.Type, MessageTypes.CreateTerminalRequest },
|
||||
{ MessageKeys.Content, JsonConvert.SerializeObject(request) }
|
||||
};
|
||||
|
||||
var responseMessage = await _appServiceConnection.SendMessageAsync(message);
|
||||
|
||||
return JsonConvert.DeserializeObject<CreateTerminalResponse>(responseMessage[MessageKeys.Content]);
|
||||
}
|
||||
|
||||
public Task Initialize(IAppServiceConnection appServiceConnection)
|
||||
{
|
||||
_appServiceConnection = appServiceConnection;
|
||||
return UpdateToggleWindowKeyBindings();
|
||||
}
|
||||
|
||||
public Task ResizeTerminal(int id, TerminalSize size)
|
||||
{
|
||||
var request = new ResizeTerminalRequest
|
||||
{
|
||||
TerminalId = id,
|
||||
NewSize = size
|
||||
};
|
||||
|
||||
var message = new Dictionary<string, string>
|
||||
{
|
||||
{ MessageKeys.Type, MessageTypes.ResizeTerminalRequest },
|
||||
{ MessageKeys.Content, JsonConvert.SerializeObject(request) }
|
||||
};
|
||||
|
||||
return _appServiceConnection.SendMessageAsync(message);
|
||||
}
|
||||
|
||||
public Task UpdateToggleWindowKeyBindings()
|
||||
{
|
||||
var keyBindings = _settingsService.GetKeyBindings()[Command.ToggleWindow];
|
||||
|
||||
var request = new SetToggleWindowKeyBindingsRequest
|
||||
{
|
||||
KeyBindings = keyBindings
|
||||
};
|
||||
|
||||
var message = new Dictionary<string, string>
|
||||
{
|
||||
{ MessageKeys.Type, MessageTypes.SetToggleWindowKeyBindingsRequest },
|
||||
{ MessageKeys.Content, JsonConvert.SerializeObject(request) }
|
||||
};
|
||||
|
||||
return _appServiceConnection.SendMessageAsync(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FluentTerminal.App.Services.Utilities
|
||||
{
|
||||
public static class EnumHelper
|
||||
{
|
||||
public static string GetEnumDescription(Enum value)
|
||||
{
|
||||
var fieldInfo = value.GetType().GetField(value.ToString());
|
||||
|
||||
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
if (attributes?.Length > 0)
|
||||
{
|
||||
return attributes[0].Description;
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using FluentTerminal.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FluentTerminal.App.Services.Utilities
|
||||
{
|
||||
public class TerminalThemeContractResolver : DefaultContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||
|
||||
if (property.DeclaringType == typeof(TerminalTheme) && property.PropertyName == nameof(TerminalTheme.PreInstalled))
|
||||
{
|
||||
property.ShouldSerialize = (_) => false;
|
||||
}
|
||||
|
||||
if (property.DeclaringType == typeof(TerminalTheme) && property.PropertyName == nameof(TerminalTheme.Id))
|
||||
{
|
||||
property.ShouldSerialize = (_) => false;
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Fleck" Version="1.0.3" />
|
||||
<PackageReference Include="MvvmLightLibs" Version="5.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
|
||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,271 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.EventArgs;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IKeyboardCommandService _keyboardCommandService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private ApplicationSettings _applicationSettings;
|
||||
private string _background;
|
||||
private double _backgroundOpacity;
|
||||
private int _nextTerminalId;
|
||||
private TerminalViewModel _selectedTerminal;
|
||||
private string _title;
|
||||
private readonly IApplicationView _applicationView;
|
||||
private readonly IDispatcherTimer _dispatcherTimer;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
|
||||
public event EventHandler Closed;
|
||||
public event EventHandler NewWindowRequested;
|
||||
public event EventHandler ShowSettingsRequested;
|
||||
|
||||
public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService,
|
||||
IApplicationView applicationView, IDispatcherTimer dispatcherTimer, IClipboardService clipboardService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
|
||||
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
_dialogService = dialogService;
|
||||
_applicationView = applicationView;
|
||||
_dispatcherTimer = dispatcherTimer;
|
||||
_clipboardService = clipboardService;
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NewTab, () => AddTerminal(null, false));
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.ConfigurableNewTab, () => AddTerminal(null, true));
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.CloseTab, CloseCurrentTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NextTab, SelectNextTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.PreviousTab, SelectPreviousTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NewWindow, NewWindow);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.ShowSettings, ShowSettings);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.ToggleFullScreen, ToggleFullScreen);
|
||||
var currentTheme = _settingsService.GetCurrentTheme();
|
||||
var options = _settingsService.GetTerminalOptions();
|
||||
Background = currentTheme.Colors.Background;
|
||||
BackgroundOpacity = options.BackgroundOpacity;
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
|
||||
AddTerminalCommand = new RelayCommand(() => AddTerminal(null, false));
|
||||
ShowSettingsCommand = new RelayCommand(ShowSettings);
|
||||
|
||||
Terminals.CollectionChanged += OnTerminalsCollectionChanged;
|
||||
|
||||
Title = "Fluent Terminal";
|
||||
|
||||
_applicationView.CloseRequested += OnCloseRequest;
|
||||
}
|
||||
|
||||
private async void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
BackgroundOpacity = e.BackgroundOpacity;
|
||||
});
|
||||
}
|
||||
|
||||
public RelayCommand AddTerminalCommand { get; }
|
||||
|
||||
public string Background
|
||||
{
|
||||
get => _background;
|
||||
set => Set(ref _background, value);
|
||||
}
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _backgroundOpacity;
|
||||
set => Set(ref _backgroundOpacity, value);
|
||||
}
|
||||
|
||||
public TerminalViewModel SelectedTerminal
|
||||
{
|
||||
get => _selectedTerminal;
|
||||
set
|
||||
{
|
||||
if (SelectedTerminal != null)
|
||||
{
|
||||
_selectedTerminal.IsSelected = false;
|
||||
}
|
||||
if (Set(ref _selectedTerminal, value))
|
||||
{
|
||||
SelectedTerminal?.FocusTerminal();
|
||||
if (SelectedTerminal != null)
|
||||
{
|
||||
SelectedTerminal.IsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand ShowSettingsCommand { get; }
|
||||
|
||||
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
}
|
||||
|
||||
public Task AddTerminal(string startupDirectory, bool showProfileSelection)
|
||||
{
|
||||
return _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
ShellProfile profile = null;
|
||||
if (showProfileSelection)
|
||||
{
|
||||
profile = await _dialogService.ShowProfileSelectionDialogAsync();
|
||||
|
||||
if (profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
profile = _settingsService.GetDefaultShellProfile();
|
||||
}
|
||||
|
||||
var terminal = new TerminalViewModel(GetNextTerminalId(), _settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService,
|
||||
_applicationSettings, startupDirectory, profile, _applicationView, _dispatcherTimer, _clipboardService);
|
||||
terminal.CloseRequested += OnTerminalCloseRequested;
|
||||
terminal.TitleChanged += OnTitleChanged;
|
||||
Terminals.Add(terminal);
|
||||
|
||||
SelectedTerminal = terminal;
|
||||
});
|
||||
}
|
||||
|
||||
public void CloseAllTerminals()
|
||||
{
|
||||
foreach (var terminal in Terminals)
|
||||
{
|
||||
terminal.CloseView();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseCurrentTab()
|
||||
{
|
||||
SelectedTerminal?.CloseCommand.Execute(null);
|
||||
}
|
||||
|
||||
private int GetNextTerminalId()
|
||||
{
|
||||
return _nextTerminalId++;
|
||||
}
|
||||
|
||||
private void NewWindow()
|
||||
{
|
||||
NewWindowRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(() => _applicationSettings = e);
|
||||
}
|
||||
|
||||
private async Task OnCloseRequest(object sender, CancelableEventArgs e)
|
||||
{
|
||||
if (_applicationSettings.ConfirmClosingWindows)
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to close this window?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(false);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
CloseAllTerminals();
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Cancelled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseAllTerminals();
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnCurrentThemeChanged(object sender, Guid e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
var currentTheme = _settingsService.GetTheme(e);
|
||||
Background = currentTheme.Colors.Background;
|
||||
});
|
||||
}
|
||||
|
||||
private void OnTerminalCloseRequested(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is TerminalViewModel terminal)
|
||||
{
|
||||
terminal.CloseView();
|
||||
if (SelectedTerminal == terminal)
|
||||
{
|
||||
SelectedTerminal = Terminals.LastOrDefault(t => t != terminal);
|
||||
}
|
||||
Terminals.Remove(terminal);
|
||||
|
||||
if (Terminals.Count == 0)
|
||||
{
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
_applicationView.TryClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTerminalsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (Terminals.Any())
|
||||
{
|
||||
Title = Terminals.First().Title;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTitleChanged(object sender, string e)
|
||||
{
|
||||
if (sender is TerminalViewModel terminal && Terminals.First() == terminal)
|
||||
{
|
||||
Title = e;
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectNextTab()
|
||||
{
|
||||
var currentIndex = Terminals.IndexOf(SelectedTerminal);
|
||||
var nextIndex = (currentIndex + 1) % Terminals.Count;
|
||||
SelectedTerminal = Terminals[nextIndex];
|
||||
}
|
||||
|
||||
private void SelectPreviousTab()
|
||||
{
|
||||
var currentIndex = Terminals.IndexOf(SelectedTerminal);
|
||||
var previousIndex = (currentIndex - 1 + Terminals.Count) % Terminals.Count;
|
||||
SelectedTerminal = Terminals[previousIndex];
|
||||
}
|
||||
|
||||
private void ShowSettings()
|
||||
{
|
||||
ShowSettingsRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void ToggleFullScreen()
|
||||
{
|
||||
_applicationView.ToggleFullScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class GeneralPageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ApplicationSettings _applicationSettings;
|
||||
private bool _editingNewTerminalLocation;
|
||||
|
||||
public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
|
||||
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
public bool ConfirmClosingTabs
|
||||
{
|
||||
get => _applicationSettings.ConfirmClosingTabs;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.ConfirmClosingTabs != value)
|
||||
{
|
||||
_applicationSettings.ConfirmClosingTabs = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ConfirmClosingWindows
|
||||
{
|
||||
get => _applicationSettings.ConfirmClosingWindows;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.ConfirmClosingWindows != value)
|
||||
{
|
||||
_applicationSettings.ConfirmClosingWindows = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool UnderlineSelectedTab
|
||||
{
|
||||
get => _applicationSettings.UnderlineSelectedTab;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.UnderlineSelectedTab != value)
|
||||
{
|
||||
_applicationSettings.UnderlineSelectedTab = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public NewTerminalLocation NewTerminalLocation
|
||||
{
|
||||
get => _applicationSettings.NewTerminalLocation;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.NewTerminalLocation != value && !_editingNewTerminalLocation)
|
||||
{
|
||||
_editingNewTerminalLocation = true;
|
||||
_applicationSettings.NewTerminalLocation = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(WindowIsSelected));
|
||||
RaisePropertyChanged(nameof(TabIsSelected));
|
||||
_editingNewTerminalLocation = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand RestoreDefaultsCommand { get; }
|
||||
|
||||
public bool TabIsSelected
|
||||
{
|
||||
get => NewTerminalLocation == NewTerminalLocation.Tab;
|
||||
set => NewTerminalLocation = NewTerminalLocation.Tab;
|
||||
}
|
||||
|
||||
public bool WindowIsSelected
|
||||
{
|
||||
get => NewTerminalLocation == NewTerminalLocation.Window;
|
||||
set => NewTerminalLocation = NewTerminalLocation.Window;
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the general settings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(false);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultApplicationSettings();
|
||||
ConfirmClosingWindows = defaults.ConfirmClosingWindows;
|
||||
ConfirmClosingTabs = defaults.ConfirmClosingTabs;
|
||||
UnderlineSelectedTab = defaults.UnderlineSelectedTab;
|
||||
NewTerminalLocation = defaults.NewTerminalLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class KeyBindingViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
|
||||
public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService)
|
||||
{
|
||||
Model = keyBinding;
|
||||
_dialogService = dialogService;
|
||||
EditCommand = new RelayCommand(async () => await Edit().ConfigureAwait(false));
|
||||
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
public event EventHandler Deleted;
|
||||
|
||||
public event EventHandler Edited;
|
||||
|
||||
public bool Meta
|
||||
{
|
||||
get => Model.Meta;
|
||||
set
|
||||
{
|
||||
if (Model.Meta != value)
|
||||
{
|
||||
Model.Meta = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Alt
|
||||
{
|
||||
get => Model.Alt;
|
||||
set
|
||||
{
|
||||
if (Model.Alt != value)
|
||||
{
|
||||
Model.Alt = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Ctrl
|
||||
{
|
||||
get => Model.Ctrl;
|
||||
set
|
||||
{
|
||||
if (Model.Ctrl != value)
|
||||
{
|
||||
Model.Ctrl = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand DeleteCommand { get; }
|
||||
public RelayCommand EditCommand { get; }
|
||||
|
||||
public int Key
|
||||
{
|
||||
get => Model.Key;
|
||||
set
|
||||
{
|
||||
if (Model.Key != value)
|
||||
{
|
||||
Model.Key = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public KeyBinding Model { get; }
|
||||
|
||||
public bool Shift
|
||||
{
|
||||
get => Model.Shift;
|
||||
set
|
||||
{
|
||||
if (Model.Shift != value)
|
||||
{
|
||||
Model.Shift = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> Edit()
|
||||
{
|
||||
var keyBinding = await _dialogService.ShowCreateKeyBindingDialog().ConfigureAwait(true);
|
||||
|
||||
if (keyBinding != null)
|
||||
{
|
||||
Alt = keyBinding.Alt;
|
||||
Ctrl = keyBinding.Ctrl;
|
||||
Shift = keyBinding.Shift;
|
||||
Meta = keyBinding.Meta;
|
||||
Key = keyBinding.Key;
|
||||
|
||||
Edited?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task Delete()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this keybinding?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Deleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class KeyBindingsPageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private IDictionary<Command, ICollection<KeyBinding>> _keyBindings;
|
||||
|
||||
public KeyBindingsPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ITrayProcessCommunicationService trayProcessCommunicationService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||
AddCommand = new RelayCommand<Command>(async command => await Add(command).ConfigureAwait(false));
|
||||
|
||||
Initialize(_settingsService.GetKeyBindings());
|
||||
}
|
||||
|
||||
public RelayCommand<Command> AddCommand { get; }
|
||||
public ObservableCollection<KeyBindingsViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingsViewModel>();
|
||||
public RelayCommand RestoreDefaultsCommand { get; }
|
||||
|
||||
private async Task Add(Command command)
|
||||
{
|
||||
var keyBinding = KeyBindings.FirstOrDefault(k => k.Command == command);
|
||||
await keyBinding?.Add();
|
||||
}
|
||||
|
||||
private void ClearKeyBindings()
|
||||
{
|
||||
foreach (var keyBinding in KeyBindings)
|
||||
{
|
||||
keyBinding.Edited -= OnEdited;
|
||||
}
|
||||
|
||||
KeyBindings.Clear();
|
||||
}
|
||||
|
||||
private void Initialize(IDictionary<Command, ICollection<KeyBinding>> keyBindings)
|
||||
{
|
||||
_keyBindings = keyBindings;
|
||||
|
||||
ClearKeyBindings();
|
||||
|
||||
foreach (var value in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
var command = (Command)value;
|
||||
var viewModel = new KeyBindingsViewModel(command, _keyBindings[command], _dialogService);
|
||||
viewModel.Edited += OnEdited;
|
||||
KeyBindings.Add(viewModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEdited(Command command, ICollection<KeyBinding> keyBindings)
|
||||
{
|
||||
_settingsService.SaveKeyBindings(command, keyBindings);
|
||||
_trayProcessCommunicationService.UpdateToggleWindowKeyBindings();
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the default keybindings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
_settingsService.ResetKeyBindings();
|
||||
Initialize(_settingsService.GetKeyBindings());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class KeyBindingsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ICollection<KeyBinding> _keyBindings;
|
||||
|
||||
public KeyBindingsViewModel(Command command, ICollection<KeyBinding> keyBindings, IDialogService dialogService)
|
||||
{
|
||||
Command = command;
|
||||
_keyBindings = keyBindings;
|
||||
_dialogService = dialogService;
|
||||
|
||||
CommandName = EnumHelper.GetEnumDescription(command);
|
||||
|
||||
foreach (var binding in keyBindings)
|
||||
{
|
||||
var viewModel = new KeyBindingViewModel(binding, _dialogService);
|
||||
viewModel.Deleted += ViewModel_Deleted;
|
||||
viewModel.Edited += ViewModel_Edited;
|
||||
KeyBindings.Add(viewModel);
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void EditedEvent(Command command, ICollection<KeyBinding> keyBindings);
|
||||
|
||||
public event EditedEvent Edited;
|
||||
|
||||
public Command Command { get; }
|
||||
|
||||
public string CommandName { get; }
|
||||
|
||||
public ObservableCollection<KeyBindingViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingViewModel>();
|
||||
|
||||
public async Task Add()
|
||||
{
|
||||
var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService);
|
||||
|
||||
if (await newKeyBinding.Edit().ConfigureAwait(true))
|
||||
{
|
||||
newKeyBinding.Deleted += ViewModel_Deleted;
|
||||
newKeyBinding.Edited += ViewModel_Edited;
|
||||
KeyBindings.Add(newKeyBinding);
|
||||
_keyBindings.Add(newKeyBinding.Model);
|
||||
Edited?.Invoke(Command, _keyBindings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewModel_Deleted(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is KeyBindingViewModel keyBinding)
|
||||
{
|
||||
_keyBindings.Remove(keyBinding.Model);
|
||||
KeyBindings.Remove(keyBinding);
|
||||
Edited?.Invoke(Command, _keyBindings);
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewModel_Edited(object sender, EventArgs e)
|
||||
{
|
||||
Edited?.Invoke(Command, _keyBindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class ProfilesPageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
private ShellProfileViewModel _selectedShellProfile;
|
||||
|
||||
|
||||
public ProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IFileSystemService fileSystemService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_fileSystemService = fileSystemService;
|
||||
|
||||
CreateShellProfileCommand = new RelayCommand(CreateShellProfile);
|
||||
|
||||
var defaultShellProfileId = _settingsService.GetDefaultShellProfileId();
|
||||
foreach (var shellProfile in _settingsService.GetShellProfiles())
|
||||
{
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, settingsService, dialogService, fileSystemService);
|
||||
viewModel.Deleted += OnShellProfileDeleted;
|
||||
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
||||
|
||||
if (shellProfile.Id == defaultShellProfileId)
|
||||
{
|
||||
viewModel.IsDefault = true;
|
||||
}
|
||||
ShellProfiles.Add(viewModel);
|
||||
}
|
||||
|
||||
SelectedShellProfile = ShellProfiles.First(p => p.IsDefault);
|
||||
}
|
||||
|
||||
private void OnShellProfileSetAsDefault(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is ShellProfileViewModel defaultShellProfile)
|
||||
{
|
||||
_settingsService.SaveDefaultShellProfileId(defaultShellProfile.Id);
|
||||
|
||||
foreach (var shellProfile in ShellProfiles)
|
||||
{
|
||||
shellProfile.IsDefault = shellProfile.Id == defaultShellProfile.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShellProfileDeleted(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is ShellProfileViewModel shellProfile)
|
||||
{
|
||||
if (SelectedShellProfile == shellProfile)
|
||||
{
|
||||
SelectedShellProfile = ShellProfiles.First();
|
||||
}
|
||||
ShellProfiles.Remove(shellProfile);
|
||||
|
||||
if (shellProfile.IsDefault)
|
||||
{
|
||||
ShellProfiles.First().IsDefault = true;
|
||||
_settingsService.SaveDefaultShellProfileId(ShellProfiles.First().Id);
|
||||
}
|
||||
_settingsService.DeleteShellProfile(shellProfile.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand CreateShellProfileCommand { get; }
|
||||
|
||||
private void CreateShellProfile()
|
||||
{
|
||||
var shellProfile = new ShellProfile
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = "New profile"
|
||||
};
|
||||
|
||||
_settingsService.SaveShellProfile(shellProfile);
|
||||
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService);
|
||||
viewModel.EditCommand.Execute(null);
|
||||
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
||||
viewModel.Deleted += OnShellProfileDeleted;
|
||||
ShellProfiles.Add(viewModel);
|
||||
SelectedShellProfile = viewModel;
|
||||
}
|
||||
|
||||
public ShellProfileViewModel SelectedShellProfile
|
||||
{
|
||||
get => _selectedShellProfile;
|
||||
set => Set(ref _selectedShellProfile, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<ShellProfileViewModel> ShellProfiles { get; } = new ObservableCollection<ShellProfileViewModel>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class TerminalPageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly TerminalOptions _terminalOptions;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private bool _isEditingCursorStyle;
|
||||
private bool _isEditingScrollBarStyle;
|
||||
|
||||
public bool BarIsSelected
|
||||
{
|
||||
get => CursorStyle == CursorStyle.Bar;
|
||||
set => CursorStyle = CursorStyle.Bar;
|
||||
}
|
||||
|
||||
public bool BlockIsSelected
|
||||
{
|
||||
get => CursorStyle == CursorStyle.Block;
|
||||
set => CursorStyle = CursorStyle.Block;
|
||||
}
|
||||
|
||||
public bool UnderlineIsSelected
|
||||
{
|
||||
get => CursorStyle == CursorStyle.Underline;
|
||||
set => CursorStyle = CursorStyle.Underline;
|
||||
}
|
||||
|
||||
public bool HiddenIsSelected
|
||||
{
|
||||
get => ScrollBarStyle == ScrollBarStyle.Hidden;
|
||||
set => ScrollBarStyle = ScrollBarStyle.Hidden;
|
||||
}
|
||||
|
||||
public bool AutoHidingIsSelected
|
||||
{
|
||||
get => ScrollBarStyle == ScrollBarStyle.AutoHiding;
|
||||
set => ScrollBarStyle = ScrollBarStyle.AutoHiding;
|
||||
}
|
||||
|
||||
public bool VisibleIsSelected
|
||||
{
|
||||
get => ScrollBarStyle == ScrollBarStyle.Visible;
|
||||
set => ScrollBarStyle = ScrollBarStyle.Visible;
|
||||
}
|
||||
|
||||
public bool CursorBlink
|
||||
{
|
||||
get => _terminalOptions.CursorBlink;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.CursorBlink != value)
|
||||
{
|
||||
_terminalOptions.CursorBlink = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string FontFamily
|
||||
{
|
||||
get => _terminalOptions.FontFamily;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.FontFamily != value)
|
||||
{
|
||||
_terminalOptions.FontFamily = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _terminalOptions.BackgroundOpacity;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.BackgroundOpacity != value)
|
||||
{
|
||||
_terminalOptions.BackgroundOpacity = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ScrollBackLimit
|
||||
{
|
||||
get => _terminalOptions.ScrollBackLimit.ToString();
|
||||
set
|
||||
{
|
||||
var oldValue = _terminalOptions.ScrollBackLimit;
|
||||
if (uint.TryParse(value, out uint intValue))
|
||||
{
|
||||
if (intValue == uint.MinValue)
|
||||
{
|
||||
intValue = uint.MaxValue;
|
||||
}
|
||||
if (_terminalOptions.ScrollBackLimit != intValue)
|
||||
{
|
||||
_terminalOptions.ScrollBackLimit = intValue;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> Fonts { get; }
|
||||
|
||||
public int FontSize
|
||||
{
|
||||
get => _terminalOptions.FontSize;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.FontSize != value)
|
||||
{
|
||||
_terminalOptions.FontSize = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<int> Sizes { get; }
|
||||
|
||||
public RelayCommand RestoreDefaultsCommand { get; }
|
||||
|
||||
private CursorStyle CursorStyle
|
||||
{
|
||||
get => _terminalOptions.CursorStyle;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.CursorStyle != value && !_isEditingCursorStyle)
|
||||
{
|
||||
_isEditingCursorStyle = true;
|
||||
_terminalOptions.CursorStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(BlockIsSelected));
|
||||
RaisePropertyChanged(nameof(BarIsSelected));
|
||||
RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||
_isEditingCursorStyle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ScrollBarStyle ScrollBarStyle
|
||||
{
|
||||
get => _terminalOptions.ScrollBarStyle;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.ScrollBarStyle != value && !_isEditingScrollBarStyle)
|
||||
{
|
||||
_isEditingScrollBarStyle = true;
|
||||
_terminalOptions.ScrollBarStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(HiddenIsSelected));
|
||||
RaisePropertyChanged(nameof(AutoHidingIsSelected));
|
||||
RaisePropertyChanged(nameof(VisibleIsSelected));
|
||||
_isEditingScrollBarStyle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the default terminal options?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultTerminalOptions();
|
||||
CursorBlink = defaults.CursorBlink;
|
||||
CursorStyle = defaults.CursorStyle;
|
||||
ScrollBarStyle = defaults.ScrollBarStyle;
|
||||
FontFamily = defaults.FontFamily;
|
||||
FontSize = defaults.FontSize;
|
||||
BackgroundOpacity = defaults.BackgroundOpacity;
|
||||
ScrollBackLimit = defaults.ScrollBackLimit.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ISystemFontService systemFontService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
|
||||
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||
|
||||
Fonts = systemFontService.GetSystemFontFamilies().OrderBy(s => s);
|
||||
Sizes = Enumerable.Range(1, 72);
|
||||
|
||||
_terminalOptions = _settingsService.GetTerminalOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class ThemesPageViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private ThemeViewModel _selectedTheme;
|
||||
private double _backgroundOpacity;
|
||||
private readonly IThemeParserFactory _themeParserFactory;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
|
||||
public event EventHandler<string> SelectedThemeBackgroundColorChanged;
|
||||
|
||||
public ThemesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider,
|
||||
IThemeParserFactory themeParserFactory, IFileSystemService fileSystemService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_themeParserFactory = themeParserFactory;
|
||||
_fileSystemService = fileSystemService;
|
||||
|
||||
CreateThemeCommand = new RelayCommand(CreateTheme);
|
||||
ImportThemeCommand = new RelayCommand(ImportTheme);
|
||||
|
||||
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
|
||||
BackgroundOpacity = _settingsService.GetTerminalOptions().BackgroundOpacity;
|
||||
|
||||
var activeThemeId = _settingsService.GetCurrentThemeId();
|
||||
foreach (var theme in _settingsService.GetThemes())
|
||||
{
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, fileSystemService);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
viewModel.Deleted += OnThemeDeleted;
|
||||
|
||||
if (theme.Id == activeThemeId)
|
||||
{
|
||||
viewModel.IsActive = true;
|
||||
}
|
||||
Themes.Add(viewModel);
|
||||
}
|
||||
|
||||
SelectedTheme = Themes.First(t => t.IsActive);
|
||||
}
|
||||
|
||||
public RelayCommand CreateThemeCommand { get; }
|
||||
public RelayCommand ImportThemeCommand { get; }
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _backgroundOpacity;
|
||||
set => Set(ref _backgroundOpacity, value);
|
||||
}
|
||||
|
||||
public ThemeViewModel SelectedTheme
|
||||
{
|
||||
get => _selectedTheme;
|
||||
set
|
||||
{
|
||||
if (_selectedTheme != null)
|
||||
{
|
||||
_selectedTheme.BackgroundChanged -= OnSelectedThemeBackgroundChanged;
|
||||
}
|
||||
Set(ref _selectedTheme, value);
|
||||
if (value != null)
|
||||
{
|
||||
value.BackgroundChanged += OnSelectedThemeBackgroundChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ThemeViewModel> Themes { get; } = new ObservableCollection<ThemeViewModel>();
|
||||
|
||||
private void CreateTheme()
|
||||
{
|
||||
var defaultTheme = _settingsService.GetTheme(_defaultValueProvider.GetDefaultThemeId());
|
||||
var theme = new TerminalTheme
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = "New Theme",
|
||||
Colors = new TerminalColors(defaultTheme.Colors)
|
||||
};
|
||||
|
||||
_settingsService.SaveTheme(theme);
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
viewModel.Deleted += OnThemeDeleted;
|
||||
Themes.Add(viewModel);
|
||||
SelectedTheme = viewModel;
|
||||
}
|
||||
|
||||
private async void ImportTheme()
|
||||
{
|
||||
var file = await _fileSystemService.OpenFile(_themeParserFactory.SupportedFileTypes).ConfigureAwait(true);
|
||||
if (file != null)
|
||||
{
|
||||
var parser = _themeParserFactory.GetParser(file.FileType);
|
||||
|
||||
if (parser == null)
|
||||
{
|
||||
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", "No suitable parser found", DialogButton.OK).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var theme = await parser.Parse(file.Name, file.Content).ConfigureAwait(true);
|
||||
|
||||
_settingsService.SaveTheme(theme);
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
viewModel.Deleted += OnThemeDeleted;
|
||||
Themes.Add(viewModel);
|
||||
SelectedTheme = viewModel;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", exception.Message, DialogButton.OK).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThemeActivated(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is ThemeViewModel activatedTheme)
|
||||
{
|
||||
_settingsService.SaveCurrentThemeId(activatedTheme.Id);
|
||||
|
||||
foreach (var theme in Themes)
|
||||
{
|
||||
theme.IsActive = theme.Id == activatedTheme.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThemeDeleted(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is ThemeViewModel theme)
|
||||
{
|
||||
if (SelectedTheme == theme)
|
||||
{
|
||||
SelectedTheme = Themes.First();
|
||||
}
|
||||
Themes.Remove(theme);
|
||||
|
||||
if (theme.IsActive)
|
||||
{
|
||||
Themes.First().IsActive = true;
|
||||
_settingsService.SaveCurrentThemeId(Themes.First().Id);
|
||||
}
|
||||
_settingsService.DeleteTheme(theme.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||
{
|
||||
BackgroundOpacity = e.BackgroundOpacity;
|
||||
}
|
||||
|
||||
private void OnSelectedThemeBackgroundChanged(object sender, string e)
|
||||
{
|
||||
SelectedThemeBackgroundColorChanged?.Invoke(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.ViewModels.Settings;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService,
|
||||
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService, IFileSystemService fileSystemService)
|
||||
{
|
||||
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService);
|
||||
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService);
|
||||
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService);
|
||||
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService);
|
||||
}
|
||||
|
||||
public event EventHandler Closed;
|
||||
|
||||
public GeneralPageViewModel General { get; }
|
||||
public KeyBindingsPageViewModel KeyBindings { get; }
|
||||
public ProfilesPageViewModel Shell { get; }
|
||||
public TerminalPageViewModel Terminal { get; }
|
||||
public ThemesPageViewModel Themes { get; }
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class ShellProfileViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ShellProfile _shellProfile;
|
||||
private string _name;
|
||||
private string _arguments;
|
||||
private string _location;
|
||||
private string _workingDirectory;
|
||||
private string _fallbackName;
|
||||
private string _fallbackArguments;
|
||||
private string _fallbackLocation;
|
||||
private string _fallbackWorkingDirectory;
|
||||
private bool _isDefault;
|
||||
private bool _inEditMode;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
|
||||
public ShellProfileViewModel(ShellProfile shellProfile, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService)
|
||||
{
|
||||
_shellProfile = shellProfile;
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_fileSystemService = fileSystemService;
|
||||
|
||||
Id = shellProfile.Id;
|
||||
Name = shellProfile.Name;
|
||||
Arguments = shellProfile.Arguments;
|
||||
Location = shellProfile.Location;
|
||||
WorkingDirectory = shellProfile.WorkingDirectory;
|
||||
|
||||
SetDefaultCommand = new RelayCommand(SetDefault);
|
||||
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false), CanDelete);
|
||||
EditCommand = new RelayCommand(Edit, CanEdit);
|
||||
CancelEditCommand = new RelayCommand(async () => await CancelEdit().ConfigureAwait(false));
|
||||
SaveChangesCommand = new RelayCommand(SaveChanges);
|
||||
BrowseForCustomShellCommand = new RelayCommand(async () => await BrowseForCustomShell().ConfigureAwait(false));
|
||||
BrowseForWorkingDirectoryCommand = new RelayCommand(async () => await BrowseForWorkingDirectory().ConfigureAwait(false));
|
||||
}
|
||||
|
||||
public event EventHandler Deleted;
|
||||
public event EventHandler SetAsDefault;
|
||||
|
||||
public RelayCommand DeleteCommand { get; }
|
||||
public RelayCommand EditCommand { get; }
|
||||
public RelayCommand SaveChangesCommand { get; }
|
||||
public RelayCommand CancelEditCommand { get; }
|
||||
public RelayCommand SetDefaultCommand { get; }
|
||||
public RelayCommand BrowseForCustomShellCommand { get; }
|
||||
public RelayCommand BrowseForWorkingDirectoryCommand { get; }
|
||||
|
||||
public Guid Id { get; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => Set(ref _name, value);
|
||||
}
|
||||
|
||||
public string Arguments
|
||||
{
|
||||
get => _arguments;
|
||||
set => Set(ref _arguments, value);
|
||||
}
|
||||
|
||||
public string Location
|
||||
{
|
||||
get => _location;
|
||||
set => Set(ref _location, value);
|
||||
}
|
||||
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get => _workingDirectory;
|
||||
set => Set(ref _workingDirectory, value);
|
||||
}
|
||||
|
||||
public bool IsDefault
|
||||
{
|
||||
get => _isDefault;
|
||||
set => Set(ref _isDefault, value);
|
||||
}
|
||||
|
||||
public bool InEditMode
|
||||
{
|
||||
get => _inEditMode;
|
||||
set => Set(ref _inEditMode, value);
|
||||
}
|
||||
|
||||
public void SaveChanges()
|
||||
{
|
||||
_shellProfile.Arguments = Arguments;
|
||||
_shellProfile.Location = Location;
|
||||
_shellProfile.Name = Name;
|
||||
_shellProfile.WorkingDirectory = WorkingDirectory;
|
||||
|
||||
_settingsService.SaveShellProfile(_shellProfile);
|
||||
|
||||
InEditMode = false;
|
||||
}
|
||||
|
||||
private async Task CancelEdit()
|
||||
{
|
||||
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 = _fallbackArguments;
|
||||
Location = _fallbackLocation;
|
||||
Name = _fallbackName;
|
||||
WorkingDirectory = _fallbackWorkingDirectory;
|
||||
|
||||
InEditMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Edit()
|
||||
{
|
||||
_fallbackArguments = Arguments;
|
||||
_fallbackLocation = Location;
|
||||
_fallbackName = Name;
|
||||
_fallbackWorkingDirectory = WorkingDirectory;
|
||||
InEditMode = true;
|
||||
}
|
||||
|
||||
private bool CanDelete()
|
||||
{
|
||||
return !_shellProfile.PreInstalled;
|
||||
}
|
||||
|
||||
private bool CanEdit()
|
||||
{
|
||||
return !_shellProfile.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 SetDefault()
|
||||
{
|
||||
SetAsDefault?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
using Fleck;
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class TerminalViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IKeyboardCommandService _keyboardCommandService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private ApplicationSettings _applicationSettings;
|
||||
private string _resizeOverlayContent;
|
||||
private bool _showResizeOverlay;
|
||||
private readonly string _startupDirectory;
|
||||
private int _terminalId;
|
||||
private ITerminalView _terminalView;
|
||||
private string _title;
|
||||
private readonly ShellProfile _shellProfile;
|
||||
private bool _showSearchPanel;
|
||||
private string _searchText;
|
||||
private bool _isSelected;
|
||||
private readonly IApplicationView _applicationView;
|
||||
private readonly IDispatcherTimer _resizeOverlayTimer;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private IWebSocketConnection _webSocket;
|
||||
private NamedPipeClientStream _namedPipe;
|
||||
|
||||
public TerminalViewModel(int id, ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService,
|
||||
IKeyboardCommandService keyboardCommandService, ApplicationSettings applicationSettings, string startupDirectory, ShellProfile shellProfile,
|
||||
IApplicationView applicationView, IDispatcherTimer dispatcherTimer, IClipboardService clipboardService)
|
||||
{
|
||||
Id = id;
|
||||
Title = DefaultTitle;
|
||||
|
||||
_settingsService = settingsService;
|
||||
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
|
||||
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
_settingsService.KeyBindingsChanged += OnKeyBindingsChanged;
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
_dialogService = dialogService;
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
_applicationSettings = applicationSettings;
|
||||
_startupDirectory = startupDirectory;
|
||||
_shellProfile = shellProfile;
|
||||
_applicationView = applicationView;
|
||||
_clipboardService = clipboardService;
|
||||
_resizeOverlayTimer = dispatcherTimer;
|
||||
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
|
||||
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
|
||||
|
||||
CloseCommand = new RelayCommand(async () => await InvokeCloseRequested().ConfigureAwait(false));
|
||||
FindNextCommand = new RelayCommand(async () => await FindNext().ConfigureAwait(false));
|
||||
FindPreviousCommand = new RelayCommand(async () => await FindPrevious().ConfigureAwait(false));
|
||||
CloseSearchPanelCommand = new RelayCommand(CloseSearchPanel);
|
||||
|
||||
}
|
||||
|
||||
private async void OnKeyBindingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
var keyBindings = _settingsService.GetKeyBindings();
|
||||
await _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
await _terminalView.ChangeKeyBindings(FlattenKeyBindings(keyBindings)).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
public event EventHandler CloseRequested;
|
||||
|
||||
public event EventHandler<string> TitleChanged;
|
||||
|
||||
public RelayCommand CloseCommand { get; }
|
||||
|
||||
public RelayCommand FindPreviousCommand { get; }
|
||||
public RelayCommand FindNextCommand { get; }
|
||||
public RelayCommand CloseSearchPanelCommand { get; }
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
set
|
||||
{
|
||||
if (Set(ref _isSelected, value))
|
||||
{
|
||||
RaisePropertyChanged(nameof(IsUnderlined));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUnderlined => IsSelected && _applicationSettings.UnderlineSelectedTab;
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
public string DefaultTitle { get; private set; } = "Fluent Terminal";
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set => Set(ref _searchText, value);
|
||||
}
|
||||
|
||||
public bool ShowSearchPanel
|
||||
{
|
||||
get => _showSearchPanel;
|
||||
set => Set(ref _showSearchPanel, value);
|
||||
}
|
||||
|
||||
public string ResizeOverlayContent
|
||||
{
|
||||
get => _resizeOverlayContent;
|
||||
set => Set(ref _resizeOverlayContent, value);
|
||||
}
|
||||
|
||||
public bool ShowResizeOverlay
|
||||
{
|
||||
get => _showResizeOverlay;
|
||||
set
|
||||
{
|
||||
Set(ref _showResizeOverlay, value);
|
||||
if (value)
|
||||
{
|
||||
if (_resizeOverlayTimer.IsEnabled)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
}
|
||||
_resizeOverlayTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
value = string.IsNullOrWhiteSpace(value) ? DefaultTitle : value;
|
||||
|
||||
if (Set(ref _title, value))
|
||||
{
|
||||
TitleChanged?.Invoke(this, Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseView()
|
||||
{
|
||||
_terminalView.Close();
|
||||
}
|
||||
|
||||
public Task FocusTerminal()
|
||||
{
|
||||
return _terminalView?.FocusTerminal();
|
||||
}
|
||||
|
||||
public async Task OnViewIsReady(ITerminalView terminalView)
|
||||
{
|
||||
_terminalView = terminalView;
|
||||
|
||||
var options = _settingsService.GetTerminalOptions();
|
||||
var theme = _settingsService.GetCurrentTheme();
|
||||
var keyBindings = _settingsService.GetKeyBindings();
|
||||
|
||||
var size = await _terminalView.CreateTerminal(options, theme.Colors, FlattenKeyBindings(keyBindings)).ConfigureAwait(true);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_startupDirectory))
|
||||
{
|
||||
_shellProfile.WorkingDirectory = _startupDirectory;
|
||||
}
|
||||
|
||||
var response = await _trayProcessCommunicationService.CreateTerminal(size, _shellProfile).ConfigureAwait(true);
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
_terminalId = response.Id;
|
||||
_terminalView.TerminalSizeChanged += OnTerminalSizeChanged;
|
||||
_terminalView.TerminalTitleChanged += OnTerminalTitleChanged;
|
||||
_terminalView.KeyboardCommandReceived += OnKeyboardCommandReceived;
|
||||
|
||||
DefaultTitle = response.ShellExecutableName;
|
||||
Title = DefaultTitle;
|
||||
|
||||
_namedPipe = new NamedPipeClientStream(".", response.PipeName, PipeDirection.InOut, PipeOptions.None);
|
||||
if (!_namedPipe.IsConnected)
|
||||
{
|
||||
_namedPipe.Connect();
|
||||
}
|
||||
|
||||
var webSocketUrl = "ws://127.0.0.1:" + response.Port;
|
||||
var webSocketServer = new WebSocketServer(webSocketUrl);
|
||||
webSocketServer.Start(socket =>
|
||||
{
|
||||
_webSocket = socket;
|
||||
socket.OnOpen = () =>
|
||||
{
|
||||
Console.WriteLine("open");
|
||||
//_connectedEvent.Set();
|
||||
};
|
||||
socket.OnClose = () =>
|
||||
{
|
||||
Console.WriteLine("closing");
|
||||
_namedPipe.Close();
|
||||
};
|
||||
socket.OnMessage = message =>
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(message);
|
||||
_namedPipe.Write(bytes, 0, bytes.Length);
|
||||
};
|
||||
});
|
||||
|
||||
ListenToNamedPipe();
|
||||
|
||||
await _terminalView.ConnectToSocket(webSocketUrl).ConfigureAwait(true);
|
||||
Initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _dialogService.ShowMessageDialogAsnyc("Error", response.Error, DialogButton.OK).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
await FocusTerminal().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private void ListenToNamedPipe()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
var reader = new StreamReader(_namedPipe);
|
||||
do
|
||||
{
|
||||
var offset = 0;
|
||||
var buffer = new char[1024];
|
||||
var readChars = await reader.ReadAsync(buffer, offset, buffer.Length - offset);
|
||||
if (readChars > 0)
|
||||
{
|
||||
await _webSocket.Send(new String(buffer));
|
||||
}
|
||||
}
|
||||
while (!reader.EndOfStream);
|
||||
_webSocket.Close();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task InvokeCloseRequested()
|
||||
{
|
||||
if (_applicationSettings.ConfirmClosingTabs)
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to close this tab?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CloseRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
_applicationSettings = e;
|
||||
RaisePropertyChanged(nameof(IsUnderlined));
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnCurrentThemeChanged(object sender, Guid e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
var currentTheme = _settingsService.GetTheme(e);
|
||||
await _terminalView.ChangeTheme(currentTheme.Colors).ConfigureAwait(true);
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnKeyboardCommandReceived(object sender, Command e)
|
||||
{
|
||||
if (e == Command.Copy)
|
||||
{
|
||||
var selection = await _terminalView.GetSelection().ConfigureAwait(true);
|
||||
_clipboardService.SetText(selection);
|
||||
}
|
||||
else if (e == Command.Paste)
|
||||
{
|
||||
var content = await _clipboardService.GetText().ConfigureAwait(true);
|
||||
if (content != null)
|
||||
{
|
||||
await _terminalView.Write(content).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
else if (e == Command.Search)
|
||||
{
|
||||
ShowSearchPanel = !ShowSearchPanel;
|
||||
if (ShowSearchPanel)
|
||||
{
|
||||
_terminalView.FocusSearchTextBox();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyboardCommandService.SendCommand(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnResizeOverlayTimerFinished(object sender, object e)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
ShowResizeOverlay = false;
|
||||
}
|
||||
|
||||
private async void OnTerminalOptionsChanged(object sender, TerminalOptions e)
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
await _terminalView.ChangeOptions(e).ConfigureAwait(true);
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnTerminalSizeChanged(object sender, TerminalSize e)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ResizeOverlayContent = $"{e.Columns} x {e.Rows}";
|
||||
ShowResizeOverlay = true;
|
||||
await _trayProcessCommunicationService.ResizeTerminal(_terminalId, e).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private void OnTerminalTitleChanged(object sender, string e)
|
||||
{
|
||||
Title = e;
|
||||
}
|
||||
|
||||
private IEnumerable<KeyBinding> FlattenKeyBindings(IDictionary<Command, ICollection<KeyBinding>> keyBindings)
|
||||
{
|
||||
return keyBindings.Values.SelectMany(k => k);
|
||||
}
|
||||
|
||||
private Task FindNext()
|
||||
{
|
||||
return _terminalView.FindNext(SearchText);
|
||||
}
|
||||
|
||||
private Task FindPrevious()
|
||||
{
|
||||
return _terminalView.FindPrevious(SearchText);
|
||||
}
|
||||
|
||||
private void CloseSearchPanel()
|
||||
{
|
||||
SearchText = string.Empty;
|
||||
ShowSearchPanel = false;
|
||||
_terminalView.FocusTerminal();
|
||||
}
|
||||
|
||||
private const int FirstDynamicPort = 49151;
|
||||
|
||||
public static int? GetAvailablePort()
|
||||
{
|
||||
var usedPorts = new List<int>();
|
||||
|
||||
var properties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
|
||||
var connections = properties.GetActiveTcpConnections();
|
||||
usedPorts.AddRange(connections.Where(c => c.LocalEndPoint.Port >= FirstDynamicPort).Select(c => c.LocalEndPoint.Port));
|
||||
|
||||
var endPoints = properties.GetActiveTcpListeners();
|
||||
usedPorts.AddRange(endPoints.Where(e => e.Port >= FirstDynamicPort).Select(e => e.Port));
|
||||
|
||||
endPoints = properties.GetActiveUdpListeners();
|
||||
usedPorts.AddRange(endPoints.Where(e => e.Port >= FirstDynamicPort).Select(e => e.Port));
|
||||
|
||||
usedPorts.Sort();
|
||||
|
||||
for (var i = FirstDynamicPort; i < UInt16.MaxValue; i++)
|
||||
{
|
||||
if (!usedPorts.Contains(i))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class ThemeViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private string _author;
|
||||
private string _background;
|
||||
private double _backgroundOpacity;
|
||||
private string _black;
|
||||
private string _blue;
|
||||
private string _brightBlack;
|
||||
private string _brightBlue;
|
||||
private string _brightCyan;
|
||||
private string _brightGreen;
|
||||
private string _brightMagenta;
|
||||
private string _brightRed;
|
||||
private string _brightWhite;
|
||||
private string _brightYellow;
|
||||
private string _cursor;
|
||||
private string _cursorAccent;
|
||||
private string _cyan;
|
||||
private string _fallBackAuthor;
|
||||
private TerminalColors _fallBackColors;
|
||||
private string _fallBackName;
|
||||
private string _foreground;
|
||||
private string _green;
|
||||
private bool _inEditMode;
|
||||
private bool _isActive;
|
||||
private string _magenta;
|
||||
private string _name;
|
||||
private string _red;
|
||||
private string _selection;
|
||||
private readonly TerminalTheme _theme;
|
||||
private string _white;
|
||||
private string _yellow;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
|
||||
public event EventHandler<string> BackgroundChanged;
|
||||
|
||||
public ThemeViewModel(TerminalTheme theme, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService)
|
||||
{
|
||||
_theme = theme;
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_fileSystemService = fileSystemService;
|
||||
|
||||
Name = _theme.Name;
|
||||
Author = _theme.Author;
|
||||
Id = _theme.Id;
|
||||
|
||||
Black = _theme.Colors.Black;
|
||||
Red = _theme.Colors.Red;
|
||||
Green = _theme.Colors.Green;
|
||||
Yellow = _theme.Colors.Yellow;
|
||||
Blue = _theme.Colors.Blue;
|
||||
Magenta = _theme.Colors.Magenta;
|
||||
Cyan = _theme.Colors.Cyan;
|
||||
White = _theme.Colors.White;
|
||||
|
||||
BrightBlack = _theme.Colors.BrightBlack;
|
||||
BrightRed = _theme.Colors.BrightRed;
|
||||
BrightGreen = _theme.Colors.BrightGreen;
|
||||
BrightYellow = _theme.Colors.BrightYellow;
|
||||
BrightBlue = _theme.Colors.BrightBlue;
|
||||
BrightMagenta = _theme.Colors.BrightMagenta;
|
||||
BrightCyan = _theme.Colors.BrightCyan;
|
||||
BrightWhite = _theme.Colors.BrightWhite;
|
||||
|
||||
Background = _theme.Colors.Background;
|
||||
Foreground = _theme.Colors.Foreground;
|
||||
Cursor = _theme.Colors.Cursor;
|
||||
CursorAccent = _theme.Colors.CursorAccent;
|
||||
Selection = _theme.Colors.Selection;
|
||||
|
||||
SetActiveCommand = new RelayCommand(SetActive);
|
||||
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false), NotPreInstalled);
|
||||
EditCommand = new RelayCommand(Edit, NotPreInstalled);
|
||||
CancelEditCommand = new RelayCommand(async () => await CancelEdit().ConfigureAwait(false));
|
||||
SaveChangesCommand = new RelayCommand(SaveChanges);
|
||||
ExportCommand = new RelayCommand(async () => await Export().ConfigureAwait(false), NotPreInstalled);
|
||||
}
|
||||
|
||||
public event EventHandler Activated;
|
||||
|
||||
public event EventHandler Deleted;
|
||||
|
||||
public string Author
|
||||
{
|
||||
get => _author;
|
||||
set => Set(ref _author, value);
|
||||
}
|
||||
|
||||
public string Background
|
||||
{
|
||||
get => _background;
|
||||
set
|
||||
{
|
||||
Set(ref _background, value);
|
||||
BackgroundChanged?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _backgroundOpacity;
|
||||
set => Set(ref _backgroundOpacity, value);
|
||||
}
|
||||
|
||||
public string Black
|
||||
{
|
||||
get => _black;
|
||||
set => Set(ref _black, value);
|
||||
}
|
||||
|
||||
public string Blue
|
||||
{
|
||||
get => _blue;
|
||||
set => Set(ref _blue, value);
|
||||
}
|
||||
|
||||
public string BrightBlack
|
||||
{
|
||||
get => _brightBlack;
|
||||
set => Set(ref _brightBlack, value);
|
||||
}
|
||||
|
||||
public string BrightBlue
|
||||
{
|
||||
get => _brightBlue;
|
||||
set => Set(ref _brightBlue, value);
|
||||
}
|
||||
|
||||
public string BrightCyan
|
||||
{
|
||||
get => _brightCyan;
|
||||
set => Set(ref _brightCyan, value);
|
||||
}
|
||||
|
||||
public string BrightGreen
|
||||
{
|
||||
get => _brightGreen;
|
||||
set => Set(ref _brightGreen, value);
|
||||
}
|
||||
|
||||
public string BrightMagenta
|
||||
{
|
||||
get => _brightMagenta;
|
||||
set => Set(ref _brightMagenta, value);
|
||||
}
|
||||
|
||||
public string BrightRed
|
||||
{
|
||||
get => _brightRed;
|
||||
set => Set(ref _brightRed, value);
|
||||
}
|
||||
|
||||
public string BrightWhite
|
||||
{
|
||||
get => _brightWhite;
|
||||
set => Set(ref _brightWhite, value);
|
||||
}
|
||||
|
||||
public string BrightYellow
|
||||
{
|
||||
get => _brightYellow;
|
||||
set => Set(ref _brightYellow, value);
|
||||
}
|
||||
|
||||
public RelayCommand CancelEditCommand { get; }
|
||||
|
||||
public string Cursor
|
||||
{
|
||||
get => _cursor;
|
||||
set => Set(ref _cursor, value);
|
||||
}
|
||||
|
||||
public string CursorAccent
|
||||
{
|
||||
get => _cursorAccent;
|
||||
set => Set(ref _cursorAccent, value);
|
||||
}
|
||||
|
||||
public string Cyan
|
||||
{
|
||||
get => _cyan;
|
||||
set => Set(ref _cyan, value);
|
||||
}
|
||||
|
||||
public RelayCommand DeleteCommand { get; }
|
||||
public RelayCommand EditCommand { get; }
|
||||
public RelayCommand ExportCommand { get; }
|
||||
|
||||
public string Foreground
|
||||
{
|
||||
get => _foreground;
|
||||
set => Set(ref _foreground, value);
|
||||
}
|
||||
|
||||
public string Green
|
||||
{
|
||||
get => _green;
|
||||
set => Set(ref _green, value);
|
||||
}
|
||||
|
||||
public Guid Id { get; }
|
||||
|
||||
public bool InEditMode
|
||||
{
|
||||
get => _inEditMode;
|
||||
set => Set(ref _inEditMode, value);
|
||||
}
|
||||
|
||||
public bool IsActive
|
||||
{
|
||||
get => _isActive;
|
||||
set => Set(ref _isActive, value);
|
||||
}
|
||||
|
||||
public string Magenta
|
||||
{
|
||||
get => _magenta;
|
||||
set => Set(ref _magenta, value);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => Set(ref _name, value);
|
||||
}
|
||||
|
||||
public string Red
|
||||
{
|
||||
get => _red;
|
||||
set => Set(ref _red, value);
|
||||
}
|
||||
|
||||
public RelayCommand SaveChangesCommand { get; }
|
||||
|
||||
public string Selection
|
||||
{
|
||||
get => _selection;
|
||||
set => Set(ref _selection, value);
|
||||
}
|
||||
|
||||
public RelayCommand SetActiveCommand { get; }
|
||||
|
||||
public string White
|
||||
{
|
||||
get => _white;
|
||||
set => Set(ref _white, value);
|
||||
}
|
||||
|
||||
public string Yellow
|
||||
{
|
||||
get => _yellow;
|
||||
set => Set(ref _yellow, value);
|
||||
}
|
||||
|
||||
public void SaveChanges()
|
||||
{
|
||||
_theme.Name = Name;
|
||||
_theme.Author = Author;
|
||||
|
||||
_theme.Colors.Black = Black;
|
||||
_theme.Colors.Red = Red;
|
||||
_theme.Colors.Green = Green;
|
||||
_theme.Colors.Yellow = Yellow;
|
||||
_theme.Colors.Blue = Blue;
|
||||
_theme.Colors.Magenta = Magenta;
|
||||
_theme.Colors.Cyan = Cyan;
|
||||
_theme.Colors.White = White;
|
||||
|
||||
_theme.Colors.BrightBlack = BrightBlack;
|
||||
_theme.Colors.BrightRed = BrightRed;
|
||||
_theme.Colors.BrightGreen = BrightGreen;
|
||||
_theme.Colors.BrightYellow = BrightYellow;
|
||||
_theme.Colors.BrightBlue = BrightBlue;
|
||||
_theme.Colors.BrightMagenta = BrightMagenta;
|
||||
_theme.Colors.BrightCyan = BrightCyan;
|
||||
_theme.Colors.BrightWhite = BrightWhite;
|
||||
|
||||
_theme.Colors.Background = Background;
|
||||
_theme.Colors.Foreground = Foreground;
|
||||
_theme.Colors.Cursor = Cursor;
|
||||
_theme.Colors.CursorAccent = CursorAccent;
|
||||
_theme.Colors.Selection = Selection;
|
||||
|
||||
_settingsService.SaveTheme(_theme);
|
||||
|
||||
InEditMode = false;
|
||||
}
|
||||
|
||||
private async Task CancelEdit()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to discard all changes?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Black = _fallBackColors.Black;
|
||||
Red = _fallBackColors.Red;
|
||||
Green = _fallBackColors.Green;
|
||||
Yellow = _fallBackColors.Yellow;
|
||||
Blue = _fallBackColors.Blue;
|
||||
Magenta = _fallBackColors.Magenta;
|
||||
Cyan = _fallBackColors.Cyan;
|
||||
White = _fallBackColors.White;
|
||||
|
||||
BrightBlack = _fallBackColors.BrightBlack;
|
||||
BrightRed = _fallBackColors.BrightRed;
|
||||
BrightGreen = _fallBackColors.BrightGreen;
|
||||
BrightYellow = _fallBackColors.BrightYellow;
|
||||
BrightBlue = _fallBackColors.BrightBlue;
|
||||
BrightMagenta = _fallBackColors.BrightMagenta;
|
||||
BrightCyan = _fallBackColors.BrightCyan;
|
||||
BrightWhite = _fallBackColors.BrightWhite;
|
||||
|
||||
Background = _fallBackColors.Background;
|
||||
Foreground = _fallBackColors.Foreground;
|
||||
Cursor = _fallBackColors.Cursor;
|
||||
CursorAccent = _fallBackColors.CursorAccent;
|
||||
Selection = _fallBackColors.Selection;
|
||||
|
||||
Name = _fallBackName;
|
||||
Author = _fallBackAuthor;
|
||||
|
||||
InEditMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool NotPreInstalled()
|
||||
{
|
||||
return !_theme.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()
|
||||
{
|
||||
_fallBackColors = new TerminalColors(_theme.Colors);
|
||||
_fallBackName = Name;
|
||||
_fallBackAuthor = Author;
|
||||
InEditMode = true;
|
||||
}
|
||||
|
||||
private void SetActive()
|
||||
{
|
||||
Activated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private Task Export()
|
||||
{
|
||||
var content = JsonConvert.SerializeObject(_theme, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new TerminalThemeContractResolver() });
|
||||
return _fileSystemService.SaveTextFile(Name, "Fluent Terminal Theme", ".flutecolors", content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class AppServiceConnectionAdapter : IAppServiceConnection
|
||||
{
|
||||
private readonly AppServiceConnection _appServiceConnection;
|
||||
|
||||
public AppServiceConnectionAdapter(AppServiceConnection appServiceConnection)
|
||||
{
|
||||
_appServiceConnection = appServiceConnection;
|
||||
}
|
||||
|
||||
public async Task<IDictionary<string, string>> SendMessageAsync(IDictionary<string, string> message)
|
||||
{
|
||||
var valueSet = new ValueSet();
|
||||
|
||||
foreach (var pair in message)
|
||||
{
|
||||
valueSet.Add(pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
var response = await _appServiceConnection.SendMessageAsync(valueSet).AsTask();
|
||||
|
||||
if (response.Status == AppServiceResponseStatus.Success)
|
||||
{
|
||||
var responseDict = new Dictionary<string, string>();
|
||||
foreach (var pair in response.Message)
|
||||
{
|
||||
responseDict.Add(pair.Key, (string)pair.Value);
|
||||
}
|
||||
return responseDict;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class ApplicationDataContainerAdapter : IApplicationDataContainer
|
||||
{
|
||||
private readonly ApplicationDataContainer _applicationDataContainer;
|
||||
|
||||
public ApplicationDataContainerAdapter(ApplicationDataContainer applicationDataContainer)
|
||||
{
|
||||
_applicationDataContainer = applicationDataContainer;
|
||||
}
|
||||
|
||||
public void Delete(string key)
|
||||
{
|
||||
_applicationDataContainer.Values.Remove(key);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetAll()
|
||||
{
|
||||
return _applicationDataContainer.Values.Select(x => x.Value);
|
||||
}
|
||||
|
||||
public T ReadValueFromJson<T>(string name, T fallbackValue)
|
||||
{
|
||||
if (_applicationDataContainer.Values.TryGetValue(name, out object value))
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(fallbackValue, default))
|
||||
{
|
||||
fallbackValue = Activator.CreateInstance<T>();
|
||||
}
|
||||
|
||||
JsonConvert.PopulateObject((string)value, fallbackValue);
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
public void SetValue(string key, object value)
|
||||
{
|
||||
_applicationDataContainer.Values[key] = value;
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out object value)
|
||||
{
|
||||
return _applicationDataContainer.Values.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
|
||||
public void WriteValueAsJson<T>(string name, T value)
|
||||
{
|
||||
_applicationDataContainer.Values[name] = JsonConvert.SerializeObject(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.EventArgs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.ViewManagement;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class ApplicationViewAdapter : IApplicationView
|
||||
{
|
||||
private readonly ApplicationView _applicationView;
|
||||
private readonly CoreDispatcher _dispatcher;
|
||||
|
||||
public event CloseRequestedHandler CloseRequested;
|
||||
|
||||
public ApplicationViewAdapter()
|
||||
{
|
||||
_applicationView = ApplicationView.GetForCurrentView();
|
||||
_dispatcher = CoreApplication.GetCurrentView().Dispatcher;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _applicationView.Title;
|
||||
set => _applicationView.Title = value;
|
||||
}
|
||||
|
||||
public Task RunOnDispatcherThread(Action action)
|
||||
{
|
||||
return _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask();
|
||||
}
|
||||
|
||||
public Task<bool> TryClose()
|
||||
{
|
||||
return _applicationView.TryConsolidateAsync().AsTask();
|
||||
}
|
||||
|
||||
public bool ToggleFullScreen()
|
||||
{
|
||||
if (_applicationView.IsFullScreenMode)
|
||||
{
|
||||
_applicationView.ExitFullScreenMode();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _applicationView.TryEnterFullScreenMode();
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
var deferral = e.GetDeferral();
|
||||
|
||||
var args = new CancelableEventArgs();
|
||||
await CloseRequested?.Invoke(this, args);
|
||||
|
||||
e.Handled = args.Cancelled;
|
||||
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using FluentTerminal.App.Services;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class DispatcherTimerAdapter : IDispatcherTimer
|
||||
{
|
||||
private readonly DispatcherTimer _dispatcherTimer;
|
||||
|
||||
public DispatcherTimerAdapter()
|
||||
{
|
||||
_dispatcherTimer = new DispatcherTimer();
|
||||
_dispatcherTimer.Tick += OnDispatcherTimerTick;
|
||||
}
|
||||
|
||||
private void OnDispatcherTimerTick(object sender, object e)
|
||||
{
|
||||
Tick?.Invoke(this, null);
|
||||
}
|
||||
|
||||
public TimeSpan Interval
|
||||
{
|
||||
get => _dispatcherTimer.Interval;
|
||||
set => _dispatcherTimer.Interval = value;
|
||||
}
|
||||
|
||||
public bool IsEnabled => _dispatcherTimer.IsEnabled;
|
||||
|
||||
public event EventHandler<object> Tick;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_dispatcherTimer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_dispatcherTimer.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Popups;
|
||||
|
||||
namespace FluentTerminal.App.Adapters
|
||||
{
|
||||
public class MessageDialogAdapter : IMessageDialog
|
||||
{
|
||||
private readonly MessageDialog _messageDialog;
|
||||
|
||||
public MessageDialogAdapter()
|
||||
{
|
||||
_messageDialog = new MessageDialog(string.Empty);
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _messageDialog.Title;
|
||||
set => _messageDialog.Title = value;
|
||||
}
|
||||
|
||||
public string Content
|
||||
{
|
||||
get => _messageDialog.Content;
|
||||
set => _messageDialog.Content = value;
|
||||
}
|
||||
|
||||
public void AddButton(DialogButton button)
|
||||
{
|
||||
_messageDialog.Commands.Add(new UICommand(button.ToString()));
|
||||
}
|
||||
|
||||
public async Task<DialogButton> ShowAsync()
|
||||
{
|
||||
var result = await _messageDialog.ShowAsync();
|
||||
|
||||
return Enum.Parse<DialogButton>(result.Label);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,34 @@
|
||||
<Application
|
||||
x:Class="FluentTerminal.App.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Styles\Custom.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="OneToVisibleConverter"
|
||||
Else="Collapsed"
|
||||
If="1"
|
||||
Then="Visible" />
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="OneToCollapsedConverter"
|
||||
Else="Visible"
|
||||
If="1"
|
||||
Then="Collapsed" />
|
||||
<converters:NegateConverter x:Key="NegateConverter" />
|
||||
<converters:FalseToVisibleConverter x:Key="FalseToVisibleConverter" />
|
||||
<converters:TrueToVisibleConverter x:Key="TrueToVisibleConverter" />
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="ZeroToCollapsedConverter"
|
||||
Else="Visible"
|
||||
If="0"
|
||||
Then="Collapsed" />
|
||||
<converters:IntToExtendedVirtualKeyConverter x:Key="IntToExtendedVirtualKeyConverter" />
|
||||
<converters:BackgroundToApplicationThemeConverter x:Key="BackgroundToApplicationThemeConverter" />
|
||||
<converters:StringToColorConverter x:Key="StringToColorConverter" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
+183
-81
@@ -1,133 +1,190 @@
|
||||
using Autofac;
|
||||
using FluentTerminal.App.Adapters;
|
||||
using FluentTerminal.App.Dialogs;
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Views;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.ApplicationModel.Background;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using IContainer = Autofac.IContainer;
|
||||
|
||||
namespace FluentTerminal.App
|
||||
{
|
||||
sealed partial class App : Application
|
||||
public sealed partial class App : Application
|
||||
{
|
||||
public static BackgroundTaskDeferral AppServiceDeferral = null;
|
||||
public static AppServiceConnection Connection = null;
|
||||
public static App Instance;
|
||||
public const string ThemesContainerName = "Themes";
|
||||
public const string KeyBindingsContainerName = "KeyBindings";
|
||||
public const string ShellProfilesContainerName = "ShellProfiles";
|
||||
|
||||
public TaskCompletionSource<int> _trayReady = new TaskCompletionSource<int>();
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private bool _alreadyLaunched;
|
||||
private IContainer _container;
|
||||
private int _openTerminalWindows;
|
||||
private ApplicationSettings _applicationSettings;
|
||||
private readonly IContainer _container;
|
||||
private readonly List<MainViewModel> _mainViewModels;
|
||||
private SettingsViewModel _settingsViewModel;
|
||||
private int? _settingsWindowId;
|
||||
private IAppServiceConnection _appServiceConnection;
|
||||
private BackgroundTaskDeferral _appServiceDeferral;
|
||||
|
||||
public App()
|
||||
{
|
||||
_mainViewModels = new List<MainViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
Instance = this;
|
||||
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
LocalSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings),
|
||||
RoamingSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings),
|
||||
KeyBindings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(KeyBindingsContainerName, ApplicationDataCreateDisposition.Always)),
|
||||
ShellProfiles = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings.CreateContainer(ShellProfilesContainerName, ApplicationDataCreateDisposition.Always)),
|
||||
Themes = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(ThemesContainerName, ApplicationDataCreateDisposition.Always))
|
||||
};
|
||||
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<SettingsService>().As<ISettingsService>().InstancePerDependency();
|
||||
builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
|
||||
builder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>().SingleInstance();
|
||||
builder.RegisterType<TerminalService>().As<ITerminalService>().SingleInstance();
|
||||
builder.RegisterType<TrayProcessCommunicationService>().As<ITrayProcessCommunicationService>().SingleInstance();
|
||||
builder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
|
||||
builder.RegisterType<KeyboardCommandService>().As<IKeyboardCommandService>().InstancePerDependency();
|
||||
builder.RegisterType<MainViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<ThemeParserFactory>().As<IThemeParserFactory>().SingleInstance();
|
||||
builder.RegisterType<ITermThemeParser>().As<IThemeParser>().SingleInstance();
|
||||
builder.RegisterType<FluentTerminalThemeParser>().As<IThemeParser>().SingleInstance();
|
||||
builder.RegisterType<ClipboardService>().As<IClipboardService>().SingleInstance();
|
||||
builder.RegisterType<FileSystemService>().As<IFileSystemService>().SingleInstance();
|
||||
builder.RegisterType<SystemFontService>().As<ISystemFontService>().SingleInstance();
|
||||
builder.RegisterType<ShellProfileSelectionDialog>().As<IShellProfileSelectionDialog>().InstancePerDependency();
|
||||
builder.RegisterType<CreateKeyBindingDialog>().As<ICreateKeyBindingDialog>().InstancePerDependency();
|
||||
builder.RegisterType<MessageDialogAdapter>().As<IMessageDialog>().InstancePerDependency();
|
||||
builder.RegisterType<ApplicationViewAdapter>().As<IApplicationView>().InstancePerDependency();
|
||||
builder.RegisterType<DispatcherTimerAdapter>().As<IDispatcherTimer>().InstancePerDependency();
|
||||
builder.RegisterInstance(applicationDataContainers);
|
||||
|
||||
_container = builder.Build();
|
||||
}
|
||||
|
||||
public async Task CreateNewTerminalWindow()
|
||||
{
|
||||
var id = await CreateSecondaryView(typeof(MainPage), true);
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
|
||||
}
|
||||
_settingsService = _container.Resolve<ISettingsService>();
|
||||
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
|
||||
public void SettingsWindowClosed()
|
||||
{
|
||||
_settingsWindowId = null;
|
||||
}
|
||||
_trayProcessCommunicationService = _container.Resolve<ITrayProcessCommunicationService>();
|
||||
|
||||
public async Task ShowSettings()
|
||||
{
|
||||
if (_settingsWindowId == null)
|
||||
{
|
||||
_settingsWindowId = await CreateSecondaryView(typeof(SettingsPage), true);
|
||||
}
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
|
||||
}
|
||||
|
||||
public void TerminalWindowClosed()
|
||||
{
|
||||
_openTerminalWindows--;
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
}
|
||||
|
||||
protected override async void OnActivated(IActivatedEventArgs args)
|
||||
{
|
||||
if (args is CommandLineActivatedEventArgs commandLineActivated)
|
||||
{
|
||||
var argument = commandLineActivated.Operation.Arguments.Trim();
|
||||
if (string.IsNullOrWhiteSpace(commandLineActivated.Operation.Arguments))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var arguments = commandLineActivated.Operation.Arguments.Split(' ', 2);
|
||||
var command = arguments[0];
|
||||
var parameter = arguments.Length > 1 ? arguments[1] : string.Empty;
|
||||
|
||||
if (_alreadyLaunched)
|
||||
{
|
||||
if (argument == "settings")
|
||||
if (command == "settings")
|
||||
{
|
||||
await ShowSettings();
|
||||
await ShowSettings().ConfigureAwait(true);
|
||||
}
|
||||
else if (argument == "new")
|
||||
else if (command == "new")
|
||||
{
|
||||
await CreateNewTerminalWindow();
|
||||
if (_applicationSettings.NewTerminalLocation == NewTerminalLocation.Tab && _mainViewModels.Count > 0)
|
||||
{
|
||||
await _mainViewModels.Last().AddTerminal(parameter, false).ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await CreateNewTerminalWindow(parameter).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (argument == "settings")
|
||||
if (command == "settings")
|
||||
{
|
||||
CreateMainView(typeof(SettingsPage), true);
|
||||
var viewModel = _container.Resolve<SettingsViewModel>();
|
||||
await CreateMainView(typeof(SettingsPage), viewModel, true).ConfigureAwait(true);
|
||||
}
|
||||
else if (argument == "new")
|
||||
else if (command == "new")
|
||||
{
|
||||
CreateMainView(typeof(MainPage), true);
|
||||
var viewModel = _container.Resolve<MainViewModel>();
|
||||
await viewModel.AddTerminal(parameter, false).ConfigureAwait(true);
|
||||
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
if (!_alreadyLaunched)
|
||||
{
|
||||
var viewModel = _container.Resolve<MainViewModel>();
|
||||
await viewModel.AddTerminal(null, false).ConfigureAwait(true);
|
||||
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
|
||||
Window.Current.Activate();
|
||||
}
|
||||
else if (_mainViewModels.Count == 0)
|
||||
{
|
||||
await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, string.Empty).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
|
||||
{
|
||||
base.OnBackgroundActivated(args);
|
||||
|
||||
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details)
|
||||
{
|
||||
AppServiceDeferral = args.TaskInstance.GetDeferral();
|
||||
args.TaskInstance.Canceled += OnTaskCanceled;
|
||||
Connection = details.AppServiceConnection;
|
||||
Connection.RequestReceived += OnRequestReceived;
|
||||
if (details.CallerPackageFamilyName == Package.Current.Id.FamilyName)
|
||||
{
|
||||
_appServiceDeferral = args.TaskInstance.GetDeferral();
|
||||
args.TaskInstance.Canceled += OnTaskCanceled;
|
||||
|
||||
_appServiceConnection = new AppServiceConnectionAdapter(details.AppServiceConnection);
|
||||
|
||||
_trayReady.SetResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs e)
|
||||
private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
{
|
||||
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
|
||||
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
|
||||
await Task.WhenAll(launch, clearCache);
|
||||
_appServiceDeferral?.Complete();
|
||||
_appServiceDeferral = null;
|
||||
_appServiceConnection = null;
|
||||
|
||||
if (!_alreadyLaunched)
|
||||
{
|
||||
CreateMainView(typeof(MainPage), true);
|
||||
Window.Current.Activate();
|
||||
}
|
||||
else if (_openTerminalWindows == 0)
|
||||
{
|
||||
await CreateSecondaryView(typeof(MainPage), true);
|
||||
}
|
||||
Application.Current.Exit();
|
||||
}
|
||||
|
||||
private void CreateMainView(Type pageType, bool extendViewIntoTitleBar)
|
||||
private async Task CreateMainView(Type pageType, INotifyPropertyChanged viewModel, bool extendViewIntoTitleBar)
|
||||
{
|
||||
await StartSystemTray().ConfigureAwait(true);
|
||||
|
||||
Frame rootFrame = Window.Current.Content as Frame;
|
||||
if (rootFrame == null)
|
||||
{
|
||||
@@ -138,66 +195,111 @@ namespace FluentTerminal.App
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar;
|
||||
rootFrame.Navigate(pageType, GetViewModelForPageType(pageType));
|
||||
|
||||
if (pageType == typeof(MainPage))
|
||||
if (viewModel is MainViewModel mainViewModel)
|
||||
{
|
||||
_openTerminalWindows++;
|
||||
mainViewModel.Closed += OnMainViewModelClosed;
|
||||
mainViewModel.NewWindowRequested += OnNewWindowRequested;
|
||||
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
|
||||
_mainViewModels.Add(mainViewModel);
|
||||
}
|
||||
|
||||
rootFrame.Navigate(pageType, viewModel);
|
||||
}
|
||||
_alreadyLaunched = true;
|
||||
Window.Current.Activate();
|
||||
}
|
||||
|
||||
private async Task<int> CreateSecondaryView(Type pageType, bool ExtendViewIntoTitleBar)
|
||||
private async Task CreateNewTerminalWindow(string startupDirectory)
|
||||
{
|
||||
var id = await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, startupDirectory).ConfigureAwait(true);
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
|
||||
}
|
||||
|
||||
private async Task<int> CreateSecondaryView<TViewModel>(Type pageType, bool ExtendViewIntoTitleBar, object parameter)
|
||||
{
|
||||
int windowId = 0;
|
||||
TViewModel viewModel = default;
|
||||
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
viewModel = _container.Resolve<TViewModel>();
|
||||
|
||||
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ExtendViewIntoTitleBar;
|
||||
var frame = new Frame();
|
||||
frame.Navigate(pageType, GetViewModelForPageType(pageType));
|
||||
frame.Navigate(pageType, viewModel);
|
||||
Window.Current.Content = frame;
|
||||
Window.Current.Activate();
|
||||
|
||||
windowId = ApplicationView.GetForCurrentView().Id;
|
||||
});
|
||||
|
||||
if (pageType == typeof(MainPage))
|
||||
if (viewModel is MainViewModel mainViewModel && parameter is string directory)
|
||||
{
|
||||
_openTerminalWindows++;
|
||||
mainViewModel.Closed += OnMainViewModelClosed;
|
||||
mainViewModel.NewWindowRequested += OnNewWindowRequested;
|
||||
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
|
||||
_mainViewModels.Add(mainViewModel);
|
||||
await mainViewModel.AddTerminal(directory, false).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
if (viewModel is SettingsViewModel settingsViewModel)
|
||||
{
|
||||
_settingsViewModel = settingsViewModel;
|
||||
_settingsViewModel.Closed += OnSettingsClosed;
|
||||
}
|
||||
|
||||
return windowId;
|
||||
}
|
||||
|
||||
private object GetViewModelForPageType(Type pageType)
|
||||
private void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
|
||||
{
|
||||
if (pageType == typeof(MainPage))
|
||||
{
|
||||
return _container.Resolve<MainViewModel>();
|
||||
}
|
||||
else if (pageType == typeof(SettingsPage))
|
||||
{
|
||||
return _container.Resolve<SettingsViewModel>();
|
||||
}
|
||||
throw new Exception("No ViewModel defined for Page: " + pageType.Name);
|
||||
_applicationSettings = e;
|
||||
}
|
||||
|
||||
private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
|
||||
private void OnMainViewModelClosed(object sender, EventArgs e)
|
||||
{
|
||||
if (args.Request.Message.ContainsKey("exit"))
|
||||
if (sender is MainViewModel viewModel)
|
||||
{
|
||||
App.Current.Exit();
|
||||
viewModel.Closed -= OnMainViewModelClosed;
|
||||
viewModel.NewWindowRequested -= OnNewWindowRequested;
|
||||
viewModel.ShowSettingsRequested -= OnShowSettingsRequested;
|
||||
|
||||
_mainViewModels.Remove(viewModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
|
||||
private async void OnNewWindowRequested(object sender, EventArgs e)
|
||||
{
|
||||
if (AppServiceDeferral != null)
|
||||
await CreateNewTerminalWindow(string.Empty).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private void OnSettingsClosed(object sender, EventArgs e)
|
||||
{
|
||||
_settingsViewModel.Closed -= OnSettingsClosed;
|
||||
_settingsViewModel = null;
|
||||
_settingsWindowId = null;
|
||||
}
|
||||
|
||||
private async void OnShowSettingsRequested(object sender, EventArgs e)
|
||||
{
|
||||
await ShowSettings().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async Task ShowSettings()
|
||||
{
|
||||
if (_settingsViewModel == null)
|
||||
{
|
||||
AppServiceDeferral.Complete();
|
||||
_settingsWindowId = await CreateSecondaryView<SettingsViewModel>(typeof(SettingsPage), true, null).ConfigureAwait(true);
|
||||
}
|
||||
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
|
||||
}
|
||||
|
||||
private async Task StartSystemTray()
|
||||
{
|
||||
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
|
||||
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
|
||||
await Task.WhenAll(launch, clearCache, _trayReady.Task).ConfigureAwait(true);
|
||||
await _trayProcessCommunicationService.Initialize(_appServiceConnection).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,40 @@
|
||||
using Microsoft.Xaml.Interactivity;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace FluentTerminal.App.Behaviors
|
||||
{
|
||||
public class MiddleClickBehavior : Trigger<UIElement>
|
||||
{
|
||||
private bool _middleButtonPressed;
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
AssociatedObject.PointerPressed += OnPointerPressed;
|
||||
AssociatedObject.PointerReleased += OnPointerReleased;
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
AssociatedObject.PointerPressed -= OnPointerPressed;
|
||||
AssociatedObject.PointerReleased -= OnPointerReleased;
|
||||
}
|
||||
|
||||
private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var point = e.GetCurrentPoint(AssociatedObject);
|
||||
_middleButtonPressed = point.Properties.IsMiddleButtonPressed;
|
||||
}
|
||||
|
||||
private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var point = e.GetCurrentPoint(AssociatedObject);
|
||||
var middleButtonStillPressed = point.Properties.IsMiddleButtonPressed;
|
||||
|
||||
if (_middleButtonPressed && !middleButtonStillPressed)
|
||||
{
|
||||
Interaction.ExecuteActions(AssociatedObject, Actions, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using FluentTerminal.App.Utilities;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class BackgroundToApplicationThemeConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
Color color;
|
||||
if (value is string colorString)
|
||||
{
|
||||
color = colorString.ToColor();
|
||||
} else if (value is Color)
|
||||
{
|
||||
color = (Color)value;
|
||||
}
|
||||
|
||||
if (color != null)
|
||||
{
|
||||
return ContrastHelper.GetIdealThemeForBackgroundColor(color);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class IntToExtendedVirtualKeyConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is int intValue)
|
||||
{
|
||||
return EnumHelper.GetEnumDescription((ExtendedVirtualKey)intValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class IntToVisibilityConverter : DependencyObject, IValueConverter
|
||||
{
|
||||
public static readonly DependencyProperty ElseProperty =
|
||||
DependencyProperty.Register(nameof(Else), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Collapsed));
|
||||
|
||||
public static readonly DependencyProperty IfProperty =
|
||||
DependencyProperty.Register(nameof(If), typeof(int), typeof(IntToVisibilityConverter), new PropertyMetadata(0));
|
||||
|
||||
public static readonly DependencyProperty ThenProperty =
|
||||
DependencyProperty.Register(nameof(Then), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Visible));
|
||||
|
||||
public Visibility Else
|
||||
{
|
||||
get { return (Visibility)GetValue(ElseProperty); }
|
||||
set { SetValue(ElseProperty, value); }
|
||||
}
|
||||
|
||||
public int If
|
||||
{
|
||||
get { return (int)GetValue(IfProperty); }
|
||||
set { SetValue(IfProperty, value); }
|
||||
}
|
||||
|
||||
public Visibility Then
|
||||
{
|
||||
get { return (Visibility)GetValue(ThenProperty); }
|
||||
set { SetValue(ThenProperty, value); }
|
||||
}
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is int count)
|
||||
{
|
||||
return count == If ? Then : Else;
|
||||
}
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class NegateConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool boolean)
|
||||
{
|
||||
return !boolean;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using FluentTerminal.App.Utilities;
|
||||
using System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class StringToColorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is string colorString)
|
||||
{
|
||||
return colorString.FromString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Color color && bool.TryParse((string)parameter, out bool allowTransparency))
|
||||
{
|
||||
return color.ToColorString(allowTransparency);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace FluentTerminal.App.Converters
|
||||
{
|
||||
internal class TerminalViewModelToViewConverter : IValueConverter
|
||||
{
|
||||
private Dictionary<int, TerminalView> _viewDictionary;
|
||||
private readonly Dictionary<int, TerminalView> _viewDictionary;
|
||||
|
||||
public TerminalViewModelToViewConverter()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<ContentDialog
|
||||
x:Class="FluentTerminal.App.Dialogs.CreateKeyBindingDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400"
|
||||
PrimaryButtonText="OK"
|
||||
SecondaryButtonText="Cancel"
|
||||
mc:Ignorable="d">
|
||||
<StackPanel>
|
||||
<TextBlock Margin="0,0,0,24" Text="Enter your keybinding now" />
|
||||
<StackPanel x:Name="Root" Orientation="Horizontal">
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Ctrl, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="Ctrl" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Meta, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<FontIcon
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="f" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Alt, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="Alt" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Shift, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="Shift" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Key, Mode=OneWay, Converter={StaticResource ZeroToCollapsedConverter}}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Key, Mode=OneWay, Converter={StaticResource IntToExtendedVirtualKeyConverter}}" />
|
||||
</Grid>
|
||||
<HyperlinkButton
|
||||
x:Name="ResetButton"
|
||||
Margin="24,0,0,0"
|
||||
Command="{x:Bind ResetCommand}"
|
||||
Content="Reset"
|
||||
PreviewKeyDown="OnResetButtonPreviewKeyDown" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ContentDialog>
|
||||
@@ -0,0 +1,131 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Dialogs
|
||||
{
|
||||
public sealed partial class CreateKeyBindingDialog : ContentDialog, ICreateKeyBindingDialog, INotifyPropertyChanged
|
||||
{
|
||||
private bool _ctrl;
|
||||
private bool _shift;
|
||||
private bool _alt;
|
||||
private bool _meta;
|
||||
private int _key;
|
||||
|
||||
public bool Ctrl
|
||||
{
|
||||
get => _ctrl;
|
||||
set => Set(ref _ctrl, value);
|
||||
}
|
||||
|
||||
public bool Shift
|
||||
{
|
||||
get => _shift;
|
||||
set => Set(ref _shift, value);
|
||||
}
|
||||
|
||||
public bool Alt
|
||||
{
|
||||
get => _alt;
|
||||
set => Set(ref _alt, value);
|
||||
}
|
||||
|
||||
public bool Meta
|
||||
{
|
||||
get => _meta;
|
||||
set => Set(ref _meta, value);
|
||||
}
|
||||
|
||||
public int Key
|
||||
{
|
||||
get => _key;
|
||||
set => Set(ref _key, value);
|
||||
}
|
||||
|
||||
public CreateKeyBindingDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
ResetCommand = new RelayCommand(Reset);
|
||||
PreviewKeyDown += RegisterKeyBindingDialog_PreviewKeyDown;
|
||||
Reset();
|
||||
}
|
||||
|
||||
public RelayCommand ResetCommand { get; }
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public async Task<KeyBinding> CreateKeyBinding()
|
||||
{
|
||||
var result = await ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
return new KeyBinding
|
||||
{
|
||||
Alt = Alt,
|
||||
Ctrl = Ctrl,
|
||||
Key = Key,
|
||||
Meta = Meta,
|
||||
Shift = Shift
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnResetButtonPreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void RegisterKeyBindingDialog_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
|
||||
{
|
||||
switch (e.Key)
|
||||
{
|
||||
case VirtualKey.Control:
|
||||
Ctrl = true;
|
||||
break;
|
||||
case VirtualKey.Shift:
|
||||
Shift = true;
|
||||
break;
|
||||
case VirtualKey.Menu:
|
||||
Alt = true;
|
||||
break;
|
||||
case VirtualKey.LeftWindows:
|
||||
case VirtualKey.RightWindows:
|
||||
Meta = true;
|
||||
break;
|
||||
default:
|
||||
Key = (int)e.Key;
|
||||
break;
|
||||
}
|
||||
|
||||
ResetButton.Visibility = Visibility.Visible;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
Alt = false;
|
||||
Ctrl = false;
|
||||
Meta = false;
|
||||
Shift = false;
|
||||
Key = 0;
|
||||
|
||||
ResetButton.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void Set<T>(ref T field, T value, [CallerMemberName]string propertyName = "")
|
||||
{
|
||||
field = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<ContentDialog
|
||||
x:Class="FluentTerminal.App.Dialogs.ShellProfileSelectionDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="Select Profile"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
CloseButtonText="Cancel"
|
||||
PrimaryButtonText="Start"
|
||||
mc:Ignorable="d">
|
||||
<ComboBox
|
||||
HorizontalAlignment="Stretch"
|
||||
DisplayMemberPath="Name"
|
||||
ItemsSource="{x:Bind Profiles}"
|
||||
SelectedItem="{x:Bind SelectedProfile, Mode=TwoWay}" />
|
||||
</ContentDialog>
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Dialogs;
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Dialogs
|
||||
{
|
||||
public sealed partial class ShellProfileSelectionDialog : ContentDialog, IShellProfileSelectionDialog
|
||||
{
|
||||
public IEnumerable<ShellProfile> Profiles { get; }
|
||||
|
||||
public ShellProfile SelectedProfile { get; private set; }
|
||||
|
||||
public ShellProfileSelectionDialog(ISettingsService settingsService)
|
||||
{
|
||||
Profiles = settingsService.GetShellProfiles();
|
||||
SelectedProfile = Profiles.First();
|
||||
this.InitializeComponent();
|
||||
var currentTheme = settingsService.GetCurrentTheme();
|
||||
RequestedTheme = ContrastHelper.GetIdealThemeForBackgroundColor(currentTheme.Colors.Background);
|
||||
}
|
||||
|
||||
public async Task<ShellProfile> SelectProfile()
|
||||
{
|
||||
var result = await ShowAsync();
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
return SelectedProfile;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@
|
||||
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
|
||||
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
|
||||
<AppInstallerUpdateFrequency>1</AppInstallerUpdateFrequency>
|
||||
<AppInstallerCheckForUpdateFrequency>OnApplicationRun</AppInstallerCheckForUpdateFrequency>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -33,6 +36,7 @@
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
@@ -45,6 +49,7 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -79,6 +84,7 @@
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
@@ -91,42 +97,55 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Adapters\ApplicationDataContainerAdapter.cs" />
|
||||
<Compile Include="Adapters\ApplicationViewAdapter.cs" />
|
||||
<Compile Include="Adapters\AppServiceConnectionAdapter.cs" />
|
||||
<Compile Include="Adapters\DispatcherTimerAdapter.cs" />
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Behaviors\MiddleClickBehavior.cs" />
|
||||
<Compile Include="Converters\BackgroundToApplicationThemeConverter.cs" />
|
||||
<Compile Include="Converters\FalseToVisibleConverter.cs" />
|
||||
<Compile Include="Converters\IntToExtendedVirtualKeyConverter.cs" />
|
||||
<Compile Include="Converters\IntToVisibilityConverter.cs" />
|
||||
<Compile Include="Converters\NegateConverter.cs" />
|
||||
<Compile Include="Converters\StringToColorConverter.cs" />
|
||||
<Compile Include="Converters\TerminalViewModelToViewConverter.cs" />
|
||||
<Compile Include="Converters\TrueToVisibleConverter.cs" />
|
||||
<Compile Include="Utilities\ApplicationDataContainerExtensions.cs" />
|
||||
<Compile Include="Services\IDefaultValueProvider.cs" />
|
||||
<Compile Include="Services\Implementation\DefaultValueProvider.cs" />
|
||||
<Compile Include="Services\Implementation\SettingsService.cs" />
|
||||
<Compile Include="Services\Implementation\TerminalService.cs" />
|
||||
<Compile Include="Services\ISettingsService.cs" />
|
||||
<Compile Include="Services\ITerminalService.cs" />
|
||||
<Compile Include="Utilities\HttpJsonContent.cs" />
|
||||
<Compile Include="Utilities\IHttpContentExtensions.cs" />
|
||||
<Compile Include="ViewModels\MainViewModel.cs" />
|
||||
<Compile Include="ViewModels\SettingsViewModel.cs" />
|
||||
<Compile Include="ViewModels\TerminalViewModel.cs" />
|
||||
<Compile Include="Views\ITerminalView.cs" />
|
||||
<Compile Include="Adapters\MessageDialogAdapter.cs" />
|
||||
<Compile Include="Dialogs\ShellProfileSelectionDialog.xaml.cs">
|
||||
<DependentUpon>ShellProfileSelectionDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Services\ClipboardService.cs" />
|
||||
<Compile Include="Services\FileSystemService.cs" />
|
||||
<Compile Include="Services\SystemFontService.cs" />
|
||||
<Compile Include="Utilities\ColorExtensions.cs" />
|
||||
<Compile Include="Utilities\ContrastHelper.cs" />
|
||||
<Compile Include="Views\KeyBindingsView.xaml.cs">
|
||||
<DependentUpon>KeyBindingsView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Dialogs\CreateKeyBindingDialog.xaml.cs">
|
||||
<DependentUpon>CreateKeyBindingDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPage.xaml.cs">
|
||||
<DependentUpon>SettingsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\ColorSettings.xaml.cs">
|
||||
<DependentUpon>ColorSettings.xaml</DependentUpon>
|
||||
<Compile Include="Views\SettingsPages\ThemeSettings.xaml.cs">
|
||||
<DependentUpon>ThemeSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\FontSettings.xaml.cs">
|
||||
<DependentUpon>FontSettings.xaml</DependentUpon>
|
||||
<Compile Include="Views\SettingsPages\TerminalSettings.xaml.cs">
|
||||
<DependentUpon>TerminalSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\GeneralSettings.xaml.cs">
|
||||
<DependentUpon>GeneralSettings.xaml</DependentUpon>
|
||||
@@ -134,8 +153,11 @@
|
||||
<Compile Include="Views\SettingsPages\KeyBindingSettings.xaml.cs">
|
||||
<DependentUpon>KeyBindingSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\ShellSettings.xaml.cs">
|
||||
<DependentUpon>ShellSettings.xaml</DependentUpon>
|
||||
<Compile Include="Views\SettingsPages\ShellProfileSettings.xaml.cs">
|
||||
<DependentUpon>ShellProfileSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\TerminalColorPicker.xaml.cs">
|
||||
<DependentUpon>TerminalColorPicker.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\TerminalView.xaml.cs">
|
||||
<DependentUpon>TerminalView.xaml</DependentUpon>
|
||||
@@ -145,10 +167,37 @@
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
<None Include="FluentTerminal.App_StoreKey.pfx" />
|
||||
<None Include="FluentTerminal.App_TemporaryKey.pfx" />
|
||||
<Content Include="Assets\settingsicons.ttf" />
|
||||
<Content Include="Win32\Autofac.dll" />
|
||||
<Content Include="Win32\Fleck.dll" />
|
||||
<Content Include="Win32\FluentTerminal.Models.dll" />
|
||||
<Content Include="Win32\FluentTerminal.Models.pdb" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.exe" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.pdb" />
|
||||
<Content Include="Win32\GlobalHotKey.dll" />
|
||||
<Content Include="Win32\Newtonsoft.Json.dll" />
|
||||
<Content Include="Win32\System.Data.Common.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.StackTrace.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Tracing.dll" />
|
||||
<Content Include="Win32\System.Globalization.Extensions.dll" />
|
||||
<Content Include="Win32\System.IO.Compression.dll" />
|
||||
<Content Include="Win32\System.Net.Http.dll" />
|
||||
<Content Include="Win32\System.Net.Sockets.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Primitives.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Algorithms.dll" />
|
||||
<Content Include="Win32\System.Security.SecureString.dll" />
|
||||
<Content Include="Win32\System.Threading.Overlapped.dll" />
|
||||
<Content Include="Win32\System.Xml.XPath.XDocument.dll" />
|
||||
<Content Include="Win32\winpty-agent.exe" />
|
||||
<Content Include="Win32\winpty-debugserver.exe" />
|
||||
<Content Include="Win32\winpty.dll" />
|
||||
<Content Include="Win32\winpty.NET.dll" />
|
||||
<None Include="FluentTerminal.App_StoreKey.pfx" />
|
||||
<None Include="FluentTerminal.App_TemporaryKey.pfx" />
|
||||
<Content Include="Client\bundle.js" />
|
||||
<Content Include="Client\index.html" />
|
||||
<Content Include="Client\style.css" />
|
||||
<Content Include="Client\xterm.css" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.exe.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -199,146 +248,20 @@
|
||||
<Content Include="Assets\AppIcons\Wide310x150Logo.scale-400.png" />
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
<Content Include="Properties\Default.rd.xml" />
|
||||
<Content Include="Win32\Client\bundle.js" />
|
||||
<Content Include="Win32\Client\index.html" />
|
||||
<Content Include="Win32\Client\style.css" />
|
||||
<Content Include="Win32\Client\xterm.css" />
|
||||
<Content Include="Win32\CommonServiceLocator.dll" />
|
||||
<Content Include="Win32\CommonServiceLocator.pdb" />
|
||||
<Content Include="Win32\Fleck.dll" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.exe" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.pdb" />
|
||||
<Content Include="Win32\Microsoft.Owin.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.FileSystems.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.Host.HttpListener.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.Hosting.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.StaticFiles.dll" />
|
||||
<Content Include="Win32\Microsoft.Win32.Primitives.dll" />
|
||||
<Content Include="Win32\netstandard.dll" />
|
||||
<Content Include="Win32\Newtonsoft.Json.dll" />
|
||||
<Content Include="Win32\Owin.dll" />
|
||||
<Content Include="Win32\System.AppContext.dll" />
|
||||
<Content Include="Win32\System.Collections.Concurrent.dll" />
|
||||
<Content Include="Win32\System.Collections.dll" />
|
||||
<Content Include="Win32\System.Collections.NonGeneric.dll" />
|
||||
<Content Include="Win32\System.Collections.Specialized.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.EventBasedAsync.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.Primitives.dll" />
|
||||
<Content Include="Win32\System.ComponentModel.TypeConverter.dll" />
|
||||
<Content Include="Win32\System.Console.dll" />
|
||||
<Content Include="Win32\System.Data.Common.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Contracts.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Debug.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.FileVersionInfo.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Process.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.StackTrace.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.TextWriterTraceListener.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Tools.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.TraceSource.dll" />
|
||||
<Content Include="Win32\System.Diagnostics.Tracing.dll" />
|
||||
<Content Include="Win32\System.Drawing.Primitives.dll" />
|
||||
<Content Include="Win32\System.Dynamic.Runtime.dll" />
|
||||
<Content Include="Win32\System.Globalization.Calendars.dll" />
|
||||
<Content Include="Win32\System.Globalization.dll" />
|
||||
<Content Include="Win32\System.Globalization.Extensions.dll" />
|
||||
<Content Include="Win32\System.IO.Compression.dll" />
|
||||
<Content Include="Win32\System.IO.Compression.ZipFile.dll" />
|
||||
<Content Include="Win32\System.IO.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.DriveInfo.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.Primitives.dll" />
|
||||
<Content Include="Win32\System.IO.FileSystem.Watcher.dll" />
|
||||
<Content Include="Win32\System.IO.IsolatedStorage.dll" />
|
||||
<Content Include="Win32\System.IO.MemoryMappedFiles.dll" />
|
||||
<Content Include="Win32\System.IO.Pipes.dll" />
|
||||
<Content Include="Win32\System.IO.UnmanagedMemoryStream.dll" />
|
||||
<Content Include="Win32\System.Linq.dll" />
|
||||
<Content Include="Win32\System.Linq.Expressions.dll" />
|
||||
<Content Include="Win32\System.Linq.Parallel.dll" />
|
||||
<Content Include="Win32\System.Linq.Queryable.dll" />
|
||||
<Content Include="Win32\System.Net.Http.dll" />
|
||||
<Content Include="Win32\System.Net.Http.Formatting.dll" />
|
||||
<Content Include="Win32\System.Net.NameResolution.dll" />
|
||||
<Content Include="Win32\System.Net.NetworkInformation.dll" />
|
||||
<Content Include="Win32\System.Net.Ping.dll" />
|
||||
<Content Include="Win32\System.Net.Primitives.dll" />
|
||||
<Content Include="Win32\System.Net.Requests.dll" />
|
||||
<Content Include="Win32\System.Net.Security.dll" />
|
||||
<Content Include="Win32\System.Net.Sockets.dll" />
|
||||
<Content Include="Win32\System.Net.WebHeaderCollection.dll" />
|
||||
<Content Include="Win32\System.Net.WebSockets.Client.dll" />
|
||||
<Content Include="Win32\System.Net.WebSockets.dll" />
|
||||
<Content Include="Win32\System.ObjectModel.dll" />
|
||||
<Content Include="Win32\System.Reflection.dll" />
|
||||
<Content Include="Win32\System.Reflection.Extensions.dll" />
|
||||
<Content Include="Win32\System.Reflection.Primitives.dll" />
|
||||
<Content Include="Win32\System.Resources.Reader.dll" />
|
||||
<Content Include="Win32\System.Resources.ResourceManager.dll" />
|
||||
<Content Include="Win32\System.Resources.Writer.dll" />
|
||||
<Content Include="Win32\System.Runtime.CompilerServices.VisualC.dll" />
|
||||
<Content Include="Win32\System.Runtime.dll" />
|
||||
<Content Include="Win32\System.Runtime.Extensions.dll" />
|
||||
<Content Include="Win32\System.Runtime.Handles.dll" />
|
||||
<Content Include="Win32\System.Runtime.InteropServices.dll" />
|
||||
<Content Include="Win32\System.Runtime.InteropServices.RuntimeInformation.dll" />
|
||||
<Content Include="Win32\System.Runtime.Numerics.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Formatters.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Json.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Primitives.dll" />
|
||||
<Content Include="Win32\System.Runtime.Serialization.Xml.dll" />
|
||||
<Content Include="Win32\System.Security.Claims.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Algorithms.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Csp.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Encoding.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.Primitives.dll" />
|
||||
<Content Include="Win32\System.Security.Cryptography.X509Certificates.dll" />
|
||||
<Content Include="Win32\System.Security.Principal.dll" />
|
||||
<Content Include="Win32\System.Security.SecureString.dll" />
|
||||
<Content Include="Win32\System.Text.Encoding.dll" />
|
||||
<Content Include="Win32\System.Text.Encoding.Extensions.dll" />
|
||||
<Content Include="Win32\System.Text.RegularExpressions.dll" />
|
||||
<Content Include="Win32\System.Threading.dll" />
|
||||
<Content Include="Win32\System.Threading.Overlapped.dll" />
|
||||
<Content Include="Win32\System.Threading.Tasks.dll" />
|
||||
<Content Include="Win32\System.Threading.Tasks.Parallel.dll" />
|
||||
<Content Include="Win32\System.Threading.Thread.dll" />
|
||||
<Content Include="Win32\System.Threading.ThreadPool.dll" />
|
||||
<Content Include="Win32\System.Threading.Timer.dll" />
|
||||
<Content Include="Win32\System.ValueTuple.dll" />
|
||||
<Content Include="Win32\System.Web.Http.dll" />
|
||||
<Content Include="Win32\System.Web.Http.Owin.dll" />
|
||||
<Content Include="Win32\System.Xml.ReaderWriter.dll" />
|
||||
<Content Include="Win32\System.Xml.XDocument.dll" />
|
||||
<Content Include="Win32\System.Xml.XmlDocument.dll" />
|
||||
<Content Include="Win32\System.Xml.XmlSerializer.dll" />
|
||||
<Content Include="Win32\System.Xml.XPath.dll" />
|
||||
<Content Include="Win32\System.Xml.XPath.XDocument.dll" />
|
||||
<Content Include="Win32\Unity.Abstractions.dll" />
|
||||
<Content Include="Win32\Unity.Abstractions.pdb" />
|
||||
<Content Include="Win32\Unity.Configuration.dll" />
|
||||
<Content Include="Win32\Unity.Configuration.pdb" />
|
||||
<Content Include="Win32\Unity.Container.dll" />
|
||||
<Content Include="Win32\Unity.Container.pdb" />
|
||||
<Content Include="Win32\Unity.Interception.Configuration.dll" />
|
||||
<Content Include="Win32\Unity.Interception.Configuration.pdb" />
|
||||
<Content Include="Win32\Unity.Interception.dll" />
|
||||
<Content Include="Win32\Unity.Interception.pdb" />
|
||||
<Content Include="Win32\Unity.RegistrationByConvention.dll" />
|
||||
<Content Include="Win32\Unity.RegistrationByConvention.pdb" />
|
||||
<Content Include="Win32\Unity.ServiceLocation.dll" />
|
||||
<Content Include="Win32\Unity.ServiceLocation.pdb" />
|
||||
<Content Include="Win32\Unity.WebApi.dll" />
|
||||
<Content Include="Win32\winpty-agent.exe" />
|
||||
<Content Include="Win32\winpty-debugserver.exe" />
|
||||
<Content Include="Win32\winpty.dll" />
|
||||
<Content Include="Win32\winpty.NET.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="Dialogs\ShellProfileSelectionDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\KeyBindingsView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\MainPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
@@ -348,15 +271,19 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Page>
|
||||
<Page Include="Dialogs\CreateKeyBindingDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\ColorSettings.xaml">
|
||||
<Page Include="Views\SettingsPages\ThemeSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\FontSettings.xaml">
|
||||
<Page Include="Views\SettingsPages\TerminalSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
@@ -368,7 +295,11 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\ShellSettings.xaml">
|
||||
<Page Include="Views\SettingsPages\ShellProfileSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\TerminalColorPicker.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
@@ -379,16 +310,25 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac">
|
||||
<Version>4.6.2</Version>
|
||||
<Version>4.8.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.0.6</Version>
|
||||
<Version>6.1.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MvvmLightLibs">
|
||||
<Version>5.3.0</Version>
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.UI">
|
||||
<Version>3.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
<Version>3.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed">
|
||||
<Version>2.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>10.0.3</Version>
|
||||
<Version>11.0.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Win2D.uwp">
|
||||
<Version>1.21.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -397,6 +337,14 @@
|
||||
</SDKReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj">
|
||||
<Project>{a1ad7385-f92e-46ac-a349-43dc99b1e72d}</Project>
|
||||
<Name>FluentTerminal.App.Services</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\FluentTerminal.App.ViewModels\FluentTerminal.App.ViewModels.csproj">
|
||||
<Project>{44e8e225-f46c-47ec-9290-ffa08fccf1b5}</Project>
|
||||
<Name>FluentTerminal.App.ViewModels</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
|
||||
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
|
||||
<Name>FluentTerminal.Models</Name>
|
||||
@@ -406,6 +354,7 @@
|
||||
<Name>FluentTerminal.RuntimeComponent</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
@@ -414,6 +363,9 @@
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>xcopy /y "$(SolutionDir)FluentTerminal.Client\dist" "$(ProjectDir)Client\"</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
|
||||
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.4.0" />
|
||||
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.11.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>Fluent Terminal</DisplayName>
|
||||
@@ -26,15 +26,15 @@
|
||||
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" uap5:Optional="true" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap:Extension Category="windows.appService">
|
||||
<uap:AppService Name="FluentTerminalAppService" />
|
||||
</uap:Extension>
|
||||
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe" />
|
||||
<uap5:Extension Category="windows.appExecutionAlias" Executable="FluentTerminal.App.exe" EntryPoint="FluentTerminal.App.App">
|
||||
<uap5:AppExecutionAlias>
|
||||
<uap5:ExecutionAlias Alias="flute.exe" />
|
||||
</uap5:AppExecutionAlias>
|
||||
</uap5:Extension>
|
||||
<uap:Extension Category="windows.appService">
|
||||
<uap:AppService Name="FluentTerminalAppService" />
|
||||
</uap:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public class ClipboardService : IClipboardService
|
||||
{
|
||||
public Task<string> GetText()
|
||||
{
|
||||
var content = Clipboard.GetContent();
|
||||
if (content.Contains(StandardDataFormats.Text))
|
||||
{
|
||||
return content.GetTextAsync().AsTask();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetText(string text)
|
||||
{
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(text);
|
||||
Clipboard.SetContent(dataPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Pickers;
|
||||
using File = FluentTerminal.Models.File;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public class FileSystemService : IFileSystemService
|
||||
{
|
||||
public async Task<string> BrowseForDirectory()
|
||||
{
|
||||
var picker = new FolderPicker();
|
||||
picker.FileTypeFilter.Add(".whatever"); // else a ComException is thrown
|
||||
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
|
||||
|
||||
var folder = await picker.PickSingleFolderAsync();
|
||||
|
||||
return folder?.Path;
|
||||
}
|
||||
|
||||
public async Task<File> OpenFile(IEnumerable<string> fileTypes)
|
||||
{
|
||||
var picker = new FileOpenPicker
|
||||
{
|
||||
SuggestedStartLocation = PickerLocationId.ComputerFolder
|
||||
};
|
||||
|
||||
foreach (var fileType in fileTypes)
|
||||
{
|
||||
picker.FileTypeFilter.Add(fileType);
|
||||
}
|
||||
|
||||
var file = await picker.PickSingleFileAsync();
|
||||
if (file != null)
|
||||
{
|
||||
var stream = await file.OpenStreamForReadAsync().ConfigureAwait(true);
|
||||
|
||||
return new File(file.DisplayName, file.FileType, file.Path, stream);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task SaveTextFile(string name, string fileTypeDescription, string fileType, string content)
|
||||
{
|
||||
var picker = new FileSavePicker();
|
||||
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
|
||||
picker.SuggestedFileName = name;
|
||||
picker.FileTypeChoices.Add(fileTypeDescription, new List<string> { fileType });
|
||||
|
||||
var file = await picker.PickSaveFileAsync();
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
await FileIO.WriteTextAsync(file, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using FluentTerminal.Models;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IDefaultValueProvider
|
||||
{
|
||||
ShellConfiguration GetDefaultShellConfiguration();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using FluentTerminal.Models;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ISettingsService
|
||||
{
|
||||
ShellConfiguration GetShellConfiguration();
|
||||
void SaveShellConfiguration(ShellConfiguration spawnConfiguration);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using FluentTerminal.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ITerminalService
|
||||
{
|
||||
Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellConfiguration shellConfiguration);
|
||||
Task ResizeTerminal(int id, TerminalSize size);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
internal class DefaultValueProvider : IDefaultValueProvider
|
||||
{
|
||||
public ShellConfiguration GetDefaultShellConfiguration()
|
||||
{
|
||||
return new ShellConfiguration
|
||||
{
|
||||
Shell = ShellType.PowerShell,
|
||||
CustomShellLocation = string.Empty,
|
||||
WorkingDirectory = string.Empty,
|
||||
Arguments = string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
internal class SettingsService : ISettingsService
|
||||
{
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private ApplicationDataContainer _localSettings;
|
||||
private ApplicationDataContainer _roamingSettings;
|
||||
|
||||
public SettingsService(IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_localSettings = ApplicationData.Current.LocalSettings;
|
||||
_roamingSettings = ApplicationData.Current.RoamingSettings;
|
||||
ApplicationData.Current.DataChanged += OnDataChanged;
|
||||
}
|
||||
|
||||
private void OnDataChanged(ApplicationData sender, object args)
|
||||
{
|
||||
//todo handle data changed for roaming settings
|
||||
}
|
||||
|
||||
public ShellConfiguration GetShellConfiguration()
|
||||
{
|
||||
return _localSettings.ReadValueFromJson(nameof(ShellConfiguration), _defaultValueProvider.GetDefaultShellConfiguration());
|
||||
}
|
||||
|
||||
public void SaveShellConfiguration(ShellConfiguration spawnConfiguration)
|
||||
{
|
||||
_localSettings.WriteValueAsJson(nameof(ShellConfiguration), spawnConfiguration);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using Windows.Web.Http;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
internal class TerminalService : ITerminalService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public TerminalService()
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
public async Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellConfiguration shellConfiguration)
|
||||
{
|
||||
var request = new CreateTerminalRequest
|
||||
{
|
||||
Size = size,
|
||||
Configuration = shellConfiguration
|
||||
};
|
||||
|
||||
var response = await _httpClient.PostAsync(new Uri($"http://localhost:9000/terminals"), HttpJsonContent.From(request));
|
||||
var createTerminalResponse = await response.Content.ReadAs<CreateTerminalResponse>();
|
||||
|
||||
return createTerminalResponse;
|
||||
}
|
||||
|
||||
public Task ResizeTerminal(int id, TerminalSize size)
|
||||
{
|
||||
return _httpClient.PostAsync(new Uri($"http://localhost:9000/terminals/{id}/resize"), HttpJsonContent.From(size)).AsTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public class SystemFontService : ISystemFontService
|
||||
{
|
||||
public IEnumerable<string> GetSystemFontFamilies()
|
||||
{
|
||||
return CanvasTextFormat.GetSystemFontFamilies();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,6 @@
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<SolidColorBrush x:Key="TitleBarHoverBackgroundColor" Color="#18FFFFFF" />
|
||||
|
||||
<SolidColorBrush x:Key="TitleBarPressedBackgroundColor" Color="#30FFFFFF" />
|
||||
|
||||
<Style x:Key="TitleBarButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
@@ -50,14 +46,14 @@
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TitleBarHoverBackgroundColor}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemListLowColor}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TitleBarPressedBackgroundColor}" />
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemListMediumColor}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
@@ -68,4 +64,36 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ColorPreviewButtonStyle" TargetType="Button">
|
||||
<Setter Property="Width" Value="24" />
|
||||
<Setter Property="Height" Value="24" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -1,23 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class ApplicationDataContainerExtensions
|
||||
{
|
||||
public static T ReadValueFromJson<T>(this ApplicationDataContainer container, string name, T fallbackValue)
|
||||
{
|
||||
if (container.Values.TryGetValue(name, out object value))
|
||||
{
|
||||
JsonConvert.PopulateObject((string)value, fallbackValue);
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
public static void WriteValueAsJson<T>(this ApplicationDataContainer container, string name, T value)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(value);
|
||||
container.Values[name] = serialized;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using System.Globalization;
|
||||
using Windows.UI;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class ColorExtensions
|
||||
{
|
||||
public static string ToColorString(this Color color, bool allowTransparency)
|
||||
{
|
||||
if (allowTransparency)
|
||||
{
|
||||
return $"rgba({color.R:G}, {color.G:G}, {color.B:G}, {ToDoubleString(color.A)})";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||
}
|
||||
}
|
||||
|
||||
private static string ToDoubleString(byte alpha)
|
||||
{
|
||||
return (alpha / 256.0).ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static Color FromString(this string colorString)
|
||||
{
|
||||
if (colorString.Contains("rgba("))
|
||||
{
|
||||
colorString = colorString.Replace("rgba(", string.Empty);
|
||||
colorString = colorString.Replace(")", string.Empty);
|
||||
|
||||
var parts = colorString.Split(", ");
|
||||
|
||||
return new Color
|
||||
{
|
||||
A = (byte)(int)(double.Parse(parts[3]) * 256.0),
|
||||
R = byte.Parse(parts[0]),
|
||||
G = byte.Parse(parts[1]),
|
||||
B = byte.Parse(parts[2])
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return colorString.ToColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Windows.UI;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class ContrastHelper
|
||||
{
|
||||
public static ElementTheme GetIdealThemeForBackgroundColor(string color)
|
||||
{
|
||||
return GetIdealThemeForBackgroundColor(color.ToColor());
|
||||
}
|
||||
|
||||
public static ElementTheme GetIdealThemeForBackgroundColor(Color color)
|
||||
{
|
||||
if (((color.R * 0.299) + (color.G * 0.587) + (color.B * 0.114)) > 186)
|
||||
{
|
||||
return ElementTheme.Light;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ElementTheme.Dark;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetTitleBarButtonsForTheme(ElementTheme theme)
|
||||
{
|
||||
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
|
||||
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
|
||||
titleBar.ButtonForegroundColor = GetColor("SystemBaseHighColor", theme);
|
||||
titleBar.ButtonInactiveForegroundColor = GetColor("SystemBaseHighColor", theme);
|
||||
titleBar.ButtonHoverForegroundColor = GetColor("SystemBaseHighColor", theme);
|
||||
titleBar.ButtonPressedForegroundColor = GetColor("SystemBaseHighColor", theme);
|
||||
|
||||
titleBar.ButtonHoverBackgroundColor = GetColor("SystemListLowColor", theme);
|
||||
titleBar.ButtonPressedBackgroundColor = GetColor("SystemListMediumColor", theme);
|
||||
}
|
||||
|
||||
private static Color? GetColor(string name, ElementTheme theme)
|
||||
{
|
||||
if (theme == ElementTheme.Light)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "SystemBaseHighColor":
|
||||
return new Color { A = 0xFF, R = 0x00, G = 0x00, B = 0x00 };
|
||||
case "SystemListLowColor":
|
||||
return new Color { A = 0x19, R = 0x00, G = 0x00, B = 0x00 };
|
||||
case "SystemListMediumColor":
|
||||
return new Color { A = 0x33, R = 0x00, G = 0x00, B = 0x00 };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "SystemBaseHighColor":
|
||||
return new Color { A = 0xFF, R = 0xFF, G = 0xFF, B = 0xFF };
|
||||
case "SystemListLowColor":
|
||||
return new Color { A = 0x19, R = 0xFF, G = 0xFF, B = 0xFF };
|
||||
case "SystemListMediumColor":
|
||||
return new Color { A = 0x33, R = 0xFF, G = 0xFF, B = 0xFF };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Web.Http;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class HttpJsonContent
|
||||
{
|
||||
public static HttpStringContent From(object content)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(content);
|
||||
return new HttpStringContent(serialized, Windows.Storage.Streams.UnicodeEncoding.Utf8, "application/json");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Web.Http;
|
||||
|
||||
namespace FluentTerminal.App.Utilities
|
||||
{
|
||||
public static class IHttpContentExtensions
|
||||
{
|
||||
public static async Task<T> ReadAs<T>(this IHttpContent content)
|
||||
{
|
||||
var contentBody = await content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<T>(contentBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITerminalService _terminalService;
|
||||
private TerminalViewModel _selectedTerminal;
|
||||
private string _title;
|
||||
|
||||
public MainViewModel(ISettingsService settingsService, ITerminalService terminalService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_terminalService = terminalService;
|
||||
Title = "Fluent Terminal";
|
||||
|
||||
AddTerminalCommand = new RelayCommand(AddTerminal);
|
||||
ShowSettingsCommand = new RelayCommand(async () => await ShowSettings());
|
||||
|
||||
AddTerminal();
|
||||
}
|
||||
|
||||
public RelayCommand AddTerminalCommand { get; }
|
||||
public RelayCommand ShowSettingsCommand { get; }
|
||||
|
||||
public TerminalViewModel SelectedTerminal
|
||||
{
|
||||
get => _selectedTerminal;
|
||||
set => Set(ref _selectedTerminal, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
}
|
||||
|
||||
private void AddTerminal()
|
||||
{
|
||||
var terminal = new TerminalViewModel(_settingsService, _terminalService);
|
||||
terminal.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(TerminalViewModel.Title) && Terminals.Count == 1)
|
||||
{
|
||||
Title = terminal.Title;
|
||||
}
|
||||
};
|
||||
Terminals.Add(terminal);
|
||||
|
||||
SelectedTerminal = terminal;
|
||||
}
|
||||
|
||||
private Task ShowSettings()
|
||||
{
|
||||
return App.Instance.ShowSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.UI.Popups;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private ShellConfiguration _shellConfiguration;
|
||||
|
||||
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
|
||||
BrowseForCustomShellCommand = new RelayCommand(async () => await BrowseForCustomShell());
|
||||
BrowseForWorkingDirectoryCommand = new RelayCommand(async () => await BrowseForWorkingDirectory());
|
||||
RestoreDefaultsCommand = new RelayCommand<string>(async (area) => await RestoreDefaults(area));
|
||||
|
||||
_shellConfiguration = _settingsService.GetShellConfiguration();
|
||||
ShellType = _shellConfiguration.Shell;
|
||||
}
|
||||
|
||||
public RelayCommand BrowseForCustomShellCommand { get; }
|
||||
public RelayCommand BrowseForWorkingDirectoryCommand { get; }
|
||||
public RelayCommand<string> RestoreDefaultsCommand { get; }
|
||||
|
||||
public bool CMDIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.CMD;
|
||||
set => ShellType = ShellType.CMD;
|
||||
}
|
||||
|
||||
public bool CustomShellIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.Custom;
|
||||
set => ShellType = ShellType.Custom;
|
||||
}
|
||||
|
||||
public string CustomShellLocation
|
||||
{
|
||||
get => _shellConfiguration.CustomShellLocation;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.CustomShellLocation != value)
|
||||
{
|
||||
_shellConfiguration.CustomShellLocation = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get => _shellConfiguration.WorkingDirectory;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.WorkingDirectory != value)
|
||||
{
|
||||
_shellConfiguration.WorkingDirectory = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Arguments
|
||||
{
|
||||
get => _shellConfiguration.Arguments;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.Arguments != value)
|
||||
{
|
||||
_shellConfiguration.Arguments = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool PoweShellIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.PowerShell;
|
||||
set => ShellType = ShellType.PowerShell;
|
||||
}
|
||||
|
||||
public ShellType ShellType
|
||||
{
|
||||
get => _shellConfiguration.Shell;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.Shell != value)
|
||||
{
|
||||
_shellConfiguration.Shell = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(CustomShellIsSelected));
|
||||
RaisePropertyChanged(nameof(PoweShellIsSelected));
|
||||
RaisePropertyChanged(nameof(CMDIsSelected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseForCustomShell()
|
||||
{
|
||||
var picker = new FileOpenPicker();
|
||||
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
|
||||
picker.FileTypeFilter.Add(".exe");
|
||||
|
||||
var file = await picker.PickSingleFileAsync().AsTask();
|
||||
if (file != null)
|
||||
{
|
||||
CustomShellLocation = file.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseForWorkingDirectory()
|
||||
{
|
||||
var picker = new FolderPicker();
|
||||
picker.FileTypeFilter.Add(".whatever"); // else a ComException is thrown
|
||||
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
|
||||
|
||||
var folder = await picker.PickSingleFolderAsync();
|
||||
if (folder != null)
|
||||
{
|
||||
WorkingDirectory = folder.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults(string area)
|
||||
{
|
||||
var dialog = new MessageDialog("Are you sure you want to restore default values for this page?", "Please confirm");
|
||||
dialog.Commands.Add(new UICommand("OK"));
|
||||
dialog.Commands.Add(new UICommand("Cancel"));
|
||||
|
||||
var result = await dialog.ShowAsync();
|
||||
|
||||
if (result.Label == "OK" && area == "shell")
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultShellConfiguration();
|
||||
ShellType = defaults.Shell;
|
||||
CustomShellLocation = defaults.CustomShellLocation;
|
||||
WorkingDirectory = defaults.WorkingDirectory;
|
||||
Arguments = defaults.Arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Views;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class TerminalViewModel : ViewModelBase
|
||||
{
|
||||
private ITerminalView _terminalView;
|
||||
private string _title;
|
||||
private string _resizeOverlayContent;
|
||||
private bool _showResizeOverlay;
|
||||
private DispatcherTimer _resizeOverlayTimer;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITerminalService _terminalService;
|
||||
private int _terminalId;
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
}
|
||||
|
||||
public bool ShowResizeOverlay
|
||||
{
|
||||
get => _showResizeOverlay;
|
||||
set
|
||||
{
|
||||
Set(ref _showResizeOverlay, value);
|
||||
if (value)
|
||||
{
|
||||
if (_resizeOverlayTimer.IsEnabled)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
}
|
||||
_resizeOverlayTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ResizeOverlayContent
|
||||
{
|
||||
get => _resizeOverlayContent;
|
||||
set => Set(ref _resizeOverlayContent, value);
|
||||
}
|
||||
|
||||
public TerminalViewModel(ISettingsService settingsService, ITerminalService terminalService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_terminalService = terminalService;
|
||||
_resizeOverlayTimer = new DispatcherTimer();
|
||||
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
|
||||
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
|
||||
}
|
||||
|
||||
private void OnResizeOverlayTimerFinished(object sender, object e)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
ShowResizeOverlay = false;
|
||||
}
|
||||
|
||||
public async Task OnViewIsReady(ITerminalView terminalView)
|
||||
{
|
||||
_terminalView = terminalView;
|
||||
|
||||
var size = await _terminalView.CreateTerminal();
|
||||
var configuration = _settingsService.GetShellConfiguration();
|
||||
|
||||
var response = await _terminalService.CreateTerminal(size, configuration);
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
_terminalId = response.Id;
|
||||
_terminalView.TerminalSizeChanged += OnTerminalSizeChanged;
|
||||
_terminalView.TerminalTitleChanged += OnTerminalTitleChanged;
|
||||
|
||||
await _terminalView.ConnectToSocket(response.WebSocketUrl);
|
||||
Initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var dialog = new MessageDialog(response.Error, "Error");
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTerminalTitleChanged(object sender, string e)
|
||||
{
|
||||
Title = e;
|
||||
}
|
||||
|
||||
private async void OnTerminalSizeChanged(object sender, TerminalSize e)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ResizeOverlayContent = $"{e.Columns} x {e.Rows}";
|
||||
ShowResizeOverlay = true;
|
||||
await _terminalService.ResizeTerminal(_terminalId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public interface ITerminalView
|
||||
{
|
||||
event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
event EventHandler<string> TerminalTitleChanged;
|
||||
|
||||
Task<TerminalSize> CreateTerminal();
|
||||
Task ConnectToSocket(string url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<UserControl
|
||||
x:Class="FluentTerminal.App.Views.KeyBindingsView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:settings="using:FluentTerminal.App.ViewModels.Settings"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,6,24,0"
|
||||
Text="{x:Bind ViewModel.CommandName, Mode=OneWay}" />
|
||||
<ItemsControl Grid.Column="1" ItemsSource="{x:Bind ViewModel.KeyBindings, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="settings:KeyBindingViewModel">
|
||||
<StackPanel
|
||||
x:Name="Root"
|
||||
Margin="0,0,0,6"
|
||||
Orientation="Horizontal">
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Ctrl, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="Ctrl" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Meta, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<FontIcon
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="f" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Alt, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="Alt" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="48"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Visibility="{x:Bind Shift, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="Shift" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Width="64"
|
||||
Height="32"
|
||||
Margin="0,0,6,0"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Key, Mode=OneWay, Converter={StaticResource IntToExtendedVirtualKeyConverter}}" />
|
||||
</Grid>
|
||||
<HyperlinkButton
|
||||
Margin="12,0,0,0"
|
||||
Command="{x:Bind EditCommand}"
|
||||
Content="Edit" />
|
||||
<HyperlinkButton
|
||||
Margin="12,0,0,0"
|
||||
Command="{x:Bind DeleteCommand}"
|
||||
Content="Delete" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,23 @@
|
||||
using FluentTerminal.App.ViewModels.Settings;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class KeyBindingsView : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty ViewModelProperty =
|
||||
DependencyProperty.Register(nameof(ViewModel), typeof(KeyBindingsViewModel), typeof(KeyBindingsView), new PropertyMetadata(null));
|
||||
|
||||
public KeyBindingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public KeyBindingsViewModel ViewModel
|
||||
{
|
||||
get { return (KeyBindingsViewModel)GetValue(ViewModelProperty); }
|
||||
set { SetValue(ViewModelProperty, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,48 +2,136 @@
|
||||
x:Class="FluentTerminal.App.Views.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:behaviors="using:FluentTerminal.App.Behaviors"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters"
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Background="{ThemeResource SystemControlAcrylicWindowBrush}"
|
||||
RequestedTheme="Dark"
|
||||
xmlns:viewmodels="using:FluentTerminal.App.ViewModels"
|
||||
RequestedTheme="{x:Bind ViewModel.Background, Mode=OneWay, Converter={StaticResource BackgroundToApplicationThemeConverter}}"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:TerminalViewModelToViewConverter x:Key="TerminalViewModelToViewConverter" />
|
||||
</Page.Resources>
|
||||
<Grid x:Name="Root">
|
||||
<Grid.Background>
|
||||
<AcrylicBrush
|
||||
BackgroundSource="HostBackdrop"
|
||||
FallbackColor="{x:Bind ViewModel.Background, Mode=OneWay}"
|
||||
TintColor="{x:Bind ViewModel.Background, Mode=OneWay}"
|
||||
TintOpacity="{x:Bind ViewModel.BackgroundOpacity, Mode=OneWay}" />
|
||||
</Grid.Background>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Rectangle x:Name="TitleBar" Fill="Transparent" />
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Height="{Binding CoreTitleBarHeight, Mode=OneWay}"
|
||||
Canvas.ZIndex="3">
|
||||
<Rectangle x:Name="TitleBar" Fill="Transparent" />
|
||||
<Grid Padding="{Binding CoreTitleBarPadding, Mode=OneWay}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Canvas.ZIndex="9"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource WindowTitleTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.Title, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Command="{x:Bind ViewModel.ShowSettingsCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<Viewbox Width="16" Height="16">
|
||||
<SymbolIcon Symbol="Setting" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
Margin="{Binding CoreTitleBarPadding, Mode=OneWay}"
|
||||
SizeChanged="Grid_SizeChanged">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource WindowTitleTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.Title, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.Terminals.Count, Mode=OneWay, Converter={StaticResource OneToVisibleConverter}}" />
|
||||
<ListView
|
||||
x:Name="Tabs"
|
||||
Grid.Column="0"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
ItemsSource="{x:Bind ViewModel.Terminals}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollMode="Auto"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedTerminal, Mode=TwoWay}"
|
||||
Visibility="{x:Bind ViewModel.Terminals.Count, Mode=OneWay, Converter={StaticResource OneToCollapsedConverter}}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:TerminalViewModel">
|
||||
<Grid
|
||||
Height="32"
|
||||
Margin="0"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<behaviors:MiddleClickBehavior>
|
||||
<behaviors:MiddleClickBehavior.Actions>
|
||||
<core:InvokeCommandAction Command="{x:Bind CloseCommand}" />
|
||||
</behaviors:MiddleClickBehavior.Actions>
|
||||
</behaviors:MiddleClickBehavior>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
<Border
|
||||
Grid.ColumnSpan="3"
|
||||
Height="3"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{ThemeResource SystemAccentColor}"
|
||||
Visibility="{x:Bind IsUnderlined, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="12,8,6,0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Title, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Width="32"
|
||||
Command="{x:Bind CloseCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="Close">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Cancel" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="MinWidth" Value="24" />
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
</ListView>
|
||||
<StackPanel
|
||||
x:Name="TitleBarButtons"
|
||||
Grid.Column="2"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.AddTerminalCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="New Tab">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Button
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.ShowSettingsCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="Settings">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Setting" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ContentControl
|
||||
x:Name="TerminalContainer"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using System.ComponentModel;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
@@ -40,12 +40,16 @@ namespace FluentTerminal.App.Views
|
||||
{
|
||||
InitializeComponent();
|
||||
Root.DataContext = this;
|
||||
SetTitleBarColors();
|
||||
Window.Current.SetTitleBar(TitleBar);
|
||||
Loaded += OnLoaded;
|
||||
Unloaded += OnUnloaded;
|
||||
Window.Current.Activated += OnWindowActivated;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
|
||||
RegisterPropertyChangedCallback(RequestedThemeProperty, (s, e) =>
|
||||
{
|
||||
ContrastHelper.SetTitleBarButtonsForTheme(RequestedTheme);
|
||||
});
|
||||
|
||||
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
@@ -56,16 +60,11 @@ namespace FluentTerminal.App.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
App.Instance.TerminalWindowClosed();
|
||||
}
|
||||
|
||||
private async void OnWindowActivated(object sender, WindowActivatedEventArgs e)
|
||||
{
|
||||
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && TerminalContainer.Content is TerminalView terminal)
|
||||
{
|
||||
await terminal.FocusWebView();
|
||||
await terminal.FocusWebView().ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,18 +84,6 @@ namespace FluentTerminal.App.Views
|
||||
UpdateLayoutMetrics();
|
||||
}
|
||||
|
||||
private void SetTitleBarColors()
|
||||
{
|
||||
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
titleBar.ButtonForegroundColor = Colors.White;
|
||||
titleBar.ButtonInactiveForegroundColor = Colors.White;
|
||||
titleBar.ButtonPressedForegroundColor = Colors.White;
|
||||
titleBar.ButtonHoverForegroundColor = Colors.White;
|
||||
titleBar.ButtonHoverBackgroundColor = Color.FromArgb(24, 255, 255, 255);
|
||||
titleBar.ButtonPressedBackgroundColor = Color.FromArgb(48, 255, 255, 255);
|
||||
}
|
||||
|
||||
private void UpdateLayoutMetrics()
|
||||
{
|
||||
@@ -106,5 +93,13 @@ namespace FluentTerminal.App.Views
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(CoreTitleBarPadding)));
|
||||
}
|
||||
}
|
||||
|
||||
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (sender is Grid grid)
|
||||
{
|
||||
Tabs.MaxWidth = grid.ActualWidth - grid.ColumnDefinitions[2].ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,11 @@
|
||||
x:Class="FluentTerminal.App.Views.SettingsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Loaded="Page_Loaded"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:TrueToVisibleConverter x:Key="TrueToVisibleConverter" />
|
||||
<converters:FalseToVisibleConverter x:Key="FalseToVisibleConverter" />
|
||||
</Page.Resources>
|
||||
<Grid x:Name="Root" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid x:Name="Root">
|
||||
<TextBlock
|
||||
x:Name="AppTitle"
|
||||
Canvas.ZIndex="1"
|
||||
@@ -20,77 +16,65 @@
|
||||
Visibility="{Binding IsPaneOpen, ElementName=NavigationView, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
<NavigationView
|
||||
x:Name="NavigationView"
|
||||
AlwaysShowHeader="False"
|
||||
Canvas.ZIndex="0"
|
||||
CompactModeThresholdWidth="560"
|
||||
ExpandedModeThresholdWidth="560"
|
||||
IsSettingsVisible="False"
|
||||
Loaded="NavigationView_Loaded"
|
||||
OpenPaneLength="280"
|
||||
OpenPaneLength="200"
|
||||
SelectionChanged="NavigationView_SelectionChanged">
|
||||
<NavigationView.MenuItems>
|
||||
<NavigationViewItem
|
||||
Content="General"
|
||||
Icon="Setting"
|
||||
Tag="general" />
|
||||
<NavigationViewItem
|
||||
Content="Key Bindings"
|
||||
Icon="Setting"
|
||||
Tag="keyBindings" />
|
||||
<NavigationViewItem
|
||||
Content="Shell"
|
||||
Icon="Setting"
|
||||
Tag="shell" />
|
||||
<NavigationViewItem
|
||||
Content="Font"
|
||||
Icon="Setting"
|
||||
Tag="font" />
|
||||
<NavigationViewItem
|
||||
Content="Colors"
|
||||
Icon="Setting"
|
||||
Tag="colors" />
|
||||
<NavigationViewItem Content="General" Tag="general">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="e"
|
||||
ToolTipService.ToolTip="General" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Terminal" Tag="terminal">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="d"
|
||||
ToolTipService.ToolTip="Terminal" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Profiles" Tag="profiles">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="a"
|
||||
ToolTipService.ToolTip="Shell" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Keybindings" Tag="keyBindings">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="b"
|
||||
ToolTipService.ToolTip="Keybindings" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Themes" Tag="themes">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="c"
|
||||
ToolTipService.ToolTip="Themes" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
</NavigationView.MenuItems>
|
||||
<NavigationView.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="24,10,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{Binding CoreTitleBarHeight}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="0,-2,0,0"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="Settings"
|
||||
Visibility="{Binding IsPaneOpen, ElementName=NavigationView, Converter={StaticResource FalseToVisibleConverter}}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="28"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="{Binding SelectedItem.Content, ElementName=NavigationView}" />
|
||||
<CommandBar
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
DefaultLabelPosition="Right">
|
||||
<AppBarButton
|
||||
Command="{Binding ViewModel.RestoreDefaultsCommand}"
|
||||
CommandParameter="{Binding SelectedItem.Tag, ElementName=NavigationView}"
|
||||
Icon="Undo"
|
||||
Label="Restore Defaults" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</NavigationView.HeaderTemplate>
|
||||
<Frame x:Name="ContentFrame" Margin="24" />
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Frame x:Name="ContentFrame" />
|
||||
<TextBlock
|
||||
Margin="12,8,0,0"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="Settings"
|
||||
Visibility="{Binding IsPaneOpen, ElementName=NavigationView, Converter={StaticResource FalseToVisibleConverter}}" />
|
||||
</Grid>
|
||||
</NavigationView>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,41 +1,70 @@
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Views.SettingsPages;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using FluentTerminal.App.Utilities;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class SettingsPage : Page
|
||||
{
|
||||
public double CoreTitleBarHeight { get; }
|
||||
|
||||
public SettingsViewModel ViewModel { get; private set; }
|
||||
private readonly UISettings _uiSettings;
|
||||
private readonly ApplicationViewTitleBar _titleBar;
|
||||
private readonly CoreDispatcher _dispatcher;
|
||||
private bool _onThemesPage;
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
Root.DataContext = this;
|
||||
|
||||
ApplicationViewTitleBar titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
titleBar.ButtonBackgroundColor = Colors.Transparent;
|
||||
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
|
||||
titleBar.ButtonForegroundColor = (Color)this.Resources["SystemBaseHighColor"];
|
||||
|
||||
ApplicationView.GetForCurrentView().Title = "Settings";
|
||||
|
||||
CoreApplicationViewTitleBar coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
|
||||
coreTitleBar.LayoutMetricsChanged += TitleBar_LayoutMetricsChanged;
|
||||
CoreTitleBarHeight = coreTitleBar.Height;
|
||||
|
||||
_titleBar = ApplicationView.GetForCurrentView().TitleBar;
|
||||
_dispatcher = Window.Current.Dispatcher;
|
||||
|
||||
SetTitleBarColors();
|
||||
|
||||
_uiSettings = new UISettings();
|
||||
_uiSettings.ColorValuesChanged += OnColorValuesChanged;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += SettingsPage_CloseRequested;
|
||||
}
|
||||
|
||||
private void OnColorValuesChanged(UISettings sender, object args)
|
||||
{
|
||||
if (_onThemesPage == false)
|
||||
{
|
||||
SetTitleBarColors();
|
||||
}
|
||||
}
|
||||
|
||||
private IAsyncAction SetTitleBarColors()
|
||||
{
|
||||
return _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
var theme = Application.Current.RequestedTheme == ApplicationTheme.Light ? ElementTheme.Light : ElementTheme.Dark;
|
||||
ContrastHelper.SetTitleBarButtonsForTheme(theme);
|
||||
});
|
||||
}
|
||||
|
||||
public double CoreTitleBarHeight { get; }
|
||||
|
||||
public SettingsViewModel ViewModel { get; private set; }
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
if (e.Parameter is SettingsViewModel viewModel)
|
||||
@@ -44,9 +73,51 @@ namespace FluentTerminal.App.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigationView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationView.SelectedItem = NavigationView.MenuItems.Cast<NavigationViewItemBase>().FirstOrDefault(m => m.Tag.ToString() == "general");
|
||||
}
|
||||
|
||||
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||
{
|
||||
_onThemesPage = false;
|
||||
|
||||
if (args.SelectedItem is NavigationViewItem item)
|
||||
{
|
||||
switch (item.Tag)
|
||||
{
|
||||
case "general":
|
||||
ContentFrame.Navigate(typeof(GeneralSettings), ViewModel.General);
|
||||
break;
|
||||
|
||||
case "profiles":
|
||||
ContentFrame.Navigate(typeof(ShellProfileSettings), ViewModel.Shell);
|
||||
break;
|
||||
|
||||
case "themes":
|
||||
ContentFrame.Navigate(typeof(ThemeSettings), ViewModel.Themes);
|
||||
_onThemesPage = true;
|
||||
break;
|
||||
|
||||
case "terminal":
|
||||
ContentFrame.Navigate(typeof(TerminalSettings), ViewModel.Terminal);
|
||||
break;
|
||||
|
||||
case "keyBindings":
|
||||
ContentFrame.Navigate(typeof(KeyBindingSettings), ViewModel.KeyBindings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_onThemesPage)
|
||||
{
|
||||
SetTitleBarColors();
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsPage_CloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
App.Instance.SettingsWindowClosed();
|
||||
ViewModel.Close();
|
||||
}
|
||||
|
||||
private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
|
||||
@@ -54,34 +125,9 @@ namespace FluentTerminal.App.Views
|
||||
AppTitle.Margin = new Thickness(CoreApplication.GetCurrentView().TitleBar.SystemOverlayLeftInset + 12, 8, 0, 0);
|
||||
}
|
||||
|
||||
private void NavigationView_Loaded(object sender, RoutedEventArgs e)
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationView.SelectedItem = NavigationView.MenuItems.Cast<NavigationViewItemBase>().FirstOrDefault(m => m.Tag.ToString() == "shell");
|
||||
}
|
||||
|
||||
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||
{
|
||||
if (args.SelectedItem is NavigationViewItem item)
|
||||
{
|
||||
switch (item.Tag)
|
||||
{
|
||||
case "general":
|
||||
ContentFrame.Navigate(typeof(GeneralSettings), ViewModel);
|
||||
break;
|
||||
case "shell":
|
||||
ContentFrame.Navigate(typeof(ShellSettings), ViewModel);
|
||||
break;
|
||||
case "colors":
|
||||
ContentFrame.Navigate(typeof(ColorSettings), ViewModel);
|
||||
break;
|
||||
case "font":
|
||||
ContentFrame.Navigate(typeof(FontSettings), ViewModel);
|
||||
break;
|
||||
case "keyBindings":
|
||||
ContentFrame.Navigate(typeof(KeyBindingSettings), ViewModel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ApplicationView.GetForCurrentView().TryResizeView(new Size { Width = 800, Height = 600 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<Page
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.ColorSettings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:FluentTerminal.App.Views.SettingsPages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
|
||||
</Page>
|
||||
Loaded 100 of 448 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user