Compare commits

...
101 Commits
Author SHA1 Message Date
FelixSe 2527657261 Increase version number 2018-05-31 23:51:59 +02:00
FelixSe 5414987ab6 made background opacity global 2018-05-31 22:30:07 +02:00
FelixSe 327f0d5dd5 Improve titlebar color handling for settings window 2018-05-31 21:10:02 +02:00
FelixSe bd64aacbab Add option to set scrollbar style 2018-05-30 21:09:06 +02:00
FelixSe c79b513470 Fixed bug that tied name and arguments in profile editor together 2018-05-30 19:46:19 +02:00
FelixSe bd3ca90514 Fix crash in SettingsService, increase version 2018-05-29 19:09:17 +02:00
Felix 88f88698a2 Update README.md 2018-05-28 22:23:27 +02:00
FelixSe dcdf8d1854 Added search function 2018-05-27 20:22:21 +02:00
FelixSe fb717b3750 Fix crashes on delete or restore keybindings 2018-05-27 13:49:53 +02:00
FelixSe aff7b636f1 Add option to select shell profile while creating a new tab (ctrl+shift+t by default) 2018-05-27 13:35:02 +02:00
FelixSe 99a7de25d3 Close tab/window when shell process has exited 2018-05-26 22:32:15 +02:00
FelixSe 28d950ed9a Support multiple shell profiles 2018-05-26 22:05:49 +02:00
Felix 1490470e91 Merge pull request #22 from skipmeister123/master
Added "Homebrew" theme
2018-05-24 21:52:38 +02:00
Hans Kokx 236e0ff2e1 Added "Homebrew" theme 2018-05-24 15:43:24 -04:00
Felix 5cb9158077 Update README.md 2018-05-23 21:47:16 +02:00
FelixSe 941c7c1948 update to xterm 3.4.0 and use dynamic charAtlas 2018-05-20 12:16:03 +02:00
Felix 45e096c033 Update README.md 2018-05-06 19:49:08 +02:00
FelixSe d37da0ef31 fixed last ambiguous xcopy 2018-05-06 18:52:29 +02:00
FelixSe 49abf56ec6 fixed another ambiguous xcopy 2018-05-02 22:42:22 +02:00
FelixSe 618a2485e1 fix postbuild event when target directory is not yet created 2018-05-02 22:27:02 +02:00
felixse cdeeeb6fa8 Make hintpath in Models.csproj use a variable for program files (x86) 2018-05-02 11:02:00 +02:00
felixse be833cc174 Make hintpaths in SystemTray.csproj use a variable for program files (x86) 2018-05-02 10:40:22 +02:00
FelixSe 39955bcb3e Started writing some unit tests 2018-05-01 21:22:34 +02:00
FelixSe e9b76a6867 Code cleanup 2018-05-01 13:10:47 +02:00
FelixSe 89bce37dbd Fix bug while adding secondary keybindings 2018-05-01 13:10:41 +02:00
FelixSe 82a195c2f2 Use AppService instead of WebAPI for inter process communication 2018-04-29 15:52:57 +02:00
FelixSe c48889cbbc Execute copy only when selection is not empty, this allows for ctrl + c to be used as the default copy keybinding 2018-04-22 13:12:50 +02:00
FelixSe dd9d5d2e72 Extend key enum, improve displaying 2018-04-22 12:45:56 +02:00
FelixSe 7f7f15ea03 Support meta key for keybindings 2018-04-22 12:45:30 +02:00
FelixSe f502303426 Update xterm to 3.3.0 2018-04-15 16:23:24 +02:00
FelixSe 2f2ed06d58 Refactored App startup 2018-04-15 16:13:20 +02:00
FelixSe 809167a872 removed unnecessary quotes in context menu integration script 2018-04-15 16:11:26 +02:00
FelixSe 5d3fd25098 remove unused field 2018-04-15 16:10:41 +02:00
FelixSe 6674fd0ad9 Fix for open Fluent Terminal here failing if no other terminal window open 2018-03-18 14:07:34 +01:00
FelixSe f874cb070d Fix race condition while retrieving the systemtrays API port 2018-03-18 13:37:20 +01:00
FelixSe 40230bfce8 Rename tray command to 'new terminal' since it does not necessarily create a new window 2018-03-14 22:20:04 +01:00
FelixSe 3afa2b5f25 Remove App.Instance singleton 2018-03-11 00:26:23 +01:00
FelixSe 94ba1b8324 Update xterm.js to 3.2.0 2018-03-10 23:33:41 +01:00
FelixSe 6dfacdfebd Remove almost unused AppService in favor of using flute.exe 2018-03-10 00:06:25 +01:00
FelixSe 1ca7371103 No longer depending on a static port for communication with Tray process 2018-03-06 21:45:24 +01:00
Felix 1cb388d4af Update README.md 2018-03-05 21:16:56 +01:00
FelixSe 3bdd96c388 Screenshots look better without cursors.. 2018-03-05 21:02:10 +01:00
FelixSe 5bc5bf224f Add Screenshots 2018-03-05 20:56:45 +01:00
FelixSe 01815db1fe Fix typo, set correct c# version in csproj, increase version 2018-03-05 19:26:33 +01:00
FelixSe 80cf43d3bf Store client in app data instead of serving it 2018-03-04 22:26:30 +01:00
FelixSe 050f162c08 Split SettingsPageViewModel into one per page, fix buggy radio buttons 2018-03-04 14:40:05 +01:00
FelixSe f5e03c9856 Register converters only once 2018-03-04 13:22:27 +01:00
FelixSe f763798683 Added option to create new terminals (e.g from context menu) as tabs instead of windows 2018-03-04 13:07:32 +01:00
FelixSe 46120e3431 Make toggle window hotkey configurable 2018-03-03 11:44:46 +01:00
FelixSe 829077883b Improved layout for keybindings settings page 2018-02-26 22:00:58 +01:00
FelixSe 8c928faa09 Some cosmetic changes 2018-02-25 22:31:52 +01:00
FelixSe 1654e9f1f5 Propagate changed keyBindings immediately 2018-02-25 21:52:17 +01:00
FelixSe f542331192 Editable keybindings 2018-02-25 15:40:36 +01:00
FelixSe 6830adb788 Increase version, add generated files to .gitignore 2018-02-22 22:33:18 +01:00
FelixSe 02e015371c Supress webview native context menu 2018-02-22 22:07:21 +01:00
FelixSe 80bdfa012c Show notification if registering hotkey for toggle window fails 2018-02-20 22:04:23 +01:00
FelixSe 70326e9585 Add missing whitespace in product name 2018-02-20 22:04:20 +01:00
FelixSe c0e1415b6e Use shell executable name as tab title 2018-02-20 21:56:51 +01:00
FelixSe 13a3402b58 Improve error message for failed shell startup 2018-02-20 18:41:34 +01:00
FelixSe 73703412f8 Fix for shells no longer getting cleaned up on window close 2018-02-20 18:18:45 +01:00
FelixSe ab226d95d4 Added option to confirm before closing tab/window 2018-02-19 23:34:22 +01:00
FelixSe de7a76bb3b Add icons to settings pages 2018-02-19 21:01:09 +01:00
FelixSe 379f58b4b8 Rename open to show in tray icon 2018-02-19 20:38:37 +01:00
FelixSe 3a04cd3db2 Close tabs with middle click 2018-02-19 20:37:25 +01:00
FelixSe df0d3f080b Use non acrylic accent color for resize overlay 2018-02-19 19:34:54 +01:00
FelixSe 04131d271a Improve size of titlebar button icons 2018-02-19 19:31:31 +01:00
FelixSe d66fe26634 Global hotkey for show/hide window 2018-02-18 23:10:05 +01:00
FelixSe 8870f0621c Keyboard shortcuts 2018-02-18 21:08:22 +01:00
FelixSe e5250a7ae8 Reorderable tabs for windows 2018-02-17 17:32:48 +01:00
FelixSe b2b6cedf2c Increase version to 0.1.5.0 2018-02-16 17:37:54 +01:00
Felix e777c2c5ac Update README.md 2018-02-16 17:31:46 +01:00
FelixSe 7dcd8e1fbb Make background opacity configurable 2018-02-15 23:00:30 +01:00
FelixSe 37518cf88b Terminal Settings 2018-02-15 22:26:55 +01:00
FelixSe ca0e22b8d0 Reenable settings window title bar when navigation view is collapsed 2018-02-15 16:50:06 +01:00
FelixSe 074f5f9c7e Wrap dialogs in service 2018-02-14 18:00:00 +01:00
FelixSe 0b40963c82 change theme also on existing windows 2018-02-14 11:56:52 +01:00
FelixSe 0b6b8a9968 Fix selection color for xterm.js theme 2018-02-14 10:04:20 +01:00
FelixSe 47613454c7 Switch to official xterm.js 2018-02-14 10:04:05 +01:00
FelixSe 96d063b4fd Theme settings 2018-02-13 23:36:23 +01:00
FelixSe fec44fb0b5 fix context menu integrations for directories that contain whitespaces 2018-02-06 19:12:39 +01:00
FelixSe 58e7de5271 fix explorer integration path for installations where HomeDrive is not C: 2018-02-06 19:09:20 +01:00
FelixSe 466b7a7503 Display default window title again when terminal is resetting its title 2018-02-05 23:30:04 +01:00
FelixSe 50f9c84420 Support specifying the startup directory via command line and provide basic Explorer context menu integration 2018-02-05 23:23:03 +01:00
FelixSe f07dae98ee Escape shell executable path in commandLine 2018-02-04 00:12:44 +01:00
FelixSe 1cc74f79b4 Fix shell arguments 2018-02-03 23:34:07 +01:00
FelixSe 60c4f0518d Recover missing Client files 2018-02-03 23:20:53 +01:00
FelixSe f9fb05b194 Removed generated files from repository and added to .gitignore 2018-02-03 22:45:17 +01:00
FelixSe d933f60b8b Merge branch 'master' of https://github.com/felixse/FluentTerminal 2018-02-03 22:40:33 +01:00
FelixSe 45bba6e2eb Use hyper.js xterm fork for now instead of including it in this repository 2018-02-03 22:39:32 +01:00
FelixSe 210aeedc25 Update winpty to 0.4.3 2018-02-03 22:04:22 +01:00
Felix acfb8e8883 Update README.md 2018-02-03 21:46:00 +01:00
FelixSe 9b61d11f6a Added settings window to configure which shell to use 2018-02-03 21:25:34 +01:00
FelixSe 49ee8489d6 finished resize overlay 2018-01-31 21:18:13 +01:00
FelixSe 7b485ce407 setup dependency injection 2018-01-31 19:37:41 +01:00
FelixSe 66465b525f add MVVM infrastructure 2018-01-31 19:26:34 +01:00
FelixSe 9c5d8c27fd Move logic from web client to app to prepare configuration 2018-01-29 22:26:37 +01:00
FelixSe 4ae3be544b Resize terminal on window resize 2018-01-28 22:06:57 +01:00
FelixSe e32e0398ee Remove unnecessary build step 2018-01-28 22:05:38 +01:00
FelixSe fab22f2479 cleanup app.xaml.cs 2018-01-28 21:50:20 +01:00
Felix b2151c69ab Merge pull request #1 from vitorgrs/master
Remove splashscreen
2018-01-25 22:45:40 +01:00
Vitor Mikaelson f5fba8517a Remove splashscreen 2018-01-25 20:51:02 +00:00
410 changed files with 8162 additions and 49504 deletions

No files matched your search

+2
View File
@@ -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,171 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{812A09E4-3A05-41AB-8E9A-DC4389B5D73E}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FluentTerminal.App.Test</RootNamespace>
<AssemblyName>FluentTerminal.App.Test</AssemblyName>
<DefaultLanguage>de-DE</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.16299.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<PackageCertificateKeyFile>FluentTerminal.App.Test_TemporaryKey.pfx</PackageCertificateKeyFile>
<UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">$(VisualStudioVersion)</UnitTestPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<SDKReference Include="TestPlatform.Universal, Version=$(UnitTestPlatformVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\DialogServiceTests.cs" />
<Compile Include="Services\KeyboardCommandServiceTests.cs" />
<Compile Include="UnitTestApp.xaml.cs">
<DependentUpon>UnitTestApp.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="UnitTestApp.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<None Include="FluentTerminal.App.Test_TemporaryKey.pfx" />
</ItemGroup>
<ItemGroup>
<Content Include="Properties\UnitTestApp.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoFixture">
<Version>4.2.1</Version>
</PackageReference>
<PackageReference Include="FluentAssertions">
<Version>5.3.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.1.2</Version>
</PackageReference>
<PackageReference Include="Moq">
<Version>4.8.2</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>1.2.1</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>1.2.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.App\FluentTerminal.App.csproj">
<Project>{41020100-3414-4e0a-9a8e-92d3c4179df8}</Project>
<Name>FluentTerminal.App</Name>
</ProjectReference>
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
<Name>FluentTerminal.Models</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
IgnorableNamespaces="uap mp">
<Identity Name="63c2a56f-7075-408a-a37d-24bb7fb21665"
Publisher="CN=Felix"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="63c2a56f-7075-408a-a37d-24bb7fb21665" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>FluentTerminal.App.Test</DisplayName>
<PublisherDisplayName>Felix</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="vstest.executionengine.universal.App"
Executable="$targetnametoken$.exe"
EntryPoint="FluentTerminal.App.Test.App">
<uap:VisualElements
DisplayName="FluentTerminal.App.Test"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="FluentTerminal.App.Test"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClientServer" />
</Capabilities>
</Package>
@@ -0,0 +1,18 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("FluentTerminal.App.Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FluentTerminal.App.Test")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyMetadata("TargetPlatform","UAP")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
@@ -0,0 +1,29 @@
<!--
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
developers. However, you can modify these parameters to modify the behavior of the .NET Native
optimizer.
Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919
To fully enable reflection for App1.MyClass and all of its public/private members
<Type Name="App1.MyClass" Dynamic="Required All"/>
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
<Namespace Name="DataClasses.ViewModels" Seralize="All" />
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<!--
An Assembly element with Name="*Application*" applies to all assemblies in
the application package. The asterisks are not wildcards.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Add your application specific runtime directives here. -->
</Application>
</Directives>
@@ -0,0 +1,62 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Implementation;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace FluentTerminal.App.Test.Services
{
[TestClass]
public class DialogServiceTests
{
private Fixture _fixture;
[TestInitialize]
public void TestInitialize()
{
_fixture = new Fixture();
}
[TestMethod]
public void ShowDialogAsync_TitleIsEmpty_ThrowsArgumentNullException()
{
var title = string.Empty;
var content = _fixture.Create<string>();
var buttons = _fixture.CreateMany<DialogButton>(2);
var dialogService = new DialogService(Mock.Of<ISettingsService>());
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("title");
}
[TestMethod]
public void ShowDialogAsync_ContentIsEmpty_ThrowsArgumentNullException()
{
var title = _fixture.Create<string>();
var content = string.Empty;
var buttons = _fixture.CreateMany<DialogButton>(2);
var dialogService = new DialogService(Mock.Of<ISettingsService>());
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("content");
}
[TestMethod]
public void ShowDialogAsync_NoButtonsPassed_ThrowsArgumentException()
{
var title = _fixture.Create<string>();
var content = _fixture.Create<string>();
var dialogService = new DialogService(Mock.Of<ISettingsService>());
Func<Task<DialogButton>> invoke = () => dialogService.ShowMessageDialogAsnyc(title, content);
invoke.Should().Throw<ArgumentException>().And.ParamName.Should().Be("buttons");
}
}
}
@@ -0,0 +1,71 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.Models.Enums;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
namespace FluentTerminal.App.Test.Services
{
[TestClass]
public class KeyboardCommandServiceTests
{
private Fixture _fixture;
[TestInitialize]
public void TestInitialize()
{
_fixture = new Fixture();
}
[TestMethod]
public void RegisterCommandHandler_HandlerIsNull_ThrowsArgumentNullException()
{
var command = _fixture.Create<Command>();
Action handler = null;
var keyboardCommandService = new KeyboardCommandService();
Action invoke = () => keyboardCommandService.RegisterCommandHandler(command, handler);
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("handler");
}
[TestMethod]
public void RegisterCommandHandler_CommandAlreadyRegisted_ThrowsInvalidOperationException()
{
var command = _fixture.Create<Command>();
var handler = _fixture.Create<Action>();
var keyboardCommandService = new KeyboardCommandService();
keyboardCommandService.RegisterCommandHandler(command, handler);
Action invoke = () => keyboardCommandService.RegisterCommandHandler(command, handler);
invoke.Should().Throw<InvalidOperationException>();
}
[TestMethod]
public void SendCommand_CommandIsRegistered_HandlerGetsInvoked()
{
var command = _fixture.Create<Command>();
var handlerCalled = false;
var keyboardCommandService = new KeyboardCommandService();
keyboardCommandService.RegisterCommandHandler(command, () => handlerCalled = true);
keyboardCommandService.SendCommand(command);
handlerCalled.Should().BeTrue();
}
[TestMethod]
public void SendCommand_CommandIsNotRegisted_KeyNotFoundExceptionIsThrown()
{
var command = _fixture.Create<Command>();
var keyboardCommandService = new KeyboardCommandService();
Action invoke = () => keyboardCommandService.SendCommand(command);
invoke.Should().Throw<KeyNotFoundException>();
}
}
}
+8
View File
@@ -0,0 +1,8 @@
<Application
x:Class="FluentTerminal.App.Test.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FluentTerminal.App.Test"
RequestedTheme="Light">
</Application>
+102
View File
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Test
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
this.DebugSettings.EnableFrameRateCounter = true;
}
#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI();
// Ensure the current window is active
Window.Current.Activate();
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments);
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}
+21 -1
View File
@@ -1,12 +1,32 @@
<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" />
</ResourceDictionary>
</Application.Resources>
</Application>
+218 -76
View File
@@ -1,6 +1,14 @@
using FluentTerminal.App.Views;
using Autofac;
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.App.ViewModels;
using FluentTerminal.App.Views;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
@@ -11,124 +19,258 @@ using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using IContainer = Autofac.IContainer;
namespace FluentTerminal.App
{
sealed partial class App : Application
public sealed partial class App : Application
{
public TaskCompletionSource<int> _trayReady = new TaskCompletionSource<int>();
private readonly ISettingsService _settingsService;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
private bool _alreadyLaunched;
private ApplicationSettings _applicationSettings;
private readonly IContainer _container;
private readonly List<MainViewModel> _mainViewModels;
private SettingsViewModel _settingsViewModel;
private int? _settingsWindowId;
private AppServiceConnection _appServiceConnection;
private BackgroundTaskDeferral _appServiceDeferral;
public App()
{
_mainViewModels = new List<MainViewModel>();
InitializeComponent();
Instance = this;
var builder = new ContainerBuilder();
builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
builder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>().SingleInstance();
builder.RegisterType<TrayProcessCommunicationService>().As<ITrayProcessCommunicationService>().SingleInstance();
builder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
builder.RegisterType<KeyboardCommandService>().As<IKeyboardCommandService>().InstancePerDependency();
builder.RegisterType<MainViewModel>().InstancePerDependency();
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
_container = builder.Build();
_settingsService = _container.Resolve<ISettingsService>();
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
_trayProcessCommunicationService = _container.Resolve<ITrayProcessCommunicationService>();
_applicationSettings = _settingsService.GetApplicationSettings();
}
public static BackgroundTaskDeferral AppServiceDeferral = null;
public static AppServiceConnection Connection = null;
public static App Instance;
int? _settingsWindowId;
int idCreate = 0;
List<int> idSaved = new List<int>();
protected override async void OnLaunched(LaunchActivatedEventArgs e)
protected override async void OnActivated(IActivatedEventArgs args)
{
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
await Task.WhenAll(launch, clearCache);
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
if (args is CommandLineActivatedEventArgs commandLineActivated)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
}
if (string.IsNullOrWhiteSpace(commandLineActivated.Operation.Arguments))
{
return;
}
if (rootFrame.Content == null)
{
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
rootFrame.Navigate(typeof(MainPage), e.Arguments);
idSaved.Add(ApplicationView.GetForCurrentView().Id);
var arguments = commandLineActivated.Operation.Arguments.Split(' ', 2);
var command = arguments[0];
var parameter = arguments.Length > 1 ? arguments[1] : string.Empty;
if (_alreadyLaunched)
{
if (command == "settings")
{
await ShowSettings().ConfigureAwait(true);
}
else if (command == "new")
{
if (_applicationSettings.NewTerminalLocation == NewTerminalLocation.Tab && _mainViewModels.Count > 0)
{
await _mainViewModels.Last().AddTerminal(parameter, false).ConfigureAwait(true);
}
else
{
await CreateNewTerminalWindow(parameter).ConfigureAwait(true);
}
}
}
else
{
if (command == "settings")
{
var viewModel = _container.Resolve<SettingsViewModel>();
await CreateMainView(typeof(SettingsPage), viewModel, true).ConfigureAwait(true);
}
else if (command == "new")
{
var viewModel = _container.Resolve<MainViewModel>();
await viewModel.AddTerminal(parameter, false).ConfigureAwait(true);
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
}
}
}
else if (false) //todo: add option to create new windows on launch
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
if (!_alreadyLaunched)
{
await CreateNewWindow(typeof(MainPage), true);
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);
}
Window.Current.Activate();
}
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
// connection established from the fulltrust process
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;
private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
if (args.Request.Message.ContainsKey("exit"))
{
App.Current.Exit();
_appServiceConnection = details.AppServiceConnection;
_trayReady.SetResult(0);
}
}
}
private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
if (AppServiceDeferral != null)
{
AppServiceDeferral.Complete();
}
_appServiceDeferral?.Complete();
_appServiceDeferral = null;
_appServiceConnection = null;
Application.Current.Exit();
}
public async Task<int> CreateNewWindow(Type pageType, bool ExtendViewIntoTitleBar)
private async Task CreateMainView(Type pageType, INotifyPropertyChanged viewModel, bool extendViewIntoTitleBar)
{
var create = CoreApplication.CreateNewView();
await create.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
await StartSystemTray().ConfigureAwait(true);
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar;
if (viewModel is MainViewModel mainViewModel)
{
mainViewModel.Closed += OnMainViewModelClosed;
mainViewModel.NewWindowRequested += OnNewWindowRequested;
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
_mainViewModels.Add(mainViewModel);
}
rootFrame.Navigate(pageType, viewModel);
}
_alreadyLaunched = true;
Window.Current.Activate();
}
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, null);
frame.Navigate(pageType, viewModel);
Window.Current.Content = frame;
Window.Current.Activate();
idCreate = ApplicationView.GetForCurrentView().Id;
windowId = ApplicationView.GetForCurrentView().Id;
});
for (int i = idSaved.Count - 1; i >= 0; i--)
if (await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
idCreate, ViewSizePreference.Default,
idSaved[i], ViewSizePreference.Default)
) break;
idSaved.Add(idCreate);
return idCreate;
}
public async Task ShowNew()
{
var id = await CreateNewWindow(typeof(MainPage), true);
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
}
public async Task ShowSettings()
{
if (_settingsWindowId == null)
if (viewModel is MainViewModel mainViewModel && parameter is string directory)
{
_settingsWindowId = await CreateNewWindow(typeof(Views.SettingsPage), false);
mainViewModel.Closed += OnMainViewModelClosed;
mainViewModel.NewWindowRequested += OnNewWindowRequested;
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
_mainViewModels.Add(mainViewModel);
await mainViewModel.AddTerminal(directory, false).ConfigureAwait(true);
}
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
if (viewModel is SettingsViewModel settingsViewModel)
{
_settingsViewModel = settingsViewModel;
_settingsViewModel.Closed += OnSettingsClosed;
}
return windowId;
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
private void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
_applicationSettings = e;
}
private void OnMainViewModelClosed(object sender, EventArgs e)
{
if (sender is MainViewModel viewModel)
{
viewModel.Closed -= OnMainViewModelClosed;
viewModel.NewWindowRequested -= OnNewWindowRequested;
viewModel.ShowSettingsRequested -= OnShowSettingsRequested;
_mainViewModels.Remove(viewModel);
}
}
private async void OnNewWindowRequested(object sender, EventArgs e)
{
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)
{
_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,27 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class FalseToVisibleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Boolean collapsed)
{
return collapsed ? Visibility.Collapsed : Visibility.Visible;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is Visibility visibility)
{
return visibility == Visibility.Collapsed;
}
return null;
}
}
}
@@ -0,0 +1,43 @@
using FluentTerminal.Models.Enums;
using System;
using System.ComponentModel;
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 GetEnumDescription((ExtendedVirtualKey)intValue);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
private static string GetEnumDescription(Enum value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
if (fieldInfo == null)
{
return value.ToString();
}
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes?.Length > 0)
{
return attributes[0].Description;
}
return value.ToString();
}
}
}
@@ -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,39 @@
using FluentTerminal.App.ViewModels;
using FluentTerminal.App.Views;
using System;
using System.Collections.Generic;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
internal class TerminalViewModelToViewConverter : IValueConverter
{
private readonly Dictionary<int, TerminalView> _viewDictionary;
public TerminalViewModelToViewConverter()
{
_viewDictionary = new Dictionary<int, TerminalView>();
}
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is TerminalViewModel terminal)
{
if (_viewDictionary.TryGetValue(terminal.Id, out TerminalView view))
{
return view;
}
var newView = new TerminalView(terminal);
_viewDictionary.Add(terminal.Id, newView);
return newView;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,27 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class TrueToVisibleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Boolean visible)
{
return visible ? Visibility.Visible : Visibility.Collapsed;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is Visibility visibility)
{
return visibility == Visibility.Visible;
}
return null;
}
}
}
@@ -0,0 +1,18 @@
<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"
RequestedTheme="Dark"
mc:Ignorable="d">
<ComboBox
HorizontalAlignment="Stretch"
DisplayMemberPath="Name"
ItemsSource="{x:Bind Profiles}"
SelectedItem="{x:Bind SelectedProfile, Mode=TwoWay}" />
</ContentDialog>
@@ -0,0 +1,22 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Dialogs
{
public sealed partial class ShellProfileSelectionDialog : ContentDialog
{
public IEnumerable<ShellProfile> Profiles { get; }
public ShellProfile SelectedProfile { get; private set; }
public ShellProfileSelectionDialog(ISettingsService settingsService)
{
Profiles = settingsService.GetShellProfiles();
SelectedProfile = Profiles.First();
this.InitializeComponent();
}
}
}
+171 -52
View File
@@ -19,9 +19,12 @@
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
<PackageCertificateKeyFile>FluentTerminal.App_StoreKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>8FA8EF727E767FAE5409DDD1694C1CEE054A2962</PackageCertificateThumbprint>
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
<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>
@@ -45,6 +48,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 +83,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,6 +96,7 @@
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
@@ -99,23 +105,111 @@
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Behaviors\MiddleClickBehavior.cs" />
<Compile Include="Converters\FalseToVisibleConverter.cs" />
<Compile Include="Converters\IntToExtendedVirtualKeyConverter.cs" />
<Compile Include="Converters\IntToVisibilityConverter.cs" />
<Compile Include="Converters\NegateConverter.cs" />
<Compile Include="Converters\TerminalViewModelToViewConverter.cs" />
<Compile Include="Converters\TrueToVisibleConverter.cs" />
<Compile Include="Dialogs\ShellProfileSelectionDialog.xaml.cs">
<DependentUpon>ShellProfileSelectionDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Services\IDialogService.cs" />
<Compile Include="Services\Implementation\DialogService.cs" />
<Compile Include="Services\Implementation\KeyboardCommandService.cs" />
<Compile Include="Services\IKeyboardCommandService.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\TrayProcessCommunicationService.cs" />
<Compile Include="Services\ISettingsService.cs" />
<Compile Include="Services\ITrayProcessCommunicationService.cs" />
<Compile Include="Utilities\ColorExtensions.cs" />
<Compile Include="ViewModels\MainViewModel.cs" />
<Compile Include="ViewModels\SettingsViewModel.cs" />
<Compile Include="ViewModels\Settings\GeneralPageViewModel.cs" />
<Compile Include="ViewModels\Settings\KeyBindingsPageViewModel.cs" />
<Compile Include="ViewModels\Settings\KeyBindingsViewModel.cs" />
<Compile Include="ViewModels\Settings\KeyBindingViewModel.cs" />
<Compile Include="ViewModels\Settings\ProfilesPageViewModel.cs" />
<Compile Include="ViewModels\Settings\TerminalPageViewModel.cs" />
<Compile Include="ViewModels\Settings\ThemesPageViewModel.cs" />
<Compile Include="ViewModels\ShellProfileViewModel.cs" />
<Compile Include="ViewModels\ThemeViewModel.cs" />
<Compile Include="ViewModels\TerminalViewModel.cs" />
<Compile Include="Views\ITerminalView.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="Views\CreateKeyBindingDialog.xaml.cs">
<DependentUpon>CreateKeyBindingDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPage.xaml.cs">
<DependentUpon>SettingsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TerminalPage.xaml.cs">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<Compile Include="Views\SettingsPages\ThemeSettings.xaml.cs">
<DependentUpon>ThemeSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\TerminalSettings.xaml.cs">
<DependentUpon>TerminalSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\GeneralSettings.xaml.cs">
<DependentUpon>GeneralSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\KeyBindingSettings.xaml.cs">
<DependentUpon>KeyBindingSettings.xaml</DependentUpon>
</Compile>
<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>
</Compile>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<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>
@@ -166,57 +260,20 @@
<Content Include="Assets\AppIcons\Wide310x150Logo.scale-400.png" />
<None Include="Package.StoreAssociation.xml" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
<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\Newtonsoft.Json.dll" />
<Content Include="Win32\Owin.dll" />
<Content Include="Win32\System.Net.Http.Formatting.dll" />
<Content Include="Win32\System.Web.Http.dll" />
<Content Include="Win32\System.Web.Http.Owin.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>
@@ -226,33 +283,95 @@
<Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page>
<Page Include="Views\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\TerminalPage.xaml">
<Page Include="Views\SettingsPages\ThemeSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\TerminalSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\GeneralSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\KeyBindingSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<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>
<Page Include="Views\TerminalView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac">
<Version>4.8.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.0.6</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI">
<Version>2.1.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
<Version>2.1.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed">
<Version>2.0.0</Version>
</PackageReference>
<PackageReference Include="MvvmLightLibs">
<Version>5.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>11.0.2</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.21.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<SDKReference Include="WindowsDesktop, Version=10.0.16299.0">
<Name>Windows Desktop Extensions for the UWP</Name>
</SDKReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
<Name>FluentTerminal.Models</Name>
</ProjectReference>
<ProjectReference Include="..\FluentTerminal.RuntimeComponent\FluentTerminal.RuntimeComponent.csproj">
<Project>{BAAE6239-343F-454C-977F-F81F91715961}</Project>
<Name>FluentTerminal.RuntimeComponent</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>mkdir "$(SolutionDir)\FluentTerminal.App\bin\$(PlatformName)\$(ConfigurationName)\AppX\Win32" 2&gt; NULL
xcopy /y /s "$(SolutionDir)FluentTerminal.SystemTray\bin\$(PlatformName)\$(ConfigurationName)" "$(SolutionDir)\FluentTerminal.App\bin\$(PlatformName)\$(ConfigurationName)\AppX\Win32"
</PostBuildEvent>
<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.
+9 -4
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns: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.3.0" />
<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.10.0" />
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>Fluent Terminal</DisplayName>
@@ -23,13 +23,18 @@
<uap:ShowOn Tile="square310x310Logo" />
</uap:ShowNameOnTiles>
</uap:DefaultTile>
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" />
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" uap5:Optional="true" />
</uap:VisualElements>
<Extensions>
<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>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe" />
</Extensions>
</Application>
</Applications>
@@ -0,0 +1,17 @@
using FluentTerminal.Models;
using System;
using System.Collections.Generic;
namespace FluentTerminal.App.Services
{
public interface IDefaultValueProvider
{
IEnumerable<ShellProfile> GetPreinstalledShellProfiles();
Guid GetDefaultShellProfileId();
TerminalOptions GetDefaultTerminalOptions();
KeyBindings GetDefaultKeyBindings();
ApplicationSettings GetDefaultApplicationSettings();
Guid GetDefaultThemeId();
IEnumerable<TerminalTheme> GetPreInstalledThemes();
}
}
@@ -0,0 +1,18 @@
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();
}
}
@@ -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,40 @@
using FluentTerminal.Models;
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<KeyBindings> 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);
KeyBindings GetKeyBindings();
void SaveKeyBindings(KeyBindings keyBindings);
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,15 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Responses;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
namespace FluentTerminal.App.Services
{
public interface ITrayProcessCommunicationService
{
Task Initialize(AppServiceConnection appServiceConnection);
Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellProfile shellProfile);
Task ResizeTerminal(int id, TerminalSize size);
Task UpdateToggleWindowKeyBindings();
}
}
@@ -0,0 +1,299 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
using Windows.System;
namespace FluentTerminal.App.Services.Implementation
{
internal class DefaultValueProvider : IDefaultValueProvider
{
public ApplicationSettings GetDefaultApplicationSettings()
{
return new ApplicationSettings
{
ConfirmClosingTabs = false,
ConfirmClosingWindows = false,
NewTerminalLocation = NewTerminalLocation.Tab
};
}
public KeyBindings GetDefaultKeyBindings()
{
return new KeyBindings
{
NewTab = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.NewTab,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)VirtualKey.T
}
},
ConfigurableNewTab = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.ConfigurableNewTab,
Ctrl = true,
Alt = false,
Shift = true,
Key = (int)VirtualKey.T
}
},
CloseTab = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.CloseTab,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)VirtualKey.W
}
},
NextTab = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.NextTab,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)VirtualKey.Tab
}
},
PreviousTab = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.PreviousTab,
Ctrl = true,
Alt = false,
Shift = true,
Key = (int)VirtualKey.Tab
}
},
NewWindow = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.NewWindow,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)VirtualKey.N
}
},
ShowSettings = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.ShowSettings,
Ctrl = true,
Alt = false,
Shift = false,
Key = 188 // comma
}
},
Copy = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.Copy,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)VirtualKey.C
}
},
Paste = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.Paste,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)VirtualKey.V
}
},
ToggleWindow = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.ToggleWindow,
Ctrl = false,
Alt = false,
Shift = false,
Key = (int)VirtualKey.Scroll
}
},
Search = new List<KeyBinding>
{
new KeyBinding
{
Command = Command.Search,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)VirtualKey.F
}
}
};
}
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
};
}
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
}
};
}
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 = "#fedba9"
}
};
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"
}
};
return new[] { defaultXterm, powerShell, homebrew };
}
}
}
@@ -0,0 +1,61 @@
using System;
using System.Threading.Tasks;
using FluentTerminal.App.Dialogs;
using FluentTerminal.Models;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Services.Implementation
{
public class DialogService : IDialogService
{
private readonly ISettingsService _settingsService;
public DialogService(ISettingsService settingsService)
{
_settingsService = settingsService;
}
public async 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 = new MessageDialog(content, title);
foreach (var button in buttons)
{
dialog.Commands.Add(new UICommand(button.ToString()));
}
var result = await dialog.ShowAsync();
return Enum.Parse<DialogButton>(result.Label);
}
public async Task<ShellProfile> ShowProfileSelectionDialogAsync()
{
var dialog = new ShellProfileSelectionDialog(_settingsService);
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
return dialog.SelectedProfile;
}
return null;
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using FluentTerminal.Models.Enums;
namespace FluentTerminal.App.Services.Implementation
{
public class KeyboardCommandService : IKeyboardCommandService
{
private readonly Dictionary<Command, Action> _commandHandlers = new Dictionary<Command, Action>();
public void RegisterCommandHandler(Command command, Action handler)
{
if (_commandHandlers.ContainsKey(command))
{
throw new InvalidOperationException("command already registered");
}
_commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler));
}
public void SendCommand(Command command)
{
if (_commandHandlers.TryGetValue(command, out Action handler))
{
handler.Invoke();
return;
}
throw new KeyNotFoundException($"No handler registered for command: {command}");
}
}
}
@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentTerminal.App.Utilities;
using FluentTerminal.Models;
using Newtonsoft.Json;
using Windows.Storage;
namespace FluentTerminal.App.Services.Implementation
{
internal class SettingsService : ISettingsService
{
public const string ThemesContainerName = "Themes";
public const string ShellProfilesContainerName = "ShellProfiles";
public const string CurrentThemeKey = "CurrentTheme";
public const string DefaultShellProfileKey = "DefaultShellProfile";
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly ApplicationDataContainer _localSettings;
private readonly ApplicationDataContainer _themes;
private readonly ApplicationDataContainer _shellProfiles;
private readonly ApplicationDataContainer _roamingSettings;
public event EventHandler<Guid> CurrentThemeChanged;
public event EventHandler<TerminalOptions> TerminalOptionsChanged;
public event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
public event EventHandler<KeyBindings> KeyBindingsChanged;
public SettingsService(IDefaultValueProvider defaultValueProvider)
{
_defaultValueProvider = defaultValueProvider;
_localSettings = ApplicationData.Current.LocalSettings;
_roamingSettings = ApplicationData.Current.RoamingSettings;
_themes = _roamingSettings.CreateContainer(ThemesContainerName, ApplicationDataCreateDisposition.Always);
_shellProfiles = _roamingSettings.CreateContainer(ShellProfilesContainerName, ApplicationDataCreateDisposition.Always);
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.Values.TryGetValue(CurrentThemeKey, out object value))
{
return (Guid)value;
}
return _defaultValueProvider.GetDefaultThemeId();
}
public void SaveCurrentThemeId(Guid id)
{
_roamingSettings.Values[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.Values.Remove(id.ToString());
}
public IEnumerable<TerminalTheme> GetThemes()
{
return _themes.Values.Select(x => JsonConvert.DeserializeObject<TerminalTheme>((string)x.Value)).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 KeyBindings GetKeyBindings()
{
var defaults = _defaultValueProvider.GetDefaultKeyBindings();
var keyBindings = _roamingSettings.ReadValueFromJson<KeyBindings>(nameof(KeyBindings), null) ?? defaults;
if (keyBindings.ConfigurableNewTab == null)
{
keyBindings.ConfigurableNewTab = defaults.ConfigurableNewTab;
}
if (keyBindings.Search == null)
{
keyBindings.Search = defaults.Search;
}
return keyBindings;
}
public void SaveKeyBindings(KeyBindings keyBindings)
{
_roamingSettings.WriteValueAsJson(nameof(KeyBindings), keyBindings);
KeyBindingsChanged?.Invoke(this, keyBindings);
}
public Guid GetDefaultShellProfileId()
{
if (_roamingSettings.Values.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.Values[DefaultShellProfileKey] = id;
}
public IEnumerable<ShellProfile> GetShellProfiles()
{
return _shellProfiles.Values.Select(x => JsonConvert.DeserializeObject<ShellProfile>((string)x.Value)).ToList();
}
public void SaveShellProfile(ShellProfile shellProfile)
{
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
}
public void DeleteShellProfile(Guid id)
{
_shellProfiles.Values.Remove(id.ToString());
}
}
}
@@ -0,0 +1,82 @@
using System;
using System.Threading.Tasks;
using FluentTerminal.Models;
using FluentTerminal.Models.Requests;
using FluentTerminal.Models.Responses;
using Newtonsoft.Json;
using Windows.ApplicationModel.AppService;
using Windows.Foundation.Collections;
namespace FluentTerminal.App.Services.Implementation
{
internal class TrayProcessCommunicationService : ITrayProcessCommunicationService
{
private readonly ISettingsService _settingsService;
private AppServiceConnection _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 ValueSet
{
{ MessageKeys.Type, MessageTypes.CreateTerminalRequest },
{ MessageKeys.Content, JsonConvert.SerializeObject(request) }
};
var responseMessage = await _appServiceConnection.SendMessageAsync(message);
return JsonConvert.DeserializeObject<CreateTerminalResponse>((string)responseMessage.Message[MessageKeys.Content]);
}
public Task Initialize(AppServiceConnection appServiceConnection)
{
_appServiceConnection = appServiceConnection;
return UpdateToggleWindowKeyBindings();
}
public Task ResizeTerminal(int id, TerminalSize size)
{
var request = new ResizeTerminalRequest
{
TerminalId = id,
NewSize = size
};
var message = new ValueSet
{
{ MessageKeys.Type, MessageTypes.ResizeTerminalRequest },
{ MessageKeys.Content, JsonConvert.SerializeObject(request) }
};
return _appServiceConnection.SendMessageAsync(message).AsTask();
}
public Task UpdateToggleWindowKeyBindings()
{
var keyBindings = _settingsService.GetKeyBindings().ToggleWindow;
var request = new SetToggleWindowKeyBindingsRequest
{
KeyBindings = keyBindings
};
var message = new ValueSet
{
{ MessageKeys.Type, MessageTypes.SetToggleWindowKeyBindingsRequest },
{ MessageKeys.Content, JsonConvert.SerializeObject(request) }
};
return _appServiceConnection.SendMessageAsync(message).AsTask();
}
}
}
+276
View File
@@ -11,6 +11,7 @@
<SolidColorBrush x:Key="TitleBarHoverBackgroundColor" Color="#18FFFFFF" />
<SolidColorBrush x:Key="TitleBarPressedBackgroundColor" Color="#30FFFFFF" />
<SolidColorBrush x:Key="DisabledForegroundColor" Color="#80FFFFFF" />
<Style x:Key="TitleBarButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Transparent" />
@@ -68,4 +69,279 @@
</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>
<Style x:Key="ThemeAppBarButtonStyle" TargetType="AppBarButton">
<Setter Property="BorderBrush" Value="{ThemeResource AppBarButtonBorderBrush}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Width" Value="68" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="AllowFocusOnInteraction" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="AppBarButton">
<Grid
x:Name="Root"
MinWidth="{TemplateBinding MinWidth}"
MaxWidth="{TemplateBinding MaxWidth}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="ContentRoot" MinHeight="{ThemeResource AppBarThemeMinHeight}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Viewbox
x:Name="ContentViewbox"
Height="20"
Margin="0,14,0,4"
HorizontalAlignment="Stretch"
AutomationProperties.AccessibilityView="Raw">
<ContentPresenter
x:Name="Content"
Height="20"
Content="{TemplateBinding Icon}"
Foreground="{TemplateBinding Foreground}" />
</Viewbox>
<TextBlock
x:Name="TextLabel"
Grid.Row="1"
Margin="2,0,2,6"
AutomationProperties.AccessibilityView="Raw"
FontFamily="{TemplateBinding FontFamily}"
FontSize="12"
Foreground="{TemplateBinding Foreground}"
Text="{TemplateBinding Label}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
x:Name="OverflowTextLabel"
Margin="12,0,12,0"
Padding="0,5,0,7"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontFamily="{TemplateBinding FontFamily}"
FontSize="15"
Foreground="{TemplateBinding Foreground}"
Text="{TemplateBinding Label}"
TextAlignment="Left"
TextTrimming="Clip"
TextWrapping="NoWrap"
Visibility="Collapsed" />
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullSize" />
<VisualState x:Name="Compact">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="LabelOnRight">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentViewbox" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="12,14,0,14" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="MinHeight">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarThemeCompactHeight}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="(Grid.Row)">
<DiscreteObjectKeyFrame KeyTime="0" Value="0" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="TextAlignment">
<DiscreteObjectKeyFrame KeyTime="0" Value="Left" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="8,15,12,17" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="LabelCollapsed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentRoot" Storyboard.TargetProperty="MinHeight">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource AppBarThemeCompactHeight}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextLabel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Overflow">
<VisualState.Setters>
<Setter Target="ContentRoot.MinHeight" Value="0" />
<Setter Target="ContentViewbox.Visibility" Value="Collapsed" />
<Setter Target="TextLabel.Visibility" Value="Collapsed" />
<Setter Target="OverflowTextLabel.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="OverflowWithToggleButtons">
<VisualState.Setters>
<Setter Target="ContentRoot.MinHeight" Value="0" />
<Setter Target="ContentViewbox.Visibility" Value="Collapsed" />
<Setter Target="TextLabel.Visibility" Value="Collapsed" />
<Setter Target="OverflowTextLabel.Visibility" Value="Visible" />
<Setter Target="OverflowTextLabel.Margin" Value="38,0,12,0" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="OverflowWithMenuIcons">
<VisualState.Setters>
<Setter Target="ContentRoot.MinHeight" Value="0" />
<Setter Target="ContentViewbox.HorizontalAlignment" Value="Left" />
<Setter Target="ContentViewbox.VerticalAlignment" Value="Center" />
<Setter Target="ContentViewbox.Width" Value="16" />
<Setter Target="ContentViewbox.Height" Value="16" />
<Setter Target="ContentViewbox.Margin" Value="12,0,12,0" />
<Setter Target="TextLabel.Visibility" Value="Collapsed" />
<Setter Target="OverflowTextLabel.Visibility" Value="Visible" />
<Setter Target="OverflowTextLabel.Margin" Value="38,0,12,0" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="OverflowWithToggleButtonsAndMenuIcons">
<VisualState.Setters>
<Setter Target="ContentRoot.MinHeight" Value="0" />
<Setter Target="ContentViewbox.HorizontalAlignment" Value="Left" />
<Setter Target="ContentViewbox.VerticalAlignment" Value="Center" />
<Setter Target="ContentViewbox.Width" Value="16" />
<Setter Target="ContentViewbox.Height" Value="16" />
<Setter Target="ContentViewbox.Margin" Value="38,0,12,0" />
<Setter Target="TextLabel.Visibility" Value="Collapsed" />
<Setter Target="OverflowTextLabel.Visibility" Value="Visible" />
<Setter Target="OverflowTextLabel.Margin" Value="76,0,12,0" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="OverflowTextLabel" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="Root.Background" Value="{StaticResource TitleBarHoverBackgroundColor}" />
<Setter Target="Root.BorderBrush" Value="{ThemeResource AppBarButtonBorderBrushPointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="OverflowTextLabel" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="Root.Background" Value="{StaticResource TitleBarPressedBackgroundColor}" />
<Setter Target="Root.BorderBrush" Value="{ThemeResource AppBarButtonBorderBrushPressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="OverflowTextLabel" />
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="Root.Background" Value="Transparent" />
<Setter Target="Root.BorderBrush" Value="{ThemeResource AppBarButtonBorderBrushDisabled}" />
<Setter Target="Content.Foreground" Value="{StaticResource DisabledForegroundColor}" />
<Setter Target="TextLabel.Foreground" Value="{StaticResource DisabledForegroundColor}" />
<Setter Target="OverflowTextLabel.Foreground" Value="{StaticResource DisabledForegroundColor}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="OverflowNormal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="ContentRoot" />
</Storyboard>
</VisualState>
<VisualState x:Name="OverflowPointerOver">
<VisualState.Setters>
<Setter Target="Root.Background" Value="{StaticResource TitleBarHoverBackgroundColor}" />
<Setter Target="Root.BorderBrush" Value="{ThemeResource AppBarButtonBorderBrushPointerOver}" />
</VisualState.Setters>
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="ContentRoot" />
</Storyboard>
</VisualState>
<VisualState x:Name="OverflowPressed">
<VisualState.Setters>
<Setter Target="Root.Background" Value="{StaticResource TitleBarPressedBackgroundColor}" />
<Setter Target="Root.BorderBrush" Value="{ThemeResource AppBarButtonBorderBrushPressed}" />
</VisualState.Setters>
<Storyboard>
<PointerDownThemeAnimation Storyboard.TargetName="ContentRoot" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="InputModeStates">
<VisualState x:Name="InputModeDefault" />
<VisualState x:Name="TouchInputMode">
<VisualState.Setters>
<Setter Target="OverflowTextLabel.Padding" Value="0,11,0,13" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="GameControllerInputMode">
<VisualState.Setters>
<Setter Target="OverflowTextLabel.Padding" Value="0,11,0,13" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,29 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
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))
{
if (EqualityComparer<T>.Default.Equals(fallbackValue, default))
{
fallbackValue = Activator.CreateInstance<T>();
}
JsonConvert.PopulateObject((string)value, fallbackValue);
}
return fallbackValue;
}
public static void WriteValueAsJson<T>(this ApplicationDataContainer container, string name, T value)
{
container.Values[name] = JsonConvert.SerializeObject(value);
}
}
}
@@ -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,257 @@
using FluentTerminal.App.Services;
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;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.UI.Core.Preview;
using Windows.UI.ViewManagement;
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 readonly CoreDispatcher _dispatcher;
private int _nextTerminalId;
private TerminalViewModel _selectedTerminal;
private string _title;
public event EventHandler Closed;
public event EventHandler NewWindowRequested;
public event EventHandler ShowSettingsRequested;
public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService)
{
_settingsService = settingsService;
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
_trayProcessCommunicationService = trayProcessCommunicationService;
_dialogService = dialogService;
_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);
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";
_dispatcher = CoreApplication.GetCurrentView().Dispatcher;
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
}
private async void OnTerminalOptionsChanged(object sender, TerminalOptions e)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
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 (Set(ref _selectedTerminal, value))
{
SelectedTerminal?.FocusTerminal();
}
}
}
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 _dispatcher.RunAsync(CoreDispatcherPriority.Normal, 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);
terminal.CloseRequested += OnTerminalCloseRequested;
terminal.TitleChanged += OnTitleChanged;
Terminals.Add(terminal);
SelectedTerminal = terminal;
}).AsTask();
}
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 _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => _applicationSettings = e);
}
private async void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
var deferral = e.GetDeferral();
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.Handled = true;
}
}
else
{
CloseAllTerminals();
Closed?.Invoke(this, EventArgs.Empty);
}
deferral.Complete();
}
private async void OnCurrentThemeChanged(object sender, Guid e)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
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.GetForCurrentView().TryConsolidateAsync();
}
}
}
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);
}
}
}
@@ -0,0 +1,101 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class GeneralPageViewModel : ViewModelBase
{
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private 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 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;
NewTerminalLocation = defaults.NewTerminalLocation;
}
}
}
}
@@ -0,0 +1,129 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Views;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.ViewModels.Settings
{
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 dialog = new CreateKeyBindingDialog(_dialogService);
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
Alt = dialog.KeyBinding.Alt;
Ctrl = dialog.KeyBinding.Ctrl;
Shift = dialog.KeyBinding.Shift;
Meta = dialog.KeyBinding.Meta;
Key = dialog.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,131 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class KeyBindingsPageViewModel : ViewModelBase
{
private KeyBindings _keyBindings;
private readonly ISettingsService _settingsService;
private readonly IDialogService _dialogService;
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
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());
}
private void Initialize(KeyBindings keyBindings)
{
_keyBindings = keyBindings;
NewTab = CreateViewModel(Command.NewTab, _keyBindings.NewTab);
ConfigurableNewTab = CreateViewModel(Command.ConfigurableNewTab, _keyBindings.ConfigurableNewTab);
ToggleWindow = CreateViewModel(Command.ToggleWindow, _keyBindings.ToggleWindow);
NextTab = CreateViewModel(Command.NextTab, _keyBindings.NextTab);
PreviousTab = CreateViewModel(Command.PreviousTab, _keyBindings.PreviousTab);
CloseTab = CreateViewModel(Command.CloseTab, _keyBindings.CloseTab);
NewWindow = CreateViewModel(Command.NewWindow, _keyBindings.NewWindow);
ShowSettings = CreateViewModel(Command.ShowSettings, _keyBindings.ShowSettings);
Copy = CreateViewModel(Command.Copy, _keyBindings.Copy);
Paste = CreateViewModel(Command.Paste, _keyBindings.Paste);
Search = CreateViewModel(Command.Search, _keyBindings.Search);
RaisePropertyChanged(nameof(NewTab));
RaisePropertyChanged(nameof(ConfigurableNewTab));
RaisePropertyChanged(nameof(ToggleWindow));
RaisePropertyChanged(nameof(NextTab));
RaisePropertyChanged(nameof(PreviousTab));
RaisePropertyChanged(nameof(CloseTab));
RaisePropertyChanged(nameof(NewWindow));
RaisePropertyChanged(nameof(ShowSettings));
RaisePropertyChanged(nameof(Copy));
RaisePropertyChanged(nameof(Paste));
RaisePropertyChanged(nameof(Search));
}
public KeyBindingsViewModel NewTab { get; private set; }
public KeyBindingsViewModel ConfigurableNewTab { get; private set; }
public KeyBindingsViewModel ToggleWindow { get; private set; }
public KeyBindingsViewModel NextTab { get; private set; }
public KeyBindingsViewModel PreviousTab { get; private set; }
public KeyBindingsViewModel CloseTab { get; private set; }
public KeyBindingsViewModel NewWindow { get; private set; }
public KeyBindingsViewModel ShowSettings { get; private set; }
public KeyBindingsViewModel Copy { get; private set; }
public KeyBindingsViewModel Paste { get; private set; }
public KeyBindingsViewModel Search { get; private set; }
public RelayCommand RestoreDefaultsCommand { get; }
public RelayCommand<Command> AddCommand { get; }
private Task Add(Command command)
{
switch (command)
{
case Command.ToggleWindow:
return ToggleWindow.Add();
case Command.NextTab:
return NextTab.Add();
case Command.PreviousTab:
return PreviousTab.Add();
case Command.NewTab:
return NewTab.Add();
case Command.ConfigurableNewTab:
return ConfigurableNewTab.Add();
case Command.CloseTab:
return CloseTab.Add();
case Command.NewWindow:
return NewWindow.Add();
case Command.ShowSettings:
return ShowSettings.Add();
case Command.Copy:
return Copy.Add();
case Command.Paste:
return Paste.Add();
case Command.Search:
return Search.Add();
}
return Task.CompletedTask;
}
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)
{
Initialize(_defaultValueProvider.GetDefaultKeyBindings());
_settingsService.SaveKeyBindings(_keyBindings);
}
}
private KeyBindingsViewModel CreateViewModel(Command command, ICollection<KeyBinding> keyBindings)
{
var viewModel = new KeyBindingsViewModel(command, keyBindings, _dialogService);
viewModel.Edited += OnEdited;
return viewModel;
}
private void OnEdited(object sender, System.EventArgs e)
{
_settingsService.SaveKeyBindings(_keyBindings);
_trayProcessCommunicationService.UpdateToggleWindowKeyBindings();
}
}
}
@@ -0,0 +1,66 @@
using FluentTerminal.App.Services;
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 Command _command;
private readonly IDialogService _dialogService;
private readonly ICollection<KeyBinding> _keyBindings;
public KeyBindingsViewModel(Command command, ICollection<KeyBinding> keyBindings, IDialogService dialogService)
{
_command = command;
_keyBindings = keyBindings;
_dialogService = dialogService;
foreach (var binding in keyBindings)
{
var viewModel = new KeyBindingViewModel(binding, _dialogService);
viewModel.Deleted += ViewModel_Deleted;
viewModel.Edited += ViewModel_Edited;
KeyBindings.Add(viewModel);
}
}
public event EventHandler Edited;
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(this, EventArgs.Empty);
}
}
private void ViewModel_Deleted(object sender, EventArgs e)
{
if (sender is KeyBindingViewModel keyBinding)
{
_keyBindings.Remove(keyBinding.Model);
KeyBindings.Remove(keyBinding);
Edited?.Invoke(this, EventArgs.Empty);
}
}
private void ViewModel_Edited(object sender, EventArgs e)
{
Edited?.Invoke(this, EventArgs.Empty);
}
}
}
@@ -0,0 +1,104 @@
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 ShellProfileViewModel _selectedShellProfile;
public ProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
CreateShellProfileCommand = new RelayCommand(CreateShellProfile);
var defaultShellProfileId = _settingsService.GetDefaultShellProfileId();
foreach (var shellProfile in _settingsService.GetShellProfiles())
{
var viewModel = new ShellProfileViewModel(shellProfile, settingsService, dialogService);
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);
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,188 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Microsoft.Graphics.Canvas.Text;
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 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;
}
}
public TerminalPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
Fonts = CanvasTextFormat.GetSystemFontFamilies().OrderBy(s => s);
Sizes = Enumerable.Range(1, 72);
_terminalOptions = _settingsService.GetTerminalOptions();
}
}
}
@@ -0,0 +1,121 @@
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;
public ThemesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
CreateThemeCommand = new RelayCommand(CreateTheme);
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
BackgroundOpacity = _settingsService.GetTerminalOptions().BackgroundOpacity;
var activeThemeId = _settingsService.GetCurrentThemeId();
foreach (var theme in _settingsService.GetThemes())
{
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService);
viewModel.Activated += OnThemeActivated;
viewModel.Deleted += OnThemeDeleted;
if (theme.Id == activeThemeId)
{
viewModel.IsActive = true;
}
Themes.Add(viewModel);
}
SelectedTheme = Themes.First(t => t.IsActive);
}
private void OnTerminalOptionsChanged(object sender, TerminalOptions e)
{
BackgroundOpacity = e.BackgroundOpacity;
}
public RelayCommand CreateThemeCommand { get; }
public double BackgroundOpacity
{
get => _backgroundOpacity;
set => Set(ref _backgroundOpacity, value);
}
public ThemeViewModel SelectedTheme
{
get => _selectedTheme;
set => Set(ref _selectedTheme, value);
}
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);
viewModel.Activated += OnThemeActivated;
viewModel.Deleted += OnThemeDeleted;
Themes.Add(viewModel);
SelectedTheme = viewModel;
}
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);
}
}
}
}
@@ -0,0 +1,32 @@
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)
{
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService);
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider);
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider);
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider);
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider);
}
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,186 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Threading.Tasks;
using Windows.Storage.Pickers;
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;
public ShellProfileViewModel(ShellProfile shellProfile, ISettingsService settingsService, IDialogService dialogService)
{
_shellProfile = shellProfile;
_settingsService = settingsService;
_dialogService = dialogService;
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 picker = new FileOpenPicker
{
SuggestedStartLocation = PickerLocationId.ComputerFolder
};
picker.FileTypeFilter.Add(".exe");
var file = await picker.PickSingleFileAsync();
if (file != null)
{
Location = 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;
}
}
}
}
@@ -0,0 +1,313 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Views;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Core;
using Windows.UI.Xaml;
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 readonly CoreDispatcher _dispatcher;
private string _resizeOverlayContent;
private readonly DispatcherTimer _resizeOverlayTimer;
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;
public TerminalViewModel(int id, ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService,
IKeyboardCommandService keyboardCommandService, ApplicationSettings applicationSettings, string startupDirectory, ShellProfile shellProfile)
{
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;
_resizeOverlayTimer = new DispatcherTimer
{
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);
_dispatcher = CoreApplication.GetCurrentView().Dispatcher;
}
private async void OnKeyBindingsChanged(object sender, KeyBindings e)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await _terminalView.ChangeKeyBindings(GetKeyBindingsCollection(e)).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 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, GetKeyBindingsCollection(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;
await _terminalView.ConnectToSocket(response.WebSocketUrl).ConfigureAwait(true);
Initialized = true;
}
else
{
await _dialogService.ShowMessageDialogAsnyc("Error", response.Error, DialogButton.OK).ConfigureAwait(true);
}
await FocusTerminal().ConfigureAwait(true);
}
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 _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => _applicationSettings = e);
}
private async void OnCurrentThemeChanged(object sender, Guid e)
{
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, 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);
var dataPackage = new DataPackage();
dataPackage.SetText(selection);
Clipboard.SetContent(dataPackage);
}
else if (e == Command.Paste)
{
var content = Clipboard.GetContent();
if (content.Contains(StandardDataFormats.Text))
{
var text = await content.GetTextAsync();
await _terminalView.Write(text).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 _dispatcher.RunAsync(CoreDispatcherPriority.Normal, 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> GetKeyBindingsCollection(KeyBindings keyBindings)
{
var list = new List<KeyBinding>();
list.AddRange(keyBindings.CloseTab);
list.AddRange(keyBindings.Copy);
list.AddRange(keyBindings.NewTab);
list.AddRange(keyBindings.ConfigurableNewTab);
list.AddRange(keyBindings.NewWindow);
list.AddRange(keyBindings.NextTab);
list.AddRange(keyBindings.Paste);
list.AddRange(keyBindings.PreviousTab);
list.AddRange(keyBindings.ShowSettings);
list.AddRange(keyBindings.Search);
return list;
}
private Task FindNext()
{
return _terminalView.FindNext(SearchText);
}
private Task FindPrevious()
{
return _terminalView.FindPrevious(SearchText);
}
private void CloseSearchPanel()
{
SearchText = string.Empty;
ShowSearchPanel = false;
_terminalView.FocusTerminal();
}
}
}
@@ -0,0 +1,364 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Utilities;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Microsoft.Toolkit.Uwp.Helpers;
using System;
using System.Threading.Tasks;
using Windows.UI;
namespace FluentTerminal.App.ViewModels
{
public class ThemeViewModel : ViewModelBase
{
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private string _author;
private Color _background;
private double _backgroundOpacity;
private Color _black;
private Color _blue;
private Color _brightBlack;
private Color _brightBlue;
private Color _brightCyan;
private Color _brightGreen;
private Color _brightMagenta;
private Color _brightRed;
private Color _brightWhite;
private Color _brightYellow;
private Color _cursor;
private Color _cursorAccent;
private Color _cyan;
private string _fallBackAuthor;
private TerminalColors _fallBackColors;
private string _fallBackName;
private Color _foreground;
private Color _green;
private bool _inEditMode;
private bool _isActive;
private Color _magenta;
private string _name;
private Color _red;
private Color _selection;
private readonly TerminalTheme _theme;
private Color _white;
private Color _yellow;
public ThemeViewModel(TerminalTheme theme, ISettingsService settingsService, IDialogService dialogService)
{
_theme = theme;
_settingsService = settingsService;
_dialogService = dialogService;
Name = _theme.Name;
Author = _theme.Author;
Id = _theme.Id;
Black = _theme.Colors.Black.ToColor();
Red = _theme.Colors.Red.ToColor();
Green = _theme.Colors.Green.ToColor();
Yellow = _theme.Colors.Yellow.ToColor();
Blue = _theme.Colors.Blue.ToColor();
Magenta = _theme.Colors.Magenta.ToColor();
Cyan = _theme.Colors.Cyan.ToColor();
White = _theme.Colors.White.ToColor();
BrightBlack = _theme.Colors.BrightBlack.ToColor();
BrightRed = _theme.Colors.BrightRed.ToColor();
BrightGreen = _theme.Colors.BrightGreen.ToColor();
BrightYellow = _theme.Colors.BrightYellow.ToColor();
BrightBlue = _theme.Colors.BrightBlue.ToColor();
BrightMagenta = _theme.Colors.BrightMagenta.ToColor();
BrightCyan = _theme.Colors.BrightCyan.ToColor();
BrightWhite = _theme.Colors.BrightWhite.ToColor();
Background = _theme.Colors.Background.ToColor();
Foreground = _theme.Colors.Foreground.ToColor();
Cursor = _theme.Colors.Cursor.ToColor();
CursorAccent = _theme.Colors.CursorAccent.ToColor();
Selection = _theme.Colors.Selection.FromString();
SetActiveCommand = new RelayCommand(SetActive);
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);
}
public event EventHandler Activated;
public event EventHandler Deleted;
public string Author
{
get => _author;
set => Set(ref _author, value);
}
public Color Background
{
get => _background;
set => Set(ref _background, value);
}
public double BackgroundOpacity
{
get => _backgroundOpacity;
set => Set(ref _backgroundOpacity, value);
}
public Color Black
{
get => _black;
set => Set(ref _black, value);
}
public Color Blue
{
get => _blue;
set => Set(ref _blue, value);
}
public Color BrightBlack
{
get => _brightBlack;
set => Set(ref _brightBlack, value);
}
public Color BrightBlue
{
get => _brightBlue;
set => Set(ref _brightBlue, value);
}
public Color BrightCyan
{
get => _brightCyan;
set => Set(ref _brightCyan, value);
}
public Color BrightGreen
{
get => _brightGreen;
set => Set(ref _brightGreen, value);
}
public Color BrightMagenta
{
get => _brightMagenta;
set => Set(ref _brightMagenta, value);
}
public Color BrightRed
{
get => _brightRed;
set => Set(ref _brightRed, value);
}
public Color BrightWhite
{
get => _brightWhite;
set => Set(ref _brightWhite, value);
}
public Color BrightYellow
{
get => _brightYellow;
set => Set(ref _brightYellow, value);
}
public RelayCommand CancelEditCommand { get; }
public Color Cursor
{
get => _cursor;
set => Set(ref _cursor, value);
}
public Color CursorAccent
{
get => _cursorAccent;
set => Set(ref _cursorAccent, value);
}
public Color Cyan
{
get => _cyan;
set => Set(ref _cyan, value);
}
public RelayCommand DeleteCommand { get; }
public RelayCommand EditCommand { get; }
public Color Foreground
{
get => _foreground;
set => Set(ref _foreground, value);
}
public Color 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 Color Magenta
{
get => _magenta;
set => Set(ref _magenta, value);
}
public string Name
{
get => _name;
set => Set(ref _name, value);
}
public Color Red
{
get => _red;
set => Set(ref _red, value);
}
public RelayCommand SaveChangesCommand { get; }
public Color Selection
{
get => _selection;
set => Set(ref _selection, value);
}
public RelayCommand SetActiveCommand { get; }
public Color White
{
get => _white;
set => Set(ref _white, value);
}
public Color Yellow
{
get => _yellow;
set => Set(ref _yellow, value);
}
public void SaveChanges()
{
_theme.Name = Name;
_theme.Author = Author;
_theme.Colors.Black = Black.ToColorString(false);
_theme.Colors.Red = Red.ToColorString(false);
_theme.Colors.Green = Green.ToColorString(false);
_theme.Colors.Yellow = Yellow.ToColorString(false);
_theme.Colors.Blue = Blue.ToColorString(false);
_theme.Colors.Magenta = Magenta.ToColorString(false);
_theme.Colors.Cyan = Cyan.ToColorString(false);
_theme.Colors.White = White.ToColorString(false);
_theme.Colors.BrightBlack = BrightBlack.ToColorString(false);
_theme.Colors.BrightRed = BrightRed.ToColorString(false);
_theme.Colors.BrightGreen = BrightGreen.ToColorString(false);
_theme.Colors.BrightYellow = BrightYellow.ToColorString(false);
_theme.Colors.BrightBlue = BrightBlue.ToColorString(false);
_theme.Colors.BrightMagenta = BrightMagenta.ToColorString(false);
_theme.Colors.BrightCyan = BrightCyan.ToColorString(false);
_theme.Colors.BrightWhite = BrightWhite.ToColorString(false);
_theme.Colors.Background = Background.ToColorString(false);
_theme.Colors.Foreground = Foreground.ToColorString(false);
_theme.Colors.Cursor = Cursor.ToColorString(false);
_theme.Colors.CursorAccent = CursorAccent.ToColorString(false);
_theme.Colors.Selection = Selection.ToColorString(true);
_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.ToColor();
Red = _fallBackColors.Red.ToColor();
Green = _fallBackColors.Green.ToColor();
Yellow = _fallBackColors.Yellow.ToColor();
Blue = _fallBackColors.Blue.ToColor();
Magenta = _fallBackColors.Magenta.ToColor();
Cyan = _fallBackColors.Cyan.ToColor();
White = _fallBackColors.White.ToColor();
BrightBlack = _fallBackColors.BrightBlack.ToColor();
BrightRed = _fallBackColors.BrightRed.ToColor();
BrightGreen = _fallBackColors.BrightGreen.ToColor();
BrightYellow = _fallBackColors.BrightYellow.ToColor();
BrightBlue = _fallBackColors.BrightBlue.ToColor();
BrightMagenta = _fallBackColors.BrightMagenta.ToColor();
BrightCyan = _fallBackColors.BrightCyan.ToColor();
BrightWhite = _fallBackColors.BrightWhite.ToColor();
Background = _fallBackColors.Background.ToColor();
Foreground = _fallBackColors.Foreground.ToColor();
Cursor = _fallBackColors.Cursor.ToColor();
CursorAccent = _fallBackColors.CursorAccent.ToColor();
Selection = _fallBackColors.Selection.FromString();
Name = _fallBackName;
Author = _fallBackAuthor;
InEditMode = false;
}
}
private bool CanDelete()
{
return !_theme.PreInstalled;
}
private bool CanEdit()
{
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);
}
}
}
@@ -0,0 +1,79 @@
<ContentDialog
x:Class="FluentTerminal.App.Views.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 KeyBinding.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 KeyBinding.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 KeyBinding.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 KeyBinding.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 KeyBinding.Key, Mode=OneWay, Converter={StaticResource ZeroToCollapsedConverter}}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{x:Bind KeyBinding.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,72 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.ViewModels.Settings;
using FluentTerminal.Models;
using GalaSoft.MvvmLight.Command;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Views
{
public sealed partial class CreateKeyBindingDialog : ContentDialog
{
public static readonly DependencyProperty KeyBindingProperty =
DependencyProperty.Register(nameof(KeyBinding), typeof(KeyBindingViewModel), typeof(CreateKeyBindingDialog), new PropertyMetadata(null));
private readonly IDialogService _dialogService;
public CreateKeyBindingDialog(IDialogService dialogService)
{
_dialogService = dialogService;
InitializeComponent();
ResetCommand = new RelayCommand(Reset);
PreviewKeyDown += RegisterKeyBindingDialog_PreviewKeyDown;
Reset();
}
public KeyBindingViewModel KeyBinding
{
get { return (KeyBindingViewModel)GetValue(KeyBindingProperty); }
set { SetValue(KeyBindingProperty, value); }
}
public RelayCommand ResetCommand { get; }
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:
KeyBinding.Ctrl = true;
break;
case VirtualKey.Shift:
KeyBinding.Shift = true;
break;
case VirtualKey.Menu:
KeyBinding.Alt = true;
break;
case VirtualKey.LeftWindows:
case VirtualKey.RightWindows:
KeyBinding.Meta = true;
break;
default:
KeyBinding.Key = (int)e.Key;
break;
}
ResetButton.Visibility = Visibility.Visible;
e.Handled = true;
}
private void Reset()
{
KeyBinding = new KeyBindingViewModel(new KeyBinding(), _dialogService);
ResetButton.Visibility = Visibility.Collapsed;
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace FluentTerminal.App.Views
{
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,91 @@
<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.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" 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); }
}
}
}
+118 -26
View File
@@ -2,44 +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}"
xmlns:viewmodels="using:FluentTerminal.App.ViewModels"
RequestedTheme="Dark"
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"
Style="{StaticResource WindowTitleTextBlockStyle}"
Text="Fluent Terminal" />
<!--<Button
Grid.Column="2"
Click="OnSettingsButtonClick"
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>
<TextBlock
Grid.Column="0"
Margin="12,8,0,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>
<Frame x:Name="RootFrame" Grid.Row="1" />
<ContentControl
x:Name="TerminalContainer"
Grid.Row="1"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Content="{x:Bind ViewModel.SelectedTerminal, Mode=OneWay, Converter={StaticResource TerminalViewModelToViewConverter}}" />
</Grid>
</Page>
+23 -6
View File
@@ -1,10 +1,12 @@
using System.ComponentModel;
using FluentTerminal.App.ViewModels;
using System.ComponentModel;
using Windows.ApplicationModel.Core;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views
{
@@ -31,6 +33,8 @@ namespace FluentTerminal.App.Views
}
}
public MainViewModel ViewModel { get; private set; }
public MainPage()
{
InitializeComponent();
@@ -40,13 +44,24 @@ namespace FluentTerminal.App.Views
Loaded += OnLoaded;
Unloaded += OnUnloaded;
Window.Current.Activated += OnWindowActivated;
ApplicationView.PreferredLaunchViewSize = new Windows.Foundation.Size(800, 600);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is MainViewModel viewModel)
{
ViewModel = viewModel;
}
}
private async void OnWindowActivated(object sender, WindowActivatedEventArgs e)
{
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && RootFrame.Content is TerminalPage terminal)
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && TerminalContainer.Content is TerminalView terminal)
{
await terminal.FocusWebView();
await terminal.FocusWebView().ConfigureAwait(true);
}
}
@@ -59,7 +74,6 @@ namespace FluentTerminal.App.Views
{
coreTitleBar.LayoutMetricsChanged += OnLayoutMetricsChanged;
UpdateLayoutMetrics();
RootFrame.Navigate(typeof(TerminalPage));
}
private void OnLayoutMetricsChanged(CoreApplicationViewTitleBar sender, object e)
@@ -89,9 +103,12 @@ namespace FluentTerminal.App.Views
}
}
private async void OnSettingsButtonClick(object sender, RoutedEventArgs e)
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
await App.Instance.ShowSettings();
if (sender is Grid grid)
{
Tabs.MaxWidth = grid.ActualWidth - grid.ColumnDefinitions[2].ActualWidth;
}
}
}
}
+71 -5
View File
@@ -3,11 +3,77 @@
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="Settings" />
<Grid x:Name="Root">
<TextBlock
x:Name="AppTitle"
Canvas.ZIndex="1"
IsHitTestVisible="False"
Style="{StaticResource CaptionTextBlockStyle}"
Text="Settings"
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="200"
SelectionChanged="NavigationView_SelectionChanged">
<NavigationView.MenuItems>
<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>
<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>
</Page>
+153 -3
View File
@@ -1,12 +1,162 @@
using Windows.UI.Xaml.Controls;
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;
namespace FluentTerminal.App.Views
{
public sealed partial class SettingsPage : Page
{
private readonly UISettings _uiSettings;
private readonly ApplicationViewTitleBar _titleBar;
private readonly CoreDispatcher _dispatcher;
private bool _onThemesPage;
public SettingsPage()
{
this.InitializeComponent();
InitializeComponent();
Root.DataContext = this;
ApplicationView.PreferredLaunchViewSize = new Windows.Foundation.Size(1024, 768);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
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, () =>
{
_titleBar.ButtonBackgroundColor = Colors.Transparent;
_titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
_titleBar.ButtonForegroundColor = (Color)Resources["SystemBaseHighColor"];
_titleBar.ButtonHoverForegroundColor = (Color)Resources["SystemBaseHighColor"];
_titleBar.ButtonPressedForegroundColor = (Color)Resources["SystemBaseHighColor"];
if (Application.Current.RequestedTheme == ApplicationTheme.Light)
{
_titleBar.ButtonHoverBackgroundColor = (Color)Resources["SystemChromeMediumColor"];
_titleBar.ButtonPressedBackgroundColor = (Color)Resources["SystemChromeHighColor"];
}
else
{
_titleBar.ButtonHoverBackgroundColor = (Color)Resources["SystemChromeLowColor"];
_titleBar.ButtonPressedBackgroundColor = (Color)Resources["SystemChromeMediumLowColor"];
}
});
}
private IAsyncAction SetTitleBarColorsForThemesPage()
{
return _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
_titleBar.ButtonBackgroundColor = Colors.Transparent;
_titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
_titleBar.ButtonForegroundColor = Colors.White;
_titleBar.ButtonHoverForegroundColor = Colors.White;
_titleBar.ButtonPressedForegroundColor = Colors.White;
_titleBar.ButtonHoverBackgroundColor = Color.FromArgb(24, 255, 255, 255);
_titleBar.ButtonPressedBackgroundColor = Color.FromArgb(48, 255, 255, 255);
});
}
public double CoreTitleBarHeight { get; }
public SettingsViewModel ViewModel { get; private set; }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is SettingsViewModel viewModel)
{
ViewModel = viewModel;
}
}
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)
{
SetTitleBarColorsForThemesPage();
}
else
{
SetTitleBarColors();
}
}
private void SettingsPage_CloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
ViewModel.Close();
}
private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
{
AppTitle.Margin = new Thickness(CoreApplication.GetCurrentView().TitleBar.SystemOverlayLeftInset + 12, 8, 0, 0);
}
}
}
}
@@ -0,0 +1,52 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.GeneralSettings"
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"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="1" Margin="24,00,0,24">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Margin="0,10,0,0"
VerticalAlignment="Center"
FontSize="28"
Style="{StaticResource TitleTextBlockStyle}"
Text="General" />
<CommandBar
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
DefaultLabelPosition="Right">
<AppBarButton
Command="{x:Bind ViewModel.RestoreDefaultsCommand}"
Icon="Undo"
Label="Restore Defaults" />
</CommandBar>
</Grid>
<StackPanel Grid.Row="2" Margin="24,0,0,0">
<TextBlock Margin="0,0,0,8" Text="Open new terminals as" />
<RadioButton Content="Tabs" IsChecked="{x:Bind ViewModel.TabIsSelected, Mode=TwoWay}" />
<RadioButton
Margin="0,0,0,24"
Content="Windows"
IsChecked="{x:Bind ViewModel.WindowIsSelected, Mode=TwoWay}" />
<ToggleSwitch
Margin="0,0,0,24"
Header="Ask before closing windows"
IsOn="{x:Bind ViewModel.ConfirmClosingWindows, Mode=TwoWay}" />
<ToggleSwitch Header="Ask before closing tabs" IsOn="{x:Bind ViewModel.ConfirmClosingTabs, Mode=TwoWay}" />
</StackPanel>
</Grid>
</Page>
@@ -0,0 +1,24 @@
using FluentTerminal.App.ViewModels.Settings;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views.SettingsPages
{
public sealed partial class GeneralSettings : Page
{
public GeneralPageViewModel ViewModel { get; private set; }
public GeneralSettings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is GeneralPageViewModel viewModel)
{
ViewModel = viewModel;
}
}
}
}
@@ -0,0 +1,244 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.KeyBindingSettings"
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:enums="using:FluentTerminal.Models.Enums"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="using:FluentTerminal.App.Views"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="1" Margin="24,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Margin="0,10,0,0"
VerticalAlignment="Center"
FontSize="28"
Style="{StaticResource TitleTextBlockStyle}"
Text="Keybindings" />
<CommandBar
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
DefaultLabelPosition="Right">
<AppBarButton Icon="Add" Label="Add">
<AppBarButton.Flyout>
<MenuFlyout>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="New tab">
<MenuFlyoutItem.CommandParameter>
<enums:Command>NewTab</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="New configurable tab">
<MenuFlyoutItem.CommandParameter>
<enums:Command>ConfigurableNewTab</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Close tab">
<MenuFlyoutItem.CommandParameter>
<enums:Command>CloseTab</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Next tab">
<MenuFlyoutItem.CommandParameter>
<enums:Command>NextTab</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Previous tab">
<MenuFlyoutItem.CommandParameter>
<enums:Command>PreviousTab</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="New window">
<MenuFlyoutItem.CommandParameter>
<enums:Command>NewWindow</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Search">
<MenuFlyoutItem.CommandParameter>
<enums:Command>Search</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Show settings">
<MenuFlyoutItem.CommandParameter>
<enums:Command>ShowSettings</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Toggle window">
<MenuFlyoutItem.CommandParameter>
<enums:Command>ToggleWindow</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Copy">
<MenuFlyoutItem.CommandParameter>
<enums:Command>Copy</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
<MenuFlyoutItem Command="{x:Bind ViewModel.AddCommand}" Text="Paste">
<MenuFlyoutItem.CommandParameter>
<enums:Command>Paste</enums:Command>
</MenuFlyoutItem.CommandParameter>
</MenuFlyoutItem>
</MenuFlyout>
</AppBarButton.Flyout>
</AppBarButton>
<AppBarButton
Command="{x:Bind ViewModel.RestoreDefaultsCommand}"
CommandParameter="keyBindings"
Icon="Undo"
Label="Restore Defaults" />
</CommandBar>
</Grid>
<ScrollViewer Grid.Row="2" Margin="24,24,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Margin="0,6,24,0"
Text="New tab" />
<views:KeyBindingsView
Grid.Row="0"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.NewTab, Mode=OneWay}" />
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="0,6,24,0"
Text="New configurable tab" />
<views:KeyBindingsView
Grid.Row="1"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.ConfigurableNewTab, Mode=OneWay}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="0,6,24,0"
Text="Close tab" />
<views:KeyBindingsView
Grid.Row="2"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.CloseTab, Mode=OneWay}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="0,6,24,0"
Text="Next tab" />
<views:KeyBindingsView
Grid.Row="3"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.NextTab, Mode=OneWay}" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="0,6,24,0"
Text="Previous tab" />
<views:KeyBindingsView
Grid.Row="4"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.PreviousTab, Mode=OneWay}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="0,6,24,0"
Text="New window" />
<views:KeyBindingsView
Grid.Row="5"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.NewWindow, Mode=OneWay}" />
<TextBlock
Grid.Row="6"
Grid.Column="0"
Margin="0,6,24,0"
Text="Search" />
<views:KeyBindingsView
Grid.Row="6"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.Search, Mode=OneWay}" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
Margin="0,6,24,0"
Text="Show settings" />
<views:KeyBindingsView
Grid.Row="7"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.ShowSettings, Mode=OneWay}" />
<TextBlock
Grid.Row="8"
Grid.Column="0"
Margin="0,6,24,0"
Text="Toggle window" />
<views:KeyBindingsView
Grid.Row="8"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.ToggleWindow, Mode=OneWay}" />
<TextBlock
Grid.Row="9"
Grid.Column="0"
Margin="0,6,24,0"
Text="Copy" />
<views:KeyBindingsView
Grid.Row="9"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.Copy, Mode=OneWay}" />
<TextBlock
Grid.Row="10"
Grid.Column="0"
Margin="0,6,24,0"
Text="Paste" />
<views:KeyBindingsView
Grid.Row="10"
Grid.Column="1"
Margin="0,0,0,24"
ViewModel="{x:Bind ViewModel.Paste, Mode=OneWay}" />
</Grid>
</ScrollViewer>
</Grid>
</Page>
@@ -0,0 +1,24 @@
using FluentTerminal.App.ViewModels.Settings;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views.SettingsPages
{
public sealed partial class KeyBindingSettings : Page
{
public KeyBindingsPageViewModel ViewModel { get; private set; }
public KeyBindingSettings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is KeyBindingsPageViewModel viewModel)
{
ViewModel = viewModel;
}
}
}
}
@@ -0,0 +1,201 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.ShellProfileSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="using:FluentTerminal.App.ViewModels"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
Grid.RowSpan="3"
Grid.Column="0"
Background="{ThemeResource SystemControlAcrylicWindowBrush}" />
<Grid
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="0,0,0,24">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Margin="24,10,0,0"
VerticalAlignment="Center"
FontSize="28"
Style="{StaticResource TitleTextBlockStyle}"
Text="Profiles" />
<CommandBar
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="Transparent"
DefaultLabelPosition="Right"
Visibility="{x:Bind ViewModel.SelectedShellProfile.InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
<AppBarButton
Command="{x:Bind ViewModel.SelectedShellProfile.SetDefaultCommand, Mode=OneWay}"
Icon="Accept"
IsEnabled="{x:Bind ViewModel.SelectedShellProfile.IsDefault, Mode=OneWay, Converter={StaticResource NegateConverter}}"
Label="Set Default" />
<AppBarButton
Command="{x:Bind ViewModel.SelectedShellProfile.EditCommand, Mode=OneWay}"
Icon="Edit"
Label="Edit" />
<AppBarButton
Command="{x:Bind ViewModel.SelectedShellProfile.DeleteCommand, Mode=OneWay}"
Icon="Delete"
Label="Delete" />
</CommandBar>
<CommandBar
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="Transparent"
DefaultLabelPosition="Right"
Visibility="{x:Bind ViewModel.SelectedShellProfile.InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<AppBarButton
Command="{x:Bind ViewModel.SelectedShellProfile.SaveChangesCommand, Mode=OneWay}"
Icon="Save"
Label="Save" />
<AppBarButton
Command="{x:Bind ViewModel.SelectedShellProfile.CancelEditCommand, Mode=OneWay}"
Icon="Cancel"
Label="Cancel" />
</CommandBar>
</Grid>
<StackPanel Grid.Row="2" Grid.Column="0">
<ListView ItemsSource="{x:Bind ViewModel.ShellProfiles}" SelectedItem="{x:Bind ViewModel.SelectedShellProfile, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:ShellProfileViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{x:Bind Name, Mode=OneWay}" />
<Viewbox
Grid.Column="1"
Width="16"
Height="16"
HorizontalAlignment="Right">
<SymbolIcon Symbol="Accept" Visibility="{x:Bind IsDefault, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
</Viewbox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<Button
Height="48"
Margin="0,12,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Background="Transparent"
Command="{x:Bind ViewModel.CreateShellProfileCommand}"
Style="{StaticResource ButtonRevealStyle}">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Viewbox
Width="12"
Height="12"
Margin="0,0,6,0">
<SymbolIcon Symbol="Add" />
</Viewbox>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Create new" />
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
<ContentControl
Grid.Row="2"
Grid.Column="1"
Margin="24,0,0,0"
Content="{x:Bind ViewModel.SelectedShellProfile, Mode=TwoWay}">
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="viewmodels:ShellProfileViewModel">
<Grid>
<StackPanel Visibility="{x:Bind InEditMode, Converter={StaticResource TrueToVisibleConverter}, Mode=OneWay}">
<TextBox
Width="300"
HorizontalAlignment="Left"
Header="Name"
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
IsSpellCheckEnabled="False"
Text="{x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
<TextBox
Width="300"
Header="Shell executable location"
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
IsSpellCheckEnabled="False"
Text="{x:Bind Location, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button
Margin="6,0,0,0"
VerticalAlignment="Bottom"
Command="{x:Bind BrowseForCustomShellCommand}"
Content="..."
Style="{StaticResource ButtonRevealStyle}" />
</StackPanel>
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
<TextBox
Width="300"
Header="Working directory"
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
IsSpellCheckEnabled="False"
Text="{x:Bind WorkingDirectory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button
Margin="6,0,0,0"
VerticalAlignment="Bottom"
Command="{x:Bind BrowseForWorkingDirectoryCommand}"
Content="..."
Style="{StaticResource ButtonRevealStyle}" />
</StackPanel>
<TextBox
Width="300"
Margin="0,24,0,0"
HorizontalAlignment="Left"
Header="Arguments"
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
IsSpellCheckEnabled="False"
Text="{x:Bind Arguments, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind Name, Mode=OneWay}" />
<controls:HeaderedTextBlock
Margin="0,58,0,0"
Header="Shell executable location"
Text="{x:Bind Location, Mode=OneWay}" />
<controls:HeaderedTextBlock
Margin="0,36,0,0"
Header="Working directory"
Text="{x:Bind WorkingDirectory, Mode=OneWay}" />
<controls:HeaderedTextBlock
Margin="0,36,0,0"
Header="Arguments"
Text="{x:Bind Arguments, Mode=OneWay}" />
</StackPanel>
</Grid>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Page>
@@ -0,0 +1,24 @@
using FluentTerminal.App.ViewModels.Settings;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views.SettingsPages
{
public sealed partial class ShellProfileSettings : Page
{
public ProfilesPageViewModel ViewModel { get; private set; }
public ShellProfileSettings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is ProfilesPageViewModel viewModel)
{
ViewModel = viewModel;
}
}
}
}
@@ -0,0 +1,94 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.TerminalSettings"
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"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="1" Margin="24,00,0,24">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Margin="0,10,0,0"
VerticalAlignment="Center"
FontSize="28"
Style="{StaticResource TitleTextBlockStyle}"
Text="Terminal" />
<CommandBar
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
DefaultLabelPosition="Right">
<AppBarButton
Command="{x:Bind ViewModel.RestoreDefaultsCommand}"
Icon="Undo"
Label="Restore Defaults" />
</CommandBar>
</Grid>
<ScrollViewer Grid.Row="2" Margin="24,0,0,0">
<StackPanel>
<ComboBox
Width="200"
Margin="0,0,0,24"
Header="Font family"
ItemsSource="{x:Bind ViewModel.Fonts}"
SelectedValue="{x:Bind ViewModel.FontFamily, Mode=TwoWay}" />
<ComboBox
Width="200"
Margin="0,0,0,24"
Header="Font size"
ItemsSource="{x:Bind ViewModel.Sizes}"
SelectedValue="{x:Bind ViewModel.FontSize, Mode=TwoWay}" />
<Slider
Width="200"
Margin="0,0,0,24"
HorizontalAlignment="Left"
Header="Background opacity"
Maximum="1"
Minimum="0"
StepFrequency="0.01"
Value="{x:Bind ViewModel.BackgroundOpacity, Mode=TwoWay}" />
<ToggleSwitch
Margin="0,0,0,24"
Header="Blinking cursor"
IsOn="{x:Bind ViewModel.CursorBlink, Mode=TwoWay}" />
<TextBlock Margin="0,0,0,8" Text="Cursor style" />
<RadioButton
Content="Block"
GroupName="Cursor"
IsChecked="{x:Bind ViewModel.BlockIsSelected, Mode=TwoWay}" />
<RadioButton
Content="Underline"
GroupName="Cursor"
IsChecked="{x:Bind ViewModel.UnderlineIsSelected, Mode=TwoWay}" />
<RadioButton
Content="Bar"
GroupName="Cursor"
IsChecked="{x:Bind ViewModel.BarIsSelected, Mode=TwoWay}" />
<TextBlock Margin="0,24,0,8" Text="Scrollbar style" />
<RadioButton
Content="Hidden"
GroupName="ScrollBar"
IsChecked="{x:Bind ViewModel.HiddenIsSelected, Mode=TwoWay}" />
<RadioButton
Content="Auto hiding"
GroupName="ScrollBar"
IsChecked="{x:Bind ViewModel.AutoHidingIsSelected, Mode=TwoWay}" />
<RadioButton
Content="Visible"
GroupName="ScrollBar"
IsChecked="{x:Bind ViewModel.VisibleIsSelected, Mode=TwoWay}" />
</StackPanel>
</ScrollViewer>
</Grid>
</Page>
@@ -0,0 +1,24 @@
using FluentTerminal.App.ViewModels.Settings;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views.SettingsPages
{
public sealed partial class TerminalSettings : Page
{
public TerminalSettings()
{
InitializeComponent();
}
public TerminalPageViewModel ViewModel { get; private set; }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is TerminalPageViewModel viewModel)
{
ViewModel = viewModel;
}
}
}
}
@@ -0,0 +1,289 @@
<Page
x:Class="FluentTerminal.App.Views.SettingsPages.ThemeSettings"
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:viewmodels="using:FluentTerminal.App.ViewModels"
xmlns:views="using:FluentTerminal.App.Views"
mc:Ignorable="d">
<Grid x:Name="Root">
<Grid.Background>
<AcrylicBrush
BackgroundSource="HostBackdrop"
FallbackColor="{x:Bind ViewModel.SelectedTheme.Background, Mode=OneWay}"
TintColor="{x:Bind ViewModel.SelectedTheme.Background, Mode=OneWay}"
TintOpacity="{x:Bind ViewModel.BackgroundOpacity, Mode=OneWay}" />
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
Grid.RowSpan="3"
Grid.Column="0"
Background="{ThemeResource SystemControlAcrylicWindowBrush}" />
<Grid
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="0,0,0,24">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Margin="24,10,0,0"
VerticalAlignment="Center"
FontSize="28"
Style="{StaticResource TitleTextBlockStyle}"
Text="Themes" />
<CommandBar
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="Transparent"
DefaultLabelPosition="Right"
Visibility="{x:Bind ViewModel.SelectedTheme.InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
<AppBarButton
Command="{x:Bind ViewModel.SelectedTheme.SetActiveCommand, Mode=OneWay}"
Foreground="White"
Icon="Accept"
IsEnabled="{x:Bind ViewModel.SelectedTheme.IsActive, Mode=OneWay, Converter={StaticResource NegateConverter}}"
Label="Set Active"
Style="{StaticResource ThemeAppBarButtonStyle}" />
<AppBarButton
Command="{x:Bind ViewModel.SelectedTheme.EditCommand, Mode=OneWay}"
Foreground="White"
Icon="Edit"
Label="Edit"
Style="{StaticResource ThemeAppBarButtonStyle}" />
<AppBarButton
Command="{x:Bind ViewModel.SelectedTheme.DeleteCommand, Mode=OneWay}"
Foreground="White"
Icon="Delete"
Label="Delete"
Style="{StaticResource ThemeAppBarButtonStyle}" />
</CommandBar>
<CommandBar
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="Transparent"
DefaultLabelPosition="Right"
Visibility="{x:Bind ViewModel.SelectedTheme.InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<AppBarButton
Command="{x:Bind ViewModel.SelectedTheme.SaveChangesCommand, Mode=OneWay}"
Foreground="White"
Icon="Save"
Label="Save"
Style="{StaticResource ThemeAppBarButtonStyle}" />
<AppBarButton
Command="{x:Bind ViewModel.SelectedTheme.CancelEditCommand, Mode=OneWay}"
Foreground="White"
Icon="Cancel"
Label="Cancel"
Style="{StaticResource ThemeAppBarButtonStyle}" />
</CommandBar>
</Grid>
<StackPanel Grid.Row="2" Grid.Column="0">
<ListView ItemsSource="{x:Bind ViewModel.Themes}" SelectedItem="{x:Bind ViewModel.SelectedTheme, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:ThemeViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{x:Bind Name, Mode=OneWay}" />
<Viewbox
Grid.Column="1"
Width="16"
Height="16"
HorizontalAlignment="Right">
<SymbolIcon Symbol="Accept" Visibility="{x:Bind IsActive, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
</Viewbox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<Button
Height="48"
Margin="0,12,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
Background="Transparent"
Command="{x:Bind ViewModel.CreateThemeCommand}"
Style="{StaticResource ButtonRevealStyle}">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Viewbox
Width="12"
Height="12"
Margin="0,0,6,0">
<SymbolIcon Symbol="Add" />
</Viewbox>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Create new" />
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
<ContentControl
Grid.Row="2"
Grid.Column="1"
Margin="24,0,0,0"
Content="{x:Bind ViewModel.SelectedTheme, Mode=TwoWay}">
<ContentControl.ContentTemplate>
<DataTemplate x:DataType="viewmodels:ThemeViewModel">
<StackPanel>
<TextBlock
Style="{StaticResource SubtitleTextBlockStyle}"
Text="{x:Bind Name, Mode=OneWay}"
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
<TextBlock.Foreground>
<SolidColorBrush Color="{x:Bind Foreground, Mode=OneWay}" />
</TextBlock.Foreground>
</TextBlock>
<TextBlock
Foreground="White"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Author, Mode=OneWay}" />
<TextBox
Header="Name"
RequestedTheme="Dark"
Text="{x:Bind Name, Mode=TwoWay}"
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
<TextBox
Margin="0,24,0,0"
Header="Author"
RequestedTheme="Dark"
Text="{x:Bind Author, Mode=TwoWay}"
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
<Grid Margin="0,48,0,0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="Border">
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
</Style>
</Grid.Resources>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<views:TerminalColorPicker
ColorName="Black"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Black, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Red"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Red, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Green"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Green, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Yellow"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Yellow, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Blue"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Blue, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Magenta"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Magenta, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Cyan"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Cyan, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="White"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind White, Mode=TwoWay}" />
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<views:TerminalColorPicker
ColorName="Bright Black"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightBlack, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Bright Red"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightRed, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Bright Green"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightGreen, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Bright Yellow"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightYellow, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Bright Blue"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightBlue, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Bright Magenta"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightMagenta, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Bright Cyan"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightCyan, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Bright White"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind BrightWhite, Mode=TwoWay}" />
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal">
<views:TerminalColorPicker
ColorName="Foreground"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Foreground, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Background"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Background, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Cursor"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind Cursor, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Cursor Accent"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
SelectedColor="{x:Bind CursorAccent, Mode=TwoWay}" />
<views:TerminalColorPicker
ColorName="Selection"
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
IsAlphaEnabled="True"
SelectedColor="{x:Bind Selection, Mode=TwoWay}" />
</StackPanel>
<TextBlock
Grid.Row="3"
Margin="0,6,0,0"
Foreground="White"
Text="Click on a color to edit"
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
</Grid>
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Page>
@@ -0,0 +1,25 @@
using FluentTerminal.App.ViewModels.Settings;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace FluentTerminal.App.Views.SettingsPages
{
public sealed partial class ThemeSettings : Page
{
public ThemesPageViewModel ViewModel { get; private set; }
public ThemeSettings()
{
InitializeComponent();
Root.DataContext = this;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is ThemesPageViewModel viewModel)
{
ViewModel = viewModel;
}
}
}
}
@@ -0,0 +1,34 @@
<UserControl
x:Class="FluentTerminal.App.Views.TerminalColorPicker"
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="24"
d:DesignWidth="24"
mc:Ignorable="d">
<Button
x:Name="Root"
IsEnabled="{x:Bind EnableEditing, Mode=OneWay}"
Style="{StaticResource ColorPreviewButtonStyle}"
ToolTipService.ToolTip="{x:Bind ColorName}">
<Button.Background>
<SolidColorBrush Color="{x:Bind SelectedColor, Mode=OneWay}" />
</Button.Background>
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock
Margin="0,0,0,6"
Style="{StaticResource TitleTextBlockStyle}"
Text="{x:Bind ColorName}" />
<ColorPicker
x:Name="colorPicker"
ColorChanged="ColorPicker_ColorChanged"
IsAlphaEnabled="{x:Bind IsAlphaEnabled, Mode=OneWay}"
IsColorChannelTextInputVisible="False" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</UserControl>
@@ -0,0 +1,62 @@
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Views
{
public sealed partial class TerminalColorPicker : UserControl
{
public static readonly DependencyProperty ColorNameProperty =
DependencyProperty.Register(nameof(ColorName), typeof(string), typeof(TerminalColorPicker), new PropertyMetadata(null));
public static readonly DependencyProperty EnableEditingProperty =
DependencyProperty.Register(nameof(EnableEditing), typeof(bool), typeof(TerminalColorPicker), new PropertyMetadata(false));
public static readonly DependencyProperty IsAlphaEnabledProperty =
DependencyProperty.Register(nameof(IsAlphaEnabled), typeof(bool), typeof(TerminalColorPicker), new PropertyMetadata(false));
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register(nameof(SelectedColor), typeof(Color), typeof(TerminalColorPicker), new PropertyMetadata(Colors.Transparent, (s, e) =>
{
if (s is TerminalColorPicker terminalColorPicker)
{
terminalColorPicker.colorPicker.Color = (Color)e.NewValue;
}
}));
public TerminalColorPicker()
{
InitializeComponent();
Root.DataContext = this;
}
public string ColorName
{
get { return (string)GetValue(ColorNameProperty); }
set { SetValue(ColorNameProperty, value); }
}
public bool EnableEditing
{
get { return (bool)GetValue(EnableEditingProperty); }
set { SetValue(EnableEditingProperty, value); }
}
public bool IsAlphaEnabled
{
get { return (bool)GetValue(IsAlphaEnabledProperty); }
set { SetValue(IsAlphaEnabledProperty, value); }
}
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
private void ColorPicker_ColorChanged(ColorPicker sender, ColorChangedEventArgs args)
{
SelectedColor = args.NewColor;
}
}
}
@@ -1,9 +0,0 @@
<Page
x:Class="FluentTerminal.App.Views.TerminalPage"
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"
mc:Ignorable="d">
<Grid x:Name="WebViewContainer" Margin="12" />
</Page>
@@ -1,65 +0,0 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Core.Preview;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Views
{
public sealed partial class TerminalPage : Page
{
private WebView _webView;
public TerminalPage()
{
InitializeComponent();
Loaded += OnLoaded;
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequested;
}
public async Task FocusWebView()
{
if (_webView != null)
{
_webView.Focus(FocusState.Programmatic);
await ExecuteScriptAsync("document.focus();");
}
}
private void OnCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
_webView.Navigate(new Uri("about:blank"));
_webView = null;
}
public Task<string> ExecuteScriptAsync(string script)
{
try
{
return _webView.InvokeScriptAsync("eval", new[]
{
"try {" + script + "} catch(error) {}"
}).AsTask();
}
catch (Exception e)
{
Debug.WriteLine($"Exception while running:\n \"{script}\"\n\n {e}");
}
return Task.FromResult(string.Empty);
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
_webView = new WebView(WebViewExecutionMode.SeparateThread)
{
DefaultBackgroundColor = Colors.Transparent
};
WebViewContainer.Children.Add(_webView);
_webView.Navigate(new Uri("http://localhost:9000/Client/index.html"));
_webView.Focus(FocusState.Programmatic);
}
}
}
@@ -0,0 +1,73 @@
<UserControl
x:Class="FluentTerminal.App.Views.TerminalView"
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="600"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid x:Name="WebViewContainer" Grid.Row="0" />
<Grid
Grid.Row="0"
Margin="12"
Padding="6"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="{x:Bind ViewModel.ShowResizeOverlay, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<Grid.Background>
<SolidColorBrush Color="{ThemeResource SystemAccentColor}" />
</Grid.Background>
<TextBlock Foreground="White" Text="{x:Bind ViewModel.ResizeOverlayContent, Mode=OneWay}" />
</Grid>
<RelativePanel
Grid.Row="1"
Padding="12"
Visibility="{x:Bind ViewModel.ShowSearchPanel, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<TextBlock
Name="SearchLabel"
Margin="0,6,0,0"
RelativePanel.AlignLeftWithPanel="True"
Text="Search" />
<TextBox
Name="SearchTextBox"
Width="300"
Margin="6,0,6,0"
KeyUp="SearchTextBox_KeyUp"
PlaceholderText="Enter search term"
RelativePanel.RightOf="SearchLabel"
Text="{x:Bind ViewModel.SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button
Name="PreviousButton"
Margin="0,0,6,0"
Command="{x:Bind ViewModel.FindPreviousCommand}"
RelativePanel.RightOf="SearchTextBox"
ToolTipService.ToolTip="Find previous">
<Button.Content>
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE76B;" />
</Button.Content>
</Button>
<Button
Command="{x:Bind ViewModel.FindNextCommand}"
RelativePanel.RightOf="PreviousButton"
ToolTipService.ToolTip="Find next">
<Button.Content>
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="&#xE76C;" />
</Button.Content>
</Button>
<Button
Command="{x:Bind ViewModel.CloseSearchPanelCommand}"
RelativePanel.AlignRightWithPanel="True"
ToolTipService.ToolTip="Close search panel">
<Button.Content>
<SymbolIcon Symbol="Cancel" />
</Button.Content>
</Button>
</RelativePanel>
</Grid>
</UserControl>
@@ -0,0 +1,268 @@
using FluentTerminal.App.ViewModels;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using FluentTerminal.RuntimeComponent.Interfaces;
using FluentTerminal.RuntimeComponent.WebAllowedObjects;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.System;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Views
{
public sealed partial class TerminalView : UserControl, ITerminalView, ITerminalEventListener
{
private MenuFlyoutItem _copyMenuItem;
private BlockingCollection<Action> _dispatcherJobs;
private readonly SemaphoreSlim _loaded;
private MenuFlyoutItem _pasteMenuItem;
private WebView _webView;
public TerminalView(TerminalViewModel viewModel)
{
ViewModel = viewModel;
InitializeComponent();
Loaded += OnLoaded;
_loaded = new SemaphoreSlim(0, 1);
JsonConvert.DefaultSettings = () =>
{
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
settings.Converters.Add(new StringEnumConverter(true));
return settings;
};
StartMediatorTask();
}
public event EventHandler<Command> KeyboardCommandReceived;
public event EventHandler<TerminalSize> TerminalSizeChanged;
public event EventHandler<string> TerminalTitleChanged;
public TerminalViewModel ViewModel { get; }
public Task ChangeOptions(TerminalOptions options)
{
var serialized = JsonConvert.SerializeObject(options);
return ExecuteScriptAsync($"changeOptions('{serialized}')");
}
public Task ChangeTheme(TerminalColors theme)
{
var serialized = JsonConvert.SerializeObject(theme);
return ExecuteScriptAsync($"changeTheme('{serialized}')");
}
public Task ChangeKeyBindings(IEnumerable<KeyBinding> keyBindings)
{
var serialized = JsonConvert.SerializeObject(keyBindings);
return ExecuteScriptAsync($"changeKeyBindings('{serialized}')");
}
public void Close()
{
_webView?.Navigate(new Uri("about:blank"));
_webView = null;
}
public Task ConnectToSocket(string url)
{
return ExecuteScriptAsync($"connectToWebSocket('{url}');");
}
public async Task<TerminalSize> CreateTerminal(TerminalOptions options, TerminalColors theme, IEnumerable<KeyBinding> keyBindings)
{
var serializedOptions = JsonConvert.SerializeObject(options);
var serializedTheme = JsonConvert.SerializeObject(theme);
var serializedKeyBindings = JsonConvert.SerializeObject(keyBindings);
var size = await ExecuteScriptAsync($"createTerminal('{serializedOptions}', '{serializedTheme}', '{serializedKeyBindings}')").ConfigureAwait(true);
return JsonConvert.DeserializeObject<TerminalSize>(size);
}
public Task<string> ExecuteScriptAsync(string script)
{
try
{
return _webView.InvokeScriptAsync("eval", new[] { script }).AsTask();
}
catch (Exception e)
{
Debug.WriteLine($"Exception while running:\n \"{script}\"\n\n {e}");
}
return Task.FromResult(string.Empty);
}
public async Task FocusTerminal()
{
if (_webView != null)
{
_webView.Focus(FocusState.Programmatic);
await ExecuteScriptAsync("document.focus();").ConfigureAwait(true);
}
}
public async Task FocusWebView()
{
if (_webView != null)
{
_webView.Focus(FocusState.Programmatic);
await ExecuteScriptAsync("document.focus();").ConfigureAwait(true);
}
}
public Task<string> GetSelection() => ExecuteScriptAsync("term.getSelection()");
public void OnKeyboardCommand(string command)
{
_dispatcherJobs.Add(() => KeyboardCommandReceived?.Invoke(this, Enum.Parse<Command>(command, true)));
}
public void OnTerminalResized(int columns, int rows)
{
_dispatcherJobs.Add(() => TerminalSizeChanged?.Invoke(this, new TerminalSize { Columns = columns, Rows = rows }));
}
public void OnTitleChanged(string title)
{
_dispatcherJobs.Add(() => TerminalTitleChanged?.Invoke(this, title));
}
public Task Write(string text)
{
text = Convert.ToBase64String(Encoding.UTF8.GetBytes(text));
return ExecuteScriptAsync($"paste('{text}')");
}
public Task FindNext(string searchText)
{
return ExecuteScriptAsync($"findNext('{searchText}')");
}
public Task FindPrevious(string searchText)
{
return ExecuteScriptAsync($"findPrevious('{searchText}')");
}
private void _webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
_loaded.Release();
}
private void _webView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
var bridge = new TerminalBridge(this);
_webView.AddWebAllowedObject("terminalBridge", bridge);
}
private void Copy_Click(object sender, RoutedEventArgs e)
{
OnKeyboardCommand(nameof(Command.Copy));
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
if (WebViewContainer.Children.Any())
{
return;
}
_webView = new WebView(WebViewExecutionMode.SeparateThread)
{
DefaultBackgroundColor = Colors.Transparent
};
WebViewContainer.Children.Add(_webView);
_webView.NavigationCompleted += _webView_NavigationCompleted;
_webView.NavigationStarting += _webView_NavigationStarting;
_copyMenuItem = new MenuFlyoutItem { Text = "Copy" };
_copyMenuItem.Click += Copy_Click;
_pasteMenuItem = new MenuFlyoutItem { Text = "Paste" };
_pasteMenuItem.Click += Paste_Click;
_webView.ContextFlyout = new MenuFlyout
{
Items = { _copyMenuItem, _pasteMenuItem }
};
_webView.Navigate(new Uri("ms-appx-web:///Client/index.html"));
await _loaded.WaitAsync().ConfigureAwait(true);
_webView.Focus(FocusState.Programmatic);
await ViewModel.OnViewIsReady(this).ConfigureAwait(true);
}
public void OnRightClick(int x, int y, bool hasSelection)
{
_dispatcherJobs.Add(() =>
{
var flyout = (MenuFlyout)_webView.ContextFlyout;
_copyMenuItem.IsEnabled = hasSelection;
flyout.ShowAt(_webView, new Point(x, y));
});
}
private void Paste_Click(object sender, RoutedEventArgs e)
{
OnKeyboardCommand(nameof(Command.Paste));
}
private void StartMediatorTask()
{
_dispatcherJobs = new BlockingCollection<Action>();
var dispatcher = CoreApplication.GetCurrentView().CoreWindow.Dispatcher;
Task.Run(async () =>
{
foreach (var job in _dispatcherJobs.GetConsumingEnumerable())
{
try
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => job.Invoke());
}
catch (Exception e)
{
Debug.WriteLine(e);
}
}
});
}
private void SearchTextBox_KeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Escape)
{
ViewModel.CloseSearchPanelCommand.Execute(null);
}
else if (e.Key == VirtualKey.Enter)
{
ViewModel.FindNextCommand.Execute(null);
}
}
public void FocusSearchTextBox()
{
SearchTextBox.Focus(FocusState.Programmatic);
}
}
}
+1
View File
@@ -1 +1,2 @@
node_modules/
dist/
File diff suppressed because it is too large. Load diff
-12
View File
@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="xterm.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="terminal-container"></div>
<script src="bundle.js"></script>
</body>
</html>
-125
View File
@@ -1,125 +0,0 @@
/**
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
* @license MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/
/**
* Default styles for xterm.js
*/
.xterm {
font-family: courier-new, courier, monospace;
font-feature-settings: "liga" 0;
position: relative;
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.xterm.focus,
.xterm:focus {
outline: none;
}
.xterm .xterm-helpers {
position: absolute;
top: 0;
/**
* The z-index of the helpers must be higher than the canvases in order for
* IMEs to appear on top.
*/
z-index: 10;
}
.xterm .xterm-helper-textarea {
/*
* HACK: to fix IE's blinking cursor
* Move textarea out of the screen to the far left, so that the cursor is not visible.
*/
position: absolute;
opacity: 0;
left: -9999em;
top: 0;
width: 0;
height: 0;
z-index: -10;
/** Prevent wrapping so the IME appears against the textarea at the correct position */
white-space: nowrap;
overflow: hidden;
resize: none;
}
.xterm .composition-view {
/* TODO: Composition position got messed up somewhere */
background: #000;
color: #FFF;
display: none;
position: absolute;
white-space: nowrap;
z-index: 1;
}
.xterm .composition-view.active {
display: block;
}
.xterm .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
cursor: default;
}
.xterm canvas {
position: absolute;
left: 0;
top: 0;
}
.xterm .xterm-scroll-area {
visibility: hidden;
}
.xterm .xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
left: -9999em;
}
.xterm.enable-mouse-events {
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
cursor: default;
}
.xterm:not(.enable-mouse-events) {
cursor: text;
}
+5 -6
View File
@@ -2472,12 +2472,6 @@
"safe-buffer": "5.1.1"
}
},
"raw-loader": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
"integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
"dev": true
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -3018,6 +3012,11 @@
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
"dev": true
},
"xterm": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-3.4.0.tgz",
"integrity": "sha512-eHH9CuKZOdKscBoUPmBxE1nTAuaKCMavcTQilyQf2u6MqB366BJP7SbkGWz+HZj7V5OjldFLLDLzlliqUzoiHQ=="
},
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+4 -2
View File
@@ -5,12 +5,14 @@
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "xcopy /y /s ..\\xterm.js\\build\\xterm.css dist\\ && npx webpack --config webpack.config.js"
"build": "xcopy /y /s node_modules\\xterm\\dist\\xterm.css dist\\ && xcopy /y /s src\\style.css dist\\ && xcopy /y /s src\\index.html dist\\ && npx webpack --config webpack.config.js"
},
"author": "FS Apps",
"license": "GPL-3.0",
"dependencies": {},
"devDependencies": {
"webpack": "^3.10.0"
},
"dependencies": {
"xterm": "^3.4.0"
}
}
+15
View File
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="xterm.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="terminal-container"></div>
<script src="bundle.js"></script>
</body>
</html>
+151 -34
View File
@@ -1,58 +1,175 @@
import * as Terminal from '../../xterm.js/build/xterm';
import * as attach from '../../xterm.js/build/addons/attach/attach';
import * as fit from '../../xterm.js/build/addons/fit/fit';
import * as winptyCompat from '../../xterm.js/build/addons/winptyCompat/winptyCompat';
import * as Terminal from '../node_modules/xterm/dist/xterm';
import * as attach from '../node_modules/xterm/dist/addons/attach/attach';
import * as fit from '../node_modules/xterm/dist/addons/fit/fit';
import * as winptyCompat from '../node_modules/xterm/dist/addons/winptyCompat/winptyCompat';
import * as search from '../node_modules/xterm/dist/addons/search/search';
Terminal.applyAddon(attach);
Terminal.applyAddon(fit);
Terminal.applyAddon(winptyCompat);
Terminal.applyAddon(search);
var term, protocol, socketURL, socket;
var term, socket;
var terminalContainer = document.getElementById('terminal-container');
createTerminal();
function createTerminal() {
function createTerminal(options, theme, keyBindings) {
while (terminalContainer.children.length) {
terminalContainer.removeChild(terminalContainer.children[0]);
}
term = new Terminal({
cursorBlink: true,
fontFamily: 'consolas',
fontSize: 13,
theme = JSON.parse(theme);
theme.background = 'transparent';
window.keyBindings = JSON.parse(keyBindings);
options = JSON.parse(options);
setScrollBarStyle(options.scrollBarStyle);
var terminalOptions = {
fontFamily: options.fontFamily,
fontSize: options.fontSize,
cursorStyle: options.cursorStyle,
cursorBlink: options.cursorBlink,
bellStyle: options.bellStyle,
allowTransparency: true,
theme: {
background: "transparent"
}
});
window.term = term; // Expose `term` to window for debugging purposes
theme: theme,
experimentalCharAtlas: 'dynamic'
};
term = new Terminal(terminalOptions);
window.term = term;
term.on('resize', function (size) {
if (!socketURL) {
return;
}
var cols = size.cols,
rows = size.rows,
url = 'http://localhost:9000/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
fetch(url, {method: 'POST'});
terminalBridge.notifySizeChanged(term.cols, term.rows);
});
term.on('title', function (title) {
terminalBridge.notifyTitleChanged(title);
});
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
term.open(terminalContainer);
term.winptyCompatInit();
term.fit();
term.focus();
fetch('http://localhost:9000/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
console.log("got response");
res.text().then(function (url) {
socketURL = JSON.parse(url);
socket = new WebSocket(socketURL);
socket.onopen = runRealTerminal;
});
});
var resizeTimeout;
window.onresize = function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(term.fit(), 500);
}
window.onmouseup = function (e) {
if (e.button == 2) {
terminalBridge.notifyRightClick(e.clientX, e.clientY, term.hasSelection());
}
}
term.attachCustomKeyEventHandler(function (e) {
for (var i = 0; i < window.keyBindings.length; i++) {
var keyBinding = window.keyBindings[i];
if (keyBinding.ctrl == e.ctrlKey
&& keyBinding.meta == e.metaKey
&& keyBinding.alt == e.altKey
&& keyBinding.shift == e.shiftKey
&& keyBinding.key == e.keyCode) {
if (document.visibilityState == 'visible') {
if (keyBinding.command == 'copy' && term.getSelection() == '') {
return true;
}
e.preventDefault();
terminalBridge.invokeCommand(keyBinding.command);
}
return false;
}
}
return true;
});
return JSON.stringify({
rows: term.rows,
columns: term.cols
});
}
function runRealTerminal() {
function attachTerminal() {
term.attach(socket);
term._initialized = true;
}
function connectToWebSocket(url) {
socket = new WebSocket(url);
socket.onopen = attachTerminal;
socket.onclose = function() {
terminalBridge.invokeCommand('CloseTab');
};
}
function changeTheme(theme) {
theme = JSON.parse(theme);
theme.background = 'transparent';
term.setOption('theme', theme);
}
function changeOptions(options) {
options = JSON.parse(options);
term.setOption('bellStyle', options.bellStyle);
term.setOption('cursorBlink', options.cursorBlink);
term.setOption('cursorStyle', options.cursorStyle);
term.setOption('fontFamily', options.fontFamily);
term.setOption('fontSize', options.fontSize);
setScrollBarStyle(options.scrollBarStyle);
}
function setScrollBarStyle(scrollBarStyle) {
if (scrollBarStyle == 'hidden') {
document.getElementById('terminal-container').style['-ms-overflow-style'] = 'none';
} else if (scrollBarStyle == 'autoHiding') {
document.getElementById('terminal-container').style['-ms-overflow-style'] = '-ms-autohiding-scrollbar';
} else if (scrollBarStyle == 'visible') {
document.getElementById('terminal-container').style['-ms-overflow-style'] = 'scrollbar';
}
}
function changeKeyBindings(keyBindings) {
keyBindings = JSON.parse(keyBindings);
window.keyBindings = keyBindings;
}
function paste(content) {
content = b64DecodeUnicode(content);
term.send(content);
}
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
}).join(''))
}
function findNext(content) {
term.findNext(content);
}
function findPrevious(content) {
term.findPrevious(content);
}
document.oncontextmenu = function () {
return false;
};
window.createTerminal = createTerminal;
window.connectToWebSocket = connectToWebSocket;
window.changeTheme = changeTheme;
window.changeOptions = changeOptions;
window.changeKeyBindings = changeKeyBindings;
window.paste = paste;
window.findNext = findNext;
window.findPrevious = findPrevious;
@@ -2,23 +2,17 @@
margin: 0;
}
html {
-ms-overflow-style: none;
html, body {
background: transparent;
height: 100%;
}
body {
background: transparent;
height: 100%;
}
#terminal-container {
width: 100%;
height: 100%;
margin: 0;
}
.terminal {
background: transparent !important;
}
padding: 12px;
}
@@ -0,0 +1,11 @@
using FluentTerminal.Models.Enums;
namespace FluentTerminal.Models
{
public class ApplicationSettings
{
public bool ConfirmClosingTabs { get; set; }
public bool ConfirmClosingWindows { get; set; }
public NewTerminalLocation NewTerminalLocation { get; set; }
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace FluentTerminal.Models.Enums
{
public enum BellStyle
{
None,
Sound
}
}
+17
View File
@@ -0,0 +1,17 @@
namespace FluentTerminal.Models.Enums
{
public enum Command
{
ToggleWindow,
NextTab,
PreviousTab,
NewTab,
ConfigurableNewTab,
CloseTab,
NewWindow,
ShowSettings,
Copy,
Paste,
Search
}
}
@@ -0,0 +1,9 @@
namespace FluentTerminal.Models.Enums
{
public enum CursorStyle
{
Block,
Underline,
Bar
}
}
@@ -0,0 +1,211 @@
using System.ComponentModel;
namespace FluentTerminal.Models.Enums
{
public enum ExtendedVirtualKey
{
None = 0,
LeftButton = 1,
RightButton = 2,
Cancel = 3,
MiddleButton = 4,
XButton1 = 5,
XButton2 = 6,
Back = 8,
Tab = 9,
Clear = 12,
Enter = 13,
Shift = 16,
Control = 17,
Menu = 18,
Pause = 19,
CapitalLock = 20,
Kana = 21,
Hangul = 21,
Junja = 23,
Final = 24,
Hanja = 25,
Kanji = 25,
Escape = 27,
Convert = 28,
NonConvert = 29,
Accept = 30,
ModeChange = 31,
Space = 32,
PageUp = 33,
PageDown = 34,
End = 35,
Home = 36,
Left = 37,
Up = 38,
Right = 39,
Down = 40,
Select = 41,
Print = 42,
Execute = 43,
Snapshot = 44,
Insert = 45,
Delete = 46,
Help = 47,
[Description("0")]
Number0 = 48,
[Description("1")]
Number1 = 49,
[Description("2")]
Number2 = 50,
[Description("3")]
Number3 = 51,
[Description("4")]
Number4 = 52,
[Description("5")]
Number5 = 53,
[Description("6")]
Number6 = 54,
[Description("7")]
Number7 = 55,
[Description("8")]
Number8 = 56,
[Description("9")]
Number9 = 57,
A = 65,
B = 66,
C = 67,
D = 68,
E = 69,
F = 70,
G = 71,
H = 72,
I = 73,
J = 74,
K = 75,
L = 76,
M = 77,
N = 78,
O = 79,
P = 80,
Q = 81,
R = 82,
S = 83,
T = 84,
U = 85,
V = 86,
W = 87,
X = 88,
Y = 89,
Z = 90,
LeftWindows = 91,
RightWindows = 92,
Application = 93,
Sleep = 95,
NumberPad0 = 96,
NumberPad1 = 97,
NumberPad2 = 98,
NumberPad3 = 99,
NumberPad4 = 100,
NumberPad5 = 101,
NumberPad6 = 102,
NumberPad7 = 103,
NumberPad8 = 104,
NumberPad9 = 105,
Multiply = 106,
Add = 107,
Separator = 108,
Subtract = 109,
Decimal = 110,
Divide = 111,
F1 = 112,
F2 = 113,
F3 = 114,
F4 = 115,
F5 = 116,
F6 = 117,
F7 = 118,
F8 = 119,
F9 = 120,
F10 = 121,
F11 = 122,
F12 = 123,
F13 = 124,
F14 = 125,
F15 = 126,
F16 = 127,
F17 = 128,
F18 = 129,
F19 = 130,
F20 = 131,
F21 = 132,
F22 = 133,
F23 = 134,
F24 = 135,
NavigationView = 136,
NavigationMenu = 137,
NavigationUp = 138,
NavigationDown = 139,
NavigationLeft = 140,
NavigationRight = 141,
NavigationAccept = 142,
NavigationCancel = 143,
NumberKeyLock = 144,
Scroll = 145,
LeftShift = 160,
RightShift = 161,
LeftControl = 162,
RightControl = 163,
LeftMenu = 164,
RightMenu = 165,
GoBack = 166,
GoForward = 167,
Refresh = 168,
Stop = 169,
Search = 170,
Favorites = 171,
GoHome = 172,
VolumeMute = 173,
VolumeDown = 174,
VolumeUp = 175,
MediaNext = 176,
MediaPrevious = 177,
MediaStop = 178,
MediaPlayPause = 179,
[Description("+")]
Plus = 187,
[Description(",")]
Comma = 188,
[Description("-")]
Minus = 189,
[Description(".")]
Period = 190,
[Description("#")]
NumberSign = 191,
GamepadA = 195,
GamepadB = 196,
GamepadX = 197,
GamepadY = 198,
GamepadRightShoulder = 199,
GamepadLeftShoulder = 200,
GamepadLeftTrigger = 201,
GamepadRightTrigger = 202,
GamepadDPadUp = 203,
GamepadDPadDown = 204,
GamepadDPadLeft = 205,
GamepadDPadRight = 206,
GamepadMenu = 207,
GamepadView = 208,
GamepadLeftThumbstickButton = 209,
GamepadRightThumbstickButton = 210,
GamepadLeftThumbstickUp = 211,
GamepadLeftThumbstickDown = 212,
GamepadLeftThumbstickRight = 213,
GamepadLeftThumbstickLeft = 214,
GamepadRightThumbstickUp = 215,
GamepadRightThumbstickDown = 216,
GamepadRightThumbstickRight = 217,
GamepadRightThumbstickLeft = 218,
[Description(")")]
OpenBrackets = 219,
[Description("^")]
Circumflex = 220,
[Description("´")]
AcuteAccent = 221
}
}
@@ -0,0 +1,8 @@
namespace FluentTerminal.Models.Enums
{
public enum NewTerminalLocation
{
Tab,
Window
}
}
@@ -0,0 +1,9 @@
namespace FluentTerminal.Models.Enums
{
public enum ScrollBarStyle
{
Hidden,
AutoHiding,
Visible
}
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="Windows">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.16299.0\Windows.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
</Reference>
</ItemGroup>
</Project>
+15
View File
@@ -0,0 +1,15 @@
using FluentTerminal.Models.Enums;
using Windows.System;
namespace FluentTerminal.Models
{
public class KeyBinding
{
public Command Command { get; set; }
public int Key { get; set; }
public bool Ctrl { get; set; }
public bool Alt { get; set; }
public bool Shift { get; set; }
public bool Meta { get; set; }
}
}
Loaded 100 of 410 files, more files were not shown because too many files have changed in this diff. Show more