Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b118c66959 | ||
|
|
131e3536f1 | ||
|
|
b47b544065 | ||
|
|
b13c40e7e8 | ||
|
|
4e47fcef72 | ||
|
|
8acf618fd4 | ||
|
|
49e09f3f8b | ||
|
|
9bf8284d9f | ||
|
|
0f8aac81de | ||
|
|
abec73b868 | ||
|
|
86486c99f2 | ||
|
|
d9b7052e87 | ||
|
|
0efe105321 | ||
|
|
ed0dad960d | ||
|
|
2f58ed63de | ||
|
|
cc7e069f11 | ||
|
|
94a0eac58f | ||
|
|
9af0556d6c | ||
|
|
b14ca2c4ed | ||
|
|
89c1a5a614 | ||
|
|
3e32f611e8 | ||
|
|
07e65ce2bf | ||
|
|
f2a48487f1 | ||
|
|
c9d0cc7368 | ||
|
|
eecc2e8243 | ||
|
|
5c1896cd3b | ||
|
|
e0cde7fe0f |
No files matched your search
@@ -20,7 +20,7 @@ namespace FluentTerminal.App.Services.Test
|
||||
[Fact]
|
||||
public void RegisterCommandHandler_HandlerIsNull_ThrowsArgumentNullException()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
Action handler = null;
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace FluentTerminal.App.Services.Test
|
||||
[Fact]
|
||||
public void RegisterCommandHandler_CommandAlreadyRegisted_ThrowsInvalidOperationException()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
var handler = _fixture.Create<Action>();
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
keyboardCommandService.RegisterCommandHandler(command, handler);
|
||||
@@ -45,7 +45,7 @@ namespace FluentTerminal.App.Services.Test
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsRegistered_HandlerGetsInvoked()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
var handlerCalled = false;
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
keyboardCommandService.RegisterCommandHandler(command, () => handlerCalled = true);
|
||||
@@ -58,12 +58,23 @@ namespace FluentTerminal.App.Services.Test
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsNotRegisted_KeyNotFoundExceptionIsThrown()
|
||||
{
|
||||
var command = (Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length);
|
||||
var command = ((Command)new Random().Next(1, Enum.GetValues(typeof(Command)).Length)).ToString();
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
Action invoke = () => keyboardCommandService.SendCommand(command);
|
||||
|
||||
invoke.Should().Throw<KeyNotFoundException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SendCommand_CommandIsToggleWindow_ShouldNotThrow()
|
||||
{
|
||||
var command = nameof(Command.ToggleWindow);
|
||||
var keyboardCommandService = new KeyboardCommandService();
|
||||
|
||||
Action invoke = () => keyboardCommandService.SendCommand(command);
|
||||
|
||||
invoke.Should().NotThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using AutoFixture;
|
||||
using FluentAssertions;
|
||||
using FluentTerminal.App.Services.Implementation;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using Moq;
|
||||
@@ -439,12 +440,12 @@ namespace FluentTerminal.App.Services.Test
|
||||
[Fact]
|
||||
public void GetKeyBindings_KeyBindingsInKeyBindingsContainer_ReturnsKeyBindingsFromKeyBindingsContainer()
|
||||
{
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(5);
|
||||
var commands = _fixture.CreateMany<Command>(5);
|
||||
var defaultValueProvider = Mock.Of<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
foreach (var keyBinding in keyBindings)
|
||||
foreach (var command in commands)
|
||||
{
|
||||
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(keyBinding.Command.ToString(), null)).Returns(new Collection<KeyBinding> { keyBinding });
|
||||
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null)).Returns(new Collection<KeyBinding> { });
|
||||
}
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
@@ -456,23 +457,23 @@ namespace FluentTerminal.App.Services.Test
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetKeyBindings();
|
||||
var result = settingsService.GetCommandKeyBindings();
|
||||
|
||||
foreach (var keyBinding in keyBindings)
|
||||
foreach (var command in commands)
|
||||
{
|
||||
keyBindingsContainer.Verify(x => x.ReadValueFromJson<Collection<KeyBinding>>(keyBinding.Command.ToString(), null), Times.Once);
|
||||
keyBindingsContainer.Verify(x => x.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null), Times.Once);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetKeyBindings_KeyBindingsNotInKeyBindingsContainer_ReturnsKeyBindingsFromDefaultValueProvider()
|
||||
{
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(5);
|
||||
var commands = _fixture.CreateMany<Command>(5);
|
||||
var defaultValueProvider = new Mock<IDefaultValueProvider>();
|
||||
var keyBindingsContainer = new Mock<IApplicationDataContainer>();
|
||||
foreach (var keyBinding in keyBindings)
|
||||
foreach (var command in commands)
|
||||
{
|
||||
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(keyBinding.Command.ToString(), null)).Returns(new Collection<KeyBinding> { keyBinding });
|
||||
keyBindingsContainer.Setup(x => x.ReadValueFromJson<Collection<KeyBinding>>(EnumHelper.GetEnumDescription(command), null)).Returns(new Collection<KeyBinding> { });
|
||||
}
|
||||
var applicationDataContainers = new ApplicationDataContainers
|
||||
{
|
||||
@@ -484,14 +485,10 @@ namespace FluentTerminal.App.Services.Test
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider.Object, applicationDataContainers);
|
||||
|
||||
var result = settingsService.GetKeyBindings();
|
||||
var result = settingsService.GetCommandKeyBindings();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -513,7 +510,7 @@ namespace FluentTerminal.App.Services.Test
|
||||
};
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
|
||||
settingsService.SaveKeyBindings(command, keyBindings);
|
||||
settingsService.SaveKeyBindings(command.ToString(), keyBindings);
|
||||
|
||||
keyBindingsContainer.Verify(x => x.WriteValueAsJson(command.ToString(), keyBindings), Times.Once);
|
||||
}
|
||||
@@ -537,7 +534,7 @@ namespace FluentTerminal.App.Services.Test
|
||||
var settingsService = new SettingsService(defaultValueProvider, applicationDataContainers);
|
||||
settingsService.KeyBindingsChanged += (s, e) => keyBindingsChangedEventInvoked = true;
|
||||
|
||||
settingsService.SaveKeyBindings(command, keyBindings);
|
||||
settingsService.SaveKeyBindings(command.ToString(), keyBindings);
|
||||
|
||||
keyBindingsChangedEventInvoked.Should().BeTrue();
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ namespace FluentTerminal.App.Services.Test
|
||||
{
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[Command.ToggleWindow] = keyBindings.ToList()
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var response = _fixture.Create<CreateTerminalResponse>();
|
||||
@@ -57,9 +57,9 @@ namespace FluentTerminal.App.Services.Test
|
||||
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>>
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[Command.ToggleWindow] = keyBindings.ToList()
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
@@ -77,9 +77,9 @@ namespace FluentTerminal.App.Services.Test
|
||||
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>>
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[Command.ToggleWindow] = keyBindings.ToList()
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
@@ -96,9 +96,9 @@ namespace FluentTerminal.App.Services.Test
|
||||
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>>
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[Command.ToggleWindow] = keyBindings.ToList()
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
@@ -126,9 +126,9 @@ namespace FluentTerminal.App.Services.Test
|
||||
};
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[Command.ToggleWindow] = keyBindings.ToList()
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
@@ -163,9 +163,9 @@ namespace FluentTerminal.App.Services.Test
|
||||
};
|
||||
var settingsService = new Mock<ISettingsService>();
|
||||
var keyBindings = _fixture.CreateMany<KeyBinding>(3);
|
||||
settingsService.Setup(x => x.GetKeyBindings()).Returns(new Dictionary<Command, ICollection<KeyBinding>>
|
||||
settingsService.Setup(x => x.GetCommandKeyBindings()).Returns(new Dictionary<string, ICollection<KeyBinding>>
|
||||
{
|
||||
[Command.ToggleWindow] = keyBindings.ToList()
|
||||
[nameof(Command.ToggleWindow)] = keyBindings.ToList()
|
||||
});
|
||||
var trayProcessCommunicationService = new TrayProcessCommunicationService(settingsService.Object);
|
||||
var appServiceConnection = new Mock<IAppServiceConnection>();
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="PListNet" Version="2.0.3" />
|
||||
<PackageReference Include="RestSharp" Version="106.5.4" />
|
||||
<PackageReference Include="System.Runtime.InteropServices.WindowsRuntime" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,18 +7,20 @@ namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IDefaultValueProvider
|
||||
{
|
||||
IEnumerable<ShellProfile> GetPreinstalledShellProfiles();
|
||||
|
||||
Guid GetDefaultShellProfileId();
|
||||
|
||||
TerminalOptions GetDefaultTerminalOptions();
|
||||
ApplicationSettings GetDefaultApplicationSettings();
|
||||
|
||||
ICollection<KeyBinding> GetDefaultKeyBindings(Command command);
|
||||
|
||||
ApplicationSettings GetDefaultApplicationSettings();
|
||||
Guid GetDefaultShellProfileId();
|
||||
|
||||
IEnumerable<TabTheme> GetDefaultTabThemes();
|
||||
|
||||
TerminalOptions GetDefaultTerminalOptions();
|
||||
|
||||
Guid GetDefaultThemeId();
|
||||
|
||||
IEnumerable<ShellProfile> GetPreinstalledShellProfiles();
|
||||
|
||||
IEnumerable<TerminalTheme> GetPreInstalledThemes();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IKeyboardCommandService
|
||||
{
|
||||
void RegisterCommandHandler(Command command, Action handler);
|
||||
void RegisterCommandHandler(string command, Action handler);
|
||||
|
||||
void SendCommand(Command command);
|
||||
void SendCommand(string command);
|
||||
void DeregisterCommandHandler(string command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface INotificationService
|
||||
{
|
||||
void ShowNotification(string title, string content, string url = null);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -7,52 +6,36 @@ namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface ISettingsService
|
||||
{
|
||||
event EventHandler<Guid> CurrentThemeChanged;
|
||||
|
||||
event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
|
||||
event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||
|
||||
event EventHandler<Guid> CurrentThemeChanged;
|
||||
event EventHandler KeyBindingsChanged;
|
||||
|
||||
Guid GetDefaultShellProfileId();
|
||||
|
||||
ShellProfile GetDefaultShellProfile();
|
||||
|
||||
void SaveDefaultShellProfileId(Guid id);
|
||||
|
||||
IEnumerable<ShellProfile> GetShellProfiles();
|
||||
|
||||
void SaveShellProfile(ShellProfile shellProfile);
|
||||
event EventHandler<ShellProfile> ShellProfileAdded;
|
||||
event EventHandler<Guid> ShellProfileDeleted;
|
||||
event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
event EventHandler<Guid> ThemeDeleted;
|
||||
event EventHandler<TerminalTheme> ThemeAdded;
|
||||
|
||||
void DeleteShellProfile(Guid id);
|
||||
|
||||
TerminalOptions GetTerminalOptions();
|
||||
|
||||
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
||||
|
||||
ApplicationSettings GetApplicationSettings();
|
||||
|
||||
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
||||
|
||||
IDictionary<Command, ICollection<KeyBinding>> GetKeyBindings();
|
||||
|
||||
void SaveKeyBindings(Command command, ICollection<KeyBinding> keyBindings);
|
||||
|
||||
void ResetKeyBindings();
|
||||
|
||||
TerminalTheme GetCurrentTheme();
|
||||
|
||||
Guid GetCurrentThemeId();
|
||||
|
||||
TerminalTheme GetTheme(Guid id);
|
||||
|
||||
void SaveCurrentThemeId(Guid id);
|
||||
|
||||
IEnumerable<TerminalTheme> GetThemes();
|
||||
|
||||
void SaveTheme(TerminalTheme theme);
|
||||
|
||||
void DeleteTheme(Guid id);
|
||||
ApplicationSettings GetApplicationSettings();
|
||||
TerminalTheme GetCurrentTheme();
|
||||
Guid GetCurrentThemeId();
|
||||
ShellProfile GetDefaultShellProfile();
|
||||
Guid GetDefaultShellProfileId();
|
||||
IDictionary<string, ICollection<KeyBinding>> GetCommandKeyBindings();
|
||||
IEnumerable<ShellProfile> GetShellProfiles();
|
||||
IEnumerable<TabTheme> GetTabThemes();
|
||||
TerminalOptions GetTerminalOptions();
|
||||
TerminalTheme GetTheme(Guid id);
|
||||
IEnumerable<TerminalTheme> GetThemes();
|
||||
void ResetKeyBindings();
|
||||
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
||||
void SaveCurrentThemeId(Guid id);
|
||||
void SaveDefaultShellProfileId(Guid id);
|
||||
void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings);
|
||||
void SaveShellProfile(ShellProfile shellProfile, bool newShell = false);
|
||||
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
||||
void SaveTheme(TerminalTheme theme, bool newTheme = false);
|
||||
ShellProfile GetShellProfile(Guid id);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
@@ -12,7 +11,7 @@ namespace FluentTerminal.App.Services
|
||||
|
||||
event EventHandler<string> TerminalTitleChanged;
|
||||
|
||||
event EventHandler<Command> KeyboardCommandReceived;
|
||||
event EventHandler<string> KeyboardCommandReceived;
|
||||
|
||||
Task ChangeTheme(TerminalColors theme);
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IUpdateService
|
||||
{
|
||||
Task CheckForUpdate(bool notifyNoUpdate = false);
|
||||
Version GetCurrentVersion();
|
||||
Task<Version> GetLatestVersionAsync();
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,15 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
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
|
||||
AlwaysShowTabs = false,
|
||||
ShowNewOutputIndicator = false,
|
||||
EnableTrayIcon = true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,7 +35,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ToggleWindow,
|
||||
Command = nameof(Command.ToggleWindow),
|
||||
Ctrl = false,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -45,7 +48,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NextTab,
|
||||
Command = nameof(Command.NextTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -58,7 +61,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.PreviousTab,
|
||||
Command = nameof(Command.PreviousTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
@@ -66,12 +69,33 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
}
|
||||
};
|
||||
|
||||
case Command.SwitchToTerm1:
|
||||
case Command.SwitchToTerm2:
|
||||
case Command.SwitchToTerm3:
|
||||
case Command.SwitchToTerm4:
|
||||
case Command.SwitchToTerm5:
|
||||
case Command.SwitchToTerm6:
|
||||
case Command.SwitchToTerm7:
|
||||
case Command.SwitchToTerm8:
|
||||
case Command.SwitchToTerm9:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = command.ToString(),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
Key = (int)ExtendedVirtualKey.Number1 + (command - Command.SwitchToTerm1)
|
||||
}
|
||||
};
|
||||
|
||||
case Command.NewTab:
|
||||
return new List<KeyBinding>
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NewTab,
|
||||
Command = nameof(Command.NewTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -84,7 +108,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ConfigurableNewTab,
|
||||
Command = nameof(Command.ConfigurableNewTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = true,
|
||||
@@ -97,7 +121,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.CloseTab,
|
||||
Command = nameof(Command.CloseTab),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -110,7 +134,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NewWindow,
|
||||
Command = nameof(Command.NewWindow),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -123,7 +147,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ShowSettings,
|
||||
Command = nameof(Command.ShowSettings),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -136,7 +160,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Copy,
|
||||
Command = nameof(Command.Copy),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -149,7 +173,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Paste,
|
||||
Command = nameof(Command.Paste),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -162,7 +186,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Search,
|
||||
Command = nameof(Command.Search),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -175,7 +199,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ToggleFullScreen,
|
||||
Command = nameof(Command.ToggleFullScreen),
|
||||
Ctrl = false,
|
||||
Alt = true,
|
||||
Shift = false,
|
||||
@@ -188,7 +212,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.SelectAll,
|
||||
Command = nameof(Command.SelectAll),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -201,7 +225,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Clear,
|
||||
Command = nameof(Command.Clear),
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Shift = false,
|
||||
@@ -218,6 +242,59 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
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
|
||||
@@ -229,6 +306,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
FontFamily = "Consolas",
|
||||
FontSize = 13,
|
||||
BackgroundOpacity = 0.8,
|
||||
Padding = 12,
|
||||
ScrollBackLimit = 1000
|
||||
};
|
||||
}
|
||||
@@ -249,7 +327,19 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty
|
||||
WorkingDirectory = string.Empty,
|
||||
KeyBindings = new []
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = GetDefaultShellProfileId().ToString(),
|
||||
Ctrl=true,
|
||||
Alt=true,
|
||||
Shift=false,
|
||||
Meta=false,
|
||||
Key=(int)ExtendedVirtualKey.Number1
|
||||
}
|
||||
}
|
||||
},
|
||||
new ShellProfile
|
||||
{
|
||||
@@ -258,7 +348,19 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\Windows\System32\cmd.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty
|
||||
WorkingDirectory = string.Empty,
|
||||
KeyBindings = new []
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = "ab942a61-7673-4755-9bd8-765aff91d9a3",
|
||||
Ctrl=true,
|
||||
Alt=true,
|
||||
Shift=false,
|
||||
Meta=false,
|
||||
Key=(int)ExtendedVirtualKey.Number2
|
||||
}
|
||||
}
|
||||
},
|
||||
new ShellProfile
|
||||
{
|
||||
@@ -267,7 +369,19 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
Arguments = string.Empty,
|
||||
Location = @"C:\windows\system32\wsl.exe",
|
||||
PreInstalled = true,
|
||||
WorkingDirectory = string.Empty
|
||||
WorkingDirectory = string.Empty,
|
||||
KeyBindings = new []
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = "e5785ad6-584f-40cb-bdcd-d5b3b3953e7f",
|
||||
Ctrl=true,
|
||||
Alt=true,
|
||||
Shift=false,
|
||||
Meta=false,
|
||||
Key=(int)ExtendedVirtualKey.Number3
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -437,4 +551,4 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
return new[] { defaultXterm, powerShell, homebrew, tomorrow, ubuntu };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,46 @@
|
||||
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;
|
||||
}
|
||||
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class KeyboardCommandService : IKeyboardCommandService
|
||||
{
|
||||
private readonly Dictionary<string, Action> _commandHandlers = new Dictionary<string, Action>();
|
||||
|
||||
public void RegisterCommandHandler(string command, Action handler)
|
||||
{
|
||||
if (_commandHandlers.ContainsKey(command))
|
||||
{
|
||||
throw new InvalidOperationException("command already registered");
|
||||
}
|
||||
|
||||
_commandHandlers[command] = handler ?? throw new ArgumentNullException(nameof(handler));
|
||||
}
|
||||
|
||||
public void DeregisterCommandHandler(string command)
|
||||
{
|
||||
if (_commandHandlers.ContainsKey(command))
|
||||
{
|
||||
_commandHandlers.Remove(command);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendCommand(string command)
|
||||
{
|
||||
if (_commandHandlers.TryGetValue(command, out Action handler))
|
||||
{
|
||||
handler.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// already registered by SystemTray's ToggleWindowService
|
||||
if (command == Command.ToggleWindow)
|
||||
if (command == nameof(Command.ToggleWindow))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"No handler registered for command: {command}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException($"No handler registered for command: {command}");
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Services
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class NotificationService
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
public void ShowNotification(string title, string content, string url = null)
|
||||
{
|
||||
@@ -14,19 +14,11 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
public const string DefaultShellProfileKey = "DefaultShellProfile";
|
||||
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IApplicationDataContainer _localSettings;
|
||||
private readonly IApplicationDataContainer _themes;
|
||||
private readonly IApplicationDataContainer _keyBindings;
|
||||
private readonly IApplicationDataContainer _shellProfiles;
|
||||
private readonly IApplicationDataContainer _localSettings;
|
||||
private readonly IApplicationDataContainer _roamingSettings;
|
||||
|
||||
public event EventHandler<Guid> CurrentThemeChanged;
|
||||
|
||||
public event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
|
||||
public event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||
|
||||
public event EventHandler KeyBindingsChanged;
|
||||
private readonly IApplicationDataContainer _shellProfiles;
|
||||
private readonly IApplicationDataContainer _themes;
|
||||
|
||||
public SettingsService(IDefaultValueProvider defaultValueProvider, ApplicationDataContainers containers)
|
||||
{
|
||||
@@ -40,15 +32,61 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
|
||||
foreach (var theme in _defaultValueProvider.GetPreInstalledThemes())
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
if (GetTheme(theme.Id) == null)
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var shellProfile in _defaultValueProvider.GetPreinstalledShellProfiles())
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
if (GetShellProfile(shellProfile.Id) == null)
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<ApplicationSettings> ApplicationSettingsChanged;
|
||||
|
||||
public event EventHandler<TerminalTheme> ThemeAdded;
|
||||
public event EventHandler<Guid> CurrentThemeChanged;
|
||||
public event EventHandler<Guid> ThemeDeleted;
|
||||
|
||||
public event EventHandler KeyBindingsChanged;
|
||||
|
||||
public event EventHandler<ShellProfile> ShellProfileAdded;
|
||||
public event EventHandler<Guid> ShellProfileDeleted;
|
||||
|
||||
public event EventHandler<TerminalOptions> TerminalOptionsChanged;
|
||||
|
||||
public void DeleteShellProfile(Guid id)
|
||||
{
|
||||
_shellProfiles.Delete(id.ToString());
|
||||
ShellProfileDeleted?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public void DeleteTheme(Guid id)
|
||||
{
|
||||
_themes.Delete(id.ToString());
|
||||
|
||||
foreach (var profile in GetShellProfiles())
|
||||
{
|
||||
if (profile.TerminalThemeId == id)
|
||||
{
|
||||
profile.TerminalThemeId = Guid.Empty;
|
||||
SaveShellProfile(profile);
|
||||
}
|
||||
}
|
||||
|
||||
ThemeDeleted?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public ApplicationSettings GetApplicationSettings()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(ApplicationSettings), _defaultValueProvider.GetDefaultApplicationSettings());
|
||||
}
|
||||
|
||||
public TerminalTheme GetCurrentTheme()
|
||||
{
|
||||
var id = GetCurrentThemeId();
|
||||
@@ -71,96 +109,6 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
return _defaultValueProvider.GetDefaultThemeId();
|
||||
}
|
||||
|
||||
public void SaveCurrentThemeId(Guid id)
|
||||
{
|
||||
_roamingSettings.SetValue(CurrentThemeKey, id);
|
||||
|
||||
CurrentThemeChanged?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public void SaveTheme(TerminalTheme theme)
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
|
||||
if (theme.Id == GetCurrentThemeId())
|
||||
{
|
||||
CurrentThemeChanged?.Invoke(this, theme.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteTheme(Guid id)
|
||||
{
|
||||
_themes.Delete(id.ToString());
|
||||
}
|
||||
|
||||
public IEnumerable<TerminalTheme> GetThemes()
|
||||
{
|
||||
return _themes.GetAll().Select(x => JsonConvert.DeserializeObject<TerminalTheme>((string)x)).ToList();
|
||||
}
|
||||
|
||||
public TerminalTheme GetTheme(Guid id)
|
||||
{
|
||||
return _themes.ReadValueFromJson(id.ToString(), default(TerminalTheme));
|
||||
}
|
||||
|
||||
public TerminalOptions GetTerminalOptions()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(TerminalOptions), _defaultValueProvider.GetDefaultTerminalOptions());
|
||||
}
|
||||
|
||||
public void SaveTerminalOptions(TerminalOptions terminalOptions)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
||||
TerminalOptionsChanged?.Invoke(this, terminalOptions);
|
||||
}
|
||||
|
||||
public ApplicationSettings GetApplicationSettings()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(ApplicationSettings), _defaultValueProvider.GetDefaultApplicationSettings());
|
||||
}
|
||||
|
||||
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
|
||||
ApplicationSettingsChanged?.Invoke(this, applicationSettings);
|
||||
}
|
||||
|
||||
public IDictionary<Command, ICollection<KeyBinding>> GetKeyBindings()
|
||||
{
|
||||
var keyBindings = new Dictionary<Command, ICollection<KeyBinding>>();
|
||||
foreach (Command command in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
keyBindings.Add(command, _keyBindings.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null) ?? _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||
}
|
||||
|
||||
return keyBindings;
|
||||
}
|
||||
|
||||
public void SaveKeyBindings(Command command, ICollection<KeyBinding> keyBindings)
|
||||
{
|
||||
_keyBindings.WriteValueAsJson(command.ToString(), keyBindings);
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
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 Guid GetDefaultShellProfileId()
|
||||
{
|
||||
if (_localSettings.TryGetValue(DefaultShellProfileKey, out object value))
|
||||
{
|
||||
return (Guid)value;
|
||||
}
|
||||
return _defaultValueProvider.GetDefaultShellProfileId();
|
||||
}
|
||||
|
||||
public ShellProfile GetDefaultShellProfile()
|
||||
{
|
||||
var id = GetDefaultShellProfileId();
|
||||
@@ -174,9 +122,29 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
return profile;
|
||||
}
|
||||
|
||||
public void SaveDefaultShellProfileId(Guid id)
|
||||
public ShellProfile GetShellProfile(Guid id)
|
||||
{
|
||||
_localSettings.SetValue(DefaultShellProfileKey, id);
|
||||
return _shellProfiles.ReadValueFromJson(id.ToString(), default(ShellProfile));
|
||||
}
|
||||
|
||||
public Guid GetDefaultShellProfileId()
|
||||
{
|
||||
if (_localSettings.TryGetValue(DefaultShellProfileKey, out object value))
|
||||
{
|
||||
return (Guid)value;
|
||||
}
|
||||
return _defaultValueProvider.GetDefaultShellProfileId();
|
||||
}
|
||||
|
||||
public IDictionary<string, ICollection<KeyBinding>> GetCommandKeyBindings()
|
||||
{
|
||||
var keyBindings = new Dictionary<string, ICollection<KeyBinding>>();
|
||||
|
||||
foreach (Command command in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
keyBindings.Add(command.ToString(), _keyBindings.ReadValueFromJson<Collection<KeyBinding>>(command.ToString(), null) ?? _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||
}
|
||||
return keyBindings;
|
||||
}
|
||||
|
||||
public IEnumerable<ShellProfile> GetShellProfiles()
|
||||
@@ -184,14 +152,97 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
return _shellProfiles.GetAll().Select(x => JsonConvert.DeserializeObject<ShellProfile>((string)x)).ToList();
|
||||
}
|
||||
|
||||
public void SaveShellProfile(ShellProfile shellProfile)
|
||||
public IEnumerable<TabTheme> GetTabThemes()
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
return _defaultValueProvider.GetDefaultTabThemes();
|
||||
}
|
||||
|
||||
public void DeleteShellProfile(Guid id)
|
||||
public TerminalOptions GetTerminalOptions()
|
||||
{
|
||||
_shellProfiles.Delete(id.ToString());
|
||||
return _roamingSettings.ReadValueFromJson(nameof(TerminalOptions), _defaultValueProvider.GetDefaultTerminalOptions());
|
||||
}
|
||||
|
||||
public TerminalTheme GetTheme(Guid id)
|
||||
{
|
||||
return _themes.ReadValueFromJson(id.ToString(), default(TerminalTheme));
|
||||
}
|
||||
|
||||
public IEnumerable<TerminalTheme> GetThemes()
|
||||
{
|
||||
return _themes.GetAll().Select(x => JsonConvert.DeserializeObject<TerminalTheme>((string)x)).ToList();
|
||||
}
|
||||
|
||||
public void ResetKeyBindings()
|
||||
{
|
||||
foreach (Command command in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
_keyBindings.WriteValueAsJson(command.ToString(), _defaultValueProvider.GetDefaultKeyBindings(command));
|
||||
}
|
||||
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
|
||||
ApplicationSettingsChanged?.Invoke(this, applicationSettings);
|
||||
}
|
||||
|
||||
public void SaveCurrentThemeId(Guid id)
|
||||
{
|
||||
_roamingSettings.SetValue(CurrentThemeKey, id);
|
||||
|
||||
CurrentThemeChanged?.Invoke(this, id);
|
||||
}
|
||||
|
||||
public void SaveDefaultShellProfileId(Guid id)
|
||||
{
|
||||
_localSettings.SetValue(DefaultShellProfileKey, id);
|
||||
}
|
||||
|
||||
public void SaveKeyBindings(string command, ICollection<KeyBinding> keyBindings)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(Command), command))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
_keyBindings.WriteValueAsJson(command.ToString(), keyBindings);
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void SaveShellProfile(ShellProfile shellProfile, bool newShell = false)
|
||||
{
|
||||
_shellProfiles.WriteValueAsJson(shellProfile.Id.ToString(), shellProfile);
|
||||
|
||||
// When saving the shell profile, we also need to update keybindings for everywhere.
|
||||
KeyBindingsChanged?.Invoke(this, System.EventArgs.Empty);
|
||||
|
||||
if (newShell)
|
||||
{
|
||||
ShellProfileAdded?.Invoke(this, shellProfile);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveTerminalOptions(TerminalOptions terminalOptions)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
||||
TerminalOptionsChanged?.Invoke(this, terminalOptions);
|
||||
}
|
||||
|
||||
public void SaveTheme(TerminalTheme theme, bool newTheme = false)
|
||||
{
|
||||
_themes.WriteValueAsJson(theme.Id.ToString(), theme);
|
||||
|
||||
if (theme.Id == GetCurrentThemeId())
|
||||
{
|
||||
CurrentThemeChanged?.Invoke(this, theme.Id);
|
||||
}
|
||||
|
||||
if (newTheme)
|
||||
{
|
||||
ThemeAdded?.Invoke(this, theme);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
|
||||
public Task UpdateToggleWindowKeyBindings()
|
||||
{
|
||||
var keyBindings = _settingsService.GetKeyBindings()[Command.ToggleWindow];
|
||||
var keyBindings = _settingsService.GetCommandKeyBindings()[nameof(Command.ToggleWindow)];
|
||||
|
||||
var request = new SetToggleWindowKeyBindingsRequest
|
||||
{
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using Windows.ApplicationModel;
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class UpdateService : IUpdateService
|
||||
{
|
||||
private const string apiEndpoint = "https://api.github.com";
|
||||
|
||||
private readonly INotificationService _notificationService;
|
||||
|
||||
public UpdateService(INotificationService notificationService)
|
||||
{
|
||||
_notificationService = notificationService;
|
||||
}
|
||||
|
||||
public async Task CheckForUpdate(bool notifyNoUpdate = false)
|
||||
{
|
||||
var latest = await GetLatestVersionAsync();
|
||||
if (latest > GetCurrentVersion())
|
||||
{
|
||||
_notificationService.ShowNotification("Update available",
|
||||
"Click to open the releases page.", "https://github.com/felixse/FluentTerminal/releases");
|
||||
}
|
||||
else if (notifyNoUpdate)
|
||||
{
|
||||
_notificationService.ShowNotification("No update available", "You're up to date!");
|
||||
}
|
||||
}
|
||||
|
||||
public Version GetCurrentVersion()
|
||||
{
|
||||
var currentVersion = Package.Current.Id.Version;
|
||||
return new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build, currentVersion.Revision);
|
||||
}
|
||||
|
||||
public async Task<Version> GetLatestVersionAsync()
|
||||
{
|
||||
var restClient = new RestClient(apiEndpoint);
|
||||
var restRequest = new RestRequest("/repos/felixse/fluentterminal/releases", Method.GET);
|
||||
|
||||
var restResponse = await restClient.ExecuteTaskAsync(restRequest);
|
||||
if (restResponse.IsSuccessful)
|
||||
{
|
||||
dynamic restResponseData = JsonConvert.DeserializeObject(restResponse.Content);
|
||||
string tag = restResponseData[0].tag_name;
|
||||
var latestVersion = new Version(tag);
|
||||
return new Version(latestVersion.Major, latestVersion.Minor, latestVersion.Build, latestVersion.Revision);
|
||||
}
|
||||
return new Version(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Fleck" Version="1.0.3" />
|
||||
<PackageReference Include="MvvmLightLibs" Version="5.4.1" />
|
||||
<PackageReference Include="ReactiveUI" Version="9.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -34,20 +34,40 @@ namespace FluentTerminal.App.ViewModels
|
||||
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
|
||||
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
_settingsService.ShellProfileAdded += OnShellProfileAdded;
|
||||
_settingsService.ShellProfileDeleted += OnShellProfileDeleted;
|
||||
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
_dialogService = dialogService;
|
||||
_applicationView = applicationView;
|
||||
_dispatcherTimer = dispatcherTimer;
|
||||
_clipboardService = clipboardService;
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NewTab, () => AddTerminal(null, false));
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.ConfigurableNewTab, () => AddTerminal(null, true));
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.CloseTab, CloseCurrentTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NextTab, SelectNextTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.PreviousTab, SelectPreviousTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NewWindow, NewWindow);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.ShowSettings, ShowSettings);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.ToggleFullScreen, ToggleFullScreen);
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewTab), () => AddTerminal(null, false, Guid.Empty));
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ConfigurableNewTab), () => AddTerminal(null, true, Guid.Empty));
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.CloseTab), CloseCurrentTab);
|
||||
|
||||
// Add all of the commands for switching to a tab of a given ID, if there's one open there
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
var switchCmd = Command.SwitchToTerm1 + i;
|
||||
int tabNumber = i;
|
||||
void handler() => SelectTabNumber(tabNumber);
|
||||
_keyboardCommandService.RegisterCommandHandler(switchCmd.ToString(), handler);
|
||||
}
|
||||
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NextTab), SelectNextTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.PreviousTab), SelectPreviousTab);
|
||||
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.NewWindow), NewWindow);
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ShowSettings), ShowSettings);
|
||||
_keyboardCommandService.RegisterCommandHandler(nameof(Command.ToggleFullScreen), ToggleFullScreen);
|
||||
|
||||
foreach (ShellProfile profile in _settingsService.GetShellProfiles())
|
||||
{
|
||||
_keyboardCommandService.RegisterCommandHandler(profile.Id.ToString(), () => AddTerminal(profile.WorkingDirectory, false, profile.Id));
|
||||
}
|
||||
|
||||
var currentTheme = _settingsService.GetCurrentTheme();
|
||||
var options = _settingsService.GetTerminalOptions();
|
||||
Background = currentTheme.Colors.Background;
|
||||
@@ -55,13 +75,24 @@ namespace FluentTerminal.App.ViewModels
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
TabsPosition = _applicationSettings.TabsPosition;
|
||||
|
||||
AddTerminalCommand = new RelayCommand(() => AddTerminal(null, false));
|
||||
AddTerminalCommand = new RelayCommand(() => AddTerminal(null, false, Guid.Empty));
|
||||
ShowAboutCommand = new RelayCommand(ShowAbout);
|
||||
ShowSettingsCommand = new RelayCommand(ShowSettings);
|
||||
|
||||
_applicationView.CloseRequested += OnCloseRequest;
|
||||
Terminals.CollectionChanged += OnTerminalsCollectionChanged;
|
||||
}
|
||||
|
||||
private void OnShellProfileDeleted(object sender, Guid e)
|
||||
{
|
||||
_keyboardCommandService.DeregisterCommandHandler(e.ToString());
|
||||
}
|
||||
|
||||
private void OnShellProfileAdded(object sender, ShellProfile e)
|
||||
{
|
||||
_keyboardCommandService.RegisterCommandHandler(e.Id.ToString(), () => AddTerminal(e.WorkingDirectory, false, e.Id));
|
||||
}
|
||||
|
||||
private void OnTerminalsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
RaisePropertyChanged(nameof(ShowTabsOnTop));
|
||||
@@ -74,6 +105,8 @@ namespace FluentTerminal.App.ViewModels
|
||||
|
||||
public event EventHandler ShowSettingsRequested;
|
||||
|
||||
public event EventHandler ShowAboutRequested;
|
||||
|
||||
public RelayCommand AddTerminalCommand { get; }
|
||||
|
||||
public bool ShowTabsOnTop
|
||||
@@ -118,6 +151,8 @@ namespace FluentTerminal.App.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand ShowAboutCommand { get; }
|
||||
|
||||
public RelayCommand ShowSettingsCommand { get; }
|
||||
|
||||
public TabsPosition TabsPosition
|
||||
@@ -128,11 +163,16 @@ namespace FluentTerminal.App.ViewModels
|
||||
|
||||
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
|
||||
|
||||
public Task AddTerminal(string startupDirectory, bool showProfileSelection)
|
||||
/// <summary>
|
||||
/// Add a new terminal window, either with a specified terminal profile, with the default, or by showing the profile selection dialog.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task AddTerminal(string startupDirectory, bool showProfileSelection, Guid profileId)
|
||||
{
|
||||
return _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
ShellProfile profile = null;
|
||||
|
||||
if (showProfileSelection)
|
||||
{
|
||||
profile = await _dialogService.ShowProfileSelectionDialogAsync();
|
||||
@@ -142,9 +182,12 @@ namespace FluentTerminal.App.ViewModels
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (profileId == Guid.Empty)
|
||||
{
|
||||
profile = _settingsService.GetDefaultShellProfile();
|
||||
} else
|
||||
{
|
||||
profile = _settingsService.GetShellProfile(profileId);
|
||||
}
|
||||
|
||||
var terminal = new TerminalViewModel(_settingsService, _trayProcessCommunicationService, _dialogService, _keyboardCommandService,
|
||||
@@ -244,6 +287,14 @@ namespace FluentTerminal.App.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
private void SelectTabNumber(int tabNumber)
|
||||
{
|
||||
if (tabNumber < Terminals.Count)
|
||||
{
|
||||
SelectedTerminal = Terminals[tabNumber];
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectNextTab()
|
||||
{
|
||||
var currentIndex = Terminals.IndexOf(SelectedTerminal);
|
||||
@@ -258,6 +309,11 @@ namespace FluentTerminal.App.ViewModels
|
||||
SelectedTerminal = Terminals[previousIndex];
|
||||
}
|
||||
|
||||
private void ShowAbout()
|
||||
{
|
||||
ShowAboutRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void ShowSettings()
|
||||
{
|
||||
ShowSettingsRequested?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
using GalaSoft.MvvmLight;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class AboutPageViewModel : ViewModelBase
|
||||
{
|
||||
private const string BaseUrl = "https://github.com/felixse/FluentTerminal/releases/tag/";
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IUpdateService _updateService;
|
||||
private string _latestVersion;
|
||||
private readonly IApplicationView _applicationView;
|
||||
|
||||
public AboutPageViewModel(ISettingsService settingsService, IUpdateService updateService, IApplicationView applicationView)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_updateService = updateService;
|
||||
_applicationView = applicationView;
|
||||
|
||||
CheckForUpdatesCommand = new AsyncCommand(() => CheckForUpdate(true));
|
||||
}
|
||||
|
||||
public IAsyncCommand CheckForUpdatesCommand { get; }
|
||||
|
||||
public string CurrentVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
var version = _updateService.GetCurrentVersion();
|
||||
return ConvertVersionToString(version);
|
||||
}
|
||||
}
|
||||
|
||||
public string CurrentVersionReleaseNotesURL => BaseUrl + CurrentVersion;
|
||||
|
||||
public string LatestVersion
|
||||
{
|
||||
get => _latestVersion;
|
||||
set
|
||||
{
|
||||
if (Set(ref _latestVersion, value))
|
||||
{
|
||||
RaisePropertyChanged(nameof(LatestVersionFound));
|
||||
RaisePropertyChanged(nameof(LatestVersionLoading));
|
||||
RaisePropertyChanged(nameof(LatestVersionNotFound));
|
||||
RaisePropertyChanged(nameof(LatestVersionReleaseNotesURL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool LatestVersionFound => !LatestVersionNotFound && !LatestVersionLoading;
|
||||
|
||||
public bool LatestVersionLoading => LatestVersion == null;
|
||||
|
||||
public bool LatestVersionNotFound => LatestVersion == "0.0.0.0";
|
||||
|
||||
public string LatestVersionReleaseNotesURL => BaseUrl + LatestVersion;
|
||||
|
||||
public Task OnNavigatedTo()
|
||||
{
|
||||
return CheckForUpdate(false);
|
||||
}
|
||||
|
||||
public async Task CheckForUpdate(bool notifyNoUpdate)
|
||||
{
|
||||
var version = ConvertVersionToString(await _updateService.GetLatestVersionAsync());
|
||||
await _applicationView.RunOnDispatcherThread(() => LatestVersion = version);
|
||||
await _updateService.CheckForUpdate(notifyNoUpdate);
|
||||
}
|
||||
|
||||
private string ConvertVersionToString(Version version)
|
||||
{
|
||||
return string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,9 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
private bool _canEnableStartupTask;
|
||||
private bool _editingNewTerminalLocation;
|
||||
private bool _editingTabsPosition;
|
||||
private bool _editingInactiveTabColorMode;
|
||||
private bool _startupTaskEnabled;
|
||||
private bool _shouldRestartForTrayMessage;
|
||||
private string _startupTaskErrorMessage;
|
||||
|
||||
public GeneralPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IStartupTaskService startupTaskService)
|
||||
@@ -52,6 +54,43 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
}
|
||||
}
|
||||
|
||||
public bool EnableTrayIcon
|
||||
{
|
||||
get => _applicationSettings.EnableTrayIcon;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.EnableTrayIcon != value)
|
||||
{
|
||||
_applicationSettings.EnableTrayIcon = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
|
||||
// Toggle message telling user to restart the program
|
||||
ShouldRestartForTrayMessage = !ShouldRestartForTrayMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldRestartForTrayMessage
|
||||
{
|
||||
get => _shouldRestartForTrayMessage;
|
||||
set => Set(ref _shouldRestartForTrayMessage, value);
|
||||
}
|
||||
|
||||
public bool ShowNewOutputIndicator
|
||||
{
|
||||
get => _applicationSettings.ShowNewOutputIndicator;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.ShowNewOutputIndicator != value)
|
||||
{
|
||||
_applicationSettings.ShowNewOutputIndicator = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool BottomIsSelected
|
||||
{
|
||||
get => TabsPosition == TabsPosition.Bottom;
|
||||
@@ -176,6 +215,36 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -192,8 +261,11 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
ConfirmClosingWindows = defaults.ConfirmClosingWindows;
|
||||
ConfirmClosingTabs = defaults.ConfirmClosingTabs;
|
||||
UnderlineSelectedTab = defaults.UnderlineSelectedTab;
|
||||
InactiveTabColorMode = defaults.InactiveTabColorMode;
|
||||
NewTerminalLocation = defaults.NewTerminalLocation;
|
||||
AlwaysShowTabs = defaults.AlwaysShowTabs;
|
||||
ShowNewOutputIndicator = defaults.ShowNewOutputIndicator;
|
||||
EnableTrayIcon = defaults.EnableTrayIcon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
using FluentTerminal.Models;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -11,18 +11,21 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
|
||||
public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService)
|
||||
public KeyBindingViewModel(KeyBinding keyBinding, IDialogService dialogService, KeyBindingsViewModel parent)
|
||||
{
|
||||
Model = keyBinding;
|
||||
Parent = parent;
|
||||
_dialogService = dialogService;
|
||||
EditCommand = new RelayCommand(async () => await Edit().ConfigureAwait(false));
|
||||
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false));
|
||||
EditCommand = new AsyncCommand(Edit);
|
||||
DeleteCommand = new AsyncCommand(Delete);
|
||||
}
|
||||
|
||||
public event EventHandler Deleted;
|
||||
|
||||
public event EventHandler Edited;
|
||||
|
||||
public KeyBindingsViewModel Parent { get; }
|
||||
|
||||
public bool Meta
|
||||
{
|
||||
get => Model.Meta;
|
||||
@@ -62,8 +65,8 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand DeleteCommand { get; }
|
||||
public RelayCommand EditCommand { get; }
|
||||
public IAsyncCommand DeleteCommand { get; }
|
||||
public IAsyncCommand EditCommand { get; }
|
||||
|
||||
public int Key
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
@@ -17,7 +18,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITrayProcessCommunicationService _trayProcessCommunicationService;
|
||||
private IDictionary<Command, ICollection<KeyBinding>> _keyBindings;
|
||||
private IDictionary<string, ICollection<KeyBinding>> _keyBindings;
|
||||
|
||||
public KeyBindingsPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, ITrayProcessCommunicationService trayProcessCommunicationService)
|
||||
{
|
||||
@@ -26,19 +27,19 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_trayProcessCommunicationService = trayProcessCommunicationService;
|
||||
RestoreDefaultsCommand = new RelayCommand(async () => await RestoreDefaults().ConfigureAwait(false));
|
||||
AddCommand = new RelayCommand<Command>(async command => await Add(command).ConfigureAwait(false));
|
||||
AddCommand = new RelayCommand<string>(async command => await Add(command).ConfigureAwait(false));
|
||||
|
||||
Initialize(_settingsService.GetKeyBindings());
|
||||
Initialize(_settingsService.GetCommandKeyBindings());
|
||||
}
|
||||
|
||||
public RelayCommand<Command> AddCommand { get; }
|
||||
public RelayCommand<string> AddCommand { get; }
|
||||
public ObservableCollection<KeyBindingsViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingsViewModel>();
|
||||
public RelayCommand RestoreDefaultsCommand { get; }
|
||||
|
||||
private async Task Add(Command command)
|
||||
private async Task Add(string command)
|
||||
{
|
||||
var keyBinding = KeyBindings.FirstOrDefault(k => k.Command == command);
|
||||
await keyBinding?.Add();
|
||||
await keyBinding?.ShowAddKeyBindingDialog();
|
||||
}
|
||||
|
||||
private void ClearKeyBindings()
|
||||
@@ -51,7 +52,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
KeyBindings.Clear();
|
||||
}
|
||||
|
||||
private void Initialize(IDictionary<Command, ICollection<KeyBinding>> keyBindings)
|
||||
private void Initialize(IDictionary<string, ICollection<KeyBinding>> keyBindings)
|
||||
{
|
||||
_keyBindings = keyBindings;
|
||||
|
||||
@@ -60,13 +61,17 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
foreach (var value in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
var command = (Command)value;
|
||||
var viewModel = new KeyBindingsViewModel(command, _keyBindings[command], _dialogService);
|
||||
var viewModel = new KeyBindingsViewModel(command.ToString(), _dialogService, EnumHelper.GetEnumDescription(command), true);
|
||||
foreach (var keyBinding in _keyBindings[command.ToString()])
|
||||
{
|
||||
viewModel.Add(keyBinding);
|
||||
}
|
||||
viewModel.Edited += OnEdited;
|
||||
KeyBindings.Add(viewModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEdited(Command command, ICollection<KeyBinding> keyBindings)
|
||||
private void OnEdited(string command, ICollection<KeyBinding> keyBindings)
|
||||
{
|
||||
_settingsService.SaveKeyBindings(command, keyBindings);
|
||||
_trayProcessCommunicationService.UpdateToggleWindowKeyBindings();
|
||||
@@ -79,7 +84,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
_settingsService.ResetKeyBindings();
|
||||
Initialize(_settingsService.GetKeyBindings());
|
||||
Initialize(_settingsService.GetCommandKeyBindings());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
@@ -13,62 +12,73 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
public class KeyBindingsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ICollection<KeyBinding> _keyBindings;
|
||||
private bool _editable;
|
||||
|
||||
public KeyBindingsViewModel(Command command, ICollection<KeyBinding> keyBindings, IDialogService dialogService)
|
||||
public KeyBindingsViewModel(string command, IDialogService dialogService, string commandName, bool editable)
|
||||
{
|
||||
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);
|
||||
}
|
||||
Command = command;
|
||||
CommandName = commandName;
|
||||
Editable = editable;
|
||||
}
|
||||
|
||||
public delegate void EditedEvent(Command command, ICollection<KeyBinding> keyBindings);
|
||||
public delegate void EditedEvent(string command, ICollection<KeyBinding> keyBindings);
|
||||
|
||||
public event EditedEvent Edited;
|
||||
|
||||
public Command Command { get; }
|
||||
public string Command { get; }
|
||||
|
||||
public string CommandName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the "Edit" and "Delete" links appear next to the key binding list.
|
||||
/// </summary>
|
||||
public bool Editable
|
||||
{
|
||||
get => _editable;
|
||||
set => Set(ref _editable, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<KeyBindingViewModel> KeyBindings { get; } = new ObservableCollection<KeyBindingViewModel>();
|
||||
|
||||
public async Task Add()
|
||||
public async Task ShowAddKeyBindingDialog()
|
||||
{
|
||||
var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService);
|
||||
var newKeyBinding = new KeyBindingViewModel(new KeyBinding { Command = Command }, _dialogService, this);
|
||||
|
||||
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);
|
||||
Add(newKeyBinding.Model);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
KeyBindings.Clear();
|
||||
}
|
||||
|
||||
public void Add(KeyBinding keyBinding)
|
||||
{
|
||||
var viewModel = new KeyBindingViewModel(keyBinding, _dialogService, this);
|
||||
|
||||
viewModel.Deleted += ViewModel_Deleted;
|
||||
viewModel.Edited += ViewModel_Edited;
|
||||
KeyBindings.Add(viewModel);
|
||||
Edited?.Invoke(Command, KeyBindings.Select(x => x.Model).ToList());
|
||||
}
|
||||
|
||||
private void ViewModel_Deleted(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is KeyBindingViewModel keyBinding)
|
||||
{
|
||||
_keyBindings.Remove(keyBinding.Model);
|
||||
KeyBindings.Remove(keyBinding);
|
||||
Edited?.Invoke(Command, _keyBindings);
|
||||
Edited?.Invoke(Command, KeyBindings.Select(x => x.Model).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewModel_Edited(object sender, EventArgs e)
|
||||
{
|
||||
Edited?.Invoke(Command, _keyBindings);
|
||||
Edited?.Invoke(Command, KeyBindings.Select(x => x.Model).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
@@ -15,20 +16,23 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
private ShellProfileViewModel _selectedShellProfile;
|
||||
private readonly IApplicationView _applicationView;
|
||||
|
||||
public ProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IFileSystemService fileSystemService)
|
||||
public ProfilesPageViewModel(ISettingsService settingsService, IDialogService dialogService, IDefaultValueProvider defaultValueProvider, IFileSystemService fileSystemService, IApplicationView applicationView)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
_fileSystemService = fileSystemService;
|
||||
_applicationView = applicationView;
|
||||
|
||||
CreateShellProfileCommand = new RelayCommand(CreateShellProfile);
|
||||
CloneCommand = new RelayCommand<ShellProfileViewModel>(Clone);
|
||||
|
||||
var defaultShellProfileId = _settingsService.GetDefaultShellProfileId();
|
||||
foreach (var shellProfile in _settingsService.GetShellProfiles())
|
||||
{
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, settingsService, dialogService, fileSystemService);
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, settingsService, dialogService, fileSystemService, applicationView, defaultValueProvider);
|
||||
viewModel.Deleted += OnShellProfileDeleted;
|
||||
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
||||
|
||||
@@ -40,6 +44,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
}
|
||||
|
||||
SelectedShellProfile = ShellProfiles.First(p => p.IsDefault);
|
||||
|
||||
}
|
||||
|
||||
private void OnShellProfileSetAsDefault(object sender, EventArgs e)
|
||||
@@ -76,18 +81,38 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
|
||||
public RelayCommand CreateShellProfileCommand { get; }
|
||||
|
||||
public RelayCommand<ShellProfileViewModel> CloneCommand { get; }
|
||||
|
||||
private void CreateShellProfile()
|
||||
{
|
||||
var shellProfile = new ShellProfile
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = "New profile"
|
||||
Name = "New profile",
|
||||
KeyBindings = new List<KeyBinding>()
|
||||
};
|
||||
|
||||
_settingsService.SaveShellProfile(shellProfile);
|
||||
AddShellProfile(shellProfile);
|
||||
}
|
||||
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService);
|
||||
private void Clone(ShellProfileViewModel shellProfile)
|
||||
{
|
||||
var cloned = new ShellProfile(shellProfile.Model)
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = $"Copy of {shellProfile.Name}"
|
||||
};
|
||||
|
||||
AddShellProfile(cloned);
|
||||
}
|
||||
|
||||
private void AddShellProfile(ShellProfile shellProfile)
|
||||
{
|
||||
_settingsService.SaveShellProfile(shellProfile, true);
|
||||
|
||||
var viewModel = new ShellProfileViewModel(shellProfile, _settingsService, _dialogService, _fileSystemService, _applicationView, _defaultValueProvider);
|
||||
viewModel.EditCommand.Execute(null);
|
||||
viewModel.SetAsDefault += OnShellProfileSetAsDefault;
|
||||
viewModel.Deleted += OnShellProfileDeleted;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using ReactiveUI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
public class TerminalPageViewModel : ViewModelBase
|
||||
public class TerminalPageViewModel : ReactiveObject
|
||||
{
|
||||
private readonly TerminalOptions _terminalOptions;
|
||||
private readonly ISettingsService _settingsService;
|
||||
@@ -63,7 +65,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
_terminalOptions.CursorBlink = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +79,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
_terminalOptions.FontFamily = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,8 +92,20 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
if (_terminalOptions.BackgroundOpacity != value)
|
||||
{
|
||||
_terminalOptions.BackgroundOpacity = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Padding
|
||||
{
|
||||
get => _terminalOptions.Padding;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.Padding != value)
|
||||
{
|
||||
_terminalOptions.Padding = value;
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,12 +126,12 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
_terminalOptions.ScrollBackLimit = intValue;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +147,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
_terminalOptions.FontSize = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
this.RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,10 +166,10 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
_isEditingCursorStyle = true;
|
||||
_terminalOptions.CursorStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(BlockIsSelected));
|
||||
RaisePropertyChanged(nameof(BarIsSelected));
|
||||
RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||
this.RaisePropertyChanged();
|
||||
this.RaisePropertyChanged(nameof(BlockIsSelected));
|
||||
this.RaisePropertyChanged(nameof(BarIsSelected));
|
||||
this.RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||
_isEditingCursorStyle = false;
|
||||
}
|
||||
}
|
||||
@@ -171,10 +185,10 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
_isEditingScrollBarStyle = true;
|
||||
_terminalOptions.ScrollBarStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(HiddenIsSelected));
|
||||
RaisePropertyChanged(nameof(AutoHidingIsSelected));
|
||||
RaisePropertyChanged(nameof(VisibleIsSelected));
|
||||
this.RaisePropertyChanged();
|
||||
this.RaisePropertyChanged(nameof(HiddenIsSelected));
|
||||
this.RaisePropertyChanged(nameof(AutoHidingIsSelected));
|
||||
this.RaisePropertyChanged(nameof(VisibleIsSelected));
|
||||
_isEditingScrollBarStyle = false;
|
||||
}
|
||||
}
|
||||
@@ -209,6 +223,15 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
Sizes = Enumerable.Range(1, 72);
|
||||
|
||||
_terminalOptions = _settingsService.GetTerminalOptions();
|
||||
|
||||
this.ObservableForProperty(x => x.BackgroundOpacity).Throttle(TimeSpan.FromMilliseconds(800)).ObserveOn(RxApp.MainThreadScheduler).Subscribe(x =>
|
||||
{
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
});
|
||||
this.ObservableForProperty(x => x.Padding).Throttle(TimeSpan.FromMilliseconds(800)).ObserveOn(RxApp.MainThreadScheduler).Subscribe(x =>
|
||||
{
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
Colors = new TerminalColors(defaultTheme.Colors)
|
||||
};
|
||||
|
||||
_settingsService.SaveTheme(theme);
|
||||
_settingsService.SaveTheme(theme, true);
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
@@ -118,7 +118,7 @@ namespace FluentTerminal.App.ViewModels.Settings
|
||||
{
|
||||
var theme = await parser.Parse(file.Name, file.Content).ConfigureAwait(true);
|
||||
|
||||
_settingsService.SaveTheme(theme);
|
||||
_settingsService.SaveTheme(theme, true);
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService, _fileSystemService);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
|
||||
@@ -8,17 +8,20 @@ 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)
|
||||
ITrayProcessCommunicationService trayProcessCommunicationService, IThemeParserFactory themeParserFactory, ISystemFontService systemFontService,
|
||||
IFileSystemService fileSystemService, IStartupTaskService startupTaskService, IUpdateService updateService, IApplicationView applicationView)
|
||||
{
|
||||
About = new AboutPageViewModel(settingsService, updateService, applicationView);
|
||||
KeyBindings = new KeyBindingsPageViewModel(settingsService, dialogService, defaultValueProvider, trayProcessCommunicationService);
|
||||
General = new GeneralPageViewModel(settingsService, dialogService, defaultValueProvider, startupTaskService);
|
||||
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService);
|
||||
Shell = new ProfilesPageViewModel(settingsService, dialogService, defaultValueProvider, fileSystemService, applicationView);
|
||||
Terminal = new TerminalPageViewModel(settingsService, dialogService, defaultValueProvider, systemFontService);
|
||||
Themes = new ThemesPageViewModel(settingsService, dialogService, defaultValueProvider, themeParserFactory, fileSystemService);
|
||||
Mouse = new MousePageViewModel(settingsService, dialogService, defaultValueProvider);
|
||||
}
|
||||
|
||||
public event EventHandler Closed;
|
||||
public event EventHandler AboutPageRequested;
|
||||
|
||||
public GeneralPageViewModel General { get; }
|
||||
public KeyBindingsPageViewModel KeyBindings { get; }
|
||||
@@ -26,6 +29,12 @@ namespace FluentTerminal.App.ViewModels
|
||||
public TerminalPageViewModel Terminal { get; }
|
||||
public ThemesPageViewModel Themes { get; }
|
||||
public MousePageViewModel Mouse { get; }
|
||||
public AboutPageViewModel About { get; }
|
||||
|
||||
public void NavigateToAboutPage()
|
||||
{
|
||||
AboutPageRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
|
||||
@@ -1,70 +1,133 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.ViewModels.Infrastructure;
|
||||
using FluentTerminal.App.ViewModels.Settings;
|
||||
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 ShellProfile _shellProfile;
|
||||
private string _name;
|
||||
private string _arguments;
|
||||
private string _location;
|
||||
private string _workingDirectory;
|
||||
private string _fallbackName;
|
||||
private string _fallbackArguments;
|
||||
private string _fallbackLocation;
|
||||
private string _fallbackWorkingDirectory;
|
||||
private bool _isDefault;
|
||||
private bool _inEditMode;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private string _arguments;
|
||||
private ShellProfile _fallbackProfile;
|
||||
private bool _inEditMode;
|
||||
private bool _isDefault;
|
||||
private string _location;
|
||||
private string _name;
|
||||
private TabTheme _selectedTabTheme;
|
||||
private TerminalTheme _selectedTerminalTheme;
|
||||
private string _workingDirectory;
|
||||
private readonly IApplicationView _applicationView;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
|
||||
public ShellProfileViewModel(ShellProfile shellProfile, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService)
|
||||
public ShellProfileViewModel(ShellProfile shellProfile, ISettingsService settingsService, IDialogService dialogService, IFileSystemService fileSystemService, IApplicationView applicationView, IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
_shellProfile = shellProfile;
|
||||
Model = shellProfile;
|
||||
_settingsService = settingsService;
|
||||
_dialogService = dialogService;
|
||||
_fileSystemService = fileSystemService;
|
||||
_applicationView = applicationView;
|
||||
_defaultValueProvider = defaultValueProvider;
|
||||
|
||||
_settingsService.ThemeAdded += OnThemeAdded;
|
||||
_settingsService.ThemeDeleted += OnThemeDeleted;
|
||||
|
||||
TabThemes = new ObservableCollection<TabTheme>(settingsService.GetTabThemes());
|
||||
|
||||
TerminalThemes = new ObservableCollection<TerminalTheme>();
|
||||
TerminalThemes.Add(new TerminalTheme
|
||||
{
|
||||
Id = Guid.Empty,
|
||||
Name = "Default"
|
||||
});
|
||||
foreach (var theme in _settingsService.GetThemes())
|
||||
{
|
||||
TerminalThemes.Add(theme);
|
||||
}
|
||||
|
||||
KeyBindings = new KeyBindingsViewModel(shellProfile.Id.ToString(), _dialogService, string.Empty, false);
|
||||
|
||||
InitializeViewModelProperties(shellProfile);
|
||||
|
||||
SetDefaultCommand = new RelayCommand(SetDefault);
|
||||
DeleteCommand = new AsyncCommand(Delete, CanDelete);
|
||||
EditCommand = new RelayCommand(Edit);
|
||||
CancelEditCommand = new AsyncCommand(CancelEdit);
|
||||
SaveChangesCommand = new RelayCommand(SaveChanges);
|
||||
AddKeyboardShortcutCommand = new AsyncCommand(AddKeyboardShortcut);
|
||||
BrowseForCustomShellCommand = new AsyncCommand(BrowseForCustomShell);
|
||||
BrowseForWorkingDirectoryCommand = new AsyncCommand(BrowseForWorkingDirectory);
|
||||
RestoreDefaultsCommand = new AsyncCommand(RestoreDefaults);
|
||||
}
|
||||
|
||||
private void InitializeViewModelProperties(ShellProfile shellProfile)
|
||||
{
|
||||
SelectedTerminalTheme = TerminalThemes.FirstOrDefault(t => t.Id == shellProfile.TerminalThemeId);
|
||||
Id = shellProfile.Id;
|
||||
Name = shellProfile.Name;
|
||||
Arguments = shellProfile.Arguments;
|
||||
Location = shellProfile.Location;
|
||||
WorkingDirectory = shellProfile.WorkingDirectory;
|
||||
SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == shellProfile.TabThemeId);
|
||||
PreInstalled = shellProfile.PreInstalled;
|
||||
|
||||
SetDefaultCommand = new RelayCommand(SetDefault);
|
||||
DeleteCommand = new RelayCommand(async () => await Delete().ConfigureAwait(false), CanDelete);
|
||||
EditCommand = new RelayCommand(Edit, CanEdit);
|
||||
CancelEditCommand = new RelayCommand(async () => await CancelEdit().ConfigureAwait(false));
|
||||
SaveChangesCommand = new RelayCommand(SaveChanges);
|
||||
BrowseForCustomShellCommand = new RelayCommand(async () => await BrowseForCustomShell().ConfigureAwait(false));
|
||||
BrowseForWorkingDirectoryCommand = new RelayCommand(async () => await BrowseForWorkingDirectory().ConfigureAwait(false));
|
||||
KeyBindings.Clear();
|
||||
foreach (var keyBinding in shellProfile.KeyBindings.Select(x => new KeyBinding(x)).ToList())
|
||||
{
|
||||
KeyBindings.Add(keyBinding);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThemeAdded(object sender, TerminalTheme e)
|
||||
{
|
||||
_applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
TerminalThemes.Add(e);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnThemeDeleted(object sender, Guid e)
|
||||
{
|
||||
if (SelectedTerminalTheme.Id == e)
|
||||
{
|
||||
_applicationView.RunOnDispatcherThread(() =>
|
||||
{
|
||||
SelectedTerminalTheme = TerminalThemes.FirstOrDefault(x => x.Id == Guid.Empty);
|
||||
Model.TerminalThemeId = Guid.Empty;
|
||||
if (_fallbackProfile != null)
|
||||
{
|
||||
_fallbackProfile.TerminalThemeId = Guid.Empty;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler Deleted;
|
||||
|
||||
public event EventHandler SetAsDefault;
|
||||
|
||||
public RelayCommand DeleteCommand { get; }
|
||||
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 CancelEditCommand { get; }
|
||||
public RelayCommand SetDefaultCommand { get; }
|
||||
public RelayCommand BrowseForCustomShellCommand { get; }
|
||||
public RelayCommand BrowseForWorkingDirectoryCommand { get; }
|
||||
public IAsyncCommand RestoreDefaultsCommand { get; }
|
||||
public IAsyncCommand AddKeyboardShortcutCommand { get; }
|
||||
public ObservableCollection<TabTheme> TabThemes { get; }
|
||||
public KeyBindingsViewModel KeyBindings { get; }
|
||||
|
||||
public Guid Id { get; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => Set(ref _name, value);
|
||||
}
|
||||
public ObservableCollection<TerminalTheme> TerminalThemes { get; }
|
||||
public ShellProfile Model { get; private set; }
|
||||
public bool PreInstalled { get; private set; }
|
||||
|
||||
public string Arguments
|
||||
{
|
||||
@@ -72,16 +135,12 @@ namespace FluentTerminal.App.ViewModels
|
||||
set => Set(ref _arguments, value);
|
||||
}
|
||||
|
||||
public string Location
|
||||
{
|
||||
get => _location;
|
||||
set => Set(ref _location, value);
|
||||
}
|
||||
public Guid Id { get; private set; }
|
||||
|
||||
public string WorkingDirectory
|
||||
public bool InEditMode
|
||||
{
|
||||
get => _workingDirectory;
|
||||
set => Set(ref _workingDirectory, value);
|
||||
get => _inEditMode;
|
||||
set => Set(ref _inEditMode, value);
|
||||
}
|
||||
|
||||
public bool IsDefault
|
||||
@@ -90,71 +149,83 @@ namespace FluentTerminal.App.ViewModels
|
||||
set => Set(ref _isDefault, value);
|
||||
}
|
||||
|
||||
public bool InEditMode
|
||||
public string Location
|
||||
{
|
||||
get => _inEditMode;
|
||||
set => Set(ref _inEditMode, value);
|
||||
get => _location;
|
||||
set => Set(ref _location, value);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _name;
|
||||
set => Set(ref _name, value);
|
||||
}
|
||||
|
||||
public TabTheme SelectedTabTheme
|
||||
{
|
||||
get => _selectedTabTheme;
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
Set(ref _selectedTabTheme, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TerminalTheme SelectedTerminalTheme
|
||||
{
|
||||
get => _selectedTerminalTheme;
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
Set(ref _selectedTerminalTheme, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get => _workingDirectory;
|
||||
set => Set(ref _workingDirectory, value);
|
||||
}
|
||||
|
||||
public void SaveChanges()
|
||||
{
|
||||
_shellProfile.Arguments = Arguments;
|
||||
_shellProfile.Location = Location;
|
||||
_shellProfile.Name = Name;
|
||||
_shellProfile.WorkingDirectory = WorkingDirectory;
|
||||
|
||||
_settingsService.SaveShellProfile(_shellProfile);
|
||||
Model.Arguments = Arguments;
|
||||
Model.Location = Location;
|
||||
Model.Name = Name;
|
||||
Model.WorkingDirectory = WorkingDirectory;
|
||||
Model.TabThemeId = SelectedTabTheme.Id;
|
||||
Model.TerminalThemeId = SelectedTerminalTheme.Id;
|
||||
Model.KeyBindings = KeyBindings.KeyBindings.Select(x => x.Model).ToList();
|
||||
_settingsService.SaveShellProfile(Model);
|
||||
|
||||
KeyBindings.Editable = false;
|
||||
InEditMode = false;
|
||||
}
|
||||
|
||||
private async Task CancelEdit()
|
||||
private async Task RestoreDefaults()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to discard all changes?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
if (InEditMode || !PreInstalled)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to restore this profile?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Arguments = _fallbackArguments;
|
||||
Location = _fallbackLocation;
|
||||
Name = _fallbackName;
|
||||
WorkingDirectory = _fallbackWorkingDirectory;
|
||||
|
||||
InEditMode = false;
|
||||
Model = _defaultValueProvider.GetPreinstalledShellProfiles().FirstOrDefault(x => x.Id == Model.Id);
|
||||
InitializeViewModelProperties(Model);
|
||||
_settingsService.SaveShellProfile(Model);
|
||||
}
|
||||
}
|
||||
|
||||
private void Edit()
|
||||
public Task AddKeyboardShortcut()
|
||||
{
|
||||
_fallbackArguments = Arguments;
|
||||
_fallbackLocation = Location;
|
||||
_fallbackName = Name;
|
||||
_fallbackWorkingDirectory = WorkingDirectory;
|
||||
InEditMode = true;
|
||||
}
|
||||
|
||||
private bool CanDelete()
|
||||
{
|
||||
return !_shellProfile.PreInstalled;
|
||||
}
|
||||
|
||||
private bool CanEdit()
|
||||
{
|
||||
return !_shellProfile.PreInstalled;
|
||||
}
|
||||
|
||||
private async Task Delete()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this theme?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Deleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDefault()
|
||||
{
|
||||
SetAsDefault?.Invoke(this, EventArgs.Empty);
|
||||
return KeyBindings.ShowAddKeyBindingDialog();
|
||||
}
|
||||
|
||||
private async Task BrowseForCustomShell()
|
||||
@@ -174,5 +245,66 @@ namespace FluentTerminal.App.ViewModels
|
||||
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 = _fallbackProfile.Arguments;
|
||||
Location = _fallbackProfile.Location;
|
||||
Name = _fallbackProfile.Name;
|
||||
WorkingDirectory = _fallbackProfile.WorkingDirectory;
|
||||
SelectedTerminalTheme = TerminalThemes.FirstOrDefault(t => t.Id == _fallbackProfile.TerminalThemeId);
|
||||
SelectedTabTheme = TabThemes.FirstOrDefault(t => t.Id == _fallbackProfile.TabThemeId);
|
||||
|
||||
KeyBindings.KeyBindings.Clear();
|
||||
foreach (var keyBinding in Model.KeyBindings.Select(x => new KeyBinding(x)).ToList())
|
||||
{
|
||||
KeyBindings.Add(keyBinding);
|
||||
}
|
||||
|
||||
KeyBindings.Editable = false;
|
||||
InEditMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanDelete()
|
||||
{
|
||||
return !Model.PreInstalled;
|
||||
}
|
||||
|
||||
private async Task Delete()
|
||||
{
|
||||
var result = await _dialogService.ShowMessageDialogAsnyc("Please confirm", "Are you sure you want to delete this theme?", DialogButton.OK, DialogButton.Cancel).ConfigureAwait(true);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
Deleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void Edit()
|
||||
{
|
||||
// todo write copy ctor
|
||||
_fallbackProfile = new ShellProfile
|
||||
{
|
||||
Arguments = Model.Arguments,
|
||||
Location = Model.Location,
|
||||
Name = Model.Name,
|
||||
WorkingDirectory = Model.WorkingDirectory,
|
||||
TabThemeId = Model.TabThemeId,
|
||||
TerminalThemeId = Model.TerminalThemeId
|
||||
};
|
||||
|
||||
KeyBindings.Editable = true;
|
||||
InEditMode = true;
|
||||
}
|
||||
|
||||
private void SetDefault()
|
||||
{
|
||||
SetAsDefault?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ 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;
|
||||
@@ -25,10 +26,13 @@ namespace FluentTerminal.App.ViewModels
|
||||
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 TerminalTheme _terminalTheme;
|
||||
private int _terminalId;
|
||||
private ITerminalView _terminalView;
|
||||
private string _title;
|
||||
@@ -59,6 +63,11 @@ namespace FluentTerminal.App.ViewModels
|
||||
_applicationView = applicationView;
|
||||
_clipboardService = clipboardService;
|
||||
|
||||
TerminalTheme = shellProfile.TerminalThemeId == Guid.Empty ? _settingsService.GetCurrentTheme() : _settingsService.GetTheme(shellProfile.TerminalThemeId);
|
||||
|
||||
TabThemes = new ObservableCollection<TabTheme>(_settingsService.GetTabThemes());
|
||||
TabTheme = TabThemes.FirstOrDefault(t => t.Id == _shellProfile.TabThemeId);
|
||||
|
||||
_resizeOverlayTimer = dispatcherTimer;
|
||||
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
|
||||
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
|
||||
@@ -67,11 +76,27 @@ namespace FluentTerminal.App.ViewModels
|
||||
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 event EventHandler Closed;
|
||||
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 TerminalTheme TerminalTheme
|
||||
{
|
||||
get => _terminalTheme;
|
||||
set => Set(ref _terminalTheme, value);
|
||||
}
|
||||
|
||||
public RelayCommand CloseCommand { get; }
|
||||
|
||||
@@ -95,13 +120,22 @@ namespace FluentTerminal.App.ViewModels
|
||||
if (IsSelected)
|
||||
{
|
||||
_applicationView.Title = Title;
|
||||
_applicationView.RunOnDispatcherThread(() => NewOutput = false);
|
||||
}
|
||||
RaisePropertyChanged(nameof(IsUnderlined));
|
||||
RaisePropertyChanged(nameof(BackgroundTabTheme));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUnderlined => IsSelected && ApplicationSettings.UnderlineSelectedTab;
|
||||
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
|
||||
{
|
||||
@@ -115,6 +149,8 @@ namespace FluentTerminal.App.ViewModels
|
||||
set => Set(ref _searchText, value);
|
||||
}
|
||||
|
||||
public RelayCommand<string> SelectTabThemeCommand { get; }
|
||||
|
||||
public bool ShowResizeOverlay
|
||||
{
|
||||
get => _showResizeOverlay;
|
||||
@@ -138,6 +174,19 @@ namespace FluentTerminal.App.ViewModels
|
||||
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;
|
||||
@@ -158,6 +207,11 @@ namespace FluentTerminal.App.ViewModels
|
||||
await _trayProcessCommunicationService.CloseTerminal(_terminalId).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
public void CopyText(string text)
|
||||
{
|
||||
_clipboardService.SetText(text);
|
||||
}
|
||||
|
||||
public Task FocusTerminal()
|
||||
{
|
||||
return _terminalView?.FocusTerminal();
|
||||
@@ -168,10 +222,10 @@ namespace FluentTerminal.App.ViewModels
|
||||
_terminalView = terminalView;
|
||||
|
||||
var options = _settingsService.GetTerminalOptions();
|
||||
var theme = _settingsService.GetCurrentTheme();
|
||||
var keyBindings = _settingsService.GetKeyBindings();
|
||||
var keyBindings = _settingsService.GetCommandKeyBindings();
|
||||
var profiles = _settingsService.GetShellProfiles();
|
||||
|
||||
var size = await _terminalView.CreateTerminal(options, theme.Colors, FlattenKeyBindings(keyBindings)).ConfigureAwait(true);
|
||||
var size = await _terminalView.CreateTerminal(options, TerminalTheme.Colors, FlattenKeyBindings(keyBindings, profiles)).ConfigureAwait(true);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_startupDirectory))
|
||||
{
|
||||
@@ -191,6 +245,10 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
_connectedEvent.Wait();
|
||||
_webSocket.Send(t);
|
||||
if (!IsSelected && ApplicationSettings.ShowNewOutputIndicator)
|
||||
{
|
||||
_applicationView.RunOnDispatcherThread(() => NewOutput = true);
|
||||
}
|
||||
});
|
||||
|
||||
DefaultTitle = response.ShellExecutableName;
|
||||
@@ -216,11 +274,6 @@ namespace FluentTerminal.App.ViewModels
|
||||
await FocusTerminal().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
public void CopyText(string text)
|
||||
{
|
||||
_clipboardService.SetText(text);
|
||||
}
|
||||
|
||||
private void CloseSearchPanel()
|
||||
{
|
||||
SearchText = string.Empty;
|
||||
@@ -238,9 +291,9 @@ namespace FluentTerminal.App.ViewModels
|
||||
return _terminalView.FindPrevious(SearchText);
|
||||
}
|
||||
|
||||
private IEnumerable<KeyBinding> FlattenKeyBindings(IDictionary<Command, ICollection<KeyBinding>> keyBindings)
|
||||
private IEnumerable<KeyBinding> FlattenKeyBindings(IDictionary<string, ICollection<KeyBinding>> commandKeyBindings, IEnumerable<ShellProfile> profiles)
|
||||
{
|
||||
return keyBindings.Values.SelectMany(k => k);
|
||||
return commandKeyBindings.Values.SelectMany(k => k).Concat(profiles.SelectMany(x => x.KeyBindings));
|
||||
}
|
||||
|
||||
private async void OnApplicationSettingsChanged(object sender, ApplicationSettings e)
|
||||
@@ -249,6 +302,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
ApplicationSettings = e;
|
||||
RaisePropertyChanged(nameof(IsUnderlined));
|
||||
RaisePropertyChanged(nameof(BackgroundTabTheme));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -256,28 +310,34 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
await _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
var currentTheme = _settingsService.GetTheme(e);
|
||||
await _terminalView.ChangeTheme(currentTheme.Colors).ConfigureAwait(true);
|
||||
// only change theme if not overwritten by profile
|
||||
if (_shellProfile.TerminalThemeId == Guid.Empty)
|
||||
{
|
||||
var currentTheme = _settingsService.GetTheme(e);
|
||||
TerminalTheme = currentTheme;
|
||||
await _terminalView.ChangeTheme(currentTheme.Colors).ConfigureAwait(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnKeyBindingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
var keyBindings = _settingsService.GetKeyBindings();
|
||||
var keyBindings = _settingsService.GetCommandKeyBindings();
|
||||
var profiles = _settingsService.GetShellProfiles();
|
||||
await _applicationView.RunOnDispatcherThread(async () =>
|
||||
{
|
||||
await _terminalView.ChangeKeyBindings(FlattenKeyBindings(keyBindings)).ConfigureAwait(false);
|
||||
await _terminalView.ChangeKeyBindings(FlattenKeyBindings(keyBindings, profiles)).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnKeyboardCommandReceived(object sender, Command e)
|
||||
private async void OnKeyboardCommandReceived(object sender, string command)
|
||||
{
|
||||
if (e == Command.Copy)
|
||||
if (command == nameof(Command.Copy))
|
||||
{
|
||||
var selection = await _terminalView.GetSelection().ConfigureAwait(true);
|
||||
_clipboardService.SetText(selection);
|
||||
}
|
||||
else if (e == Command.Paste)
|
||||
else if (command == nameof(Command.Paste))
|
||||
{
|
||||
var content = await _clipboardService.GetText().ConfigureAwait(true);
|
||||
if (content != null)
|
||||
@@ -285,7 +345,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
await _trayProcessCommunicationService.WriteText(_terminalId, content).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
else if (e == Command.Search)
|
||||
else if (command == nameof(Command.Search))
|
||||
{
|
||||
ShowSearchPanel = !ShowSearchPanel;
|
||||
if (ShowSearchPanel)
|
||||
@@ -295,7 +355,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyboardCommandService.SendCommand(e);
|
||||
_keyboardCommandService.SendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +397,11 @@ namespace FluentTerminal.App.ViewModels
|
||||
Title = e;
|
||||
}
|
||||
|
||||
private void SelectTabTheme(string name)
|
||||
{
|
||||
TabTheme = TabThemes.FirstOrDefault(t => t.Name == name);
|
||||
}
|
||||
|
||||
private async Task TryClose()
|
||||
{
|
||||
if (ApplicationSettings.ConfirmClosingTabs)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,10 @@ namespace FluentTerminal.App.Adapters
|
||||
var deferral = e.GetDeferral();
|
||||
|
||||
var args = new CancelableEventArgs();
|
||||
await CloseRequested?.Invoke(this, args);
|
||||
if (CloseRequested != null)
|
||||
{
|
||||
await CloseRequested.Invoke(this, args);
|
||||
}
|
||||
|
||||
e.Handled = args.Cancelled;
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Styles\Custom.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<LinearGradientBrush x:Key="MissingColorSelectionBrush" StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Offset="0" Color="Black" />
|
||||
<GradientStop Offset="1.0" Color="White" />
|
||||
</LinearGradientBrush>
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="OneToVisibleConverter"
|
||||
Else="Collapsed"
|
||||
@@ -21,6 +25,7 @@
|
||||
<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"
|
||||
@@ -30,6 +35,8 @@
|
||||
<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>
|
||||
@@ -63,6 +63,8 @@ namespace FluentTerminal.App
|
||||
builder.RegisterType<TrayProcessCommunicationService>().As<ITrayProcessCommunicationService>().SingleInstance();
|
||||
builder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
|
||||
builder.RegisterType<KeyboardCommandService>().As<IKeyboardCommandService>().InstancePerDependency();
|
||||
builder.RegisterType<NotificationService>().As<INotificationService>().InstancePerDependency();
|
||||
builder.RegisterType<UpdateService>().As<IUpdateService>().InstancePerDependency();
|
||||
builder.RegisterType<MainViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<ThemeParserFactory>().As<IThemeParserFactory>().SingleInstance();
|
||||
@@ -112,7 +114,7 @@ namespace FluentTerminal.App
|
||||
{
|
||||
if (_applicationSettings.NewTerminalLocation == NewTerminalLocation.Tab && _mainViewModels.Count > 0)
|
||||
{
|
||||
await _mainViewModels.Last().AddTerminal(parameter, false).ConfigureAwait(true);
|
||||
await _mainViewModels.Last().AddTerminal(parameter, false, Guid.Empty).ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -130,7 +132,7 @@ namespace FluentTerminal.App
|
||||
else if (command == "new")
|
||||
{
|
||||
var viewModel = _container.Resolve<MainViewModel>();
|
||||
await viewModel.AddTerminal(parameter, false).ConfigureAwait(true);
|
||||
await viewModel.AddTerminal(parameter, false, Guid.Empty).ConfigureAwait(true);
|
||||
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
@@ -142,7 +144,7 @@ namespace FluentTerminal.App
|
||||
if (!_alreadyLaunched)
|
||||
{
|
||||
var viewModel = _container.Resolve<MainViewModel>();
|
||||
await viewModel.AddTerminal(null, false).ConfigureAwait(true);
|
||||
await viewModel.AddTerminal(null, false, Guid.Empty).ConfigureAwait(true);
|
||||
await CreateMainView(typeof(MainPage), viewModel, true).ConfigureAwait(true);
|
||||
Window.Current.Activate();
|
||||
}
|
||||
@@ -199,6 +201,7 @@ namespace FluentTerminal.App
|
||||
mainViewModel.Closed += OnMainViewModelClosed;
|
||||
mainViewModel.NewWindowRequested += OnNewWindowRequested;
|
||||
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
|
||||
mainViewModel.ShowAboutRequested += OnShowAboutRequested;
|
||||
_mainViewModels.Add(mainViewModel);
|
||||
}
|
||||
|
||||
@@ -236,8 +239,9 @@ namespace FluentTerminal.App
|
||||
mainViewModel.Closed += OnMainViewModelClosed;
|
||||
mainViewModel.NewWindowRequested += OnNewWindowRequested;
|
||||
mainViewModel.ShowSettingsRequested += OnShowSettingsRequested;
|
||||
mainViewModel.ShowAboutRequested += OnShowAboutRequested;
|
||||
_mainViewModels.Add(mainViewModel);
|
||||
await mainViewModel.AddTerminal(directory, false).ConfigureAwait(true);
|
||||
await mainViewModel.AddTerminal(directory, false, Guid.Empty).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
if (viewModel is SettingsViewModel settingsViewModel)
|
||||
@@ -278,11 +282,22 @@ namespace FluentTerminal.App
|
||||
_settingsWindowId = null;
|
||||
}
|
||||
|
||||
private void OnShowAboutRequested(object sender, EventArgs e)
|
||||
{
|
||||
ShowAbout().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async void OnShowSettingsRequested(object sender, EventArgs e)
|
||||
{
|
||||
await ShowSettings().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private async Task ShowAbout()
|
||||
{
|
||||
await ShowSettings().ConfigureAwait(true);
|
||||
_settingsViewModel.NavigateToAboutPage();
|
||||
}
|
||||
|
||||
private async Task ShowSettings()
|
||||
{
|
||||
if (_settingsViewModel == null)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ namespace FluentTerminal.App.Converters
|
||||
{
|
||||
return colorString.FromString();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class ToolTipValueToPixelConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
return string.Format("{0} px", value);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,13 +112,17 @@
|
||||
</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\ToolTipValueToPixelConverter.cs" />
|
||||
<Compile Include="Converters\TrueToVisibleConverter.cs" />
|
||||
<Compile Include="Adapters\MessageDialogAdapter.cs" />
|
||||
<Compile Include="Dialogs\ShellProfileSelectionDialog.xaml.cs">
|
||||
@@ -143,6 +147,9 @@
|
||||
<Compile Include="Views\SettingsPage.xaml.cs">
|
||||
<DependentUpon>SettingsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\About.xaml.cs">
|
||||
<DependentUpon>About.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPages\MouseSettings.xaml.cs">
|
||||
<DependentUpon>MouseSettings.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -164,9 +171,12 @@
|
||||
<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\TerminalThemeTemplateSelector.cs" />
|
||||
<Compile Include="Views\TerminalView.xaml.cs">
|
||||
<DependentUpon>TerminalView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -175,6 +185,7 @@
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
<Content Include="Assets\AppIcons\Square106x106Logo.black.png" />
|
||||
<Content Include="Assets\settingsicons.ttf" />
|
||||
<Content Include="Win32\Autofac.dll" />
|
||||
<Content Include="Win32\FluentTerminal.App.Services.dll" />
|
||||
@@ -289,6 +300,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\About.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPages\MouseSettings.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -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>
|
||||
@@ -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.2.0.0" />
|
||||
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.3.0.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>Fluent Terminal</DisplayName>
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace FluentTerminal.App.Services
|
||||
{
|
||||
return content.GetTextAsync().AsTask();
|
||||
}
|
||||
return null;
|
||||
// Otherwise return a new task that just sends an empty string.
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
|
||||
public void SetText(string text)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace FluentTerminal.App.Utilities
|
||||
titleBar.ButtonPressedBackgroundColor = GetColor("SystemListMediumColor", theme);
|
||||
}
|
||||
|
||||
private static Color? GetColor(string name, ElementTheme theme)
|
||||
public static Color? GetColor(string name, ElementTheme theme)
|
||||
{
|
||||
if (theme == ElementTheme.Light)
|
||||
{
|
||||
|
||||
@@ -10,13 +10,15 @@
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Width="180"
|
||||
Margin="0,6,24,0"
|
||||
Text="{x:Bind ViewModel.CommandName, Mode=OneWay}" />
|
||||
Text="{x:Bind ViewModel.CommandName, Mode=OneWay}"
|
||||
Visibility="{x:Bind ShowCommandName, Mode=OneWay}" />
|
||||
<ItemsControl Grid.Column="1" ItemsSource="{x:Bind ViewModel.KeyBindings, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="settings:KeyBindingViewModel">
|
||||
@@ -82,11 +84,13 @@
|
||||
<HyperlinkButton
|
||||
Margin="12,0,0,0"
|
||||
Command="{x:Bind EditCommand}"
|
||||
Content="Edit" />
|
||||
Content="Edit"
|
||||
Visibility="{x:Bind Parent.Editable, Converter={StaticResource TrueToVisibleConverter}, Mode=OneWay}" />
|
||||
<HyperlinkButton
|
||||
Margin="12,0,0,0"
|
||||
Command="{x:Bind DeleteCommand}"
|
||||
Content="Delete" />
|
||||
Content="Delete"
|
||||
Visibility="{x:Bind Parent.Editable, Converter={StaticResource TrueToVisibleConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
||||
@@ -6,14 +6,23 @@ namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class KeyBindingsView : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty ShowCommandNameProperty =
|
||||
DependencyProperty.Register(nameof(ShowCommandName), typeof(bool), typeof(KeyBindingsView), new PropertyMetadata(true));
|
||||
|
||||
public static readonly DependencyProperty ViewModelProperty =
|
||||
DependencyProperty.Register(nameof(ViewModel), typeof(KeyBindingsViewModel), typeof(KeyBindingsView), new PropertyMetadata(null));
|
||||
DependencyProperty.Register(nameof(ViewModel), typeof(KeyBindingsViewModel), typeof(KeyBindingsView), new PropertyMetadata(null));
|
||||
|
||||
public KeyBindingsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public bool ShowCommandName
|
||||
{
|
||||
get { return (bool)GetValue(ShowCommandNameProperty); }
|
||||
set { SetValue(ShowCommandNameProperty, value); }
|
||||
}
|
||||
|
||||
public KeyBindingsViewModel ViewModel
|
||||
{
|
||||
get { return (KeyBindingsViewModel)GetValue(ViewModelProperty); }
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:views="using:FluentTerminal.App.Views"
|
||||
RequestedTheme="{x:Bind ViewModel.Background, Mode=OneWay, Converter={StaticResource BackgroundToApplicationThemeConverter}}"
|
||||
RequestedTheme="{x:Bind ViewModel.SelectedTerminal.TerminalTheme.Colors.Background, Mode=OneWay, Converter={StaticResource BackgroundToApplicationThemeConverter}}"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:TerminalViewModelToViewConverter x:Key="TerminalViewModelToViewConverter" />
|
||||
@@ -15,9 +15,11 @@
|
||||
<Grid.Background>
|
||||
<AcrylicBrush
|
||||
BackgroundSource="HostBackdrop"
|
||||
FallbackColor="{x:Bind ViewModel.Background, Mode=OneWay}"
|
||||
TintColor="{x:Bind ViewModel.Background, Mode=OneWay}"
|
||||
TintOpacity="{x:Bind ViewModel.BackgroundOpacity, Mode=OneWay}" />
|
||||
FallbackColor="{x:Bind ViewModel.SelectedTerminal.TerminalTheme.Colors.Background, Mode=OneWay}"
|
||||
TintColor="{x:Bind ViewModel.SelectedTerminal.TerminalTheme.Colors.Background, Mode=OneWay}"
|
||||
TintOpacity="{x:Bind ViewModel.BackgroundOpacity, Mode=OneWay}"
|
||||
TintTransitionDuration="{x:Bind NoDuration}" />
|
||||
|
||||
</Grid.Background>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -60,6 +62,17 @@
|
||||
<SymbolIcon Symbol="Setting" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
<MenuFlyoutItem
|
||||
Width="160"
|
||||
Command="{x:Bind ViewModel.ShowAboutCommand}"
|
||||
Text="About">
|
||||
<MenuFlyoutItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Glyph=""
|
||||
ToolTipService.ToolTip="About" />
|
||||
</MenuFlyoutItem.Icon>
|
||||
</MenuFlyoutItem>
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI.Core;
|
||||
@@ -18,6 +19,8 @@ namespace FluentTerminal.App.Views
|
||||
|
||||
public double CoreTitleBarHeight => coreTitleBar.Height;
|
||||
|
||||
public TimeSpan NoDuration => TimeSpan.Zero;
|
||||
|
||||
public Thickness CoreTitleBarPadding
|
||||
{
|
||||
get
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
x:Name="NavigationView"
|
||||
AlwaysShowHeader="False"
|
||||
Canvas.ZIndex="0"
|
||||
CompactModeThresholdWidth="560"
|
||||
CompactModeThresholdWidth="0"
|
||||
ExpandedModeThresholdWidth="560"
|
||||
IsSettingsVisible="False"
|
||||
Loaded="NavigationView_Loaded"
|
||||
@@ -74,6 +74,16 @@
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
</NavigationView.MenuItems>
|
||||
<NavigationView.PaneFooter>
|
||||
<NavigationViewItem Content="About" Tapped="AboutTapped">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Glyph=""
|
||||
ToolTipService.ToolTip="About" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
</NavigationView.PaneFooter>
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Frame x:Name="ContentFrame" />
|
||||
<TextBlock
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using FluentTerminal.App.Utilities;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.App.Views.SettingsPages;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Foundation;
|
||||
@@ -9,6 +10,7 @@ using Windows.UI.Core.Preview;
|
||||
using Windows.UI.ViewManagement;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
@@ -18,6 +20,7 @@ namespace FluentTerminal.App.Views
|
||||
private readonly UISettings _uiSettings;
|
||||
private readonly ApplicationViewTitleBar _titleBar;
|
||||
private readonly CoreDispatcher _dispatcher;
|
||||
private readonly NavigationViewItem hiddenNavigationItem;
|
||||
private bool _onThemesPage;
|
||||
|
||||
public SettingsPage()
|
||||
@@ -39,6 +42,8 @@ namespace FluentTerminal.App.Views
|
||||
_uiSettings = new UISettings();
|
||||
_uiSettings.ColorValuesChanged += OnColorValuesChanged;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += SettingsPage_CloseRequested;
|
||||
|
||||
hiddenNavigationItem = new NavigationViewItem();
|
||||
}
|
||||
|
||||
private void OnColorValuesChanged(UISettings sender, object args)
|
||||
@@ -67,9 +72,24 @@ namespace FluentTerminal.App.Views
|
||||
if (e.Parameter is SettingsViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
ViewModel.AboutPageRequested += OnAboutPageRequested;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAboutPageRequested(object sender, System.EventArgs e)
|
||||
{
|
||||
_dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
{
|
||||
ContentFrame.Navigate(typeof(About), ViewModel.About);
|
||||
|
||||
// Deselect item in NavigationView (https://stackoverflow.com/a/49082640/4132379)
|
||||
NavigationView.MenuItems.Add(hiddenNavigationItem);
|
||||
NavigationView.SelectedItem = hiddenNavigationItem;
|
||||
NavigationView.SelectedItem = null;
|
||||
NavigationView.MenuItems.Remove(hiddenNavigationItem);
|
||||
});
|
||||
}
|
||||
|
||||
private void NavigationView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationView.SelectedItem = NavigationView.MenuItems.Cast<NavigationViewItemBase>().FirstOrDefault(m => m.Tag.ToString() == "general");
|
||||
@@ -116,6 +136,11 @@ namespace FluentTerminal.App.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void AboutTapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
OnAboutPageRequested(sender, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void SettingsPage_CloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
ViewModel.Close();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<Page
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.About"
|
||||
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"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="1" Margin="24,0,0,24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="28"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="About" />
|
||||
<CommandBar
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
DefaultLabelPosition="Right">
|
||||
<AppBarButton
|
||||
Command="{x:Bind ViewModel.CheckForUpdatesCommand}"
|
||||
Icon="Sync"
|
||||
Label="Check for updates" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
<ScrollViewer Grid.Row="2" Margin="24,0,0,0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<!-- App Logo -->
|
||||
<Image
|
||||
MaxWidth="106"
|
||||
MaxHeight="106"
|
||||
Margin="24,0,24,0"
|
||||
HorizontalAlignment="Left"
|
||||
Source="/Assets/AppIcons/Square106x106Logo.black.png"
|
||||
Stretch="None" />
|
||||
|
||||
<!-- Current version -->
|
||||
<TextBlock Margin="0,24,0,8" Text="Current version" />
|
||||
<TextBlock><Run Text="{x:Bind ViewModel.CurrentVersion}" />
|
||||
- <Hyperlink NavigateUri="{x:Bind ViewModel.CurrentVersionReleaseNotesURL}">Release notes</Hyperlink></TextBlock>
|
||||
|
||||
<!-- Latest version -->
|
||||
<TextBlock Margin="0,24,0,8" Text="Latest version" />
|
||||
<TextBlock Visibility="{x:Bind ViewModel.LatestVersionLoading, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}"><Italic>loading...</Italic></TextBlock>
|
||||
<TextBlock Visibility="{x:Bind ViewModel.LatestVersionNotFound, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}"><Italic>Not found</Italic></TextBlock>
|
||||
<TextBlock Visibility="{x:Bind ViewModel.LatestVersionFound, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<Run Text="{x:Bind ViewModel.LatestVersion, Mode=OneWay}" />
|
||||
- <Hyperlink NavigateUri="{x:Bind ViewModel.LatestVersionReleaseNotesURL, Mode=OneWay}">
|
||||
Release notes
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
|
||||
<!-- License -->
|
||||
<TextBlock Margin="0,24,0,8" Text="License" />
|
||||
<TextBlock><Hyperlink NavigateUri="https://github.com/felixse/FluentTerminal/blob/master/LICENSE">GNU General Public License v3.0</Hyperlink></TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="0,72,0,24"
|
||||
VerticalAlignment="Bottom">
|
||||
<!-- Created by... -->
|
||||
<TextBlock>
|
||||
Created by <Italic>felixse</Italic>
|
||||
and <Hyperlink NavigateUri="https://github.com/felixse/FluentTerminal/graphs/contributors">contributors</Hyperlink></TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,25 @@
|
||||
using FluentTerminal.App.ViewModels.Settings;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
public sealed partial class About : Page
|
||||
{
|
||||
public AboutPageViewModel ViewModel { get; private set; }
|
||||
|
||||
public About()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
if (e.Parameter is AboutPageViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
ViewModel.OnNavigatedTo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,48 +34,75 @@
|
||||
Label="Restore Defaults" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="2" Margin="24,0,0,0">
|
||||
<ToggleSwitch
|
||||
Header="Run on startup"
|
||||
IsEnabled="{x:Bind ViewModel.CanEnableStartupTask, Mode=OneWay}"
|
||||
IsOn="{x:Bind ViewModel.StartupTaskEnabled, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Foreground="Red"
|
||||
Text="{x:Bind ViewModel.StartupTaskErrorMessage, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.CanEnableStartupTask, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}" />
|
||||
<TextBlock Margin="0,24,0,8" Text="Open new terminals as" />
|
||||
<RadioButton
|
||||
Content="Tabs"
|
||||
GroupName="NewTerminalLocation"
|
||||
IsChecked="{x:Bind ViewModel.TabIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
Margin="0,0,0,24"
|
||||
Content="Windows"
|
||||
GroupName="NewTerminalLocation"
|
||||
IsChecked="{x:Bind ViewModel.WindowIsSelected, Mode=TwoWay}" />
|
||||
<TextBlock Margin="0,0,0,8" Text="Show tabs on" />
|
||||
<RadioButton
|
||||
Content="Top"
|
||||
GroupName="TabsPosition"
|
||||
IsChecked="{x:Bind ViewModel.TopIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
Margin="0,0,0,24"
|
||||
Content="Bottom"
|
||||
GroupName="TabsPosition"
|
||||
IsChecked="{x:Bind ViewModel.BottomIsSelected, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Always show tabs"
|
||||
IsOn="{x:Bind ViewModel.AlwaysShowTabs, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Underline selected tab"
|
||||
IsOn="{x:Bind ViewModel.UnderlineSelectedTab, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Ask before closing windows"
|
||||
IsOn="{x:Bind ViewModel.ConfirmClosingWindows, Mode=TwoWay}" />
|
||||
<ToggleSwitch Header="Ask before closing tabs" IsOn="{x:Bind ViewModel.ConfirmClosingTabs, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="2" Margin="24,0,0,0">
|
||||
<StackPanel>
|
||||
<ToggleSwitch
|
||||
Header="Run on startup"
|
||||
IsEnabled="{x:Bind ViewModel.CanEnableStartupTask, Mode=OneWay}"
|
||||
IsOn="{x:Bind ViewModel.StartupTaskEnabled, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Foreground="Red"
|
||||
Text="{x:Bind ViewModel.StartupTaskErrorMessage, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.CanEnableStartupTask, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,24,0,0"
|
||||
Header="Show in system tray"
|
||||
IsOn="{x:Bind ViewModel.EnableTrayIcon, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Foreground="Red"
|
||||
Text="Relaunch required to apply setting"
|
||||
Visibility="{x:Bind ViewModel.ShouldRestartForTrayMessage, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
<TextBlock Margin="0,24,0,8" Text="Open new terminals as" />
|
||||
<RadioButton
|
||||
Content="Tabs"
|
||||
GroupName="NewTerminalLocation"
|
||||
IsChecked="{x:Bind ViewModel.TabIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
Margin="0,0,0,24"
|
||||
Content="Windows"
|
||||
GroupName="NewTerminalLocation"
|
||||
IsChecked="{x:Bind ViewModel.WindowIsSelected, Mode=TwoWay}" />
|
||||
<TextBlock Margin="0,0,0,8" Text="Show tabs on" />
|
||||
<RadioButton
|
||||
Content="Top"
|
||||
GroupName="TabsPosition"
|
||||
IsChecked="{x:Bind ViewModel.TopIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
Margin="0,0,0,24"
|
||||
Content="Bottom"
|
||||
GroupName="TabsPosition"
|
||||
IsChecked="{x:Bind ViewModel.BottomIsSelected, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Always show tabs"
|
||||
IsOn="{x:Bind ViewModel.AlwaysShowTabs, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Underline selected tab"
|
||||
IsOn="{x:Bind ViewModel.UnderlineSelectedTab, Mode=TwoWay}" />
|
||||
<TextBlock Margin="0,0,0,8" Text="Color on inactive tabs" />
|
||||
<RadioButton
|
||||
Content="Background"
|
||||
GroupName="InactiveTabColorMode"
|
||||
IsChecked="{x:Bind ViewModel.BackgroundIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
Margin="0,0,0,24"
|
||||
Content="Underline"
|
||||
GroupName="InactiveTabColorMode"
|
||||
IsChecked="{x:Bind ViewModel.UnderlinedIsSelected, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Ask before closing windows"
|
||||
IsOn="{x:Bind ViewModel.ConfirmClosingWindows, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Ask before closing tabs"
|
||||
IsOn="{x:Bind ViewModel.ConfirmClosingTabs, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Show new output indicator on background tabs"
|
||||
IsOn="{x:Bind ViewModel.ShowNewOutputIndicator, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,6 +1,7 @@
|
||||
using FluentTerminal.App.Services.Utilities;
|
||||
using FluentTerminal.App.ViewModels.Settings;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
@@ -22,17 +23,18 @@ namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
|
||||
// Add the commands corresponding to all of the application's static commands
|
||||
foreach (var value in Enum.GetValues(typeof(Command)))
|
||||
{
|
||||
var command = (Command)value;
|
||||
Command command = (Command)value;
|
||||
AddCommandMenu.Items.Add(new MenuFlyoutItem
|
||||
{
|
||||
Text = EnumHelper.GetEnumDescription(command),
|
||||
Command = ViewModel.AddCommand,
|
||||
CommandParameter = command
|
||||
CommandParameter = command.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,39 +36,41 @@
|
||||
Label="Restore Defaults" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="2" Margin="24,0,0,0">
|
||||
<ToggleSwitch
|
||||
<ScrollViewer Grid.Row="2" Margin="24,0,0,0">
|
||||
<StackPanel>
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Copy on select"
|
||||
IsOn="{x:Bind ViewModel.CopyOnSelect, Mode=TwoWay}" />
|
||||
<TextBlock Margin="0,0,0,8" Text="Middle button action" />
|
||||
<RadioButton
|
||||
<TextBlock Margin="0,0,0,8" Text="Middle button action" />
|
||||
<RadioButton
|
||||
Content="Nothing"
|
||||
GroupName="MiddleButton"
|
||||
IsChecked="{x:Bind ViewModel.MouseMiddleClickNoneIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
<RadioButton
|
||||
Content="Context menu"
|
||||
GroupName="MiddleButton"
|
||||
IsChecked="{x:Bind ViewModel.MouseMiddleClickContextMenuIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
<RadioButton
|
||||
Margin="0,0,0,24"
|
||||
Content="Paste"
|
||||
GroupName="MiddleButton"
|
||||
IsChecked="{x:Bind ViewModel.MouseMiddleClickPasteIsSelected, Mode=TwoWay}" />
|
||||
<TextBlock Margin="0,0,0,8" Text="Right button action" />
|
||||
<RadioButton
|
||||
<TextBlock Margin="0,0,0,8" Text="Right button action" />
|
||||
<RadioButton
|
||||
Content="Nothing"
|
||||
GroupName="RightButton"
|
||||
IsChecked="{x:Bind ViewModel.MouseRightClickNoneIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
<RadioButton
|
||||
Content="Context menu"
|
||||
GroupName="RightButton"
|
||||
IsChecked="{x:Bind ViewModel.MouseRightClickContextMenuIsSelected, Mode=TwoWay}" />
|
||||
<RadioButton
|
||||
<RadioButton
|
||||
Margin="0,0,0,24"
|
||||
Content="Paste"
|
||||
GroupName="RightButton"
|
||||
IsChecked="{x:Bind ViewModel.MouseRightClickPasteIsSelected, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -5,8 +5,50 @@
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:FluentTerminal.Models"
|
||||
xmlns:settings="using:FluentTerminal.App.ViewModels.Settings"
|
||||
xmlns:viewmodels="using:FluentTerminal.App.ViewModels"
|
||||
xmlns:views="using:FluentTerminal.App.Views"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<views:TabThemeTemplateSelector x:Key="TabThemeTemplateSelector">
|
||||
<views:TabThemeTemplateSelector.DefaultTemplate>
|
||||
<DataTemplate x:DataType="models:TabTheme">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon
|
||||
Margin="0,0,6,0"
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Glyph="" />
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</views:TabThemeTemplateSelector.DefaultTemplate>
|
||||
<views:TabThemeTemplateSelector.ColoredTemplate>
|
||||
<DataTemplate x:DataType="models:TabTheme">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon
|
||||
Margin="0,0,6,0"
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Foreground="{x:Bind Color}"
|
||||
Glyph="" />
|
||||
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</views:TabThemeTemplateSelector.ColoredTemplate>
|
||||
</views:TabThemeTemplateSelector>
|
||||
<views:TerminalThemeTemplateSelector x:Key="TerminalThemeTemplateSelector">
|
||||
<views:TerminalThemeTemplateSelector.DefaultTemplate>
|
||||
<DataTemplate x:DataType="models:TerminalTheme">
|
||||
<TextBlock FontStyle="Italic" Text="{x:Bind Name}" />
|
||||
</DataTemplate>
|
||||
</views:TerminalThemeTemplateSelector.DefaultTemplate>
|
||||
<views:TerminalThemeTemplateSelector.ThemeTemplate>
|
||||
<DataTemplate x:DataType="models:TerminalTheme">
|
||||
<TextBlock Text="{x:Bind Name}" />
|
||||
</DataTemplate>
|
||||
</views:TerminalThemeTemplateSelector.ThemeTemplate>
|
||||
</views:TerminalThemeTemplateSelector>
|
||||
</Page.Resources>
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="160" />
|
||||
@@ -47,6 +89,11 @@
|
||||
Icon="Accept"
|
||||
IsEnabled="{x:Bind ViewModel.SelectedShellProfile.IsDefault, Mode=OneWay, Converter={StaticResource NegateConverter}}"
|
||||
Label="Set Default" />
|
||||
<AppBarButton
|
||||
Command="{x:Bind ViewModel.CloneCommand, Mode=OneWay}"
|
||||
CommandParameter="{x:Bind ViewModel.SelectedShellProfile, Mode=OneWay}"
|
||||
Icon="Copy"
|
||||
Label="Clone" />
|
||||
<AppBarButton
|
||||
Command="{x:Bind ViewModel.SelectedShellProfile.EditCommand, Mode=OneWay}"
|
||||
Icon="Edit"
|
||||
@@ -54,7 +101,13 @@
|
||||
<AppBarButton
|
||||
Command="{x:Bind ViewModel.SelectedShellProfile.DeleteCommand, Mode=OneWay}"
|
||||
Icon="Delete"
|
||||
Label="Delete" />
|
||||
Label="Delete"
|
||||
Visibility="{x:Bind ViewModel.SelectedShellProfile.PreInstalled, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}" />
|
||||
<AppBarButton
|
||||
Command="{x:Bind ViewModel.SelectedShellProfile.RestoreDefaultsCommand, Mode=OneWay}"
|
||||
Icon="Undo"
|
||||
Label="Restore Defaults"
|
||||
Visibility="{x:Bind ViewModel.SelectedShellProfile.PreInstalled, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
</CommandBar>
|
||||
<CommandBar
|
||||
Grid.Column="1"
|
||||
@@ -73,127 +126,192 @@
|
||||
Label="Cancel" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="2" Grid.Column="0">
|
||||
<ListView ItemsSource="{x:Bind ViewModel.ShellProfiles}" SelectedItem="{x:Bind ViewModel.SelectedShellProfile, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:ShellProfileViewModel">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<ScrollViewer
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<ListView ItemsSource="{x:Bind ViewModel.ShellProfiles}" SelectedItem="{x:Bind ViewModel.SelectedShellProfile, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:ShellProfileViewModel">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<Viewbox
|
||||
Grid.Column="1"
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Right">
|
||||
<SymbolIcon Symbol="Accept" Visibility="{x:Bind IsDefault, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
<Button
|
||||
Height="48"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="Transparent"
|
||||
Command="{x:Bind ViewModel.CreateShellProfileCommand}"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox
|
||||
Grid.Column="1"
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Right">
|
||||
<SymbolIcon Symbol="Accept" Visibility="{x:Bind IsDefault, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,6,0">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
<Button
|
||||
Height="48"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="Transparent"
|
||||
Command="{x:Bind ViewModel.CreateShellProfileCommand}"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,6,0">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Create new" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<ContentControl
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Create new" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="24,0,0,0"
|
||||
Content="{x:Bind ViewModel.SelectedShellProfile, Mode=TwoWay}">
|
||||
<ContentControl.ContentTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:ShellProfileViewModel">
|
||||
<Grid>
|
||||
<StackPanel Visibility="{x:Bind InEditMode, Converter={StaticResource TrueToVisibleConverter}, Mode=OneWay}">
|
||||
<TextBox
|
||||
Width="300"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Name"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<ContentControl HorizontalContentAlignment="Stretch" Content="{x:Bind ViewModel.SelectedShellProfile, Mode=OneWay}">
|
||||
<ContentControl.ContentTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:ShellProfileViewModel">
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<StackPanel Margin="0,0,24,0" Visibility="{x:Bind InEditMode, Converter={StaticResource TrueToVisibleConverter}, Mode=OneWay}">
|
||||
<TextBox
|
||||
Width="300"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Name"
|
||||
IsEnabled="{x:Bind PreInstalled, Converter={StaticResource NegateConverter}, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
|
||||
<TextBox
|
||||
Width="300"
|
||||
Header="Shell executable location"
|
||||
IsEnabled="{x:Bind PreInstalled, Converter={StaticResource NegateConverter}, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind Location, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button
|
||||
Margin="6,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Command="{x:Bind BrowseForCustomShellCommand}"
|
||||
Content="..."
|
||||
IsEnabled="{x:Bind PreInstalled, Converter={StaticResource NegateConverter}, Mode=OneWay}"
|
||||
Style="{StaticResource ButtonRevealStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
|
||||
<TextBox
|
||||
Width="300"
|
||||
Header="Working directory"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind WorkingDirectory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button
|
||||
Margin="6,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Command="{x:Bind BrowseForWorkingDirectoryCommand}"
|
||||
Content="..."
|
||||
Style="{StaticResource ButtonRevealStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBox
|
||||
Width="300"
|
||||
Margin="0,24,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Arguments"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind Arguments, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ComboBox
|
||||
Width="150"
|
||||
Margin="0,24,0,0"
|
||||
Header="Tab color"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
ItemTemplateSelector="{StaticResource TabThemeTemplateSelector}"
|
||||
ItemsSource="{x:Bind TabThemes, Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedTabTheme, Mode=TwoWay}" />
|
||||
<ComboBox
|
||||
Width="150"
|
||||
Margin="0,24,0,0"
|
||||
Header="Theme"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
ItemTemplateSelector="{StaticResource TerminalThemeTemplateSelector}"
|
||||
ItemsSource="{x:Bind TerminalThemes, Mode=OneWay}"
|
||||
SelectedItem="{Binding SelectedTerminalTheme, Mode=TwoWay}" />
|
||||
<StackPanel
|
||||
Width="300"
|
||||
Margin="0,24,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,6,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="Keyboard shortcut" />
|
||||
<HyperlinkButton Command="{x:Bind AddKeyboardShortcutCommand}" Content="Add" />
|
||||
</StackPanel>
|
||||
<views:KeyBindingsView ShowCommandName="False" ViewModel="{x:Bind KeyBindings}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,24,0" Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<controls:HeaderedTextBlock
|
||||
Margin="0,58,0,0"
|
||||
Header="Shell executable location"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind Location, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button
|
||||
Margin="6,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Command="{x:Bind BrowseForCustomShellCommand}"
|
||||
Content="..."
|
||||
Style="{StaticResource ButtonRevealStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,24,0,0" Orientation="Horizontal">
|
||||
<TextBox
|
||||
Width="300"
|
||||
Text="{x:Bind Location, Mode=OneWay}" />
|
||||
<controls:HeaderedTextBlock
|
||||
Margin="0,36,0,0"
|
||||
Header="Working directory"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind WorkingDirectory, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button
|
||||
Margin="6,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Command="{x:Bind BrowseForWorkingDirectoryCommand}"
|
||||
Content="..."
|
||||
Style="{StaticResource ButtonRevealStyle}" />
|
||||
</StackPanel>
|
||||
Text="{x:Bind WorkingDirectory, Mode=OneWay}" />
|
||||
<controls:HeaderedTextBlock
|
||||
Margin="0,36,0,0"
|
||||
Header="Arguments"
|
||||
Text="{x:Bind Arguments, Mode=OneWay}" />
|
||||
<controls:HeaderedContentControl
|
||||
Margin="0,36,0,0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Header="Theme">
|
||||
<ContentControl
|
||||
Margin="0,6,0,0"
|
||||
Content="{x:Bind SelectedTerminalTheme, Mode=OneWay}"
|
||||
ContentTemplateSelector="{StaticResource TerminalThemeTemplateSelector}" />
|
||||
</controls:HeaderedContentControl>
|
||||
<controls:HeaderedContentControl
|
||||
Margin="0,36,0,0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Header="Tab color">
|
||||
<ContentControl
|
||||
Margin="0,6,0,0"
|
||||
Content="{x:Bind SelectedTabTheme, Mode=OneWay}"
|
||||
ContentTemplateSelector="{StaticResource TabThemeTemplateSelector}" />
|
||||
</controls:HeaderedContentControl>
|
||||
<controls:HeaderedContentControl
|
||||
Margin="0,36,0,0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Header="Keyboard shortcut">
|
||||
<views:KeyBindingsView
|
||||
Margin="0,6,0,0"
|
||||
ShowCommandName="False"
|
||||
ViewModel="{x:Bind KeyBindings}" />
|
||||
</controls:HeaderedContentControl>
|
||||
|
||||
<TextBox
|
||||
Width="300"
|
||||
Margin="0,24,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Arguments"
|
||||
IsEnabled="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Text="{x:Bind Arguments, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
<StackPanel Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<controls:HeaderedTextBlock
|
||||
Margin="0,58,0,0"
|
||||
Header="Shell executable location"
|
||||
Text="{x:Bind Location, Mode=OneWay}" />
|
||||
<controls:HeaderedTextBlock
|
||||
Margin="0,36,0,0"
|
||||
Header="Working directory"
|
||||
Text="{x:Bind WorkingDirectory, Mode=OneWay}" />
|
||||
<controls:HeaderedTextBlock
|
||||
Margin="0,36,0,0"
|
||||
Header="Arguments"
|
||||
Text="{x:Bind Arguments, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ContentControl.ContentTemplate>
|
||||
</ContentControl>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ContentControl.ContentTemplate>
|
||||
</ContentControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -2,9 +2,13 @@
|
||||
x:Class="FluentTerminal.App.Views.SettingsPages.TerminalSettings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:ToolTipValueToPixelConverter x:Key="ToolTipValueToPixelConverter" />
|
||||
</Page.Resources>
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32" />
|
||||
@@ -57,6 +61,16 @@
|
||||
Minimum="0"
|
||||
StepFrequency="0.01"
|
||||
Value="{x:Bind ViewModel.BackgroundOpacity, Mode=TwoWay}" />
|
||||
<Slider
|
||||
Width="200"
|
||||
Margin="0,0,0,24"
|
||||
HorizontalAlignment="Left"
|
||||
Header="Padding"
|
||||
Maximum="64"
|
||||
Minimum="0"
|
||||
StepFrequency="1"
|
||||
ThumbToolTipValueConverter="{StaticResource ToolTipValueToPixelConverter}"
|
||||
Value="{x:Bind ViewModel.Padding, Mode=TwoWay}" />
|
||||
<TextBox
|
||||
Width="200"
|
||||
Margin="0,0,0,24"
|
||||
|
||||
@@ -93,213 +93,221 @@
|
||||
Label="Cancel" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="2" Grid.Column="0">
|
||||
<ListView ItemsSource="{x:Bind ViewModel.Themes}" SelectedItem="{x:Bind ViewModel.SelectedTheme, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:ThemeViewModel">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<ScrollViewer
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<ListView ItemsSource="{x:Bind ViewModel.Themes}" SelectedItem="{x:Bind ViewModel.SelectedTheme, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:ThemeViewModel">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{x:Bind Name, Mode=OneWay}" />
|
||||
<Viewbox
|
||||
Grid.Column="1"
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Right">
|
||||
<SymbolIcon Symbol="Accept" Visibility="{x:Bind IsActive, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
<Button
|
||||
Height="48"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="Transparent"
|
||||
Command="{x:Bind ViewModel.CreateThemeCommand}"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox
|
||||
Grid.Column="1"
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Right">
|
||||
<SymbolIcon Symbol="Accept" Visibility="{x:Bind IsActive, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,6,0">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
</ListView>
|
||||
<Button
|
||||
Height="48"
|
||||
Margin="0,12,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="Transparent"
|
||||
Command="{x:Bind ViewModel.CreateThemeCommand}"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,6,0">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Create new" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<Button
|
||||
Height="48"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="Transparent"
|
||||
Command="{x:Bind ViewModel.ImportThemeCommand}"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,6,0">
|
||||
<SymbolIcon Symbol="OpenFile" />
|
||||
</Viewbox>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Import" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Create new" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<Button
|
||||
Height="48"
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Background="Transparent"
|
||||
Command="{x:Bind ViewModel.ImportThemeCommand}"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Viewbox
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,6,0">
|
||||
<SymbolIcon Symbol="OpenFile" />
|
||||
</Viewbox>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="Import" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<ContentControl
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="24,0,0,0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Content="{x:Bind ViewModel.SelectedTheme, Mode=TwoWay}"
|
||||
RequestedTheme="{x:Bind ViewModel.SelectedTheme.Background, Mode=OneWay, Converter={StaticResource BackgroundToApplicationThemeConverter}}">
|
||||
<ContentControl.ContentTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:ThemeViewModel">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{x:Bind Name, Mode=OneWay}"
|
||||
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{x:Bind Foreground, Mode=OneWay}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind Author, Mode=OneWay}" />
|
||||
<TextBox
|
||||
Header="Name"
|
||||
Text="{x:Bind Name, Mode=TwoWay}"
|
||||
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
<TextBox
|
||||
Margin="0,24,0,0"
|
||||
Header="Author"
|
||||
Text="{x:Bind Author, Mode=TwoWay}"
|
||||
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
<Grid Margin="0,48,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.Resources>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Margin" Value="0,0,0,0" />
|
||||
<Setter Property="Width" Value="24" />
|
||||
<Setter Property="Height" Value="24" />
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Black"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Black, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Red"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Red, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Green"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Green, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Yellow"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Yellow, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Blue"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Blue, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Magenta"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Magenta, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Cyan"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Cyan, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="White"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind White, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Black"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightBlack, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Red"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightRed, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Green"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightGreen, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Yellow"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightYellow, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Blue"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightBlue, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Magenta"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightMagenta, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Cyan"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightCyan, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright White"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightWhite, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal">
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Foreground"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Foreground, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Background"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Background, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Cursor"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Cursor, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Cursor Accent"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind CursorAccent, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Selection"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsAlphaEnabled="True"
|
||||
SelectedColor="{x:Bind Selection, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=true}" />
|
||||
</StackPanel>
|
||||
<ScrollViewer Padding="0,0,24,0" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Margin="0,6,0,0"
|
||||
Text="Click on a color to edit"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{x:Bind Name, Mode=OneWay}"
|
||||
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource FalseToVisibleConverter}}">
|
||||
<TextBlock.Foreground>
|
||||
<SolidColorBrush Color="{x:Bind Foreground, Mode=OneWay}" />
|
||||
</TextBlock.Foreground>
|
||||
</TextBlock>
|
||||
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind Author, Mode=OneWay}" />
|
||||
<TextBox
|
||||
Header="Name"
|
||||
Text="{x:Bind Name, Mode=TwoWay}"
|
||||
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<TextBox
|
||||
Margin="0,24,0,0"
|
||||
Header="Author"
|
||||
Text="{x:Bind Author, Mode=TwoWay}"
|
||||
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
<Grid Margin="0,48,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.Resources>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Margin" Value="0,0,0,0" />
|
||||
<Setter Property="Width" Value="24" />
|
||||
<Setter Property="Height" Value="24" />
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Black"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Black, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Red"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Red, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Green"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Green, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Yellow"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Yellow, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Blue"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Blue, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Magenta"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Magenta, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Cyan"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Cyan, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="White"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind White, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Black"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightBlack, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Red"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightRed, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Green"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightGreen, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Yellow"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightYellow, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Blue"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightBlue, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Magenta"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightMagenta, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright Cyan"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightCyan, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Bright White"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind BrightWhite, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal">
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Foreground"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Foreground, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Background"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Background, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Cursor"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind Cursor, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Cursor Accent"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
SelectedColor="{x:Bind CursorAccent, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=false}" />
|
||||
<views:TerminalColorPicker
|
||||
ColorName="Selection"
|
||||
EnableEditing="{x:Bind InEditMode, Mode=OneWay}"
|
||||
IsAlphaEnabled="True"
|
||||
SelectedColor="{x:Bind Selection, Mode=TwoWay, Converter={StaticResource StringToColorConverter}, ConverterParameter=true}" />
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Margin="0,6,0,0"
|
||||
Text="Click on a color to edit"
|
||||
Visibility="{x:Bind InEditMode, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</DataTemplate>
|
||||
</ContentControl.ContentTemplate>
|
||||
</ContentControl>
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:FluentTerminal.Models"
|
||||
xmlns:viewmodels="using:FluentTerminal.App.ViewModels"
|
||||
xmlns:views="using:FluentTerminal.App.Views"
|
||||
d:DesignHeight="32"
|
||||
d:DesignWidth="200"
|
||||
mc:Ignorable="d">
|
||||
@@ -44,6 +46,7 @@
|
||||
<ListView
|
||||
x:Name="ListView"
|
||||
AllowDrop="True"
|
||||
Background="Transparent"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
ItemsSource="{x:Bind ItemsSource}"
|
||||
@@ -53,7 +56,9 @@
|
||||
<RelativePanel
|
||||
Height="32"
|
||||
Margin="0"
|
||||
VerticalAlignment="Stretch">
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
ToolTipService.ToolTip="{x:Bind Title, Mode=OneWay}">
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<behaviors:MiddleClickBehavior>
|
||||
<behaviors:MiddleClickBehavior.Actions>
|
||||
@@ -61,11 +66,100 @@
|
||||
</behaviors:MiddleClickBehavior.Actions>
|
||||
</behaviors:MiddleClickBehavior>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
<RelativePanel.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutSubItem Text="Color">
|
||||
<ToggleMenuFlyoutItem
|
||||
Command="{x:Bind SelectTabThemeCommand}"
|
||||
CommandParameter="None"
|
||||
IsChecked="{x:Bind TabTheme, Mode=OneWay, Converter={StaticResource TabThemeSelectedConverter}, ConverterParameter=None}"
|
||||
Text="None">
|
||||
<ToggleMenuFlyoutItem.Icon>
|
||||
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" />
|
||||
</ToggleMenuFlyoutItem.Icon>
|
||||
</ToggleMenuFlyoutItem>
|
||||
<ToggleMenuFlyoutItem
|
||||
Command="{x:Bind SelectTabThemeCommand}"
|
||||
CommandParameter="Red"
|
||||
IsChecked="{x:Bind TabTheme, Mode=OneWay, Converter={StaticResource TabThemeSelectedConverter}, ConverterParameter=Red}"
|
||||
Text="Red">
|
||||
<ToggleMenuFlyoutItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Foreground="#E81123"
|
||||
Glyph="" />
|
||||
</ToggleMenuFlyoutItem.Icon>
|
||||
</ToggleMenuFlyoutItem>
|
||||
<ToggleMenuFlyoutItem
|
||||
Command="{x:Bind SelectTabThemeCommand}"
|
||||
CommandParameter="Green"
|
||||
IsChecked="{x:Bind TabTheme, Mode=OneWay, Converter={StaticResource TabThemeSelectedConverter}, ConverterParameter=Green}"
|
||||
Text="Green">
|
||||
<ToggleMenuFlyoutItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Foreground="#10893E"
|
||||
Glyph="" />
|
||||
</ToggleMenuFlyoutItem.Icon>
|
||||
</ToggleMenuFlyoutItem>
|
||||
<ToggleMenuFlyoutItem
|
||||
Command="{x:Bind SelectTabThemeCommand}"
|
||||
CommandParameter="Blue"
|
||||
IsChecked="{x:Bind TabTheme, Mode=OneWay, Converter={StaticResource TabThemeSelectedConverter}, ConverterParameter=Blue}"
|
||||
Text="Blue">
|
||||
<ToggleMenuFlyoutItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Foreground="#0078D7"
|
||||
Glyph="" />
|
||||
</ToggleMenuFlyoutItem.Icon>
|
||||
</ToggleMenuFlyoutItem>
|
||||
<ToggleMenuFlyoutItem
|
||||
Command="{x:Bind SelectTabThemeCommand}"
|
||||
CommandParameter="Purple"
|
||||
IsChecked="{x:Bind TabTheme, Mode=OneWay, Converter={StaticResource TabThemeSelectedConverter}, ConverterParameter=Purple}"
|
||||
Text="Purple">
|
||||
<ToggleMenuFlyoutItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Foreground="#881798"
|
||||
Glyph="" />
|
||||
</ToggleMenuFlyoutItem.Icon>
|
||||
</ToggleMenuFlyoutItem>
|
||||
<ToggleMenuFlyoutItem
|
||||
Command="{x:Bind SelectTabThemeCommand}"
|
||||
CommandParameter="Orange"
|
||||
IsChecked="{x:Bind TabTheme, Mode=OneWay, Converter={StaticResource TabThemeSelectedConverter}, ConverterParameter=Orange}"
|
||||
Text="Orange">
|
||||
<ToggleMenuFlyoutItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Foreground="#FF8C00"
|
||||
Glyph="" />
|
||||
</ToggleMenuFlyoutItem.Icon>
|
||||
</ToggleMenuFlyoutItem>
|
||||
<ToggleMenuFlyoutItem
|
||||
Command="{x:Bind SelectTabThemeCommand}"
|
||||
CommandParameter="Teal"
|
||||
IsChecked="{x:Bind TabTheme, Mode=OneWay, Converter={StaticResource TabThemeSelectedConverter}, ConverterParameter=Teal}"
|
||||
Text="Teal">
|
||||
<ToggleMenuFlyoutItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Foreground="#00B7C3"
|
||||
Glyph="" />
|
||||
</ToggleMenuFlyoutItem.Icon>
|
||||
</ToggleMenuFlyoutItem>
|
||||
</MenuFlyoutSubItem>
|
||||
<MenuFlyoutSeparator />
|
||||
<MenuFlyoutItem Command="{x:Bind CloseCommand}" Text="Close" />
|
||||
</MenuFlyout>
|
||||
</RelativePanel.ContextFlyout>
|
||||
<Border
|
||||
Grid.ColumnSpan="3"
|
||||
Height="3"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{ThemeResource SystemAccentColor}"
|
||||
Background="{x:Bind TabTheme.Color, Mode=OneWay, Converter={StaticResource ColorResourceKeyFallbackConverter}, ConverterParameter=SystemControlHighlightAccentBrush}"
|
||||
RelativePanel.AlignBottomWithPanel="True"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
@@ -76,19 +170,34 @@
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.LeftOf="CloseButton"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Title, Mode=OneWay}" />
|
||||
<Button
|
||||
Text="{x:Bind Title, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap" />
|
||||
<Grid
|
||||
x:Name="CloseButton"
|
||||
Grid.Column="2"
|
||||
Width="32"
|
||||
Command="{x:Bind CloseCommand}"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="Close">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Cancel" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
Width="32"
|
||||
RelativePanel.AlignRightWithPanel="True">
|
||||
<Button
|
||||
HorizontalAlignment="Center"
|
||||
Command="{x:Bind CloseCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="Close"
|
||||
Visibility="{x:Bind IsSelected, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Cancel" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Grid Height="32" Visibility="{x:Bind NewOutput, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<Viewbox
|
||||
Width="12"
|
||||
Height="12"
|
||||
VerticalAlignment="Center"
|
||||
ToolTipService.ToolTip="New output">
|
||||
<FontIcon FontFamily="Segoe MDL2 Assets" Glyph="" />
|
||||
</Viewbox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</RelativePanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
@@ -100,10 +209,111 @@
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="MinWidth" Value="100" />
|
||||
<Setter Property="MaxWidth" Value="200" />
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="views:TabBarBackgroundBindingHelper.BackgroundBindingPath" Value="BackgroundTabTheme" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ListViewItemForeground}" />
|
||||
<Setter Property="TabNavigation" Value="Local" />
|
||||
<Setter Property="IsHoldingEnabled" Value="True" />
|
||||
<Setter Property="AllowDrop" Value="False" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||
<Setter Property="FocusVisualMargin" Value="0" />
|
||||
<Setter Property="FocusVisualPrimaryBrush" Value="{ThemeResource ListViewItemFocusVisualPrimaryBrush}" />
|
||||
<Setter Property="FocusVisualPrimaryThickness" Value="2" />
|
||||
<Setter Property="FocusVisualSecondaryBrush" Value="{ThemeResource ListViewItemFocusVisualSecondaryBrush}" />
|
||||
<Setter Property="FocusVisualSecondaryThickness" Value="1" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewItem">
|
||||
<ListViewItemPresenter
|
||||
x:Name="Root"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
CheckBoxBrush="{ThemeResource ListViewItemCheckBoxBrush}"
|
||||
CheckBrush="{ThemeResource ListViewItemCheckBrush}"
|
||||
CheckMode="{ThemeResource ListViewItemCheckMode}"
|
||||
ContentMargin="{TemplateBinding Padding}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
|
||||
DragBackground="{ThemeResource ListViewItemDragBackground}"
|
||||
DragForeground="{ThemeResource ListViewItemDragForeground}"
|
||||
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
|
||||
FocusBorderBrush="{ThemeResource ListViewItemFocusBorderBrush}"
|
||||
FocusSecondaryBorderBrush="{ThemeResource ListViewItemFocusSecondaryBorderBrush}"
|
||||
FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
|
||||
FocusVisualPrimaryBrush="{TemplateBinding FocusVisualPrimaryBrush}"
|
||||
FocusVisualPrimaryThickness="{TemplateBinding FocusVisualPrimaryThickness}"
|
||||
FocusVisualSecondaryBrush="{TemplateBinding FocusVisualSecondaryBrush}"
|
||||
FocusVisualSecondaryThickness="{TemplateBinding FocusVisualSecondaryThickness}"
|
||||
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackground}"
|
||||
PointerOverBackground="{Binding TabTheme, Mode=OneWay, Converter={StaticResource TabColorFallbackConverter}, ConverterParameter=BackgroundPointerOver}"
|
||||
PointerOverForeground="{ThemeResource ListViewItemForegroundPointerOver}"
|
||||
PressedBackground="{Binding TabTheme, Mode=OneWay, Converter={StaticResource TabColorFallbackConverter}, ConverterParameter=BackgroundPressed}"
|
||||
ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
|
||||
RevealBackground="{ThemeResource ListViewItemRevealBackground}"
|
||||
RevealBorderBrush="{ThemeResource ListViewItemRevealBorderBrush}"
|
||||
RevealBorderThickness="{ThemeResource ListViewItemRevealBorderThemeThickness}"
|
||||
SelectedBackground="{Binding TabTheme, Mode=OneWay, Converter={StaticResource TabColorFallbackConverter}, ConverterParameter=BackgroundSelected}"
|
||||
SelectedForeground="{ThemeResource ListViewItemForegroundSelected}"
|
||||
SelectedPointerOverBackground="{Binding TabTheme, Mode=OneWay, Converter={StaticResource TabColorFallbackConverter}, ConverterParameter=BackgroundSelectedPointerOver}"
|
||||
SelectedPressedBackground="{Binding TabTheme, Mode=OneWay, Converter={StaticResource TabColorFallbackConverter}, ConverterParameter=BackgroundSelectedPressed}"
|
||||
SelectionCheckMarkVisualEnabled="{ThemeResource ListViewItemSelectionCheckMarkVisualEnabled}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Selected" />
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="PointerOver" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PointerOverSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="PointerOver" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOverPressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PressedSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.(RevealBrush.State)" Value="Pressed" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="DisabledStates">
|
||||
<VisualState x:Name="Enabled" />
|
||||
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Root.RevealBorderThickness" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ListViewItemPresenter>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemsPanel>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// see: https://stackoverflow.com/questions/33573929/uwp-binding-in-style-setter-not-working
|
||||
/// </summary>
|
||||
public class TabBarBackgroundBindingHelper
|
||||
{
|
||||
public static readonly DependencyProperty BackgroundBindingPathProperty =
|
||||
DependencyProperty.RegisterAttached("BackgroundBindingPath", typeof(string), typeof(TabBarBackgroundBindingHelper), new PropertyMetadata(null, BindingPathPropertyChanged));
|
||||
|
||||
public static string GetBackgroundBindingPath(DependencyObject obj)
|
||||
{
|
||||
return (string)obj.GetValue(BackgroundBindingPathProperty);
|
||||
}
|
||||
|
||||
public static void SetBackgroundBindingPath(DependencyObject obj, string value)
|
||||
{
|
||||
obj.SetValue(BackgroundBindingPathProperty, value);
|
||||
}
|
||||
private static void BindingPathPropertyChanged(DependencyObject obj,
|
||||
DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is string propertyPath)
|
||||
{
|
||||
var binding = new Binding
|
||||
{
|
||||
Path = new PropertyPath($"Content.{propertyPath}"),
|
||||
Mode = BindingMode.OneWay,
|
||||
RelativeSource = new RelativeSource
|
||||
{
|
||||
Mode = RelativeSourceMode.Self
|
||||
},
|
||||
Converter = (IValueConverter)Application.Current.Resources["TabColorFallbackConverter"],
|
||||
ConverterParameter = TabThemeKey.Background.ToString()
|
||||
};
|
||||
BindingOperations.SetBinding(obj, ListViewItem.BackgroundProperty, binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using FluentTerminal.Models;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public class TabThemeTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate DefaultTemplate { get; set; }
|
||||
public DataTemplate ColoredTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
return SelectTemplateCore(item);
|
||||
}
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
if (item is TabTheme tabTheme)
|
||||
{
|
||||
return tabTheme.Id == 0 ? DefaultTemplate : ColoredTemplate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,11 @@
|
||||
x:Name="Root"
|
||||
IsEnabled="{x:Bind EnableEditing, Mode=OneWay}"
|
||||
Style="{StaticResource ColorPreviewButtonStyle}"
|
||||
ToolTipService.ToolTip="{x:Bind ColorName}">
|
||||
<Button.Background>
|
||||
<SolidColorBrush Color="{x:Bind SelectedColor, Mode=OneWay}" />
|
||||
</Button.Background>
|
||||
ToolTipService.ToolTip="{x:Bind ColorName}"
|
||||
Background="{x:Bind ButtonBackground, Mode=OneWay}">
|
||||
<!--<Button.Background>
|
||||
<SolidColorBrush Color="{x:Bind VisibleColor, Mode=OneWay}" />
|
||||
</Button.Background>-->
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using Windows.UI;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class TerminalColorPicker : UserControl
|
||||
{
|
||||
bool colorIsNull = false;
|
||||
|
||||
public static readonly DependencyProperty ColorNameProperty =
|
||||
DependencyProperty.Register(nameof(ColorName), typeof(string), typeof(TerminalColorPicker), new PropertyMetadata(null));
|
||||
|
||||
@@ -16,14 +19,25 @@ namespace FluentTerminal.App.Views
|
||||
DependencyProperty.Register(nameof(IsAlphaEnabled), typeof(bool), typeof(TerminalColorPicker), new PropertyMetadata(false));
|
||||
|
||||
public static readonly DependencyProperty SelectedColorProperty =
|
||||
DependencyProperty.Register(nameof(SelectedColor), typeof(Color), typeof(TerminalColorPicker), new PropertyMetadata(Colors.Transparent, (s, e) =>
|
||||
DependencyProperty.Register(nameof(SelectedColor), typeof(Color?), typeof(TerminalColorPicker), new PropertyMetadata(Colors.Transparent, (s, e) =>
|
||||
{
|
||||
if (s is TerminalColorPicker terminalColorPicker)
|
||||
{
|
||||
terminalColorPicker.colorPicker.Color = (Color)e.NewValue;
|
||||
terminalColorPicker.colorIsNull = ((Color?)e.NewValue) == null;
|
||||
|
||||
// When leading the color, or changing the selected color (happens on page load)
|
||||
// only set the picker (triggering the event action later) if the color is non-null
|
||||
if (((Color?)e.NewValue) != null)
|
||||
{
|
||||
terminalColorPicker.colorPicker.Color = (Color)e.NewValue;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
public static readonly DependencyProperty ButtonBackgroundProperty =
|
||||
DependencyProperty.Register(nameof(ButtonBackground), typeof(Brush), typeof(TerminalColorPicker), new PropertyMetadata(false));
|
||||
|
||||
|
||||
public TerminalColorPicker()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -48,10 +62,30 @@ namespace FluentTerminal.App.Views
|
||||
set { SetValue(IsAlphaEnabledProperty, value); }
|
||||
}
|
||||
|
||||
public Color SelectedColor
|
||||
public Color? SelectedColor
|
||||
{
|
||||
get { return (Color)GetValue(SelectedColorProperty); }
|
||||
set { SetValue(SelectedColorProperty, value); }
|
||||
get { return (Color?)GetValue(SelectedColorProperty); }
|
||||
set
|
||||
{
|
||||
SetValue(SelectedColorProperty, value);
|
||||
SetValue(ButtonBackgroundProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Brush ButtonBackground
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedColor == null)
|
||||
{
|
||||
return (Brush)Application.Current.Resources["MissingColorSelectionBrush"];
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SolidColorBrush((Color)GetValue(SelectedColorProperty));
|
||||
}
|
||||
}
|
||||
set { }
|
||||
}
|
||||
|
||||
private void ColorPicker_ColorChanged(ColorPicker sender, ColorChangedEventArgs args)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using FluentTerminal.Models;
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
{
|
||||
public class TerminalThemeTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate DefaultTemplate { get; set; }
|
||||
public DataTemplate ThemeTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
return SelectTemplateCore(item);
|
||||
}
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
if (item is TerminalTheme theme)
|
||||
{
|
||||
return theme.Id == Guid.Empty ? DefaultTemplate : ThemeTemplate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace FluentTerminal.App.Views
|
||||
StartMediatorTask();
|
||||
}
|
||||
|
||||
public event EventHandler<Command> KeyboardCommandReceived;
|
||||
public event EventHandler<string> KeyboardCommandReceived;
|
||||
|
||||
public event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
|
||||
@@ -135,7 +135,14 @@ namespace FluentTerminal.App.Views
|
||||
|
||||
public void OnKeyboardCommand(string command)
|
||||
{
|
||||
_dispatcherJobs.Add(() => KeyboardCommandReceived?.Invoke(this, Enum.Parse<Command>(command, true)));
|
||||
if (Enum.TryParse(command, true, out Command commandValue))
|
||||
{
|
||||
_dispatcherJobs.Add(() => KeyboardCommandReceived?.Invoke(this, commandValue.ToString()));
|
||||
}
|
||||
else if (Guid.TryParse(command, out Guid shellProfileId))
|
||||
{
|
||||
_dispatcherJobs.Add(() => KeyboardCommandReceived?.Invoke(this, shellProfileId.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
public void OnTerminalResized(int columns, int rows)
|
||||
|
||||
Generated
+3413
-2117
File diff suppressed because it is too large.
Load diff
@@ -10,9 +10,10 @@
|
||||
"author": "FS Apps",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"webpack": "^3.10.0"
|
||||
"webpack": "^4.16.5",
|
||||
"webpack-cli": "^3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"xterm": "^3.5.1"
|
||||
"xterm": "^3.9.1"
|
||||
}
|
||||
}
|
||||
@@ -55,9 +55,11 @@ function createTerminal(options, theme, keyBindings) {
|
||||
});
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.winptyCompatInit();
|
||||
term.fit();
|
||||
term.winptyCompatInit();
|
||||
term.focus();
|
||||
|
||||
setPadding(options.padding);
|
||||
|
||||
var resizeTimeout;
|
||||
window.onresize = function () {
|
||||
@@ -139,6 +141,7 @@ function changeOptions(options) {
|
||||
term.setOption('fontSize', options.fontSize);
|
||||
term.setOption('scrollback', options.scrollBackLimit);
|
||||
setScrollBarStyle(options.scrollBarStyle);
|
||||
setPadding(options.padding);
|
||||
}
|
||||
|
||||
function setScrollBarStyle(scrollBarStyle) {
|
||||
@@ -152,6 +155,11 @@ function setScrollBarStyle(scrollBarStyle) {
|
||||
|
||||
}
|
||||
|
||||
function setPadding(padding) {
|
||||
document.querySelector('.terminal').style.padding = padding + 'px';
|
||||
term.fit();
|
||||
}
|
||||
|
||||
function changeKeyBindings(keyBindings) {
|
||||
keyBindings = JSON.parse(keyBindings);
|
||||
window.keyBindings = keyBindings;
|
||||
@@ -181,4 +189,4 @@ window.changeTheme = changeTheme;
|
||||
window.changeOptions = changeOptions;
|
||||
window.changeKeyBindings = changeKeyBindings;
|
||||
window.findNext = findNext;
|
||||
window.findPrevious = findPrevious;
|
||||
window.findPrevious = findPrevious;
|
||||
@@ -18,5 +18,4 @@ html, body {
|
||||
|
||||
.terminal {
|
||||
background: transparent !important;
|
||||
padding: 12px;
|
||||
}
|
||||
@@ -7,11 +7,14 @@ namespace FluentTerminal.Models
|
||||
public bool ConfirmClosingTabs { get; set; }
|
||||
public bool ConfirmClosingWindows { get; set; }
|
||||
public bool UnderlineSelectedTab { get; set; }
|
||||
public InactiveTabColorMode InactiveTabColorMode { get; set; }
|
||||
public NewTerminalLocation NewTerminalLocation { get; set; }
|
||||
public TabsPosition TabsPosition { get; set; }
|
||||
public bool CopyOnSelect { get; set; }
|
||||
public MouseAction MouseMiddleClickAction { get; set; }
|
||||
public MouseAction MouseRightClickAction { get; set; }
|
||||
public bool AlwaysShowTabs { get; set; }
|
||||
public bool ShowNewOutputIndicator { get; set; }
|
||||
public bool EnableTrayIcon { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,33 @@ namespace FluentTerminal.Models.Enums
|
||||
SelectAll,
|
||||
|
||||
[Description("Clear")]
|
||||
Clear
|
||||
Clear,
|
||||
|
||||
[Description("Switch to Terminal 1")]
|
||||
SwitchToTerm1,
|
||||
|
||||
[Description("Switch to Terminal 2")]
|
||||
SwitchToTerm2,
|
||||
|
||||
[Description("Switch to Terminal 3")]
|
||||
SwitchToTerm3,
|
||||
|
||||
[Description("Switch to Terminal 4")]
|
||||
SwitchToTerm4,
|
||||
|
||||
[Description("Switch to Terminal 5")]
|
||||
SwitchToTerm5,
|
||||
|
||||
[Description("Switch to Terminal 6")]
|
||||
SwitchToTerm6,
|
||||
|
||||
[Description("Switch to Terminal 7")]
|
||||
SwitchToTerm7,
|
||||
|
||||
[Description("Switch to Terminal 8")]
|
||||
SwitchToTerm8,
|
||||
|
||||
[Description("Switch to Terminal 9")]
|
||||
SwitchToTerm9
|
||||
}
|
||||
}
|
||||
@@ -179,6 +179,9 @@ namespace FluentTerminal.Models.Enums
|
||||
MediaStop = 178,
|
||||
MediaPlayPause = 179,
|
||||
|
||||
[Description(";")]
|
||||
OEM_1 = 186,
|
||||
|
||||
[Description("+")]
|
||||
Plus = 187,
|
||||
|
||||
@@ -191,11 +194,11 @@ namespace FluentTerminal.Models.Enums
|
||||
[Description(".")]
|
||||
Period = 190,
|
||||
|
||||
[Description("#")]
|
||||
NumberSign = 191,
|
||||
[Description("/")]
|
||||
OEM_2 = 191,
|
||||
|
||||
[Description("`")]
|
||||
GraveAccent = 192,
|
||||
OEM_3 = 192,
|
||||
|
||||
GamepadA = 195,
|
||||
GamepadB = 196,
|
||||
@@ -222,13 +225,19 @@ namespace FluentTerminal.Models.Enums
|
||||
GamepadRightThumbstickRight = 217,
|
||||
GamepadRightThumbstickLeft = 218,
|
||||
|
||||
[Description(")")]
|
||||
OpenBrackets = 219,
|
||||
[Description("[")]
|
||||
OEM_4 = 219,
|
||||
|
||||
[Description("^")]
|
||||
Circumflex = 220,
|
||||
[Description(@"\")]
|
||||
OEM_5 = 220,
|
||||
|
||||
[Description("´")]
|
||||
AcuteAccent = 221
|
||||
[Description("]")]
|
||||
OEM_6 = 221,
|
||||
|
||||
[Description("'")]
|
||||
OEM_7 = 222,
|
||||
|
||||
[Description("<")]
|
||||
OEM_102 = 226
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FluentTerminal.Models.Enums
|
||||
{
|
||||
public enum InactiveTabColorMode
|
||||
{
|
||||
Background,
|
||||
Underlined
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FluentTerminal.Models.Enums
|
||||
{
|
||||
public enum TabThemeKey
|
||||
{
|
||||
Background,
|
||||
BackgroundPointerOver,
|
||||
BackgroundPressed,
|
||||
BackgroundSelected,
|
||||
BackgroundSelectedPointerOver,
|
||||
BackgroundSelectedPressed
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,23 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.Models
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class KeyBinding
|
||||
{
|
||||
public Command Command { get; set; }
|
||||
public KeyBinding()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public KeyBinding(KeyBinding other)
|
||||
{
|
||||
Command = other.Command;
|
||||
Key = other.Key;
|
||||
Ctrl = other.Ctrl;
|
||||
Alt = other.Alt;
|
||||
Shift = other.Shift;
|
||||
Meta = other.Meta;
|
||||
}
|
||||
|
||||
public string Command { get; set; }
|
||||
public int Key { get; set; }
|
||||
public bool Ctrl { get; set; }
|
||||
public bool Alt { get; set; }
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class ShellProfile
|
||||
{
|
||||
public ShellProfile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ShellProfile(ShellProfile other)
|
||||
{
|
||||
Id = other.Id;
|
||||
PreInstalled = other.PreInstalled;
|
||||
Name = other.Name;
|
||||
Arguments = other.Arguments;
|
||||
Location = other.Location;
|
||||
WorkingDirectory = other.WorkingDirectory;
|
||||
TabThemeId = other.TabThemeId;
|
||||
TerminalThemeId = other.TerminalThemeId;
|
||||
KeyBindings = other.KeyBindings.Select(x => new KeyBinding(x)).ToList();
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public bool PreInstalled { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Arguments { get; set; }
|
||||
public string Location { get; set; }
|
||||
public string WorkingDirectory { get; set; }
|
||||
public int TabThemeId { get; set; }
|
||||
public Guid TerminalThemeId { get; set; }
|
||||
public ICollection<KeyBinding> KeyBindings { get; set; } = new List<KeyBinding>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class TabTheme
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Color { get; set; }
|
||||
public double BackgroundOpacity { get; set; } = 0.35;
|
||||
public double BackgroundPointerOverOpacity { get; set; } = 0.4;
|
||||
public double BackgroundPressedOpacity { get; set; } = 0.45;
|
||||
public double BackgroundSelectedOpacity { get; set; } = 0.7;
|
||||
public double BackgroundSelectedPointerOverOpacity { get; set; } = 0.8;
|
||||
public double BackgroundSelectedPressedOpacity { get; set; } = 0.9;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ namespace FluentTerminal.Models
|
||||
|
||||
public double BackgroundOpacity { get; set; }
|
||||
|
||||
public int Padding { get; set; }
|
||||
|
||||
public uint ScrollBackLimit { get; set; }
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -116,10 +116,8 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\AppCommunicationService.cs" />
|
||||
<Compile Include="Services\ToggleWindowService.cs" />
|
||||
<Compile Include="Services\NotificationService.cs" />
|
||||
<Compile Include="Services\TerminalSession.cs" />
|
||||
<Compile Include="Services\TerminalsManager.cs" />
|
||||
<Compile Include="Services\UpdateService.cs" />
|
||||
<Compile Include="SystemTrayApplicationContext.cs" />
|
||||
<Compile Include="Utilities.cs" />
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
@@ -156,12 +154,6 @@
|
||||
<PackageReference Include="GlobalHotKey">
|
||||
<Version>1.1.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>11.0.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="RestSharp">
|
||||
<Version>106.3.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="winpty.NET">
|
||||
<Version>0.4.2</Version>
|
||||
</PackageReference>
|
||||
|
||||
@@ -39,8 +39,7 @@ namespace FluentTerminal.SystemTray
|
||||
var containerBuilder = new ContainerBuilder();
|
||||
|
||||
containerBuilder.RegisterInstance(applicationDataContainers);
|
||||
containerBuilder.RegisterType<UpdateService>().SingleInstance();
|
||||
containerBuilder.RegisterType<NotificationService>().SingleInstance();
|
||||
containerBuilder.RegisterType<NotificationService>().As<INotificationService>().InstancePerDependency();
|
||||
containerBuilder.RegisterType<TerminalsManager>().SingleInstance();
|
||||
containerBuilder.RegisterType<ToggleWindowService>().SingleInstance();
|
||||
containerBuilder.RegisterType<HotKeyManager>().SingleInstance();
|
||||
@@ -48,6 +47,7 @@ namespace FluentTerminal.SystemTray
|
||||
containerBuilder.RegisterType<AppCommunicationService>().SingleInstance();
|
||||
containerBuilder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>();
|
||||
containerBuilder.RegisterType<SettingsService>().As<ISettingsService>();
|
||||
containerBuilder.RegisterType<UpdateService>().As<IUpdateService>();
|
||||
containerBuilder.RegisterInstance(Dispatcher.CurrentDispatcher).SingleInstance();
|
||||
|
||||
var container = containerBuilder.Build();
|
||||
@@ -59,8 +59,17 @@ namespace FluentTerminal.SystemTray
|
||||
appCommunicationService.StartAppServiceConnection();
|
||||
}
|
||||
|
||||
Task.Run(() => CheckForNewVersion(container.Resolve<UpdateService>(), container.Resolve<NotificationService>()));
|
||||
Application.Run(container.Resolve<SystemTrayApplicationContext>());
|
||||
Task.Run(() => container.Resolve<IUpdateService>().CheckForUpdate());
|
||||
|
||||
var settingsService = container.Resolve<ISettingsService>();
|
||||
if (settingsService.GetApplicationSettings().EnableTrayIcon)
|
||||
{
|
||||
Application.Run(container.Resolve<SystemTrayApplicationContext>());
|
||||
}
|
||||
else
|
||||
{
|
||||
Application.Run();
|
||||
}
|
||||
|
||||
mutex.Close();
|
||||
}
|
||||
@@ -70,15 +79,5 @@ namespace FluentTerminal.SystemTray
|
||||
eventWaitHandle.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckForNewVersion(UpdateService updateService, NotificationService notificationService)
|
||||
{
|
||||
var newVersion = updateService.IsNewerVersionAvailable();
|
||||
if (newVersion)
|
||||
{
|
||||
notificationService.ShowNotification("Update available",
|
||||
"Click to open the releases page.", "https://github.com/felixse/FluentTerminal/releases");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,7 +180,7 @@ namespace FluentTerminal.SystemTray.Services
|
||||
|
||||
if (readChars > 0)
|
||||
{
|
||||
var output = new String(buffer, 0, readChars);
|
||||
var output = new System.String(buffer, 0, readChars);
|
||||
_terminalsManager.DisplayTerminalOutput(Id, output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,166 +1,166 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GlobalHotKey;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Core;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Services
|
||||
{
|
||||
public class ToggleWindowService : IDisposable
|
||||
{
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GlobalHotKey;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Core;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Services
|
||||
{
|
||||
public class ToggleWindowService : IDisposable
|
||||
{
|
||||
private const int SW_MINIMIZE = 6;
|
||||
private readonly HotKeyManager _hotKeyManager;
|
||||
private readonly NotificationService _notificationService;
|
||||
private readonly Dispatcher _dispatcher;
|
||||
private bool _disposedValue;
|
||||
private readonly HotKeyManager _hotKeyManager;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly Dispatcher _dispatcher;
|
||||
private bool _disposedValue;
|
||||
private readonly List<HotKey> _hotKeys;
|
||||
|
||||
public ToggleWindowService(Dispatcher dispatcher, HotKeyManager hotKeyManager, NotificationService notificationService, ISettingsService settingsService)
|
||||
{
|
||||
_dispatcher = dispatcher;
|
||||
_notificationService = notificationService;
|
||||
_hotKeyManager = hotKeyManager;
|
||||
_hotKeys = new List<HotKey>();
|
||||
_hotKeyManager.KeyPressed += OnKeyPressed;
|
||||
|
||||
var keyBindings = settingsService.GetKeyBindings()[Command.ToggleWindow];
|
||||
SetHotKeys(keyBindings);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public void SetHotKeys(IEnumerable<KeyBinding> keyBindings)
|
||||
{
|
||||
foreach (var hotKey in _hotKeys)
|
||||
{
|
||||
_dispatcher.Invoke(() =>
|
||||
{
|
||||
_hotKeyManager.Unregister(hotKey);
|
||||
});
|
||||
}
|
||||
|
||||
_hotKeys.Clear();
|
||||
|
||||
foreach (var keyBinding in keyBindings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = Utilities.ExtendVirtualKeyToInputKey((ExtendedVirtualKey)keyBinding.Key);
|
||||
var modifiers = ModifierKeys.None;
|
||||
if (keyBinding.Alt)
|
||||
{
|
||||
modifiers |= ModifierKeys.Alt;
|
||||
}
|
||||
if (keyBinding.Ctrl)
|
||||
{
|
||||
modifiers |= ModifierKeys.Control;
|
||||
}
|
||||
if (keyBinding.Shift)
|
||||
{
|
||||
modifiers |= ModifierKeys.Shift;
|
||||
}
|
||||
if (keyBinding.Meta)
|
||||
{
|
||||
modifiers |= ModifierKeys.Windows;
|
||||
}
|
||||
|
||||
var hotKey = new HotKey(key, modifiers);
|
||||
|
||||
_dispatcher.Invoke(() =>
|
||||
{
|
||||
_hotKeyManager.Register(hotKey);
|
||||
});
|
||||
|
||||
_hotKeys.Add(hotKey);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_notificationService.ShowNotification("Error", "Failed to register the following hotkey: " + GetKeyBindingRepresentation(keyBinding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_hotKeyManager?.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
private static string GetKeyBindingRepresentation(KeyBinding keyBinding)
|
||||
{
|
||||
var keys = new List<string>();
|
||||
if (keyBinding.Ctrl)
|
||||
{
|
||||
keys.Add("Ctrl");
|
||||
}
|
||||
if (keyBinding.Alt)
|
||||
{
|
||||
keys.Add("Alt");
|
||||
}
|
||||
if (keyBinding.Shift)
|
||||
{
|
||||
keys.Add("Shift");
|
||||
}
|
||||
|
||||
keys.Add(((ExtendedVirtualKey)keyBinding.Key).ToString());
|
||||
|
||||
return string.Join(" + ", keys);
|
||||
|
||||
public ToggleWindowService(Dispatcher dispatcher, HotKeyManager hotKeyManager, INotificationService notificationService, ISettingsService settingsService)
|
||||
{
|
||||
_dispatcher = dispatcher;
|
||||
_notificationService = notificationService;
|
||||
_hotKeyManager = hotKeyManager;
|
||||
_hotKeys = new List<HotKey>();
|
||||
_hotKeyManager.KeyPressed += OnKeyPressed;
|
||||
|
||||
var keyBindings = settingsService.GetCommandKeyBindings()[nameof(Command.ToggleWindow)];
|
||||
SetHotKeys(keyBindings);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public void SetHotKeys(IEnumerable<KeyBinding> keyBindings)
|
||||
{
|
||||
foreach (var hotKey in _hotKeys)
|
||||
{
|
||||
_dispatcher.Invoke(() =>
|
||||
{
|
||||
_hotKeyManager.Unregister(hotKey);
|
||||
});
|
||||
}
|
||||
|
||||
_hotKeys.Clear();
|
||||
|
||||
foreach (var keyBinding in keyBindings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = Utilities.ExtendVirtualKeyToInputKey((ExtendedVirtualKey)keyBinding.Key);
|
||||
var modifiers = ModifierKeys.None;
|
||||
if (keyBinding.Alt)
|
||||
{
|
||||
modifiers |= ModifierKeys.Alt;
|
||||
}
|
||||
if (keyBinding.Ctrl)
|
||||
{
|
||||
modifiers |= ModifierKeys.Control;
|
||||
}
|
||||
if (keyBinding.Shift)
|
||||
{
|
||||
modifiers |= ModifierKeys.Shift;
|
||||
}
|
||||
if (keyBinding.Meta)
|
||||
{
|
||||
modifiers |= ModifierKeys.Windows;
|
||||
}
|
||||
|
||||
var hotKey = new HotKey(key, modifiers);
|
||||
|
||||
_dispatcher.Invoke(() =>
|
||||
{
|
||||
_hotKeyManager.Register(hotKey);
|
||||
});
|
||||
|
||||
_hotKeys.Add(hotKey);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_notificationService.ShowNotification("Error", "Failed to register the following hotkey: " + GetKeyBindingRepresentation(keyBinding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_hotKeyManager?.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
private static string GetKeyBindingRepresentation(KeyBinding keyBinding)
|
||||
{
|
||||
var keys = new List<string>();
|
||||
if (keyBinding.Ctrl)
|
||||
{
|
||||
keys.Add("Ctrl");
|
||||
}
|
||||
if (keyBinding.Alt)
|
||||
{
|
||||
keys.Add("Alt");
|
||||
}
|
||||
if (keyBinding.Shift)
|
||||
{
|
||||
keys.Add("Shift");
|
||||
}
|
||||
|
||||
keys.Add(((ExtendedVirtualKey)keyBinding.Key).ToString());
|
||||
|
||||
return string.Join(" + ", keys);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
private string GetActiveProcessFileName()
|
||||
{
|
||||
try
|
||||
{
|
||||
var hwnd = GetForegroundWindow();
|
||||
GetWindowThreadProcessId(hwnd, out uint pid);
|
||||
var process = Process.GetProcessById((int)pid);
|
||||
return process.MainWindowTitle;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnKeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
if (GetActiveProcessFileName().EndsWith("Fluent Terminal"))
|
||||
|
||||
private string GetActiveProcessFileName()
|
||||
{
|
||||
try
|
||||
{
|
||||
var hwnd = GetForegroundWindow();
|
||||
ShowWindow(hwnd, SW_MINIMIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<AppListEntry> appListEntries = await Package.Current.GetAppListEntriesAsync();
|
||||
await appListEntries.First().LaunchAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
GetWindowThreadProcessId(hwnd, out uint pid);
|
||||
var process = Process.GetProcessById((int)pid);
|
||||
return process.MainWindowTitle;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnKeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
if (GetActiveProcessFileName().EndsWith("Fluent Terminal"))
|
||||
{
|
||||
var hwnd = GetForegroundWindow();
|
||||
ShowWindow(hwnd, SW_MINIMIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<AppListEntry> appListEntries = await Package.Current.GetAppListEntriesAsync();
|
||||
await appListEntries.First().LaunchAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace FluentTerminal.SystemTray.Services
|
||||
{
|
||||
public class UpdateService
|
||||
{
|
||||
private const string apiEndpoint = "https://api.github.com";
|
||||
|
||||
public bool IsNewerVersionAvailable()
|
||||
{
|
||||
var restClient = new RestClient(apiEndpoint);
|
||||
var restRequest = new RestRequest("/repos/felixse/fluentterminal/releases", Method.GET);
|
||||
|
||||
var restResponse = restClient.Execute(restRequest);
|
||||
if (restResponse.IsSuccessful)
|
||||
{
|
||||
dynamic restResponseData = JsonConvert.DeserializeObject(restResponse.Content);
|
||||
string tag = restResponseData[0].tag_name;
|
||||
var latestVersion = new Version(tag);
|
||||
var currentVersion = Package.Current.Id.Version;
|
||||
return latestVersion > new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build, currentVersion.Revision);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -462,21 +462,27 @@ namespace FluentTerminal.SystemTray
|
||||
case ExtendedVirtualKey.Period:
|
||||
return Key.OemPeriod;
|
||||
|
||||
case ExtendedVirtualKey.NumberSign:
|
||||
return Key.OemQuestion;
|
||||
case ExtendedVirtualKey.OEM_1:
|
||||
return Key.Oem1;
|
||||
|
||||
case ExtendedVirtualKey.OpenBrackets:
|
||||
return Key.OemOpenBrackets;
|
||||
case ExtendedVirtualKey.OEM_2:
|
||||
return Key.Oem1;
|
||||
|
||||
case ExtendedVirtualKey.GraveAccent:
|
||||
case ExtendedVirtualKey.OEM_3:
|
||||
return Key.Oem3;
|
||||
|
||||
case ExtendedVirtualKey.Circumflex:
|
||||
case ExtendedVirtualKey.OEM_5:
|
||||
return Key.Oem5;
|
||||
|
||||
case ExtendedVirtualKey.AcuteAccent:
|
||||
case ExtendedVirtualKey.OEM_6:
|
||||
return Key.Oem6;
|
||||
|
||||
case ExtendedVirtualKey.OEM_7:
|
||||
return Key.Oem7;
|
||||
|
||||
case ExtendedVirtualKey.OEM_102:
|
||||
return Key.Oem102;
|
||||
|
||||
default:
|
||||
return Key.None;
|
||||
}
|
||||
|
||||
+642
@@ -0,0 +1,642 @@
|
||||
#
|
||||
# Install.ps1 is a PowerShell script designed to sideload the Fluent Terminal
|
||||
# app. To run this script from Explorer, right-click on its icon and choose
|
||||
# "Run with PowerShell".
|
||||
#
|
||||
# It is heavily based on the Add-AppxDevPackage.ps1 script generated by
|
||||
# Visual Studio. Visual Studio supplies this script in the folder generated
|
||||
# with its "Prepare Package" command. The same folder will also contain the
|
||||
# app package (a .appx file), the signing certificate (a .cer file), and a
|
||||
# "Dependencies" subfolder containing all the framework packages used by the
|
||||
# app.
|
||||
#
|
||||
# This script simplifies installing these packages by automating the
|
||||
# following functions:
|
||||
# 1. Find the app package and signing certificate in the script directory
|
||||
# 2. Prompt the user to enable sideloading apps and to install the
|
||||
# certificate if necessary
|
||||
# 3. Find dependency packages that are applicable to the operating system's
|
||||
# CPU architecture
|
||||
# 4. Install the package along with all applicable dependencies
|
||||
#
|
||||
# All command line parameters are reserved for use internally by the script.
|
||||
# Users should launch this script from Explorer.
|
||||
#
|
||||
|
||||
# .Link
|
||||
# http://go.microsoft.com/fwlink/?LinkId=243053
|
||||
|
||||
param(
|
||||
[switch]$Force = $false,
|
||||
[switch]$GetDeveloperLicense = $false,
|
||||
[string]$CertificatePath = $null
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# UI Strings
|
||||
class UiStrings
|
||||
{
|
||||
static [string] $PromptYesString = "&Yes"
|
||||
static [string] $PromptNoString = "&No"
|
||||
static [string] $BundleFound = "Found bundle: {0}"
|
||||
static [string] $PackageFound = "Found package: {0}"
|
||||
static [string] $CertificateFound = "Found certificate: {0}"
|
||||
static [string] $DependenciesFound = "Found dependency package(s):"
|
||||
static [string] $GettingDeveloperLicense = "Please select `"Sideload apps`" or `"Developer mode`" in the following window."
|
||||
static [string] $InstallingCertificate = "Installing certificate..."
|
||||
static [string] $InstallingPackage = "`nInstalling app..."
|
||||
static [string] $AcquireLicenseSuccessful = "Sideloading was successfully enabled."
|
||||
static [string] $InstallCertificateSuccessful = "The certificate was successfully installed."
|
||||
static [string] $PromptContextMenuInstall = "Fluent Terminal can be integrated into the Windows Explorer context menu, allowing you to quickly open the terminal to a directory.`n`nDo you want to enable this integration?`n`n"
|
||||
static [string] $Success = "`nSuccess: Your app was successfully installed."
|
||||
static [string] $WarningInstallCert = "`nYou are about to install a digital certificate to your computer's Trusted People certificate store. Doing so carries serious security risk and should only be done if you trust the originator of this digital certificate.`n`nWhen you are done using this app, you should manually remove the associated digital certificate. Instructions for doing so can be found here: http://go.microsoft.com/fwlink/?LinkId=243053`n`nAre you sure you wish to continue?`n`n"
|
||||
static [string] $ElevateActions = "`nBefore installing this app, you need to do the following:"
|
||||
static [string] $ElevateActionDevLicense = "`t- Enable sideloading apps"
|
||||
static [string] $ElevateActionCertificate = "`t- Install the signing certificate"
|
||||
static [string] $ElevateActionsContinue = "Administrator credentials are required to continue. Please accept the UAC prompt and provide your administrator password if asked."
|
||||
static [string] $ErrorForceElevate = "You must provide administrator credentials to proceed. Please run this script without the -Force parameter or from an elevated PowerShell window."
|
||||
static [string] $ErrorForceDeveloperLicense = "Enabling sideloading requires user interaction. Please rerun the script without the -Force parameter."
|
||||
static [string] $ErrorLaunchAdminFailed = "Error: Could not start a new process as administrator."
|
||||
static [string] $ErrorNoScriptPath = "Error: You must launch this script from a file."
|
||||
static [string] $ErrorNoPackageFound = "Error: No package or bundle found in the script directory. Please make sure the package or bundle you want to install is placed in the same directory as this script."
|
||||
static [string] $ErrorManyPackagesFound = "Error: More than one package or bundle found in the script directory. Please make sure only the package or bundle you want to install is placed in the same directory as this script."
|
||||
static [string] $ErrorPackageUnsigned = "Error: The package or bundle is not digitally signed or its signature is corrupted."
|
||||
static [string] $ErrorNoCertificateFound = "Error: No certificate found in the script directory. Please make sure the certificate used to sign the package or bundle you are installing is placed in the same directory as this script."
|
||||
static [string] $ErrorManyCertificatesFound = "Error: More than one certificate found in the script directory. Please make sure only the certificate used to sign the package or bundle you are installing is placed in the same directory as this script."
|
||||
static [string] $ErrorBadCertificate = "Error: The file `"{0}`" is not a valid digital certificate. CertUtil returned with error code {1}."
|
||||
static [string] $ErrorExpiredCertificate = "Error: The developer certificate `"{0}`" has expired. One possible cause is the system clock isn't set to the correct date and time. If the system settings are correct, contact the app owner to re-create a package or bundle with a valid certificate."
|
||||
static [string] $ErrorCertificateMismatch = "Error: The certificate does not match the one used to sign the package or bundle."
|
||||
static [string] $ErrorCertIsCA = "Error: The certificate can't be a certificate authority."
|
||||
static [string] $ErrorBannedKeyUsage = "Error: The certificate can't have the following key usage: {0}. Key usage must be unspecified or equal to `"DigitalSignature`"."
|
||||
static [string] $ErrorBannedEKU = "Error: The certificate can't have the following extended key usage: {0}. Only the Code Signing and Lifetime Signing EKUs are allowed."
|
||||
static [string] $ErrorNoBasicConstraints = "Error: The certificate is missing the basic constraints extension."
|
||||
static [string] $ErrorNoCodeSigningEku = "Error: The certificate is missing the extended key usage for Code Signing."
|
||||
static [string] $ErrorInstallCertificateCancelled = "Error: Installation of the certificate was cancelled."
|
||||
static [string] $ErrorCertUtilInstallFailed = "Error: Could not install the certificate. CertUtil returned with error code {0}."
|
||||
static [string] $ErrorGetDeveloperLicenseFailed = "Error: Sideloading is not enabled."
|
||||
static [string] $ErrorInstallCertificateFailed = "Error: Could not install the certificate. Status: {0}. For more information, see http://go.microsoft.com/fwlink/?LinkID=252740."
|
||||
static [string] $ErrorAddPackageFailed = "Error: Could not install the app."
|
||||
static [string] $ErrorAddPackageFailedWithCert = "Error: Could not install the app. To ensure security, please consider uninstalling the signing certificate until you can install the app. Instructions for doing so can be found here:`nhttp://go.microsoft.com/fwlink/?LinkId=243053}"
|
||||
}
|
||||
|
||||
$ScriptPath = $null
|
||||
try
|
||||
{
|
||||
$ScriptPath = (Get-Variable MyInvocation).Value.MyCommand.Path
|
||||
$ScriptDir = Split-Path -Parent $ScriptPath
|
||||
}
|
||||
catch {}
|
||||
|
||||
if (!$ScriptPath)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorNoScriptPath) $ErrorCodes.NoScriptPath
|
||||
}
|
||||
|
||||
$ErrorCodes = Data {
|
||||
ConvertFrom-StringData @'
|
||||
Success = 0
|
||||
NoScriptPath = 1
|
||||
NoPackageFound = 2
|
||||
ManyPackagesFound = 3
|
||||
NoCertificateFound = 4
|
||||
ManyCertificatesFound = 5
|
||||
BadCertificate = 6
|
||||
PackageUnsigned = 7
|
||||
CertificateMismatch = 8
|
||||
ForceElevate = 9
|
||||
LaunchAdminFailed = 10
|
||||
GetDeveloperLicenseFailed = 11
|
||||
InstallCertificateFailed = 12
|
||||
AddPackageFailed = 13
|
||||
ForceDeveloperLicense = 14
|
||||
CertUtilInstallFailed = 17
|
||||
CertIsCA = 18
|
||||
BannedEKU = 19
|
||||
NoBasicConstraints = 20
|
||||
NoCodeSigningEku = 21
|
||||
InstallCertificateCancelled = 22
|
||||
BannedKeyUsage = 23
|
||||
ExpiredCertificate = 24
|
||||
'@
|
||||
}
|
||||
|
||||
function PrintMessageAndExit($ErrorMessage, $ReturnCode)
|
||||
{
|
||||
Write-Host $ErrorMessage
|
||||
if (!$Force)
|
||||
{
|
||||
Pause
|
||||
}
|
||||
exit $ReturnCode
|
||||
}
|
||||
|
||||
#
|
||||
# Warns the user about installing certificates, and presents a Yes/No prompt
|
||||
# to confirm the action. The default is set to No.
|
||||
#
|
||||
function ConfirmCertificateInstall
|
||||
{
|
||||
$Answer = $host.UI.PromptForChoice(
|
||||
"",
|
||||
([UiStrings]::WarningInstallCert),
|
||||
[System.Management.Automation.Host.ChoiceDescription[]]@(([UiStrings]::PromptYesString), ([UiStrings]::PromptNoString)),
|
||||
1)
|
||||
|
||||
return $Answer -eq 0
|
||||
}
|
||||
|
||||
#
|
||||
# Asks the user whether they want to install context menu integration for Fluent Terminal,
|
||||
# and presents a Yes/No prompt to confirm the action. The default is set to No.
|
||||
#
|
||||
function ConfirmContextMenuInstall
|
||||
{
|
||||
$Answer = $host.UI.PromptForChoice(
|
||||
"",
|
||||
([UiStrings]::PromptContextMenuInstall),
|
||||
[System.Management.Automation.Host.ChoiceDescription[]]@(([UiStrings]::PromptYesString), ([UiStrings]::PromptNoString)),
|
||||
1)
|
||||
|
||||
return $Answer -eq 0
|
||||
}
|
||||
|
||||
#
|
||||
# Validates whether a file is a valid certificate using CertUtil.
|
||||
# This needs to be done before calling Get-PfxCertificate on the file, otherwise
|
||||
# the user will get a cryptic "Password: " prompt for invalid certs.
|
||||
#
|
||||
function ValidateCertificateFormat($FilePath)
|
||||
{
|
||||
# certutil -verify prints a lot of text that we don't need, so it's redirected to $null here
|
||||
certutil.exe -verify $FilePath > $null
|
||||
if ($LastExitCode -lt 0)
|
||||
{
|
||||
PrintMessageAndExit (([UiStrings]::ErrorBadCertificate) -f $FilePath, $LastExitCode) $ErrorCodes.BadCertificate
|
||||
}
|
||||
|
||||
# Check if certificate is expired
|
||||
$cert = Get-PfxCertificate $FilePath
|
||||
if (($cert.NotBefore -gt (Get-Date)) -or ($cert.NotAfter -lt (Get-Date)))
|
||||
{
|
||||
PrintMessageAndExit (([UiStrings]::ErrorExpiredCertificate) -f $FilePath) $ErrorCodes.ExpiredCertificate
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Verify that the developer certificate meets the following restrictions:
|
||||
# - The certificate must contain a Basic Constraints extension, and its
|
||||
# Certificate Authority (CA) property must be false.
|
||||
# - The certificate's Key Usage extension must be either absent, or set to
|
||||
# only DigitalSignature.
|
||||
# - The certificate must contain an Extended Key Usage (EKU) extension with
|
||||
# Code Signing usage.
|
||||
# - The certificate must NOT contain any other EKU except Code Signing and
|
||||
# Lifetime Signing.
|
||||
#
|
||||
# These restrictions are enforced to decrease security risks that arise from
|
||||
# trusting digital certificates.
|
||||
#
|
||||
function CheckCertificateRestrictions
|
||||
{
|
||||
Set-Variable -Name BasicConstraintsExtensionOid -Value "2.5.29.19" -Option Constant
|
||||
Set-Variable -Name KeyUsageExtensionOid -Value "2.5.29.15" -Option Constant
|
||||
Set-Variable -Name EkuExtensionOid -Value "2.5.29.37" -Option Constant
|
||||
Set-Variable -Name CodeSigningEkuOid -Value "1.3.6.1.5.5.7.3.3" -Option Constant
|
||||
Set-Variable -Name LifetimeSigningEkuOid -Value "1.3.6.1.4.1.311.10.3.13" -Option Constant
|
||||
|
||||
$CertificateExtensions = (Get-PfxCertificate $CertificatePath).Extensions
|
||||
$HasBasicConstraints = $false
|
||||
$HasCodeSigningEku = $false
|
||||
|
||||
foreach ($Extension in $CertificateExtensions)
|
||||
{
|
||||
# Certificate must contain the Basic Constraints extension
|
||||
if ($Extension.oid.value -eq $BasicConstraintsExtensionOid)
|
||||
{
|
||||
# CA property must be false
|
||||
if ($Extension.CertificateAuthority)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorCertIsCA) $ErrorCodes.CertIsCA
|
||||
}
|
||||
$HasBasicConstraints = $true
|
||||
}
|
||||
|
||||
# If key usage is present, it must be set to digital signature
|
||||
elseif ($Extension.oid.value -eq $KeyUsageExtensionOid)
|
||||
{
|
||||
if ($Extension.KeyUsages -ne "DigitalSignature")
|
||||
{
|
||||
PrintMessageAndExit (([UiStrings]::ErrorBannedKeyUsage) -f $Extension.KeyUsages) $ErrorCodes.BannedKeyUsage
|
||||
}
|
||||
}
|
||||
|
||||
elseif ($Extension.oid.value -eq $EkuExtensionOid)
|
||||
{
|
||||
# Certificate must contain the Code Signing EKU
|
||||
$EKUs = $Extension.EnhancedKeyUsages.Value
|
||||
if ($EKUs -contains $CodeSigningEkuOid)
|
||||
{
|
||||
$HasCodeSigningEKU = $True
|
||||
}
|
||||
|
||||
# EKUs other than code signing and lifetime signing are not allowed
|
||||
foreach ($EKU in $EKUs)
|
||||
{
|
||||
if ($EKU -ne $CodeSigningEkuOid -and $EKU -ne $LifetimeSigningEkuOid)
|
||||
{
|
||||
PrintMessageAndExit (([UiStrings]::ErrorBannedEKU) -f $EKU) $ErrorCodes.BannedEKU
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$HasBasicConstraints)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorNoBasicConstraints) $ErrorCodes.NoBasicConstraints
|
||||
}
|
||||
if (!$HasCodeSigningEKU)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorNoCodeSigningEku) $ErrorCodes.NoCodeSigningEku
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Performs operations that require administrative privileges:
|
||||
# - Prompt the user to obtain a developer license
|
||||
# - Install the developer certificate (if -Force is not specified, also prompts the user to confirm)
|
||||
#
|
||||
function DoElevatedOperations
|
||||
{
|
||||
if ($GetDeveloperLicense)
|
||||
{
|
||||
if ($Force)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorForceDeveloperLicense) $ErrorCodes.ForceDeveloperLicense
|
||||
}
|
||||
|
||||
Write-Host ([UiStrings]::GettingDeveloperLicense)
|
||||
Pause
|
||||
|
||||
try
|
||||
{
|
||||
Show-WindowsDeveloperLicenseRegistration
|
||||
Pause
|
||||
}
|
||||
catch
|
||||
{
|
||||
$Error[0] # Dump details about the last error
|
||||
PrintMessageAndExit ([UiStrings]::ErrorGetDeveloperLicenseFailed) $ErrorCodes.GetDeveloperLicenseFailed
|
||||
}
|
||||
}
|
||||
|
||||
if ($CertificatePath)
|
||||
{
|
||||
Write-Host ([UiStrings]::InstallingCertificate)
|
||||
|
||||
# Make sure certificate format is valid and usage constraints are followed
|
||||
ValidateCertificateFormat $CertificatePath
|
||||
CheckCertificateRestrictions
|
||||
|
||||
# If -Force is not specified, warn the user and get consent
|
||||
if ($Force -or (ConfirmCertificateInstall))
|
||||
{
|
||||
# Add cert to store
|
||||
certutil.exe -addstore TrustedPeople $CertificatePath
|
||||
if ($LastExitCode -lt 0)
|
||||
{
|
||||
PrintMessageAndExit (([UiStrings]::ErrorCertUtilInstallFailed) -f $LastExitCode) $ErrorCodes.CertUtilInstallFailed
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorInstallCertificateCancelled) $ErrorCodes.InstallCertificateCancelled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Checks whether the machine has app sideloading enabled.
|
||||
#
|
||||
function CheckIfNeedToEnableSideloading
|
||||
{
|
||||
$RegistryPath = "hklm:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
|
||||
$ValueName = "AllowAllTrustedApps"
|
||||
|
||||
$Result = $true
|
||||
if (Test-Path $RegistryPath)
|
||||
{
|
||||
$SideloadingEnabledProperty = (Get-ItemProperty -Path $RegistryPath -Name $ValueName -ErrorAction SilentlyContinue)
|
||||
if ($SideloadingEnabledProperty -ne $null -and $SideloadingEnabledProperty.$ValueName -eq 1)
|
||||
{
|
||||
$Result = $false
|
||||
}
|
||||
}
|
||||
|
||||
return $Result
|
||||
}
|
||||
|
||||
#
|
||||
# Launches an elevated process running the current script to perform tasks
|
||||
# that require administrative privileges. This function waits until the
|
||||
# elevated process terminates, and checks whether those tasks were successful.
|
||||
#
|
||||
function LaunchElevated
|
||||
{
|
||||
# Set up command line arguments to the elevated process
|
||||
$RelaunchArgs = '-ExecutionPolicy Unrestricted -file "' + $ScriptPath + '"'
|
||||
|
||||
if ($Force)
|
||||
{
|
||||
$RelaunchArgs += ' -Force'
|
||||
}
|
||||
if ($NeedDeveloperLicense)
|
||||
{
|
||||
$RelaunchArgs += ' -GetDeveloperLicense'
|
||||
}
|
||||
if ($NeedInstallCertificate)
|
||||
{
|
||||
$RelaunchArgs += ' -CertificatePath "' + $DeveloperCertificatePath.FullName + '"'
|
||||
}
|
||||
|
||||
# Launch the process and wait for it to finish
|
||||
try
|
||||
{
|
||||
$AdminProcess = Start-Process "$PsHome\PowerShell.exe" -Verb RunAs -ArgumentList $RelaunchArgs -PassThru
|
||||
}
|
||||
catch
|
||||
{
|
||||
$Error[0] # Dump details about the last error
|
||||
PrintMessageAndExit ([UiStrings]::ErrorLaunchAdminFailed) $ErrorCodes.LaunchAdminFailed
|
||||
}
|
||||
|
||||
while (!($AdminProcess.HasExited))
|
||||
{
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
|
||||
# Check if all elevated operations were successful
|
||||
if ($NeedDeveloperLicense)
|
||||
{
|
||||
if (CheckIfNeedToEnableSideloading)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorGetDeveloperLicenseFailed) $ErrorCodes.GetDeveloperLicenseFailed
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host ([UiStrings]::AcquireLicenseSuccessful)
|
||||
}
|
||||
}
|
||||
if ($NeedInstallCertificate)
|
||||
{
|
||||
$Signature = Get-AuthenticodeSignature $DeveloperPackagePath -Verbose
|
||||
if ($Signature.Status -ne "Valid")
|
||||
{
|
||||
PrintMessageAndExit (([UiStrings]::ErrorInstallCertificateFailed) -f $Signature.Status) $ErrorCodes.InstallCertificateFailed
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host ([UiStrings]::InstallCertificateSuccessful)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Finds all applicable dependency packages according to OS architecture, and
|
||||
# installs the developer package with its dependencies. The expected layout
|
||||
# of dependencies is:
|
||||
#
|
||||
# <current dir>
|
||||
# \Dependencies
|
||||
# <Architecture neutral dependencies>.appx
|
||||
# \x86
|
||||
# <x86 dependencies>.appx
|
||||
# \x64
|
||||
# <x64 dependencies>.appx
|
||||
# \arm
|
||||
# <arm dependencies>.appx
|
||||
#
|
||||
function InstallPackageWithDependencies
|
||||
{
|
||||
$DependencyPackagesDir = (Join-Path $ScriptDir "Dependencies")
|
||||
$DependencyPackages = @()
|
||||
if (Test-Path $DependencyPackagesDir)
|
||||
{
|
||||
# Get architecture-neutral dependencies
|
||||
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "*.appx") | Where-Object { $_.Mode -NotMatch "d" }
|
||||
|
||||
# Get architecture-specific dependencies
|
||||
if (($Env:Processor_Architecture -eq "x86" -or $Env:Processor_Architecture -eq "amd64") -and (Test-Path (Join-Path $DependencyPackagesDir "x86")))
|
||||
{
|
||||
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x86\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
|
||||
}
|
||||
if (($Env:Processor_Architecture -eq "amd64") -and (Test-Path (Join-Path $DependencyPackagesDir "x64")))
|
||||
{
|
||||
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "x64\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
|
||||
}
|
||||
if (($Env:Processor_Architecture -eq "arm") -and (Test-Path (Join-Path $DependencyPackagesDir "arm")))
|
||||
{
|
||||
$DependencyPackages += Get-ChildItem (Join-Path $DependencyPackagesDir "arm\*.appx") | Where-Object { $_.Mode -NotMatch "d" }
|
||||
}
|
||||
}
|
||||
Write-Host ([UiStrings]::InstallingPackage)
|
||||
|
||||
$AddPackageSucceeded = $False
|
||||
try
|
||||
{
|
||||
if ($DependencyPackages.FullName.Count -gt 0)
|
||||
{
|
||||
Write-Host ([UiStrings]::DependenciesFound)
|
||||
$DependencyPackages.FullName
|
||||
Add-AppxPackage -Path $DeveloperPackagePath.FullName -DependencyPath $DependencyPackages.FullName -ForceApplicationShutdown
|
||||
}
|
||||
else
|
||||
{
|
||||
Add-AppxPackage -Path $DeveloperPackagePath.FullName -ForceApplicationShutdown
|
||||
}
|
||||
$AddPackageSucceeded = $?
|
||||
}
|
||||
catch
|
||||
{
|
||||
$Error[0] # Dump details about the last error
|
||||
}
|
||||
|
||||
if (!$AddPackageSucceeded)
|
||||
{
|
||||
if ($NeedInstallCertificate)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorAddPackageFailedWithCert) $ErrorCodes.AddPackageFailed
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorAddPackageFailed) $ErrorCodes.AddPackageFailed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Adds a regkey with the specified value, creating any missing keys as needed.
|
||||
#
|
||||
function ForceAddRegKeyValue($RegistryPath, $Name, $Type, $Value)
|
||||
{
|
||||
if (!(Test-Path $RegistryPath))
|
||||
{
|
||||
New-Item -Path $RegistryPath -Force | Out-Null
|
||||
}
|
||||
|
||||
New-ItemProperty -Path $RegistryPath -Name $Name -PropertyType $Type -Value $Value -Force | Out-Null
|
||||
}
|
||||
|
||||
#
|
||||
# Adds the needed regkeys to enable Explorer context menu integration.
|
||||
#
|
||||
function InstallContextMenuIntegration
|
||||
{
|
||||
ForceAddRegKeyValue `
|
||||
-RegistryPath "hkcu:\Software\Classes\Directory\shell\Open Fluent Terminal here\command"`
|
||||
-Name "(Default)"`
|
||||
-Type "ExpandString"`
|
||||
-Value "`"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe`" new %1"
|
||||
|
||||
ForceAddRegKeyValue `
|
||||
-RegistryPath "hkcu:\Software\Classes\Directory\Background\shell\Open Fluent Terminal here\command"`
|
||||
-Name "(Default)"`
|
||||
-Type "ExpandString"`
|
||||
-Value "`"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe`" new %V"
|
||||
|
||||
ForceAddRegKeyValue `
|
||||
-RegistryPath "hkcu:\Software\Classes\Directory\LibraryFolder\shell\Open Fluent Terminal here\command"`
|
||||
-Name "(Default)"`
|
||||
-Type "ExpandString"`
|
||||
-Value "`"%LOCALAPPDATA%\Microsoft\WindowsApps\flute.exe`" new %V"
|
||||
}
|
||||
|
||||
#
|
||||
# Main script logic when the user launches the script without parameters.
|
||||
#
|
||||
function DoStandardOperations
|
||||
{
|
||||
# List all .appxbundle files in the script directory
|
||||
$BundlePath = Get-ChildItem (Join-Path $ScriptDir "*.appxbundle") | Where-Object { $_.Mode -NotMatch "d" }
|
||||
$BundleCount = ($BundlePath | Measure-Object).Count
|
||||
|
||||
# There must be exactly one bundle
|
||||
if ($BundleCount -lt 1)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorNoPackageFound) $ErrorCodes.NoPackageFound
|
||||
}
|
||||
elseif ($BundleCount -gt 1)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorManyPackagesFound) $ErrorCodes.ManyPackagesFound
|
||||
}
|
||||
|
||||
# First attempt to install a bundle.
|
||||
if ($BundleCount -eq 1)
|
||||
{
|
||||
$DeveloperPackagePath = $BundlePath
|
||||
Write-Host (([UiStrings]::BundleFound) -f $DeveloperPackagePath.FullName)
|
||||
}
|
||||
|
||||
# The package must be signed
|
||||
$PackageSignature = Get-AuthenticodeSignature $DeveloperPackagePath
|
||||
$PackageCertificate = $PackageSignature.SignerCertificate
|
||||
if (!$PackageCertificate)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorPackageUnsigned) $ErrorCodes.PackageUnsigned
|
||||
}
|
||||
|
||||
# Test if the package signature is trusted. If not, the corresponding certificate
|
||||
# needs to be present in the current directory and needs to be installed.
|
||||
$NeedInstallCertificate = ($PackageSignature.Status -ne "Valid")
|
||||
|
||||
if ($NeedInstallCertificate)
|
||||
{
|
||||
# List all .cer files in the script directory
|
||||
$DeveloperCertificatePath = Get-ChildItem (Join-Path $ScriptDir "*.cer") | Where-Object { $_.Mode -NotMatch "d" }
|
||||
$DeveloperCertificateCount = ($DeveloperCertificatePath | Measure-Object).Count
|
||||
|
||||
# There must be exactly 1 certificate
|
||||
if ($DeveloperCertificateCount -lt 1)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorNoCertificateFound) $ErrorCodes.NoCertificateFound
|
||||
}
|
||||
elseif ($DeveloperCertificateCount -gt 1)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorManyCertificatesFound) $ErrorCodes.ManyCertificatesFound
|
||||
}
|
||||
|
||||
Write-Host (([UiStrings]::CertificateFound) -f $DeveloperCertificatePath.FullName)
|
||||
|
||||
# The .cer file must have the format of a valid certificate
|
||||
ValidateCertificateFormat $DeveloperCertificatePath
|
||||
|
||||
# The package signature must match the certificate file
|
||||
if ($PackageCertificate -ne (Get-PfxCertificate $DeveloperCertificatePath))
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorCertificateMismatch) $ErrorCodes.CertificateMismatch
|
||||
}
|
||||
}
|
||||
|
||||
$NeedDeveloperLicense = CheckIfNeedToEnableSideloading
|
||||
|
||||
# Relaunch the script elevated with the necessary parameters if needed
|
||||
if ($NeedDeveloperLicense -or $NeedInstallCertificate)
|
||||
{
|
||||
Write-Host ([UiStrings]::ElevateActions)
|
||||
if ($NeedDeveloperLicense)
|
||||
{
|
||||
Write-Host ([UiStrings]::ElevateActionDevLicense)
|
||||
}
|
||||
if ($NeedInstallCertificate)
|
||||
{
|
||||
Write-Host ([UiStrings]::ElevateActionCertificate)
|
||||
}
|
||||
|
||||
$IsAlreadyElevated = ([Security.Principal.WindowsIdentity]::GetCurrent().Groups.Value -contains "S-1-5-32-544")
|
||||
if ($IsAlreadyElevated)
|
||||
{
|
||||
if ($Force -and $NeedDeveloperLicense)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorForceDeveloperLicense) $ErrorCodes.ForceDeveloperLicense
|
||||
}
|
||||
if ($Force -and $NeedInstallCertificate)
|
||||
{
|
||||
Write-Warning ([UiStrings]::WarningInstallCert)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($Force)
|
||||
{
|
||||
PrintMessageAndExit ([UiStrings]::ErrorForceElevate) $ErrorCodes.ForceElevate
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Host ([UiStrings]::ElevateActionsContinue)
|
||||
Pause
|
||||
}
|
||||
}
|
||||
|
||||
LaunchElevated
|
||||
}
|
||||
|
||||
InstallPackageWithDependencies
|
||||
|
||||
if (ConfirmContextMenuInstall)
|
||||
{
|
||||
InstallContextMenuIntegration
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Main script entry point
|
||||
#
|
||||
if ($GetDeveloperLicense -or $CertificatePath)
|
||||
{
|
||||
DoElevatedOperations
|
||||
}
|
||||
else
|
||||
{
|
||||
DoStandardOperations
|
||||
PrintMessageAndExit ([UiStrings]::Success) $ErrorCodes.Success
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
# Fluent Terminal
|
||||
|
||||
[](https://ci.appveyor.com/project/felixse/fluentterminal/branch/master)
|
||||
[](https://sonarcloud.io/dashboard?id=FluentTerminal)
|
||||
[](https://gitter.im/FluentTerminal)
|
||||
|
||||
# Fluent Terminal
|
||||
A Terminal Emulator based on UWP and web technologies.
|
||||
|
||||
## Features:
|
||||
## Features
|
||||
|
||||
- Terminal for PowerShell, CMD, WSL or custom shells
|
||||
- Supports tabs and multiple windows
|
||||
- Theming and appearance configuration
|
||||
@@ -18,24 +19,91 @@ A Terminal Emulator based on UWP and web technologies.
|
||||
- Configure shell profiles to quickly switch between different shells
|
||||
- Explorer context menu integration (Installation script can be found [here](https://github.com/felixse/FluentTerminal/tree/master/Explorer%20Context%20Menu%20Integration))
|
||||
|
||||
## Screenshots:
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||
## Up Next:
|
||||
## Up Next
|
||||
|
||||
- Launch shell profile with a custom defined keybinding
|
||||
- Copy&Paste options
|
||||
- Improved tabs
|
||||
- Split screen support
|
||||
|
||||
## How to install
|
||||
- [Enable sideloading apps ](https://www.windowscentral.com/how-enable-windows-10-sideload-apps-outside-store
|
||||
)
|
||||
## How to install (as an end-user)
|
||||
|
||||
### Prerequisite
|
||||
- You need to update to Fall Creators Update or later.
|
||||
|
||||
### Automatic install
|
||||
|
||||
- Download and extract the latest [release](https://github.com/felixse/FluentTerminal/releases).
|
||||
- If not already present, download [`Install.ps1`](Install.ps1) to the extracted directory.
|
||||
- Right-click on `Install.ps1`, and choose "Run with Powershell".
|
||||
- The installer script will walk you through the rest of the process.
|
||||
|
||||
### Manual install
|
||||
|
||||
- Download the latest [release](https://github.com/felixse/FluentTerminal/releases)
|
||||
- [Enable sideloading apps](https://www.windowscentral.com/how-enable-windows-10-sideload-apps-outside-store)
|
||||
- Alternatively, [enable developer mode](https://docs.microsoft.com/en-US/windows/uwp/get-started/enable-your-device-for-development) if you plan to do UWP app development. **For most users that do not intend to do UWP app development, developer mode will not be necessary.**
|
||||
- Install the *.cer file into `Local Machine` -> `Trusted Root Certification Authorities`
|
||||
- This will require administrator elevation. If you installed the certificate and did not have to elevate, you likely installed it into an incorrect certificate store.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- double click the *.appxbundle
|
||||
- **Optional:** Install Context menu integration from [here](https://github.com/felixse/FluentTerminal/tree/master/Explorer%20Context%20Menu%20Integration)
|
||||
|
||||
## How to build:
|
||||
Build the Client first, or whenever edited by running `npm run build` in FluentTerminal.Client
|
||||
Everything else is part of the solution.
|
||||
## How to set up a development environment
|
||||
|
||||
This is not for the faint of heart, and won't be necessary unless you intend to contribute back to the project.
|
||||
|
||||
**When using unattended install helpers, like Chocolatey, you may not have access to all of the necessary installation configuration options. For this it is recommended that you use the installers directly. This applies to both NodeJS and Visual Studio.**
|
||||
|
||||
- Grab the [latest NodeJS 8.x installer](https://nodejs.org/en/download/), and install ensuring that both NPM and "install into system PATH" are checked during the install process.
|
||||
- Grab the [latest Visual Studio 2017 Community installer](https://www.visualstudio.com/downloads/) and install (See next section)
|
||||
- [Enable developer mode in Windows 10](https://docs.microsoft.com/en-US/windows/uwp/get-started/enable-your-device-for-development)
|
||||
|
||||
### Installing Visual Studio 2017
|
||||
|
||||
There's some specific parts of VS2017 that we need to make sure are available when you go through the install process.
|
||||
|
||||
- Under "Workloads" select:
|
||||
- Universal Windows Platform development
|
||||
- Node.js development (Probably not required, given we installed Node.js separately)
|
||||
- Under individual components, you'll need to install the following:
|
||||
- Git for Windows
|
||||
- .NET Core runtime
|
||||
- .NET 4.7.1 Targeting Pack
|
||||
- Windows SDK 16299 for UWP: C#, VB, JS
|
||||
|
||||
An example screenshot from the main page of the VS2017 install process for a functioning development environment:
|
||||
|
||||

|
||||
|
||||
### The First Build
|
||||
|
||||
- Before opening the solution, open a Powershell terminal, go into the `FluentTerminal.Client` folder and run:
|
||||
- `npm install` (Ignore the warnings related to `fluent-terminal-client`)
|
||||
- `npm run build`
|
||||
- When the solution is first opened, it will help to set the architecture to x64 for testing, as we didn't install the ARM cross-compiling stuff for VS2017 and we only include pre-compiled winpty binaries for x86 and x64.
|
||||
- **For this reason, the default architecture of "Any CPU" will fail to build, and you will have to choose either x64 or x86**
|
||||
- When first built, Visual studio will spend a significant amount of time resolving dependencies, and fetching additional components via nuGet. This is normal.
|
||||
- If you have done the steps so far correctly, and the build is left as a debug build, then the solution should build and launch when you press F5.
|
||||
|
||||
### Subsequent Builds
|
||||
|
||||
- If you change the `FluentTerminal.Client` contents (**OR** if you merge or pull changes that resulted in changes to files in that folder),you'll need to re-run the `npm run build` (and `npm install` if you, or the pulled-in changes, change the dependencies).
|
||||
- The rest of the build process is as usual through Visual Studio.
|
||||
|
||||
### Packaging
|
||||
|
||||
- To start, copy the `Package.StoreAssociation.xml.dev` to `Package.StoreAssociation.xml`, this will provide a basic StoreAssociation file to use when building the app.
|
||||
- With the solution open in Visual Studio, you can build a package for your own use by right-clicking on the `FluentTerminal.App` project in the Solution Explorer and choosing `Store->Create App Packages`
|
||||
- Follow the prompts to create a package for sideloading, and if this is only for your personal use, it is OK to only build the x64 package, and not include the debug (i.e PDB) symbols.
|
||||
- Note that your commits with these settings (as well as a newly generated temporary store key) will involve changes to the `FluentTerminal.App.csproj` file, so be careful about that if you plan to submit them for PRs.
|
||||
- This will produce, if successful, a folder in `FluentTerminal.App\AppPackages` that you can zip up and share. You'll need to follow the same install instructions as when installing a release from the GitHub repo, including installing the certificate and then installing the appxpackage.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 201 KiB |
Reference in new issue
Block a user