Compare commits

...
161 Commits
Author SHA1 Message Date
FelixSe cc7e069f11 Increase version 2018-10-20 14:51:50 +02:00
FelixSe 94a0eac58f Fixed tab width, Added option to indicate new output on background tabs 2018-10-20 12:20:41 +02:00
FelixSe 9af0556d6c Update xterm.js to 3.8.0 2018-10-12 07:39:52 +02:00
A.J. Ruckman b14ca2c4ed update winpty dependencies from current winpty master code (#95) 2018-10-09 22:27:26 +02:00
FelixSe 89c1a5a614 ExtendedVirtualKey now supports all OEM keys, and uses the qwerty default for description 2018-10-09 22:20:05 +02:00
Justin Gallagher 3e32f611e8 Add automatic install script (#92) 2018-10-08 22:02:58 +02:00
Mike 07e65ce2bf Clarifying installing and dev environment setup instructions. (#83)
* Clarifying installing vs dev environment setup.

* Adding details on installing the app, as well as dev dependencies.
2018-10-05 10:55:41 +02:00
Mike f2a48487f1 Return an empty string when attempting to paste non-text clipboard buffer (#86)
* Return an empty stirng when attempting to paste non-text clipboard buffer.

Returning null Task reference was causing a crash when attempting to paste a non-text buffer.

* Simplifying returned empty string task.
2018-10-01 21:40:18 +02:00
Mike c9d0cc7368 Tab theme colours (#73) 2018-09-10 07:50:20 +02:00
Mike eecc2e8243 Development environment documentation, and npm package bump (#68)
* Updating documentation and npm packages

- The webpack 3.10 was too old, as webpack-cli and webpack-command, required dependencies for building, need 4.x.x or 4.4.0 respectively.
- `npm-check-updates` (`npm t -g npm-check-updates`) run with `ncu -u` auto-bumped xterm.js to 3.6.0, and webpack to 4.16.5. Unfortunately this also reordered the file, so those changes aren't obvious.
- I also added webpack-cli as an explicit dependency.
- Updates to the readme to detail putting together a build environment.

* Noting that the package should be for sideloading

For dev, and non-published packages, the sideloading option is best.

* Adding dev StoreAssociation XML file for dev building.

* Cleaning up the packaging instructions.

* Changed instruction from rename to copy.

- Copy will not try to clobber the working directory of the template
file.

* Bumping xterm to 3.6.0, adding notes on installing built packages
2018-08-18 20:27:53 +02:00
Mike 5c1896cd3b Added support for Ctrl+<N> to switch to tab N. (#70) 2018-08-18 10:37:51 +02:00
Jakob Hermann Preusker e0cde7fe0f Fixed Issue #63: No scrolling in some pages (#66)
* Fixed Issue #63

* Requested changes to PR #66 done

* Fixed scrollbar position in themes/profiles
2018-08-17 07:48:15 +02:00
FelixSe 2d4d0bc23a Update version 2018-08-01 22:25:38 +02:00
FelixSe 9a5bff3d37 Update screenshots 2018-08-01 21:40:15 +02:00
FelixSe 28f212b7d6 Pin language version for services dll 2018-07-31 21:29:07 +02:00
FelixSe bfad59fa7a Added option to start SystemTray on windows startup 2018-07-31 21:13:29 +02:00
FelixSe f0e8f01d30 Added option to always show tabs 2018-07-28 23:02:42 +02:00
FelixSe 3ffd94e99d Clear selection after selecting with 'select to copy' enabled 2018-07-28 22:28:55 +02:00
FelixSe 1be49d27ab Default profile is now saved in local settings 2018-07-28 22:10:41 +02:00
FelixSe 4f43f64783 Fixed a bug that preventend toggle window from hiding the terminal 2018-07-20 23:38:46 +02:00
FelixSe e7997a5272 increase version 2018-07-19 23:52:45 +02:00
FelixSe 507138364e Improved handling of missing profiles/themes 2018-07-19 23:28:57 +02:00
FelixSe 0a4a1baeba increase version 2018-07-19 21:59:19 +02:00
FelixSe 14d111c4de Added mouse settings for copy&paste 2018-07-19 21:22:47 +02:00
FelixSe db73d7e1cb Fixed orphaned terminals on window close 2018-07-19 18:54:52 +02:00
FelixSe 825e9883d7 increase version 2018-07-19 12:39:04 +02:00
Felix b2866c34df Update README.md 2018-07-19 12:08:34 +02:00
Felix 58e3ab9115 Use AppService for Commnunication instead of WebSocket (#55)
* Use AppService for Commnunication instead of WebSocket

* Code cleanup

* Some polishing

* Added some tests
2018-07-19 12:05:02 +02:00
Felix 2b66b3c87f Update README.md 2018-07-18 21:14:11 +02:00
FelixSe 4505504732 Refactored paste to be compatible with xterm 3.5 2018-07-17 21:55:10 +02:00
FelixSe 567da9b0a3 Update xterm to 3.5.1 2018-07-17 20:44:45 +02:00
FelixSe 7ccc36b121 Added option to display tabs on the bottom of the window 2018-07-08 20:47:26 +02:00
Felix 559bf9d394 Improved scrolling behavior of the tabs bar (#54) 2018-07-08 18:28:16 +02:00
FelixSe cee34dfb63 Show title of selected tab in windows task bar 2018-07-06 23:56:24 +02:00
Felix 55995bd7a9 Update README.md 2018-07-02 22:43:28 +02:00
Felix 36edfddf74 Tests for Services (#52) 2018-07-01 13:54:14 +02:00
FelixSe 188046e60a Fixed invisible text when background color was used as text color (e.g. micro editor) 2018-06-25 21:48:55 +02:00
FelixSe 5338f3cae8 fix reversed blue/red colors in ubuntu theme 2018-06-25 21:40:40 +02:00
Felix 624146ecd7 Update README.md 2018-06-25 07:46:31 +02:00
Mike 8f711d1a9e Update readme to address issues with loopback exemption (#51)
* Update README for installation instructions

Changed from requiring developer mode to demonstrate to install when sideloading the app. Including the Powershell snippet necessary to enable the loopback exemption for the package family that is FluentTerminal.

* Updated notes on developer mode

* Linking to issue 43 directly.
2018-06-24 22:45:20 +02:00
Felix b50090060b Update README.md 2018-06-24 12:59:26 +02:00
DF 7db2321a82 Add notification for new version (#46)
* Add notification for new version

* fix spelling error

* Fix review comments
2018-06-23 17:30:47 +02:00
cxfksword def3aa3683 Fix toggle window exception (#49) 2018-06-21 22:27:12 +02:00
FelixSe 77d9a42c36 Adapted KeyBoardCommandServiceTests to not use ToggleWindow command 2018-06-21 22:04:08 +02:00
cxfksword d395085064 Add support grave accent as hotkey (#48) 2018-06-21 21:29:48 +02:00
Felix 56e5d60dae Update README.md 2018-06-17 15:53:37 +02:00
FelixSe 780cbf64b1 Added AppVeyor CI 2018-06-17 15:52:31 +02:00
FelixSe 6b25d78ce6 Remove unused nuget package 2018-06-17 14:49:22 +02:00
FelixSe 53726e0e68 Moved ViewModels into a separate asembly 2018-06-17 14:34:20 +02:00
FelixSe 5e11e5d58b Move existing tests to xUnit test project 2018-06-16 23:33:11 +02:00
FelixSe 3e0417ce47 Move services to a separate assembly 2018-06-16 22:50:27 +02:00
Felix 417bc843b0 Added missing '#' in ubuntu theme 2018-06-16 10:00:54 +02:00
Felix 6d6bdcca3e Merge pull request #41 from Linard11/master
Add WSL as default profile & terminal colors used in Ubuntu Bash
2018-06-15 11:17:46 +02:00
Linard Hug b4bfe39586 Fix typo and cursor selection color 2018-06-15 10:35:16 +02:00
Linard Hug 940cb6f38b Add 'Ubuntu Bash' as default theme 2018-06-15 02:23:29 +02:00
Linard Hug 59c6a39458 Add WSL as default profile 2018-06-15 02:23:04 +02:00
FelixSe 05ce19ec8c Increase version number 2018-06-12 22:59:05 +02:00
FelixSe 7b2bd59365 Improve clear and selectAll behaviour 2018-06-12 22:39:44 +02:00
FelixSe 6a67883777 Revert updating Win2D.uwp 2018-06-12 22:22:23 +02:00
FelixSe 847e1c045a Added option to set scrollback limit, comands to select all and clear buffer, cleaned up keyBindings UI code 2018-06-11 23:11:38 +02:00
FelixSe e918c5492e Update dependencies 2018-06-11 22:04:03 +02:00
FelixSe 1aba3d4926 Fixed text margin in tabs and added option to underline the selected tab 2018-06-10 20:54:51 +02:00
FelixSe 5ad51b6bf7 Added 'Tomorrow' as default theme to showcase light themes 2018-06-10 20:27:28 +02:00
FelixSe 72cfe1168f Export themes 2018-06-10 20:17:46 +02:00
FelixSe 5a96a0387b Set application theme based on the terminal's background color 2018-06-10 15:04:41 +02:00
FelixSe 00a5b6377d Fix selection color in imported themes 2018-06-10 12:14:25 +02:00
FelixSe f610aa8a0f Import iTerm themes 2018-06-09 19:34:32 +02:00
FelixSe c32342bca8 Remember window size and position 2018-06-08 21:42:12 +02:00
FelixSe 84af36f6bc Added fullscreen mode 2018-06-07 22:18:43 +02:00
FelixSe f56465f54a Improve storage of keyBindings 2018-06-07 21:57:45 +02:00
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
494 changed files with 16525 additions and 52960 deletions

No files matched your search

+3
View File
@@ -1,5 +1,8 @@
# Project specific
FluentTerminal.App/Win32
FluentTerminal.App/Client/
FluentTerminal.Client/dist
/.sonarqube
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
@@ -0,0 +1,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,157 @@
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.Models.Enums;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class DefaultValueProviderTests
{
public static IEnumerable<object[]> Commands => System.Enum.GetValues(typeof(Command)).Cast<Command>().Select(x => new object[] { x });
[Fact]
public void GetDefaultApplicationSettings_Default_DoesNotReturnNull()
{
var defaultValueProvider = new DefaultValueProvider();
var applicationSettings = defaultValueProvider.GetDefaultApplicationSettings();
applicationSettings.Should().NotBeNull();
}
[Theory]
[MemberData(nameof(Commands))]
public void GetDefaultKeyBindings_AllCommands_ReturnsANonEmptyList(Command command)
{
var defaultValueProvider = new DefaultValueProvider();
var keyBindings = defaultValueProvider.GetDefaultKeyBindings(command);
keyBindings.Should().NotBeNullOrEmpty();
}
[Fact]
public void GetDefaultShellProfileId_Default_DoesNotReturnEmptyGuid()
{
var defaultValueProvider = new DefaultValueProvider();
var defaultShellProfileId = defaultValueProvider.GetDefaultShellProfileId();
defaultShellProfileId.Should().NotBeEmpty();
}
[Fact]
public void GetDefaultShellProfileId_Default_IdIsOneOfThePreInstalledShellProfiles()
{
var defaultValueProvider = new DefaultValueProvider();
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
var defaultShellProfileId = defaultValueProvider.GetDefaultShellProfileId();
shellProfiles.Select(x => x.Id).Should().Contain(defaultShellProfileId);
}
[Fact]
public void GetDefaultTerminalOptions_Default_DoesNotReturnNull()
{
var defaultValueProvider = new DefaultValueProvider();
var defaultTerminalOptions = defaultValueProvider.GetDefaultTerminalOptions();
defaultTerminalOptions.Should().NotBeNull();
}
[Fact]
public void GetDefaultThemeId_Default_DoesNotReturnEmptyGuid()
{
var defaultValueProvider = new DefaultValueProvider();
var defaultThemeId = defaultValueProvider.GetDefaultThemeId();
defaultThemeId.Should().NotBeEmpty();
}
[Fact]
public void GetDefaultThemeId_Default_IdIsOneOfThePreInstalledThemes()
{
var defaultValueProvider = new DefaultValueProvider();
var themes = defaultValueProvider.GetPreInstalledThemes();
var defaultThemeId = defaultValueProvider.GetDefaultThemeId();
themes.Select(x => x.Id).Should().Contain(defaultThemeId);
}
[Fact]
public void GetPreinstalledShellProfiles_Default_ReturnsANonEmptyList()
{
var defaultValueProvider = new DefaultValueProvider();
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
shellProfiles.Should().NotBeNullOrEmpty();
}
[Fact]
public void GetPreinstalledShellProfiles_Default_HaveUniqueIds()
{
var defaultValueProvider = new DefaultValueProvider();
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
shellProfiles.Select(x => x.Id).Should().OnlyHaveUniqueItems();
}
[Fact]
public void GetPreinstalledShellProfiles_Default_HavePreInstalledSetToTrue()
{
var defaultValueProvider = new DefaultValueProvider();
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
shellProfiles.Select(x => x.PreInstalled).Should().AllBeEquivalentTo(true);
}
[Fact]
public void GetPreinstalledShellProfiles_Default_HaveNonEmptyLocations()
{
var defaultValueProvider = new DefaultValueProvider();
var shellProfiles = defaultValueProvider.GetPreinstalledShellProfiles();
shellProfiles.Select(x => x.Location).Should().NotBeNullOrEmpty();
}
[Fact]
public void GetPreInstalledThemes_Default_ReturnsANonEmptyList()
{
var defaultValueProvider = new DefaultValueProvider();
var themes = defaultValueProvider.GetPreInstalledThemes();
themes.Should().NotBeNullOrEmpty();
}
[Fact]
public void GetPreinstalledThemes_Default_HaveUniqueIds()
{
var defaultValueProvider = new DefaultValueProvider();
var themes = defaultValueProvider.GetPreInstalledThemes();
themes.Select(x => x.Id).Should().OnlyHaveUniqueItems();
}
[Fact]
public void GetPreinstalledThemes_Default_HavePreInstalledSetToTrue()
{
var defaultValueProvider = new DefaultValueProvider();
var themes = defaultValueProvider.GetPreInstalledThemes();
themes.Select(x => x.PreInstalled).Should().AllBeEquivalentTo(true);
}
}
}
@@ -0,0 +1,96 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Dialogs;
using FluentTerminal.App.Services.Implementation;
using Moq;
using System;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class DialogServiceTests
{
private readonly Fixture _fixture;
public DialogServiceTests()
{
_fixture = new Fixture();
}
[Fact]
public void ShowCreateKeyBindingDialog_Default_UsesCreateKeyBindingDialog()
{
var createKeyBindingDialog = new Mock<ICreateKeyBindingDialog>();
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, () => createKeyBindingDialog.Object);
dialogService.ShowCreateKeyBindingDialog();
createKeyBindingDialog.Verify(x => x.CreateKeyBinding(), Times.Once);
}
[Fact]
public void ShowMessageDialogAsnyc_TitleIsEmpty_ThrowsArgumentNullException()
{
var title = string.Empty;
var content = _fixture.Create<string>();
var buttons = _fixture.CreateMany<DialogButton>(2);
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>);
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("title");
}
[Fact]
public void ShowMessageDialogAsnyc_ContentIsEmpty_ThrowsArgumentNullException()
{
var title = _fixture.Create<string>();
var content = string.Empty;
var buttons = _fixture.CreateMany<DialogButton>(2);
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>);
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
showMessageDialogAsnyc.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("content");
}
[Fact]
public void ShowMessageDialogAsnyc_NoButtonsPassed_ThrowsArgumentException()
{
var title = _fixture.Create<string>();
var content = _fixture.Create<string>();
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>);
Func<Task<DialogButton>> showMessageDialogAsnyc = () => dialogService.ShowMessageDialogAsnyc(title, content);
showMessageDialogAsnyc.Should().Throw<ArgumentException>().And.ParamName.Should().Be("buttons");
}
[Fact]
public void ShowMessageDialogAsnyc_Default_UsesMessageDialog()
{
var title = _fixture.Create<string>();
var content = _fixture.Create<string>();
var buttons = _fixture.CreateMany<DialogButton>(2);
var messageDialog = new Mock<IMessageDialog>();
var dialogService = new DialogService(Mock.Of<IShellProfileSelectionDialog>, () => messageDialog.Object, Mock.Of<ICreateKeyBindingDialog>);
dialogService.ShowMessageDialogAsnyc(title, content, buttons.ElementAt(0), buttons.ElementAt(1));
messageDialog.Verify(x => x.ShowAsync(), Times.Once);
}
[Fact]
public void ShowProfileSelectionDialogAsync_Default_UsesShellProfileSelectionDialog()
{
var shellProfileSelectionDialog = new Mock<IShellProfileSelectionDialog>();
var dialogService = new DialogService(() => shellProfileSelectionDialog.Object, Mock.Of<IMessageDialog>, Mock.Of<ICreateKeyBindingDialog>);
dialogService.ShowProfileSelectionDialogAsync();
shellProfileSelectionDialog.Verify(x => x.SelectProfile(), Times.Once);
}
}
}
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="TestData\AdventureTime.itermcolors" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.4.0" />
<PackageReference Include="FluentAssertions" Version="5.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="Moq" Version="4.8.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TestData\AdventureTime.itermcolors">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,80 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.Models;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class FluentTerminalThemeParserTests
{
private readonly Fixture _fixture;
public FluentTerminalThemeParserTests()
{
_fixture = new Fixture();
}
[Fact]
public async Task Parse_ValidFileStream_ReturnsTheme()
{
var theme = _fixture.Create<TerminalTheme>();
var serialized = JsonConvert.SerializeObject(theme);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
var parser = new FluentTerminalThemeParser();
var parsedTheme = await parser.Parse(theme.Name, stream);
parsedTheme.Name.Should().Be(theme.Name);
parsedTheme.Author.Should().Be(theme.Author);
parsedTheme.PreInstalled.Should().Be(false);
parsedTheme.Colors.Should().BeEquivalentTo(theme.Colors);
}
[Fact]
public void Parse_InvalidFileStream_ThrowsException()
{
var fileName = _fixture.Create<string>();
var serialized = _fixture.Create<string>();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
var parser = new FluentTerminalThemeParser();
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
parse.Should().Throw<Exception>();
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Parse_InvalidFileName_ThrowsArgumentNullException(string fileName)
{
var theme = _fixture.Create<TerminalTheme>();
var serialized = JsonConvert.SerializeObject(theme);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
var parser = new FluentTerminalThemeParser();
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileName");
}
[Fact]
public void Parse_FileStreamIsNull_ThrowsArgumentNullException()
{
var fileName = _fixture.Create<string>();
Stream stream = null;
var parser = new FluentTerminalThemeParser();
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileContent");
}
}
}
@@ -0,0 +1,91 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.Models;
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class ITermThemeParserTests
{
private readonly Fixture _fixture;
public ITermThemeParserTests()
{
_fixture = new Fixture();
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Parse_InvalidFileName_ThrowsArgumentNullException(string fileName)
{
var stream = typeof(ITermThemeParserTests).Assembly.GetManifestResourceStream("FluentTerminal.App.Services.Test.TestData.AdventureTime.itermcolors");
var parser = new ITermThemeParser();
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileName");
}
[Fact]
public void Parse_FileStreamIsNull_ThrowsArgumentNullException()
{
var fileName = _fixture.Create<string>();
Stream stream = null;
var parser = new ITermThemeParser();
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
parse.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("fileContent");
}
[Fact]
public void Parse_InvalidFileStream_ThrowsException()
{
var fileName = _fixture.Create<string>();
var serialized = _fixture.Create<string>();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
var parser = new ITermThemeParser();
Func<Task<TerminalTheme>> parse = () => parser.Parse(fileName, stream);
parse.Should().Throw<Exception>();
}
[Fact]
public async Task Parse_ValidThemeFile_ParsesCorrectly()
{
var fileName = _fixture.Create<string>();
var stream = typeof(ITermThemeParserTests).Assembly.GetManifestResourceStream("FluentTerminal.App.Services.Test.TestData.AdventureTime.itermcolors");
var parser = new ITermThemeParser();
var theme = await parser.Parse(fileName, stream);
theme.Colors.Background.ToLower().Should().Be("#1f1d45");
theme.Colors.Cursor.ToLower().Should().Be("#efbf38");
theme.Colors.Foreground.ToLower().Should().Be("#f8dcc0");
theme.Colors.Black.ToLower().Should().Be("#050404");
theme.Colors.Red.ToLower().Should().Be("#bd0013");
theme.Colors.Green.ToLower().Should().Be("#4ab118");
theme.Colors.Yellow.ToLower().Should().Be("#e7741e");
theme.Colors.Blue.ToLower().Should().Be("#0f4ac6");
theme.Colors.Magenta.ToLower().Should().Be("#665993");
theme.Colors.Cyan.ToLower().Should().Be("#70a598");
theme.Colors.White.ToLower().Should().Be("#f8dcc0");
theme.Colors.BrightBlack.ToLower().Should().Be("#4e7cbf");
theme.Colors.BrightRed.ToLower().Should().Be("#fc5f5a");
theme.Colors.BrightGreen.ToLower().Should().Be("#9eff6e");
theme.Colors.BrightYellow.ToLower().Should().Be("#efc11a");
theme.Colors.BrightBlue.ToLower().Should().Be("#1997c6");
theme.Colors.BrightMagenta.ToLower().Should().Be("#9b5953");
theme.Colors.BrightCyan.ToLower().Should().Be("#c8faf4");
theme.Colors.BrightWhite.ToLower().Should().Be("#f6f5fb");
}
}
}
@@ -0,0 +1,69 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class KeyboardCommandServiceTests
{
private readonly Fixture _fixture;
public KeyboardCommandServiceTests()
{
_fixture = new Fixture();
}
[Fact]
public void RegisterCommandHandler_HandlerIsNull_ThrowsArgumentNullException()
{
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
Action handler = null;
var keyboardCommandService = new KeyboardCommandService();
Action invoke = () => keyboardCommandService.RegisterCommandHandler(command, handler);
invoke.Should().Throw<ArgumentNullException>().And.ParamName.Should().Be("handler");
}
[Fact]
public void RegisterCommandHandler_CommandAlreadyRegisted_ThrowsInvalidOperationException()
{
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
var handler = _fixture.Create<Action>();
var keyboardCommandService = new KeyboardCommandService();
keyboardCommandService.RegisterCommandHandler(command, handler);
Action invoke = () => keyboardCommandService.RegisterCommandHandler(command, handler);
invoke.Should().Throw<InvalidOperationException>();
}
[Fact]
public void SendCommand_CommandIsRegistered_HandlerGetsInvoked()
{
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
var handlerCalled = false;
var keyboardCommandService = new KeyboardCommandService();
keyboardCommandService.RegisterCommandHandler(command, () => handlerCalled = true);
keyboardCommandService.SendCommand(command);
handlerCalled.Should().BeTrue();
}
[Fact]
public void SendCommand_CommandIsNotRegisted_KeyNotFoundExceptionIsThrown()
{
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
var keyboardCommandService = new KeyboardCommandService();
Action invoke = () => keyboardCommandService.SendCommand(command);
invoke.Should().Throw<KeyNotFoundException>();
}
}
}
@@ -0,0 +1,761 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using Moq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class SettingsServiceTests
{
private readonly Fixture _fixture;
public SettingsServiceTests()
{
_fixture = new Fixture();
}
[Fact]
public void Constructor_Default_WritesAllPreinstalledThemesInThemeContainer()
{
var themes = _fixture.CreateMany<TerminalTheme>(3);
var defaultValueProvider = new Mock<IDefaultValueProvider>();
defaultValueProvider.Setup(x => x.GetPreInstalledThemes()).Returns(themes);
var themesContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
Themes = themesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
new SettingsService(defaultValueProvider.Object, applicationDataContainers);
defaultValueProvider.Verify(x => x.GetPreInstalledThemes(), Times.Once);
foreach (var theme in themes)
{
themesContainer.Verify(x => x.WriteValueAsJson(theme.Id.ToString(), theme), Times.Once);
}
}
[Fact]
public void Constructor_Default_WritesAllPreinstalledShellProfilesInShellProfilesContainer()
{
var shellProfiles = _fixture.CreateMany<ShellProfile>(3);
var defaultValueProvider = new Mock<IDefaultValueProvider>();
defaultValueProvider.Setup(x => x.GetPreinstalledShellProfiles()).Returns(shellProfiles);
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
ShellProfiles = shellProfilesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
new SettingsService(defaultValueProvider.Object, applicationDataContainers);
defaultValueProvider.Verify(x => x.GetPreinstalledShellProfiles(), Times.Once);
foreach (var shellProfile in shellProfiles)
{
shellProfilesContainer.Verify(x => x.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile), Times.Once);
}
}
[Fact]
public void GetCurrentThemeId_ValueExistsInRoamingSettings_ReturnsCurrentThemeId()
{
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var currentThemeId = (object)_fixture.Create<Guid>();
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(true);
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetCurrentThemeId();
result.Should().Be((Guid)currentThemeId);
roamingSettings.Verify(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId), Times.Once);
}
[Fact]
public void GetCurrentThemeId_ValueDoesNotExistInRoamingSettings_ReturnsDefaultThemeIdFromDefaultValueProvider()
{
var defaultValueProvider = new Mock<IDefaultValueProvider>();
var defaultThemeId = _fixture.Create<Guid>();
defaultValueProvider.Setup(x => x.GetDefaultThemeId()).Returns(defaultThemeId);
var roamingSettings = new Mock<IApplicationDataContainer>();
var currentThemeId = (object)_fixture.Create<Guid>();
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(false);
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
var result = settingsService.GetCurrentThemeId();
result.Should().Be(defaultThemeId);
defaultValueProvider.Verify(x => x.GetDefaultThemeId(), Times.Once);
roamingSettings.Verify(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId), Times.Once);
}
[Fact]
public void SaveCurrentThemeId_Default_SetsValueOnRoamingSettings()
{
var currentThemeId = _fixture.Create<Guid>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.SaveCurrentThemeId(currentThemeId);
roamingSettings.Verify(x => x.SetValue(SettingsService.CurrentThemeKey, currentThemeId), Times.Once);
}
[Fact]
public void SaveCurrentThemeId_Default_InvokesCurrentThemeChangedEvent()
{
var currentThemeId = _fixture.Create<Guid>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var currentThemeChangedEventInvoked = false;
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.CurrentThemeChanged += (s, e) => currentThemeChangedEventInvoked = true;
settingsService.SaveCurrentThemeId(currentThemeId);
currentThemeChangedEventInvoked.Should().BeTrue();
}
[Fact]
public void SaveTheme_Default_WritesToThemesContainer()
{
var theme = _fixture.Create<TerminalTheme>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var themesContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
Themes = themesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.SaveTheme(theme);
themesContainer.Verify(x => x.WriteValueAsJson(theme.Id.ToString(), theme), Times.Once);
}
[Fact]
public void SaveTheme_ThemeIsCurrentTheme_InvokesCurrentThemeChangedEvent()
{
var theme = _fixture.Create<TerminalTheme>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var themesContainer = new Mock<IApplicationDataContainer>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var currentThemeId = (object)theme.Id;
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(true);
var currentThemeChangedEventInvoked = false;
var applicationDataContainers = new ApplicationDataContainers
{
Themes = themesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = roamingSettings.Object,
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.CurrentThemeChanged += (s, e) => currentThemeChangedEventInvoked = true;
settingsService.SaveTheme(theme);
currentThemeChangedEventInvoked.Should().BeTrue();
}
[Fact]
public void DeleteTheme_Default_CallsDeleteOnThemesContainer()
{
var themeId = _fixture.Create<Guid>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var themesContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
Themes = themesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.DeleteTheme(themeId);
themesContainer.Verify(x => x.Delete(themeId.ToString()), Times.Once);
}
[Fact]
public void GetThemes_Default_CallsGetAllOnThemesContainer()
{
var themes = _fixture.CreateMany<TerminalTheme>(3);
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var themesContainer = new Mock<IApplicationDataContainer>();
themesContainer.Setup(x => x.GetAll()).Returns(themes.Select(JsonConvert.SerializeObject));
var applicationDataContainers = new ApplicationDataContainers
{
Themes = themesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetThemes();
result.Should().BeEquivalentTo(themes);
themesContainer.Verify(x => x.GetAll(), Times.Once);
}
[Fact]
public void GetTheme_ValidThemeId_ReturnsThemeFromThemesContainer()
{
var theme = _fixture.Create<TerminalTheme>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var themesContainer = new Mock<IApplicationDataContainer>();
themesContainer.Setup(x => x.ReadValueFromJson(theme.Id.ToString(), default(TerminalTheme))).Returns(theme);
var applicationDataContainers = new ApplicationDataContainers
{
Themes = themesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetTheme(theme.Id);
result.Should().BeEquivalentTo(theme);
themesContainer.Verify(x => x.ReadValueFromJson(theme.Id.ToString(), default(TerminalTheme)), Times.Once);
}
[Fact]
public void GetTheme_UnknownThemeId_ReturnsNull()
{
var themeId = _fixture.Create<Guid>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var themesContainer = new Mock<IApplicationDataContainer>();
themesContainer.Setup(x => x.ReadValueFromJson(themeId.ToString(), default(TerminalTheme))).Returns(value: null);
var applicationDataContainers = new ApplicationDataContainers
{
Themes = themesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetTheme(themeId);
result.Should().BeNull();
themesContainer.Verify(x => x.ReadValueFromJson(themeId.ToString(), default(TerminalTheme)), Times.Once);
}
[Fact]
public void GetTerminalOptions_Default_ReturnsTerminalOptionsFromRoamingSettings()
{
var terminalOptions = _fixture.Create<TerminalOptions>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
roamingSettings.Setup(x => x.ReadValueFromJson(nameof(TerminalOptions), It.IsAny<TerminalOptions>())).Returns(terminalOptions);
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetTerminalOptions();
result.Should().BeEquivalentTo(terminalOptions);
roamingSettings.Verify(x => x.ReadValueFromJson(nameof(TerminalOptions), It.IsAny<TerminalOptions>()), Times.Once);
}
[Fact]
public void SaveTerminalOptions_Default_WritesToRoamingSettings()
{
var terminalOptions = _fixture.Create<TerminalOptions>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.SaveTerminalOptions(terminalOptions);
roamingSettings.Verify(x => x.WriteValueAsJson(nameof(TerminalOptions), terminalOptions), Times.Once);
}
[Fact]
public void SaveTerminalOptions_Default_InvokesTerminalOptionsChangedEvent()
{
var terminalOptions = _fixture.Create<TerminalOptions>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var terminalOptionsChangedEventInvoked = false;
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.TerminalOptionsChanged += (s, e) => terminalOptionsChangedEventInvoked = true;
settingsService.SaveTerminalOptions(terminalOptions);
terminalOptionsChangedEventInvoked.Should().BeTrue();
}
[Fact]
public void GetApplicationSettings_Default_ReturnsApplicationSettingsFromRoamingSettings()
{
var applicationSettings = _fixture.Create<ApplicationSettings>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
roamingSettings.Setup(x => x.ReadValueFromJson(nameof(ApplicationSettings), It.IsAny<ApplicationSettings>())).Returns(applicationSettings);
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetApplicationSettings();
result.Should().BeEquivalentTo(applicationSettings);
roamingSettings.Verify(x => x.ReadValueFromJson(nameof(ApplicationSettings), It.IsAny<ApplicationSettings>()), Times.Once);
}
[Fact]
public void SaveApplicationSettings_Default_WritesToRoamingSettings()
{
var applicationSettings = _fixture.Create<ApplicationSettings>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.SaveApplicationSettings(applicationSettings);
roamingSettings.Verify(x => x.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings), Times.Once);
}
[Fact]
public void SaveApplicationSettings_Default_InvokesApplicationSettingsChangedEvent()
{
var applicationSettings = _fixture.Create<ApplicationSettings>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var applicationSettingsChangedEventInvoked = false;
var applicationDataContainers = new ApplicationDataContainers
{
RoamingSettings = roamingSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.ApplicationSettingsChanged += (s, e) => applicationSettingsChangedEventInvoked = true;
settingsService.SaveApplicationSettings(applicationSettings);
applicationSettingsChangedEventInvoked.Should().BeTrue();
}
[Fact]
public void GetKeyBindings_KeyBindingsInKeyBindingsContainer_ReturnsKeyBindingsFromKeyBindingsContainer()
{
var keyBindings = _fixture.CreateMany<KeyBinding>(5);
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
foreach (var keyBinding in keyBindings)
{
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(keyBinding.Command.ToString(), null)).Returns(new Collection<KeyBinding> { keyBinding });
}
var applicationDataContainers = new ApplicationDataContainers
{
KeyBindings = keyBindingsContainer.Object,
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetKeyBindings();
foreach (var keyBinding in keyBindings)
{
keyBindingsContainer.Verify(x => x.ReadValueFromJson<Collection<KeyBinding>>(keyBinding.Command.ToString(), null), Times.Once);
}
}
[Fact]
public void GetKeyBindings_KeyBindingsNotInKeyBindingsContainer_ReturnsKeyBindingsFromDefaultValueProvider()
{
var keyBindings = _fixture.CreateMany<KeyBinding>(5);
var defaultValueProvider = new Mock<IDefaultValueProvider>();
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
foreach (var keyBinding in keyBindings)
{
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(keyBinding.Command.ToString(), null)).Returns(new Collection<KeyBinding> { keyBinding });
}
var applicationDataContainers = new ApplicationDataContainers
{
KeyBindings = keyBindingsContainer.Object,
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
var result = settingsService.GetKeyBindings();
foreach (Command command in Enum.GetValues(typeof(Command)))
{
if (keyBindings.Select(x => x.Command).Contains(command))
{
continue;
}
defaultValueProvider.Verify(x => x.GetDefaultKeyBindings(command), Times.Once);
}
}
[Fact]
public void SaveKeyBindings_Default_WritesToKeyBindingsContainer()
{
var command = _fixture.Create<Command>();
var keyBindings = _fixture.Create<ICollection<KeyBinding>>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
KeyBindings = keyBindingsContainer.Object,
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.SaveKeyBindings(command, keyBindings);
keyBindingsContainer.Verify(x => x.WriteValueAsJson(command.ToString(), keyBindings), Times.Once);
}
[Fact]
public void SaveKeyBindings_Default_InvokesKeyBindingsChangedEvent()
{
var command = _fixture.Create<Command>();
var keyBindings = _fixture.Create<Collection<KeyBinding>>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
var keyBindingsChangedEventInvoked = false;
var applicationDataContainers = new ApplicationDataContainers
{
KeyBindings = keyBindingsContainer.Object,
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true;
settingsService.SaveKeyBindings(command, keyBindings);
keyBindingsChangedEventInvoked.Should().BeTrue();
}
[Fact]
public void ResetKeyBindings_Default_WritesToKeyBindingsContainerFromDefaultValueProvider()
{
var defaultValueProvider = new Mock<IDefaultValueProvider>();
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
KeyBindings = keyBindingsContainer.Object,
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
settingsService.ResetKeyBindings();
foreach (Command command in Enum.GetValues(typeof(Command)))
{
keyBindingsContainer.Verify(x => x.WriteValueAsJson(command.ToString(), It.IsAny<ICollection<KeyBinding>>()), Times.Once);
defaultValueProvider.Verify(x => x.GetDefaultKeyBindings(command), Times.Once);
}
}
[Fact]
public void ResetKeyBindings_Default_InvokesKeyBindingsChangedEvent()
{
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
var keyBindingsChangedEventInvoked = false;
var applicationDataContainers = new ApplicationDataContainers
{
KeyBindings = keyBindingsContainer.Object,
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true;
settingsService.ResetKeyBindings();
keyBindingsChangedEventInvoked.Should().BeTrue();
}
[Fact]
public void GetDefaultShellProfileId_ValueExistsInRoamingSettings_ReturnsDefaultShellProfileId()
{
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var localSettings = new Mock<IApplicationDataContainer>();
var defaultShellProfileId = (object)_fixture.Create<Guid>();
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId)).Returns(true);
var applicationDataContainers = new ApplicationDataContainers
{
LocalSettings = localSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetDefaultShellProfileId();
result.Should().Be((Guid)defaultShellProfileId);
localSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out defaultShellProfileId), Times.Once);
}
[Fact]
public void GetDefaultShellProfileId_ValueDoesNotExistInRoamingSettings_ReturnsDefaultShellProfileIdFromDefaultValueProvider()
{
var defaultValueProvider = new Mock<IDefaultValueProvider>();
var defaultShellProfileId = _fixture.Create<Guid>();
defaultValueProvider.Setup(x => x.GetDefaultShellProfileId()).Returns(defaultShellProfileId);
var localSettings = new Mock<IApplicationDataContainer>();
var currentDefaultShellProfileId = (object)_fixture.Create<Guid>();
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId)).Returns(false);
var applicationDataContainers = new ApplicationDataContainers
{
LocalSettings = localSettings.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
var result = settingsService.GetDefaultShellProfileId();
result.Should().Be(defaultShellProfileId);
defaultValueProvider.Verify(x => x.GetDefaultShellProfileId(), Times.Once);
localSettings.Verify(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentDefaultShellProfileId), Times.Once);
}
[Fact]
public void GetShellProfiles_Default_CallsGetAllOnShellProfilesContainer()
{
var shellProfiles = _fixture.CreateMany<ShellProfile>(3);
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
shellProfilesContainer.Setup(x => x.GetAll()).Returns(shellProfiles.Select(JsonConvert.SerializeObject));
var applicationDataContainers = new ApplicationDataContainers
{
ShellProfiles = shellProfilesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
var result = settingsService.GetShellProfiles();
result.Should().BeEquivalentTo(shellProfiles);
shellProfilesContainer.Verify(x => x.GetAll(), Times.Once);
}
[Fact]
public void SaveShellProfile_Default_WritesToShellProfilesContainer()
{
var shellProfile = _fixture.Create<ShellProfile>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
ShellProfiles = shellProfilesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.SaveShellProfile(shellProfile);
shellProfilesContainer.Verify(x => x.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile), Times.Once);
}
[Fact]
public void DeleteShellProfile_Default_CallsDeleteOnShellProfilesContainer()
{
var shellProfileId = _fixture.Create<Guid>();
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
ShellProfiles = shellProfilesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
Themes = Mock.Of<IApplicationDataContainer>()
};
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
settingsService.DeleteShellProfile(shellProfileId);
shellProfilesContainer.Verify(x => x.Delete(shellProfileId.ToString()), Times.Once);
}
[Fact]
public void GetDefaultShellProfile_ProfileNotFound_DefaultShellProfileIdGetsResetToDefault()
{
var currentShellProfileId = (object)_fixture.Create<Guid>();
var defaultShellProfile = _fixture.Create<ShellProfile>();
var defaultValueProvider = new Mock<IDefaultValueProvider>();
defaultValueProvider.Setup(x => x.GetDefaultShellProfileId()).Returns(defaultShellProfile.Id);
var shellProfilesContainer = new Mock<IApplicationDataContainer>();
var localSettings = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
ShellProfiles = shellProfilesContainer.Object,
KeyBindings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = localSettings.Object,
Themes = Mock.Of<IApplicationDataContainer>()
};
shellProfilesContainer.Setup(x => x.ReadValueFromJson(currentShellProfileId.ToString(), default(ShellProfile))).Returns(value: null);
shellProfilesContainer.Setup(x => x.ReadValueFromJson(defaultShellProfile.Id.ToString(), default(ShellProfile))).Returns(defaultShellProfile);
localSettings.Setup(x => x.TryGetValue(SettingsService.DefaultShellProfileKey, out currentShellProfileId)).Returns(true);
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
var result = settingsService.GetDefaultShellProfile();
result.Should().Be(defaultShellProfile);
localSettings.Verify(x => x.SetValue(SettingsService.DefaultShellProfileKey, defaultShellProfile.Id), Times.Once);
}
[Fact]
public void GetCurrentTheme_ThemeNotFound_CurrentThemeIdGetsResetToDefault()
{
var currentThemeId = (object)_fixture.Create<Guid>();
var defaultTheme = _fixture.Create<TerminalTheme>();
var defaultValueProvider = new Mock<IDefaultValueProvider>();
defaultValueProvider.Setup(x => x.GetDefaultThemeId()).Returns(defaultTheme.Id);
var themesContainer = new Mock<IApplicationDataContainer>();
var roamingSettings = new Mock<IApplicationDataContainer>();
var applicationDataContainers = new ApplicationDataContainers
{
ShellProfiles = Mock.Of<IApplicationDataContainer>(),
KeyBindings = Mock.Of<IApplicationDataContainer>(),
LocalSettings = Mock.Of<IApplicationDataContainer>(),
RoamingSettings = roamingSettings.Object,
Themes = themesContainer.Object
};
themesContainer.Setup(x => x.ReadValueFromJson(currentThemeId.ToString(), default(TerminalTheme))).Returns(value: null);
themesContainer.Setup(x => x.ReadValueFromJson(defaultTheme.Id.ToString(), default(TerminalTheme))).Returns(defaultTheme);
roamingSettings.Setup(x => x.TryGetValue(SettingsService.CurrentThemeKey, out currentThemeId)).Returns(true);
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
var result = settingsService.GetCurrentTheme();
result.Should().Be(defaultTheme);
roamingSettings.Verify(x => x.SetValue(SettingsService.CurrentThemeKey, defaultTheme.Id), Times.Once);
}
}
}
@@ -0,0 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Ansi 0 Color</key>
<dict>
<key>Blue Component</key>
<real>0.016337040811777115</real>
<key>Green Component</key>
<real>0.016232240945100784</real>
<key>Red Component</key>
<real>0.020433688536286354</real>
</dict>
<key>Ansi 1 Color</key>
<dict>
<key>Blue Component</key>
<real>0.075083047151565552</real>
<key>Green Component</key>
<real>0.0</real>
<key>Red Component</key>
<real>0.73976802825927734</real>
</dict>
<key>Ansi 10 Color</key>
<dict>
<key>Blue Component</key>
<real>0.43171143531799316</real>
<key>Green Component</key>
<real>1</real>
<key>Red Component</key>
<real>0.61819148063659668</real>
</dict>
<key>Ansi 11 Color</key>
<dict>
<key>Blue Component</key>
<real>0.10242157429456711</real>
<key>Green Component</key>
<real>0.7578997015953064</real>
<key>Red Component</key>
<real>0.93761706352233887</real>
</dict>
<key>Ansi 12 Color</key>
<dict>
<key>Blue Component</key>
<real>0.77801346778869629</real>
<key>Green Component</key>
<real>0.59102588891983032</real>
<key>Red Component</key>
<real>0.096501544117927551</real>
</dict>
<key>Ansi 13 Color</key>
<dict>
<key>Blue Component</key>
<real>0.32394066452980042</real>
<key>Green Component</key>
<real>0.34950375556945801</real>
<key>Red Component</key>
<real>0.60686224699020386</real>
</dict>
<key>Ansi 14 Color</key>
<dict>
<key>Blue Component</key>
<real>0.95550096035003662</real>
<key>Green Component</key>
<real>0.97915208339691162</real>
<key>Red Component</key>
<real>0.78500419855117798</real>
</dict>
<key>Ansi 15 Color</key>
<dict>
<key>Blue Component</key>
<real>0.98438382148742676</real>
<key>Green Component</key>
<real>0.96043074131011963</real>
<key>Red Component</key>
<real>0.96449893712997437</real>
</dict>
<key>Ansi 2 Color</key>
<dict>
<key>Blue Component</key>
<real>0.092498354613780975</real>
<key>Green Component</key>
<real>0.6950421929359436</real>
<key>Red Component</key>
<real>0.28945007920265198</real>
</dict>
<key>Ansi 3 Color</key>
<dict>
<key>Blue Component</key>
<real>0.1164485365152359</real>
<key>Green Component</key>
<real>0.45553147792816162</real>
<key>Red Component</key>
<real>0.90543299913406372</real>
</dict>
<key>Ansi 4 Color</key>
<dict>
<key>Blue Component</key>
<real>0.77829605340957642</real>
<key>Green Component</key>
<real>0.28880593180656433</real>
<key>Red Component</key>
<real>0.059818677604198456</real>
</dict>
<key>Ansi 5 Color</key>
<dict>
<key>Blue Component</key>
<real>0.57602518796920776</real>
<key>Green Component</key>
<real>0.35004982352256775</real>
<key>Red Component</key>
<real>0.40191537141799927</real>
</dict>
<key>Ansi 6 Color</key>
<dict>
<key>Blue Component</key>
<real>0.59465551376342773</real>
<key>Green Component</key>
<real>0.64633101224899292</real>
<key>Red Component</key>
<real>0.43781715631484985</real>
</dict>
<key>Ansi 7 Color</key>
<dict>
<key>Blue Component</key>
<real>0.75428962707519531</real>
<key>Green Component</key>
<real>0.86175870895385742</real>
<key>Red Component</key>
<real>0.97308963537216187</real>
</dict>
<key>Ansi 8 Color</key>
<dict>
<key>Blue Component</key>
<real>0.7491685152053833</real>
<key>Green Component</key>
<real>0.48549851775169373</real>
<key>Red Component</key>
<real>0.30726605653762817</real>
</dict>
<key>Ansi 9 Color</key>
<dict>
<key>Blue Component</key>
<real>0.35247993469238281</real>
<key>Green Component</key>
<real>0.37095117568969727</real>
<key>Red Component</key>
<real>0.98876720666885376</real>
</dict>
<key>Background Color</key>
<dict>
<key>Blue Component</key>
<real>0.27054747939109802</real>
<key>Green Component</key>
<real>0.11274909973144531</real>
<key>Red Component</key>
<real>0.12003477662801743</real>
</dict>
<key>Bold Color</key>
<dict>
<key>Blue Component</key>
<real>0.075083047151565552</real>
<key>Green Component</key>
<real>0.0</real>
<key>Red Component</key>
<real>0.73976802825927734</real>
</dict>
<key>Cursor Color</key>
<dict>
<key>Blue Component</key>
<real>0.21945112943649292</real>
<key>Green Component</key>
<real>0.74987590312957764</real>
<key>Red Component</key>
<real>0.93704277276992798</real>
</dict>
<key>Cursor Text Color</key>
<dict>
<key>Blue Component</key>
<real>0.037519864737987518</real>
<key>Green Component</key>
<real>0.032785907387733459</real>
<key>Red Component</key>
<real>0.02988031692802906</real>
</dict>
<key>Foreground Color</key>
<dict>
<key>Blue Component</key>
<real>0.75428962707519531</real>
<key>Green Component</key>
<real>0.86175870895385742</real>
<key>Red Component</key>
<real>0.97308963537216187</real>
</dict>
<key>Selected Text Color</key>
<dict>
<key>Blue Component</key>
<real>0.767447829246521</real>
<key>Green Component</key>
<real>0.85237473249435425</real>
<key>Red Component</key>
<real>0.95287787914276123</real>
</dict>
<key>Selection Color</key>
<dict>
<key>Blue Component</key>
<real>0.30699285864830017</real>
<key>Green Component</key>
<real>0.41871801018714905</real>
<key>Red Component</key>
<real>0.43789717555046082</real>
</dict>
</dict>
</plist>
@@ -0,0 +1,45 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using Moq;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class ThemeParserFactoryTests
{
private readonly Fixture _fixture;
public ThemeParserFactoryTests()
{
_fixture = new Fixture();
}
[Fact]
public void GetParser_SupportedFileType_ReturnsParser()
{
var supportedFileType = _fixture.Create<string>();
var parser = new Mock<IThemeParser>();
parser.Setup(x => x.SupportedFileTypes).Returns(new string[] { supportedFileType });
var themeParserFactory = new ThemeParserFactory(new IThemeParser[] { parser.Object });
var result = themeParserFactory.GetParser(supportedFileType);
parser.Should().Be(parser);
}
[Fact]
public void GetParser_UnsupportedFileType_ReturnsNull()
{
var unSupportedFileType = _fixture.Create<string>();
var supportedFileType = _fixture.Create<string>();
var parser = new Mock<IThemeParser>();
parser.Setup(x => x.SupportedFileTypes).Returns(new string[] { supportedFileType });
var themeParserFactory = new ThemeParserFactory(new IThemeParser[] { parser.Object });
var result = themeParserFactory.GetParser(unSupportedFileType);
result.Should().BeNull();
}
}
}
@@ -0,0 +1,183 @@
using AutoFixture;
using FluentAssertions;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using FluentTerminal.Models.Requests;
using FluentTerminal.Models.Responses;
using Moq;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace FluentTerminal.App.Services.Test
{
public class TrayProcessCommunicationServiceTests
{
private readonly Fixture _fixture;
public TrayProcessCommunicationServiceTests()
{
_fixture = new Fixture();
}
[Fact]
public async Task CreateTerminal_Default_ReturnsResponseFromAppServiceConnection()
{
var settingsService = new Mock<ISettingsService>();
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
{
[Command.ToggleWindow] = keyBindings.ToList()
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var response = _fixture.Create<CreateTerminalResponse>();
var responseMessage = new Dictionary<string, string>
{
[MessageKeys.Type] = _fixture.Create<string>(),
[MessageKeys.Content] = JsonConvert.SerializeObject(response)
};
appServiceConnection.Setup(x => x.SendMessageAsync(It.IsAny<IDictionary<string, string>>())).Returns(Task.FromResult((IDictionary<string, string>)responseMessage));
var terminalSize = _fixture.Create<TerminalSize>();
var shellProfile = _fixture.Create<ShellProfile>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
var result = await trayProcessCommunicationService.CreateTerminal(terminalSize, shellProfile);
result.Should().BeEquivalentTo(response);
}
[Fact]
public async Task ResizeTerminal_Default_SendsResizeTerminalRequest()
{
var terminalId = _fixture.Create<int>();
var terminalSize = _fixture.Create<TerminalSize>();
var settingsService = new Mock<ISettingsService>();
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
{
[Command.ToggleWindow] = keyBindings.ToList()
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
await trayProcessCommunicationService.ResizeTerminal(terminalId, terminalSize);
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(ResizeTerminalRequest))), Times.Once);
}
[Fact]
public async Task WriteText_Default_SendsWriteTextRequest()
{
var terminalId = _fixture.Create<int>();
var text = _fixture.Create<string>();
var settingsService = new Mock<ISettingsService>();
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
{
[Command.ToggleWindow] = keyBindings.ToList()
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
await trayProcessCommunicationService.WriteText(terminalId, text);
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(WriteTextRequest))), Times.Once);
}
[Fact]
public async Task CloseTerminal_Default_SendsTerminalExitedRequest()
{
var terminalId = _fixture.Create<int>();
var settingsService = new Mock<ISettingsService>();
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
{
[Command.ToggleWindow] = keyBindings.ToList()
});
var appServiceConnection = new Mock<IAppServiceConnection>();
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
await trayProcessCommunicationService.CloseTerminal(terminalId);
appServiceConnection.Verify(x => x.SendMessageAsync(It.Is<IDictionary<string, string>>(d => d[MessageKeys.Type] == nameof(TerminalExitedRequest))), Times.Once);
}
[Fact]
public void OnMessageReceived_TerminalExitedRequest_InvokesTerminalExitedEvent()
{
var terminalId = _fixture.Create<int>();
var receivedTerminalId = 0;
var terminalExitedEventCalled = false;
var request = new TerminalExitedRequest
{
TerminalId = terminalId
};
var message = new Dictionary<string, string>
{
[MessageKeys.Type] = nameof(TerminalExitedRequest),
[MessageKeys.Content] = JsonConvert.SerializeObject(request)
};
var settingsService = new Mock<ISettingsService>();
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
{
[Command.ToggleWindow] = keyBindings.ToList()
});
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
var appServiceConnection = new Mock<IAppServiceConnection>();
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.TerminalExited += (s, e) =>
{
terminalExitedEventCalled = true;
receivedTerminalId = e;
};
appServiceConnection.Raise(x => x.MessageReceived += null, null, message);
terminalExitedEventCalled.Should().BeTrue();
receivedTerminalId.Should().Be(terminalId);
}
[Fact]
public void OnMessageReceived_DisplayTerminalOutputRequest_InvokesCorrectOutputHandler()
{
var terminalId = _fixture.Create<int>();
var output = _fixture.Create<string>();
var receivedOutput = string.Empty;
var request = new DisplayTerminalOutputRequest
{
TerminalId = terminalId,
Output = output
};
var message = new Dictionary<string, string>
{
[MessageKeys.Type] = nameof(DisplayTerminalOutputRequest),
[MessageKeys.Content] = JsonConvert.SerializeObject(request)
};
var settingsService = new Mock<ISettingsService>();
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
{
[Command.ToggleWindow] = keyBindings.ToList()
});
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
var appServiceConnection = new Mock<IAppServiceConnection>();
trayProcessCommunicationService.Initialize(appServiceConnection.Object);
trayProcessCommunicationService.SubscribeForTerminalOutput(terminalId, o =>
{
receivedOutput = o;
});
appServiceConnection.Raise(x => x.MessageReceived += null, null, message);
receivedOutput.Should().Be(output);
}
}
}
@@ -0,0 +1,57 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.Storage;
namespace FluentTerminal.App.Services.Adapters
{
public class ApplicationDataContainerAdapter : IApplicationDataContainer
{
private readonly ApplicationDataContainer _applicationDataContainer;
public ApplicationDataContainerAdapter(ApplicationDataContainer applicationDataContainer)
{
_applicationDataContainer = applicationDataContainer;
}
public void Delete(string key)
{
_applicationDataContainer.Values.Remove(key);
}
public IEnumerable<object> GetAll()
{
return _applicationDataContainer.Values.Select(x => x.Value);
}
public T ReadValueFromJson<T>(string name, T fallbackValue)
{
if (_applicationDataContainer.Values.TryGetValue(name, out object value))
{
if (EqualityComparer<T>.Default.Equals(fallbackValue, default))
{
fallbackValue = Activator.CreateInstance<T>();
}
JsonConvert.PopulateObject((string)value, fallbackValue);
}
return fallbackValue;
}
public void SetValue(string key, object value)
{
_applicationDataContainer.Values[key] = value;
}
public bool TryGetValue(string key, out object value)
{
return _applicationDataContainer.Values.TryGetValue(key, out value);
}
public void WriteValueAsJson<T>(string name, T value)
{
_applicationDataContainer.Values[name] = JsonConvert.SerializeObject(value);
}
}
}
@@ -0,0 +1,11 @@
namespace FluentTerminal.App.Services
{
public class ApplicationDataContainers
{
public IApplicationDataContainer LocalSettings { get; set; }
public IApplicationDataContainer RoamingSettings { get; set; }
public IApplicationDataContainer Themes { get; set; }
public IApplicationDataContainer KeyBindings { get; set; }
public IApplicationDataContainer ShellProfiles { get; set; }
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace FluentTerminal.App.Services
{
public static class Constants
{
public const string ThemesContainerName = "Themes";
public const string KeyBindingsContainerName = "KeyBindings";
public const string ShellProfilesContainerName = "ShellProfiles";
}
}
@@ -0,0 +1,10 @@
using FluentTerminal.Models;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services.Dialogs
{
public interface ICreateKeyBindingDialog
{
Task<KeyBinding> CreateKeyBinding();
}
}
@@ -0,0 +1,14 @@
using System.Threading.Tasks;
namespace FluentTerminal.App.Services.Dialogs
{
public interface IMessageDialog
{
string Title { get; set; }
string Content { get; set; }
void AddButton(DialogButton button);
Task<DialogButton> ShowAsync();
}
}
@@ -0,0 +1,12 @@
using FluentTerminal.Models;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services.Dialogs
{
public interface IShellProfileSelectionDialog
{
ShellProfile SelectedProfile { get; }
Task<ShellProfile> SelectProfile();
}
}
@@ -0,0 +1,7 @@
namespace FluentTerminal.App.Services.EventArgs
{
public class CancelableEventArgs : System.EventArgs
{
public bool Cancelled { get; set; }
}
}
@@ -0,0 +1,23 @@
using System;
namespace FluentTerminal.App.Services.Exceptions
{
public class ParseThemeException : Exception
{
public ParseThemeException() : base()
{
}
public ParseThemeException(string message) : base(message)
{
}
public ParseThemeException(string message, Exception innerException) : base(message, innerException)
{
}
protected ParseThemeException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
{
}
}
}
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="PListNet" Version="2.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Windows.Foundation.FoundationContract">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.16299.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Windows.Foundation.UniversalApiContract">
<HintPath>$(MSBuildProgramFiles32)\Windows Kits\10\References\10.0.16299.0\Windows.Foundation.UniversalApiContract\5.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface IAppServiceConnection
{
event EventHandler<IDictionary<string, string>> MessageReceived;
Task<IDictionary<string, string>> SendMessageAsync(IDictionary<string, string> message);
}
}
@@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace FluentTerminal.App.Services
{
public interface IApplicationDataContainer
{
T ReadValueFromJson<T>(string name, T fallbackValue);
void WriteValueAsJson<T>(string name, T value);
bool TryGetValue(string key, out object value);
void SetValue(string key, object value);
void Delete(string key);
IEnumerable<object> GetAll();
}
}
@@ -0,0 +1,21 @@
using FluentTerminal.App.Services.EventArgs;
using System;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public delegate Task CloseRequestedHandler(object sender, CancelableEventArgs args);
public interface IApplicationView
{
event CloseRequestedHandler CloseRequested;
string Title { get; set; }
Task RunOnDispatcherThread(Action action);
Task<bool> TryClose();
bool ToggleFullScreen();
}
}
@@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface IClipboardService
{
Task<string> GetText();
void SetText(string text);
}
}
@@ -0,0 +1,26 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
namespace FluentTerminal.App.Services
{
public interface IDefaultValueProvider
{
ApplicationSettings GetDefaultApplicationSettings();
ICollection<KeyBinding> GetDefaultKeyBindings(Command command);
Guid GetDefaultShellProfileId();
IEnumerable<TabTheme> GetDefaultTabThemes();
TerminalOptions GetDefaultTerminalOptions();
Guid GetDefaultThemeId();
IEnumerable<ShellProfile> GetPreinstalledShellProfiles();
IEnumerable<TerminalTheme> GetPreInstalledThemes();
}
}
@@ -0,0 +1,20 @@
using FluentTerminal.Models;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public enum DialogButton
{
OK,
Cancel
}
public interface IDialogService
{
Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons);
Task<ShellProfile> ShowProfileSelectionDialogAsync();
Task<KeyBinding> ShowCreateKeyBindingDialog();
}
}
@@ -0,0 +1,16 @@
using System;
namespace FluentTerminal.App.Services
{
public interface IDispatcherTimer
{
void Start();
void Stop();
TimeSpan Interval { get; set; }
bool IsEnabled { get; }
event EventHandler<object> Tick;
}
}
@@ -0,0 +1,15 @@
using FluentTerminal.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface IFileSystemService
{
Task<File> OpenFile(IEnumerable<string> fileTypes);
Task<string> BrowseForDirectory();
Task SaveTextFile(string name, string fileTypeDescription, string fileType, string content);
}
}
@@ -0,0 +1,12 @@
using 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,36 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
namespace FluentTerminal.App.Services
{
public interface ISettingsService
{
event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
event EventHandler<Guid> CurrentThemeChanged;
event EventHandler KeyBindingsChanged;
event EventHandler<TerminalOptions> TerminalOptionsChanged;
void DeleteShellProfile(Guid id);
void DeleteTheme(Guid id);
ApplicationSettings GetApplicationSettings();
TerminalTheme GetCurrentTheme();
Guid GetCurrentThemeId();
ShellProfile GetDefaultShellProfile();
Guid GetDefaultShellProfileId();
IDictionary<Command, ICollection<KeyBinding>> GetKeyBindings();
IEnumerable<ShellProfile> GetShellProfiles();
IEnumerable<TabTheme> GetTabThemes();
TerminalOptions GetTerminalOptions();
TerminalTheme GetTheme(Guid id);
IEnumerable<TerminalTheme> GetThemes();
void ResetKeyBindings();
void SaveApplicationSettings(ApplicationSettings applicationSettings);
void SaveCurrentThemeId(Guid id);
void SaveDefaultShellProfileId(Guid id);
void SaveKeyBindings(Command command, ICollection<KeyBinding> keyBindings);
void SaveShellProfile(ShellProfile shellProfile);
void SaveTerminalOptions(TerminalOptions terminalOptions);
void SaveTheme(TerminalTheme theme);
}
}
@@ -0,0 +1,12 @@
using FluentTerminal.Models.Enums;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface IStartupTaskService
{
Task DisableStartupTask();
Task<StartupTaskStatus> EnableStartupTask();
Task<StartupTaskStatus> GetStatus();
}
}
@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace FluentTerminal.App.Services
{
public interface ISystemFontService
{
IEnumerable<string> GetSystemFontFamilies();
}
}
@@ -0,0 +1,39 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface ITerminalView
{
event EventHandler<TerminalSize> TerminalSizeChanged;
event EventHandler<string> TerminalTitleChanged;
event EventHandler<Command> KeyboardCommandReceived;
Task ChangeTheme(TerminalColors theme);
Task ChangeOptions(TerminalOptions options);
Task ChangeKeyBindings(IEnumerable<KeyBinding> keyBindings);
Task<TerminalSize> CreateTerminal(TerminalOptions options, TerminalColors theme, IEnumerable<KeyBinding> keyBindings);
Task ConnectToSocket(string url);
void Close();
Task FocusTerminal();
Task<string> GetSelection();
Task FindNext(string searchText);
Task FindPrevious(string searchText);
void FocusSearchTextBox();
}
}
@@ -0,0 +1,14 @@
using FluentTerminal.Models;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface IThemeParser
{
IEnumerable<string> SupportedFileTypes { get; }
Task<TerminalTheme> Parse(string fileName, Stream fileContent);
}
}
@@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace FluentTerminal.App.Services
{
public interface IThemeParserFactory
{
IEnumerable<string> SupportedFileTypes { get; }
IThemeParser GetParser(string fileType);
}
}
@@ -0,0 +1,26 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Responses;
using System;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services
{
public interface ITrayProcessCommunicationService
{
event EventHandler<int> TerminalExited;
void Initialize(IAppServiceConnection appServiceConnection);
Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellProfile shellProfile);
Task ResizeTerminal(int id, TerminalSize size);
Task UpdateToggleWindowKeyBindings();
Task WriteText(int terminalId, string text);
void SubscribeForTerminalOutput(int terminalId, Action<string> callback);
Task CloseTerminal(int terminalId);
}
}
@@ -0,0 +1,516 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
namespace FluentTerminal.App.Services.Implementation
{
public class DefaultValueProvider : IDefaultValueProvider
{
public ApplicationSettings GetDefaultApplicationSettings()
{
return new ApplicationSettings
{
ConfirmClosingTabs = false,
ConfirmClosingWindows = false,
UnderlineSelectedTab = false,
InactiveTabColorMode = InactiveTabColorMode.Background,
NewTerminalLocation = NewTerminalLocation.Tab,
TabsPosition = TabsPosition.Top,
CopyOnSelect = false,
MouseMiddleClickAction = MouseAction.None,
MouseRightClickAction = MouseAction.ContextMenu,
AlwaysShowTabs = false,
ShowNewOutputIndicator = false
};
}
public ICollection<KeyBinding> GetDefaultKeyBindings(Command command)
{
switch (command)
{
case Command.ToggleWindow:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.ToggleWindow,
Ctrl = false,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.Scroll
}
};
case Command.NextTab:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.NextTab,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.Tab
}
};
case Command.PreviousTab:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.PreviousTab,
Ctrl = true,
Alt = false,
Shift = true,
Key = (int)ExtendedVirtualKey.Tab
}
};
case Command.SwitchToTerm1:
case Command.SwitchToTerm2:
case Command.SwitchToTerm3:
case Command.SwitchToTerm4:
case Command.SwitchToTerm5:
case Command.SwitchToTerm6:
case Command.SwitchToTerm7:
case Command.SwitchToTerm8:
case Command.SwitchToTerm9:
return new List<KeyBinding>
{
new KeyBinding
{
Command = command,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.Number1 + (command - Command.SwitchToTerm1)
}
};
case Command.NewTab:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.NewTab,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.T
}
};
case Command.ConfigurableNewTab:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.ConfigurableNewTab,
Ctrl = true,
Alt = false,
Shift = true,
Key = (int)ExtendedVirtualKey.T
}
};
case Command.CloseTab:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.CloseTab,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.W
}
};
case Command.NewWindow:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.NewWindow,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.N
}
};
case Command.ShowSettings:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.ShowSettings,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.Comma
}
};
case Command.Copy:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.Copy,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.C
}
};
case Command.Paste:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.Paste,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.V
}
};
case Command.Search:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.Search,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.F
}
};
case Command.ToggleFullScreen:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.ToggleFullScreen,
Ctrl = false,
Alt = true,
Shift = false,
Key = (int)ExtendedVirtualKey.Enter
}
};
case Command.SelectAll:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.SelectAll,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.A
}
};
case Command.Clear:
return new List<KeyBinding>
{
new KeyBinding
{
Command = Command.Clear,
Ctrl = true,
Alt = false,
Shift = false,
Key = (int)ExtendedVirtualKey.L
}
};
}
return null;
}
public Guid GetDefaultShellProfileId()
{
return Guid.Parse("813f2298-210a-481a-bdbf-c17bc637a3e2");
}
public IEnumerable<TabTheme> GetDefaultTabThemes()
{
return new[]
{
new TabTheme
{
Id = 0,
Name = "None",
BackgroundOpacity = double.NaN,
BackgroundPointerOverOpacity = double.NaN,
BackgroundPressedOpacity = double.NaN,
BackgroundSelectedOpacity = double.NaN,
BackgroundSelectedPointerOverOpacity = double.NaN,
BackgroundSelectedPressedOpacity = double.NaN
},
new TabTheme
{
Id = 1,
Name = "Red",
Color = "#E81123"
},
new TabTheme
{
Id = 2,
Name = "Green",
Color = "#10893E"
},
new TabTheme
{
Id = 3,
Name = "Blue",
Color = "#0078D7"
},
new TabTheme
{
Id = 4,
Name = "Purple",
Color = "#881798"
},
new TabTheme
{
Id = 5,
Name = "Orange",
Color = "#FF8C00"
},
new TabTheme
{
Id = 6,
Name = "Teal",
Color = "#00B7C3"
}
};
}
public TerminalOptions GetDefaultTerminalOptions()
{
return new TerminalOptions
{
BellStyle = BellStyle.None,
CursorBlink = true,
CursorStyle = CursorStyle.Block,
ScrollBarStyle = ScrollBarStyle.Hidden,
FontFamily = "Consolas",
FontSize = 13,
BackgroundOpacity = 0.8,
ScrollBackLimit = 1000
};
}
public Guid GetDefaultThemeId()
{
return Guid.Parse("281e4352-bb50-47b7-a691-2b13830df95e");
}
public IEnumerable<ShellProfile> GetPreinstalledShellProfiles()
{
return new[]
{
new ShellProfile
{
Id = GetDefaultShellProfileId(),
Name = "Powershell",
Arguments = string.Empty,
Location = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe",
PreInstalled = true,
WorkingDirectory = string.Empty
},
new ShellProfile
{
Id = Guid.Parse("ab942a61-7673-4755-9bd8-765aff91d9a3"),
Name = "CMD",
Arguments = string.Empty,
Location = @"C:\Windows\System32\cmd.exe",
PreInstalled = true,
WorkingDirectory = string.Empty
},
new ShellProfile
{
Id= Guid.Parse("e5785ad6-584f-40cb-bdcd-d5b3b3953e7f"),
Name = "WSL",
Arguments = string.Empty,
Location = @"C:\windows\system32\wsl.exe",
PreInstalled = true,
WorkingDirectory = string.Empty
}
};
}
public IEnumerable<TerminalTheme> GetPreInstalledThemes()
{
var defaultXterm = new TerminalTheme
{
Id = GetDefaultThemeId(),
Author = "xterm.js",
Name = "Xterm.js Default",
PreInstalled = true,
Colors = new TerminalColors
{
Black = "#2e3436",
Red = "#cc0000",
Green = "#4e9a06",
Yellow = "#c4a000",
Blue = "#3465a4",
Magenta = "#75507b",
Cyan = "#06989a",
White = "#d3d7cf",
BrightBlack = "#555753",
BrightRed = "#ef2929",
BrightGreen = "#8ae234",
BrightYellow = "#fce94f",
BrightBlue = "#729fcf",
BrightMagenta = "#ad7fa8",
BrightCyan = "#34e2e2",
BrightWhite = "#eeeeec",
Foreground = "#ffffff",
Background = "#000000",
Cursor = "#ffffff",
CursorAccent = "#000000",
Selection = "rgba(255, 255, 255, 0.3)"
}
};
var powerShell = new TerminalTheme
{
Id = Guid.Parse("3571ce1b-31ce-4cf7-ae15-e0bff70c3eea"),
Author = "Microsoft",
Name = "PowerShell",
PreInstalled = true,
Colors = new TerminalColors
{
Black = "#000000",
Red = "#800000",
Green = "#008000",
Yellow = "#EEEDF0",
Blue = "#000080",
Magenta = "#012456",
Cyan = "#008080",
White = "#c0c0c0",
BrightBlack = "#808080",
BrightRed = "#ff0000",
BrightGreen = "#00ff00",
BrightYellow = "#ffff00",
BrightBlue = "#0000ff",
BrightMagenta = "#ff00ff",
BrightCyan = "#00ffff",
BrightWhite = "#ffffff",
Foreground = "#ffffff",
Background = "#012456",
Cursor = "#fedba9",
CursorAccent = "#000000",
Selection = "rgba(254, 219, 169, 0.3)"
}
};
var homebrew = new TerminalTheme
{
Id = Guid.Parse("5f034210-067b-40e2-b9c9-6a25eb6fb8e6"),
Author = "Hans Kokx",
Name = "Homebrew",
PreInstalled = true,
Colors = new TerminalColors
{
Black = "#000000",
Red = "#A0160B",
Green = "#00AF21",
Yellow = "#A1A222",
Blue = "#192AB7",
Magenta = "#AA2FAE",
Cyan = "#12B1BC",
White = "#BBB5AF",
BrightBlack = "#747876",
BrightRed = "#E52213",
BrightGreen = "#00D92B",
BrightYellow = "#E6E435",
BrightBlue = "#283EF9",
BrightMagenta = "#EB43E6",
BrightCyan = "#15E7E8",
BrightWhite = "#E9E9E9",
Foreground = "#00D92B",
Background = "#000000",
Cursor = "#00D92B",
CursorAccent = "#000000",
Selection = "#4CFFFFFF"
}
};
var tomorrow = new TerminalTheme
{
Id = Guid.Parse("0cbbf805-fa86-4be5-ac51-75f3054dcc3a"),
Author = "Chris Kempson",
Name = "Tomorrow",
PreInstalled = true,
Colors = new TerminalColors
{
Foreground = "#4D4D4C",
Background = "#FFFFFF",
Cursor = "#4D4D4C",
CursorAccent = "#FFFFFF",
Selection = "rgba(214, 214, 214, 0.5)",
Black = "#000000",
Red = "#C82829",
Green = "#718C00",
Yellow = "#EAB700",
Blue = "#4271AE",
Magenta = "#8959A8",
Cyan = "#3E999F",
White = "#FFFFFF",
BrightBlack = "#000000",
BrightRed = "#C82829",
BrightGreen = "#718C00",
BrightYellow = "#EAB700",
BrightBlue = "#4271AE",
BrightMagenta = "#8959A8",
BrightCyan = "#3E999F",
BrightWhite = "#FFFFFF"
}
};
var ubuntu = new TerminalTheme
{
Id = Guid.Parse("32519543-bf94-4db6-92a1-7bcec3966c82"),
Author = "Canonical",
Name = "Ubuntu Bash",
PreInstalled = true,
Colors = new TerminalColors
{
Foreground = "#EEEEEE",
Background = "#300A24",
Cursor = "#CFF5DB",
CursorAccent = "#000000",
Selection = "#4DABEDC1",
Black = "#300A24",
Red = "#CC0000",
Green = "#4E9A06",
Yellow = "#06989A",
Blue = "#3465A4",
Magenta = "#75507B",
Cyan = "#C4A000",
White = "#D3D7CF",
BrightBlack = "#554E53",
BrightRed = "#EF2929",
BrightGreen = "#8AE234",
BrightYellow = "#34E2E2",
BrightBlue = "#729FCF",
BrightMagenta = "#AD7FA8",
BrightCyan = "#FCE94F",
BrightWhite = "#EEEEEE"
}
};
return new[] { defaultXterm, powerShell, homebrew, tomorrow, ubuntu };
}
}
}
@@ -0,0 +1,64 @@
using FluentTerminal.App.Services.Dialogs;
using FluentTerminal.Models;
using System;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services.Implementation
{
public class DialogService : IDialogService
{
private readonly Func<IShellProfileSelectionDialog> _shellProfileSelectionDialogFactory;
private readonly Func<IMessageDialog> _messageDialogFactory;
private readonly Func<ICreateKeyBindingDialog> _createKeyBindingDialogFactory;
public DialogService(Func<IShellProfileSelectionDialog> shellProfileSelectionDialogFactory, Func<IMessageDialog> messageDialogFactory, Func<ICreateKeyBindingDialog> createKeyBindingDialogFactory)
{
_shellProfileSelectionDialogFactory = shellProfileSelectionDialogFactory;
_messageDialogFactory = messageDialogFactory;
_createKeyBindingDialogFactory = createKeyBindingDialogFactory;
}
public Task<KeyBinding> ShowCreateKeyBindingDialog()
{
var dialog = _createKeyBindingDialogFactory();
return dialog.CreateKeyBinding();
}
public Task<DialogButton> ShowMessageDialogAsnyc(string title, string content, params DialogButton[] buttons)
{
if (string.IsNullOrWhiteSpace(title))
{
throw new ArgumentNullException(nameof(title));
}
if (string.IsNullOrWhiteSpace(content))
{
throw new ArgumentNullException(nameof(content));
}
if (buttons.Length == 0)
{
throw new ArgumentException("Must not be empty", nameof(buttons));
}
var dialog = _messageDialogFactory();
dialog.Content = content;
dialog.Title = title;
foreach (var button in buttons)
{
dialog.AddButton(button);
}
return dialog.ShowAsync();
}
public Task<ShellProfile> ShowProfileSelectionDialogAsync()
{
var dialog = _shellProfileSelectionDialogFactory();
return dialog.SelectProfile();
}
}
}
@@ -0,0 +1,38 @@
using FluentTerminal.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services.Implementation
{
public class FluentTerminalThemeParser : IThemeParser
{
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".flutecolors" };
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
{
if (string.IsNullOrWhiteSpace(fileName))
{
throw new ArgumentNullException(nameof(fileName));
}
if (fileContent == null)
{
throw new ArgumentNullException(nameof(fileContent));
}
using (var streamReader = new StreamReader(fileContent))
{
var content = await streamReader.ReadToEndAsync();
var theme = JsonConvert.DeserializeObject<TerminalTheme>(content);
theme.PreInstalled = false;
theme.Id = Guid.NewGuid();
return theme;
}
}
}
}
@@ -0,0 +1,137 @@
using FluentTerminal.App.Services.Exceptions;
using FluentTerminal.Models;
using PListNet;
using PListNet.Nodes;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services.Implementation
{
public class ITermThemeParser : IThemeParser
{
private const byte Opacity30Percent = 77;
public IEnumerable<string> SupportedFileTypes { get; } = new string[] { ".itermcolors" };
private static class ITermThemeKeys
{
public const string Ansi0Color = "Ansi 0 Color";
public const string Ansi1Color = "Ansi 1 Color";
public const string Ansi2Color = "Ansi 2 Color";
public const string Ansi3Color = "Ansi 3 Color";
public const string Ansi4Color = "Ansi 4 Color";
public const string Ansi5Color = "Ansi 5 Color";
public const string Ansi6Color = "Ansi 6 Color";
public const string Ansi7Color = "Ansi 7 Color";
public const string Ansi8Color = "Ansi 8 Color";
public const string Ansi9Color = "Ansi 9 Color";
public const string Ansi10Color = "Ansi 10 Color";
public const string Ansi11Color = "Ansi 11 Color";
public const string Ansi12Color = "Ansi 12 Color";
public const string Ansi13Color = "Ansi 13 Color";
public const string Ansi14Color = "Ansi 14 Color";
public const string Ansi15Color = "Ansi 15 Color";
public const string BackgroundColor = "Background Color";
public const string BoldColor = "Bold Color";
public const string CursorColor = "Cursor Color";
public const string CursorTextColor = "Cursor Text Color";
public const string ForegroundColor = "Foreground Color";
public const string SelectedTextColor = "Selected Text Color";
public const string SelectionColor = "Selection Color";
}
private static class ITermThemeColorKeys
{
public const string BlueComponent = "Blue Component";
public const string GreenComponent = "Green Component";
public const string RedComponent = "Red Component";
}
public async Task<TerminalTheme> Parse(string fileName, Stream fileContent)
{
if (string.IsNullOrWhiteSpace(fileName))
{
throw new ArgumentNullException(nameof(fileName));
}
if (fileContent == null)
{
throw new ArgumentNullException(nameof(fileContent));
}
var node = PList.Load(fileContent) as DictionaryNode ?? throw new ParseThemeException("Root node was not a dictionary.");
return new TerminalTheme
{
Name = Path.GetFileNameWithoutExtension(fileName),
Colors = GetColors(node),
Id = Guid.NewGuid(),
PreInstalled = false
};
}
private TerminalColors GetColors(DictionaryNode themeDictionary)
{
return new TerminalColors
{
Background = GetColorString(themeDictionary[ITermThemeKeys.BackgroundColor]),
Foreground = GetColorString(themeDictionary[ITermThemeKeys.ForegroundColor]),
Cursor = GetColorString(themeDictionary[ITermThemeKeys.CursorColor]),
CursorAccent = GetColorString(themeDictionary[ITermThemeKeys.CursorTextColor]),
Selection = GetColorString(themeDictionary[ITermThemeKeys.SelectionColor], Opacity30Percent),
Black = GetColorString(themeDictionary[ITermThemeKeys.Ansi0Color]),
Red = GetColorString(themeDictionary[ITermThemeKeys.Ansi1Color]),
Green = GetColorString(themeDictionary[ITermThemeKeys.Ansi2Color]),
Yellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi3Color]),
Blue = GetColorString(themeDictionary[ITermThemeKeys.Ansi4Color]),
Magenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi5Color]),
Cyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi6Color]),
White = GetColorString(themeDictionary[ITermThemeKeys.Ansi7Color]),
BrightBlack = GetColorString(themeDictionary[ITermThemeKeys.Ansi8Color]),
BrightRed = GetColorString(themeDictionary[ITermThemeKeys.Ansi9Color]),
BrightGreen = GetColorString(themeDictionary[ITermThemeKeys.Ansi10Color]),
BrightYellow = GetColorString(themeDictionary[ITermThemeKeys.Ansi11Color]),
BrightBlue = GetColorString(themeDictionary[ITermThemeKeys.Ansi12Color]),
BrightMagenta = GetColorString(themeDictionary[ITermThemeKeys.Ansi13Color]),
BrightCyan = GetColorString(themeDictionary[ITermThemeKeys.Ansi14Color]),
BrightWhite = GetColorString(themeDictionary[ITermThemeKeys.Ansi15Color]),
};
}
private string GetColorString(PNode colorNode, byte alpha = Byte.MaxValue)
{
var dictionaryNode = colorNode as DictionaryNode ?? throw new ParseThemeException("Color node was not a dictionary");
var red = dictionaryNode[ITermThemeColorKeys.RedComponent] as RealNode ?? throw new ParseThemeException("Red node value was not a real number");
var green = dictionaryNode[ITermThemeColorKeys.GreenComponent] as RealNode ?? throw new ParseThemeException("Green node value was not a real number");
var blue = dictionaryNode[ITermThemeColorKeys.BlueComponent] as RealNode ?? throw new ParseThemeException("Blue node value was not a real number");
var color = $"#{alpha:X2}{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
if (alpha == byte.MaxValue)
{
return $"#{GetByteValue(red):X2}{GetByteValue(green):X2}{GetByteValue(blue):X2}";
}
else
{
return $"rgba({GetByteValue(red):G}, {GetByteValue(green):G}, {GetByteValue(blue):G}, {ToDoubleString(alpha)})";
}
}
private byte GetByteValue(RealNode node)
{
var doubleValue = node.Value * Byte.MaxValue;
return Convert.ToByte(doubleValue);
}
private static string ToDoubleString(byte alpha)
{
return (alpha / 256.0).ToString(CultureInfo.InvariantCulture);
}
}
}
@@ -0,0 +1,38 @@
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
namespace FluentTerminal.App.Services.Implementation
{
public class KeyboardCommandService : IKeyboardCommandService
{
private readonly Dictionary<Command, Action> _commandHandlers = new Dictionary<Command, Action>();
public void RegisterCommandHandler(Command command, Action handler)
{
if (_commandHandlers.ContainsKey(command))
{
throw new InvalidOperationException("command already registered");
}
_commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler));
}
public void SendCommand(Command command)
{
if (_commandHandlers.TryGetValue(command, out Action handler))
{
handler.Invoke();
return;
}
// already registered by SystemTray's ToggleWindowService
if (command == Command.ToggleWindow)
{
return;
}
throw new KeyNotFoundException($"No handler registered for command: {command}");
}
}
}
@@ -0,0 +1,211 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace FluentTerminal.App.Services.Implementation
{
public class SettingsService : ISettingsService
{
public const string CurrentThemeKey = "CurrentTheme";
public const string DefaultShellProfileKey = "DefaultShellProfile";
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly IApplicationDataContainer _keyBindings;
private readonly IApplicationDataContainer _localSettings;
private readonly IApplicationDataContainer _roamingSettings;
private readonly IApplicationDataContainer _shellProfiles;
private readonly IApplicationDataContainer _themes;
public SettingsService(IDefaultValueProvider defaultValueProvider, ApplicationDataContainers containers)
{
_defaultValueProvider = defaultValueProvider;
_localSettings = containers.LocalSettings;
_roamingSettings = containers.RoamingSettings;
_themes = containers.Themes;
_keyBindings = containers.KeyBindings;
_shellProfiles = containers.ShellProfiles;
foreach (var theme in _defaultValueProvider.GetPreInstalledThemes())
{
if (GetTheme(theme.Id) == null)
{
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
}
}
foreach (var shellProfile in _defaultValueProvider.GetPreinstalledShellProfiles())
{
if (GetShellProfile(shellProfile.Id) == null)
{
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
}
}
}
public event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
public event EventHandler<Guid> CurrentThemeChanged;
public event EventHandler KeyBindingsChanged;
public event EventHandler<TerminalOptions> TerminalOptionsChanged;
public void DeleteShellProfile(Guid id)
{
_shellProfiles.Delete(id.ToString());
}
public void DeleteTheme(Guid id)
{
_themes.Delete(id.ToString());
}
public ApplicationSettings GetApplicationSettings()
{
return _roamingSettings.ReadValueFromJson(nameof(ApplicationSettings), _defaultValueProvider.GetDefaultApplicationSettings());
}
public TerminalTheme GetCurrentTheme()
{
var id = GetCurrentThemeId();
var theme = GetTheme(id);
if (theme == null)
{
id = _defaultValueProvider.GetDefaultThemeId();
SaveCurrentThemeId(id);
theme = GetTheme(id);
}
return theme;
}
public Guid GetCurrentThemeId()
{
if (_roamingSettings.TryGetValue(CurrentThemeKey, out object value))
{
return (Guid)value;
}
return _defaultValueProvider.GetDefaultThemeId();
}
public ShellProfile GetDefaultShellProfile()
{
var id = GetDefaultShellProfileId();
var profile = _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
if (profile == null)
{
id = _defaultValueProvider.GetDefaultShellProfileId();
SaveDefaultShellProfileId(id);
profile = _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
}
return profile;
}
public ShellProfile GetShellProfile(Guid id)
{
return _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
}
public Guid GetDefaultShellProfileId()
{
if (_localSettings.TryGetValue(DefaultShellProfileKey, out object value))
{
return (Guid)value;
}
return _defaultValueProvider.GetDefaultShellProfileId();
}
public IDictionary<Command, ICollection<KeyBinding>> GetKeyBindings()
{
var keyBindings = new Dictionary<Command, ICollection<KeyBinding>>();
foreach (Command command in Enum.GetValues(typeof(Command)))
{
keyBindings.Add(command, _keyBindings.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null) ?? _defaultValueProvider.GetDefaultKeyBindings(command));
}
return keyBindings;
}
public IEnumerable<ShellProfile> GetShellProfiles()
{
return _shellProfiles.GetAll().Select(x => JsonConvert.DeserializeObject<ShellProfile>((string)x)).ToList();
}
public IEnumerable<TabTheme> GetTabThemes()
{
return _defaultValueProvider.GetDefaultTabThemes();
}
public TerminalOptions GetTerminalOptions()
{
return _roamingSettings.ReadValueFromJson(nameof(TerminalOptions), _defaultValueProvider.GetDefaultTerminalOptions());
}
public TerminalTheme GetTheme(Guid id)
{
return _themes.ReadValueFromJson(id.ToString(), default(TerminalTheme));
}
public IEnumerable<TerminalTheme> GetThemes()
{
return _themes.GetAll().Select(x => JsonConvert.DeserializeObject<TerminalTheme>((string)x)).ToList();
}
public void ResetKeyBindings()
{
foreach (Command command in Enum.GetValues(typeof(Command)))
{
_keyBindings.WriteValueAsJson(command.ToString(), _defaultValueProvider.GetDefaultKeyBindings(command));
}
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
}
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
{
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
ApplicationSettingsChanged?.Invoke(this, applicationSettings);
}
public void SaveCurrentThemeId(Guid id)
{
_roamingSettings.SetValue(CurrentThemeKey, id);
CurrentThemeChanged?.Invoke(this, id);
}
public void SaveDefaultShellProfileId(Guid id)
{
_localSettings.SetValue(DefaultShellProfileKey, id);
}
public void SaveKeyBindings(Command command, ICollection<KeyBinding> keyBindings)
{
_keyBindings.WriteValueAsJson(command.ToString(), keyBindings);
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
}
public void SaveShellProfile(ShellProfile shellProfile)
{
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
}
public void SaveTerminalOptions(TerminalOptions terminalOptions)
{
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
TerminalOptionsChanged?.Invoke(this, terminalOptions);
}
public void SaveTheme(TerminalTheme theme)
{
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
if (theme.Id == GetCurrentThemeId())
{
CurrentThemeChanged?.Invoke(this, theme.Id);
}
}
}
}
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Linq;
namespace FluentTerminal.App.Services.Implementation
{
public class ThemeParserFactory : IThemeParserFactory
{
private IEnumerable<IThemeParser> _parsers;
public IEnumerable<string> SupportedFileTypes { get; }
public ThemeParserFactory(IEnumerable<IThemeParser> parsers)
{
_parsers = parsers;
SupportedFileTypes = _parsers.SelectMany(p => p.SupportedFileTypes);
}
public IThemeParser GetParser(string fileType)
{
return _parsers.FirstOrDefault(p => p.SupportedFileTypes.Contains(fileType));
}
}
}
@@ -0,0 +1,130 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using FluentTerminal.Models.Requests;
using FluentTerminal.Models.Responses;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace FluentTerminal.App.Services.Implementation
{
public class TrayProcessCommunicationService : ITrayProcessCommunicationService
{
private readonly ISettingsService _settingsService;
private IAppServiceConnection _appServiceConnection;
private Dictionary<int, Action<string>> _terminalOutputHandlers;
public event EventHandler<int> TerminalExited;
public TrayProcessCommunicationService(ISettingsService settingsService)
{
_settingsService = settingsService;
_terminalOutputHandlers = new Dictionary<int, Action<string>>();
}
public async Task<CreateTerminalResponse> CreateTerminal(TerminalSize size, ShellProfile shellProfile)
{
var request = new CreateTerminalRequest
{
Size = size,
Profile = shellProfile
};
var responseMessage = await _appServiceConnection.SendMessageAsync(CreateMessage(request));
return JsonConvert.DeserializeObject<CreateTerminalResponse>(responseMessage[MessageKeys.Content]);
}
public void Initialize(IAppServiceConnection appServiceConnection)
{
_appServiceConnection = appServiceConnection;
_appServiceConnection.MessageReceived += OnMessageReceived;
}
private void OnMessageReceived(object sender, IDictionary<string, string> e)
{
var messageType = e[MessageKeys.Type];
var messageContent = e[MessageKeys.Content];
if (messageType == nameof(DisplayTerminalOutputRequest))
{
var request = JsonConvert.DeserializeObject<DisplayTerminalOutputRequest>(messageContent);
if (_terminalOutputHandlers.ContainsKey(request.TerminalId))
{
_terminalOutputHandlers[request.TerminalId].Invoke(request.Output);
}
else
{
Debug.WriteLine("output was not handled: " + request.Output);
}
}
else if (messageType == nameof(TerminalExitedRequest))
{
var request = JsonConvert.DeserializeObject<TerminalExitedRequest>(messageContent);
TerminalExited?.Invoke(this, request.TerminalId);
}
}
public Task ResizeTerminal(int id, TerminalSize size)
{
var request = new ResizeTerminalRequest
{
TerminalId = id,
NewSize = size
};
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
}
public void SubscribeForTerminalOutput(int terminalId, Action<string> callback)
{
_terminalOutputHandlers[terminalId] = callback;
}
public Task UpdateToggleWindowKeyBindings()
{
var keyBindings = _settingsService.GetKeyBindings()[Command.ToggleWindow];
var request = new SetToggleWindowKeyBindingsRequest
{
KeyBindings = keyBindings
};
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
}
public Task WriteText(int id, string text)
{
var request = new WriteTextRequest
{
TerminalId = id,
Text = text
};
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
}
public Task CloseTerminal(int terminalId)
{
var request = new TerminalExitedRequest
{
TerminalId = terminalId
};
return _appServiceConnection.SendMessageAsync(CreateMessage(request));
}
private IDictionary<string, string> CreateMessage(object content)
{
return new Dictionary<string, string>
{
[MessageKeys.Type] = content.GetType().Name,
[MessageKeys.Content] = JsonConvert.SerializeObject(content)
};
}
}
}
@@ -0,0 +1,21 @@
using System;
using System.ComponentModel;
namespace FluentTerminal.App.Services.Utilities
{
public static class EnumHelper
{
public static string GetEnumDescription(Enum value)
{
var fieldInfo = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes?.Length > 0)
{
return attributes[0].Description;
}
return value.ToString();
}
}
}
@@ -0,0 +1,27 @@
using FluentTerminal.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Reflection;
namespace FluentTerminal.App.Services.Utilities
{
public class TerminalThemeContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(TerminalTheme) && property.PropertyName == nameof(TerminalTheme.PreInstalled))
{
property.ShouldSerialize = (_) => false;
}
if (property.DeclaringType == typeof(TerminalTheme) && property.PropertyName == nameof(TerminalTheme.Id))
{
property.ShouldSerialize = (_) => false;
}
return property;
}
}
}
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fleck" Version="1.0.3" />
<PackageReference Include="MvvmLightLibs" Version="5.4.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj" />
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,124 @@
using FluentTerminal.App.ViewModels.Utilities;
using System;
using System.Threading.Tasks;
using System.Windows.Input;
namespace FluentTerminal.App.ViewModels.Infrastructure
{
public class AsyncCommand : IAsyncCommand
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
private readonly IErrorHandler _errorHandler;
public AsyncCommand(
Func<Task> execute,
Func<bool> canExecute = null,
IErrorHandler errorHandler = null)
{
_execute = execute;
_canExecute = canExecute;
_errorHandler = errorHandler;
}
public bool CanExecute()
{
return !_isExecuting && (_canExecute?.Invoke() ?? true);
}
public async Task ExecuteAsync()
{
if (CanExecute())
{
try
{
_isExecuting = true;
await _execute();
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
#region Explicit implementations
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
ExecuteAsync().FireAndForgetSafeAsync(_errorHandler);
}
#endregion
}
public class AsyncCommand<T> : IAsyncCommand<T>
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<T, Task> _execute;
private readonly Func<T, bool> _canExecute;
private readonly IErrorHandler _errorHandler;
public AsyncCommand(Func<T, Task> execute, Func<T, bool> canExecute = null, IErrorHandler errorHandler = null)
{
_execute = execute;
_canExecute = canExecute;
_errorHandler = errorHandler;
}
public bool CanExecute(T parameter)
{
return !_isExecuting && (_canExecute?.Invoke(parameter) ?? true);
}
public async Task ExecuteAsync(T parameter)
{
if (CanExecute(parameter))
{
try
{
_isExecuting = true;
await _execute(parameter);
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
#region Explicit implementations
bool ICommand.CanExecute(object parameter)
{
return CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
ExecuteAsync((T)parameter).FireAndForgetSafeAsync(_errorHandler);
}
#endregion
}
}
@@ -0,0 +1,20 @@
using System.Threading.Tasks;
using System.Windows.Input;
namespace FluentTerminal.App.ViewModels.Infrastructure
{
/// <summary>
/// based on: https://github.com/johnthiriet/AsyncVoid
/// </summary>
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync();
bool CanExecute();
}
public interface IAsyncCommand<T> : ICommand
{
Task ExecuteAsync(T parameter);
bool CanExecute(T parameter);
}
}
@@ -0,0 +1,9 @@
using System;
namespace FluentTerminal.App.ViewModels.Infrastructure
{
public interface IErrorHandler
{
void HandleError(Exception ex);
}
}
@@ -0,0 +1,289 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.EventArgs;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels
{
public class MainViewModel : ViewModelBase
{
private readonly IApplicationView _applicationView;
private readonly IClipboardService _clipboardService;
private readonly IDialogService _dialogService;
private readonly IDispatcherTimer _dispatcherTimer;
private readonly IKeyboardCommandService _keyboardCommandService;
private readonly ISettingsService _settingsService;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
private ApplicationSettings _applicationSettings;
private string _background;
private double _backgroundOpacity;
private TerminalViewModel _selectedTerminal;
private TabsPosition _tabsPosition;
public MainViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService,
IApplicationView applicationView, IDispatcherTimer dispatcherTimer, IClipboardService clipboardService)
{
_settingsService = settingsService;
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
_trayProcessCommunicationService = trayProcessCommunicationService;
_dialogService = dialogService;
_applicationView = applicationView;
_dispatcherTimer = dispatcherTimer;
_clipboardService = clipboardService;
_keyboardCommandService = keyboardCommandService;
_keyboardCommandService.RegisterCommandHandler(Command.NewTab, () => AddTerminal(null, false));
_keyboardCommandService.RegisterCommandHandler(Command.ConfigurableNewTab, () => AddTerminal(null, true));
_keyboardCommandService.RegisterCommandHandler(Command.CloseTab, CloseCurrentTab);
// Add all of the commands for switching to a tab of a given ID, if there's one open there
for (int i = 0; i < 9; i++)
{
Command switchCmd = Command.SwitchToTerm1 + i;
int tabNumber = i;
Action handler = () => SelectTabNumber(tabNumber);
_keyboardCommandService.RegisterCommandHandler(switchCmd, handler);
}
_keyboardCommandService.RegisterCommandHandler(Command.NextTab, SelectNextTab);
_keyboardCommandService.RegisterCommandHandler(Command.PreviousTab, SelectPreviousTab);
_keyboardCommandService.RegisterCommandHandler(Command.NewWindow, NewWindow);
_keyboardCommandService.RegisterCommandHandler(Command.ShowSettings, ShowSettings);
_keyboardCommandService.RegisterCommandHandler(Command.ToggleFullScreen, ToggleFullScreen);
var currentTheme = _settingsService.GetCurrentTheme();
var options = _settingsService.GetTerminalOptions();
Background = currentTheme.Colors.Background;
BackgroundOpacity = options.BackgroundOpacity;
_applicationSettings = _settingsService.GetApplicationSettings();
TabsPosition = _applicationSettings.TabsPosition;
AddTerminalCommand = new RelayCommand(() => AddTerminal(null, false));
ShowSettingsCommand = new RelayCommand(ShowSettings);
_applicationView.CloseRequested += OnCloseRequest;
Terminals.CollectionChanged += OnTerminalsCollectionChanged;
}
private void OnTerminalsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged(nameof(ShowTabsOnTop));
RaisePropertyChanged(nameof(ShowTabsOnBottom));
}
public event EventHandler Closed;
public event EventHandler NewWindowRequested;
public event EventHandler ShowSettingsRequested;
public RelayCommand AddTerminalCommand { get; }
public bool ShowTabsOnTop
{
get => TabsPosition == TabsPosition.Top && (Terminals.Count > 1 || _applicationSettings.AlwaysShowTabs);
}
public bool ShowTabsOnBottom
{
get => TabsPosition == TabsPosition.Bottom && (Terminals.Count > 1 || _applicationSettings.AlwaysShowTabs);
}
public string Background
{
get => _background;
set => Set(ref _background, value);
}
public double BackgroundOpacity
{
get => _backgroundOpacity;
set => Set(ref _backgroundOpacity, value);
}
public TerminalViewModel SelectedTerminal
{
get => _selectedTerminal;
set
{
if (SelectedTerminal != null)
{
_selectedTerminal.IsSelected = false;
}
if (Set(ref _selectedTerminal, value))
{
SelectedTerminal?.FocusTerminal();
if (SelectedTerminal != null)
{
SelectedTerminal.IsSelected = true;
}
}
}
}
public RelayCommand ShowSettingsCommand { get; }
public TabsPosition TabsPosition
{
get => _tabsPosition;
set => Set(ref _tabsPosition, value);
}
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
public Task AddTerminal(string startupDirectory, bool showProfileSelection)
{
return _applicationView.RunOnDispatcherThread(async () =>
{
ShellProfile profile = null;
if (showProfileSelection)
{
profile = await _dialogService.ShowProfileSelectionDialogAsync();
if (profile == null)
{
return;
}
}
else
{
profile = _settingsService.GetDefaultShellProfile();
}
var terminal = new TerminalViewModel(_settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService,
_applicationSettings, startupDirectory, profile, _applicationView, _dispatcherTimer, _clipboardService);
terminal.Closed += OnTerminalCloseRequested;
Terminals.Add(terminal);
SelectedTerminal = terminal;
});
}
public void CloseAllTerminals()
{
foreach (var terminal in Terminals)
{
terminal.CloseView();
}
}
private void CloseCurrentTab()
{
SelectedTerminal?.CloseCommand.Execute(null);
}
private void NewWindow()
{
NewWindowRequested?.Invoke(this, EventArgs.Empty);
}
private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
{
await _applicationView.RunOnDispatcherThread(() =>
{
_applicationSettings = e;
TabsPosition = e.TabsPosition;
RaisePropertyChanged(nameof(ShowTabsOnTop));
RaisePropertyChanged(nameof(ShowTabsOnBottom));
});
}
private async Task OnCloseRequest(object sender, CancelableEventArgs e)
{
if (_applicationSettings.ConfirmClosingWindows)
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to close this window?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(false);
if (result == DialogButton.OK)
{
CloseAllTerminals();
Closed?.Invoke(this, EventArgs.Empty);
}
else
{
e.Cancelled = true;
}
}
else
{
CloseAllTerminals();
Closed?.Invoke(this, EventArgs.Empty);
}
}
private async void OnCurrentThemeChanged(object sender, Guid e)
{
await _applicationView.RunOnDispatcherThread(() =>
{
var currentTheme = _settingsService.GetTheme(e);
Background = currentTheme.Colors.Background;
});
}
private void OnTerminalCloseRequested(object sender, EventArgs e)
{
if (sender is TerminalViewModel terminal)
{
terminal.CloseView();
if (SelectedTerminal == terminal)
{
SelectedTerminal = Terminals.LastOrDefault(t => t != terminal);
}
Terminals.Remove(terminal);
if (Terminals.Count == 0)
{
Closed?.Invoke(this, EventArgs.Empty);
_applicationView.TryClose();
}
}
}
private async void OnTerminalOptionsChanged(object sender, TerminalOptions e)
{
await _applicationView.RunOnDispatcherThread(() =>
{
BackgroundOpacity = e.BackgroundOpacity;
});
}
private void SelectTabNumber(int tabNumber)
{
if (tabNumber < Terminals.Count)
{
SelectedTerminal = Terminals[tabNumber];
}
}
private void SelectNextTab()
{
var currentIndex = Terminals.IndexOf(SelectedTerminal);
var nextIndex = (currentIndex + 1) % Terminals.Count;
SelectedTerminal = Terminals[nextIndex];
}
private void SelectPreviousTab()
{
var currentIndex = Terminals.IndexOf(SelectedTerminal);
var previousIndex = (currentIndex - 1 + Terminals.Count) % Terminals.Count;
SelectedTerminal = Terminals[previousIndex];
}
private void ShowSettings()
{
ShowSettingsRequested?.Invoke(this, EventArgs.Empty);
}
private void ToggleFullScreen()
{
_applicationView.ToggleFullScreen();
}
}
}
@@ -0,0 +1,292 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class GeneralPageViewModel : ViewModelBase
{
private readonly ApplicationSettings _applicationSettings;
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private readonly IStartupTaskService _startupTaskService;
private bool _canEnableStartupTask;
private bool _editingNewTerminalLocation;
private bool _editingTabsPosition;
private bool _editingInactiveTabColorMode;
private bool _startupTaskEnabled;
private string _startupTaskErrorMessage;
public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IStartupTaskService startupTaskService)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
_startupTaskService = startupTaskService;
_applicationSettings = _settingsService.GetApplicationSettings();
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
}
public async Task OnNavigatedTo()
{
var startupTaskStatus = await _startupTaskService.GetStatus();
SetStartupTaskPropertiesForStatus(startupTaskStatus);
}
public bool AlwaysShowTabs
{
get => _applicationSettings.AlwaysShowTabs;
set
{
if (_applicationSettings.AlwaysShowTabs != value)
{
_applicationSettings.AlwaysShowTabs = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public bool ShowNewOutputIndicator
{
get => _applicationSettings.ShowNewOutputIndicator;
set
{
if (_applicationSettings.ShowNewOutputIndicator != value)
{
_applicationSettings.ShowNewOutputIndicator = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public bool BottomIsSelected
{
get => TabsPosition == TabsPosition.Bottom;
set => TabsPosition = TabsPosition.Bottom;
}
public bool CanEnableStartupTask
{
get => _canEnableStartupTask;
set => Set(ref _canEnableStartupTask, value);
}
public bool ConfirmClosingTabs
{
get => _applicationSettings.ConfirmClosingTabs;
set
{
if (_applicationSettings.ConfirmClosingTabs != value)
{
_applicationSettings.ConfirmClosingTabs = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public bool ConfirmClosingWindows
{
get => _applicationSettings.ConfirmClosingWindows;
set
{
if (_applicationSettings.ConfirmClosingWindows != value)
{
_applicationSettings.ConfirmClosingWindows = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public NewTerminalLocation NewTerminalLocation
{
get => _applicationSettings.NewTerminalLocation;
set
{
if (_applicationSettings.NewTerminalLocation != value && !_editingNewTerminalLocation)
{
_editingNewTerminalLocation = true;
_applicationSettings.NewTerminalLocation = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
RaisePropertyChanged(nameof(WindowIsSelected));
RaisePropertyChanged(nameof(TabIsSelected));
_editingNewTerminalLocation = false;
}
}
}
public RelayCommand RestoreDefaultsCommand { get; }
public bool StartupTaskEnabled
{
get => _startupTaskEnabled;
set
{
if (_startupTaskEnabled != value)
{
_startupTaskEnabled = value;
RaisePropertyChanged(nameof(StartupTaskEnabled));
SetStartupTaskState(value);
}
}
}
public string StartupTaskErrorMessage
{
get => _startupTaskErrorMessage;
set => Set(ref _startupTaskErrorMessage, value);
}
public bool TabIsSelected
{
get => NewTerminalLocation == NewTerminalLocation.Tab;
set => NewTerminalLocation = NewTerminalLocation.Tab;
}
public TabsPosition TabsPosition
{
get => _applicationSettings.TabsPosition;
set
{
if (_applicationSettings.TabsPosition != value && !_editingTabsPosition)
{
_editingTabsPosition = true;
_applicationSettings.TabsPosition = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
RaisePropertyChanged(nameof(TopIsSelected));
RaisePropertyChanged(nameof(BottomIsSelected));
_editingTabsPosition = false;
}
}
}
public bool TopIsSelected
{
get => TabsPosition == TabsPosition.Top;
set => TabsPosition = TabsPosition.Top;
}
public bool UnderlineSelectedTab
{
get => _applicationSettings.UnderlineSelectedTab;
set
{
if (_applicationSettings.UnderlineSelectedTab != value)
{
_applicationSettings.UnderlineSelectedTab = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public bool BackgroundIsSelected
{
get => InactiveTabColorMode == InactiveTabColorMode.Background;
set => InactiveTabColorMode = InactiveTabColorMode.Background;
}
public bool UnderlinedIsSelected
{
get => InactiveTabColorMode == InactiveTabColorMode.Underlined;
set => InactiveTabColorMode = InactiveTabColorMode.Underlined;
}
public InactiveTabColorMode InactiveTabColorMode
{
get => _applicationSettings.InactiveTabColorMode;
set
{
if (_applicationSettings.InactiveTabColorMode != value && !_editingInactiveTabColorMode)
{
_editingInactiveTabColorMode = true;
_applicationSettings.InactiveTabColorMode = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
RaisePropertyChanged(nameof(BackgroundIsSelected));
RaisePropertyChanged(nameof(UnderlinedIsSelected));
_editingInactiveTabColorMode = false;
}
}
}
public bool WindowIsSelected
{
get => NewTerminalLocation == NewTerminalLocation.Window;
set => NewTerminalLocation = NewTerminalLocation.Window;
}
private async Task RestoreDefaults()
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the general settings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.OK)
{
var defaults = _defaultValueProvider.GetDefaultApplicationSettings();
ConfirmClosingWindows = defaults.ConfirmClosingWindows;
ConfirmClosingTabs = defaults.ConfirmClosingTabs;
UnderlineSelectedTab = defaults.UnderlineSelectedTab;
InactiveTabColorMode = defaults.InactiveTabColorMode;
NewTerminalLocation = defaults.NewTerminalLocation;
AlwaysShowTabs = defaults.AlwaysShowTabs;
ShowNewOutputIndicator = defaults.ShowNewOutputIndicator;
}
}
private void SetStartupTaskPropertiesForStatus(StartupTaskStatus startupTaskStatus)
{
switch (startupTaskStatus)
{
case StartupTaskStatus.Enabled:
StartupTaskEnabled = true;
StartupTaskErrorMessage = string.Empty;
CanEnableStartupTask = true;
break;
case StartupTaskStatus.Disabled:
StartupTaskEnabled = false;
StartupTaskErrorMessage = string.Empty;
CanEnableStartupTask = true;
break;
case StartupTaskStatus.DisabledByUser:
StartupTaskEnabled = false;
StartupTaskErrorMessage = "Disabled by user. Please reactivate it in the Startup tab of the Task Manager.";
CanEnableStartupTask = false;
break;
case StartupTaskStatus.DisabledByPolicy:
StartupTaskEnabled = false;
StartupTaskErrorMessage = "Disabled by policy.";
CanEnableStartupTask = false;
break;
}
}
private async Task SetStartupTaskState(bool enabled)
{
StartupTaskStatus status;
if (enabled)
{
status = await _startupTaskService.EnableStartupTask();
}
else
{
_startupTaskService.DisableStartupTask();
status = await _startupTaskService.GetStatus();
}
SetStartupTaskPropertiesForStatus(status);
}
}
}
@@ -0,0 +1,126 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class KeyBindingViewModel : ViewModelBase
{
private readonly IDialogService _dialogService;
public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService)
{
Model = keyBinding;
_dialogService = dialogService;
EditCommand = new RelayCommand(async () => await Edit().ConfigureAwait(false));
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false));
}
public event EventHandler Deleted;
public event EventHandler Edited;
public bool Meta
{
get => Model.Meta;
set
{
if (Model.Meta != value)
{
Model.Meta = value;
RaisePropertyChanged();
}
}
}
public bool Alt
{
get => Model.Alt;
set
{
if (Model.Alt != value)
{
Model.Alt = value;
RaisePropertyChanged();
}
}
}
public bool Ctrl
{
get => Model.Ctrl;
set
{
if (Model.Ctrl != value)
{
Model.Ctrl = value;
RaisePropertyChanged();
}
}
}
public RelayCommand DeleteCommand { get; }
public RelayCommand EditCommand { get; }
public int Key
{
get => Model.Key;
set
{
if (Model.Key != value)
{
Model.Key = value;
RaisePropertyChanged();
}
}
}
public KeyBinding Model { get; }
public bool Shift
{
get => Model.Shift;
set
{
if (Model.Shift != value)
{
Model.Shift = value;
RaisePropertyChanged();
}
}
}
public async Task<bool> Edit()
{
var keyBinding = await _dialogService.ShowCreateKeyBindingDialog().ConfigureAwait(true);
if (keyBinding != null)
{
Alt = keyBinding.Alt;
Ctrl = keyBinding.Ctrl;
Shift = keyBinding.Shift;
Meta = keyBinding.Meta;
Key = keyBinding.Key;
Edited?.Invoke(this, EventArgs.Empty);
return true;
}
return false;
}
private async Task Delete()
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this keybinding?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.OK)
{
Deleted?.Invoke(this, EventArgs.Empty);
}
}
}
}
@@ -0,0 +1,86 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class KeyBindingsPageViewModel : ViewModelBase
{
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
private IDictionary<Command, ICollection<KeyBinding>> _keyBindings;
public KeyBindingsPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ITrayProcessCommunicationService trayProcessCommunicationService)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
_trayProcessCommunicationService = trayProcessCommunicationService;
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
AddCommand = new RelayCommand<Command>(async command => await Add(command).ConfigureAwait(false));
Initialize(_settingsService.GetKeyBindings());
}
public RelayCommand<Command> AddCommand { get; }
public ObservableCollection<KeyBindingsViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingsViewModel>();
public RelayCommand RestoreDefaultsCommand { get; }
private async Task Add(Command command)
{
var keyBinding = KeyBindings.FirstOrDefault(k => k.Command == command);
await keyBinding?.Add();
}
private void ClearKeyBindings()
{
foreach (var keyBinding in KeyBindings)
{
keyBinding.Edited -= OnEdited;
}
KeyBindings.Clear();
}
private void Initialize(IDictionary<Command, ICollection<KeyBinding>> keyBindings)
{
_keyBindings = keyBindings;
ClearKeyBindings();
foreach (var value in Enum.GetValues(typeof(Command)))
{
var command = (Command)value;
var viewModel = new KeyBindingsViewModel(command, _keyBindings[command], _dialogService);
viewModel.Edited += OnEdited;
KeyBindings.Add(viewModel);
}
}
private void OnEdited(Command command, ICollection<KeyBinding> keyBindings)
{
_settingsService.SaveKeyBindings(command, keyBindings);
_trayProcessCommunicationService.UpdateToggleWindowKeyBindings();
}
private async Task RestoreDefaults()
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the default keybindings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.OK)
{
_settingsService.ResetKeyBindings();
Initialize(_settingsService.GetKeyBindings());
}
}
}
}
@@ -0,0 +1,74 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Utilities;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class KeyBindingsViewModel : ViewModelBase
{
private readonly IDialogService _dialogService;
private readonly ICollection<KeyBinding> _keyBindings;
public KeyBindingsViewModel(Command command, ICollection<KeyBinding> keyBindings, IDialogService dialogService)
{
Command = command;
_keyBindings = keyBindings;
_dialogService = dialogService;
CommandName = EnumHelper.GetEnumDescription(command);
foreach (var binding in keyBindings)
{
var viewModel = new KeyBindingViewModel(binding, _dialogService);
viewModel.Deleted += ViewModel_Deleted;
viewModel.Edited += ViewModel_Edited;
KeyBindings.Add(viewModel);
}
}
public delegate void EditedEvent(Command command, ICollection<KeyBinding> keyBindings);
public event EditedEvent Edited;
public Command Command { get; }
public string CommandName { get; }
public ObservableCollection<KeyBindingViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingViewModel>();
public async Task Add()
{
var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService);
if (await newKeyBinding.Edit().ConfigureAwait(true))
{
newKeyBinding.Deleted += ViewModel_Deleted;
newKeyBinding.Edited += ViewModel_Edited;
KeyBindings.Add(newKeyBinding);
_keyBindings.Add(newKeyBinding.Model);
Edited?.Invoke(Command, _keyBindings);
}
}
private void ViewModel_Deleted(object sender, EventArgs e)
{
if (sender is KeyBindingViewModel keyBinding)
{
_keyBindings.Remove(keyBinding.Model);
KeyBindings.Remove(keyBinding);
Edited?.Invoke(Command, _keyBindings);
}
}
private void ViewModel_Edited(object sender, EventArgs e)
{
Edited?.Invoke(Command, _keyBindings);
}
}
}
@@ -0,0 +1,135 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class MousePageViewModel : ViewModelBase
{
private readonly ISettingsService _settingsService;
private readonly IDialogService _dialogService;
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly ApplicationSettings _applicationSettings;
private bool _editingMouseRightClickAction;
private bool _editingMouseMiddleClickAction;
public MousePageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
_applicationSettings = _settingsService.GetApplicationSettings();
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
}
public bool CopyOnSelect
{
get => _applicationSettings.CopyOnSelect;
set
{
if (_applicationSettings.CopyOnSelect != value)
{
_applicationSettings.CopyOnSelect = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
}
}
}
public MouseAction MouseRightClickAction
{
get => _applicationSettings.MouseRightClickAction;
set
{
if (_applicationSettings.MouseRightClickAction != value && !_editingMouseRightClickAction)
{
_editingMouseRightClickAction = true;
_applicationSettings.MouseRightClickAction = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
RaisePropertyChanged(nameof(MouseRightClickNoneIsSelected));
RaisePropertyChanged(nameof(MouseRightClickContextMenuIsSelected));
RaisePropertyChanged(nameof(MouseRightClickPasteIsSelected));
_editingMouseRightClickAction = false;
}
}
}
public bool MouseRightClickNoneIsSelected
{
get => MouseRightClickAction == MouseAction.None;
set => MouseRightClickAction = MouseAction.None;
}
public bool MouseRightClickContextMenuIsSelected
{
get => MouseRightClickAction == MouseAction.ContextMenu;
set => MouseRightClickAction = MouseAction.ContextMenu;
}
public bool MouseRightClickPasteIsSelected
{
get => MouseRightClickAction == MouseAction.Paste;
set => MouseRightClickAction = MouseAction.Paste;
}
public MouseAction MouseMiddleClickAction
{
get => _applicationSettings.MouseMiddleClickAction;
set
{
if (_applicationSettings.MouseMiddleClickAction != value && !_editingMouseMiddleClickAction)
{
_editingMouseMiddleClickAction = true;
_applicationSettings.MouseMiddleClickAction = value;
_settingsService.SaveApplicationSettings(_applicationSettings);
RaisePropertyChanged();
RaisePropertyChanged(nameof(MouseMiddleClickNoneIsSelected));
RaisePropertyChanged(nameof(MouseMiddleClickContextMenuIsSelected));
RaisePropertyChanged(nameof(MouseMiddleClickPasteIsSelected));
_editingMouseMiddleClickAction = false;
}
}
}
public bool MouseMiddleClickNoneIsSelected
{
get => MouseMiddleClickAction == MouseAction.None;
set => MouseMiddleClickAction = MouseAction.None;
}
public bool MouseMiddleClickContextMenuIsSelected
{
get => MouseMiddleClickAction == MouseAction.ContextMenu;
set => MouseMiddleClickAction = MouseAction.ContextMenu;
}
public bool MouseMiddleClickPasteIsSelected
{
get => MouseMiddleClickAction == MouseAction.Paste;
set => MouseMiddleClickAction = MouseAction.Paste;
}
public RelayCommand RestoreDefaultsCommand { get; }
private async Task RestoreDefaults()
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the mouse settings?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.OK)
{
var defaults = _defaultValueProvider.GetDefaultApplicationSettings();
CopyOnSelect = defaults.CopyOnSelect;
MouseMiddleClickAction = defaults.MouseMiddleClickAction;
MouseRightClickAction = defaults.MouseRightClickAction;
}
}
}
}
@@ -0,0 +1,106 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace FluentTerminal.App.ViewModels.Settings
{
public class ProfilesPageViewModel : ViewModelBase
{
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private readonly IFileSystemService _fileSystemService;
private ShellProfileViewModel _selectedShellProfile;
public ProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IFileSystemService fileSystemService)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
_fileSystemService = fileSystemService;
CreateShellProfileCommand = new RelayCommand(CreateShellProfile);
var defaultShellProfileId = _settingsService.GetDefaultShellProfileId();
foreach (var shellProfile in _settingsService.GetShellProfiles())
{
var viewModel = new ShellProfileViewModel(shellProfile, settingsService, dialogService, fileSystemService);
viewModel.Deleted += OnShellProfileDeleted;
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
if (shellProfile.Id == defaultShellProfileId)
{
viewModel.IsDefault = true;
}
ShellProfiles.Add(viewModel);
}
SelectedShellProfile = ShellProfiles.First(p => p.IsDefault);
}
private void OnShellProfileSetAsDefault(object sender, EventArgs e)
{
if (sender is ShellProfileViewModel defaultShellProfile)
{
_settingsService.SaveDefaultShellProfileId(defaultShellProfile.Id);
foreach (var shellProfile in ShellProfiles)
{
shellProfile.IsDefault = shellProfile.Id == defaultShellProfile.Id;
}
}
}
private void OnShellProfileDeleted(object sender, EventArgs e)
{
if (sender is ShellProfileViewModel shellProfile)
{
if (SelectedShellProfile == shellProfile)
{
SelectedShellProfile = ShellProfiles.First();
}
ShellProfiles.Remove(shellProfile);
if (shellProfile.IsDefault)
{
ShellProfiles.First().IsDefault = true;
_settingsService.SaveDefaultShellProfileId(ShellProfiles.First().Id);
}
_settingsService.DeleteShellProfile(shellProfile.Id);
}
}
public RelayCommand CreateShellProfileCommand { get; }
private void CreateShellProfile()
{
var shellProfile = new ShellProfile
{
Id = Guid.NewGuid(),
PreInstalled = false,
Name = "New profile"
};
_settingsService.SaveShellProfile(shellProfile);
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService);
viewModel.EditCommand.Execute(null);
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
viewModel.Deleted += OnShellProfileDeleted;
ShellProfiles.Add(viewModel);
SelectedShellProfile = viewModel;
}
public ShellProfileViewModel SelectedShellProfile
{
get => _selectedShellProfile;
set => Set(ref _selectedShellProfile, value);
}
public ObservableCollection<ShellProfileViewModel> ShellProfiles { get; } = new ObservableCollection<ShellProfileViewModel>();
}
}
@@ -0,0 +1,214 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Settings
{
public class TerminalPageViewModel : ViewModelBase
{
private readonly TerminalOptions _terminalOptions;
private readonly ISettingsService _settingsService;
private readonly IDialogService _dialogService;
private readonly IDefaultValueProvider _defaultValueProvider;
private bool _isEditingCursorStyle;
private bool _isEditingScrollBarStyle;
public bool BarIsSelected
{
get => CursorStyle == CursorStyle.Bar;
set => CursorStyle = CursorStyle.Bar;
}
public bool BlockIsSelected
{
get => CursorStyle == CursorStyle.Block;
set => CursorStyle = CursorStyle.Block;
}
public bool UnderlineIsSelected
{
get => CursorStyle == CursorStyle.Underline;
set => CursorStyle = CursorStyle.Underline;
}
public bool HiddenIsSelected
{
get => ScrollBarStyle == ScrollBarStyle.Hidden;
set => ScrollBarStyle = ScrollBarStyle.Hidden;
}
public bool AutoHidingIsSelected
{
get => ScrollBarStyle == ScrollBarStyle.AutoHiding;
set => ScrollBarStyle = ScrollBarStyle.AutoHiding;
}
public bool VisibleIsSelected
{
get => ScrollBarStyle == ScrollBarStyle.Visible;
set => ScrollBarStyle = ScrollBarStyle.Visible;
}
public bool CursorBlink
{
get => _terminalOptions.CursorBlink;
set
{
if (_terminalOptions.CursorBlink != value)
{
_terminalOptions.CursorBlink = value;
_settingsService.SaveTerminalOptions(_terminalOptions);
RaisePropertyChanged();
}
}
}
public string FontFamily
{
get => _terminalOptions.FontFamily;
set
{
if (_terminalOptions.FontFamily != value)
{
_terminalOptions.FontFamily = value;
_settingsService.SaveTerminalOptions(_terminalOptions);
RaisePropertyChanged();
}
}
}
public double BackgroundOpacity
{
get => _terminalOptions.BackgroundOpacity;
set
{
if (_terminalOptions.BackgroundOpacity != value)
{
_terminalOptions.BackgroundOpacity = value;
_settingsService.SaveTerminalOptions(_terminalOptions);
RaisePropertyChanged();
}
}
}
public string ScrollBackLimit
{
get => _terminalOptions.ScrollBackLimit.ToString();
set
{
var oldValue = _terminalOptions.ScrollBackLimit;
if (uint.TryParse(value, out uint intValue))
{
if (intValue == uint.MinValue)
{
intValue = uint.MaxValue;
}
if (_terminalOptions.ScrollBackLimit != intValue)
{
_terminalOptions.ScrollBackLimit = intValue;
_settingsService.SaveTerminalOptions(_terminalOptions);
RaisePropertyChanged();
}
}
else
{
RaisePropertyChanged();
}
}
}
public IEnumerable<string> Fonts { get; }
public int FontSize
{
get => _terminalOptions.FontSize;
set
{
if (_terminalOptions.FontSize != value)
{
_terminalOptions.FontSize = value;
_settingsService.SaveTerminalOptions(_terminalOptions);
RaisePropertyChanged();
}
}
}
public IEnumerable<int> Sizes { get; }
public RelayCommand RestoreDefaultsCommand { get; }
private CursorStyle CursorStyle
{
get => _terminalOptions.CursorStyle;
set
{
if (_terminalOptions.CursorStyle != value && !_isEditingCursorStyle)
{
_isEditingCursorStyle = true;
_terminalOptions.CursorStyle = value;
_settingsService.SaveTerminalOptions(_terminalOptions);
RaisePropertyChanged();
RaisePropertyChanged(nameof(BlockIsSelected));
RaisePropertyChanged(nameof(BarIsSelected));
RaisePropertyChanged(nameof(UnderlineIsSelected));
_isEditingCursorStyle = false;
}
}
}
private ScrollBarStyle ScrollBarStyle
{
get => _terminalOptions.ScrollBarStyle;
set
{
if (_terminalOptions.ScrollBarStyle != value && !_isEditingScrollBarStyle)
{
_isEditingScrollBarStyle = true;
_terminalOptions.ScrollBarStyle = value;
_settingsService.SaveTerminalOptions(_terminalOptions);
RaisePropertyChanged();
RaisePropertyChanged(nameof(HiddenIsSelected));
RaisePropertyChanged(nameof(AutoHidingIsSelected));
RaisePropertyChanged(nameof(VisibleIsSelected));
_isEditingScrollBarStyle = false;
}
}
}
private async Task RestoreDefaults()
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore the default terminal options?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.OK)
{
var defaults = _defaultValueProvider.GetDefaultTerminalOptions();
CursorBlink = defaults.CursorBlink;
CursorStyle = defaults.CursorStyle;
ScrollBarStyle = defaults.ScrollBarStyle;
FontFamily = defaults.FontFamily;
FontSize = defaults.FontSize;
BackgroundOpacity = defaults.BackgroundOpacity;
ScrollBackLimit = defaults.ScrollBackLimit.ToString();
}
}
public TerminalPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ISystemFontService systemFontService)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
Fonts = systemFontService.GetSystemFontFamilies().OrderBy(s => s);
Sizes = Enumerable.Range(1, 72);
_terminalOptions = _settingsService.GetTerminalOptions();
}
}
}
@@ -0,0 +1,178 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace FluentTerminal.App.ViewModels.Settings
{
public class ThemesPageViewModel : ViewModelBase
{
private readonly IDefaultValueProvider _defaultValueProvider;
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private ThemeViewModel _selectedTheme;
private double _backgroundOpacity;
private readonly IThemeParserFactory _themeParserFactory;
private readonly IFileSystemService _fileSystemService;
public event EventHandler<string> SelectedThemeBackgroundColorChanged;
public ThemesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider,
IThemeParserFactory themeParserFactory, IFileSystemService fileSystemService)
{
_settingsService = settingsService;
_dialogService = dialogService;
_defaultValueProvider = defaultValueProvider;
_themeParserFactory = themeParserFactory;
_fileSystemService = fileSystemService;
CreateThemeCommand = new RelayCommand(CreateTheme);
ImportThemeCommand = new RelayCommand(ImportTheme);
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
BackgroundOpacity = _settingsService.GetTerminalOptions().BackgroundOpacity;
var activeThemeId = _settingsService.GetCurrentThemeId();
foreach (var theme in _settingsService.GetThemes())
{
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, fileSystemService);
viewModel.Activated += OnThemeActivated;
viewModel.Deleted += OnThemeDeleted;
if (theme.Id == activeThemeId)
{
viewModel.IsActive = true;
}
Themes.Add(viewModel);
}
SelectedTheme = Themes.First(t => t.IsActive);
}
public RelayCommand CreateThemeCommand { get; }
public RelayCommand ImportThemeCommand { get; }
public double BackgroundOpacity
{
get => _backgroundOpacity;
set => Set(ref _backgroundOpacity, value);
}
public ThemeViewModel SelectedTheme
{
get => _selectedTheme;
set
{
if (_selectedTheme != null)
{
_selectedTheme.BackgroundChanged -= OnSelectedThemeBackgroundChanged;
}
Set(ref _selectedTheme, value);
if (value != null)
{
value.BackgroundChanged += OnSelectedThemeBackgroundChanged;
}
}
}
public ObservableCollection<ThemeViewModel> Themes { get; } = new ObservableCollection<ThemeViewModel>();
private void CreateTheme()
{
var defaultTheme = _settingsService.GetTheme(_defaultValueProvider.GetDefaultThemeId());
var theme = new TerminalTheme
{
Id = Guid.NewGuid(),
PreInstalled = false,
Name = "New Theme",
Colors = new TerminalColors(defaultTheme.Colors)
};
_settingsService.SaveTheme(theme);
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService);
viewModel.Activated += OnThemeActivated;
viewModel.Deleted += OnThemeDeleted;
Themes.Add(viewModel);
SelectedTheme = viewModel;
}
private async void ImportTheme()
{
var file = await _fileSystemService.OpenFile(_themeParserFactory.SupportedFileTypes).ConfigureAwait(true);
if (file != null)
{
var parser = _themeParserFactory.GetParser(file.FileType);
if (parser == null)
{
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", "No suitable parser found", DialogButton.OK).ConfigureAwait(false);
return;
}
try
{
var theme = await parser.Parse(file.Name, file.Content).ConfigureAwait(true);
_settingsService.SaveTheme(theme);
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService);
viewModel.Activated += OnThemeActivated;
viewModel.Deleted += OnThemeDeleted;
Themes.Add(viewModel);
SelectedTheme = viewModel;
}
catch (Exception exception)
{
await _dialogService.ShowMessageDialogAsnyc("Import theme failed", exception.Message, DialogButton.OK).ConfigureAwait(false);
}
}
}
private void OnThemeActivated(object sender, EventArgs e)
{
if (sender is ThemeViewModel activatedTheme)
{
_settingsService.SaveCurrentThemeId(activatedTheme.Id);
foreach (var theme in Themes)
{
theme.IsActive = theme.Id == activatedTheme.Id;
}
}
}
private void OnThemeDeleted(object sender, EventArgs e)
{
if (sender is ThemeViewModel theme)
{
if (SelectedTheme == theme)
{
SelectedTheme = Themes.First();
}
Themes.Remove(theme);
if (theme.IsActive)
{
Themes.First().IsActive = true;
_settingsService.SaveCurrentThemeId(Themes.First().Id);
}
_settingsService.DeleteTheme(theme.Id);
}
}
private void OnTerminalOptionsChanged(object sender, TerminalOptions e)
{
BackgroundOpacity = e.BackgroundOpacity;
}
private void OnSelectedThemeBackgroundChanged(object sender, string e)
{
SelectedThemeBackgroundColorChanged?.Invoke(this, e);
}
}
}
@@ -0,0 +1,35 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.ViewModels.Settings;
using GalaSoft.MvvmLight;
using System;
namespace FluentTerminal.App.ViewModels
{
public class SettingsViewModel : ViewModelBase
{
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService,
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService, IFileSystemService fileSystemService, IStartupTaskService startupTaskService)
{
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService);
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService);
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService);
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService);
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService);
Mouse = new MousePageViewModel(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 MousePageViewModel Mouse { get; }
public void Close()
{
Closed?.Invoke(this, EventArgs.Empty);
}
}
}
@@ -0,0 +1,200 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.ViewModels.Infrastructure;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels
{
public class ShellProfileViewModel : ViewModelBase
{
private readonly IDialogService _dialogService;
private readonly IFileSystemService _fileSystemService;
private readonly ISettingsService _settingsService;
private readonly ShellProfile _shellProfile;
private string _arguments;
private string _fallbackArguments;
private string _fallbackLocation;
private string _fallbackName;
private int _fallbackTabThemeId;
private string _fallbackWorkingDirectory;
private bool _inEditMode;
private bool _isDefault;
private string _location;
private string _name;
private TabTheme _selectedTabTheme;
private string _workingDirectory;
public ShellProfileViewModel(ShellProfile shellProfile, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService)
{
_shellProfile = shellProfile;
_settingsService = settingsService;
_dialogService = dialogService;
_fileSystemService = fileSystemService;
TabThemes = new ObservableCollection<TabTheme>(settingsService.GetTabThemes());
Id = shellProfile.Id;
Name = shellProfile.Name;
Arguments = shellProfile.Arguments;
Location = shellProfile.Location;
WorkingDirectory = shellProfile.WorkingDirectory;
SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == shellProfile.TabThemeId);
PreInstalled = shellProfile.PreInstalled;
SetDefaultCommand = new RelayCommand(SetDefault);
DeleteCommand = new AsyncCommand(Delete, CanDelete);
EditCommand = new RelayCommand(Edit);
CancelEditCommand = new AsyncCommand(CancelEdit);
SaveChangesCommand = new RelayCommand(SaveChanges);
BrowseForCustomShellCommand = new AsyncCommand(BrowseForCustomShell);
BrowseForWorkingDirectoryCommand = new AsyncCommand(BrowseForWorkingDirectory);
}
public event EventHandler Deleted;
public event EventHandler SetAsDefault;
public IAsyncCommand BrowseForCustomShellCommand { get; }
public IAsyncCommand BrowseForWorkingDirectoryCommand { get; }
public IAsyncCommand CancelEditCommand { get; }
public IAsyncCommand DeleteCommand { get; }
public RelayCommand EditCommand { get; }
public RelayCommand SaveChangesCommand { get; }
public RelayCommand SetDefaultCommand { get; }
public ObservableCollection<TabTheme> TabThemes { get; }
public bool PreInstalled { get; }
public string Arguments
{
get => _arguments;
set => Set(ref _arguments, value);
}
public Guid Id { get; }
public bool InEditMode
{
get => _inEditMode;
set => Set(ref _inEditMode, value);
}
public bool IsDefault
{
get => _isDefault;
set => Set(ref _isDefault, value);
}
public string Location
{
get => _location;
set => Set(ref _location, value);
}
public string Name
{
get => _name;
set => Set(ref _name, value);
}
public TabTheme SelectedTabTheme
{
get => _selectedTabTheme;
set
{
if (value != null)
{
Set(ref _selectedTabTheme, value);
}
}
}
public string WorkingDirectory
{
get => _workingDirectory;
set => Set(ref _workingDirectory, value);
}
public void SaveChanges()
{
_shellProfile.Arguments = Arguments;
_shellProfile.Location = Location;
_shellProfile.Name = Name;
_shellProfile.WorkingDirectory = WorkingDirectory;
_shellProfile.TabThemeId = SelectedTabTheme.Id;
_settingsService.SaveShellProfile(_shellProfile);
InEditMode = false;
}
private async Task BrowseForCustomShell()
{
var file = await _fileSystemService.OpenFile(new[] { ".exe" }).ConfigureAwait(true);
if (file != null)
{
Location = file.Path;
}
}
private async Task BrowseForWorkingDirectory()
{
var directory = await _fileSystemService.BrowseForDirectory().ConfigureAwait(true);
if (directory != null)
{
WorkingDirectory = directory;
}
}
private async Task CancelEdit()
{
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;
SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == _fallbackTabThemeId);
InEditMode = false;
}
}
private bool CanDelete()
{
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 Edit()
{
_fallbackArguments = _shellProfile.Arguments;
_fallbackLocation = _shellProfile.Location;
_fallbackName = _shellProfile.Name;
_fallbackWorkingDirectory = _shellProfile.WorkingDirectory;
_fallbackTabThemeId = _shellProfile.TabThemeId;
InEditMode = true;
}
private void SetDefault()
{
SetAsDefault?.Invoke(this, EventArgs.Empty);
}
}
}
@@ -0,0 +1,409 @@
using Fleck;
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels
{
public class TerminalViewModel : ViewModelBase
{
private readonly IApplicationView _applicationView;
private readonly IClipboardService _clipboardService;
private readonly ManualResetEventSlim _connectedEvent;
private readonly IDialogService _dialogService;
private readonly IKeyboardCommandService _keyboardCommandService;
private readonly IDispatcherTimer _resizeOverlayTimer;
private readonly ISettingsService _settingsService;
private readonly ShellProfile _shellProfile;
private readonly string _startupDirectory;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
private bool _isSelected;
private bool _newOutput;
private string _resizeOverlayContent;
private string _searchText;
private bool _showResizeOverlay;
private bool _showSearchPanel;
private TabTheme _tabTheme;
private int _terminalId;
private ITerminalView _terminalView;
private string _title;
private IWebSocketConnection _webSocket;
public TerminalViewModel(ISettingsService settingsService, ITrayProcessCommunicationService trayProcessCommunicationService, IDialogService dialogService,
IKeyboardCommandService keyboardCommandService, ApplicationSettings applicationSettings, string startupDirectory, ShellProfile shellProfile,
IApplicationView applicationView, IDispatcherTimer dispatcherTimer, IClipboardService clipboardService)
{
Title = DefaultTitle;
_connectedEvent = new ManualResetEventSlim(false);
_settingsService = settingsService;
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
_settingsService.KeyBindingsChanged += OnKeyBindingsChanged;
_trayProcessCommunicationService = trayProcessCommunicationService;
_trayProcessCommunicationService.TerminalExited += OnTerminalExited;
_dialogService = dialogService;
_keyboardCommandService = keyboardCommandService;
ApplicationSettings = applicationSettings;
_startupDirectory = startupDirectory;
_shellProfile = shellProfile;
_applicationView = applicationView;
_clipboardService = clipboardService;
TabThemes = new ObservableCollection<TabTheme>(_settingsService.GetTabThemes());
TabTheme = TabThemes.FirstOrDefault(t => t.Id == _shellProfile.TabThemeId);
_resizeOverlayTimer = dispatcherTimer;
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
CloseCommand = new RelayCommand(async () => await TryClose().ConfigureAwait(false));
FindNextCommand = new RelayCommand(async () => await FindNext().ConfigureAwait(false));
FindPreviousCommand = new RelayCommand(async () => await FindPrevious().ConfigureAwait(false));
CloseSearchPanelCommand = new RelayCommand(CloseSearchPanel);
SelectTabThemeCommand = new RelayCommand<string>(SelectTabTheme);
}
public event EventHandler Closed;
public ApplicationSettings ApplicationSettings { get; private set; }
public TabTheme BackgroundTabTheme
{
// The effective background theme depends on whether it is selected (use the theme), or if it is inactive
// (if we're set to underline inactive tabs, use the null theme).
get => IsSelected || (!IsSelected && ApplicationSettings.InactiveTabColorMode == InactiveTabColorMode.Background) ?
_tabTheme :
_settingsService.GetTabThemes().FirstOrDefault(t => t.Color == null);
}
public RelayCommand CloseCommand { get; }
public RelayCommand CloseSearchPanelCommand { get; }
public string DefaultTitle { get; private set; } = string.Empty;
public RelayCommand FindNextCommand { get; }
public RelayCommand FindPreviousCommand { get; }
public bool Initialized { get; private set; }
public bool IsSelected
{
get => _isSelected;
set
{
if (Set(ref _isSelected, value))
{
if (IsSelected)
{
_applicationView.Title = Title;
_applicationView.RunOnDispatcherThread(() => NewOutput = false);
}
RaisePropertyChanged(nameof(IsUnderlined));
RaisePropertyChanged(nameof(BackgroundTabTheme));
}
}
}
public bool IsUnderlined => (IsSelected && ApplicationSettings.UnderlineSelectedTab) ||
(!IsSelected && ApplicationSettings.InactiveTabColorMode == InactiveTabColorMode.Underlined && TabTheme.Color != null);
public bool NewOutput
{
get => _newOutput;
set => Set(ref _newOutput, value);
}
public string ResizeOverlayContent
{
get => _resizeOverlayContent;
set => Set(ref _resizeOverlayContent, value);
}
public string SearchText
{
get => _searchText;
set => Set(ref _searchText, value);
}
public RelayCommand<string> SelectTabThemeCommand { get; }
public bool ShowResizeOverlay
{
get => _showResizeOverlay;
set
{
Set(ref _showResizeOverlay, value);
if (value)
{
if (_resizeOverlayTimer.IsEnabled)
{
_resizeOverlayTimer.Stop();
}
_resizeOverlayTimer.Start();
}
}
}
public bool ShowSearchPanel
{
get => _showSearchPanel;
set => Set(ref _showSearchPanel, value);
}
public TabTheme TabTheme
{
get => _tabTheme;
set
{
Set(ref _tabTheme, value);
RaisePropertyChanged(nameof(IsUnderlined));
RaisePropertyChanged(nameof(BackgroundTabTheme));
}
}
public ObservableCollection<TabTheme> TabThemes { get; }
public string Title
{
get => _title;
set
{
value = string.IsNullOrWhiteSpace(value) ? DefaultTitle : value;
if (Set(ref _title, value) && IsSelected)
{
_applicationView.Title = Title;
}
}
}
public async Task CloseView()
{
_terminalView.Close();
await _trayProcessCommunicationService.CloseTerminal(_terminalId).ConfigureAwait(true);
}
public void CopyText(string text)
{
_clipboardService.SetText(text);
}
public Task FocusTerminal()
{
return _terminalView?.FocusTerminal();
}
public async Task OnViewIsReady(ITerminalView terminalView)
{
_terminalView = terminalView;
var options = _settingsService.GetTerminalOptions();
var theme = _settingsService.GetCurrentTheme();
var keyBindings = _settingsService.GetKeyBindings();
var size = await _terminalView.CreateTerminal(options, theme.Colors, FlattenKeyBindings(keyBindings)).ConfigureAwait(true);
if (!string.IsNullOrWhiteSpace(_startupDirectory))
{
_shellProfile.WorkingDirectory = _startupDirectory;
}
var response = await _trayProcessCommunicationService.CreateTerminal(size, _shellProfile).ConfigureAwait(true);
if (response.Success)
{
_terminalId = response.Id;
_terminalView.TerminalSizeChanged += OnTerminalSizeChanged;
_terminalView.TerminalTitleChanged += OnTerminalTitleChanged;
_terminalView.KeyboardCommandReceived += OnKeyboardCommandReceived;
_trayProcessCommunicationService.SubscribeForTerminalOutput(_terminalId, t =>
{
_connectedEvent.Wait();
_webSocket.Send(t);
if (!IsSelected && ApplicationSettings.ShowNewOutputIndicator)
{
_applicationView.RunOnDispatcherThread(() => NewOutput = true);
}
});
DefaultTitle = response.ShellExecutableName;
Title = DefaultTitle;
var webSocketUrl = "ws://127.0.0.1:" + response.Id;
var webSocketServer = new WebSocketServer(webSocketUrl);
webSocketServer.Start(socket =>
{
_webSocket = socket;
socket.OnOpen = () => _connectedEvent.Set();
socket.OnMessage = message => _trayProcessCommunicationService.WriteText(_terminalId, message);
});
await _terminalView.ConnectToSocket(webSocketUrl).ConfigureAwait(true);
Initialized = true;
}
else
{
await _dialogService.ShowMessageDialogAsnyc("Error", response.Error, DialogButton.OK).ConfigureAwait(true);
}
await FocusTerminal().ConfigureAwait(true);
}
private void CloseSearchPanel()
{
SearchText = string.Empty;
ShowSearchPanel = false;
_terminalView.FocusTerminal();
}
private Task FindNext()
{
return _terminalView.FindNext(SearchText);
}
private Task FindPrevious()
{
return _terminalView.FindPrevious(SearchText);
}
private IEnumerable<KeyBinding> FlattenKeyBindings(IDictionary<Command, ICollection<KeyBinding>> keyBindings)
{
return keyBindings.Values.SelectMany(k => k);
}
private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
{
await _applicationView.RunOnDispatcherThread(() =>
{
ApplicationSettings = e;
RaisePropertyChanged(nameof(IsUnderlined));
RaisePropertyChanged(nameof(BackgroundTabTheme));
});
}
private async void OnCurrentThemeChanged(object sender, Guid e)
{
await _applicationView.RunOnDispatcherThread(async () =>
{
RaisePropertyChanged(nameof(IsUnderlined));
RaisePropertyChanged(nameof(BackgroundTabTheme));
var currentTheme = _settingsService.GetTheme(e);
await _terminalView.ChangeTheme(currentTheme.Colors).ConfigureAwait(true);
});
}
private async void OnKeyBindingsChanged(object sender, EventArgs e)
{
var keyBindings = _settingsService.GetKeyBindings();
await _applicationView.RunOnDispatcherThread(async () =>
{
await _terminalView.ChangeKeyBindings(FlattenKeyBindings(keyBindings)).ConfigureAwait(false);
});
}
private async void OnKeyboardCommandReceived(object sender, Command e)
{
if (e == Command.Copy)
{
var selection = await _terminalView.GetSelection().ConfigureAwait(true);
_clipboardService.SetText(selection);
}
else if (e == Command.Paste)
{
var content = await _clipboardService.GetText().ConfigureAwait(true);
if (content != null)
{
await _trayProcessCommunicationService.WriteText(_terminalId, content).ConfigureAwait(true);
}
}
else if (e == Command.Search)
{
ShowSearchPanel = !ShowSearchPanel;
if (ShowSearchPanel)
{
_terminalView.FocusSearchTextBox();
}
}
else
{
_keyboardCommandService.SendCommand(e);
}
}
private void OnResizeOverlayTimerFinished(object sender, object e)
{
_resizeOverlayTimer.Stop();
ShowResizeOverlay = false;
}
private void OnTerminalExited(object sender, int e)
{
if (e == _terminalId)
{
_applicationView.RunOnDispatcherThread(() => Closed?.Invoke(this, EventArgs.Empty));
}
}
private async void OnTerminalOptionsChanged(object sender, TerminalOptions e)
{
await _applicationView.RunOnDispatcherThread(async () =>
{
await _terminalView.ChangeOptions(e).ConfigureAwait(true);
});
}
private async void OnTerminalSizeChanged(object sender, TerminalSize e)
{
if (!Initialized)
{
return;
}
ResizeOverlayContent = $"{e.Columns} x {e.Rows}";
ShowResizeOverlay = true;
await _trayProcessCommunicationService.ResizeTerminal(_terminalId, e).ConfigureAwait(true);
}
private void OnTerminalTitleChanged(object sender, string e)
{
Title = e;
}
private void SelectTabTheme(string name)
{
TabTheme = TabThemes.FirstOrDefault(t => t.Name == name);
}
private async Task TryClose()
{
if (ApplicationSettings.ConfirmClosingTabs)
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to close this tab?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.Cancel)
{
return;
}
}
await _trayProcessCommunicationService.CloseTerminal(_terminalId).ConfigureAwait(true);
Closed?.Invoke(this, EventArgs.Empty);
}
}
}
@@ -0,0 +1,374 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Utilities;
using FluentTerminal.Models;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels
{
public class ThemeViewModel : ViewModelBase
{
private readonly IDialogService _dialogService;
private readonly ISettingsService _settingsService;
private string _author;
private string _background;
private double _backgroundOpacity;
private string _black;
private string _blue;
private string _brightBlack;
private string _brightBlue;
private string _brightCyan;
private string _brightGreen;
private string _brightMagenta;
private string _brightRed;
private string _brightWhite;
private string _brightYellow;
private string _cursor;
private string _cursorAccent;
private string _cyan;
private string _fallBackAuthor;
private TerminalColors _fallBackColors;
private string _fallBackName;
private string _foreground;
private string _green;
private bool _inEditMode;
private bool _isActive;
private string _magenta;
private string _name;
private string _red;
private string _selection;
private readonly TerminalTheme _theme;
private string _white;
private string _yellow;
private readonly IFileSystemService _fileSystemService;
public event EventHandler<string> BackgroundChanged;
public ThemeViewModel(TerminalTheme theme, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService)
{
_theme = theme;
_settingsService = settingsService;
_dialogService = dialogService;
_fileSystemService = fileSystemService;
Name = _theme.Name;
Author = _theme.Author;
Id = _theme.Id;
Black = _theme.Colors.Black;
Red = _theme.Colors.Red;
Green = _theme.Colors.Green;
Yellow = _theme.Colors.Yellow;
Blue = _theme.Colors.Blue;
Magenta = _theme.Colors.Magenta;
Cyan = _theme.Colors.Cyan;
White = _theme.Colors.White;
BrightBlack = _theme.Colors.BrightBlack;
BrightRed = _theme.Colors.BrightRed;
BrightGreen = _theme.Colors.BrightGreen;
BrightYellow = _theme.Colors.BrightYellow;
BrightBlue = _theme.Colors.BrightBlue;
BrightMagenta = _theme.Colors.BrightMagenta;
BrightCyan = _theme.Colors.BrightCyan;
BrightWhite = _theme.Colors.BrightWhite;
Background = _theme.Colors.Background;
Foreground = _theme.Colors.Foreground;
Cursor = _theme.Colors.Cursor;
CursorAccent = _theme.Colors.CursorAccent;
Selection = _theme.Colors.Selection;
SetActiveCommand = new RelayCommand(SetActive);
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false), NotPreInstalled);
EditCommand = new RelayCommand(Edit, NotPreInstalled);
CancelEditCommand = new RelayCommand(async () => await CancelEdit().ConfigureAwait(false));
SaveChangesCommand = new RelayCommand(SaveChanges);
ExportCommand = new RelayCommand(async () => await Export().ConfigureAwait(false), NotPreInstalled);
}
public event EventHandler Activated;
public event EventHandler Deleted;
public string Author
{
get => _author;
set => Set(ref _author, value);
}
public string Background
{
get => _background;
set
{
Set(ref _background, value);
BackgroundChanged?.Invoke(this, value);
}
}
public double BackgroundOpacity
{
get => _backgroundOpacity;
set => Set(ref _backgroundOpacity, value);
}
public string Black
{
get => _black;
set => Set(ref _black, value);
}
public string Blue
{
get => _blue;
set => Set(ref _blue, value);
}
public string BrightBlack
{
get => _brightBlack;
set => Set(ref _brightBlack, value);
}
public string BrightBlue
{
get => _brightBlue;
set => Set(ref _brightBlue, value);
}
public string BrightCyan
{
get => _brightCyan;
set => Set(ref _brightCyan, value);
}
public string BrightGreen
{
get => _brightGreen;
set => Set(ref _brightGreen, value);
}
public string BrightMagenta
{
get => _brightMagenta;
set => Set(ref _brightMagenta, value);
}
public string BrightRed
{
get => _brightRed;
set => Set(ref _brightRed, value);
}
public string BrightWhite
{
get => _brightWhite;
set => Set(ref _brightWhite, value);
}
public string BrightYellow
{
get => _brightYellow;
set => Set(ref _brightYellow, value);
}
public RelayCommand CancelEditCommand { get; }
public string Cursor
{
get => _cursor;
set => Set(ref _cursor, value);
}
public string CursorAccent
{
get => _cursorAccent;
set => Set(ref _cursorAccent, value);
}
public string Cyan
{
get => _cyan;
set => Set(ref _cyan, value);
}
public RelayCommand DeleteCommand { get; }
public RelayCommand EditCommand { get; }
public RelayCommand ExportCommand { get; }
public string Foreground
{
get => _foreground;
set => Set(ref _foreground, value);
}
public string Green
{
get => _green;
set => Set(ref _green, value);
}
public Guid Id { get; }
public bool InEditMode
{
get => _inEditMode;
set => Set(ref _inEditMode, value);
}
public bool IsActive
{
get => _isActive;
set => Set(ref _isActive, value);
}
public string Magenta
{
get => _magenta;
set => Set(ref _magenta, value);
}
public string Name
{
get => _name;
set => Set(ref _name, value);
}
public string Red
{
get => _red;
set => Set(ref _red, value);
}
public RelayCommand SaveChangesCommand { get; }
public string Selection
{
get => _selection;
set => Set(ref _selection, value);
}
public RelayCommand SetActiveCommand { get; }
public string White
{
get => _white;
set => Set(ref _white, value);
}
public string Yellow
{
get => _yellow;
set => Set(ref _yellow, value);
}
public void SaveChanges()
{
_theme.Name = Name;
_theme.Author = Author;
_theme.Colors.Black = Black;
_theme.Colors.Red = Red;
_theme.Colors.Green = Green;
_theme.Colors.Yellow = Yellow;
_theme.Colors.Blue = Blue;
_theme.Colors.Magenta = Magenta;
_theme.Colors.Cyan = Cyan;
_theme.Colors.White = White;
_theme.Colors.BrightBlack = BrightBlack;
_theme.Colors.BrightRed = BrightRed;
_theme.Colors.BrightGreen = BrightGreen;
_theme.Colors.BrightYellow = BrightYellow;
_theme.Colors.BrightBlue = BrightBlue;
_theme.Colors.BrightMagenta = BrightMagenta;
_theme.Colors.BrightCyan = BrightCyan;
_theme.Colors.BrightWhite = BrightWhite;
_theme.Colors.Background = Background;
_theme.Colors.Foreground = Foreground;
_theme.Colors.Cursor = Cursor;
_theme.Colors.CursorAccent = CursorAccent;
_theme.Colors.Selection = Selection;
_settingsService.SaveTheme(_theme);
InEditMode = false;
}
private async Task CancelEdit()
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to discard all changes?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.OK)
{
Black = _fallBackColors.Black;
Red = _fallBackColors.Red;
Green = _fallBackColors.Green;
Yellow = _fallBackColors.Yellow;
Blue = _fallBackColors.Blue;
Magenta = _fallBackColors.Magenta;
Cyan = _fallBackColors.Cyan;
White = _fallBackColors.White;
BrightBlack = _fallBackColors.BrightBlack;
BrightRed = _fallBackColors.BrightRed;
BrightGreen = _fallBackColors.BrightGreen;
BrightYellow = _fallBackColors.BrightYellow;
BrightBlue = _fallBackColors.BrightBlue;
BrightMagenta = _fallBackColors.BrightMagenta;
BrightCyan = _fallBackColors.BrightCyan;
BrightWhite = _fallBackColors.BrightWhite;
Background = _fallBackColors.Background;
Foreground = _fallBackColors.Foreground;
Cursor = _fallBackColors.Cursor;
CursorAccent = _fallBackColors.CursorAccent;
Selection = _fallBackColors.Selection;
Name = _fallBackName;
Author = _fallBackAuthor;
InEditMode = false;
}
}
private bool NotPreInstalled()
{
return !_theme.PreInstalled;
}
private async Task Delete()
{
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this theme?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
if (result == DialogButton.OK)
{
Deleted?.Invoke(this, EventArgs.Empty);
}
}
private void Edit()
{
_fallBackColors = new TerminalColors(_theme.Colors);
_fallBackName = Name;
_fallBackAuthor = Author;
InEditMode = true;
}
private void SetActive()
{
Activated?.Invoke(this, EventArgs.Empty);
}
private Task Export()
{
var content = JsonConvert.SerializeObject(_theme, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new TerminalThemeContractResolver() });
return _fileSystemService.SaveTextFile(Name, "Fluent Terminal Theme", ".flutecolors", content);
}
}
}
@@ -0,0 +1,23 @@
using FluentTerminal.App.ViewModels.Infrastructure;
using System;
using System.Threading.Tasks;
namespace FluentTerminal.App.ViewModels.Utilities
{
public static class TaskUtilities
{
#pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void
public static async void FireAndForgetSafeAsync(this Task task, IErrorHandler handler = null)
#pragma warning restore RECS0165 // Asynchronous methods should return a Task instead of void
{
try
{
await task;
}
catch (Exception ex)
{
handler?.HandleError(ex);
}
}
}
}
@@ -0,0 +1,58 @@
using FluentTerminal.App.Services;
using FluentTerminal.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
using Windows.Foundation.Collections;
namespace FluentTerminal.App.Adapters
{
public class AppServiceConnectionAdapter : IAppServiceConnection
{
private readonly AppServiceConnection _appServiceConnection;
public event EventHandler<IDictionary<string, string>> MessageReceived;
public AppServiceConnectionAdapter(AppServiceConnection appServiceConnection)
{
_appServiceConnection = appServiceConnection;
_appServiceConnection.RequestReceived += OnRequestReceived;
}
private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
var messageType = (string)args.Request.Message[MessageKeys.Type];
var messageContent = (string)args.Request.Message[MessageKeys.Content];
MessageReceived?.Invoke(this, new Dictionary<string, string>
{
[MessageKeys.Type] = messageType,
[MessageKeys.Content] = messageContent
});
}
public async Task<IDictionary<string, string>> SendMessageAsync(IDictionary<string, string> message)
{
var valueSet = new ValueSet();
foreach (var pair in message)
{
valueSet.Add(pair.Key, pair.Value);
}
var response = await _appServiceConnection.SendMessageAsync(valueSet).AsTask();
if (response.Status == AppServiceResponseStatus.Success)
{
var responseDict = new Dictionary<string, string>();
foreach (var pair in response.Message)
{
responseDict.Add(pair.Key, (string)pair.Value);
}
return responseDict;
}
return null;
}
}
}
@@ -0,0 +1,67 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.EventArgs;
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
using Windows.UI.Core.Preview;
using Windows.UI.ViewManagement;
namespace FluentTerminal.App.Adapters
{
public class ApplicationViewAdapter : IApplicationView
{
private readonly ApplicationView _applicationView;
private readonly CoreDispatcher _dispatcher;
public event CloseRequestedHandler CloseRequested;
public ApplicationViewAdapter()
{
_applicationView = ApplicationView.GetForCurrentView();
_dispatcher = CoreApplication.GetCurrentView().Dispatcher;
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
}
public string Title
{
get => _applicationView.Title;
set => _applicationView.Title = value;
}
public Task RunOnDispatcherThread(Action action)
{
return _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask();
}
public Task<bool> TryClose()
{
return _applicationView.TryConsolidateAsync().AsTask();
}
public bool ToggleFullScreen()
{
if (_applicationView.IsFullScreenMode)
{
_applicationView.ExitFullScreenMode();
return true;
}
else
{
return _applicationView.TryEnterFullScreenMode();
}
}
private async void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
{
var deferral = e.GetDeferral();
var args = new CancelableEventArgs();
await CloseRequested?.Invoke(this, args);
e.Handled = args.Cancelled;
deferral.Complete();
}
}
}
@@ -0,0 +1,42 @@
using FluentTerminal.App.Services;
using System;
using Windows.UI.Xaml;
namespace FluentTerminal.App.Adapters
{
public class DispatcherTimerAdapter : IDispatcherTimer
{
private readonly DispatcherTimer _dispatcherTimer;
public DispatcherTimerAdapter()
{
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick += OnDispatcherTimerTick;
}
private void OnDispatcherTimerTick(object sender, object e)
{
Tick?.Invoke(this, null);
}
public TimeSpan Interval
{
get => _dispatcherTimer.Interval;
set => _dispatcherTimer.Interval = value;
}
public bool IsEnabled => _dispatcherTimer.IsEnabled;
public event EventHandler<object> Tick;
public void Start()
{
_dispatcherTimer.Start();
}
public void Stop()
{
_dispatcherTimer.Stop();
}
}
}
@@ -0,0 +1,42 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Dialogs;
using System;
using System.Threading.Tasks;
using Windows.UI.Popups;
namespace FluentTerminal.App.Adapters
{
public class MessageDialogAdapter : IMessageDialog
{
private readonly MessageDialog _messageDialog;
public MessageDialogAdapter()
{
_messageDialog = new MessageDialog(string.Empty);
}
public string Title
{
get => _messageDialog.Title;
set => _messageDialog.Title = value;
}
public string Content
{
get => _messageDialog.Content;
set => _messageDialog.Content = value;
}
public void AddButton(DialogButton button)
{
_messageDialog.Commands.Add(new UICommand(button.ToString()));
}
public async Task<DialogButton> ShowAsync()
{
var result = await _messageDialog.ShowAsync();
return Enum.Parse<DialogButton>(result.Label);
}
}
}
+32 -2
View File
@@ -1,12 +1,42 @@
<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>
<LinearGradientBrush x:Key="MissingColorSelectionBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0" Color="Black" />
<GradientStop Offset="1.0" Color="White" />
</LinearGradientBrush>
<converters:IntToVisibilityConverter
x:Key="OneToVisibleConverter"
Else="Collapsed"
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:TabColorFallbackConverter x:Key="TabColorFallbackConverter" />
<converters:IntToVisibilityConverter
x:Key="ZeroToCollapsedConverter"
Else="Visible"
If="0"
Then="Collapsed" />
<converters:IntToExtendedVirtualKeyConverter x:Key="IntToExtendedVirtualKeyConverter" />
<converters:BackgroundToApplicationThemeConverter x:Key="BackgroundToApplicationThemeConverter" />
<converters:StringToColorConverter x:Key="StringToColorConverter" />
<converters:EnumValueToVisibiltyConverter x:Key="EnumValueToVisibiltyConverter" />
<converters:TabThemeSelectedConverter x:Key="TabThemeSelectedConverter" />
<converters:ColorResourceKeyFallbackConverter x:Key="ColorResourceKeyFallbackConverter" />
</ResourceDictionary>
</Application.Resources>
</Application>
</Application>
+181 -81
View File
@@ -1,133 +1,188 @@
using Autofac;
using FluentTerminal.App.Adapters;
using FluentTerminal.App.Dialogs;
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Adapters;
using FluentTerminal.App.Services.Dialogs;
using FluentTerminal.App.Services.Implementation;
using FluentTerminal.App.ViewModels;
using FluentTerminal.App.Views;
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.Core;
using Windows.Storage;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using IContainer = Autofac.IContainer;
namespace FluentTerminal.App
{
sealed partial class App : Application
public sealed partial class App : Application
{
public static BackgroundTaskDeferral AppServiceDeferral = null;
public static AppServiceConnection Connection = null;
public static App Instance;
public TaskCompletionSource<int> _trayReady = new TaskCompletionSource<int>();
private readonly ISettingsService _settingsService;
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
private bool _alreadyLaunched;
private IContainer _container;
private int _openTerminalWindows;
private ApplicationSettings _applicationSettings;
private readonly IContainer _container;
private readonly List<MainViewModel> _mainViewModels;
private SettingsViewModel _settingsViewModel;
private int? _settingsWindowId;
private IAppServiceConnection _appServiceConnection;
private BackgroundTaskDeferral _appServiceDeferral;
public App()
{
_mainViewModels = new List<MainViewModel>();
InitializeComponent();
Instance = this;
var applicationDataContainers = new ApplicationDataContainers
{
LocalSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings),
RoamingSettings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings),
KeyBindings = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.KeyBindingsContainerName, ApplicationDataCreateDisposition.Always)),
ShellProfiles = new ApplicationDataContainerAdapter(ApplicationData.Current.LocalSettings.CreateContainer(Constants.ShellProfilesContainerName, ApplicationDataCreateDisposition.Always)),
Themes = new ApplicationDataContainerAdapter(ApplicationData.Current.RoamingSettings.CreateContainer(Constants.ThemesContainerName, ApplicationDataCreateDisposition.Always))
};
var builder = new ContainerBuilder();
builder.RegisterType<SettingsService>().As<ISettingsService>().InstancePerDependency();
builder.RegisterType<SettingsService>().As<ISettingsService>().SingleInstance();
builder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>().SingleInstance();
builder.RegisterType<TerminalService>().As<ITerminalService>().SingleInstance();
builder.RegisterType<TrayProcessCommunicationService>().As<ITrayProcessCommunicationService>().SingleInstance();
builder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
builder.RegisterType<KeyboardCommandService>().As<IKeyboardCommandService>().InstancePerDependency();
builder.RegisterType<MainViewModel>().InstancePerDependency();
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
builder.RegisterType<ThemeParserFactory>().As<IThemeParserFactory>().SingleInstance();
builder.RegisterType<ITermThemeParser>().As<IThemeParser>().SingleInstance();
builder.RegisterType<FluentTerminalThemeParser>().As<IThemeParser>().SingleInstance();
builder.RegisterType<ClipboardService>().As<IClipboardService>().SingleInstance();
builder.RegisterType<FileSystemService>().As<IFileSystemService>().SingleInstance();
builder.RegisterType<SystemFontService>().As<ISystemFontService>().SingleInstance();
builder.RegisterType<ShellProfileSelectionDialog>().As<IShellProfileSelectionDialog>().InstancePerDependency();
builder.RegisterType<CreateKeyBindingDialog>().As<ICreateKeyBindingDialog>().InstancePerDependency();
builder.RegisterType<MessageDialogAdapter>().As<IMessageDialog>().InstancePerDependency();
builder.RegisterType<ApplicationViewAdapter>().As<IApplicationView>().InstancePerDependency();
builder.RegisterType<DispatcherTimerAdapter>().As<IDispatcherTimer>().InstancePerDependency();
builder.RegisterType<StartupTaskService>().As<IStartupTaskService>().SingleInstance();
builder.RegisterInstance(applicationDataContainers);
_container = builder.Build();
}
public async Task CreateNewTerminalWindow()
{
var id = await CreateSecondaryView(typeof(MainPage), true);
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
}
_settingsService = _container.Resolve<ISettingsService>();
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
public void SettingsWindowClosed()
{
_settingsWindowId = null;
}
_trayProcessCommunicationService = _container.Resolve<ITrayProcessCommunicationService>();
public async Task ShowSettings()
{
if (_settingsWindowId == null)
{
_settingsWindowId = await CreateSecondaryView(typeof(SettingsPage), true);
}
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
}
public void TerminalWindowClosed()
{
_openTerminalWindows--;
_applicationSettings = _settingsService.GetApplicationSettings();
}
protected override async void OnActivated(IActivatedEventArgs args)
{
if (args is CommandLineActivatedEventArgs commandLineActivated)
{
var argument = commandLineActivated.Operation.Arguments.Trim();
if (string.IsNullOrWhiteSpace(commandLineActivated.Operation.Arguments))
{
return;
}
var arguments = commandLineActivated.Operation.Arguments.Split(' ', 2);
var command = arguments[0];
var parameter = arguments.Length > 1 ? arguments[1] : string.Empty;
if (_alreadyLaunched)
{
if (argument == "settings")
if (command == "settings")
{
await ShowSettings();
await ShowSettings().ConfigureAwait(true);
}
else if (argument == "new")
else if (command == "new")
{
await CreateNewTerminalWindow();
if (_applicationSettings.NewTerminalLocation == NewTerminalLocation.Tab && _mainViewModels.Count > 0)
{
await _mainViewModels.Last().AddTerminal(parameter, false).ConfigureAwait(true);
}
else
{
await CreateNewTerminalWindow(parameter).ConfigureAwait(true);
}
}
}
else
{
if (argument == "settings")
if (command == "settings")
{
CreateMainView(typeof(SettingsPage), true);
var viewModel = _container.Resolve<SettingsViewModel>();
await CreateMainView(typeof(SettingsPage), viewModel, true).ConfigureAwait(true);
}
else if (argument == "new")
else if (command == "new")
{
CreateMainView(typeof(MainPage), true);
var viewModel = _container.Resolve<MainViewModel>();
await viewModel.AddTerminal(parameter, false).ConfigureAwait(true);
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
}
}
}
}
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
if (!_alreadyLaunched)
{
var viewModel = _container.Resolve<MainViewModel>();
await viewModel.AddTerminal(null, false).ConfigureAwait(true);
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
Window.Current.Activate();
}
else if (_mainViewModels.Count == 0)
{
await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, string.Empty).ConfigureAwait(true);
}
}
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details)
{
AppServiceDeferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Canceled += OnTaskCanceled;
Connection = details.AppServiceConnection;
Connection.RequestReceived += OnRequestReceived;
if (details.CallerPackageFamilyName == Package.Current.Id.FamilyName)
{
_appServiceDeferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Canceled += OnTaskCanceled;
_appServiceConnection = new AppServiceConnectionAdapter(details.AppServiceConnection);
_trayReady.SetResult(0);
}
}
}
protected override async void OnLaunched(LaunchActivatedEventArgs e)
private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync().AsTask();
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
await Task.WhenAll(launch, clearCache);
_appServiceDeferral?.Complete();
_appServiceDeferral = null;
_appServiceConnection = null;
if (!_alreadyLaunched)
{
CreateMainView(typeof(MainPage), true);
Window.Current.Activate();
}
else if (_openTerminalWindows == 0)
{
await CreateSecondaryView(typeof(MainPage), true);
}
Application.Current.Exit();
}
private void CreateMainView(Type pageType, bool extendViewIntoTitleBar)
private async Task CreateMainView(Type pageType, INotifyPropertyChanged viewModel, bool extendViewIntoTitleBar)
{
await StartSystemTray().ConfigureAwait(true);
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
@@ -138,66 +193,111 @@ namespace FluentTerminal.App
if (rootFrame.Content == null)
{
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = extendViewIntoTitleBar;
rootFrame.Navigate(pageType, GetViewModelForPageType(pageType));
if (pageType == typeof(MainPage))
if (viewModel is MainViewModel mainViewModel)
{
_openTerminalWindows++;
mainViewModel.Closed += OnMainViewModelClosed;
mainViewModel.NewWindowRequested += OnNewWindowRequested;
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
_mainViewModels.Add(mainViewModel);
}
rootFrame.Navigate(pageType, viewModel);
}
_alreadyLaunched = true;
Window.Current.Activate();
}
private async Task<int> CreateSecondaryView(Type pageType, bool ExtendViewIntoTitleBar)
private async Task CreateNewTerminalWindow(string startupDirectory)
{
var id = await CreateSecondaryView<MainViewModel>(typeof(MainPage), true, startupDirectory).ConfigureAwait(true);
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(id);
}
private async Task<int> CreateSecondaryView<TViewModel>(Type pageType, bool ExtendViewIntoTitleBar, object parameter)
{
int windowId = 0;
TViewModel viewModel = default;
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
viewModel = _container.Resolve<TViewModel>();
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = ExtendViewIntoTitleBar;
var frame = new Frame();
frame.Navigate(pageType, GetViewModelForPageType(pageType));
frame.Navigate(pageType, viewModel);
Window.Current.Content = frame;
Window.Current.Activate();
windowId = ApplicationView.GetForCurrentView().Id;
});
if (pageType == typeof(MainPage))
if (viewModel is MainViewModel mainViewModel && parameter is string directory)
{
_openTerminalWindows++;
mainViewModel.Closed += OnMainViewModelClosed;
mainViewModel.NewWindowRequested += OnNewWindowRequested;
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
_mainViewModels.Add(mainViewModel);
await mainViewModel.AddTerminal(directory, false).ConfigureAwait(true);
}
if (viewModel is SettingsViewModel settingsViewModel)
{
_settingsViewModel = settingsViewModel;
_settingsViewModel.Closed += OnSettingsClosed;
}
return windowId;
}
private object GetViewModelForPageType(Type pageType)
private void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
{
if (pageType == typeof(MainPage))
{
return _container.Resolve<MainViewModel>();
}
else if (pageType == typeof(SettingsPage))
{
return _container.Resolve<SettingsViewModel>();
}
throw new Exception("No ViewModel defined for Page: " + pageType.Name);
_applicationSettings = e;
}
private void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
private void OnMainViewModelClosed(object sender, EventArgs e)
{
if (args.Request.Message.ContainsKey("exit"))
if (sender is MainViewModel viewModel)
{
App.Current.Exit();
viewModel.Closed -= OnMainViewModelClosed;
viewModel.NewWindowRequested -= OnNewWindowRequested;
viewModel.ShowSettingsRequested -= OnShowSettingsRequested;
_mainViewModels.Remove(viewModel);
}
}
private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
private async void OnNewWindowRequested(object sender, EventArgs e)
{
if (AppServiceDeferral != null)
await CreateNewTerminalWindow(string.Empty).ConfigureAwait(true);
}
private void OnSettingsClosed(object sender, EventArgs e)
{
_settingsViewModel.Closed -= OnSettingsClosed;
_settingsViewModel = null;
_settingsWindowId = null;
}
private async void OnShowSettingsRequested(object sender, EventArgs e)
{
await ShowSettings().ConfigureAwait(true);
}
private async Task ShowSettings()
{
if (_settingsViewModel == null)
{
AppServiceDeferral.Complete();
_settingsWindowId = await CreateSecondaryView<SettingsViewModel>(typeof(SettingsPage), true, null).ConfigureAwait(true);
}
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(_settingsWindowId.Value);
}
private async Task StartSystemTray()
{
var launch = FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("AppLaunchedParameterGroup").AsTask();
var clearCache = WebView.ClearTemporaryWebDataAsync().AsTask();
await Task.WhenAll(launch, clearCache, _trayReady.Task).ConfigureAwait(true);
_trayProcessCommunicationService.Initialize(_appServiceConnection);
}
}
}
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,36 @@
using FluentTerminal.App.Utilities;
using Microsoft.Toolkit.Uwp.Helpers;
using System;
using Windows.UI;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class BackgroundToApplicationThemeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
Color color;
if (value is string colorString)
{
color = colorString.ToColor();
}
else if (value is Color)
{
color = (Color)value;
}
if (color != null)
{
return ContrastHelper.GetIdealThemeForBackgroundColor(color);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,30 @@
using Microsoft.Toolkit.Uwp.Helpers;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace FluentTerminal.App.Converters
{
public class ColorResourceKeyFallbackConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
{
return Application.Current.Resources[(string)parameter];
}
else if (value is string hex)
{
var color = hex.ToColor();
return new SolidColorBrush(color);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,23 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class EnumValueToVisibiltyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value.GetType().IsEnum && Enum.TryParse(value.GetType(), parameter.ToString(), true, out object result))
{
return Equals(value, result) ? Visibility.Visible : Visibility.Collapsed;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -24,4 +24,4 @@ namespace FluentTerminal.App.Converters
return null;
}
}
}
}
@@ -0,0 +1,24 @@
using FluentTerminal.App.Services.Utilities;
using FluentTerminal.Models.Enums;
using System;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class IntToExtendedVirtualKeyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is int intValue)
{
return EnumHelper.GetEnumDescription((ExtendedVirtualKey)intValue);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,50 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class IntToVisibilityConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty ElseProperty =
DependencyProperty.Register(nameof(Else), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Collapsed));
public static readonly DependencyProperty IfProperty =
DependencyProperty.Register(nameof(If), typeof(int), typeof(IntToVisibilityConverter), new PropertyMetadata(0));
public static readonly DependencyProperty ThenProperty =
DependencyProperty.Register(nameof(Then), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Visible));
public Visibility Else
{
get { return (Visibility)GetValue(ElseProperty); }
set { SetValue(ElseProperty, value); }
}
public int If
{
get { return (int)GetValue(IfProperty); }
set { SetValue(IfProperty, value); }
}
public Visibility Then
{
get { return (Visibility)GetValue(ThenProperty); }
set { SetValue(ThenProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is int count)
{
return count == If ? Then : Else;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,22 @@
using System;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class NegateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is bool boolean)
{
return !boolean;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,29 @@
using FluentTerminal.App.Utilities;
using System;
using Windows.UI;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is string colorString)
{
return colorString.FromString();
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is Color color && bool.TryParse((string)parameter, out bool allowTransparency))
{
return color.ToColorString(allowTransparency);
}
return null;
}
}
}
@@ -0,0 +1,82 @@
using FluentTerminal.Models;
using FluentTerminal.Models.Enums;
using Microsoft.Toolkit.Uwp.Helpers;
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
namespace FluentTerminal.App.Converters
{
public class TabColorFallbackConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is TabTheme theme && Enum.TryParse<TabThemeKey>((string)parameter, true, out TabThemeKey tabThemeKey))
{
switch (tabThemeKey)
{
case TabThemeKey.Background:
return GetBrush(tabThemeKey, theme.Color, theme.BackgroundOpacity);
case TabThemeKey.BackgroundPointerOver:
return GetBrush(tabThemeKey, theme.Color, theme.BackgroundPointerOverOpacity);
case TabThemeKey.BackgroundPressed:
return GetBrush(tabThemeKey, theme.Color, theme.BackgroundPressedOpacity);
case TabThemeKey.BackgroundSelected:
return GetBrush(tabThemeKey, theme.Color, theme.BackgroundSelectedOpacity);
case TabThemeKey.BackgroundSelectedPointerOver:
return GetBrush(tabThemeKey, theme.Color, theme.BackgroundSelectedPointerOverOpacity);
case TabThemeKey.BackgroundSelectedPressed:
return GetBrush(tabThemeKey, theme.Color, theme.BackgroundSelectedPressedOpacity);
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
private Brush GetBrush(TabThemeKey tabThemeKey, string color, double opacity)
{
if (double.IsNaN(opacity))
{
return (Brush)Application.Current.Resources[GetFallbackRessourceKey(tabThemeKey)];
}
else
{
return CreateBrush(color, opacity);
}
}
private Brush CreateBrush(string hex, double opacity)
{
var color = hex.ToColor();
return new SolidColorBrush(color)
{
Opacity = opacity
};
}
private string GetFallbackRessourceKey(TabThemeKey tabThemeKey)
{
switch (tabThemeKey)
{
case TabThemeKey.Background:
return "SystemControlTransparentBrush";
case TabThemeKey.BackgroundPointerOver:
return "SystemControlHighlightListLowBrush";
case TabThemeKey.BackgroundPressed:
return "SystemControlHighlightListMediumBrush";
case TabThemeKey.BackgroundSelected:
return "SystemControlHighlightListAccentLowBrush";
case TabThemeKey.BackgroundSelectedPointerOver:
return "SystemControlHighlightListAccentMediumBrush";
case TabThemeKey.BackgroundSelectedPressed:
return "SystemControlHighlightListAccentHighBrush";
}
return null;
}
}
}
@@ -0,0 +1,23 @@
using FluentTerminal.Models;
using System;
using Windows.UI.Xaml.Data;
namespace FluentTerminal.App.Converters
{
public class TabThemeSelectedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is TabTheme theme && parameter is string name)
{
return theme.Name == name;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}
@@ -8,24 +8,24 @@ namespace FluentTerminal.App.Converters
{
internal class TerminalViewModelToViewConverter : IValueConverter
{
private Dictionary<int, TerminalView> _viewDictionary;
private readonly Dictionary<TerminalViewModel, TerminalView> _viewDictionary;
public TerminalViewModelToViewConverter()
{
_viewDictionary = new Dictionary<int, TerminalView>();
_viewDictionary = new Dictionary<TerminalViewModel, 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))
if (_viewDictionary.TryGetValue(terminal, out TerminalView view))
{
return view;
}
var newView = new TerminalView(terminal);
_viewDictionary.Add(terminal.Id, newView);
_viewDictionary.Add(terminal, newView);
return newView;
}
return null;
@@ -24,4 +24,4 @@ namespace FluentTerminal.App.Converters
return null;
}
}
}
}
@@ -0,0 +1,79 @@
<ContentDialog
x:Class="FluentTerminal.App.Dialogs.CreateKeyBindingDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="400"
PrimaryButtonText="OK"
SecondaryButtonText="Cancel"
mc:Ignorable="d">
<StackPanel>
<TextBlock Margin="0,0,0,24" Text="Enter your keybinding now" />
<StackPanel x:Name="Root" Orientation="Horizontal">
<Grid
Width="48"
Height="32"
Margin="0,0,6,0"
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
Visibility="{x:Bind Ctrl, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="Ctrl" />
</Grid>
<Grid
Width="48"
Height="32"
Margin="0,0,6,0"
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
Visibility="{x:Bind Meta, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<FontIcon
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="/Assets/settingsicons.ttf#settingsicons"
Glyph="f" />
</Grid>
<Grid
Width="48"
Height="32"
Margin="0,0,6,0"
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
Visibility="{x:Bind Alt, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="Alt" />
</Grid>
<Grid
Width="48"
Height="32"
Margin="0,0,6,0"
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
Visibility="{x:Bind Shift, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="Shift" />
</Grid>
<Grid
Width="48"
Height="32"
Margin="0,0,6,0"
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
Visibility="{x:Bind Key, Mode=OneWay, Converter={StaticResource ZeroToCollapsedConverter}}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{x:Bind Key, Mode=OneWay, Converter={StaticResource IntToExtendedVirtualKeyConverter}}" />
</Grid>
<HyperlinkButton
x:Name="ResetButton"
Margin="24,0,0,0"
Command="{x:Bind ResetCommand}"
Content="Reset"
PreviewKeyDown="OnResetButtonPreviewKeyDown" />
</StackPanel>
</StackPanel>
</ContentDialog>
@@ -0,0 +1,135 @@
using FluentTerminal.App.Services.Dialogs;
using FluentTerminal.Models;
using GalaSoft.MvvmLight.Command;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Dialogs
{
public sealed partial class CreateKeyBindingDialog : ContentDialog, ICreateKeyBindingDialog, INotifyPropertyChanged
{
private bool _ctrl;
private bool _shift;
private bool _alt;
private bool _meta;
private int _key;
public bool Ctrl
{
get => _ctrl;
set => Set(ref _ctrl, value);
}
public bool Shift
{
get => _shift;
set => Set(ref _shift, value);
}
public bool Alt
{
get => _alt;
set => Set(ref _alt, value);
}
public bool Meta
{
get => _meta;
set => Set(ref _meta, value);
}
public int Key
{
get => _key;
set => Set(ref _key, value);
}
public CreateKeyBindingDialog()
{
InitializeComponent();
ResetCommand = new RelayCommand(Reset);
PreviewKeyDown += RegisterKeyBindingDialog_PreviewKeyDown;
Reset();
}
public RelayCommand ResetCommand { get; }
public event PropertyChangedEventHandler PropertyChanged;
public async Task<KeyBinding> CreateKeyBinding()
{
var result = await ShowAsync();
if (result == ContentDialogResult.Primary)
{
return new KeyBinding
{
Alt = Alt,
Ctrl = Ctrl,
Key = Key,
Meta = Meta,
Shift = Shift
};
}
return null;
}
private void OnResetButtonPreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
e.Handled = true;
}
private void RegisterKeyBindingDialog_PreviewKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
switch (e.Key)
{
case VirtualKey.Control:
Ctrl = true;
break;
case VirtualKey.Shift:
Shift = true;
break;
case VirtualKey.Menu:
Alt = true;
break;
case VirtualKey.LeftWindows:
case VirtualKey.RightWindows:
Meta = true;
break;
default:
Key = (int)e.Key;
break;
}
ResetButton.Visibility = Visibility.Visible;
e.Handled = true;
}
private void Reset()
{
Alt = false;
Ctrl = false;
Meta = false;
Shift = false;
Key = 0;
ResetButton.Visibility = Visibility.Collapsed;
}
private void Set<T>(ref T field, T value, [CallerMemberName]string propertyName = "")
{
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
@@ -0,0 +1,17 @@
<ContentDialog
x:Class="FluentTerminal.App.Dialogs.ShellProfileSelectionDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Select Profile"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
CloseButtonText="Cancel"
PrimaryButtonText="Start"
mc:Ignorable="d">
<ComboBox
HorizontalAlignment="Stretch"
DisplayMemberPath="Name"
ItemsSource="{x:Bind Profiles}"
SelectedItem="{x:Bind SelectedProfile, Mode=TwoWay}" />
</ContentDialog>
@@ -0,0 +1,38 @@
using FluentTerminal.App.Services;
using FluentTerminal.App.Services.Dialogs;
using FluentTerminal.App.Utilities;
using FluentTerminal.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
namespace FluentTerminal.App.Dialogs
{
public sealed partial class ShellProfileSelectionDialog : ContentDialog, IShellProfileSelectionDialog
{
public IEnumerable<ShellProfile> Profiles { get; }
public ShellProfile SelectedProfile { get; private set; }
public ShellProfileSelectionDialog(ISettingsService settingsService)
{
Profiles = settingsService.GetShellProfiles();
SelectedProfile = Profiles.First();
this.InitializeComponent();
var currentTheme = settingsService.GetCurrentTheme();
RequestedTheme = ContrastHelper.GetIdealThemeForBackgroundColor(currentTheme.Colors.Background);
}
public async Task<ShellProfile> SelectProfile()
{
var result = await ShowAsync();
if (result == ContentDialogResult.Primary)
{
return SelectedProfile;
}
return null;
}
}
}
+138 -163
View File
@@ -22,6 +22,10 @@
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
<AppInstallerUpdateFrequency>1</AppInstallerUpdateFrequency>
<AppInstallerCheckForUpdateFrequency>OnApplicationRun</AppInstallerCheckForUpdateFrequency>
<AppxSymbolPackageEnabled>True</AppxSymbolPackageEnabled>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
@@ -33,6 +37,7 @@
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
@@ -45,6 +50,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 +85,7 @@
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
@@ -91,42 +98,62 @@
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="Adapters\ApplicationViewAdapter.cs" />
<Compile Include="Adapters\AppServiceConnectionAdapter.cs" />
<Compile Include="Adapters\DispatcherTimerAdapter.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Behaviors\MiddleClickBehavior.cs" />
<Compile Include="Converters\BackgroundToApplicationThemeConverter.cs" />
<Compile Include="Converters\ColorResourceKeyFallbackConverter.cs" />
<Compile Include="Converters\TabColorFallbackConverter.cs" />
<Compile Include="Converters\EnumValueToVisibiltyConverter.cs" />
<Compile Include="Converters\FalseToVisibleConverter.cs" />
<Compile Include="Converters\IntToExtendedVirtualKeyConverter.cs" />
<Compile Include="Converters\IntToVisibilityConverter.cs" />
<Compile Include="Converters\NegateConverter.cs" />
<Compile Include="Converters\StringToColorConverter.cs" />
<Compile Include="Converters\TabThemeSelectedConverter.cs" />
<Compile Include="Converters\TerminalViewModelToViewConverter.cs" />
<Compile Include="Converters\TrueToVisibleConverter.cs" />
<Compile Include="Utilities\ApplicationDataContainerExtensions.cs" />
<Compile Include="Services\IDefaultValueProvider.cs" />
<Compile Include="Services\Implementation\DefaultValueProvider.cs" />
<Compile Include="Services\Implementation\SettingsService.cs" />
<Compile Include="Services\Implementation\TerminalService.cs" />
<Compile Include="Services\ISettingsService.cs" />
<Compile Include="Services\ITerminalService.cs" />
<Compile Include="Utilities\HttpJsonContent.cs" />
<Compile Include="Utilities\IHttpContentExtensions.cs" />
<Compile Include="ViewModels\MainViewModel.cs" />
<Compile Include="ViewModels\SettingsViewModel.cs" />
<Compile Include="ViewModels\TerminalViewModel.cs" />
<Compile Include="Views\ITerminalView.cs" />
<Compile Include="Adapters\MessageDialogAdapter.cs" />
<Compile Include="Dialogs\ShellProfileSelectionDialog.xaml.cs">
<DependentUpon>ShellProfileSelectionDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Services\ClipboardService.cs" />
<Compile Include="Services\FileSystemService.cs" />
<Compile Include="Services\StartupTaskService.cs" />
<Compile Include="Services\SystemFontService.cs" />
<Compile Include="Utilities\ColorExtensions.cs" />
<Compile Include="Utilities\ContrastHelper.cs" />
<Compile Include="Views\KeyBindingsView.xaml.cs">
<DependentUpon>KeyBindingsView.xaml</DependentUpon>
</Compile>
<Compile Include="Views\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Dialogs\CreateKeyBindingDialog.xaml.cs">
<DependentUpon>CreateKeyBindingDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPage.xaml.cs">
<DependentUpon>SettingsPage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\ColorSettings.xaml.cs">
<DependentUpon>ColorSettings.xaml</DependentUpon>
<Compile Include="Views\SettingsPages\MouseSettings.xaml.cs">
<DependentUpon>MouseSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\FontSettings.xaml.cs">
<DependentUpon>FontSettings.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>
@@ -134,8 +161,16 @@
<Compile Include="Views\SettingsPages\KeyBindingSettings.xaml.cs">
<DependentUpon>KeyBindingSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\SettingsPages\ShellSettings.xaml.cs">
<DependentUpon>ShellSettings.xaml</DependentUpon>
<Compile Include="Views\SettingsPages\ShellProfileSettings.xaml.cs">
<DependentUpon>ShellProfileSettings.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TabBar.xaml.cs">
<DependentUpon>TabBar.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TabBarBackgroundBindingHelper.cs" />
<Compile Include="Views\TabThemeTemplateSelector.cs" />
<Compile Include="Views\TerminalColorPicker.xaml.cs">
<DependentUpon>TerminalColorPicker.xaml</DependentUpon>
</Compile>
<Compile Include="Views\TerminalView.xaml.cs">
<DependentUpon>TerminalView.xaml</DependentUpon>
@@ -145,10 +180,39 @@
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<None Include="FluentTerminal.App_StoreKey.pfx" />
<None Include="FluentTerminal.App_TemporaryKey.pfx" />
<Content Include="Assets\settingsicons.ttf" />
<Content Include="Win32\Autofac.dll" />
<Content Include="Win32\FluentTerminal.App.Services.dll" />
<Content Include="Win32\FluentTerminal.App.Services.pdb" />
<Content Include="Win32\FluentTerminal.Models.dll" />
<Content Include="Win32\FluentTerminal.Models.pdb" />
<Content Include="Win32\FluentTerminal.SystemTray.exe" />
<Content Include="Win32\FluentTerminal.SystemTray.pdb" />
<Content Include="Win32\GlobalHotKey.dll" />
<Content Include="Win32\Newtonsoft.Json.dll" />
<Content Include="Win32\RestSharp.dll" />
<Content Include="Win32\System.Data.Common.dll" />
<Content Include="Win32\System.Diagnostics.StackTrace.dll" />
<Content Include="Win32\System.Diagnostics.Tracing.dll" />
<Content Include="Win32\System.Globalization.Extensions.dll" />
<Content Include="Win32\System.IO.Compression.dll" />
<Content Include="Win32\System.Net.Http.dll" />
<Content Include="Win32\System.Net.Sockets.dll" />
<Content Include="Win32\System.Runtime.Serialization.Primitives.dll" />
<Content Include="Win32\System.Security.Cryptography.Algorithms.dll" />
<Content Include="Win32\System.Security.SecureString.dll" />
<Content Include="Win32\System.Threading.Overlapped.dll" />
<Content Include="Win32\System.Xml.XPath.XDocument.dll" />
<Content Include="Win32\winpty-agent.exe" />
<Content Include="Win32\winpty-debugserver.exe" />
<Content Include="Win32\winpty.dll" />
<Content Include="Win32\winpty.NET.dll" />
<None Include="FluentTerminal.App_StoreKey.pfx" />
<None Include="FluentTerminal.App_TemporaryKey.pfx" />
<Content Include="Client\bundle.js" />
<Content Include="Client\index.html" />
<Content Include="Client\style.css" />
<Content Include="Client\xterm.css" />
<Content Include="Win32\FluentTerminal.SystemTray.exe.config" />
</ItemGroup>
<ItemGroup>
@@ -199,146 +263,20 @@
<Content Include="Assets\AppIcons\Wide310x150Logo.scale-400.png" />
<None Include="Package.StoreAssociation.xml" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Win32\Client\bundle.js" />
<Content Include="Win32\Client\index.html" />
<Content Include="Win32\Client\style.css" />
<Content Include="Win32\Client\xterm.css" />
<Content Include="Win32\CommonServiceLocator.dll" />
<Content Include="Win32\CommonServiceLocator.pdb" />
<Content Include="Win32\Fleck.dll" />
<Content Include="Win32\FluentTerminal.SystemTray.exe" />
<Content Include="Win32\FluentTerminal.SystemTray.pdb" />
<Content Include="Win32\Microsoft.Owin.dll" />
<Content Include="Win32\Microsoft.Owin.FileSystems.dll" />
<Content Include="Win32\Microsoft.Owin.Host.HttpListener.dll" />
<Content Include="Win32\Microsoft.Owin.Hosting.dll" />
<Content Include="Win32\Microsoft.Owin.StaticFiles.dll" />
<Content Include="Win32\Microsoft.Win32.Primitives.dll" />
<Content Include="Win32\netstandard.dll" />
<Content Include="Win32\Newtonsoft.Json.dll" />
<Content Include="Win32\Owin.dll" />
<Content Include="Win32\System.AppContext.dll" />
<Content Include="Win32\System.Collections.Concurrent.dll" />
<Content Include="Win32\System.Collections.dll" />
<Content Include="Win32\System.Collections.NonGeneric.dll" />
<Content Include="Win32\System.Collections.Specialized.dll" />
<Content Include="Win32\System.ComponentModel.dll" />
<Content Include="Win32\System.ComponentModel.EventBasedAsync.dll" />
<Content Include="Win32\System.ComponentModel.Primitives.dll" />
<Content Include="Win32\System.ComponentModel.TypeConverter.dll" />
<Content Include="Win32\System.Console.dll" />
<Content Include="Win32\System.Data.Common.dll" />
<Content Include="Win32\System.Diagnostics.Contracts.dll" />
<Content Include="Win32\System.Diagnostics.Debug.dll" />
<Content Include="Win32\System.Diagnostics.FileVersionInfo.dll" />
<Content Include="Win32\System.Diagnostics.Process.dll" />
<Content Include="Win32\System.Diagnostics.StackTrace.dll" />
<Content Include="Win32\System.Diagnostics.TextWriterTraceListener.dll" />
<Content Include="Win32\System.Diagnostics.Tools.dll" />
<Content Include="Win32\System.Diagnostics.TraceSource.dll" />
<Content Include="Win32\System.Diagnostics.Tracing.dll" />
<Content Include="Win32\System.Drawing.Primitives.dll" />
<Content Include="Win32\System.Dynamic.Runtime.dll" />
<Content Include="Win32\System.Globalization.Calendars.dll" />
<Content Include="Win32\System.Globalization.dll" />
<Content Include="Win32\System.Globalization.Extensions.dll" />
<Content Include="Win32\System.IO.Compression.dll" />
<Content Include="Win32\System.IO.Compression.ZipFile.dll" />
<Content Include="Win32\System.IO.dll" />
<Content Include="Win32\System.IO.FileSystem.dll" />
<Content Include="Win32\System.IO.FileSystem.DriveInfo.dll" />
<Content Include="Win32\System.IO.FileSystem.Primitives.dll" />
<Content Include="Win32\System.IO.FileSystem.Watcher.dll" />
<Content Include="Win32\System.IO.IsolatedStorage.dll" />
<Content Include="Win32\System.IO.MemoryMappedFiles.dll" />
<Content Include="Win32\System.IO.Pipes.dll" />
<Content Include="Win32\System.IO.UnmanagedMemoryStream.dll" />
<Content Include="Win32\System.Linq.dll" />
<Content Include="Win32\System.Linq.Expressions.dll" />
<Content Include="Win32\System.Linq.Parallel.dll" />
<Content Include="Win32\System.Linq.Queryable.dll" />
<Content Include="Win32\System.Net.Http.dll" />
<Content Include="Win32\System.Net.Http.Formatting.dll" />
<Content Include="Win32\System.Net.NameResolution.dll" />
<Content Include="Win32\System.Net.NetworkInformation.dll" />
<Content Include="Win32\System.Net.Ping.dll" />
<Content Include="Win32\System.Net.Primitives.dll" />
<Content Include="Win32\System.Net.Requests.dll" />
<Content Include="Win32\System.Net.Security.dll" />
<Content Include="Win32\System.Net.Sockets.dll" />
<Content Include="Win32\System.Net.WebHeaderCollection.dll" />
<Content Include="Win32\System.Net.WebSockets.Client.dll" />
<Content Include="Win32\System.Net.WebSockets.dll" />
<Content Include="Win32\System.ObjectModel.dll" />
<Content Include="Win32\System.Reflection.dll" />
<Content Include="Win32\System.Reflection.Extensions.dll" />
<Content Include="Win32\System.Reflection.Primitives.dll" />
<Content Include="Win32\System.Resources.Reader.dll" />
<Content Include="Win32\System.Resources.ResourceManager.dll" />
<Content Include="Win32\System.Resources.Writer.dll" />
<Content Include="Win32\System.Runtime.CompilerServices.VisualC.dll" />
<Content Include="Win32\System.Runtime.dll" />
<Content Include="Win32\System.Runtime.Extensions.dll" />
<Content Include="Win32\System.Runtime.Handles.dll" />
<Content Include="Win32\System.Runtime.InteropServices.dll" />
<Content Include="Win32\System.Runtime.InteropServices.RuntimeInformation.dll" />
<Content Include="Win32\System.Runtime.Numerics.dll" />
<Content Include="Win32\System.Runtime.Serialization.Formatters.dll" />
<Content Include="Win32\System.Runtime.Serialization.Json.dll" />
<Content Include="Win32\System.Runtime.Serialization.Primitives.dll" />
<Content Include="Win32\System.Runtime.Serialization.Xml.dll" />
<Content Include="Win32\System.Security.Claims.dll" />
<Content Include="Win32\System.Security.Cryptography.Algorithms.dll" />
<Content Include="Win32\System.Security.Cryptography.Csp.dll" />
<Content Include="Win32\System.Security.Cryptography.Encoding.dll" />
<Content Include="Win32\System.Security.Cryptography.Primitives.dll" />
<Content Include="Win32\System.Security.Cryptography.X509Certificates.dll" />
<Content Include="Win32\System.Security.Principal.dll" />
<Content Include="Win32\System.Security.SecureString.dll" />
<Content Include="Win32\System.Text.Encoding.dll" />
<Content Include="Win32\System.Text.Encoding.Extensions.dll" />
<Content Include="Win32\System.Text.RegularExpressions.dll" />
<Content Include="Win32\System.Threading.dll" />
<Content Include="Win32\System.Threading.Overlapped.dll" />
<Content Include="Win32\System.Threading.Tasks.dll" />
<Content Include="Win32\System.Threading.Tasks.Parallel.dll" />
<Content Include="Win32\System.Threading.Thread.dll" />
<Content Include="Win32\System.Threading.ThreadPool.dll" />
<Content Include="Win32\System.Threading.Timer.dll" />
<Content Include="Win32\System.ValueTuple.dll" />
<Content Include="Win32\System.Web.Http.dll" />
<Content Include="Win32\System.Web.Http.Owin.dll" />
<Content Include="Win32\System.Xml.ReaderWriter.dll" />
<Content Include="Win32\System.Xml.XDocument.dll" />
<Content Include="Win32\System.Xml.XmlDocument.dll" />
<Content Include="Win32\System.Xml.XmlSerializer.dll" />
<Content Include="Win32\System.Xml.XPath.dll" />
<Content Include="Win32\System.Xml.XPath.XDocument.dll" />
<Content Include="Win32\Unity.Abstractions.dll" />
<Content Include="Win32\Unity.Abstractions.pdb" />
<Content Include="Win32\Unity.Configuration.dll" />
<Content Include="Win32\Unity.Configuration.pdb" />
<Content Include="Win32\Unity.Container.dll" />
<Content Include="Win32\Unity.Container.pdb" />
<Content Include="Win32\Unity.Interception.Configuration.dll" />
<Content Include="Win32\Unity.Interception.Configuration.pdb" />
<Content Include="Win32\Unity.Interception.dll" />
<Content Include="Win32\Unity.Interception.pdb" />
<Content Include="Win32\Unity.RegistrationByConvention.dll" />
<Content Include="Win32\Unity.RegistrationByConvention.pdb" />
<Content Include="Win32\Unity.ServiceLocation.dll" />
<Content Include="Win32\Unity.ServiceLocation.pdb" />
<Content Include="Win32\Unity.WebApi.dll" />
<Content Include="Win32\winpty-agent.exe" />
<Content Include="Win32\winpty-debugserver.exe" />
<Content Include="Win32\winpty.dll" />
<Content Include="Win32\winpty.NET.dll" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Dialogs\ShellProfileSelectionDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\KeyBindingsView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -348,15 +286,23 @@
<Generator>MSBuild:Compile</Generator>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Page>
<Page Include="Dialogs\CreateKeyBindingDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\ColorSettings.xaml">
<Page Include="Views\SettingsPages\MouseSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\FontSettings.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>
@@ -368,7 +314,15 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\SettingsPages\ShellSettings.xaml">
<Page Include="Views\SettingsPages\ShellProfileSettings.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\TabBar.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\TerminalColorPicker.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@@ -379,16 +333,25 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac">
<Version>4.6.2</Version>
<Version>4.8.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.0.6</Version>
<Version>6.1.5</Version>
</PackageReference>
<PackageReference Include="MvvmLightLibs">
<Version>5.3.0</Version>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI">
<Version>3.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
<Version>3.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed">
<Version>2.0.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>10.0.3</Version>
<Version>11.0.2</Version>
</PackageReference>
<PackageReference Include="Win2D.uwp">
<Version>1.21.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
@@ -397,6 +360,14 @@
</SDKReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FluentTerminal.App.Services\FluentTerminal.App.Services.csproj">
<Project>{a1ad7385-f92e-46ac-a349-43dc99b1e72d}</Project>
<Name>FluentTerminal.App.Services</Name>
</ProjectReference>
<ProjectReference Include="..\FluentTerminal.App.ViewModels\FluentTerminal.App.ViewModels.csproj">
<Project>{44e8e225-f46c-47ec-9290-ffa08fccf1b5}</Project>
<Name>FluentTerminal.App.ViewModels</Name>
</ProjectReference>
<ProjectReference Include="..\FluentTerminal.Models\FluentTerminal.Models.csproj">
<Project>{5EA40453-A0AA-449E-8406-D6EA51D7E70E}</Project>
<Name>FluentTerminal.Models</Name>
@@ -406,6 +377,7 @@
<Name>FluentTerminal.RuntimeComponent</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
@@ -414,6 +386,9 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PreBuildEvent>xcopy /y "$(SolutionDir)FluentTerminal.Client\dist" "$(ProjectDir)Client\"</PreBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
@@ -0,0 +1,360 @@
<?xml version="1.0" encoding="utf-8"?>
<StoreAssociation xmlns="http://schemas.microsoft.com/appx/2010/storeassociation">
<Publisher>CN=BDC72197-B634-438A-B863-16DCB7D90C85</Publisher>
<NamespacePrefix>53621FSApps</NamespacePrefix>
<PublisherDisplayName>FS Apps</PublisherDisplayName>
<GeneratePackageHash>http://www.w3.org/2001/04/xmlenc#sha256</GeneratePackageHash>
<SupportedLocales>
<Language Code="af" InMinimumRequirementSet="false" />
<Language Code="af-za" InMinimumRequirementSet="false" />
<Language Code="am" InMinimumRequirementSet="false" />
<Language Code="am-et" InMinimumRequirementSet="false" />
<Language Code="ar" InMinimumRequirementSet="true" />
<Language Code="ar-ae" InMinimumRequirementSet="true" />
<Language Code="ar-bh" InMinimumRequirementSet="true" />
<Language Code="ar-dz" InMinimumRequirementSet="true" />
<Language Code="ar-eg" InMinimumRequirementSet="true" />
<Language Code="ar-iq" InMinimumRequirementSet="true" />
<Language Code="ar-jo" InMinimumRequirementSet="true" />
<Language Code="ar-kw" InMinimumRequirementSet="true" />
<Language Code="ar-lb" InMinimumRequirementSet="true" />
<Language Code="ar-ly" InMinimumRequirementSet="true" />
<Language Code="ar-ma" InMinimumRequirementSet="true" />
<Language Code="ar-om" InMinimumRequirementSet="true" />
<Language Code="ar-qa" InMinimumRequirementSet="true" />
<Language Code="ar-sa" InMinimumRequirementSet="true" />
<Language Code="ar-sy" InMinimumRequirementSet="true" />
<Language Code="ar-tn" InMinimumRequirementSet="true" />
<Language Code="ar-ye" InMinimumRequirementSet="true" />
<Language Code="as" InMinimumRequirementSet="false" />
<Language Code="as-in" InMinimumRequirementSet="false" />
<Language Code="az" InMinimumRequirementSet="false" />
<Language Code="az-arab" InMinimumRequirementSet="false" />
<Language Code="az-arab-az" InMinimumRequirementSet="false" />
<Language Code="az-cyrl" InMinimumRequirementSet="false" />
<Language Code="az-cyrl-az" InMinimumRequirementSet="false" />
<Language Code="az-latn" InMinimumRequirementSet="false" />
<Language Code="az-latn-az" InMinimumRequirementSet="false" />
<Language Code="be" InMinimumRequirementSet="false" />
<Language Code="be-by" InMinimumRequirementSet="false" />
<Language Code="bg" InMinimumRequirementSet="true" />
<Language Code="bg-bg" InMinimumRequirementSet="true" />
<Language Code="bn" InMinimumRequirementSet="false" />
<Language Code="bn-bd" InMinimumRequirementSet="false" />
<Language Code="bn-in" InMinimumRequirementSet="false" />
<Language Code="bs" InMinimumRequirementSet="false" />
<Language Code="bs-cyrl" InMinimumRequirementSet="false" />
<Language Code="bs-cyrl-ba" InMinimumRequirementSet="false" />
<Language Code="bs-latn" InMinimumRequirementSet="false" />
<Language Code="bs-latn-ba" InMinimumRequirementSet="false" />
<Language Code="ca" InMinimumRequirementSet="false" />
<Language Code="ca-es" InMinimumRequirementSet="false" />
<Language Code="ca-es-valencia" InMinimumRequirementSet="false" />
<Language Code="chr-cher" InMinimumRequirementSet="false" />
<Language Code="chr-cher-us" InMinimumRequirementSet="false" />
<Language Code="chr-latn" InMinimumRequirementSet="false" />
<Language Code="cs" InMinimumRequirementSet="true" />
<Language Code="cs-cz" InMinimumRequirementSet="true" />
<Language Code="cy" InMinimumRequirementSet="false" />
<Language Code="cy-gb" InMinimumRequirementSet="false" />
<Language Code="da" InMinimumRequirementSet="true" />
<Language Code="da-dk" InMinimumRequirementSet="true" />
<Language Code="de" InMinimumRequirementSet="true" />
<Language Code="de-at" InMinimumRequirementSet="true" />
<Language Code="de-ch" InMinimumRequirementSet="true" />
<Language Code="de-de" InMinimumRequirementSet="true" />
<Language Code="de-li" InMinimumRequirementSet="true" />
<Language Code="de-lu" InMinimumRequirementSet="true" />
<Language Code="el" InMinimumRequirementSet="true" />
<Language Code="el-gr" InMinimumRequirementSet="true" />
<Language Code="en" InMinimumRequirementSet="true" />
<Language Code="en-au" InMinimumRequirementSet="true" />
<Language Code="en-bz" InMinimumRequirementSet="true" />
<Language Code="en-ca" InMinimumRequirementSet="true" />
<Language Code="en-gb" InMinimumRequirementSet="true" />
<Language Code="en-hk" InMinimumRequirementSet="true" />
<Language Code="en-id" InMinimumRequirementSet="true" />
<Language Code="en-ie" InMinimumRequirementSet="true" />
<Language Code="en-in" InMinimumRequirementSet="true" />
<Language Code="en-jm" InMinimumRequirementSet="true" />
<Language Code="en-kz" InMinimumRequirementSet="true" />
<Language Code="en-mt" InMinimumRequirementSet="true" />
<Language Code="en-my" InMinimumRequirementSet="true" />
<Language Code="en-nz" InMinimumRequirementSet="true" />
<Language Code="en-ph" InMinimumRequirementSet="true" />
<Language Code="en-pk" InMinimumRequirementSet="true" />
<Language Code="en-sg" InMinimumRequirementSet="true" />
<Language Code="en-tt" InMinimumRequirementSet="true" />
<Language Code="en-us" InMinimumRequirementSet="true" />
<Language Code="en-vn" InMinimumRequirementSet="true" />
<Language Code="en-za" InMinimumRequirementSet="true" />
<Language Code="es" InMinimumRequirementSet="true" />
<Language Code="es-ar" InMinimumRequirementSet="true" />
<Language Code="es-bo" InMinimumRequirementSet="true" />
<Language Code="es-cl" InMinimumRequirementSet="true" />
<Language Code="es-co" InMinimumRequirementSet="true" />
<Language Code="es-cr" InMinimumRequirementSet="true" />
<Language Code="es-do" InMinimumRequirementSet="true" />
<Language Code="es-ec" InMinimumRequirementSet="true" />
<Language Code="es-es" InMinimumRequirementSet="true" />
<Language Code="es-gt" InMinimumRequirementSet="true" />
<Language Code="es-hn" InMinimumRequirementSet="true" />
<Language Code="es-mx" InMinimumRequirementSet="true" />
<Language Code="es-ni" InMinimumRequirementSet="true" />
<Language Code="es-pa" InMinimumRequirementSet="true" />
<Language Code="es-pe" InMinimumRequirementSet="true" />
<Language Code="es-pr" InMinimumRequirementSet="true" />
<Language Code="es-py" InMinimumRequirementSet="true" />
<Language Code="es-sv" InMinimumRequirementSet="true" />
<Language Code="es-us" InMinimumRequirementSet="true" />
<Language Code="es-uy" InMinimumRequirementSet="true" />
<Language Code="es-ve" InMinimumRequirementSet="true" />
<Language Code="et" InMinimumRequirementSet="true" />
<Language Code="et-ee" InMinimumRequirementSet="true" />
<Language Code="eu" InMinimumRequirementSet="false" />
<Language Code="eu-es" InMinimumRequirementSet="false" />
<Language Code="fa" InMinimumRequirementSet="false" />
<Language Code="fa-ir" InMinimumRequirementSet="false" />
<Language Code="fi" InMinimumRequirementSet="true" />
<Language Code="fi-fi" InMinimumRequirementSet="true" />
<Language Code="fil" InMinimumRequirementSet="false" />
<Language Code="fil-latn" InMinimumRequirementSet="false" />
<Language Code="fil-ph" InMinimumRequirementSet="false" />
<Language Code="fr" InMinimumRequirementSet="true" />
<Language Code="fr-015" InMinimumRequirementSet="true" />
<Language Code="fr-be" InMinimumRequirementSet="true" />
<Language Code="fr-ca" InMinimumRequirementSet="true" />
<Language Code="fr-cd" InMinimumRequirementSet="true" />
<Language Code="fr-ch" InMinimumRequirementSet="true" />
<Language Code="fr-ci" InMinimumRequirementSet="true" />
<Language Code="fr-cm" InMinimumRequirementSet="true" />
<Language Code="fr-fr" InMinimumRequirementSet="true" />
<Language Code="fr-ht" InMinimumRequirementSet="true" />
<Language Code="fr-lu" InMinimumRequirementSet="true" />
<Language Code="fr-ma" InMinimumRequirementSet="true" />
<Language Code="fr-mc" InMinimumRequirementSet="true" />
<Language Code="fr-ml" InMinimumRequirementSet="true" />
<Language Code="fr-re" InMinimumRequirementSet="true" />
<Language Code="frc" InMinimumRequirementSet="true" />
<Language Code="frc-latn" InMinimumRequirementSet="true" />
<Language Code="frp" InMinimumRequirementSet="true" />
<Language Code="frp-latn" InMinimumRequirementSet="true" />
<Language Code="ga" InMinimumRequirementSet="false" />
<Language Code="ga-ie" InMinimumRequirementSet="false" />
<Language Code="gd" InMinimumRequirementSet="false" />
<Language Code="gd-gb" InMinimumRequirementSet="false" />
<Language Code="gd-latn" InMinimumRequirementSet="false" />
<Language Code="gl" InMinimumRequirementSet="false" />
<Language Code="gl-es" InMinimumRequirementSet="false" />
<Language Code="gu" InMinimumRequirementSet="false" />
<Language Code="gu-in" InMinimumRequirementSet="false" />
<Language Code="ha-latn" InMinimumRequirementSet="false" />
<Language Code="ha-latn-ng" InMinimumRequirementSet="false" />
<Language Code="he" InMinimumRequirementSet="true" />
<Language Code="he-il" InMinimumRequirementSet="true" />
<Language Code="hi" InMinimumRequirementSet="true" />
<Language Code="hi-in" InMinimumRequirementSet="true" />
<Language Code="hr" InMinimumRequirementSet="true" />
<Language Code="hr-ba" InMinimumRequirementSet="true" />
<Language Code="hr-hr" InMinimumRequirementSet="true" />
<Language Code="hu" InMinimumRequirementSet="true" />
<Language Code="hu-hu" InMinimumRequirementSet="true" />
<Language Code="hy" InMinimumRequirementSet="false" />
<Language Code="hy-am" InMinimumRequirementSet="false" />
<Language Code="id" InMinimumRequirementSet="true" />
<Language Code="id-id" InMinimumRequirementSet="true" />
<Language Code="ig" InMinimumRequirementSet="false" />
<Language Code="ig-latn" InMinimumRequirementSet="false" />
<Language Code="ig-ng" InMinimumRequirementSet="false" />
<Language Code="is" InMinimumRequirementSet="false" />
<Language Code="is-is" InMinimumRequirementSet="false" />
<Language Code="it" InMinimumRequirementSet="true" />
<Language Code="it-ch" InMinimumRequirementSet="true" />
<Language Code="it-it" InMinimumRequirementSet="true" />
<Language Code="iu-cans" InMinimumRequirementSet="false" />
<Language Code="iu-latn" InMinimumRequirementSet="false" />
<Language Code="iu-latn-ca" InMinimumRequirementSet="false" />
<Language Code="ja" InMinimumRequirementSet="true" />
<Language Code="ja-jp" InMinimumRequirementSet="true" />
<Language Code="ka" InMinimumRequirementSet="false" />
<Language Code="ka-ge" InMinimumRequirementSet="false" />
<Language Code="kk" InMinimumRequirementSet="false" />
<Language Code="kk-kz" InMinimumRequirementSet="false" />
<Language Code="km" InMinimumRequirementSet="false" />
<Language Code="km-kh" InMinimumRequirementSet="false" />
<Language Code="kn" InMinimumRequirementSet="false" />
<Language Code="kn-in" InMinimumRequirementSet="false" />
<Language Code="ko" InMinimumRequirementSet="true" />
<Language Code="ko-kr" InMinimumRequirementSet="true" />
<Language Code="kok" InMinimumRequirementSet="false" />
<Language Code="kok-in" InMinimumRequirementSet="false" />
<Language Code="ku-arab" InMinimumRequirementSet="false" />
<Language Code="ku-arab-iq" InMinimumRequirementSet="false" />
<Language Code="ky" InMinimumRequirementSet="false" />
<Language Code="ky-cyrl" InMinimumRequirementSet="false" />
<Language Code="ky-kg" InMinimumRequirementSet="false" />
<Language Code="lb" InMinimumRequirementSet="false" />
<Language Code="lb-lu" InMinimumRequirementSet="false" />
<Language Code="lt" InMinimumRequirementSet="true" />
<Language Code="lt-lt" InMinimumRequirementSet="true" />
<Language Code="lv" InMinimumRequirementSet="true" />
<Language Code="lv-lv" InMinimumRequirementSet="true" />
<Language Code="mi" InMinimumRequirementSet="false" />
<Language Code="mi-latn" InMinimumRequirementSet="false" />
<Language Code="mi-nz" InMinimumRequirementSet="false" />
<Language Code="mk" InMinimumRequirementSet="false" />
<Language Code="mk-mk" InMinimumRequirementSet="false" />
<Language Code="ml" InMinimumRequirementSet="false" />
<Language Code="ml-in" InMinimumRequirementSet="false" />
<Language Code="mn" InMinimumRequirementSet="false" />
<Language Code="mn-cyrl" InMinimumRequirementSet="false" />
<Language Code="mn-mn" InMinimumRequirementSet="false" />
<Language Code="mn-mong" InMinimumRequirementSet="false" />
<Language Code="mn-phag" InMinimumRequirementSet="false" />
<Language Code="mr" InMinimumRequirementSet="false" />
<Language Code="mr-in" InMinimumRequirementSet="false" />
<Language Code="ms" InMinimumRequirementSet="false" />
<Language Code="ms-bn" InMinimumRequirementSet="false" />
<Language Code="ms-my" InMinimumRequirementSet="false" />
<Language Code="mt" InMinimumRequirementSet="false" />
<Language Code="mt-mt" InMinimumRequirementSet="false" />
<Language Code="nb" InMinimumRequirementSet="true" />
<Language Code="nb-no" InMinimumRequirementSet="true" />
<Language Code="ne" InMinimumRequirementSet="false" />
<Language Code="ne-np" InMinimumRequirementSet="false" />
<Language Code="nl" InMinimumRequirementSet="true" />
<Language Code="nl-be" InMinimumRequirementSet="true" />
<Language Code="nl-nl" InMinimumRequirementSet="true" />
<Language Code="nn" InMinimumRequirementSet="false" />
<Language Code="nn-no" InMinimumRequirementSet="false" />
<Language Code="no" InMinimumRequirementSet="true" />
<Language Code="no-no" InMinimumRequirementSet="true" />
<Language Code="nso" InMinimumRequirementSet="false" />
<Language Code="nso-za" InMinimumRequirementSet="false" />
<Language Code="or" InMinimumRequirementSet="false" />
<Language Code="or-in" InMinimumRequirementSet="false" />
<Language Code="pa" InMinimumRequirementSet="false" />
<Language Code="pa-arab" InMinimumRequirementSet="false" />
<Language Code="pa-arab-pk" InMinimumRequirementSet="false" />
<Language Code="pa-deva" InMinimumRequirementSet="false" />
<Language Code="pa-in" InMinimumRequirementSet="false" />
<Language Code="pl" InMinimumRequirementSet="true" />
<Language Code="pl-pl" InMinimumRequirementSet="true" />
<Language Code="prs" InMinimumRequirementSet="false" />
<Language Code="prs-af" InMinimumRequirementSet="false" />
<Language Code="prs-arab" InMinimumRequirementSet="false" />
<Language Code="pt" InMinimumRequirementSet="true" />
<Language Code="pt-br" InMinimumRequirementSet="true" />
<Language Code="pt-pt" InMinimumRequirementSet="true" />
<Language Code="quc" InMinimumRequirementSet="false" />
<Language Code="quc-latn" InMinimumRequirementSet="false" />
<Language Code="qut" InMinimumRequirementSet="false" />
<Language Code="qut-gt" InMinimumRequirementSet="false" />
<Language Code="qut-latn" InMinimumRequirementSet="false" />
<Language Code="quz" InMinimumRequirementSet="false" />
<Language Code="quz-bo" InMinimumRequirementSet="false" />
<Language Code="quz-ec" InMinimumRequirementSet="false" />
<Language Code="quz-pe" InMinimumRequirementSet="false" />
<Language Code="ro" InMinimumRequirementSet="true" />
<Language Code="ro-ro" InMinimumRequirementSet="true" />
<Language Code="ru" InMinimumRequirementSet="true" />
<Language Code="ru-ru" InMinimumRequirementSet="true" />
<Language Code="rw" InMinimumRequirementSet="false" />
<Language Code="rw-rw" InMinimumRequirementSet="false" />
<Language Code="sd-arab" InMinimumRequirementSet="false" />
<Language Code="sd-arab-pk" InMinimumRequirementSet="false" />
<Language Code="sd-deva" InMinimumRequirementSet="false" />
<Language Code="si" InMinimumRequirementSet="false" />
<Language Code="si-lk" InMinimumRequirementSet="false" />
<Language Code="sk" InMinimumRequirementSet="true" />
<Language Code="sk-sk" InMinimumRequirementSet="true" />
<Language Code="sl" InMinimumRequirementSet="true" />
<Language Code="sl-si" InMinimumRequirementSet="true" />
<Language Code="sq" InMinimumRequirementSet="false" />
<Language Code="sq-al" InMinimumRequirementSet="false" />
<Language Code="sr" InMinimumRequirementSet="true" />
<Language Code="sr-cyrl" InMinimumRequirementSet="false" />
<Language Code="sr-cyrl-ba" InMinimumRequirementSet="false" />
<Language Code="sr-cyrl-cs" InMinimumRequirementSet="false" />
<Language Code="sr-cyrl-me" InMinimumRequirementSet="false" />
<Language Code="sr-cyrl-rs" InMinimumRequirementSet="false" />
<Language Code="sr-latn" InMinimumRequirementSet="true" />
<Language Code="sr-latn-ba" InMinimumRequirementSet="true" />
<Language Code="sr-latn-cs" InMinimumRequirementSet="true" />
<Language Code="sr-latn-me" InMinimumRequirementSet="true" />
<Language Code="sr-latn-rs" InMinimumRequirementSet="true" />
<Language Code="sv" InMinimumRequirementSet="true" />
<Language Code="sv-fi" InMinimumRequirementSet="true" />
<Language Code="sv-se" InMinimumRequirementSet="true" />
<Language Code="sw" InMinimumRequirementSet="false" />
<Language Code="sw-ke" InMinimumRequirementSet="false" />
<Language Code="ta" InMinimumRequirementSet="false" />
<Language Code="ta-in" InMinimumRequirementSet="false" />
<Language Code="te" InMinimumRequirementSet="false" />
<Language Code="te-in" InMinimumRequirementSet="false" />
<Language Code="tg-arab" InMinimumRequirementSet="false" />
<Language Code="tg-cyrl" InMinimumRequirementSet="false" />
<Language Code="tg-cyrl-tj" InMinimumRequirementSet="false" />
<Language Code="tg-latn" InMinimumRequirementSet="false" />
<Language Code="th" InMinimumRequirementSet="true" />
<Language Code="th-th" InMinimumRequirementSet="true" />
<Language Code="ti" InMinimumRequirementSet="false" />
<Language Code="ti-et" InMinimumRequirementSet="false" />
<Language Code="tk" InMinimumRequirementSet="false" />
<Language Code="tk-cyrl" InMinimumRequirementSet="false" />
<Language Code="tk-latn" InMinimumRequirementSet="false" />
<Language Code="tk-tm" InMinimumRequirementSet="false" />
<Language Code="tk-tr" InMinimumRequirementSet="false" />
<Language Code="tn" InMinimumRequirementSet="false" />
<Language Code="tn-bw" InMinimumRequirementSet="false" />
<Language Code="tn-za" InMinimumRequirementSet="false" />
<Language Code="tr" InMinimumRequirementSet="true" />
<Language Code="tr-tr" InMinimumRequirementSet="true" />
<Language Code="tt" InMinimumRequirementSet="false" />
<Language Code="tt-arab" InMinimumRequirementSet="false" />
<Language Code="tt-cyrl" InMinimumRequirementSet="false" />
<Language Code="tt-latn" InMinimumRequirementSet="false" />
<Language Code="tt-ru" InMinimumRequirementSet="false" />
<Language Code="ug" InMinimumRequirementSet="false" />
<Language Code="ug-arab" InMinimumRequirementSet="false" />
<Language Code="ug-cn" InMinimumRequirementSet="false" />
<Language Code="ug-cyrl" InMinimumRequirementSet="false" />
<Language Code="ug-latn" InMinimumRequirementSet="false" />
<Language Code="uk" InMinimumRequirementSet="true" />
<Language Code="uk-ua" InMinimumRequirementSet="true" />
<Language Code="ur" InMinimumRequirementSet="false" />
<Language Code="ur-pk" InMinimumRequirementSet="false" />
<Language Code="uz-cyrl" InMinimumRequirementSet="false" />
<Language Code="uz-latn" InMinimumRequirementSet="false" />
<Language Code="uz-latn-uz" InMinimumRequirementSet="false" />
<Language Code="vi" InMinimumRequirementSet="false" />
<Language Code="vi-vn" InMinimumRequirementSet="false" />
<Language Code="wo" InMinimumRequirementSet="false" />
<Language Code="wo-sn" InMinimumRequirementSet="false" />
<Language Code="xh" InMinimumRequirementSet="false" />
<Language Code="xh-za" InMinimumRequirementSet="false" />
<Language Code="yo" InMinimumRequirementSet="false" />
<Language Code="yo-latn" InMinimumRequirementSet="false" />
<Language Code="yo-ng" InMinimumRequirementSet="false" />
<Language Code="zh-cn" InMinimumRequirementSet="true" />
<Language Code="zh-hans" InMinimumRequirementSet="true" />
<Language Code="zh-hans-cn" InMinimumRequirementSet="true" />
<Language Code="zh-hans-sg" InMinimumRequirementSet="true" />
<Language Code="zh-hant" InMinimumRequirementSet="true" />
<Language Code="zh-hant-hk" InMinimumRequirementSet="true" />
<Language Code="zh-hant-mo" InMinimumRequirementSet="true" />
<Language Code="zh-hant-tw" InMinimumRequirementSet="true" />
<Language Code="zh-hk" InMinimumRequirementSet="true" />
<Language Code="zh-tw" InMinimumRequirementSet="true" />
<Language Code="zu" InMinimumRequirementSet="false" />
<Language Code="zu-za" InMinimumRequirementSet="false" />
</SupportedLocales>
<ProductReservedInfo>
<MainPackageIdentityName>53621FSApps.FluentTerminal</MainPackageIdentityName>
<ReservedNames>
<ReservedName>Fluent Terminal</ReservedName>
</ReservedNames>
</ProductReservedInfo>
<AccountPackageIdentityNames />
<PackageInfoList LandingUrl="https://appdev.microsoft.com:443/StorePortals/Developer/Catalog/ReleaseAnchor/f9c07088-9f3e-4312-ad5b-0f3b4aeb9b37" />
</StoreAssociation>
Binary file not shown.
+12 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.4.0" />
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.2.1.0" />
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>Fluent Terminal</DisplayName>
@@ -26,15 +26,22 @@
<uap:SplashScreen Image="Assets\AppIcons\SplashScreen.png" uap5:Optional="true" />
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.appService">
<uap:AppService Name="FluentTerminalAppService" />
</uap:Extension>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe" />
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\FluentTerminal.SystemTray.exe">
<desktop:FullTrustProcess>
<desktop:ParameterGroup GroupId="AppLaunchedParameterGroup" Parameters="appLaunched" />
</desktop:FullTrustProcess>
</desktop:Extension>
<uap5:Extension Category="windows.appExecutionAlias" Executable="FluentTerminal.App.exe" EntryPoint="FluentTerminal.App.App">
<uap5:AppExecutionAlias>
<uap5:ExecutionAlias Alias="flute.exe" />
</uap5:AppExecutionAlias>
</uap5:Extension>
<desktop:Extension Category="windows.startupTask" Executable="Win32\FluentTerminal.SystemTray.exe" EntryPoint="Windows.FullTrustApplication">
<desktop:StartupTask TaskId="FluentTerminalStartupTask" Enabled="false" DisplayName="Fluent Terminal" />
</desktop:Extension>
<uap:Extension Category="windows.appService">
<uap:AppService Name="FluentTerminalAppService" />
</uap:Extension>
</Extensions>
</Application>
</Applications>
@@ -1,8 +1,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FluentTerminal.App")]
@@ -17,11 +16,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
+2 -5
View File
@@ -6,7 +6,7 @@
Laufzeitdirektiven sind hier dokumentiert: https://go.microsoft.com/fwlink/?LinkID=391919
Vollständige Aktivierung der Reflektion von "App1.MyClass" und allen zugehörigen öffentlichen/privaten Membern:
<Type Name="App1.MyClass" Dynamic="Required All"/>
<Type Name="App1.MyClass" Dynamic="Required All" />
Aktivierung der dynamischen Erstellung der spezifischen Instanziierung von "AppClass<T>" über "System.Int32":
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
@@ -22,10 +22,7 @@
Anwendungspaket. Die Sternchen sind keine Platzhalter.
-->
<Assembly Name="*Application*" Dynamic="Required All" />
<!-- Fügen Sie hier Ihre anwendungsspezifischen Laufzeitdirektiven ein. -->
</Application>
</Directives>
@@ -0,0 +1,27 @@
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
namespace FluentTerminal.App.Services
{
public class ClipboardService : IClipboardService
{
public Task<string> GetText()
{
var content = Clipboard.GetContent();
if (content.Contains(StandardDataFormats.Text))
{
return content.GetTextAsync().AsTask();
}
// Otherwise return a new task that just sends an empty string.
return Task.FromResult(string.Empty);
}
public void SetText(string text)
{
var dataPackage = new DataPackage();
dataPackage.SetText(text);
Clipboard.SetContent(dataPackage);
}
}
}
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Pickers;
using File = FluentTerminal.Models.File;
namespace FluentTerminal.App.Services
{
public class FileSystemService : IFileSystemService
{
public async Task<string> BrowseForDirectory()
{
var picker = new FolderPicker();
picker.FileTypeFilter.Add(".whatever"); // else a ComException is thrown
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
var folder = await picker.PickSingleFolderAsync();
return folder?.Path;
}
public async Task<File> OpenFile(IEnumerable<string> fileTypes)
{
var picker = new FileOpenPicker
{
SuggestedStartLocation = PickerLocationId.ComputerFolder
};
foreach (var fileType in fileTypes)
{
picker.FileTypeFilter.Add(fileType);
}
var file = await picker.PickSingleFileAsync();
if (file != null)
{
var stream = await file.OpenStreamForReadAsync().ConfigureAwait(true);
return new File(file.DisplayName, file.FileType, file.Path, stream);
}
return null;
}
public async Task SaveTextFile(string name, string fileTypeDescription, string fileType, string content)
{
var picker = new FileSavePicker();
picker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
picker.SuggestedFileName = name;
picker.FileTypeChoices.Add(fileTypeDescription, new List<string> { fileType });
var file = await picker.PickSaveFileAsync();
if (file != null)
{
await FileIO.WriteTextAsync(file, content);
}
}
}
}
@@ -1,9 +0,0 @@
using FluentTerminal.Models;
namespace FluentTerminal.App.Services
{
public interface IDefaultValueProvider
{
ShellConfiguration GetDefaultShellConfiguration();
}
}
Loaded 100 of 494 files, more files were not shown because too many files have changed in this diff. Show more