Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6830adb788 | ||
|
|
02e015371c | ||
|
|
80bdfa012c | ||
|
|
70326e9585 | ||
|
|
c0e1415b6e | ||
|
|
13a3402b58 | ||
|
|
73703412f8 | ||
|
|
ab226d95d4 | ||
|
|
de7a76bb3b | ||
|
|
379f58b4b8 | ||
|
|
3a04cd3db2 | ||
|
|
df0d3f080b | ||
|
|
04131d271a | ||
|
|
d66fe26634 | ||
|
|
8870f0621c | ||
|
|
e5250a7ae8 | ||
|
|
b2b6cedf2c | ||
|
|
e777c2c5ac |
No files matched your search
@@ -1,6 +1,7 @@
|
||||
# Project specific
|
||||
FluentTerminal.App/Win32
|
||||
FluentTerminal.SystemTray/Client
|
||||
FluentTerminal.Client/dist
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
@@ -290,3 +291,4 @@ __pycache__/
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
FluentTerminal.Client/dist/bundle.js
|
||||
@@ -39,6 +39,7 @@ namespace FluentTerminal.App
|
||||
builder.RegisterType<DefaultValueProvider>().As<IDefaultValueProvider>().SingleInstance();
|
||||
builder.RegisterType<TerminalService>().As<ITerminalService>().SingleInstance();
|
||||
builder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
|
||||
builder.RegisterType<KeyboardCommandService>().As<IKeyboardCommandService>().InstancePerDependency();
|
||||
builder.RegisterType<MainViewModel>().InstancePerDependency();
|
||||
builder.RegisterType<SettingsViewModel>().InstancePerDependency();
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,40 @@
|
||||
using Microsoft.Xaml.Interactivity;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace FluentTerminal.App.Behaviors
|
||||
{
|
||||
public class MiddleClickBehavior : Trigger<UIElement>
|
||||
{
|
||||
private bool _middleButtonPressed;
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
AssociatedObject.PointerPressed += OnPointerPressed;
|
||||
AssociatedObject.PointerReleased += OnPointerReleased;
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
AssociatedObject.PointerPressed -= OnPointerPressed;
|
||||
AssociatedObject.PointerReleased -= OnPointerReleased;
|
||||
}
|
||||
|
||||
private void OnPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var point = e.GetCurrentPoint(AssociatedObject);
|
||||
_middleButtonPressed = point.Properties.IsMiddleButtonPressed;
|
||||
}
|
||||
|
||||
private void OnPointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var point = e.GetCurrentPoint(AssociatedObject);
|
||||
var middleButtonStillPressed = point.Properties.IsMiddleButtonPressed;
|
||||
|
||||
if (_middleButtonPressed && !middleButtonStillPressed)
|
||||
{
|
||||
Interaction.ExecuteActions(AssociatedObject, Actions, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace FluentTerminal.App.Converters
|
||||
{
|
||||
public class IntToVisibilityConverter : DependencyObject, IValueConverter
|
||||
{
|
||||
public static readonly DependencyProperty ElseProperty =
|
||||
DependencyProperty.Register(nameof(Else), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Collapsed));
|
||||
|
||||
public static readonly DependencyProperty IfProperty =
|
||||
DependencyProperty.Register(nameof(If), typeof(int), typeof(IntToVisibilityConverter), new PropertyMetadata(0));
|
||||
|
||||
public static readonly DependencyProperty ThenProperty =
|
||||
DependencyProperty.Register(nameof(Then), typeof(Visibility), typeof(IntToVisibilityConverter), new PropertyMetadata(Visibility.Visible));
|
||||
|
||||
public Visibility Else
|
||||
{
|
||||
get { return (Visibility)GetValue(ElseProperty); }
|
||||
set { SetValue(ElseProperty, value); }
|
||||
}
|
||||
|
||||
public int If
|
||||
{
|
||||
get { return (int)GetValue(IfProperty); }
|
||||
set { SetValue(IfProperty, value); }
|
||||
}
|
||||
|
||||
public Visibility Then
|
||||
{
|
||||
get { return (Visibility)GetValue(ThenProperty); }
|
||||
set { SetValue(ThenProperty, value); }
|
||||
}
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is int count)
|
||||
{
|
||||
return count == If ? Then : Else;
|
||||
}
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,12 +99,16 @@
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Behaviors\MiddleClickBehavior.cs" />
|
||||
<Compile Include="Converters\FalseToVisibleConverter.cs" />
|
||||
<Compile Include="Converters\IntToVisibilityConverter.cs" />
|
||||
<Compile Include="Converters\NegateConverter.cs" />
|
||||
<Compile Include="Converters\TerminalViewModelToViewConverter.cs" />
|
||||
<Compile Include="Converters\TrueToVisibleConverter.cs" />
|
||||
<Compile Include="Services\IDialogService.cs" />
|
||||
<Compile Include="Services\Implementation\DialogService.cs" />
|
||||
<Compile Include="Services\Implementation\KeyboardCommandService.cs" />
|
||||
<Compile Include="Services\IKeyboardCommandService.cs" />
|
||||
<Compile Include="Utilities\ApplicationDataContainerExtensions.cs" />
|
||||
<Compile Include="Services\IDefaultValueProvider.cs" />
|
||||
<Compile Include="Services\Implementation\DefaultValueProvider.cs" />
|
||||
@@ -154,6 +158,7 @@
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
<Content Include="Assets\settingsicons.ttf" />
|
||||
<None Include="FluentTerminal.App_StoreKey.pfx" />
|
||||
<None Include="FluentTerminal.App_TemporaryKey.pfx" />
|
||||
<Content Include="Win32\FluentTerminal.Models.dll" />
|
||||
@@ -217,6 +222,7 @@
|
||||
<Content Include="Win32\Fleck.dll" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.exe" />
|
||||
<Content Include="Win32\FluentTerminal.SystemTray.pdb" />
|
||||
<Content Include="Win32\GlobalHotKey.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.FileSystems.dll" />
|
||||
<Content Include="Win32\Microsoft.Owin.Host.HttpListener.dll" />
|
||||
@@ -403,6 +409,9 @@
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
<Version>2.1.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed">
|
||||
<Version>2.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MvvmLightLibs">
|
||||
<Version>5.3.0</Version>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" IgnorableNamespaces="uap mp desktop rescap">
|
||||
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.4.0" />
|
||||
<Identity Name="53621FSApps.FluentTerminal" Publisher="CN=BDC72197-B634-438A-B863-16DCB7D90C85" Version="0.1.6.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="f7225014-f405-4342-ad2e-ebf6ac60ea2c" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>Fluent Terminal</DisplayName>
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace FluentTerminal.App.Services
|
||||
{
|
||||
ShellConfiguration GetDefaultShellConfiguration();
|
||||
TerminalOptions GetDefaultTerminalOptions();
|
||||
IEnumerable<KeyBinding> GetDefaultKeyBindings();
|
||||
ApplicationSettings GetDefaultApplicationSettings();
|
||||
Guid GetDefaultThemeId();
|
||||
IEnumerable<TerminalTheme> GetPreInstalledThemes();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
|
||||
namespace FluentTerminal.App.Services
|
||||
{
|
||||
public interface IKeyboardCommandService
|
||||
{
|
||||
void RegisterCommandHandler(Command command, Action handler);
|
||||
void SendCommand(Command command);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ namespace FluentTerminal.App.Services
|
||||
{
|
||||
event EventHandler CurrentThemeChanged;
|
||||
event EventHandler TerminalOptionsChanged;
|
||||
event EventHandler ApplicationSettingsChanged;
|
||||
|
||||
ShellConfiguration GetShellConfiguration();
|
||||
void SaveShellConfiguration(ShellConfiguration shellConfiguration);
|
||||
@@ -15,6 +16,11 @@ namespace FluentTerminal.App.Services
|
||||
TerminalOptions GetTerminalOptions();
|
||||
void SaveTerminalOptions(TerminalOptions terminalOptions);
|
||||
|
||||
ApplicationSettings GetApplicationSettings();
|
||||
void SaveApplicationSettings(ApplicationSettings applicationSettings);
|
||||
|
||||
IEnumerable<KeyBinding> GetKeyBindings();
|
||||
|
||||
TerminalTheme GetCurrentTheme();
|
||||
Guid GetCurrentThemeId();
|
||||
TerminalTheme GetTheme(Guid id);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.System;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
@@ -105,5 +106,93 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
|
||||
return new[] { defaultXterm, powerShell };
|
||||
}
|
||||
|
||||
public IEnumerable<KeyBinding> GetDefaultKeyBindings()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NewTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = false,
|
||||
Key = (int)VirtualKey.T
|
||||
},
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.CloseTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = false,
|
||||
Key = (int)VirtualKey.W
|
||||
},
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NextTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = false,
|
||||
Key = (int)VirtualKey.Tab
|
||||
},
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.PreviousTab,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = true,
|
||||
Key = (int)VirtualKey.Tab
|
||||
},
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.NewWindow,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = false,
|
||||
Key = (int)VirtualKey.N
|
||||
},
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.ShowSettings,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = false,
|
||||
Key = 188 // comma
|
||||
},
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Copy,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = true,
|
||||
Key = (int)VirtualKey.C
|
||||
},
|
||||
new KeyBinding
|
||||
{
|
||||
Command = Command.Paste,
|
||||
Ctrl = true,
|
||||
Alt = false,
|
||||
Meta = false,
|
||||
Shift = false,
|
||||
Key = (int)VirtualKey.V
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public ApplicationSettings GetDefaultApplicationSettings()
|
||||
{
|
||||
return new ApplicationSettings
|
||||
{
|
||||
ConfirmClosingTabs = false,
|
||||
ConfirmClosingWindows = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentTerminal.Models.Enums;
|
||||
|
||||
namespace FluentTerminal.App.Services.Implementation
|
||||
{
|
||||
public class KeyboardCommandService : IKeyboardCommandService
|
||||
{
|
||||
private Dictionary<Command, Action> _commandHandlers = new Dictionary<Command, Action>();
|
||||
|
||||
public void RegisterCommandHandler(Command command, Action handler)
|
||||
{
|
||||
_commandHandlers[command] = handler;
|
||||
}
|
||||
|
||||
public void SendCommand(Command command)
|
||||
{
|
||||
if (_commandHandlers.TryGetValue(command, out Action handler))
|
||||
{
|
||||
handler.Invoke();
|
||||
return;
|
||||
}
|
||||
throw new Exception($"No handler registered for command: {command}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
|
||||
public event EventHandler CurrentThemeChanged;
|
||||
public event EventHandler TerminalOptionsChanged;
|
||||
public event EventHandler ApplicationSettingsChanged;
|
||||
|
||||
public SettingsService(IDefaultValueProvider defaultValueProvider)
|
||||
{
|
||||
@@ -98,13 +99,29 @@ namespace FluentTerminal.App.Services.Implementation
|
||||
|
||||
public TerminalOptions GetTerminalOptions()
|
||||
{
|
||||
return _localSettings.ReadValueFromJson(nameof(TerminalOptions), _defaultValueProvider.GetDefaultTerminalOptions());
|
||||
return _roamingSettings.ReadValueFromJson(nameof(TerminalOptions), _defaultValueProvider.GetDefaultTerminalOptions());
|
||||
}
|
||||
|
||||
public void SaveTerminalOptions(TerminalOptions terminalOptions)
|
||||
{
|
||||
_localSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
||||
_roamingSettings.WriteValueAsJson(nameof(TerminalOptions), terminalOptions);
|
||||
TerminalOptionsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public ApplicationSettings GetApplicationSettings()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson(nameof(ApplicationSettings), _defaultValueProvider.GetDefaultApplicationSettings());
|
||||
}
|
||||
|
||||
public void SaveApplicationSettings(ApplicationSettings applicationSettings)
|
||||
{
|
||||
_roamingSettings.WriteValueAsJson(nameof(ApplicationSettings), applicationSettings);
|
||||
ApplicationSettingsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public IEnumerable<KeyBinding> GetKeyBindings()
|
||||
{
|
||||
return _roamingSettings.ReadValueFromJson("KeyBindings", _defaultValueProvider.GetDefaultKeyBindings());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:input="using:Telerik.UI.Xaml.Controls.Input">
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<Style
|
||||
x:Key="WindowTitleTextBlockStyle"
|
||||
|
||||
@@ -1,96 +1,153 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
|
||||
namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class MainViewModel : ViewModelBase
|
||||
{
|
||||
private const string FallbackTitle = "Fluent Terminal";
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IKeyboardCommandService _keyboardCommandService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITerminalService _terminalService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private ApplicationSettings _applicationSettings;
|
||||
private string _background;
|
||||
private double _backgroundOpacity;
|
||||
private CoreDispatcher _dispatcher;
|
||||
private int _nextTerminalId;
|
||||
private TerminalViewModel _selectedTerminal;
|
||||
private string _title;
|
||||
|
||||
public MainViewModel(ISettingsService settingsService, ITerminalService terminalService, IDialogService dialogService)
|
||||
public MainViewModel(ISettingsService settingsService, ITerminalService terminalService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
|
||||
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
_terminalService = terminalService;
|
||||
_dialogService = dialogService;
|
||||
Title = FallbackTitle;
|
||||
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NewTab, () => AddTerminal(null));
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.CloseTab, CloseCurrentTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NextTab, SelectNextTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.PreviousTab, SelectPreviousTab);
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.NewWindow, async () => await NewWindow());
|
||||
_keyboardCommandService.RegisterCommandHandler(Command.ShowSettings, async () => await ShowSettings());
|
||||
var currentTheme = _settingsService.GetCurrentTheme();
|
||||
Background = currentTheme.Colors.Background;
|
||||
BackgroundOpacity = currentTheme.BackgroundOpacity;
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
|
||||
AddTerminalCommand = new RelayCommand(() => AddTerminal(null));
|
||||
ShowSettingsCommand = new RelayCommand(async () => await ShowSettings());
|
||||
|
||||
_dispatcher = CoreApplication.GetCurrentView().Dispatcher;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
|
||||
}
|
||||
|
||||
public RelayCommand AddTerminalCommand { get; }
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _backgroundOpacity;
|
||||
set => Set(ref _backgroundOpacity, value);
|
||||
}
|
||||
|
||||
public string Background
|
||||
{
|
||||
get => _background;
|
||||
set => Set(ref _background, value);
|
||||
}
|
||||
|
||||
public double BackgroundOpacity
|
||||
{
|
||||
get => _backgroundOpacity;
|
||||
set => Set(ref _backgroundOpacity, value);
|
||||
}
|
||||
|
||||
public TerminalViewModel SelectedTerminal
|
||||
{
|
||||
get => _selectedTerminal;
|
||||
set => Set(ref _selectedTerminal, value);
|
||||
set
|
||||
{
|
||||
if (Set(ref _selectedTerminal, value))
|
||||
{
|
||||
SelectedTerminal?.FocusTerminal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand ShowSettingsCommand { get; }
|
||||
|
||||
public ObservableCollection<TerminalViewModel> Terminals { get; } = new ObservableCollection<TerminalViewModel>();
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
}
|
||||
|
||||
public void AddTerminal(string startupDirectory)
|
||||
{
|
||||
var terminal = new TerminalViewModel(_settingsService, _terminalService, _dialogService, startupDirectory);
|
||||
terminal.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(TerminalViewModel.Title) && Terminals.Count == 1)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(terminal.Title))
|
||||
{
|
||||
Title = FallbackTitle;
|
||||
}
|
||||
else
|
||||
{
|
||||
Title = terminal.Title;
|
||||
}
|
||||
}
|
||||
};
|
||||
var terminal = new TerminalViewModel(GetNextTerminalId(), _settingsService, _terminalService, _dialogService, _keyboardCommandService, _applicationSettings, startupDirectory);
|
||||
terminal.CloseRequested += OnTerminalCloseRequested;
|
||||
Terminals.Add(terminal);
|
||||
|
||||
SelectedTerminal = terminal;
|
||||
}
|
||||
|
||||
public void CloseAllTerminals()
|
||||
{
|
||||
foreach (var terminal in Terminals)
|
||||
{
|
||||
terminal.CloseView();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseCurrentTab()
|
||||
{
|
||||
SelectedTerminal?.CloseCommand.Execute(null);
|
||||
}
|
||||
|
||||
private int GetNextTerminalId()
|
||||
{
|
||||
return _nextTerminalId++;
|
||||
}
|
||||
|
||||
private Task NewWindow()
|
||||
{
|
||||
return App.Instance.CreateNewTerminalWindow(null);
|
||||
}
|
||||
|
||||
private async void OnApplicationSettingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
|
||||
{
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
var deferral = e.GetDeferral();
|
||||
|
||||
if (_applicationSettings.ConfirmClosingWindows)
|
||||
{
|
||||
var result = await _dialogService.ShowDialogAsnyc("Please confirm", "Are you sure you want to close this window?", DialogButton.OK, DialogButton.Cancel);
|
||||
|
||||
if (result == DialogButton.OK)
|
||||
{
|
||||
CloseAllTerminals();
|
||||
App.Instance.TerminalWindowClosed();
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseAllTerminals();
|
||||
App.Instance.TerminalWindowClosed();
|
||||
}
|
||||
deferral.Complete();
|
||||
}
|
||||
|
||||
private async void OnCurrentThemeChanged(object sender, System.EventArgs e)
|
||||
{
|
||||
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
|
||||
@@ -101,6 +158,38 @@ namespace FluentTerminal.App.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
private void OnTerminalCloseRequested(object sender, EventArgs e)
|
||||
{
|
||||
if (Terminals.Count == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender is TerminalViewModel terminal)
|
||||
{
|
||||
terminal.CloseView();
|
||||
if (SelectedTerminal == terminal)
|
||||
{
|
||||
SelectedTerminal = Terminals.LastOrDefault(t => t != terminal);
|
||||
}
|
||||
Terminals.Remove(terminal);
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectNextTab()
|
||||
{
|
||||
var currentIndex = Terminals.IndexOf(SelectedTerminal);
|
||||
var nextIndex = (currentIndex + 1) % Terminals.Count;
|
||||
SelectedTerminal = Terminals[nextIndex];
|
||||
}
|
||||
|
||||
private void SelectPreviousTab()
|
||||
{
|
||||
var currentIndex = Terminals.IndexOf(SelectedTerminal);
|
||||
var previousIndex = (currentIndex - 1 + Terminals.Count) % Terminals.Count;
|
||||
SelectedTerminal = Terminals[previousIndex];
|
||||
}
|
||||
|
||||
private Task ShowSettings()
|
||||
{
|
||||
return App.Instance.ShowSettings();
|
||||
|
||||
@@ -15,12 +15,13 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class SettingsViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IDefaultValueProvider _defaultValueProvider;
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private ApplicationSettings _applicationSettings;
|
||||
private ThemeViewModel _selectedTheme;
|
||||
private ShellConfiguration _shellConfiguration;
|
||||
private TerminalOptions _terminalOptions;
|
||||
private ThemeViewModel _selectedTheme;
|
||||
|
||||
public SettingsViewModel(ISettingsService settingsService, IDefaultValueProvider defaultValueProvider, IDialogService dialogService)
|
||||
{
|
||||
@@ -34,6 +35,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
|
||||
_shellConfiguration = _settingsService.GetShellConfiguration();
|
||||
_terminalOptions = _settingsService.GetTerminalOptions();
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
ShellType = _shellConfiguration.Shell;
|
||||
|
||||
var activeThemeId = _settingsService.GetCurrentThemeId();
|
||||
@@ -57,94 +59,72 @@ namespace FluentTerminal.App.ViewModels
|
||||
Sizes = Enumerable.Range(1, 72);
|
||||
}
|
||||
|
||||
private void OnThemeActivated(object sender, EventArgs e)
|
||||
public string Arguments
|
||||
{
|
||||
if (sender is ThemeViewModel activatedTheme)
|
||||
get => _shellConfiguration.Arguments;
|
||||
set
|
||||
{
|
||||
_settingsService.SaveCurrentThemeId(activatedTheme.Id);
|
||||
|
||||
foreach (var theme in Themes)
|
||||
if (_shellConfiguration.Arguments != value)
|
||||
{
|
||||
theme.IsActive = theme.Id == activatedTheme.Id;
|
||||
_shellConfiguration.Arguments = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTheme()
|
||||
public bool BarIsSelected
|
||||
{
|
||||
var defaultTheme = _settingsService.GetTheme(_defaultValueProvider.GetDefaultThemeId());
|
||||
var theme = new TerminalTheme
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = "New Theme",
|
||||
BackgroundOpacity = defaultTheme.BackgroundOpacity,
|
||||
Colors = new TerminalColors(defaultTheme.Colors)
|
||||
};
|
||||
|
||||
_settingsService.SaveTheme(theme);
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
viewModel.Deleted += OnThemeDeleted;
|
||||
Themes.Add(viewModel);
|
||||
SelectedTheme = viewModel;
|
||||
get => CursorStyle == CursorStyle.Bar;
|
||||
set => CursorStyle = CursorStyle.Bar;
|
||||
}
|
||||
|
||||
private void OnThemeDeleted(object sender, EventArgs e)
|
||||
public bool BlockIsSelected
|
||||
{
|
||||
if (sender is ThemeViewModel theme)
|
||||
{
|
||||
if (SelectedTheme == theme)
|
||||
{
|
||||
SelectedTheme = Themes.First();
|
||||
}
|
||||
Themes.Remove(theme);
|
||||
|
||||
if (theme.IsActive)
|
||||
{
|
||||
Themes.First().IsActive = true;
|
||||
_settingsService.SaveCurrentThemeId(Themes.First().Id);
|
||||
}
|
||||
_settingsService.DeleteTheme(theme.Id);
|
||||
|
||||
}
|
||||
get => CursorStyle == CursorStyle.Block;
|
||||
set => CursorStyle = CursorStyle.Block;
|
||||
}
|
||||
|
||||
public RelayCommand BrowseForCustomShellCommand { get; }
|
||||
|
||||
public RelayCommand BrowseForWorkingDirectoryCommand { get; }
|
||||
public RelayCommand<string> RestoreDefaultsCommand { get; }
|
||||
public RelayCommand CreateThemeCommand { get; set; }
|
||||
|
||||
public ObservableCollection<ThemeViewModel> Themes { get; } = new ObservableCollection<ThemeViewModel>();
|
||||
|
||||
public IEnumerable<string> Fonts { get; }
|
||||
|
||||
public IEnumerable<int> Sizes { get; }
|
||||
|
||||
public ThemeViewModel SelectedTheme
|
||||
public bool CMDIsSelected
|
||||
{
|
||||
get => _selectedTheme;
|
||||
set => Set(ref _selectedTheme, value);
|
||||
get => ShellType == ShellType.CMD;
|
||||
set => ShellType = ShellType.CMD;
|
||||
}
|
||||
|
||||
private CursorStyle CursorStyle
|
||||
public bool ConfirmClosingTabs
|
||||
{
|
||||
get => _terminalOptions.CursorStyle;
|
||||
get => _applicationSettings.ConfirmClosingTabs;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.CursorStyle != value)
|
||||
if (_applicationSettings.ConfirmClosingTabs != value)
|
||||
{
|
||||
_terminalOptions.CursorStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
_applicationSettings.ConfirmClosingTabs = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(BlockIsSelected));
|
||||
RaisePropertyChanged(nameof(BarIsSelected));
|
||||
RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ConfirmClosingWindows
|
||||
{
|
||||
get => _applicationSettings.ConfirmClosingWindows;
|
||||
set
|
||||
{
|
||||
if (_applicationSettings.ConfirmClosingWindows != value)
|
||||
{
|
||||
_applicationSettings.ConfirmClosingWindows = value;
|
||||
_settingsService.SaveApplicationSettings(_applicationSettings);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RelayCommand CreateThemeCommand { get; set; }
|
||||
|
||||
public bool CursorBlink
|
||||
{
|
||||
get => _terminalOptions.CursorBlink;
|
||||
@@ -159,58 +139,6 @@ namespace FluentTerminal.App.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public int FontSize
|
||||
{
|
||||
get => _terminalOptions.FontSize;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.FontSize != value)
|
||||
{
|
||||
_terminalOptions.FontSize = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string FontFamily
|
||||
{
|
||||
get => _terminalOptions.FontFamily;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.FontFamily != value)
|
||||
{
|
||||
_terminalOptions.FontFamily = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool BlockIsSelected
|
||||
{
|
||||
get => CursorStyle == CursorStyle.Block;
|
||||
set => CursorStyle = CursorStyle.Block;
|
||||
}
|
||||
|
||||
public bool BarIsSelected
|
||||
{
|
||||
get => CursorStyle == CursorStyle.Bar;
|
||||
set => CursorStyle = CursorStyle.Bar;
|
||||
}
|
||||
|
||||
public bool UnderlineIsSelected
|
||||
{
|
||||
get => CursorStyle == CursorStyle.Underline;
|
||||
set => CursorStyle = CursorStyle.Underline;
|
||||
}
|
||||
|
||||
public bool CMDIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.CMD;
|
||||
set => ShellType = ShellType.CMD;
|
||||
}
|
||||
|
||||
public bool CustomShellIsSelected
|
||||
{
|
||||
get => ShellType == ShellType.Custom;
|
||||
@@ -231,29 +159,31 @@ namespace FluentTerminal.App.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public string WorkingDirectory
|
||||
public string FontFamily
|
||||
{
|
||||
get => _shellConfiguration.WorkingDirectory;
|
||||
get => _terminalOptions.FontFamily;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.WorkingDirectory != value)
|
||||
if (_terminalOptions.FontFamily != value)
|
||||
{
|
||||
_shellConfiguration.WorkingDirectory = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
_terminalOptions.FontFamily = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Arguments
|
||||
public IEnumerable<string> Fonts { get; }
|
||||
|
||||
public int FontSize
|
||||
{
|
||||
get => _shellConfiguration.Arguments;
|
||||
get => _terminalOptions.FontSize;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.Arguments != value)
|
||||
if (_terminalOptions.FontSize != value)
|
||||
{
|
||||
_shellConfiguration.Arguments = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
_terminalOptions.FontSize = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
@@ -265,6 +195,14 @@ namespace FluentTerminal.App.ViewModels
|
||||
set => ShellType = ShellType.PowerShell;
|
||||
}
|
||||
|
||||
public RelayCommand<string> RestoreDefaultsCommand { get; }
|
||||
|
||||
public ThemeViewModel SelectedTheme
|
||||
{
|
||||
get => _selectedTheme;
|
||||
set => Set(ref _selectedTheme, value);
|
||||
}
|
||||
|
||||
public ShellType ShellType
|
||||
{
|
||||
get => _shellConfiguration.Shell;
|
||||
@@ -282,6 +220,47 @@ namespace FluentTerminal.App.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<int> Sizes { get; }
|
||||
|
||||
public ObservableCollection<ThemeViewModel> Themes { get; } = new ObservableCollection<ThemeViewModel>();
|
||||
|
||||
public bool UnderlineIsSelected
|
||||
{
|
||||
get => CursorStyle == CursorStyle.Underline;
|
||||
set => CursorStyle = CursorStyle.Underline;
|
||||
}
|
||||
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get => _shellConfiguration.WorkingDirectory;
|
||||
set
|
||||
{
|
||||
if (_shellConfiguration.WorkingDirectory != value)
|
||||
{
|
||||
_shellConfiguration.WorkingDirectory = value;
|
||||
_settingsService.SaveShellConfiguration(_shellConfiguration);
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CursorStyle CursorStyle
|
||||
{
|
||||
get => _terminalOptions.CursorStyle;
|
||||
set
|
||||
{
|
||||
if (_terminalOptions.CursorStyle != value)
|
||||
{
|
||||
_terminalOptions.CursorStyle = value;
|
||||
_settingsService.SaveTerminalOptions(_terminalOptions);
|
||||
RaisePropertyChanged();
|
||||
RaisePropertyChanged(nameof(BlockIsSelected));
|
||||
RaisePropertyChanged(nameof(BarIsSelected));
|
||||
RaisePropertyChanged(nameof(UnderlineIsSelected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BrowseForCustomShell()
|
||||
{
|
||||
var picker = new FileOpenPicker();
|
||||
@@ -308,6 +287,59 @@ namespace FluentTerminal.App.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTheme()
|
||||
{
|
||||
var defaultTheme = _settingsService.GetTheme(_defaultValueProvider.GetDefaultThemeId());
|
||||
var theme = new TerminalTheme
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
PreInstalled = false,
|
||||
Name = "New Theme",
|
||||
BackgroundOpacity = defaultTheme.BackgroundOpacity,
|
||||
Colors = new TerminalColors(defaultTheme.Colors)
|
||||
};
|
||||
|
||||
_settingsService.SaveTheme(theme);
|
||||
|
||||
var viewModel = new ThemeViewModel(theme, _settingsService, _dialogService);
|
||||
viewModel.Activated += OnThemeActivated;
|
||||
viewModel.Deleted += OnThemeDeleted;
|
||||
Themes.Add(viewModel);
|
||||
SelectedTheme = viewModel;
|
||||
}
|
||||
|
||||
private void OnThemeActivated(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is ThemeViewModel activatedTheme)
|
||||
{
|
||||
_settingsService.SaveCurrentThemeId(activatedTheme.Id);
|
||||
|
||||
foreach (var theme in Themes)
|
||||
{
|
||||
theme.IsActive = theme.Id == activatedTheme.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThemeDeleted(object sender, EventArgs e)
|
||||
{
|
||||
if (sender is ThemeViewModel theme)
|
||||
{
|
||||
if (SelectedTheme == theme)
|
||||
{
|
||||
SelectedTheme = Themes.First();
|
||||
}
|
||||
Themes.Remove(theme);
|
||||
|
||||
if (theme.IsActive)
|
||||
{
|
||||
Themes.First().IsActive = true;
|
||||
_settingsService.SaveCurrentThemeId(Themes.First().Id);
|
||||
}
|
||||
_settingsService.DeleteTheme(theme.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestoreDefaults(string area)
|
||||
{
|
||||
var result = await _dialogService.ShowDialogAsnyc("Please confirm", "Are you sure you want to restore default values for this page?", DialogButton.OK, DialogButton.Cancel);
|
||||
@@ -320,7 +352,7 @@ namespace FluentTerminal.App.ViewModels
|
||||
WorkingDirectory = defaults.WorkingDirectory;
|
||||
Arguments = defaults.Arguments;
|
||||
}
|
||||
else if(result == DialogButton.OK && area == "terminal")
|
||||
else if (result == DialogButton.OK && area == "terminal")
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultTerminalOptions();
|
||||
CursorBlink = defaults.CursorBlink;
|
||||
@@ -328,6 +360,12 @@ namespace FluentTerminal.App.ViewModels
|
||||
FontFamily = defaults.FontFamily;
|
||||
FontSize = defaults.FontSize;
|
||||
}
|
||||
else if (result == DialogButton.OK && area == "general")
|
||||
{
|
||||
var defaults = _defaultValueProvider.GetDefaultApplicationSettings();
|
||||
ConfirmClosingWindows = defaults.ConfirmClosingWindows;
|
||||
ConfirmClosingTabs = defaults.ConfirmClosingTabs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
using FluentTerminal.App.Services;
|
||||
using FluentTerminal.App.Views;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using GalaSoft.MvvmLight;
|
||||
using GalaSoft.MvvmLight.Command;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
@@ -12,9 +15,11 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
public class TerminalViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly IKeyboardCommandService _keyboardCommandService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ITerminalService _terminalService;
|
||||
private readonly IDialogService _dialogService;
|
||||
private ApplicationSettings _applicationSettings;
|
||||
private CoreDispatcher _dispatcher;
|
||||
private string _resizeOverlayContent;
|
||||
private DispatcherTimer _resizeOverlayTimer;
|
||||
@@ -24,25 +29,41 @@ namespace FluentTerminal.App.ViewModels
|
||||
private ITerminalView _terminalView;
|
||||
private string _title;
|
||||
|
||||
public TerminalViewModel(ISettingsService settingsService, ITerminalService terminalService, IDialogService dialogService, string startupDirectory)
|
||||
public TerminalViewModel(int id, ISettingsService settingsService, ITerminalService terminalService, IDialogService dialogService, IKeyboardCommandService keyboardCommandService, ApplicationSettings applicationSettings, string startupDirectory)
|
||||
{
|
||||
Id = id;
|
||||
Title = DefaultTitle;
|
||||
|
||||
_settingsService = settingsService;
|
||||
_settingsService.CurrentThemeChanged += OnCurrentThemeChanged;
|
||||
_settingsService.TerminalOptionsChanged += OnTerminalOptionsChanged;
|
||||
_settingsService.ApplicationSettingsChanged += OnApplicationSettingsChanged;
|
||||
_terminalService = terminalService;
|
||||
_dialogService = dialogService;
|
||||
_keyboardCommandService = keyboardCommandService;
|
||||
_applicationSettings = applicationSettings;
|
||||
_startupDirectory = startupDirectory;
|
||||
_resizeOverlayTimer = new DispatcherTimer();
|
||||
_resizeOverlayTimer.Interval = new TimeSpan(0, 0, 2);
|
||||
_resizeOverlayTimer.Tick += OnResizeOverlayTimerFinished;
|
||||
|
||||
CloseCommand = new RelayCommand(async () => await InvokeCloseRequested());
|
||||
|
||||
_dispatcher = CoreApplication.GetCurrentView().Dispatcher;
|
||||
}
|
||||
|
||||
public event EventHandler CloseRequested;
|
||||
|
||||
public event EventHandler<string> TitleChanged;
|
||||
|
||||
public RelayCommand CloseCommand { get; }
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
public string DefaultTitle { get; private set; } = "Fluent Terminal";
|
||||
|
||||
public string ResizeOverlayContent
|
||||
{
|
||||
get => _resizeOverlayContent;
|
||||
@@ -69,7 +90,25 @@ namespace FluentTerminal.App.ViewModels
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
set
|
||||
{
|
||||
value = string.IsNullOrWhiteSpace(value) ? DefaultTitle: value;
|
||||
|
||||
if (Set(ref _title, value))
|
||||
{
|
||||
TitleChanged?.Invoke(this, Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseView()
|
||||
{
|
||||
_terminalView.Close();
|
||||
}
|
||||
|
||||
public Task FocusTerminal()
|
||||
{
|
||||
return _terminalView?.FocusTerminal();
|
||||
}
|
||||
|
||||
public async Task OnViewIsReady(ITerminalView terminalView)
|
||||
@@ -78,8 +117,9 @@ namespace FluentTerminal.App.ViewModels
|
||||
|
||||
var options = _settingsService.GetTerminalOptions();
|
||||
var theme = _settingsService.GetCurrentTheme();
|
||||
var keyBindings = _settingsService.GetKeyBindings();
|
||||
|
||||
var size = await _terminalView.CreateTerminal(options, theme.Colors);
|
||||
var size = await _terminalView.CreateTerminal(options, theme.Colors, keyBindings);
|
||||
var configuration = _settingsService.GetShellConfiguration();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_startupDirectory))
|
||||
@@ -94,6 +134,10 @@ namespace FluentTerminal.App.ViewModels
|
||||
_terminalId = response.Id;
|
||||
_terminalView.TerminalSizeChanged += OnTerminalSizeChanged;
|
||||
_terminalView.TerminalTitleChanged += OnTerminalTitleChanged;
|
||||
_terminalView.KeyboardCommandReceived += OnKeyboardCommandReceived;
|
||||
|
||||
DefaultTitle = response.ShellExecutableName;
|
||||
Title = DefaultTitle;
|
||||
|
||||
await _terminalView.ConnectToSocket(response.WebSocketUrl);
|
||||
Initialized = true;
|
||||
@@ -102,6 +146,31 @@ namespace FluentTerminal.App.ViewModels
|
||||
{
|
||||
await _dialogService.ShowDialogAsnyc("Error", response.Error, DialogButton.OK);
|
||||
}
|
||||
|
||||
await FocusTerminal();
|
||||
}
|
||||
|
||||
private async Task InvokeCloseRequested()
|
||||
{
|
||||
if (_applicationSettings.ConfirmClosingTabs)
|
||||
{
|
||||
var result = await _dialogService.ShowDialogAsnyc("Please confirm", "Are you sure you want to close this tab?", DialogButton.OK, DialogButton.Cancel);
|
||||
|
||||
if (result == DialogButton.Cancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CloseRequested?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async void OnApplicationSettingsChanged(object sender, EventArgs e)
|
||||
{
|
||||
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
|
||||
{
|
||||
_applicationSettings = _settingsService.GetApplicationSettings();
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnCurrentThemeChanged(object sender, EventArgs e)
|
||||
@@ -113,6 +182,36 @@ namespace FluentTerminal.App.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnKeyboardCommandReceived(object sender, Command e)
|
||||
{
|
||||
if (e == Command.Copy)
|
||||
{
|
||||
var selection = await _terminalView.GetSelection();
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(selection);
|
||||
Clipboard.SetContent(dataPackage);
|
||||
}
|
||||
else if (e == Command.Paste)
|
||||
{
|
||||
var content = Clipboard.GetContent();
|
||||
if (content.Contains(StandardDataFormats.Text))
|
||||
{
|
||||
var text = await content.GetTextAsync();
|
||||
await _terminalView.Write(text);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_keyboardCommandService.SendCommand(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnResizeOverlayTimerFinished(object sender, object e)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
ShowResizeOverlay = false;
|
||||
}
|
||||
|
||||
private async void OnTerminalOptionsChanged(object sender, EventArgs e)
|
||||
{
|
||||
await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
|
||||
@@ -122,12 +221,6 @@ namespace FluentTerminal.App.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
private void OnResizeOverlayTimerFinished(object sender, object e)
|
||||
{
|
||||
_resizeOverlayTimer.Stop();
|
||||
ShowResizeOverlay = false;
|
||||
}
|
||||
|
||||
private async void OnTerminalSizeChanged(object sender, TerminalSize e)
|
||||
{
|
||||
if (!Initialized)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.App.Views
|
||||
@@ -8,10 +10,15 @@ namespace FluentTerminal.App.Views
|
||||
{
|
||||
event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
event EventHandler<string> TerminalTitleChanged;
|
||||
event EventHandler<Command> KeyboardCommandReceived;
|
||||
|
||||
Task ChangeTheme(TerminalColors theme);
|
||||
Task ChangeOptions(TerminalOptions options);
|
||||
Task<TerminalSize> CreateTerminal(TerminalOptions options, TerminalColors theme);
|
||||
Task<TerminalSize> CreateTerminal(TerminalOptions options, TerminalColors theme, IEnumerable<KeyBinding> keyBindings);
|
||||
Task ConnectToSocket(string url);
|
||||
void Close();
|
||||
Task FocusTerminal();
|
||||
Task<string> GetSelection();
|
||||
Task Write(string text);
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,27 @@
|
||||
x:Class="FluentTerminal.App.Views.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:behaviors="using:FluentTerminal.App.Behaviors"
|
||||
xmlns:converters="using:FluentTerminal.App.Converters"
|
||||
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewmodels="using:FluentTerminal.App.ViewModels"
|
||||
RequestedTheme="Dark"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:TerminalViewModelToViewConverter x:Key="TerminalViewModelToViewConverter" />
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="OneToVisibleConverter"
|
||||
Else="Collapsed"
|
||||
If="1"
|
||||
Then="Visible" />
|
||||
<converters:IntToVisibilityConverter
|
||||
x:Key="OneToCollapsedConverter"
|
||||
Else="Visible"
|
||||
If="1"
|
||||
Then="Collapsed" />
|
||||
</Page.Resources>
|
||||
<Grid x:Name="Root">
|
||||
<Grid.Background>
|
||||
@@ -22,34 +36,106 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Rectangle x:Name="TitleBar" Fill="Transparent" />
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Height="{Binding CoreTitleBarHeight, Mode=OneWay}"
|
||||
Canvas.ZIndex="3">
|
||||
<Rectangle x:Name="TitleBar" Fill="Transparent" />
|
||||
<Grid Padding="{Binding CoreTitleBarPadding, Mode=OneWay}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Canvas.ZIndex="9"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource WindowTitleTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.Title, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Command="{x:Bind ViewModel.ShowSettingsCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<Viewbox Width="16" Height="16">
|
||||
<SymbolIcon Symbol="Setting" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
Margin="{Binding CoreTitleBarPadding, Mode=OneWay}"
|
||||
SizeChanged="Grid_SizeChanged">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
IsHitTestVisible="False"
|
||||
Style="{StaticResource WindowTitleTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.Terminals[0].Title, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.Terminals.Count, Mode=OneWay, Converter={StaticResource OneToVisibleConverter}}" />
|
||||
<ListView
|
||||
x:Name="Tabs"
|
||||
Grid.Column="0"
|
||||
AllowDrop="True"
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
ItemsSource="{x:Bind ViewModel.Terminals}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollMode="Auto"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedTerminal, Mode=TwoWay}"
|
||||
Visibility="{x:Bind ViewModel.Terminals.Count, Mode=OneWay, Converter={StaticResource OneToCollapsedConverter}}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewmodels:TerminalViewModel">
|
||||
<Grid
|
||||
Height="32"
|
||||
Margin="0"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<behaviors:MiddleClickBehavior>
|
||||
<behaviors:MiddleClickBehavior.Actions>
|
||||
<core:InvokeCommandAction Command="{x:Bind CloseCommand}" />
|
||||
</behaviors:MiddleClickBehavior.Actions>
|
||||
</behaviors:MiddleClickBehavior>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="12,8,0,0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Title, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Width="32"
|
||||
Command="{x:Bind CloseCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="Close">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Cancel" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="MinWidth" Value="24" />
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
</ListView>
|
||||
<StackPanel
|
||||
x:Name="TitleBarButtons"
|
||||
Grid.Column="2"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.AddTerminalCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="New Tab">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Add" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
<Button
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.ShowSettingsCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
ToolTipService.ToolTip="Settings">
|
||||
<Viewbox Width="12" Height="12">
|
||||
<SymbolIcon Symbol="Setting" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<ContentControl
|
||||
x:Name="TerminalContainer"
|
||||
|
||||
@@ -45,7 +45,6 @@ namespace FluentTerminal.App.Views
|
||||
Loaded += OnLoaded;
|
||||
Unloaded += OnUnloaded;
|
||||
Window.Current.Activated += OnWindowActivated;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequest;
|
||||
|
||||
ApplicationView.PreferredLaunchViewSize = new Windows.Foundation.Size(800, 600);
|
||||
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
|
||||
@@ -59,11 +58,6 @@ namespace FluentTerminal.App.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseRequest(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
{
|
||||
App.Instance.TerminalWindowClosed();
|
||||
}
|
||||
|
||||
private async void OnWindowActivated(object sender, WindowActivatedEventArgs e)
|
||||
{
|
||||
if (e.WindowActivationState != CoreWindowActivationState.Deactivated && TerminalContainer.Content is TerminalView terminal)
|
||||
@@ -109,5 +103,13 @@ namespace FluentTerminal.App.Views
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(CoreTitleBarPadding)));
|
||||
}
|
||||
}
|
||||
|
||||
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (sender is Grid grid)
|
||||
{
|
||||
Tabs.MaxWidth = grid.ActualWidth - grid.ColumnDefinitions[2].ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,26 +29,46 @@
|
||||
OpenPaneLength="200"
|
||||
SelectionChanged="NavigationView_SelectionChanged">
|
||||
<NavigationView.MenuItems>
|
||||
<NavigationViewItem
|
||||
Content="General"
|
||||
Icon="Setting"
|
||||
Tag="general" />
|
||||
<NavigationViewItem
|
||||
Content="Terminal"
|
||||
Icon="Setting"
|
||||
Tag="terminal" />
|
||||
<NavigationViewItem
|
||||
Content="Shell"
|
||||
Icon="Setting"
|
||||
Tag="shell" />
|
||||
<NavigationViewItem
|
||||
Content="Key Bindings"
|
||||
Icon="Setting"
|
||||
Tag="keyBindings" />
|
||||
<NavigationViewItem
|
||||
Content="Themes"
|
||||
Icon="Setting"
|
||||
Tag="themes" />
|
||||
<NavigationViewItem Content="General" Tag="general">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="e"
|
||||
ToolTipService.ToolTip="General" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Terminal" Tag="terminal">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="d"
|
||||
ToolTipService.ToolTip="Terminal" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Shell" Tag="shell">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="a"
|
||||
ToolTipService.ToolTip="Shell" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Keybindings" Tag="keyBindings">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="b"
|
||||
ToolTipService.ToolTip="Keybindings" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Themes" Tag="themes">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon
|
||||
FontFamily="/Assets/settingsicons.ttf#settingsicons"
|
||||
Glyph="c"
|
||||
ToolTipService.ToolTip="Themes" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
</NavigationView.MenuItems>
|
||||
<Grid>
|
||||
<Frame x:Name="ContentFrame" />
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace FluentTerminal.App.Views
|
||||
|
||||
private void NavigationView_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationView.SelectedItem = NavigationView.MenuItems.Cast<NavigationViewItemBase>().FirstOrDefault(m => m.Tag.ToString() == "shell");
|
||||
NavigationView.SelectedItem = NavigationView.MenuItems.Cast<NavigationViewItemBase>().FirstOrDefault(m => m.Tag.ToString() == "general");
|
||||
}
|
||||
|
||||
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||
|
||||
@@ -6,5 +6,44 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
NavigationCacheMode="Required"
|
||||
mc:Ignorable="d">
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="1" Margin="24,00,0,24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="28"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="General" />
|
||||
<CommandBar
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
DefaultLabelPosition="Right">
|
||||
<AppBarButton
|
||||
Command="{Binding ViewModel.RestoreDefaultsCommand}"
|
||||
CommandParameter="general"
|
||||
Icon="Undo"
|
||||
Label="Restore Defaults" />
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="2" Margin="24,0,0,0">
|
||||
|
||||
<ToggleSwitch
|
||||
Margin="0,0,0,24"
|
||||
Header="Ask before closing windows"
|
||||
IsOn="{x:Bind ViewModel.ConfirmClosingWindows, Mode=TwoWay}" />
|
||||
<ToggleSwitch Header="Ask before closing tabs" IsOn="{x:Bind ViewModel.ConfirmClosingTabs, Mode=TwoWay}" />
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,12 +1,24 @@
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace FluentTerminal.App.Views.SettingsPages
|
||||
{
|
||||
public sealed partial class GeneralSettings : Page
|
||||
{
|
||||
public SettingsViewModel ViewModel { get; private set; }
|
||||
|
||||
public GeneralSettings()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
if (e.Parameter is SettingsViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
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"
|
||||
NavigationCacheMode="Required"
|
||||
mc:Ignorable="d">
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
Padding="6"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{ThemeResource SystemControlAccentAcrylicWindowAccentMediumHighBrush}"
|
||||
Visibility="{x:Bind ViewModel.ShowResizeOverlay, Mode=OneWay, Converter={StaticResource TrueToVisibleConverter}}">
|
||||
<Grid.Background>
|
||||
<SolidColorBrush Color="{ThemeResource SystemAccentColor}" />
|
||||
</Grid.Background>
|
||||
<TextBlock Foreground="White" Text="{x:Bind ViewModel.ResizeOverlayContent, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FluentTerminal.App.ViewModels;
|
||||
using FluentTerminal.Models;
|
||||
using FluentTerminal.Models.Enums;
|
||||
using FluentTerminal.RuntimeComponent.Interfaces;
|
||||
using FluentTerminal.RuntimeComponent.WebAllowedObjects;
|
||||
using Newtonsoft.Json;
|
||||
@@ -7,13 +8,16 @@ using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Core.Preview;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
@@ -21,26 +25,21 @@ namespace FluentTerminal.App.Views
|
||||
{
|
||||
public sealed partial class TerminalView : UserControl, ITerminalView, ITerminalEventListener
|
||||
{
|
||||
private WebView _webView;
|
||||
|
||||
private SemaphoreSlim _loaded;
|
||||
|
||||
private MenuFlyoutItem _copyMenuItem;
|
||||
private BlockingCollection<Action> _dispatcherJobs;
|
||||
|
||||
public event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
public event EventHandler<string> TerminalTitleChanged;
|
||||
|
||||
public TerminalViewModel ViewModel { get; }
|
||||
private SemaphoreSlim _loaded;
|
||||
private MenuFlyoutItem _pasteMenuItem;
|
||||
private WebView _webView;
|
||||
|
||||
public TerminalView(TerminalViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
InitializeComponent();
|
||||
Loaded += OnLoaded;
|
||||
SystemNavigationManagerPreview.GetForCurrentView().CloseRequested += OnCloseRequested; //todo move to mainpage
|
||||
_loaded = new SemaphoreSlim(0, 1);
|
||||
|
||||
JsonConvert.DefaultSettings = () => {
|
||||
JsonConvert.DefaultSettings = () =>
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
@@ -53,21 +52,44 @@ namespace FluentTerminal.App.Views
|
||||
StartMediatorTask();
|
||||
}
|
||||
|
||||
public async Task FocusWebView()
|
||||
public event EventHandler<Command> KeyboardCommandReceived;
|
||||
public event EventHandler<TerminalSize> TerminalSizeChanged;
|
||||
public event EventHandler<string> TerminalTitleChanged;
|
||||
|
||||
public TerminalViewModel ViewModel { get; }
|
||||
|
||||
public Task ChangeOptions(TerminalOptions options)
|
||||
{
|
||||
if (_webView != null)
|
||||
{
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
await ExecuteScriptAsync("document.focus();");
|
||||
}
|
||||
var serialized = JsonConvert.SerializeObject(options);
|
||||
return ExecuteScriptAsync($"changeOptions('{serialized}')");
|
||||
}
|
||||
|
||||
private void OnCloseRequested(object sender, SystemNavigationCloseRequestedPreviewEventArgs e)
|
||||
public Task ChangeTheme(TerminalColors theme)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(theme);
|
||||
return ExecuteScriptAsync($"changeTheme('{serialized}')");
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_webView?.Navigate(new Uri("about:blank"));
|
||||
_webView = null;
|
||||
}
|
||||
|
||||
public Task ConnectToSocket(string url)
|
||||
{
|
||||
return ExecuteScriptAsync($"connectToWebSocket('{url}');");
|
||||
}
|
||||
|
||||
public async Task<TerminalSize> CreateTerminal(TerminalOptions options, TerminalColors theme, IEnumerable<KeyBinding> keyBindings)
|
||||
{
|
||||
var serializedOptions = JsonConvert.SerializeObject(options);
|
||||
var serializedTheme = JsonConvert.SerializeObject(theme);
|
||||
var serializedKeyBindings = JsonConvert.SerializeObject(keyBindings);
|
||||
var size = await ExecuteScriptAsync($"createTerminal('{serializedOptions}', '{serializedTheme}', '{serializedKeyBindings}')");
|
||||
return JsonConvert.DeserializeObject<TerminalSize>(size);
|
||||
}
|
||||
|
||||
public Task<string> ExecuteScriptAsync(string script)
|
||||
{
|
||||
try
|
||||
@@ -81,6 +103,117 @@ namespace FluentTerminal.App.Views
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
|
||||
public async Task FocusTerminal()
|
||||
{
|
||||
if (_webView != null)
|
||||
{
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
await ExecuteScriptAsync("document.focus();");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task FocusWebView()
|
||||
{
|
||||
if (_webView != null)
|
||||
{
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
await ExecuteScriptAsync("document.focus();");
|
||||
}
|
||||
}
|
||||
|
||||
public Task<string> GetSelection()
|
||||
{
|
||||
return ExecuteScriptAsync("term.getSelection()");
|
||||
}
|
||||
|
||||
public void OnKeyboardCommand(string command)
|
||||
{
|
||||
_dispatcherJobs.Add(() => KeyboardCommandReceived?.Invoke(this, Enum.Parse<Command>(command, true)));
|
||||
}
|
||||
|
||||
public void OnTerminalResized(int columns, int rows)
|
||||
{
|
||||
_dispatcherJobs.Add(() => TerminalSizeChanged?.Invoke(this, new TerminalSize { Columns = columns, Rows = rows }));
|
||||
}
|
||||
|
||||
public void OnTitleChanged(string title)
|
||||
{
|
||||
_dispatcherJobs.Add(() => TerminalTitleChanged?.Invoke(this, title));
|
||||
}
|
||||
|
||||
public Task Write(string text)
|
||||
{
|
||||
text = Convert.ToBase64String(Encoding.UTF8.GetBytes(text));
|
||||
return ExecuteScriptAsync($"paste('{text}')");
|
||||
}
|
||||
|
||||
private void _webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
|
||||
{
|
||||
_loaded.Release();
|
||||
}
|
||||
|
||||
private void _webView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
|
||||
{
|
||||
var bridge = new TerminalBridge(this);
|
||||
_webView.AddWebAllowedObject("terminalBridge", bridge);
|
||||
}
|
||||
|
||||
private void Copy_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OnKeyboardCommand(Command.Copy.ToString());
|
||||
}
|
||||
|
||||
private async void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (WebViewContainer.Children.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_webView = new WebView(WebViewExecutionMode.SeparateThread)
|
||||
{
|
||||
DefaultBackgroundColor = Colors.Transparent
|
||||
};
|
||||
WebViewContainer.Children.Add(_webView);
|
||||
|
||||
_webView.NavigationCompleted += _webView_NavigationCompleted;
|
||||
_webView.NavigationStarting += _webView_NavigationStarting;
|
||||
|
||||
_copyMenuItem = new MenuFlyoutItem { Text = "Copy" };
|
||||
_copyMenuItem.Click += Copy_Click;
|
||||
|
||||
_pasteMenuItem = new MenuFlyoutItem { Text = "Paste" };
|
||||
_pasteMenuItem.Click += Paste_Click;
|
||||
|
||||
_webView.ContextFlyout = new MenuFlyout
|
||||
{
|
||||
Items = { _copyMenuItem, _pasteMenuItem }
|
||||
};
|
||||
|
||||
_webView.Navigate(new Uri("http://localhost:9000/Client/index.html"));
|
||||
|
||||
await _loaded.WaitAsync();
|
||||
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
|
||||
await ViewModel.OnViewIsReady(this);
|
||||
}
|
||||
|
||||
public void OnRightClick(int x, int y, bool hasSelection)
|
||||
{
|
||||
_dispatcherJobs.Add(() =>
|
||||
{
|
||||
var flyout = (MenuFlyout)_webView.ContextFlyout;
|
||||
_copyMenuItem.IsEnabled = hasSelection;
|
||||
flyout.ShowAt(_webView, new Point(x, y));
|
||||
});
|
||||
}
|
||||
|
||||
private void Paste_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OnKeyboardCommand(Command.Paste.ToString());
|
||||
}
|
||||
|
||||
private void StartMediatorTask()
|
||||
{
|
||||
_dispatcherJobs = new BlockingCollection<Action>();
|
||||
@@ -100,70 +233,5 @@ namespace FluentTerminal.App.Views
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_webView = new WebView(WebViewExecutionMode.SeparateThread)
|
||||
{
|
||||
DefaultBackgroundColor = Colors.Transparent
|
||||
};
|
||||
WebViewContainer.Children.Add(_webView);
|
||||
|
||||
_webView.NavigationCompleted += _webView_NavigationCompleted;
|
||||
_webView.NavigationStarting += _webView_NavigationStarting;
|
||||
_webView.Navigate(new Uri("http://localhost:9000/Client/index.html"));
|
||||
|
||||
await _loaded.WaitAsync();
|
||||
|
||||
_webView.Focus(FocusState.Programmatic);
|
||||
|
||||
await ViewModel.OnViewIsReady(this);
|
||||
}
|
||||
|
||||
private void _webView_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
|
||||
{
|
||||
_loaded.Release();
|
||||
}
|
||||
|
||||
private void _webView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
|
||||
{
|
||||
var bridge = new TerminalBridge(this);
|
||||
_webView.AddWebAllowedObject("terminalBridge", bridge);
|
||||
}
|
||||
|
||||
public async Task<TerminalSize> CreateTerminal(TerminalOptions options, TerminalColors theme)
|
||||
{
|
||||
var serializedOptions = JsonConvert.SerializeObject(options);
|
||||
var serializedTheme = JsonConvert.SerializeObject(theme);
|
||||
var size = await ExecuteScriptAsync($"createTerminal('{serializedOptions}', '{serializedTheme}')");
|
||||
return JsonConvert.DeserializeObject<TerminalSize>(size);
|
||||
}
|
||||
|
||||
public Task ConnectToSocket(string url)
|
||||
{
|
||||
return ExecuteScriptAsync($"connectToWebSocket('{url}');");
|
||||
}
|
||||
|
||||
public void OnTerminalResized(int columns, int rows)
|
||||
{
|
||||
_dispatcherJobs.Add(() => TerminalSizeChanged?.Invoke(this, new TerminalSize { Columns = columns, Rows = rows }));
|
||||
}
|
||||
|
||||
public void OnTitleChanged(string title)
|
||||
{
|
||||
_dispatcherJobs.Add(() => TerminalTitleChanged?.Invoke(this, title));
|
||||
}
|
||||
|
||||
public Task ChangeTheme(TerminalColors theme)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(theme);
|
||||
return ExecuteScriptAsync($"changeTheme('{serialized}')");
|
||||
}
|
||||
|
||||
public Task ChangeOptions(TerminalOptions options)
|
||||
{
|
||||
var serialized = JsonConvert.SerializeObject(options);
|
||||
return ExecuteScriptAsync($"changeOptions('{serialized}')");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
-6936
File diff suppressed because it is too large.
Load diff
Vendored
-134
@@ -1,134 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
|
||||
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||
* https://github.com/chjj/term.js
|
||||
* @license MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Originally forked from (with the author's permission):
|
||||
* Fabrice Bellard's javascript vt100 for jslinux:
|
||||
* http://bellard.org/jslinux/
|
||||
* Copyright (c) 2011 Fabrice Bellard
|
||||
* The original design remains. The terminal itself
|
||||
* has been extended to include xterm CSI codes, among
|
||||
* other features.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default styles for xterm.js
|
||||
*/
|
||||
|
||||
.xterm {
|
||||
font-family: courier-new, courier, monospace;
|
||||
font-feature-settings: "liga" 0;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.xterm.focus,
|
||||
.xterm:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.xterm .xterm-helpers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
/**
|
||||
* The z-index of the helpers must be higher than the canvases in order for
|
||||
* IMEs to appear on top.
|
||||
*/
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.xterm .xterm-helper-textarea {
|
||||
/*
|
||||
* HACK: to fix IE's blinking cursor
|
||||
* Move textarea out of the screen to the far left, so that the cursor is not visible.
|
||||
*/
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
left: -9999em;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: -10;
|
||||
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.xterm .composition-view {
|
||||
/* TODO: Composition position got messed up somewhere */
|
||||
background: #000;
|
||||
color: #FFF;
|
||||
display: none;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.xterm .composition-view.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.xterm .xterm-viewport {
|
||||
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||
background-color: #000;
|
||||
overflow-y: scroll;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xterm .xterm-screen canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-scroll-area {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.xterm .xterm-char-measure-element {
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
left: -9999em;
|
||||
}
|
||||
|
||||
.xterm.enable-mouse-events {
|
||||
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.xterm:not(.enable-mouse-events) {
|
||||
cursor: text;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ Terminal.applyAddon(winptyCompat);
|
||||
var term, socket;
|
||||
var terminalContainer = document.getElementById('terminal-container');
|
||||
|
||||
function createTerminal(options, theme) {
|
||||
function createTerminal(options, theme, keyBindings) {
|
||||
while (terminalContainer.children.length) {
|
||||
terminalContainer.removeChild(terminalContainer.children[0]);
|
||||
}
|
||||
@@ -19,6 +19,8 @@ function createTerminal(options, theme) {
|
||||
theme = JSON.parse(theme);
|
||||
theme.background = 'transparent';
|
||||
|
||||
window.keyBindings = JSON.parse(keyBindings);
|
||||
|
||||
options = JSON.parse(options);
|
||||
|
||||
var terminalOptions = {
|
||||
@@ -54,6 +56,30 @@ function createTerminal(options, theme) {
|
||||
resizeTimeout = setTimeout(term.fit(), 500);
|
||||
}
|
||||
|
||||
window.onmouseup = function (e) {
|
||||
if (e.button == 2) {
|
||||
terminalBridge.notifyRightClick(e.clientX, e.clientY, term.hasSelection());
|
||||
}
|
||||
}
|
||||
|
||||
term.attachCustomKeyEventHandler(function (e) {
|
||||
for (var i = 0; i < window.keyBindings.length; i++) {
|
||||
var keyBinding = window.keyBindings[i];
|
||||
if (keyBinding.ctrl == e.ctrlKey
|
||||
&& keyBinding.alt == e.altKey
|
||||
&& keyBinding.meta == e.metaKey
|
||||
&& keyBinding.shift == e.shiftKey
|
||||
&& keyBinding.key == e.keyCode) {
|
||||
if (document.visibilityState == 'visible') {
|
||||
e.preventDefault();
|
||||
terminalBridge.invokeCommand(keyBinding.command);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return JSON.stringify({
|
||||
rows: term.rows,
|
||||
columns: term.cols
|
||||
@@ -87,7 +113,23 @@ function changeOptions(options) {
|
||||
term.setOption('fontSize', options.fontSize);
|
||||
}
|
||||
|
||||
function paste(content) {
|
||||
content = b64DecodeUnicode(content);
|
||||
term.send(content);
|
||||
}
|
||||
|
||||
function b64DecodeUnicode(str) {
|
||||
return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
|
||||
}).join(''))
|
||||
}
|
||||
|
||||
document.oncontextmenu = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
window.createTerminal = createTerminal;
|
||||
window.connectToWebSocket = connectToWebSocket;
|
||||
window.changeTheme = changeTheme;
|
||||
window.changeOptions = changeOptions;
|
||||
window.changeOptions = changeOptions;
|
||||
window.paste = paste;
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class ApplicationSettings
|
||||
{
|
||||
public bool ConfirmClosingTabs { get; set; }
|
||||
public bool ConfirmClosingWindows { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,6 @@
|
||||
public bool Success { get; set; }
|
||||
public string WebSocketUrl { get; set; }
|
||||
public string Error { get; set; }
|
||||
public string ShellExecutableName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace FluentTerminal.Models.Enums
|
||||
{
|
||||
public enum Command
|
||||
{
|
||||
ToggleWindow,
|
||||
NextTab,
|
||||
PreviousTab,
|
||||
NewTab,
|
||||
CloseTab,
|
||||
NewWindow,
|
||||
ShowSettings,
|
||||
Copy,
|
||||
Paste
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,11 @@
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Windows">
|
||||
<HintPath>..\..\..\..\..\..\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.16299.0\Windows.winmd</HintPath>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
using FluentTerminal.Models.Enums;
|
||||
using Windows.System;
|
||||
|
||||
namespace FluentTerminal.Models
|
||||
{
|
||||
public class KeyBinding
|
||||
{
|
||||
public Command Command { get; set; }
|
||||
public int Key { get; set; }
|
||||
public bool Ctrl { get; set; }
|
||||
public bool Alt { get; set; }
|
||||
public bool Meta { get; set; }
|
||||
public bool Shift { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FluentTerminal.RuntimeComponent.Interfaces
|
||||
namespace FluentTerminal.RuntimeComponent.Interfaces
|
||||
{
|
||||
public interface ITerminalEventListener
|
||||
{
|
||||
void OnTerminalResized(int columns, int rows);
|
||||
|
||||
void OnTitleChanged(string title);
|
||||
|
||||
void OnKeyboardCommand(string command);
|
||||
|
||||
void OnRightClick(int x, int y, bool hasSelection);
|
||||
}
|
||||
}
|
||||
@@ -22,5 +22,15 @@ namespace FluentTerminal.RuntimeComponent.WebAllowedObjects
|
||||
{
|
||||
_terminalEventListener.OnTitleChanged(title);
|
||||
}
|
||||
|
||||
public void InvokeCommand(string command)
|
||||
{
|
||||
_terminalEventListener.OnKeyboardCommand(command);
|
||||
}
|
||||
|
||||
public void NotifyRightClick(int x, int y, bool hasSelection)
|
||||
{
|
||||
_terminalEventListener.OnRightClick(x, y, hasSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,6 +152,9 @@
|
||||
<PackageReference Include="Fleck">
|
||||
<Version>0.14.0.59</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="GlobalHotKey">
|
||||
<Version>1.1.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.OwinSelfHost">
|
||||
<Version>5.2.3</Version>
|
||||
</PackageReference>
|
||||
|
||||
@@ -51,8 +51,10 @@ namespace FluentTerminal.SystemTray.Services
|
||||
|
||||
if (!winpty_spawn(_handle, spawnConfigHandle, out IntPtr process, out IntPtr thread, out int procError, out errorHandle))
|
||||
{
|
||||
throw new Exception(winpty_error_msg(errorHandle).ToString());
|
||||
throw new Exception($"Failed to start the shell process. Please check your shell settings.\nTried to start: {args}");
|
||||
}
|
||||
|
||||
ShellExecutableName = Path.GetFileNameWithoutExtension(exe);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -87,7 +89,7 @@ namespace FluentTerminal.SystemTray.Services
|
||||
};
|
||||
socket.OnMessage = message =>
|
||||
{
|
||||
var bytes = Encoding.ASCII.GetBytes(message);
|
||||
var bytes = Encoding.UTF8.GetBytes(message);
|
||||
_stdin.Write(bytes, 0, bytes.Length);
|
||||
};
|
||||
});
|
||||
@@ -128,6 +130,7 @@ namespace FluentTerminal.SystemTray.Services
|
||||
|
||||
public int Id { get; }
|
||||
public string WebSocketUrl { get; }
|
||||
public string ShellExecutableName { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -33,7 +33,8 @@ namespace FluentTerminal.SystemTray.Services
|
||||
{
|
||||
Success = true,
|
||||
Id = terminal.Id,
|
||||
WebSocketUrl = terminal.WebSocketUrl
|
||||
WebSocketUrl = terminal.WebSocketUrl,
|
||||
ShellExecutableName = terminal.ShellExecutableName
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
using System;
|
||||
using GlobalHotKey;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.AppService;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
namespace FluentTerminal.SystemTray
|
||||
{
|
||||
public class SystemTrayApplicationContext : ApplicationContext
|
||||
{
|
||||
private AppServiceConnection connection = null;
|
||||
private HotKeyManager hotKeyManager = null;
|
||||
private NotifyIcon notifyIcon = null;
|
||||
|
||||
public SystemTrayApplicationContext()
|
||||
|
||||
{
|
||||
var openMenuItem = new MenuItem("Open", new EventHandler(OpenApp));
|
||||
var openMenuItem = new MenuItem("Show", new EventHandler(OpenApp));
|
||||
var newWindowItem = new MenuItem("New Window", new EventHandler(NewWindow));
|
||||
var settingsMenuItem = new MenuItem("Show Settings", new EventHandler(ShowSettings));
|
||||
var exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
|
||||
@@ -32,22 +38,35 @@ namespace FluentTerminal.SystemTray
|
||||
notifyIcon.Icon = Properties.Resources.Square44x44Logo_scale_100;
|
||||
notifyIcon.ContextMenu = new ContextMenu(new MenuItem[] { openMenuItem, newWindowItem, settingsMenuItem, exitMenuItem });
|
||||
notifyIcon.Visible = true;
|
||||
|
||||
hotKeyManager = new HotKeyManager();
|
||||
try
|
||||
{
|
||||
hotKeyManager.Register(Key.Scroll, ModifierKeys.None);
|
||||
hotKeyManager.KeyPressed += HotkeyManager_KeyPressed;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
ShowNotification("Fluent Terminal", "Unable to register hotkey for toggle window command");
|
||||
}
|
||||
}
|
||||
|
||||
private async void OpenApp(object sender, EventArgs e)
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
IEnumerable<AppListEntry> appListEntries = await Package.Current.GetAppListEntriesAsync();
|
||||
await appListEntries.First().LaunchAsync();
|
||||
hotKeyManager.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void ShowSettings(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("flute.exe", "settings");
|
||||
}
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
private void NewWindow(object sender, EventArgs e)
|
||||
private void Connection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
||||
{
|
||||
Process.Start("flute.exe", "new");
|
||||
connection.ServiceClosed -= Connection_ServiceClosed;
|
||||
connection = null;
|
||||
}
|
||||
|
||||
private async void Exit(object sender, EventArgs e)
|
||||
@@ -60,6 +79,47 @@ namespace FluentTerminal.SystemTray
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private string GetActiveProcessFileName()
|
||||
{
|
||||
try
|
||||
{
|
||||
IntPtr hwnd = GetForegroundWindow();
|
||||
GetWindowThreadProcessId(hwnd, out uint pid);
|
||||
Process p = Process.GetProcessById((int)pid);
|
||||
return p.MainWindowTitle;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void HotkeyManager_KeyPressed(object sender, KeyPressedEventArgs e)
|
||||
{
|
||||
if (e.HotKey.Key == Key.Scroll)
|
||||
{
|
||||
if (GetActiveProcessFileName() == "Fluent Terminal")
|
||||
{
|
||||
SendKeys.Send("%{Tab}");
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenApp(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NewWindow(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("flute.exe", "new");
|
||||
}
|
||||
|
||||
private async void OpenApp(object sender, EventArgs e)
|
||||
{
|
||||
IEnumerable<AppListEntry> appListEntries = await Package.Current.GetAppListEntriesAsync();
|
||||
await appListEntries.First().LaunchAsync();
|
||||
}
|
||||
|
||||
private async Task SendMessage(ValueSet message)
|
||||
|
||||
{
|
||||
@@ -82,10 +142,27 @@ namespace FluentTerminal.SystemTray
|
||||
await connection.SendMessageAsync(message);
|
||||
}
|
||||
|
||||
private void Connection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
|
||||
private void ShowNotification(string title, string content)
|
||||
{
|
||||
connection.ServiceClosed -= Connection_ServiceClosed;
|
||||
connection = null;
|
||||
string xml = $@"<toast>
|
||||
<visual>
|
||||
<binding template='ToastGeneric'>
|
||||
<text>{title}</text>
|
||||
<text>{content}</text>
|
||||
</binding>
|
||||
</visual>
|
||||
</toast>";
|
||||
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(xml);
|
||||
|
||||
var toast = new ToastNotification(doc);
|
||||
ToastNotificationManager.CreateToastNotifier().Show(toast);
|
||||
}
|
||||
|
||||
private void ShowSettings(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("flute.exe", "settings");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user