diff --git a/App.config b/App.config index 56efbc7..775e457 100644 --- a/App.config +++ b/App.config @@ -1,6 +1,14 @@ - + + + + + + + + + \ No newline at end of file diff --git a/Config/Config.cs b/Config/Config.cs new file mode 100644 index 0000000..d28d232 --- /dev/null +++ b/Config/Config.cs @@ -0,0 +1,24 @@ +using WindowsTermialTray.Keys; + +namespace WindowsTermialTray.Config +{ + public class App + { + public string ProcessName { get; set; } + public string ExeFilePath { get; set; } + public ModifierKeys ModifierKeys { get; set; } + public System.Windows.Forms.Keys Keys { get; set; } + } + + public class Config + { + public virtual App[] Apps { get; set; } + } + + public class DefaultConfig : Config + { + public override App[] Apps { get; set; } = new App[] { + new App { ProcessName = "WindowsTerminal", ExeFilePath = "wt.exe", ModifierKeys = ModifierKeys.Alt, Keys = System.Windows.Forms.Keys.T} + }; + } +} diff --git a/Config/ConfigBuilder.cs b/Config/ConfigBuilder.cs new file mode 100644 index 0000000..70d140a --- /dev/null +++ b/Config/ConfigBuilder.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.IO; +using WindowsTermialTray.Config.Provider; + +namespace WindowsTermialTray.Config +{ + public class ConfigBuilder + { + private readonly List _providers; + + private ConfigBuilder(List providers) + { + _providers = providers; + } + + public static ConfigBuilder Create() + { + return new ConfigBuilder(new List { }); + } + + public ConfigBuilder AddDefault() + { + _providers.Add(new DefaultProvider()); + return new ConfigBuilder(_providers); + } + + public ConfigBuilder AddJsonFile(string jsonPath) + { + if (File.Exists(jsonPath)) + { + var jsonString = File.ReadAllText(jsonPath); + _providers.Add(new JsonProvider(jsonString)); + } + + return new ConfigBuilder(_providers); + } + + public Config Build() + { + foreach (var provider in _providers) + { + var config = provider.Load(); + if (config != null) + { + return config; + } + else + { + continue; + } + } + return null; + } + } +} diff --git a/Config/Provider/DefaultProvider.cs b/Config/Provider/DefaultProvider.cs new file mode 100644 index 0000000..c606e98 --- /dev/null +++ b/Config/Provider/DefaultProvider.cs @@ -0,0 +1,10 @@ +namespace WindowsTermialTray.Config.Provider +{ + class DefaultProvider : IProvider + { + public Config Load() + { + return new DefaultConfig(); + } + } +} diff --git a/Config/Provider/IProvider.cs b/Config/Provider/IProvider.cs new file mode 100644 index 0000000..29016f8 --- /dev/null +++ b/Config/Provider/IProvider.cs @@ -0,0 +1,11 @@ +namespace WindowsTermialTray.Config.Provider +{ + interface IProvider + { + /// + /// Load configurations. + /// + /// Thrown when invalid data provided. + Config Load(); + } +} diff --git a/Config/Provider/JsonProvider.cs b/Config/Provider/JsonProvider.cs new file mode 100644 index 0000000..0af99db --- /dev/null +++ b/Config/Provider/JsonProvider.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Text.Json; + +namespace WindowsTermialTray.Config.Provider +{ + public class JsonProvider : IProvider + { + private readonly string _jsonString; + + public JsonProvider(string jsonString) + { + _jsonString = jsonString; + } + + public Config Load() + { + try + { + var config = JsonSerializer.Deserialize(_jsonString); + var hasNull = config.Apps.Any(a => a.GetType().GetProperties().Any(p => p.GetValue(a) == default)); + if (hasNull) + { + throw new FormatException("json includes null value or some fields are missing"); + } + + return config; + } + catch (JsonException e) + { + throw new FormatException($"invalid json", e); + } + } + } +} diff --git a/README.md b/README.md index d2046a6..d31645a 100644 --- a/README.md +++ b/README.md @@ -73,28 +73,44 @@ If you had multiple instances of an app, Termial Tray will store only one, so fe ## FAQ ### **Can I manage other apps like Windows Terminal with Quake Style?** -Yes of course! But you need to dig into code. Just download this repo, go to `TerminalTracyIcon.cs` file and find this section: -```C# -_appTrayList.AddRange(new [] - { - new TrayApp("WindowsTerminal", "wt.exe", ModifierKeys.Alt, Keys.Oemtilde) - // Add your other tray apps here - // new TrayApp("ProcessName", "exec.exe", ModifierKeys.Ctrl, Keys.Oemtilde) - }); +You can configure settings by `config.json` like below. + +#### config.json +```json +{ + "Apps": [ + { + "ProcessName": "WindowsTerminal", + "ExeFilePath": "wt.exe", + "ModifierKeys": 1, + "Keys": 84 + }, + { + "ProcessName": "Spotify", + "ExeFilePath": "Spotify.exe", + "ModifierKeys": 1, + "Keys": 83 + } + ] +} ``` +The json should be located at: +1. same directory as `WindowsTermialTray.exe` +1. `$env:LOCALAPPDATA\WindowsTermialTray\config.json` + From here you can add other apps, but you need to specify and know: * Your app [ProcessName](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-process?view=powershell-7). * Your app executable. -* Unique pair of `ModifierKey` and `Key`. - -When you find out everything - add your app in code with parameters (and comment Windows Terminal if don't want it), and follow steps in [installation section](#installation), to build `.exe` file. - -### **Why building and adding other apps is so difficult?** -Because I didn't prepared this. For now this little app is for me, but if you like it too - tell me, and I'll continue to contribuiting it! +* Unique pair of `ModifierKey` and [Key](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.keys). +#### ModifierKey +- Alt: 1 +- Control: 2 +- Shift: 4 +- Win: 8 ### **Will you be contributing this Termial Tray?** Nah. I hope that MS add *Quake Style* behaviour for Windows Terminal, but if not, or some people find Termial Tray useful for other Jobs - I'll start to work on this app, to make it more powerfull and usefull. ### **Your code is a mess, shame on you** -Thanks for your opinion! I'm not an expert in .NET and this kind of system apps - I created this to fit my personal needs and added it here for people (if they want to benefit from this solution). But if you're expert, please share your opinion about this project with me - I'll fix and update code to be clean and fast. \ No newline at end of file +Thanks for your opinion! I'm not an expert in .NET and this kind of system apps - I created this to fit my personal needs and added it here for people (if they want to benefit from this solution). But if you're expert, please share your opinion about this project with me - I'll fix and update code to be clean and fast. diff --git a/TerminalTrayIcon.cs b/TerminalTrayIcon.cs index 973cd96..8a4d3f6 100644 --- a/TerminalTrayIcon.cs +++ b/TerminalTrayIcon.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.IO; using System.Windows.Forms; using WindowsTermialTray; -using WindowsTermialTray.Keys; +using WindowsTermialTray.Config; using WindowsTermialTray.Properties; namespace WindowsTerminalTray @@ -28,13 +28,28 @@ public TerminalTrayIcon() contextMenu.MenuItems.Add(exitItem); + var configFileName = "config.json"; + Config config = null; + try + { + config = ConfigBuilder.Create() + .AddJsonFile(Path.Combine(Environment.CurrentDirectory, configFileName)) + .AddJsonFile(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.ProductName, configFileName)) + .AddDefault().Build(); + } + catch (FormatException) + { + OpenMessageBox("Settings couldn't be loaded from file.\nCheck for syntax error, including invalid value."); + Application.Exit(); + } + _appTrayList = new List(); - _appTrayList.AddRange(new [] + foreach (var app in config.Apps) { - new TrayApp("WindowsTerminal", "wt.exe", ModifierKeys.Alt, Keys.Oemtilde) - // Add your other tray apps here - // new TrayApp("ProcessName", "exec.exe", ModifierKeys.Ctrl, Keys.Oemtilde) - }); + _appTrayList.AddRange(new[] { + new TrayApp(app.ProcessName, app.ExeFilePath, app.ModifierKeys, app.Keys) + }); + } Ni = new NotifyIcon { @@ -54,5 +69,10 @@ private void Close(object Sender, EventArgs e) } public void Dispose() { } + + private void OpenMessageBox(string message) + { + MessageBox.Show(message, Application.ProductName); + } } -} \ No newline at end of file +} diff --git a/TrayApp.cs b/TrayApp.cs index f7b8a7b..1220f78 100644 --- a/TrayApp.cs +++ b/TrayApp.cs @@ -86,7 +86,9 @@ private void RunApp() process.StartInfo.FileName = _exec; process.StartInfo.WindowStyle = ProcessWindowStyle.Maximized; process.Start(); - while (_appProcess == null) + + var start = DateTime.Now; + while (_appProcess == null && DateTime.Now - start < TimeSpan.FromMilliseconds(1000)) { _appProcess = Process.GetProcesses().FirstOrDefault(x => x.ProcessName.Equals(_processName) && !x.HasExited); Thread.Sleep(100); @@ -108,4 +110,4 @@ public void Dispose() _hook.Dispose(); } } -} \ No newline at end of file +} diff --git a/WindowsTermialTray.sln b/WindowsTermialTray.sln index 9674dac..10c0c08 100644 --- a/WindowsTermialTray.sln +++ b/WindowsTermialTray.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29806.167 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminalTray", "WindowsTerminalTray.csproj", "{CBC9BB16-8421-4641-9850-B950D82F4022}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "test\UnitTests\UnitTests.csproj", "{B783C3E2-7D28-4217-A915-266562E2C3F5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {CBC9BB16-8421-4641-9850-B950D82F4022}.Debug|Any CPU.Build.0 = Debug|Any CPU {CBC9BB16-8421-4641-9850-B950D82F4022}.Release|Any CPU.ActiveCfg = Release|Any CPU {CBC9BB16-8421-4641-9850-B950D82F4022}.Release|Any CPU.Build.0 = Release|Any CPU + {B783C3E2-7D28-4217-A915-266562E2C3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B783C3E2-7D28-4217-A915-266562E2C3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B783C3E2-7D28-4217-A915-266562E2C3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B783C3E2-7D28-4217-A915-266562E2C3F5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WindowsTerminalTray.csproj b/WindowsTerminalTray.csproj index a70930e..e1debfd 100644 --- a/WindowsTerminalTray.csproj +++ b/WindowsTerminalTray.csproj @@ -58,8 +58,39 @@ terminal-tray.ico + + packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll + + + packages\System.Text.Encodings.Web.5.0.1\lib\net461\System.Text.Encodings.Web.dll + + + packages\System.Text.Json.5.0.2\lib\net461\System.Text.Json.dll + + + packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + @@ -71,6 +102,11 @@ + + + + + @@ -86,6 +122,7 @@ Resources.resx True + SettingsSingleFileGenerator Settings.Designer.cs diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..5fbc070 --- /dev/null +++ b/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/test/UnitTests/Config/Provider/JsonProviderTests.cs b/test/UnitTests/Config/Provider/JsonProviderTests.cs new file mode 100644 index 0000000..88c1042 --- /dev/null +++ b/test/UnitTests/Config/Provider/JsonProviderTests.cs @@ -0,0 +1,62 @@ +using System; +using WindowsTermialTray.Config.Provider; +using WindowsTermialTray.Keys; +using Xunit; + +namespace UnitTests +{ + public class JsonProviderTests + { + [Fact] + public void LoadFromValidJson() + { + var json = @" +{ + ""Apps"": [ + { + ""ProcessName"": ""Windows Terminal"", + ""ExeFilePath"": ""wt.exe"", + ""ModifierKeys"": 1, + ""Keys"": 192 + } + ] +}"; + var jsonProvider = new JsonProvider(json); + var config = jsonProvider.Load(); + + Assert.Equal("Windows Terminal", config.Apps[0].ProcessName); + Assert.Equal("wt.exe", config.Apps[0].ExeFilePath); + Assert.Equal(ModifierKeys.Alt, config.Apps[0].ModifierKeys); + Assert.Equal(System.Windows.Forms.Keys.Oemtilde, config.Apps[0].Keys); + } + + [Fact] + public void LoadFromInvalidJson() + { + var json = @"""test"""; + var jsonProvider = new JsonProvider(json); + var exception = Assert.Throws( + () => jsonProvider.Load()); + + Assert.NotNull(exception.Message); + } + + [Fact] + public void LoadFromInvalidJson_MissingFields() + { + var json = @" +{ + ""Apps"": [ + { + ""ProcessName"": ""Windows Terminal"" + } + ] +}"; + var jsonProvider = new JsonProvider(json); + var exception = Assert.Throws( + () => jsonProvider.Load()); + + Assert.NotNull(exception.Message); + } + } +} diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj new file mode 100644 index 0000000..cc49aa3 --- /dev/null +++ b/test/UnitTests/UnitTests.csproj @@ -0,0 +1,30 @@ + + + + net472 + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + +