├── .github
├── Recording.gif
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature-request.md
├── PowerAccent.UI
├── Resources
│ ├── Flags
│ │ ├── BG.jpg
│ │ ├── CY.jpg
│ │ ├── CZ.jpg
│ │ ├── DE.jpg
│ │ ├── FI.jpg
│ │ ├── FR.jpg
│ │ ├── HR.jpg
│ │ ├── HU.jpg
│ │ ├── IS.jpg
│ │ ├── IT.jpg
│ │ ├── JP.jpg
│ │ ├── MI.jpg
│ │ ├── NL.jpg
│ │ ├── PI.jpg
│ │ ├── PL.jpg
│ │ ├── PT.jpg
│ │ ├── RO.jpg
│ │ ├── RS.jpg
│ │ ├── SA.jpg
│ │ ├── SK.jpg
│ │ ├── SP.jpg
│ │ ├── SV.jpg
│ │ ├── TK.jpg
│ │ ├── ALL.jpg
│ │ └── CUR.jpg
│ ├── a-icon.ico
│ └── win11desktop.jpg
├── Themes
│ ├── CustomLibraryThemeProvider.cs
│ ├── HighContrast1.xaml
│ ├── HighContrast2.xaml
│ ├── HighContrastBlack.xaml
│ ├── HighContrastWhite.xaml
│ ├── Dark.xaml
│ ├── Light.xaml
│ └── ThemeManager.cs
├── AssemblyInfo.cs
├── App.xaml
├── MainWindow.xaml
├── Selector.xaml.cs
├── App.xaml.cs
├── Settings.xaml.cs
├── SettingsPage
│ ├── OptionsPage.xaml.cs
│ ├── PositionPage.xaml.cs
│ ├── CountriesPage.xaml.cs
│ ├── CountriesPage.xaml
│ ├── SortPage.xaml.cs
│ ├── PositionPage.xaml
│ ├── OptionsPage.xaml
│ └── SortPage.xaml
├── Settings.xaml
├── MainWindow.xaml.cs
├── PowerAccent.UI.csproj
└── Selector.xaml
├── PowerAccent.Core
├── Models
│ ├── TriggerEnum.cs
│ ├── Country.cs
│ ├── Point.cs
│ ├── Size.cs
│ ├── KeysEnums.cs
│ └── Rect.cs
├── Extensions
│ └── CharExtensions.cs
├── ModuleHandlers
│ ├── CustomModules
│ │ ├── PauseModuleHandler.cs
│ │ ├── CheckGameModeModuleHandler.cs
│ │ ├── DisplaySelectorModuleHandler.cs
│ │ ├── StrokeArrowsModuleHandler.cs
│ │ ├── StrokeLetterModuleHandler.cs
│ │ └── StrokeSpaceModuleHandler.cs
│ ├── ModuleHandler.cs
│ └── ModuleDirector.cs
├── PowerAccent.Core.csproj
├── Tools
│ ├── Calculation.cs
│ ├── WindowsFunctions.cs
│ └── KeyboardListener.cs
├── PowerAccent.cs
├── Services
│ └── SettingsService.cs
└── Languages.cs
├── .editorconfig
├── .gitignore
├── LICENSE
├── PowerAccent.Wix
├── Product.wxs
└── PowerAccent.Wix.SelfContained.wixproj
├── README.md
└── PowerAccent.sln
/.github/Recording.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/.github/Recording.gif
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/BG.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/BG.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/CY.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/CY.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/CZ.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/CZ.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/DE.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/DE.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/FI.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/FI.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/FR.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/FR.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/HR.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/HR.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/HU.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/HU.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/IS.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/IS.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/IT.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/IT.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/JP.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/JP.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/MI.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/MI.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/NL.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/NL.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/PI.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/PI.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/PL.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/PL.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/PT.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/PT.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/RO.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/RO.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/RS.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/RS.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/SA.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/SA.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/SK.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/SK.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/SP.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/SP.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/SV.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/SV.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/TK.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/TK.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/a-icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/a-icon.ico
--------------------------------------------------------------------------------
/PowerAccent.Core/Models/TriggerEnum.cs:
--------------------------------------------------------------------------------
1 | public enum TriggerEnum
2 | {
3 | Space = 0,
4 | Arrows = 1,
5 | LongPress = 2,
6 | }
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/ALL.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/ALL.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/Flags/CUR.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/Flags/CUR.jpg
--------------------------------------------------------------------------------
/PowerAccent.UI/Resources/win11desktop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damienleroy/PowerAccent/HEAD/PowerAccent.UI/Resources/win11desktop.jpg
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CS8846: L'expression switch ne prend pas en charge toutes les valeurs possibles de son type d'entrée (elle n'est pas exhaustive).
4 | dotnet_diagnostic.CS8846.severity = none
5 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Models/Country.cs:
--------------------------------------------------------------------------------
1 | namespace PowerAccent.Core;
2 |
3 | public class Country
4 | {
5 | public string Name { get; set; }
6 | public string ImageUrl { get; set; }
7 | public bool IsChecked { get; set; }
8 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Additional context**
11 | - Version: x.x.x
12 | - Which tool: PowerAccent / PowerToys
13 |
14 | **Describe the bug and how to reproduce it**
15 | A clear and concise description of what the bug is.
16 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **What is your problem**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Extensions/CharExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace PowerAccent.Core.Extensions
2 | {
3 | internal static class CharExtensions
4 | {
5 | public static char[] ToUpper(this char[] array)
6 | {
7 | char[] result = new char[array.Length];
8 | for (int i = 0; i < array.Length; i++)
9 | {
10 | result[i] = Char.ToUpper(array[i]);
11 | }
12 | return result;
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 | nupkg/
7 |
8 | # Visual Studio Code
9 | .vscode
10 |
11 | # Rider
12 | .idea
13 |
14 | # User-specific files
15 | *.suo
16 | *.user
17 | *.userosscache
18 | *.sln.docstates
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | build/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Oo]ut/
32 | msbuild.log
33 | msbuild.err
34 | msbuild.wrn
35 |
36 | # Visual Studio 2015
37 | .vs/
38 |
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/CustomModules/PauseModuleHandler.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Services;
2 |
3 | namespace PowerAccent.Core.ModuleHandlers.CustomModules;
4 |
5 | internal class PauseModuleHandler : ModuleHandler
6 | {
7 | public PauseModuleHandler(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options) : base(powerAccent, settingsService, options)
8 | {
9 | }
10 |
11 | public override bool InvokeKeyDown(uint key)
12 | {
13 | if (PowerAccent.IsPaused)
14 | return true;
15 |
16 | return base.InvokeKeyDown(key);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PowerAccent.Core/PowerAccent.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | disable
7 | True
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/CustomLibraryThemeProvider.cs:
--------------------------------------------------------------------------------
1 | using ControlzEx.Theming;
2 | using System.Collections.Generic;
3 |
4 | namespace PowerAccent.UI.Themes;
5 |
6 | public class CustomLibraryThemeProvider : LibraryThemeProvider
7 | {
8 | public static readonly CustomLibraryThemeProvider DefaultInstance = new CustomLibraryThemeProvider();
9 |
10 | public CustomLibraryThemeProvider()
11 | : base(true)
12 | {
13 | }
14 |
15 | ///
16 | public override void FillColorSchemeValues(Dictionary values, RuntimeThemeColorValues colorValues)
17 | {
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/PowerAccent.UI/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/PowerAccent.UI/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/PowerAccent.UI/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/CustomModules/CheckGameModeModuleHandler.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Services;
2 | using PowerAccent.Core.Tools;
3 |
4 | namespace PowerAccent.Core.ModuleHandlers.CustomModules;
5 |
6 | internal class CheckGameModeModuleHandler : ModuleHandler
7 | {
8 | public CheckGameModeModuleHandler(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options) : base(powerAccent, settingsService, options)
9 | {
10 | }
11 |
12 | public override bool InvokeKeyDown(uint key)
13 | {
14 | if (SettingsService.DisableInFullScreen
15 | && !Options.IsVisible && Options.LetterPressed.HasValue && Options.TriggerPressed.HasValue
16 | && WindowsFunctions.IsGameMode())
17 | {
18 | Options.Reset();
19 | return true;
20 | }
21 |
22 | return base.InvokeKeyDown(key);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Selector.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace PowerAccent.UI;
4 |
5 | public partial class Selector : Window
6 | {
7 | public Selector(char[] selectedCharacters)
8 | {
9 | InitializeComponent();
10 | this.ShowActivated = false;
11 | this.Topmost = true;
12 | characters.ItemsSource = selectedCharacters;
13 | characters.SelectedIndex = 0;
14 | }
15 |
16 | public void SetIndex(int index)
17 | {
18 | characters.SelectedIndex = index;
19 | }
20 |
21 | public void SetPosition(double left, double top)
22 | {
23 | this.Left = left;
24 | this.Top = top;
25 | }
26 |
27 | public void SetBorderWindowAlignment(bool? isLeft)
28 | {
29 | gridBorder.HorizontalAlignment = isLeft switch
30 | {
31 | true => HorizontalAlignment.Left,
32 | false => HorizontalAlignment.Right,
33 | _ => HorizontalAlignment.Center,
34 | };
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/ModuleHandler.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Services;
2 |
3 | namespace PowerAccent.Core.ModuleHandlers;
4 |
5 | internal abstract class ModuleHandler
6 | {
7 | public ModuleHandler(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options)
8 | {
9 | PowerAccent = powerAccent;
10 | SettingsService = settingsService;
11 | Options = options;
12 | }
13 |
14 | protected ModuleHandler _next;
15 |
16 | public PowerAccent PowerAccent { get; }
17 | public SettingsService SettingsService { get; }
18 | public KeyOptions Options { get; }
19 |
20 | public ModuleHandler SetNext(ModuleHandler next)
21 | {
22 | _next = next;
23 | return next;
24 | }
25 |
26 | public virtual bool InvokeKeyDown(uint key)
27 | {
28 | return _next?.InvokeKeyDown(key) ?? true;
29 | }
30 |
31 | public virtual bool InvokeKeyUp(uint key)
32 | {
33 | return _next?.InvokeKeyUp(key) ?? true;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Damien Leroy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Models/Point.cs:
--------------------------------------------------------------------------------
1 | namespace PowerAccent.Core;
2 |
3 | public struct Point
4 | {
5 | public Point()
6 | {
7 | X = 0;
8 | Y = 0;
9 | }
10 |
11 | public Point(double x, double y)
12 | {
13 | X = x;
14 | Y = y;
15 | }
16 |
17 | public Point(int x, int y)
18 | {
19 | X = x;
20 | Y = y;
21 | }
22 |
23 | public Point(System.Drawing.Point point)
24 | {
25 | X = point.X;
26 | Y = point.Y;
27 | }
28 |
29 | public double X { get; init; }
30 | public double Y { get; init; }
31 |
32 | public static implicit operator Point(System.Drawing.Point point) => new Point(point.X, point.Y);
33 |
34 | public static Point operator /(Point point, double divider)
35 | {
36 | if (divider == 0)
37 | {
38 | throw new DivideByZeroException();
39 | }
40 | return new Point(point.X / divider, point.Y / divider);
41 | }
42 |
43 | public static Point operator /(Point point, Point divider)
44 | {
45 | if (divider.X == 0 || divider.Y == 0)
46 | {
47 | throw new DivideByZeroException();
48 | }
49 | return new Point(point.X / divider.X, point.Y / divider.Y);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/HighContrast1.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | HighContrast.Accent2
8 | PowerAccent
9 | Accent2 (HighContrast)
10 | HighContrast
11 | Accent2
12 | White
13 |
14 |
15 |
19 |
20 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/HighContrast2.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | HighContrast.Accent3
8 | PowerAccent
9 | Accent3 (HighContrast)
10 | HighContrast
11 | Accent3
12 | White
13 |
14 |
15 |
19 |
20 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/HighContrastBlack.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | HighContrast.Accent4
8 | PowerAccent
9 | Accent4 (HighContrast)
10 | HighContrast
11 | Accent4
12 | White
13 |
14 |
15 |
19 |
20 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/HighContrastWhite.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | HighContrast.Accent5
8 | PowerAccent
9 | Accent5 (HighContrast)
10 | HighContrast
11 | Accent5
12 | White
13 |
14 |
15 |
19 |
20 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/Dark.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | Dark.Accent1
8 | PowerAccent
9 | Accent1 (Dark)
10 | Dark
11 | Accent1
12 | Black
13 |
14 |
15 |
19 |
23 |
27 |
28 |
32 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Models/Size.cs:
--------------------------------------------------------------------------------
1 | namespace PowerAccent.Core;
2 |
3 | public struct Size
4 | {
5 | public Size()
6 | {
7 | Width = 0;
8 | Height = 0;
9 | }
10 |
11 | public Size(double width, double height)
12 | {
13 | Width = width;
14 | Height = height;
15 | }
16 |
17 | public Size(int width, int height)
18 | {
19 | Width = width;
20 | Height = height;
21 | }
22 |
23 | public double Width { get; init; }
24 | public double Height { get; init; }
25 |
26 | public static implicit operator Size(System.Drawing.Size size) => new Size(size.Width, size.Height);
27 |
28 | public static Size operator /(Size size, double divider)
29 | {
30 | if (divider == 0)
31 | {
32 | throw new DivideByZeroException();
33 | }
34 | return new Size(size.Width / divider, size.Height / divider);
35 | }
36 |
37 | public static Size operator /(Size size, Size divider)
38 | {
39 | if (divider.Width == 0 || divider.Height == 0 || divider.Width == 0 || divider.Height == 0)
40 | {
41 | throw new DivideByZeroException();
42 | }
43 | return new Size(size.Width / divider.Width, size.Height / divider.Height);
44 | }
45 |
46 | public override string ToString()
47 | {
48 | return $"Width: {Width}, Height: {Height}";
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/Light.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | Light.Accent1
8 | PowerAccent
9 | Accent1 (Light)
10 | Light
11 | Accent1
12 | White
13 |
14 |
15 |
19 |
23 |
27 |
31 |
35 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Models/KeysEnums.cs:
--------------------------------------------------------------------------------
1 | using Vanara.PInvoke;
2 |
3 | namespace PowerAccent.Core;
4 |
5 | public enum LetterKey : uint
6 | {
7 | _0 = User32.VK.VK_0,
8 | _1 = User32.VK.VK_1,
9 | _2 = User32.VK.VK_2,
10 | _3 = User32.VK.VK_3,
11 | _4 = User32.VK.VK_4,
12 | _5 = User32.VK.VK_5,
13 | _6 = User32.VK.VK_6,
14 | _7 = User32.VK.VK_7,
15 | _8 = User32.VK.VK_8,
16 | _9 = User32.VK.VK_9,
17 | A = User32.VK.VK_A,
18 | B = User32.VK.VK_B,
19 | C = User32.VK.VK_C,
20 | D = User32.VK.VK_D,
21 | E = User32.VK.VK_E,
22 | F = User32.VK.VK_F,
23 | G = User32.VK.VK_G,
24 | H = User32.VK.VK_H,
25 | I = User32.VK.VK_I,
26 | J = User32.VK.VK_J,
27 | K = User32.VK.VK_K,
28 | L = User32.VK.VK_L,
29 | M = User32.VK.VK_M,
30 | N = User32.VK.VK_N,
31 | O = User32.VK.VK_O,
32 | P = User32.VK.VK_P,
33 | R = User32.VK.VK_R,
34 | S = User32.VK.VK_S,
35 | T = User32.VK.VK_T,
36 | U = User32.VK.VK_U,
37 | V = User32.VK.VK_V,
38 | W = User32.VK.VK_W,
39 | X = User32.VK.VK_X,
40 | Y = User32.VK.VK_Y,
41 | Z = User32.VK.VK_Z,
42 | _ = User32.VK.VK_OEM_COMMA,
43 | }
44 |
45 | public enum TriggerKey : uint
46 | {
47 | Left = User32.VK.VK_LEFT,
48 | Right = User32.VK.VK_RIGHT,
49 | Space = User32.VK.VK_SPACE,
50 | }
51 |
52 | public enum BackwardKey : uint
53 | {
54 | LeftShift = User32.VK.VK_LSHIFT,
55 | RightShift = User32.VK.VK_RSHIFT,
56 | }
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/CustomModules/DisplaySelectorModuleHandler.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Services;
2 | using PowerAccent.Core.Tools;
3 | using System.Diagnostics;
4 |
5 | namespace PowerAccent.Core.ModuleHandlers.CustomModules;
6 |
7 | internal class DisplaySelectorModuleHandler : ModuleHandler
8 | {
9 | public DisplaySelectorModuleHandler(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options) : base(powerAccent, settingsService, options)
10 | {
11 | }
12 |
13 | public override bool InvokeKeyDown(uint key)
14 | {
15 | if (!Options.IsVisible && Options.LetterPressed.HasValue && Options.TriggerPressed.HasValue)
16 | {
17 | Debug.WriteLine($"InvokeKeyDown DisplaySelectorModuleHandler - Begin delay {SettingsService.InputTime}");
18 | Options.IsVisible = true;
19 | Task.Delay(SettingsService.InputTime).ContinueWith(t =>
20 | {
21 | Debug.WriteLine($"InvokeKeyDown DisplaySelectorModuleHandler - End delay. Visible: {Options.IsVisible}");
22 | if (Options.IsVisible && WindowsFunctions.IsKeyPressed(Options.LetterPressed.Value))
23 | {
24 | Options.IsDelayOk = true;
25 | PowerAccent.ChangeDisplay(true, Options.Characters);
26 | }
27 |
28 | }, TaskScheduler.FromCurrentSynchronizationContext());
29 | }
30 |
31 | if (Options.CancelTrigger)
32 | {
33 | Options.CancelTrigger = false;
34 | return false;
35 | }
36 |
37 | return base.InvokeKeyDown(key);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Models/Rect.cs:
--------------------------------------------------------------------------------
1 | namespace PowerAccent.Core;
2 |
3 | public struct Rect
4 | {
5 | public Rect()
6 | {
7 | X = 0;
8 | Y = 0;
9 | Width = 0;
10 | Height = 0;
11 | }
12 |
13 | public Rect(int x, int y, int width, int height)
14 | {
15 | X = x;
16 | Y = y;
17 | Width = width;
18 | Height = height;
19 | }
20 |
21 | public Rect(double x, double y, double width, double height)
22 | {
23 | X = x;
24 | Y = y;
25 | Width = width;
26 | Height = height;
27 | }
28 |
29 | public Rect(Point coord, Size size)
30 | {
31 | X = coord.X;
32 | Y = coord.Y;
33 | Width = size.Width;
34 | Height = size.Height;
35 | }
36 |
37 | public double X { get; init; }
38 | public double Y { get; init; }
39 | public double Width { get; init; }
40 | public double Height { get; init; }
41 |
42 | public static Rect operator /(Rect rect, double divider)
43 | {
44 | if (divider == 0)
45 | {
46 | throw new DivideByZeroException();
47 | }
48 | return new Rect(rect.X / divider, rect.Y / divider, rect.Width / divider, rect.Height / divider);
49 | }
50 |
51 | public static Rect operator /(Rect rect, Rect divider)
52 | {
53 | if (divider.X == 0 || divider.Y == 0)
54 | {
55 | throw new DivideByZeroException();
56 | }
57 | return new Rect(rect.X / divider.X, rect.Y / divider.Y, rect.Width / divider.Width, rect.Height / divider.Height);
58 | }
59 |
60 | public override string ToString()
61 | {
62 | return $"X: {X}, Y: {Y}, Width: {Width}, Height: {Height}";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/PowerAccent.UI/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.UI.Themes;
2 | using System;
3 | using System.Threading;
4 | using System.Windows;
5 |
6 | namespace PowerAccent.UI
7 | {
8 | ///
9 | /// Interaction logic for App.xaml
10 | ///
11 | public partial class App : Application
12 | {
13 | private static Mutex _mutex = null;
14 | private bool _disposed;
15 | private ThemeManager _themeManager;
16 |
17 | protected override void OnStartup(StartupEventArgs e)
18 | {
19 | const string appName = "PowerAccent";
20 | bool createdNew;
21 |
22 | _mutex = new Mutex(true, appName, out createdNew);
23 |
24 | if (!createdNew)
25 | {
26 | //app is already running! Exiting the application
27 | Application.Current.Shutdown();
28 | }
29 |
30 | _themeManager = new ThemeManager();
31 |
32 | base.OnStartup(e);
33 | }
34 |
35 | // dispose
36 | protected override void OnExit(ExitEventArgs e)
37 | {
38 | _mutex.ReleaseMutex();
39 | base.OnExit(e);
40 | }
41 |
42 | protected virtual void Dispose(bool disposing)
43 | {
44 | if (_disposed)
45 | {
46 | return;
47 | }
48 |
49 | if (disposing)
50 | {
51 | _mutex?.Dispose();
52 | _themeManager?.Dispose();
53 | }
54 |
55 | _disposed = true;
56 | }
57 |
58 | public void Dispose()
59 | {
60 | Dispose(disposing: true);
61 | GC.SuppressFinalize(this);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Settings.xaml.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.UI.SettingsPage;
2 | using System;
3 | using System.Windows;
4 | using System.Windows.Input;
5 |
6 | namespace PowerAccent.UI
7 | {
8 | ///
9 | /// Logique d'interaction pour Settings.xaml
10 | ///
11 | public partial class Settings : Window
12 | {
13 | public Settings()
14 | {
15 | InitializeComponent();
16 | }
17 |
18 | protected override void OnInitialized(EventArgs e)
19 | {
20 | base.OnInitialized(e);
21 | Countries.IsChecked = true;
22 | }
23 |
24 | private void Countries_Checked(object sender, RoutedEventArgs e)
25 | {
26 | Position.IsChecked = false;
27 | Options.IsChecked = false;
28 | Sort.IsChecked = false;
29 | this.ParentFrame.Navigate(new CountriesPage());
30 | }
31 |
32 | private void Position_Checked(object sender, RoutedEventArgs e)
33 | {
34 | Countries.IsChecked = false;
35 | Options.IsChecked = false;
36 | Sort.IsChecked = false;
37 | this.ParentFrame.Navigate(new PositionPage());
38 | }
39 |
40 | private void Options_Checked(object sender, RoutedEventArgs e)
41 | {
42 | Countries.IsChecked = false;
43 | Position.IsChecked = false;
44 | Sort.IsChecked = false;
45 | this.ParentFrame.Navigate(new OptionsPage());
46 | }
47 |
48 | private void Sort_Checked(object sender, RoutedEventArgs e)
49 | {
50 | Countries.IsChecked = false;
51 | Options.IsChecked = false;
52 | Position.IsChecked = false;
53 | this.ParentFrame.Navigate(new SortPage());
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/CustomModules/StrokeArrowsModuleHandler.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Extensions;
2 | using PowerAccent.Core.Services;
3 | using PowerAccent.Core.Tools;
4 | using System.Diagnostics;
5 |
6 | namespace PowerAccent.Core.ModuleHandlers.CustomModules;
7 |
8 | internal class StrokeArrowsModuleHandler : ModuleHandler
9 | {
10 | public StrokeArrowsModuleHandler(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options) : base(powerAccent, settingsService, options)
11 | {
12 | }
13 |
14 | public override bool InvokeKeyDown(uint key)
15 | {
16 | if (Options.LetterPressed.HasValue && (key == (uint)TriggerKey.Left || key == (uint)TriggerKey.Right))
17 | {
18 | Options.TriggerPressed = (TriggerKey)key;
19 | Debug.WriteLine($"InvokeKeyDown StrokeArrowsModuleHandler - Key: {(TriggerKey)key}");
20 | if (!Options.IsVisible)
21 | {
22 | if (!WindowsFunctions.IsKeyPressed(Options.LetterPressed.Value))
23 | {
24 | Options.Reset();
25 | return true;
26 | }
27 | Options.Characters = WindowsFunctions.IsCapitalState()
28 | ? SettingsService.GetLetterKey(Options.LetterPressed.Value).ToUpper()
29 | : SettingsService.GetLetterKey(Options.LetterPressed.Value);
30 | }
31 |
32 | if (Options.Characters == Array.Empty())
33 | {
34 | Debug.WriteLine($"InvokeKeyDown StrokeArrowsModuleHandler - No characters for key: {Options.LetterPressed.Value}");
35 | Options.Reset();
36 | return true;
37 | }
38 |
39 | Options.SelectedIndex += key == (uint)TriggerKey.Left ? -1 : 1;
40 |
41 | if (Options.SelectedIndex < 0) Options.SelectedIndex = Options.Characters.Length - 1;
42 | if (Options.SelectedIndex > Options.Characters.Length - 1) Options.SelectedIndex = 0;
43 | Debug.WriteLine($"InvokeKeyDown StrokeArrowsModuleHandler - SelectedIndex: {Options.SelectedIndex}");
44 | PowerAccent.SelectCharacter(Options.SelectedIndex);
45 | Options.CancelTrigger = true;
46 | }
47 |
48 | return base.InvokeKeyDown(key);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/CustomModules/StrokeLetterModuleHandler.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Services;
2 | using PowerAccent.Core.Tools;
3 | using System.Diagnostics;
4 |
5 | namespace PowerAccent.Core.ModuleHandlers.CustomModules
6 | {
7 | internal class StrokeLetterModuleHandler : ModuleHandler
8 | {
9 | public StrokeLetterModuleHandler(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options) : base(powerAccent, settingsService, options)
10 | {
11 | }
12 |
13 | public override bool InvokeKeyDown(uint key)
14 | {
15 | if (Enum.IsDefined(typeof(LetterKey), key))
16 | {
17 | Options.LetterPressed = (LetterKey)key;
18 | Debug.WriteLine($"InvokeKeyDown StrokeLetterModuleHandler - Key: {(LetterKey)key}");
19 | }
20 |
21 | return base.InvokeKeyDown(key);
22 | }
23 |
24 | public override bool InvokeKeyUp(uint key)
25 | {
26 | Debug.WriteLine($"InvokeKeyUp StrokeLetterModuleHandler - KeyUp: {(LetterKey)key}");
27 | if (Enum.IsDefined(typeof(LetterKey), key))
28 | {
29 | Options.LetterPressed = null;
30 | if (Options.IsVisible)
31 | {
32 | PowerAccent.ChangeDisplay(false, null);
33 |
34 | if (!Options.IsDelayOk)
35 | {
36 | Debug.WriteLine("InvokeKeyUp StrokeLetterModuleHandler - Delay not ok");
37 | if (Options.TriggerPressed == TriggerKey.Space)
38 | WindowsFunctions.Insert(' ');
39 | Options.Reset();
40 | return false;
41 | }
42 |
43 | Debug.WriteLine($"InvokeKeyUp StrokeLetterModuleHandler - KeyUp: {(LetterKey)key} - {Options.SelectedIndex} - {Options.Characters.Length}");
44 | if (Options.SelectedIndex != -1)
45 | WindowsFunctions.Insert(Options.Characters[Options.SelectedIndex], true);
46 | if (SettingsService.InsertSpaceAfterSelection)
47 | WindowsFunctions.Insert(' ', false);
48 |
49 | Options.Reset();
50 | }
51 | }
52 |
53 | return base.InvokeKeyUp(key);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Tools/Calculation.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Services;
2 |
3 | namespace PowerAccent.Core.Tools
4 | {
5 | internal static class Calculation
6 | {
7 | public static Point GetRawCoordinatesFromCaret(Point caret, Rect screen, Size window)
8 | {
9 | var left = caret.X - window.Width / 2;
10 | var top = caret.Y - window.Height - 20;
11 |
12 | return new Point(left < screen.X ? screen.X : (left + window.Width > (screen.X + screen.Width) ? (screen.X + screen.Width) - window.Width : left)
13 | , top < screen.Y ? caret.Y + 20 : top);
14 | }
15 |
16 | public static Point GetRawCoordinatesFromPosition(Position position, Rect screen, Size window)
17 | {
18 | int offset = 10;
19 |
20 | double pointX = position switch
21 | {
22 | var x when
23 | x == Position.Top ||
24 | x == Position.Bottom ||
25 | x == Position.Center
26 | => screen.X + screen.Width / 2 - window.Width / 2,
27 | var x when
28 | x == Position.TopLeft ||
29 | x == Position.Left ||
30 | x == Position.BottomLeft
31 | => screen.X + offset,
32 | var x when
33 | x == Position.TopRight ||
34 | x == Position.Right ||
35 | x == Position.BottomRight
36 | => screen.X + screen.Width - (window.Width + offset),
37 | };
38 |
39 | double pointY = position switch
40 | {
41 | var x when
42 | x == Position.TopLeft ||
43 | x == Position.Top ||
44 | x == Position.TopRight
45 | => screen.Y + offset,
46 | var x when
47 | x == Position.Left ||
48 | x == Position.Center ||
49 | x == Position.Right
50 | => screen.Y + screen.Height / 2 - window.Height / 2,
51 | var x when
52 | x == Position.BottomLeft ||
53 | x == Position.Bottom ||
54 | x == Position.BottomRight
55 | => screen.Y + screen.Height - (window.Height + offset),
56 | };
57 |
58 | return new Point(pointX, pointY);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/OptionsPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using ModernWpf.Controls;
2 | using PowerAccent.Core;
3 | using PowerAccent.Core.Services;
4 | using System;
5 | using System.Globalization;
6 | using System.Linq;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 | using System.Windows.Media.Imaging;
11 |
12 | namespace PowerAccent.UI.SettingsPage;
13 |
14 | ///
15 | /// Logique d'interaction pour OptionsPage.xaml
16 | ///
17 | public partial class OptionsPage : System.Windows.Controls.Page
18 | {
19 | private readonly SettingsService _settingService = new SettingsService();
20 |
21 | public OptionsPage()
22 | {
23 | InitializeComponent();
24 | }
25 |
26 | protected override void OnInitialized(EventArgs e)
27 | {
28 | base.OnInitialized(e);
29 |
30 | IsUseCaretPosition.IsOn = _settingService.UseCaretPosition;
31 | IsSpaceBarActive.IsOn = _settingService.IsSpaceBarActive;
32 | DisableInFullScreen.IsOn = _settingService.DisableInFullScreen;
33 | InputTime.Value = _settingService.InputTime;
34 | }
35 |
36 | private void UseCaretPosition_Checked(object sender, RoutedEventArgs e)
37 | {
38 | _settingService.UseCaretPosition = ((ToggleSwitch)sender).IsOn;
39 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
40 | }
41 |
42 | private void SpaceBarActive_Checked(object sender, RoutedEventArgs e)
43 | {
44 | _settingService.IsSpaceBarActive = ((ToggleSwitch)sender).IsOn;
45 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
46 | }
47 |
48 | private void DisableInFullScreen_Toggled(object sender, RoutedEventArgs e)
49 | {
50 | _settingService.DisableInFullScreen = ((ToggleSwitch)sender).IsOn;
51 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
52 | }
53 |
54 | private void InsertSpaceAfterSelection_Toggled(object sender, RoutedEventArgs e)
55 | {
56 | _settingService.InsertSpaceAfterSelection = ((ToggleSwitch)sender).IsOn;
57 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
58 | }
59 |
60 | private void InputTime_ValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args)
61 | {
62 | int value = (int)args.NewValue;
63 | _settingService.InputTime = value >= 0 ? value : 200;
64 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
65 | }
66 | }
--------------------------------------------------------------------------------
/PowerAccent.Wix/Product.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | NOT REMOVE
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # How it works
2 |
3 | Press and hold a letter that supports diacritic marks, then press space bar or arrow key to select the accent. For example E + Space. With spacebar, repeat presses to change accent mark. When releasing the letter key, the accented letter is inserted.
4 |
5 | 
6 | > Note: Some characters (like `¡` for Spanish are available with the comma (`,`) key).
7 |
8 | The software is currently working with the most of accents for several (Latin script based) countries, including **Czech**, **German**, **France**, **Maori** and many others. The list can be found in [Languages.cs](/PowerAccent.Core/Languages.cs).
9 | If any language is missing, don't hesitate to [create an issue][newissue].
10 |
11 | All letters are sorted by usage frequency by default, as found on [Wikipedia](https://wikipedia.org/wiki/Letter_frequency).
12 |
13 | # Download
14 |
15 | See the [Releases][releases] page.
16 |
17 | # Known problems
18 |
19 | - Some keys can have interference with some actions or some tools (for example: AutoHotkey)
20 | - (Experimental feature) Inside browsers and some other software, the tool can't detect the caret position. Default position is applied, meaning the one you choose in the position settings.
21 | - Mathematical symbols are often requested. But for several reasons (technical and functional), this feature can't be implemented now in the tool. So currently mathematical symbol will not be accepted (cf: [issue](https://github.com/damienleroy/PowerAccent/issues/23#issuecomment-1301532208)).
22 |
23 | # The future
24 |
25 | - More letters and accents
26 | - Theme and design improvement
27 | - Some other [asked features][issues]
28 |
29 | # PowerToys
30 | PowerAccent has been implemented in [PowerToys](https://github.com/microsoft/PowerToys) and renamed as [QuickAccent](https://aka.ms/PowerToysOverview_QuickAccent). Both have globally the same features and offer different experiences in the usage (taskbar icon, and different settings view).
31 |
32 | # If you like it
33 |
34 | Buy me a coffee. ☕
35 | https://www.buymeacoffee.com/dams
36 |
37 | # Thanks to
38 |
39 | - [Ciantic](https://gist.github.com/Ciantic/471698) for the implementation of the Keyboard Listener.
40 | - [Saurabh Singh](https://www.codeproject.com/Articles/34520/Getting-Caret-Position-Inside-Any-Application) to share how get the caret position.
41 | - [PowerToys team](https://github.com/microsoft/PowerToys) to integrated PowerAccent in PowerToys (named as Quick Accent).
42 |
43 | [issues]: https://github.com/damienleroy/PowerAccent/issues
44 | [newissue]: https://github.com/damienleroy/PowerAccent/issues/new
45 | [releases]: https://github.com/damienleroy/PowerAccent/releases
46 |
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/ModuleDirector.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.ModuleHandlers.CustomModules;
2 | using PowerAccent.Core.Services;
3 | using System.Diagnostics;
4 |
5 | namespace PowerAccent.Core.ModuleHandlers;
6 |
7 | // Chain of responsability pattern
8 | internal class ModuleDirector
9 | {
10 | private ModuleHandler _moduleHandler;
11 |
12 | public ModuleDirector(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options)
13 | {
14 | _moduleHandler = new PauseModuleHandler(powerAccent, settingsService, options);
15 | _moduleHandler
16 | .SetNext(new CheckGameModeModuleHandler(powerAccent, settingsService, options))
17 | .SetNext(new StrokeLetterModuleHandler(powerAccent, settingsService, options))
18 | .SetNext(new StrokeArrowsModuleHandler(powerAccent, settingsService, options))
19 | .SetNext(new StrokeSpaceModuleHandler(powerAccent, settingsService, options))
20 | .SetNext(new DisplaySelectorModuleHandler(powerAccent, settingsService, options))
21 | ;
22 | }
23 |
24 | public bool InvokeKeyDown(uint key)
25 | {
26 | try
27 | {
28 | return _moduleHandler.InvokeKeyDown(key);
29 | }
30 | catch (Exception e)
31 | {
32 | Console.WriteLine($"Error in InvokeKeyDown: {e.Message}");
33 | Console.WriteLine(e.StackTrace);
34 | throw;
35 | }
36 | }
37 |
38 | public bool InvokeKeyUp(uint key)
39 | {
40 | try
41 | {
42 | return _moduleHandler.InvokeKeyUp(key);
43 | }
44 | catch (Exception e)
45 | {
46 | Console.WriteLine($"Error in InvokeKeyUp: {e.Message}");
47 | Console.WriteLine(e.StackTrace);
48 | throw;
49 | }
50 | }
51 | }
52 |
53 | internal record KeyOptions
54 | {
55 | public LetterKey? LetterPressed { get; set; } = default!;
56 | public TriggerKey? TriggerPressed { get; set; } = default!;
57 | public bool IsVisible { get; set; } = default!;
58 | public bool IsDelayOk { get; set; } = default!;
59 | public bool IsBackwardShiftPressed { get; set; } = default!;
60 | public int SelectedIndex { get; set; } = -1;
61 | public char[] Characters { get; set; } = Array.Empty();
62 | public bool CancelTrigger { get; set; } = default!;
63 |
64 | public void Reset()
65 | {
66 | Debug.WriteLine("Reset");
67 | LetterPressed = null;
68 | TriggerPressed = null;
69 | IsVisible = false;
70 | IsDelayOk = false;
71 | IsBackwardShiftPressed = false;
72 | SelectedIndex = -1;
73 | Characters = Array.Empty();
74 | CancelTrigger = false;
75 | }
76 | }
--------------------------------------------------------------------------------
/PowerAccent.Core/ModuleHandlers/CustomModules/StrokeSpaceModuleHandler.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Extensions;
2 | using PowerAccent.Core.Services;
3 | using PowerAccent.Core.Tools;
4 | using System.Diagnostics;
5 |
6 | namespace PowerAccent.Core.ModuleHandlers.CustomModules;
7 |
8 | internal class StrokeSpaceModuleHandler : ModuleHandler
9 | {
10 | public StrokeSpaceModuleHandler(PowerAccent powerAccent, SettingsService settingsService, KeyOptions options) : base(powerAccent, settingsService, options)
11 | {
12 | }
13 |
14 | public override bool InvokeKeyDown(uint key)
15 | {
16 | if (Options.LetterPressed.HasValue && SettingsService.IsSpaceBarActive && key == (uint)TriggerKey.Space)
17 | {
18 | Options.TriggerPressed = (TriggerKey)key;
19 |
20 | if (Enum.IsDefined(typeof(BackwardKey), key) && !Options.IsBackwardShiftPressed)
21 | Options.IsBackwardShiftPressed = true;
22 | Debug.WriteLine($"InvokeKeyDown StrokeSpaceModuleHandler - Key: {(TriggerKey)key}, Backward: {Options.IsBackwardShiftPressed}");
23 |
24 | if (!Options.IsVisible)
25 | {
26 | if (!WindowsFunctions.IsKeyPressed(Options.LetterPressed.Value))
27 | {
28 | Options.Reset();
29 | return true;
30 | }
31 | Options.Characters = WindowsFunctions.IsCapitalState()
32 | ? SettingsService.GetLetterKey(Options.LetterPressed.Value).ToUpper()
33 | : SettingsService.GetLetterKey(Options.LetterPressed.Value);
34 | }
35 |
36 | if (Options.Characters.Length == 0)
37 | {
38 | Debug.WriteLine($"InvokeKeyDown StrokeSpaceModuleHandler - No characters found for {Options.LetterPressed.Value}");
39 | Options.Reset();
40 | return true;
41 | }
42 |
43 | Options.SelectedIndex += Options.IsBackwardShiftPressed ? -1 : 1;
44 |
45 | if (Options.SelectedIndex < 0) Options.SelectedIndex = Options.Characters.Length - 1;
46 | if (Options.SelectedIndex > Options.Characters.Length - 1) Options.SelectedIndex = 0;
47 | Debug.WriteLine($"InvokeKeyDown StrokeSpaceModuleHandler - SelectedIndex: {Options.SelectedIndex}");
48 | PowerAccent.SelectCharacter(Options.SelectedIndex);
49 | Options.CancelTrigger = true;
50 | }
51 |
52 | if (Options.IsVisible && Enum.IsDefined(typeof(BackwardKey), key))
53 | Options.IsBackwardShiftPressed = true;
54 |
55 | return base.InvokeKeyDown(key);
56 | }
57 |
58 | public override bool InvokeKeyUp(uint key)
59 | {
60 | if (Options.IsBackwardShiftPressed && Enum.IsDefined(typeof(BackwardKey), key))
61 | Options.IsBackwardShiftPressed = false;
62 |
63 | return base.InvokeKeyUp(key);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Settings.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/PositionPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core.Services;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 |
5 | namespace PowerAccent.UI.SettingsPage
6 | {
7 | ///
8 | /// Logique d'interaction pour Position.xaml
9 | ///
10 | public partial class PositionPage : Page
11 | {
12 | private readonly SettingsService _settingService = new SettingsService();
13 |
14 | public PositionPage()
15 | {
16 | InitializeComponent();
17 | RefreshPosition();
18 | }
19 |
20 | private void Position_Up_Checked(object sender, RoutedEventArgs e)
21 | {
22 | _settingService.Position = Position.Top;
23 | RefreshPosition();
24 | }
25 | private void Position_Down_Checked(object sender, RoutedEventArgs e)
26 | {
27 | _settingService.Position = Position.Bottom;
28 | RefreshPosition();
29 | }
30 | private void Position_Left_Checked(object sender, RoutedEventArgs e)
31 | {
32 | _settingService.Position = Position.Left;
33 | RefreshPosition();
34 | }
35 | private void Position_Right_Checked(object sender, RoutedEventArgs e)
36 | {
37 | _settingService.Position = Position.Right;
38 | RefreshPosition();
39 | }
40 | private void Position_UpLeft_Checked(object sender, RoutedEventArgs e)
41 | {
42 | _settingService.Position = Position.TopLeft;
43 | RefreshPosition();
44 | }
45 | private void Position_UpRight_Checked(object sender, RoutedEventArgs e)
46 | {
47 | _settingService.Position = Position.TopRight;
48 | RefreshPosition();
49 | }
50 | private void Position_DownLeft_Checked(object sender, RoutedEventArgs e)
51 | {
52 | _settingService.Position = Position.BottomLeft;
53 | RefreshPosition();
54 | }
55 | private void Position_DownRight_Checked(object sender, RoutedEventArgs e)
56 | {
57 | _settingService.Position = Position.BottomRight;
58 | RefreshPosition();
59 | }
60 | private void Position_Center_Checked(object sender, RoutedEventArgs e)
61 | {
62 | _settingService.Position = Position.Center;
63 | RefreshPosition();
64 | }
65 |
66 | private void RefreshPosition()
67 | {
68 | var position = _settingService.Position;
69 | Position_Up.IsChecked = position == Position.Top;
70 | Position_Down.IsChecked = position == Position.Bottom;
71 | Position_Left.IsChecked = position == Position.Left;
72 | Position_Right.IsChecked = position == Position.Right;
73 | Position_UpRight.IsChecked = position == Position.TopRight;
74 | Position_UpLeft.IsChecked = position == Position.TopLeft;
75 | Position_DownRight.IsChecked = position == Position.BottomRight;
76 | Position_DownLeft.IsChecked = position == Position.BottomLeft;
77 | Position_Center.IsChecked = position == Position.Center;
78 |
79 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/PowerAccent.UI/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Forms;
5 | using Point = PowerAccent.Core.Point;
6 | using Size = PowerAccent.Core.Size;
7 | using Application = System.Windows.Application;
8 | using System.Collections.Generic;
9 |
10 | namespace PowerAccent.UI;
11 |
12 | public partial class MainWindow : Window
13 | {
14 | private Core.PowerAccent _powerAccent = new Core.PowerAccent();
15 | private Selector _selector;
16 | private Stack _selectorStack = new Stack();
17 |
18 | public MainWindow()
19 | {
20 | InitializeComponent();
21 | }
22 |
23 | protected override void OnSourceInitialized(EventArgs e)
24 | {
25 | base.OnSourceInitialized(e);
26 | _powerAccent.OnChangeDisplay += PowerAccent_OnChangeDisplay;
27 | _powerAccent.OnSelectCharacter += PowerAccent_OnSelectionCharacter;
28 | this.Visibility = Visibility.Hidden;
29 | _powerAccent.CheckVersion();
30 | }
31 |
32 | private void PowerAccent_OnSelectionCharacter(int index)
33 | {
34 | _selector?.SetIndex(index);
35 | }
36 |
37 | private void PowerAccent_OnChangeDisplay(bool isActive, char[] chars)
38 | {
39 | if (isActive)
40 | {
41 | Selector selector = new Selector(chars);
42 | selector.Show();
43 | CenterWindow(selector);
44 | _selectorStack.Push(selector);
45 | _selector = selector;
46 | }
47 | else
48 | {
49 | while (_selectorStack.Count > 0)
50 | {
51 | _selectorStack.Pop().Close();
52 | }
53 | }
54 | }
55 |
56 | private void CenterWindow(Selector selector)
57 | {
58 | Size window = new Size(((System.Windows.Controls.Panel)selector.Content).ActualWidth, ((System.Windows.Controls.Panel)selector.Content).ActualHeight);
59 | double primaryDPI = Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth;
60 | Point position = _powerAccent.GetDisplayCoordinates(window, primaryDPI);
61 | selector.SetPosition(position.X, position.Y);
62 | selector.SetBorderWindowAlignment(_powerAccent.IsLeftPosition());
63 | }
64 |
65 | #region TaskBar
66 | private void Settings_Click(object sender, RoutedEventArgs e)
67 | {
68 | Settings settings = new Settings();
69 | settings.Show();
70 | }
71 |
72 | private void MenuExit_Click(object sender, RoutedEventArgs e)
73 | {
74 | Application.Current.Shutdown();
75 | }
76 |
77 | private void Pause_Click(object sender, RoutedEventArgs e)
78 | {
79 | _powerAccent.IsPaused = !_powerAccent.IsPaused;
80 | ((MenuItem)sender).FontWeight = _powerAccent.IsPaused ? FontWeights.Bold : FontWeights.Thin;
81 | }
82 |
83 | #endregion
84 |
85 | public void RefreshSettings()
86 | {
87 | _powerAccent.ReloadSettings();
88 | }
89 |
90 | protected override void OnClosed(EventArgs e)
91 | {
92 | _powerAccent.Dispose();
93 | base.OnClosed(e);
94 | }
95 |
96 | public void Dispose()
97 | {
98 | _powerAccent.Dispose();
99 | GC.SuppressFinalize(this);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/CountriesPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core;
2 | using PowerAccent.Core.Services;
3 | using System;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Media.Imaging;
10 |
11 | namespace PowerAccent.UI.SettingsPage;
12 |
13 | ///
14 | /// Logique d'interaction pour CountriesPage.xaml
15 | ///
16 | public partial class CountriesPage : Page
17 | {
18 | private readonly SettingsService _settingService = new SettingsService();
19 |
20 |
21 | public CountriesPage()
22 | {
23 | InitializeComponent();
24 | }
25 |
26 | protected override void OnInitialized(EventArgs e)
27 | {
28 | base.OnInitialized(e);
29 |
30 | RefreshData();
31 | }
32 |
33 | private void RefreshData()
34 | {
35 | Countries.ItemsSource = Enum.GetNames().Select(l => new Country
36 | {
37 | Name = l,
38 | ImageUrl = $"/Resources/Flags/{l}.jpg",
39 | IsChecked = _settingService.SelectedLanguages.Any(s => s.ToString() == l)
40 | }).ToList();
41 | }
42 |
43 | private void CheckBox_OnChanged(object sender, RoutedEventArgs e)
44 | {
45 | var selectedCountry = ((CheckBox)sender).DataContext as Country;
46 | var doRefresh = false;
47 | var selectedLanguages = Countries.Items.Cast().Where(c => c.IsChecked).Select(c => Enum.Parse(c.Name)).ToArray();
48 | if (selectedCountry.Name == Core.Language.ALL.ToString() && selectedCountry.IsChecked)
49 | {
50 | selectedLanguages = new[] { Core.Language.ALL };
51 | Countries.ItemsSource.Cast().ToList().ForEach(c => c.IsChecked = false);
52 | Countries.ItemsSource.Cast().First(c => c.Name == Core.Language.ALL.ToString()).IsChecked = true;
53 | doRefresh = true;
54 | }
55 | else if (selectedLanguages.Length == 0)
56 | {
57 | selectedLanguages = new[] { Core.Language.ALL };
58 | Countries.ItemsSource.Cast().First(c => c.Name == Core.Language.ALL.ToString()).IsChecked = true;
59 | doRefresh = true;
60 | }
61 | else if (selectedLanguages.Length > 1 && selectedLanguages.Any(s => s == Core.Language.ALL))
62 | {
63 | selectedLanguages = selectedLanguages.Where(s => s != Core.Language.ALL).ToArray();
64 | Countries.ItemsSource.Cast().First(c => c.Name == Core.Language.ALL.ToString()).IsChecked = false;
65 | doRefresh = true;
66 | }
67 |
68 | _settingService.SelectedLanguages = selectedLanguages;
69 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
70 | if (doRefresh)
71 | RefreshData();
72 | }
73 | }
74 |
75 | public class StringToImageSourceConverter : IValueConverter
76 | {
77 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
78 | {
79 | return new BitmapImage(new Uri("pack://application:,,,/PowerAccent;component/" + value.ToString(), UriKind.Absolute));
80 | }
81 |
82 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
83 | {
84 | throw new NotImplementedException();
85 | }
86 | }
--------------------------------------------------------------------------------
/PowerAccent.Wix/PowerAccent.Wix.SelfContained.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 3.10
7 | 0c31f068-83e6-48ed-ab58-4e8ebfe51d9a
8 | 2.0
9 | PowerAccent
10 | Package
11 | x64
12 | x64
13 |
14 |
15 | bin\$(Configuration)\
16 | obj\$(Configuration)\
17 | Debug
18 |
19 |
20 | bin\$(Configuration)\
21 | obj\$(Configuration)\
22 |
23 |
24 |
25 |
26 |
27 |
28 | PowerAccent
29 | {8f823b66-7b84-4d2e-b9f8-06846ddc5951}
30 | True
31 | True
32 | Binaries;Content;Satellites
33 | INSTALLFOLDER
34 | net7.0-windows10.0.19041.0
35 | win-x64
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/CountriesPage.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/PowerAccent.Core/PowerAccent.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using PowerAccent.Core.ModuleHandlers;
3 | using PowerAccent.Core.Services;
4 | using PowerAccent.Core.Tools;
5 |
6 | namespace PowerAccent.Core;
7 |
8 | public class PowerAccent : IDisposable
9 | {
10 | internal readonly SettingsService _settingService = new SettingsService();
11 | private readonly KeyboardListener _keyboardListener = new KeyboardListener();
12 | private readonly KeyOptions _moduleOptions = new KeyOptions();
13 | private readonly ModuleDirector _moduleDirector;
14 |
15 | public event Action OnChangeDisplay;
16 | public event Action OnSelectCharacter;
17 |
18 | public bool IsPaused { get; set; }
19 |
20 | public PowerAccent()
21 | {
22 | _moduleDirector = new ModuleDirector(this, _settingService, _moduleOptions);
23 | _keyboardListener.KeyDown += PowerAccent_KeyDown;
24 | _keyboardListener.KeyUp += PowerAccent_KeyUp;
25 | }
26 |
27 | private bool PowerAccent_KeyDown(object sender, KeyboardListener.RawKeyEventArgs args)
28 | {
29 | return _moduleDirector.InvokeKeyDown(args.Key);
30 | }
31 |
32 | private bool PowerAccent_KeyUp(object sender, KeyboardListener.RawKeyEventArgs args)
33 | {
34 | return _moduleDirector.InvokeKeyUp(args.Key);
35 | }
36 |
37 | public void ChangeDisplay(bool visible, char[] characters)
38 | {
39 | OnChangeDisplay?.Invoke(visible, characters);
40 | }
41 |
42 | public void SelectCharacter(int index)
43 | {
44 | OnSelectCharacter?.Invoke(index);
45 | }
46 |
47 | public Point GetDisplayCoordinates(Size window, double primaryDpi)
48 | {
49 | var activeDisplay = WindowsFunctions.GetActiveDisplay();
50 | Rect screen = new Rect(activeDisplay.Location, activeDisplay.Size) / primaryDpi;
51 | Position position = _settingService.Position;
52 |
53 | Debug.WriteLine($"Window {window} - Screen {screen}");
54 | Debug.WriteLine($"Primary Dpi: {primaryDpi} - Screen Dpi: {activeDisplay.Dpi}");
55 |
56 | if (!_settingService.UseCaretPosition)
57 | {
58 | return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
59 | }
60 |
61 | Point carretPixel = WindowsFunctions.GetCaretPosition();
62 | if (carretPixel.X == 0 && carretPixel.Y == 0)
63 | {
64 | return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
65 | }
66 |
67 | Point caret = new Point(carretPixel.X, carretPixel.Y) / primaryDpi;
68 | return Calculation.GetRawCoordinatesFromCaret(caret, screen, window);
69 | }
70 |
71 | public char[] GetLettersFromKey(LetterKey letter)
72 | {
73 | return _settingService.GetLetterKey(letter);
74 | }
75 |
76 | public bool? IsLeftPosition()
77 | {
78 | return _settingService.Position switch
79 | {
80 | Position.Left or Position.TopLeft or Position.BottomLeft => true,
81 | Position.Right or Position.TopRight or Position.BottomRight => false,
82 | _ => null
83 | };
84 | }
85 |
86 | public void ReloadSettings()
87 | {
88 | _settingService.Reload();
89 | }
90 |
91 | public void CheckVersion()
92 | {
93 | _settingService.UpdateSettingsVersion();
94 | }
95 |
96 | public void Dispose()
97 | {
98 | _keyboardListener.Dispose();
99 | GC.SuppressFinalize(this);
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/PowerAccent.UI/PowerAccent.UI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net9.0-windows10.0.19041.0
6 | disable
7 | true
8 | True
9 | Resources/a-icon.ico
10 | PowerAccent
11 | True
12 | False
13 | $(NoWarn);CA1416
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | $(DefaultXamlRuntime)
89 | Designer
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/SortPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using PowerAccent.Core;
2 | using PowerAccent.Core.Services;
3 | using System;
4 | using System.Collections.ObjectModel;
5 | using System.ComponentModel;
6 | using System.Globalization;
7 | using System.Linq;
8 | using System.Windows;
9 | using System.Windows.Controls;
10 | using System.Windows.Data;
11 | using Application = System.Windows.Application;
12 | using ListBox = System.Windows.Controls.ListBox;
13 |
14 | namespace PowerAccent.UI.SettingsPage;
15 |
16 | ///
17 | /// Logique d'interaction pour SortPage.xaml
18 | ///
19 | public partial class SortPage : Page, INotifyPropertyChanged
20 | {
21 | private readonly SettingsService _settingService = new SettingsService();
22 |
23 | public SortPage()
24 | {
25 | InitializeComponent();
26 | }
27 |
28 | protected override void OnInitialized(EventArgs e)
29 | {
30 | base.OnInitialized(e);
31 | Letters.ItemsSource = Enum.GetValues(typeof(LetterKey)).Cast()
32 | .Where(k => k >= LetterKey.A && k <= LetterKey.Z)
33 | .Where(k => _settingService.GetLetterKey(k).Length > 0);
34 | CharacterList.DataContext = this;
35 | }
36 |
37 | private ObservableCollection _characters;
38 |
39 | public event PropertyChangedEventHandler PropertyChanged;
40 | private void NotifyPropertyChanged(string property)
41 | {
42 | if (PropertyChanged != null)
43 | {
44 | PropertyChanged(this, new PropertyChangedEventArgs(property));
45 | }
46 | }
47 |
48 | public ObservableCollection Characters
49 | {
50 | get
51 | {
52 | if (_characters == null)
53 | _characters = new ObservableCollection();
54 | return _characters;
55 | }
56 | set
57 | {
58 | _characters = value;
59 | NotifyPropertyChanged("Characters");
60 | }
61 | }
62 |
63 | private void Letters_SelectionChanged(object sender, SelectionChangedEventArgs e)
64 | {
65 | LetterKey key = (LetterKey)((ListBox)sender).SelectedItem;
66 | Characters = new ObservableCollection(_settingService.GetLetterKey(key));
67 | CharacterList.Visibility = Visibility.Visible;
68 | }
69 |
70 | private void Back_Click(object sender, RoutedEventArgs e)
71 | {
72 | LetterKey key = (LetterKey)Letters.SelectedItem;
73 | Characters = new ObservableCollection(_settingService.GetDefaultLetterKey(key));
74 | }
75 |
76 | private void Save_Click(object sender, RoutedEventArgs e)
77 | {
78 | LetterKey key = (LetterKey)Letters.SelectedItem;
79 | _settingService.SetLetterKey(key, Characters.ToArray());
80 | _settingService.Save();
81 | (Application.Current.MainWindow as MainWindow).RefreshSettings();
82 | }
83 | }
84 |
85 | class VisibilityNullConverter : IValueConverter
86 | {
87 | #region IValueConverter Members
88 |
89 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
90 | {
91 | return value == null ? Visibility.Collapsed : Visibility.Visible;
92 | }
93 |
94 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
95 | {
96 | throw new NotImplementedException();
97 | }
98 |
99 | #endregion
100 | }
101 |
102 | class BooleanNullConverter : IValueConverter
103 | {
104 | #region IValueConverter Members
105 |
106 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
107 | {
108 | return value != null;
109 | }
110 |
111 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
112 | {
113 | return value == null;
114 | }
115 |
116 | #endregion
117 | }
--------------------------------------------------------------------------------
/PowerAccent.Core/Tools/WindowsFunctions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Runtime.InteropServices;
3 | using Vanara.PInvoke;
4 |
5 | namespace PowerAccent.Core.Tools;
6 |
7 | internal static class WindowsFunctions
8 | {
9 | public static void Insert(char c, bool back = false)
10 | {
11 | Debug.WriteLine($"Insert character: '{c}'. Back: {back}");
12 | unsafe
13 | {
14 | if (back)
15 | {
16 | // Split in 2 different SendInput (Powershell doesn't take back issue)
17 | var inputsBack = new User32.INPUT[]
18 | {
19 | new User32.INPUT {type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT {wVk = (ushort) User32.VK.VK_BACK}},
20 | new User32.INPUT {type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT {wVk = (ushort) User32.VK.VK_BACK, dwFlags = User32.KEYEVENTF.KEYEVENTF_KEYUP}}
21 | };
22 |
23 | _ = User32.SendInput((uint)inputsBack.Length, inputsBack, sizeof(User32.INPUT));
24 | System.Threading.Thread.Sleep(1); // Some apps, like Terminal, need a little wait to process the sent backspace or they'll ignore it.
25 | }
26 |
27 | // Letter
28 | var inputsInsert = new User32.INPUT[1]
29 | {
30 | new User32.INPUT {type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT {wVk = 0, dwFlags = User32.KEYEVENTF.KEYEVENTF_UNICODE, wScan = c}}
31 | };
32 | _ = User32.SendInput((uint)inputsInsert.Length, inputsInsert, sizeof(User32.INPUT));
33 | System.Threading.Thread.Sleep(1);
34 | }
35 | }
36 |
37 | public static Point GetCaretPosition()
38 | {
39 | User32.GUITHREADINFO guiInfo = new User32.GUITHREADINFO();
40 | guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
41 | User32.GetGUIThreadInfo(0, ref guiInfo);
42 | System.Drawing.Point caretPosition = new System.Drawing.Point(guiInfo.rcCaret.left, guiInfo.rcCaret.top);
43 | User32.ClientToScreen(guiInfo.hwndCaret, ref caretPosition);
44 |
45 | if (caretPosition.X == 0)
46 | {
47 | System.Drawing.Point testPoint;
48 | User32.GetCaretPos(out testPoint);
49 | return testPoint;
50 | }
51 |
52 | return caretPosition;
53 | }
54 |
55 | public static (Point Location, Size Size, double Dpi) GetActiveDisplay()
56 | {
57 | User32.GUITHREADINFO guiInfo = new User32.GUITHREADINFO();
58 | guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
59 | User32.GetGUIThreadInfo(0, ref guiInfo);
60 | var res = User32.MonitorFromWindow(guiInfo.hwndActive, User32.MonitorFlags.MONITOR_DEFAULTTONEAREST);
61 |
62 | User32.MONITORINFO monitorInfo = new User32.MONITORINFO();
63 | monitorInfo.cbSize = (uint)Marshal.SizeOf(monitorInfo);
64 | User32.GetMonitorInfo(res, ref monitorInfo);
65 |
66 | double dpi = User32.GetDpiForWindow(guiInfo.hwndActive) / 96d;
67 |
68 | return (monitorInfo.rcWork.Location, monitorInfo.rcWork.Size, dpi);
69 | }
70 |
71 | public static bool IsCapitalState()
72 | {
73 | var capital = User32.GetKeyState((int)User32.VK.VK_CAPITAL);
74 | var shift = User32.GetKeyState((int)User32.VK.VK_SHIFT);
75 | return capital != 0 || shift < 0;
76 | }
77 |
78 | public static bool IsKeyPressed(LetterKey key)
79 | {
80 | var result = User32.GetAsyncKeyState((int)key);
81 | Debug.WriteLine("Result - " + result);
82 | return result != 0;
83 | }
84 |
85 | public static bool IsGameMode()
86 | {
87 | Shell32.QUERY_USER_NOTIFICATION_STATE state;
88 | if (Shell32.SHQueryUserNotificationState(out state) != 0)
89 | {
90 | return false;
91 | }
92 |
93 | return state == Shell32.QUERY_USER_NOTIFICATION_STATE.QUNS_BUSY || state == Shell32.QUERY_USER_NOTIFICATION_STATE.QUNS_RUNNING_D3D_FULL_SCREEN;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/PositionPage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Selector.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
27 |
28 |
61 |
62 |
63 |
64 |
65 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Services/SettingsService.cs:
--------------------------------------------------------------------------------
1 | using System.Configuration;
2 |
3 | namespace PowerAccent.Core.Services;
4 |
5 | public class SettingsService : ApplicationSettingsBase
6 | {
7 |
8 | [UserScopedSetting]
9 | [DefaultSettingValue("ALL")]
10 | public Language SelectedLanguage
11 | {
12 | get { return (Language)this["SelectedLanguage"]; }
13 | set { this["SelectedLanguage"] = value; Save(); }
14 | }
15 |
16 | [UserScopedSetting]
17 | [DefaultSettingValue("ALL")]
18 | public Language[] SelectedLanguages
19 | {
20 | get { return (Language[])this["SelectedLanguages"]; }
21 | set { this["SelectedLanguages"] = value; Save(); }
22 | }
23 |
24 | [UserScopedSetting]
25 | [DefaultSettingValue("Top")]
26 | public Position Position
27 | {
28 | get { return (Position)this["Position"]; }
29 | set { this["Position"] = value; Save(); }
30 | }
31 |
32 | [UserScopedSetting]
33 | [DefaultSettingValue("False")]
34 | public bool UseCaretPosition
35 | {
36 | get { return (bool)this["UseCaretPosition"]; }
37 | set { this["UseCaretPosition"] = value; Save(); }
38 | }
39 |
40 | [UserScopedSetting]
41 | [DefaultSettingValue("True")]
42 | public bool IsSpaceBarActive
43 | {
44 | get { return (bool)this["IsSpaceBarActive"]; }
45 | set { this["IsSpaceBarActive"] = value; Save(); }
46 | }
47 |
48 | [UserScopedSetting]
49 | [DefaultSettingValue("200")]
50 | public int InputTime
51 | {
52 | get { return (int)this["InputTime"]; }
53 | set { this["InputTime"] = value; Save(); }
54 | }
55 |
56 | [UserScopedSetting]
57 | [DefaultSettingValue("True")]
58 | public bool DisableInFullScreen
59 | {
60 | get { return (bool)this["DisableInFullScreen"]; }
61 | set { this["DisableInFullScreen"] = value; Save(); }
62 | }
63 |
64 | [UserScopedSetting]
65 | [DefaultSettingValue("False")]
66 | public bool InsertSpaceAfterSelection
67 | {
68 | get { return (bool)this["InsertSpaceAfterSelection"]; }
69 | set { this["InsertSpaceAfterSelection"] = value; Save(); }
70 | }
71 |
72 | [UserScopedSetting]
73 | [DefaultSettingValue("0")]
74 | public int SettingsVersion
75 | {
76 | get { return (int)this["SettingsVersion"]; }
77 | set { this["SettingsVersion"] = value; Save(); }
78 | }
79 |
80 | #region LetterKey
81 |
82 | public void SetLetterKey(LetterKey letter, char[] value)
83 | {
84 | string key = $"LetterKey{letter}_{SelectedLanguage}";
85 | AddingProperty(key);
86 |
87 | this[key] = value;
88 | this.Save();
89 | }
90 |
91 | public char[] GetLetterKey(LetterKey letter)
92 | {
93 | string key = $"LetterKey{letter}_{SelectedLanguage}";
94 | AddingProperty(key);
95 | if (this.PropertyValues.Cast().Any(s => s.Name == key) && this[key] != null)
96 | return (char[])this[key];
97 |
98 | return Languages.GetMultipleLetterKey(letter, SelectedLanguages);
99 | }
100 |
101 | public char[] GetDefaultLetterKey(LetterKey key)
102 | {
103 | return Languages.GetMultipleLetterKey(key, SelectedLanguages);
104 | }
105 |
106 | private void AddingProperty(string key)
107 | {
108 | if (!this.PropertyValues.Cast().Any(s => s.Name == key))
109 | {
110 | SettingsProvider sp = this.Providers["LocalFileSettingsProvider"];
111 | SettingsProperty p = new SettingsProperty(key);
112 | p.PropertyType = typeof(char[]);
113 | p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
114 | p.Provider = sp;
115 | p.SerializeAs = SettingsSerializeAs.Xml;
116 | SettingsPropertyValue v = new SettingsPropertyValue(p);
117 | this.Properties.Add(p);
118 | this.Reload();
119 | }
120 | }
121 |
122 | // Check and update properties from previous version
123 | public void UpdateSettingsVersion()
124 | {
125 | if (this.SettingsVersion == 1)
126 | return;
127 |
128 | if (this.SettingsVersion == 0)
129 | {
130 | SelectedLanguages = new[] { SelectedLanguage };
131 | }
132 |
133 | SettingsVersion = 1;
134 | this.Save();
135 | }
136 |
137 | #endregion
138 | }
139 |
140 | public enum Position
141 | {
142 | Top,
143 | Bottom,
144 | Left,
145 | Right,
146 | TopLeft,
147 | TopRight,
148 | BottomLeft,
149 | BottomRight,
150 | Center
151 | }
152 |
--------------------------------------------------------------------------------
/PowerAccent.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32014.148
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerAccent.UI", "PowerAccent.UI\PowerAccent.UI.csproj", "{8F823B66-7B84-4D2E-B9F8-06846DDC5951}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerAccent.Core", "PowerAccent.Core\PowerAccent.Core.csproj", "{0124DDDD-22C0-482C-B96A-92B8F9B25464}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A101495-C7E7-4A21-A6F1-8BD3A6204EFA}"
11 | ProjectSection(SolutionItems) = preProject
12 | .editorconfig = .editorconfig
13 | .gitignore = .gitignore
14 | .github\workflows\publish.yml = .github\workflows\publish.yml
15 | README.md = README.md
16 | EndProjectSection
17 | EndProject
18 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerAccent.Wix.SelfContained", "PowerAccent.Wix\PowerAccent.Wix.SelfContained.wixproj", "{0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Debug|ARM = Debug|ARM
24 | Debug|ARM64 = Debug|ARM64
25 | Debug|x64 = Debug|x64
26 | Debug|x86 = Debug|x86
27 | Release|Any CPU = Release|Any CPU
28 | Release|ARM = Release|ARM
29 | Release|ARM64 = Release|ARM64
30 | Release|x64 = Release|x64
31 | Release|x86 = Release|x86
32 | EndGlobalSection
33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
34 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|ARM.ActiveCfg = Debug|Any CPU
37 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|ARM.Build.0 = Debug|Any CPU
38 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|ARM64.ActiveCfg = Debug|Any CPU
39 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|ARM64.Build.0 = Debug|Any CPU
40 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|x64.ActiveCfg = Debug|Any CPU
41 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|x64.Build.0 = Debug|Any CPU
42 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|x86.ActiveCfg = Debug|Any CPU
43 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Debug|x86.Build.0 = Debug|Any CPU
44 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|ARM.ActiveCfg = Release|Any CPU
47 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|ARM.Build.0 = Release|Any CPU
48 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|ARM64.ActiveCfg = Release|Any CPU
49 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|ARM64.Build.0 = Release|Any CPU
50 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|x64.ActiveCfg = Release|Any CPU
51 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|x64.Build.0 = Release|Any CPU
52 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|x86.ActiveCfg = Release|Any CPU
53 | {8F823B66-7B84-4D2E-B9F8-06846DDC5951}.Release|x86.Build.0 = Release|Any CPU
54 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|Any CPU.Build.0 = Debug|Any CPU
56 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|ARM.ActiveCfg = Debug|Any CPU
57 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|ARM.Build.0 = Debug|Any CPU
58 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|ARM64.ActiveCfg = Debug|Any CPU
59 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|ARM64.Build.0 = Debug|Any CPU
60 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|x64.ActiveCfg = Debug|Any CPU
61 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|x64.Build.0 = Debug|Any CPU
62 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|x86.ActiveCfg = Debug|Any CPU
63 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Debug|x86.Build.0 = Debug|Any CPU
64 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|Any CPU.ActiveCfg = Release|Any CPU
65 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|Any CPU.Build.0 = Release|Any CPU
66 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|ARM.ActiveCfg = Release|Any CPU
67 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|ARM.Build.0 = Release|Any CPU
68 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|ARM64.ActiveCfg = Release|Any CPU
69 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|ARM64.Build.0 = Release|Any CPU
70 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|x64.ActiveCfg = Release|Any CPU
71 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|x64.Build.0 = Release|Any CPU
72 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|x86.ActiveCfg = Release|Any CPU
73 | {0124DDDD-22C0-482C-B96A-92B8F9B25464}.Release|x86.Build.0 = Release|Any CPU
74 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|Any CPU.ActiveCfg = Debug|x86
75 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|ARM.ActiveCfg = Debug|x86
76 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|ARM.Build.0 = Debug|x86
77 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|ARM64.ActiveCfg = Debug|x86
78 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|ARM64.Build.0 = Debug|x86
79 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|x64.ActiveCfg = Debug|x86
80 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|x64.Build.0 = Debug|x86
81 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|x86.ActiveCfg = Debug|x86
82 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Debug|x86.Build.0 = Debug|x86
83 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|Any CPU.ActiveCfg = Release|x86
84 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|Any CPU.Build.0 = Release|x86
85 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|ARM.ActiveCfg = Release|x86
86 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|ARM.Build.0 = Release|x86
87 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|ARM64.ActiveCfg = Release|x86
88 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|ARM64.Build.0 = Release|x86
89 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|x64.ActiveCfg = Release|x86
90 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|x64.Build.0 = Release|x86
91 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|x86.ActiveCfg = Release|x86
92 | {0C31F068-83E6-48ED-AB58-4E8EBFE51D9A}.Release|x86.Build.0 = Release|x86
93 | EndGlobalSection
94 | GlobalSection(SolutionProperties) = preSolution
95 | HideSolutionNode = FALSE
96 | EndGlobalSection
97 | GlobalSection(ExtensibilityGlobals) = postSolution
98 | SolutionGuid = {46E35B45-7B0B-4B86-BA3A-AF4B406CF608}
99 | EndGlobalSection
100 | EndGlobal
101 |
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/OptionsPage.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
86 |
89 |
92 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/PowerAccent.UI/Themes/ThemeManager.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation
2 | // The Microsoft Corporation licenses this file to you under the MIT license.
3 | // See the LICENSE file in the project root for more information.
4 |
5 | using System;
6 | using System.Linq;
7 | using System.Windows;
8 | using ControlzEx.Theming;
9 | using Microsoft.Win32;
10 |
11 | namespace PowerAccent.UI.Themes
12 | {
13 | public enum Theme
14 | {
15 | System,
16 | Light,
17 | Dark,
18 | HighContrastOne,
19 | HighContrastTwo,
20 | HighContrastBlack,
21 | HighContrastWhite,
22 | }
23 |
24 | public class ThemeManager : IDisposable
25 | {
26 | private const string LightTheme = "Light.Accent1";
27 | private const string DarkTheme = "Dark.Accent1";
28 | private const string HighContrastOneTheme = "HighContrast.Accent2";
29 | private const string HighContrastTwoTheme = "HighContrast.Accent3";
30 | private const string HighContrastBlackTheme = "HighContrast.Accent4";
31 | private const string HighContrastWhiteTheme = "HighContrast.Accent5";
32 |
33 | private Theme _currentTheme;
34 | private Theme _settingsTheme;
35 | private bool _disposed;
36 |
37 | public event ThemeChangedHandler ThemeChanged;
38 |
39 | public ThemeManager()
40 | {
41 | Uri highContrastOneThemeUri = new Uri("pack://application:,,,/Themes/HighContrast1.xaml");
42 | Uri highContrastTwoThemeUri = new Uri("pack://application:,,,/Themes/HighContrast2.xaml");
43 | Uri highContrastBlackThemeUri = new Uri("pack://application:,,,/Themes/HighContrastWhite.xaml");
44 | Uri highContrastWhiteThemeUri = new Uri("pack://application:,,,/Themes/HighContrastBlack.xaml");
45 | Uri lightThemeUri = new Uri("pack://application:,,,/Themes/Light.xaml");
46 | Uri darkThemeUri = new Uri("pack://application:,,,/Themes/Dark.xaml");
47 |
48 | ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
49 | new LibraryTheme(
50 | highContrastOneThemeUri,
51 | CustomLibraryThemeProvider.DefaultInstance));
52 | ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
53 | new LibraryTheme(
54 | highContrastTwoThemeUri,
55 | CustomLibraryThemeProvider.DefaultInstance));
56 | ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
57 | new LibraryTheme(
58 | highContrastBlackThemeUri,
59 | CustomLibraryThemeProvider.DefaultInstance));
60 | ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
61 | new LibraryTheme(
62 | highContrastWhiteThemeUri,
63 | CustomLibraryThemeProvider.DefaultInstance));
64 | ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
65 | new LibraryTheme(
66 | lightThemeUri,
67 | CustomLibraryThemeProvider.DefaultInstance));
68 | ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
69 | new LibraryTheme(
70 | darkThemeUri,
71 | CustomLibraryThemeProvider.DefaultInstance));
72 |
73 | ResetTheme();
74 | ControlzEx.Theming.ThemeManager.Current.ThemeSyncMode = ThemeSyncMode.SyncWithAppMode;
75 | ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged;
76 | SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
77 | }
78 |
79 | private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
80 | {
81 | if (e.PropertyName == nameof(SystemParameters.HighContrast))
82 | {
83 | ResetTheme();
84 | }
85 | }
86 |
87 | public Theme GetCurrentTheme()
88 | {
89 | return _currentTheme;
90 | }
91 |
92 | private static Theme GetHighContrastBaseType()
93 | {
94 | string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
95 | string theme = (string)Registry.GetValue(registryKey, "CurrentTheme", string.Empty);
96 | theme = theme.Split('\\').Last().Split('.').First().ToString();
97 |
98 | switch (theme)
99 | {
100 | case "hc1":
101 | return Theme.HighContrastOne;
102 | case "hc2":
103 | return Theme.HighContrastTwo;
104 | case "hcwhite":
105 | return Theme.HighContrastWhite;
106 | case "hcblack":
107 | return Theme.HighContrastBlack;
108 | default:
109 | return Theme.HighContrastOne;
110 | }
111 | }
112 |
113 | private void ResetTheme()
114 | {
115 | ChangeTheme(_settingsTheme == Theme.System ? Theme.System : _currentTheme);
116 | }
117 |
118 | public static string GetWindowsBaseColor()
119 | {
120 | return WindowsThemeHelper.GetWindowsBaseColor();
121 | }
122 |
123 | public void ChangeTheme(Theme theme, bool fromSettings = false)
124 | {
125 | if (fromSettings)
126 | {
127 | _settingsTheme = theme;
128 | }
129 |
130 | Theme oldTheme = _currentTheme;
131 |
132 | if (theme == Theme.System)
133 | {
134 | _currentTheme = Theme.System;
135 | if (WindowsThemeHelper.IsHighContrastEnabled())
136 | {
137 | Theme highContrastBaseType = GetHighContrastBaseType();
138 | ChangeTheme(highContrastBaseType, false);
139 | }
140 | else
141 | {
142 | string baseColor = WindowsThemeHelper.GetWindowsBaseColor();
143 | ChangeTheme((Theme)Enum.Parse(typeof(Theme), baseColor));
144 | }
145 | }
146 | else if (theme == Theme.HighContrastOne)
147 | {
148 | _currentTheme = Theme.HighContrastOne;
149 | ControlzEx.Theming.ThemeManager.Current.ChangeTheme(App.Current, HighContrastOneTheme);
150 | }
151 | else if (theme == Theme.HighContrastTwo)
152 | {
153 | _currentTheme = Theme.HighContrastTwo;
154 | ControlzEx.Theming.ThemeManager.Current.ChangeTheme(App.Current, HighContrastTwoTheme);
155 | }
156 | else if (theme == Theme.HighContrastWhite)
157 | {
158 | _currentTheme = Theme.HighContrastWhite;
159 | ControlzEx.Theming.ThemeManager.Current.ChangeTheme(App.Current, HighContrastWhiteTheme);
160 | }
161 | else if (theme == Theme.HighContrastBlack)
162 | {
163 | _currentTheme = Theme.HighContrastBlack;
164 | ControlzEx.Theming.ThemeManager.Current.ChangeTheme(App.Current, HighContrastBlackTheme);
165 | }
166 | else if (theme == Theme.Light)
167 | {
168 | _currentTheme = Theme.Light;
169 | ControlzEx.Theming.ThemeManager.Current.ChangeTheme(App.Current, LightTheme);
170 | }
171 | else if (theme == Theme.Dark)
172 | {
173 | _currentTheme = Theme.Dark;
174 | ControlzEx.Theming.ThemeManager.Current.ChangeTheme(App.Current, DarkTheme);
175 | }
176 |
177 | ThemeChanged?.Invoke(oldTheme, _currentTheme);
178 | }
179 |
180 | private void Current_ThemeChanged(object sender, ThemeChangedEventArgs e)
181 | {
182 | ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged;
183 | try
184 | {
185 | ResetTheme();
186 | }
187 | finally
188 | {
189 | ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged;
190 | }
191 | }
192 |
193 | protected virtual void Dispose(bool disposing)
194 | {
195 | if (!_disposed)
196 | {
197 | if (disposing)
198 | {
199 | ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged;
200 | _disposed = true;
201 | }
202 | }
203 | }
204 |
205 | public void Dispose()
206 | {
207 | Dispose(disposing: true);
208 | GC.SuppressFinalize(this);
209 | }
210 | }
211 |
212 | public delegate void ThemeChangedHandler(Theme oldTheme, Theme newTheme);
213 | }
--------------------------------------------------------------------------------
/PowerAccent.UI/SettingsPage/SortPage.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Tools/KeyboardListener.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace PowerAccent.Core.Tools;
5 |
6 | internal class KeyboardListener : IDisposable
7 | {
8 | ///
9 | /// Creates global keyboard listener.
10 | ///
11 | public KeyboardListener()
12 | {
13 | // We have to store the LowLevelKeyboardProc, so that it is not garbage collected runtime
14 | _hookedLowLevelKeyboardProc = LowLevelKeyboardProc;
15 |
16 | // Set the hook
17 | _hookId = InterceptKeys.SetHook(_hookedLowLevelKeyboardProc);
18 |
19 | // Assign the asynchronous callback event
20 | hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
21 | }
22 |
23 | ///
24 | /// Fired when any of the keys is pressed down.
25 | ///
26 | public event RawKeyEventHandler KeyDown;
27 |
28 | ///
29 | /// Fired when any of the keys is released.
30 | ///
31 | public event RawKeyEventHandler KeyUp;
32 |
33 | ///
34 | /// Hook ID
35 | ///
36 | private readonly IntPtr _hookId = IntPtr.Zero;
37 |
38 | ///
39 | /// Contains the hooked callback in runtime.
40 | ///
41 | private readonly InterceptKeys.LowLevelKeyboardProc _hookedLowLevelKeyboardProc;
42 |
43 | ///
44 | /// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
45 | ///
46 | private KeyboardCallbackAsync hookedKeyboardCallbackAsync;
47 |
48 | ///
49 | /// Raw keyevent handler.
50 | ///
51 | /// sender
52 | /// raw keyevent arguments
53 | public delegate bool RawKeyEventHandler(object sender, RawKeyEventArgs args);
54 |
55 | ///
56 | /// Asynchronous callback hook.
57 | ///
58 | /// Character
59 | /// Keyboard event
60 | /// VKCode
61 | private delegate bool KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character);
62 |
63 | ///
64 | /// Actual callback hook.
65 | ///
66 | /// Calls asynchronously the asyncCallback.
67 | ///
68 | ///
69 | ///
70 | ///
71 | ///
72 | [MethodImpl(MethodImplOptions.NoInlining)]
73 | private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
74 | {
75 | if (nCode == 0)
76 | //Captures the character(s) pressed only on WM_KEYDOWN or WM_KEYUP
77 | if (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
78 | wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYUP)
79 | {
80 | var chars = InterceptKeys.VKCodeToString((uint)Marshal.ReadInt32(lParam),
81 | wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN);
82 |
83 | if (!hookedKeyboardCallbackAsync.Invoke((InterceptKeys.KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), chars))
84 | {
85 | return (IntPtr)1;
86 | }
87 | }
88 |
89 | return InterceptKeys.CallNextHookEx(_hookId, nCode, wParam, lParam);
90 | }
91 |
92 | ///
93 | /// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
94 | ///
95 | /// Keyboard event
96 | /// VKCode
97 | /// Character as string.
98 | bool KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character)
99 | {
100 | switch (keyEvent)
101 | {
102 | // KeyDown events
103 | case InterceptKeys.KeyEvent.WM_KEYDOWN:
104 | if (KeyDown != null)
105 | return KeyDown.Invoke(this, new RawKeyEventArgs(vkCode, false, character));
106 | break;
107 |
108 | // KeyUp events
109 | case InterceptKeys.KeyEvent.WM_KEYUP:
110 | if (KeyUp != null)
111 | return KeyUp.Invoke(this, new RawKeyEventArgs(vkCode, false, character));
112 | break;
113 |
114 | default:
115 | break;
116 | }
117 | return true;
118 | }
119 | public void Dispose()
120 | {
121 | InterceptKeys.UnhookWindowsHookEx(_hookId);
122 | }
123 |
124 | ///
125 | /// Raw KeyEvent arguments.
126 | ///
127 | public class RawKeyEventArgs : EventArgs
128 | {
129 | ///
130 | /// VKCode of the key.
131 | ///
132 | public int VKCode;
133 |
134 | ///
135 | /// WPF Key of the key.
136 | ///
137 | public uint Key;
138 |
139 | ///
140 | /// Is the hitted key system key.
141 | ///
142 | public bool IsSysKey;
143 |
144 | ///
145 | /// Convert to string.
146 | ///
147 | /// Returns string representation of this key, if not possible empty string is returned.
148 | public override string ToString()
149 | {
150 | return Character;
151 | }
152 |
153 | ///
154 | /// Unicode character of key pressed.
155 | ///
156 | public string Character;
157 |
158 | ///
159 | /// Create raw keyevent arguments.
160 | ///
161 | ///
162 | ///
163 | /// Character
164 | public RawKeyEventArgs(int VKCode, bool isSysKey, string Character)
165 | {
166 | this.VKCode = VKCode;
167 | IsSysKey = isSysKey;
168 | this.Character = Character;
169 | Key = (uint)VKCode; //User32.MapVirtualKey((uint)VKCode, User32.MAPVK.MAPVK_VK_TO_VSC_EX);
170 | }
171 |
172 | }
173 | }
174 |
175 | ///
176 | /// Winapi Key interception helper class.
177 | ///
178 | internal static class InterceptKeys
179 | {
180 | public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
181 | public static int WH_KEYBOARD_LL = 13;
182 |
183 | ///
184 | /// Key event
185 | ///
186 | public enum KeyEvent : int
187 | {
188 | ///
189 | /// Key down
190 | ///
191 | WM_KEYDOWN = 256,
192 |
193 | ///
194 | /// Key up
195 | ///
196 | WM_KEYUP = 257,
197 | }
198 |
199 | public static IntPtr SetHook(LowLevelKeyboardProc proc)
200 | {
201 | return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(null), 0);
202 | }
203 |
204 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
205 | public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
206 |
207 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
208 | [return: MarshalAs(UnmanagedType.Bool)]
209 | public static extern bool UnhookWindowsHookEx(IntPtr hhk);
210 |
211 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
212 | public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
213 |
214 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
215 | public static extern IntPtr GetModuleHandle(string lpModuleName);
216 |
217 | #region Convert VKCode to string
218 | // Note: Sometimes single VKCode represents multiple chars, thus string.
219 | // E.g. typing "^1" (notice that when pressing 1 the both characters appear,
220 | // because of this behavior, "^" is called dead key)
221 |
222 | [DllImport("user32.dll")]
223 | private static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
224 |
225 | [DllImport("user32.dll")]
226 | private static extern bool GetKeyboardState(byte[] lpKeyState);
227 |
228 | [DllImport("user32.dll")]
229 | private static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);
230 |
231 | [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
232 | private static extern IntPtr GetKeyboardLayout(uint dwLayout);
233 |
234 | [DllImport("User32.dll")]
235 | private static extern IntPtr GetForegroundWindow();
236 |
237 | [DllImport("User32.dll")]
238 | private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
239 |
240 | [DllImport("user32.dll")]
241 | private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
242 |
243 | [DllImport("kernel32.dll")]
244 | private static extern uint GetCurrentThreadId();
245 |
246 | private static uint lastVKCode = 0;
247 | private static uint lastScanCode = 0;
248 | private static byte[] lastKeyState = new byte[255];
249 | private static bool lastIsDead = false;
250 |
251 | ///
252 | /// Convert VKCode to Unicode.
253 | /// isKeyDown is required for because of keyboard state inconsistencies!
254 | ///
255 | /// VKCode
256 | /// Is the key down event?
257 | /// String representing single unicode character.
258 | public static string VKCodeToString(uint VKCode, bool isKeyDown)
259 | {
260 | // ToUnicodeEx needs StringBuilder, it populates that during execution.
261 | System.Text.StringBuilder sbString = new System.Text.StringBuilder(5);
262 |
263 | byte[] bKeyState = new byte[255];
264 | bool bKeyStateStatus;
265 | bool isDead = false;
266 |
267 | // Gets the current windows window handle, threadID, processID
268 | IntPtr currentHWnd = GetForegroundWindow();
269 | uint currentProcessID;
270 | uint currentWindowThreadID = GetWindowThreadProcessId(currentHWnd, out currentProcessID);
271 |
272 | // This programs Thread ID
273 | uint thisProgramThreadId = GetCurrentThreadId();
274 |
275 | // Attach to active thread so we can get that keyboard state
276 | if (AttachThreadInput(thisProgramThreadId, currentWindowThreadID, true))
277 | {
278 | // Current state of the modifiers in keyboard
279 | bKeyStateStatus = GetKeyboardState(bKeyState);
280 |
281 | // Detach
282 | AttachThreadInput(thisProgramThreadId, currentWindowThreadID, false);
283 | }
284 | else
285 | {
286 | // Could not attach, perhaps it is this process?
287 | bKeyStateStatus = GetKeyboardState(bKeyState);
288 | }
289 |
290 | // On failure we return empty string.
291 | if (!bKeyStateStatus)
292 | return "";
293 |
294 | // Gets the layout of keyboard
295 | IntPtr HKL = GetKeyboardLayout(currentWindowThreadID);
296 |
297 | // Maps the virtual keycode
298 | uint lScanCode = MapVirtualKeyEx(VKCode, 0, HKL);
299 |
300 | // Keyboard state goes inconsistent if this is not in place. In other words, we need to call above commands in UP events also.
301 | if (!isKeyDown)
302 | return "";
303 |
304 | // Converts the VKCode to unicode
305 | int relevantKeyCountInBuffer = ToUnicodeEx(VKCode, lScanCode, bKeyState, sbString, sbString.Capacity, 0, HKL);
306 |
307 | string ret = "";
308 |
309 | switch (relevantKeyCountInBuffer)
310 | {
311 | // Dead keys (^,`...)
312 | case -1:
313 | isDead = true;
314 |
315 | // We must clear the buffer because ToUnicodeEx messed it up, see below.
316 | ClearKeyboardBuffer(VKCode, lScanCode, HKL);
317 | break;
318 |
319 | case 0:
320 | break;
321 |
322 | // Single character in buffer
323 | case 1:
324 | ret = sbString.Length == 0 ? "" : sbString[0].ToString();
325 | break;
326 |
327 | // Two or more (only two of them is relevant)
328 | case 2:
329 | default:
330 | ret = sbString.ToString().Substring(0, 2);
331 | break;
332 | }
333 |
334 | // We inject the last dead key back, since ToUnicodeEx removed it.
335 | // More about this peculiar behavior see e.g:
336 | // http://www.experts-exchange.com/Programming/System/Windows__Programming/Q_23453780.html
337 | // http://blogs.msdn.com/michkap/archive/2005/01/19/355870.aspx
338 | // http://blogs.msdn.com/michkap/archive/2007/10/27/5717859.aspx
339 | if (lastVKCode != 0 && lastIsDead)
340 | {
341 | System.Text.StringBuilder sbTemp = new System.Text.StringBuilder(5);
342 | ToUnicodeEx(lastVKCode, lastScanCode, lastKeyState, sbTemp, sbTemp.Capacity, 0, HKL);
343 | lastVKCode = 0;
344 |
345 | return ret;
346 | }
347 |
348 | // Save these
349 | lastScanCode = lScanCode;
350 | lastVKCode = VKCode;
351 | lastIsDead = isDead;
352 | lastKeyState = (byte[])bKeyState.Clone();
353 |
354 | return ret;
355 | }
356 |
357 | private static void ClearKeyboardBuffer(uint vk, uint sc, IntPtr hkl)
358 | {
359 | System.Text.StringBuilder sb = new System.Text.StringBuilder(10);
360 |
361 | int rc;
362 | do
363 | {
364 | byte[] lpKeyStateNull = new byte[255];
365 | rc = ToUnicodeEx(vk, sc, lpKeyStateNull, sb, sb.Capacity, 0, hkl);
366 | } while (rc < 0);
367 | }
368 | #endregion
369 | }
370 |
--------------------------------------------------------------------------------
/PowerAccent.Core/Languages.cs:
--------------------------------------------------------------------------------
1 | namespace PowerAccent.Core;
2 |
3 | ///
4 | /// Languages, ordered alphabetically by country code (except ALL & Currency). It is set the order of the flags in the settings.
5 | ///
6 | public enum Language
7 | {
8 | ALL,
9 | BG,
10 | CUR,
11 | CY,
12 | CZ,
13 | DE,
14 | FI,
15 | FR,
16 | HR,
17 | HU,
18 | IS,
19 | IT,
20 | JP,
21 | MI,
22 | NL,
23 | PI,
24 | PL,
25 | PT,
26 | RO,
27 | RS,
28 | SA,
29 | SK,
30 | SP,
31 | SV,
32 | TK,
33 | }
34 |
35 | internal static class Languages
36 | {
37 | public static char[] GetMultipleLetterKey(LetterKey letter, Language[] langs)
38 | {
39 | var multipleLetterKey = new List();
40 | foreach (var lang in langs)
41 | {
42 | multipleLetterKey.Add(GetDefaultLetterKey(letter, lang));
43 | }
44 |
45 | var result = new List();
46 | for (int i = 0; i < multipleLetterKey.Max(x => x.Length); i++)
47 | {
48 | for (int j = 0; j < multipleLetterKey.Count; j++)
49 | {
50 | if (i < multipleLetterKey[j].Length)
51 | {
52 | result.Add(multipleLetterKey[j][i]);
53 | }
54 |
55 | if (j > multipleLetterKey.Count)
56 | j = 0;
57 | }
58 | }
59 |
60 | // remove duplicate
61 | int index = 0;
62 | while (index < result.Count)
63 | {
64 | int index2 = result.FindIndex(index + 1, x => x == result[index]);
65 | if (index2 != -1)
66 | {
67 | result.RemoveAt(index);
68 | }
69 | else
70 | {
71 | index++;
72 | }
73 | }
74 |
75 | return result.ToArray();
76 | }
77 |
78 | public static char[] GetDefaultLetterKey(LetterKey letter, Language lang)
79 | {
80 | switch (lang)
81 | {
82 | case Language.ALL: return GetDefaultLetterKeyALL(letter); // ALL
83 | case Language.BG: return GetDefaultLetterKeyBG(letter); // Bulgarian
84 | case Language.CUR: return GetDefaultLetterKeyCUR(letter); // Currency
85 | case Language.CY: return GetDefaultLetterKeyCY(letter); // Welsh
86 | case Language.CZ: return GetDefaultLetterKeyCZ(letter); // Czech
87 | case Language.DE: return GetDefaultLetterKeyDE(letter); // Germany
88 | case Language.FI: return GetDefaultLetterKeyFI(letter); // Finnish
89 | case Language.FR: return GetDefaultLetterKeyFR(letter); // French
90 | case Language.HR: return GetDefaultLetterKeyHR(letter); // Croatian
91 | case Language.HU: return GetDefaultLetterKeyHU(letter); // Hungarian
92 | case Language.IS: return GetDefaultLetterKeyIS(letter); // Iceland
93 | case Language.IT: return GetDefaultLetterKeyIT(letter); // Italian
94 | case Language.JP: return GetDefaultLetterKeyJP(letter); // Japanese
95 | case Language.MI: return GetDefaultLetterKeyMI(letter); // Maori
96 | case Language.NL: return GetDefaultLetterKeyNL(letter); // Netherlands
97 | case Language.PI: return GetDefaultLetterKeyPI(letter); // Pinyin
98 | case Language.PL: return GetDefaultLetterKeyPL(letter); // Polish
99 | case Language.PT: return GetDefaultLetterKeyPT(letter); // Portuguese
100 | case Language.RO: return GetDefaultLetterKeyRO(letter); // Romanian
101 | case Language.RS: return GetDefaultLetterKeySR(letter); // Serbian
102 | case Language.SA: return GetDefaultLetterKeySA(letter); // Sanskrit
103 | case Language.SK: return GetDefaultLetterKeySK(letter); // Slovak
104 | case Language.SP: return GetDefaultLetterKeySP(letter); // Spain
105 | case Language.SV: return GetDefaultLetterKeySV(letter); // Swedish
106 | case Language.TK: return GetDefaultLetterKeyTK(letter); // Turkish
107 | }
108 |
109 | throw new ArgumentException("The language {0} is not know in this context", lang.ToString());
110 | }
111 |
112 | public static string GetFlag(Language lang)
113 | {
114 | return string.Concat(lang.ToString().ToUpper().Select(x => char.ConvertFromUtf32(x + 0x1F1A5)));
115 | }
116 |
117 | // All
118 | private static char[] GetDefaultLetterKeyALL(LetterKey letter)
119 | {
120 | switch (letter)
121 | {
122 | case LetterKey._0:
123 | return new char[] { '₀', '⁰' };
124 | case LetterKey._1:
125 | return new char[] { '₁', '¹' };
126 | case LetterKey._2:
127 | return new char[] { '₂', '²' };
128 | case LetterKey._3:
129 | return new char[] { '₃', '³' };
130 | case LetterKey._4:
131 | return new char[] { '₄', '⁴' };
132 | case LetterKey._5:
133 | return new char[] { '₅', '⁵' };
134 | case LetterKey._6:
135 | return new char[] { '₆', '⁶' };
136 | case LetterKey._7:
137 | return new char[] { '₇', '⁷' };
138 | case LetterKey._8:
139 | return new char[] { '₈', '⁸' };
140 | case LetterKey._9:
141 | return new char[] { '₉', '⁹' };
142 | case LetterKey.A:
143 | return new char[] { 'á', 'à', 'ä', 'â', 'ă', 'å', 'α', 'ā', 'ą', 'ȧ', 'ã', 'æ' };
144 | case LetterKey.B:
145 | return new char[] { 'ḃ', 'β' };
146 | case LetterKey.C:
147 | return new char[] { 'ç', 'ć', 'ĉ', 'č', 'ċ', '¢', 'χ' };
148 | case LetterKey.D:
149 | return new char[] { 'ď', 'ḋ', 'đ', 'δ', 'ð' };
150 | case LetterKey.E:
151 | return new char[] { 'é', 'è', 'ê', 'ë', 'ě', 'ē', 'ę', 'ė', 'ε', 'η', '€' };
152 | case LetterKey.F:
153 | return new char[] { 'ƒ', 'ḟ' };
154 | case LetterKey.G:
155 | return new char[] { 'ğ', 'ģ', 'ǧ', 'ġ', 'ĝ', 'ǥ', 'γ' };
156 | case LetterKey.H:
157 | return new char[] { 'ḣ', 'ĥ', 'ħ' };
158 | case LetterKey.I:
159 | return new char[] { 'ï', 'î', 'í', 'ì', 'ī', 'į', 'i', 'ı', 'İ', 'ι', 'ѝ', 'Ѝ' };
160 | case LetterKey.J:
161 | return new char[] { 'ĵ' };
162 | case LetterKey.K:
163 | return new char[] { 'ķ', 'ǩ', 'κ' };
164 | case LetterKey.L:
165 | return new char[] { 'ĺ', 'ľ', 'ļ', 'ł', '₺', 'λ' };
166 | case LetterKey.M:
167 | return new char[] { 'ṁ' };
168 | case LetterKey.N:
169 | return new char[] { 'ñ', 'ń', 'ŋ', 'ň', 'ņ', 'ṅ', 'ⁿ', 'ν' };
170 | case LetterKey.O:
171 | return new char[] { 'ô', 'ó', 'ö', 'ő', 'ò', 'ō', 'ȯ', 'ø', 'õ', 'œ', 'ω' };
172 | case LetterKey.P:
173 | return new char[] { 'ṗ', '₽', 'π', 'φ', 'ψ' };
174 | case LetterKey.R:
175 | return new char[] { 'ŕ', 'ř', 'ṙ', '₹', 'ρ' };
176 | case LetterKey.S:
177 | return new char[] { 'ś', 'ş', 'š', 'ș', 'ṡ', 'ŝ', 'ß', 'σ', '$' };
178 | case LetterKey.T:
179 | return new char[] { 'ţ', 'ť', 'ț', 'ṫ', 'ŧ', 'θ', 'τ' };
180 | case LetterKey.U:
181 | return new char[] { 'û', 'ú', 'ü', 'ŭ', 'ű', 'ù', 'ů', 'ū', 'ų' };
182 | case LetterKey.W:
183 | return new char[] { 'ẇ', 'ŵ', '₩' };
184 | case LetterKey.X:
185 | return new char[] { 'ẋ', 'ξ' };
186 | case LetterKey.Y:
187 | return new char[] { 'ÿ', 'ŷ', 'ý', 'ẏ' };
188 | case LetterKey.Z:
189 | return new char[] { 'ź', 'ž', 'ż', 'ʒ', 'ǯ', 'ζ' };
190 | case LetterKey._:
191 | return new char[] { '¿', '¡', '∙', '₋', '⁻', '–', '≤', '≥', '≠', '≈', '≙', '±', '₊', '⁺', 'þ' };
192 | }
193 |
194 | return Array.Empty();
195 | }
196 |
197 | // Bulgarian
198 | // Alphabet: "АаБбВвГгДдЕеЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъьЮюЯя"
199 | private static char[] GetDefaultLetterKeyBG(LetterKey letter)
200 | {
201 | switch (letter)
202 | {
203 | // There could be more cases ?
204 | case LetterKey.I:
205 | return new char[] { 'ѝ', 'Ѝ' };
206 | }
207 |
208 | return Array.Empty();
209 | }
210 |
211 | // Currencies (source: https://www.eurochange.co.uk/travel-money/world-currency-abbreviations-symbols-and-codes-travel-money)
212 | private static char[] GetDefaultLetterKeyCUR(LetterKey letter)
213 | {
214 | switch (letter)
215 | {
216 | case LetterKey.B:
217 | return new char[] { '฿', 'в' };
218 | case LetterKey.C:
219 | return new char[] { '¢', '₡', 'č' };
220 | case LetterKey.D:
221 | return new char[] { '₫' };
222 | case LetterKey.E:
223 | return new char[] { '€' };
224 | case LetterKey.F:
225 | return new char[] { 'ƒ' };
226 | case LetterKey.H:
227 | return new char[] { '₴' };
228 | case LetterKey.K:
229 | return new char[] { '₭' };
230 | case LetterKey.L:
231 | return new char[] { 'ł' };
232 | case LetterKey.N:
233 | return new char[] { 'л' };
234 | case LetterKey.M:
235 | return new char[] { '₼' };
236 | case LetterKey.P:
237 | return new char[] { '£', '₽' };
238 | case LetterKey.R:
239 | return new char[] { '₹', '៛', '﷼' };
240 | case LetterKey.S:
241 | return new char[] { '$', '₪' };
242 | case LetterKey.T:
243 | return new char[] { '₮', '₺' };
244 | case LetterKey.W:
245 | return new char[] { '₩' };
246 | case LetterKey.Y:
247 | return new char[] { '¥' };
248 | case LetterKey.Z:
249 | return new char[] { 'z' };
250 | }
251 |
252 | return Array.Empty();
253 | }
254 |
255 | //Croatian
256 | private static char[] GetDefaultLetterKeyHR(LetterKey letter)
257 | {
258 | switch (letter)
259 | {
260 |
261 | case LetterKey.C:
262 | return new char[] { 'ć', 'č' };
263 | case LetterKey.D:
264 | return new char[] { 'đ' };
265 | case LetterKey.S:
266 | return new char[] { 'š' };
267 | case LetterKey.Z:
268 | return new char[] { 'ž' };
269 | }
270 |
271 | return Array.Empty();
272 | }
273 |
274 | // Finnish
275 | private static char[] GetDefaultLetterKeyFI(LetterKey letter)
276 | {
277 | switch (letter)
278 | {
279 | case LetterKey.A:
280 | return new char[] { 'ä', 'å' };
281 | case LetterKey.E:
282 | return new char[] { '€' };
283 | case LetterKey.O:
284 | return new char[] { 'ö' };
285 | }
286 |
287 | return Array.Empty();
288 | }
289 |
290 | // French
291 | private static char[] GetDefaultLetterKeyFR(LetterKey letter)
292 | {
293 | switch (letter)
294 | {
295 | case LetterKey.A:
296 | return new char[] { 'à', 'â', 'á', 'ä', 'ã', 'æ' };
297 | case LetterKey.C:
298 | return new char[] { 'ç' };
299 | case LetterKey.E:
300 | return new char[] { 'é', 'è', 'ê', 'ë', '€' };
301 | case LetterKey.I:
302 | return new char[] { 'î', 'ï', 'í', 'ì' };
303 | case LetterKey.O:
304 | return new char[] { 'ô', 'ö', 'ó', 'ò', 'õ', 'œ' };
305 | case LetterKey.U:
306 | return new char[] { 'û', 'ù', 'ü', 'ú' };
307 | case LetterKey.Y:
308 | return new char[] { 'ÿ', 'ý' };
309 | }
310 |
311 | return Array.Empty();
312 | }
313 |
314 | // Icelandic
315 | private static char[] GetDefaultLetterKeyIS(LetterKey letter)
316 | {
317 | switch (letter)
318 | {
319 | case LetterKey.A: return new char[] { 'á', 'æ' };
320 | case LetterKey.D: return new char[] { 'ð' };
321 | case LetterKey.E: return new char[] { 'é' };
322 | case LetterKey.O: return new char[] { 'ó', 'ö' };
323 | case LetterKey.U: return new char[] { 'ú' };
324 | case LetterKey.Y: return new char[] { 'ý' };
325 | case LetterKey._: return new char[] { 'þ' };
326 | }
327 |
328 | return Array.Empty();
329 | }
330 |
331 | // Spanish
332 | private static char[] GetDefaultLetterKeySP(LetterKey letter)
333 | {
334 | switch (letter)
335 | {
336 | case LetterKey.A:
337 | return new char[] { 'á' };
338 | case LetterKey.E:
339 | return new char[] { 'é', '€' };
340 | case LetterKey.I:
341 | return new char[] { 'í' };
342 | case LetterKey.N:
343 | return new char[] { 'ñ' };
344 | case LetterKey.O:
345 | return new char[] { 'ó' };
346 | case LetterKey.U:
347 | return new char[] { 'ú', 'ü' };
348 | case LetterKey._:
349 | return new char[] { '¿', '¡' };
350 | }
351 |
352 | return Array.Empty();
353 | }
354 |
355 | // Swedish
356 | private static char[] GetDefaultLetterKeySV(LetterKey letter)
357 | {
358 | switch (letter)
359 | {
360 | case LetterKey.A:
361 | return new char[] { 'å', 'ä' };
362 | case LetterKey.O:
363 | return new char[] { 'ö' };
364 | }
365 |
366 | return Array.Empty();
367 | }
368 |
369 | // Maori
370 | private static char[] GetDefaultLetterKeyMI(LetterKey letter)
371 | {
372 | switch (letter)
373 | {
374 | case LetterKey.A:
375 | return new char[] { 'ā' };
376 | case LetterKey.E:
377 | return new char[] { 'ē' };
378 | case LetterKey.I:
379 | return new char[] { 'ī' };
380 | case LetterKey.O:
381 | return new char[] { 'ō' };
382 | case LetterKey.S:
383 | return new char[] { '$' };
384 | case LetterKey.U:
385 | return new char[] { 'ū' };
386 | }
387 |
388 | return Array.Empty();
389 | }
390 |
391 | // Netherlands
392 | private static char[] GetDefaultLetterKeyNL(LetterKey letter)
393 | {
394 | switch (letter)
395 | {
396 | case LetterKey.A:
397 | return new char[] { 'á', 'à', 'ä' };
398 | case LetterKey.C:
399 | return new char[] { 'ç' };
400 | case LetterKey.E:
401 | return new char[] { 'é', 'è', 'ë', 'ê', '€' };
402 | case LetterKey.I:
403 | return new char[] { 'í', 'ï', 'î' };
404 | case LetterKey.N:
405 | return new char[] { 'ñ' };
406 | case LetterKey.O:
407 | return new char[] { 'ó', 'ö', 'ô' };
408 | case LetterKey.U:
409 | return new char[] { 'ú', 'ü', 'û' };
410 | }
411 |
412 | return Array.Empty();
413 | }
414 |
415 | // Pinyin
416 | private static char[] GetDefaultLetterKeyPI(LetterKey letter)
417 | {
418 | switch (letter)
419 | {
420 | case LetterKey.A:
421 | return new char[] { 'ā', 'á', 'ǎ', 'à', 'a' };
422 | case LetterKey.E:
423 | return new char[] { 'ē', 'é', 'ě', 'è', 'e' };
424 | case LetterKey.I:
425 | return new char[] { 'ī', 'í', 'ǐ', 'ì', 'i' };
426 | case LetterKey.O:
427 | return new char[] { 'ō', 'ó', 'ǒ', 'ò', 'o' };
428 | case LetterKey.U:
429 | return new char[] { 'ū', 'ú', 'ǔ', 'ù', 'u' };
430 | case LetterKey.V:
431 | return new char[] { 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'ü' };
432 | }
433 |
434 | return Array.Empty();
435 | }
436 |
437 | // Turkish
438 | private static char[] GetDefaultLetterKeyTK(LetterKey letter)
439 | {
440 | switch (letter)
441 | {
442 | case LetterKey.A:
443 | return new char[] { 'â' };
444 | case LetterKey.C:
445 | return new char[] { 'ç' };
446 | case LetterKey.E:
447 | return new char[] { 'ë', '€' };
448 | case LetterKey.G:
449 | return new char[] { 'ğ' };
450 | case LetterKey.I:
451 | return new char[] { 'ı', 'İ', 'î' };
452 | case LetterKey.L:
453 | return new char[] { '₺' };
454 | case LetterKey.O:
455 | return new char[] { 'ö', 'ô' };
456 | case LetterKey.S:
457 | return new char[] { 'ş' };
458 | case LetterKey.U:
459 | return new char[] { 'ü', 'û' };
460 | }
461 |
462 | return Array.Empty();
463 | }
464 |
465 | // Polish
466 | private static char[] GetDefaultLetterKeyPL(LetterKey letter)
467 | {
468 | switch (letter)
469 | {
470 | case LetterKey.A:
471 | return new char[] { 'ą' };
472 | case LetterKey.C:
473 | return new char[] { 'ć' };
474 | case LetterKey.E:
475 | return new char[] { 'ę', '€' };
476 | case LetterKey.L:
477 | return new char[] { 'ł' };
478 | case LetterKey.N:
479 | return new char[] { 'ń' };
480 | case LetterKey.O:
481 | return new char[] { 'ó' };
482 | case LetterKey.S:
483 | return new char[] { 'ś' };
484 | case LetterKey.Z:
485 | return new char[] { 'ż', 'ź' };
486 | }
487 |
488 | return Array.Empty();
489 | }
490 |
491 | // Portuguese
492 | private static char[] GetDefaultLetterKeyPT(LetterKey letter)
493 | {
494 | switch (letter)
495 | {
496 | case LetterKey.A:
497 | return new char[] { 'á', 'à', 'â', 'ã' };
498 | case LetterKey.C:
499 | return new char[] { 'ç' };
500 | case LetterKey.E:
501 | return new char[] { 'é', 'ê', '€' };
502 | case LetterKey.I:
503 | return new char[] { 'í' };
504 | case LetterKey.O:
505 | return new char[] { 'ó', 'ô', 'õ' };
506 | case LetterKey.P:
507 | return new char[] { 'π' };
508 | case LetterKey.U:
509 | return new char[] { 'ú', 'ü' };
510 | }
511 |
512 | return Array.Empty();
513 | }
514 |
515 | // Slovak
516 | private static char[] GetDefaultLetterKeySK(LetterKey letter)
517 | {
518 | switch (letter)
519 | {
520 | case LetterKey.A:
521 | return new char[] { 'á', 'ä' };
522 | case LetterKey.C:
523 | return new char[] { 'č' };
524 | case LetterKey.D:
525 | return new char[] { 'ď' };
526 | case LetterKey.E:
527 | return new char[] { 'é', '€' };
528 | case LetterKey.I:
529 | return new char[] { 'í' };
530 | case LetterKey.L:
531 | return new char[] { 'ľ', 'ĺ' };
532 | case LetterKey.N:
533 | return new char[] { 'ň' };
534 | case LetterKey.O:
535 | return new char[] { 'ó', 'ô' };
536 | case LetterKey.R:
537 | return new char[] { 'ŕ' };
538 | case LetterKey.S:
539 | return new char[] { 'š' };
540 | case LetterKey.T:
541 | return new char[] { 'ť' };
542 | case LetterKey.U:
543 | return new char[] { 'ú' };
544 | case LetterKey.Y:
545 | return new char[] { 'ý' };
546 | case LetterKey.Z:
547 | return new char[] { 'ž' };
548 | }
549 |
550 | return Array.Empty();
551 | }
552 |
553 | // Czech
554 | private static char[] GetDefaultLetterKeyCZ(LetterKey letter)
555 | {
556 | switch (letter)
557 | {
558 | case LetterKey.A:
559 | return new char[] { 'á' };
560 | case LetterKey.C:
561 | return new char[] { 'č' };
562 | case LetterKey.D:
563 | return new char[] { 'ď' };
564 | case LetterKey.E:
565 | return new char[] { 'ě', 'é' };
566 | case LetterKey.I:
567 | return new char[] { 'í' };
568 | case LetterKey.N:
569 | return new char[] { 'ň' };
570 | case LetterKey.O:
571 | return new char[] { 'ó' };
572 | case LetterKey.R:
573 | return new char[] { 'ř' };
574 | case LetterKey.S:
575 | return new char[] { 'š' };
576 | case LetterKey.T:
577 | return new char[] { 'ť' };
578 | case LetterKey.U:
579 | return new char[] { 'ů', 'ú' };
580 | case LetterKey.Y:
581 | return new char[] { 'ý' };
582 | case LetterKey.Z:
583 | return new char[] { 'ž' };
584 | }
585 |
586 | return Array.Empty();
587 | }
588 |
589 | // Germany
590 | private static char[] GetDefaultLetterKeyDE(LetterKey letter)
591 | {
592 | switch (letter)
593 | {
594 | case LetterKey.A:
595 | return new char[] { 'ä' };
596 | case LetterKey.E:
597 | return new char[] { '€' };
598 | case LetterKey.O:
599 | return new char[] { 'ö' };
600 | case LetterKey.S:
601 | return new char[] { 'ß' };
602 | case LetterKey.U:
603 | return new char[] { 'ü' };
604 | }
605 |
606 | return Array.Empty();
607 | }
608 |
609 | // Hungarian
610 | private static char[] GetDefaultLetterKeyHU(LetterKey letter)
611 | {
612 | switch (letter)
613 | {
614 | case LetterKey.A:
615 | return new char[] { 'á' };
616 | case LetterKey.E:
617 | return new char[] { 'é' };
618 | case LetterKey.I:
619 | return new char[] { 'í' };
620 | case LetterKey.O:
621 | return new char[] { 'ó', 'ő', 'ö' };
622 | case LetterKey.U:
623 | return new char[] { 'ú', 'ű', 'ü' };
624 | }
625 |
626 | return Array.Empty();
627 | }
628 |
629 | // Romanian
630 | private static char[] GetDefaultLetterKeyRO(LetterKey letter)
631 | {
632 | switch (letter)
633 | {
634 | case LetterKey.A:
635 | return new char[] { 'ă', 'â' };
636 | case LetterKey.I:
637 | return new char[] { 'î' };
638 | case LetterKey.S:
639 | return new char[] { 'ș' };
640 | case LetterKey.T:
641 | return new char[] { 'ț' };
642 | }
643 |
644 | return Array.Empty();
645 | }
646 |
647 | // Italian
648 | private static char[] GetDefaultLetterKeyIT(LetterKey letter)
649 | {
650 | switch (letter)
651 | {
652 | case LetterKey.A:
653 | return new char[] { 'à' };
654 | case LetterKey.E:
655 | return new char[] { 'è', 'é', '€' };
656 | case LetterKey.I:
657 | return new char[] { 'ì', 'í' };
658 | case LetterKey.O:
659 | return new char[] { 'ò', 'ó' };
660 | case LetterKey.U:
661 | return new char[] { 'ù', 'ú' };
662 | }
663 |
664 | return Array.Empty();
665 | }
666 |
667 | // Japanese
668 | private static char[] GetDefaultLetterKeyJP(LetterKey letter)
669 | {
670 | switch (letter)
671 | {
672 | case LetterKey.A:
673 | return new char[] { 'ā', 'â' };
674 | case LetterKey.E:
675 | return new char[] { 'ē', 'ê' };
676 | case LetterKey.I:
677 | return new char[] { 'ī', 'î' };
678 | case LetterKey.O:
679 | return new char[] { 'ō', 'ô' };
680 | case LetterKey.U:
681 | return new char[] { 'ū', 'û' };
682 | case LetterKey.Y:
683 | return new char[] { '¥' };
684 | }
685 |
686 | return Array.Empty();
687 | }
688 |
689 | // Welsh
690 | private static char[] GetDefaultLetterKeyCY(LetterKey letter)
691 | {
692 | switch (letter)
693 | {
694 | case LetterKey.A:
695 | return new char[] { 'â' };
696 | case LetterKey.E:
697 | return new char[] { 'ê' };
698 | case LetterKey.I:
699 | return new char[] { 'î' };
700 | case LetterKey.O:
701 | return new char[] { 'ô' };
702 | case LetterKey.U:
703 | return new char[] { 'û' };
704 | case LetterKey.Y:
705 | return new char[] { 'ŷ' };
706 | case LetterKey.W:
707 | return new char[] { 'ŵ' };
708 | }
709 |
710 | return Array.Empty();
711 | }
712 |
713 | // Serbian
714 | private static char[] GetDefaultLetterKeySR(LetterKey letter)
715 | {
716 | switch (letter)
717 | {
718 | case LetterKey.C:
719 | return new char[] { 'ć', 'č' };
720 | case LetterKey.D:
721 | return new char[] { 'đ' };
722 | case LetterKey.S:
723 | return new char[] { 'š' };
724 | case LetterKey.Z:
725 | return new char[] { 'ž' };
726 | }
727 |
728 | return Array.Empty();
729 | }
730 |
731 | // Sanskrit
732 | private static char[] GetDefaultLetterKeySA(LetterKey letter)
733 | {
734 | switch (letter)
735 | {
736 | case LetterKey.A:
737 | return new char[] { 'ā', 'ǣ' };
738 | case LetterKey.C:
739 | return new char[] { 'ĉ' };
740 | case LetterKey.D:
741 | return new char[] { 'ḍ' };
742 | case LetterKey.E:
743 | return new char[] { 'ē', 'ê' };
744 | case LetterKey.G:
745 | return new char[] { 'ġ' };
746 | case LetterKey.H:
747 | return new char[] { 'ḥ', 'ẖ', 'ḫ' };
748 | case LetterKey.I:
749 | return new char[] { 'ī' };
750 | case LetterKey.K:
751 | return new char[] { 'ḵ' };
752 | case LetterKey.L:
753 | return new char[] { 'ḷ', 'ḻ' };
754 | case LetterKey.M:
755 | return new char[] { 'ṃ', 'ṁ' };
756 | case LetterKey.N:
757 | return new char[] { 'ṅ', 'ñ' };
758 | case LetterKey.O:
759 | return new char[] { 'ō', 'ô' };
760 | case LetterKey.R:
761 | return new char[] { 'ṛ', 'ṝ', 'ṟ', 'ṙ' };
762 | case LetterKey.S:
763 | return new char[] { 'ś', 'ṣ' };
764 | case LetterKey.T:
765 | return new char[] { 'ṭ', 'ṯ' };
766 | case LetterKey.U:
767 | return new char[] { 'ū', 'ŭ' };
768 | case LetterKey.Y:
769 | return new char[] { 'ẏ' };
770 | case LetterKey.Z:
771 | return new char[] { 'ẓ', 'ẕ', 'ź', 'ž' };
772 | }
773 |
774 | return Array.Empty();
775 | }
776 |
777 | }
778 |
--------------------------------------------------------------------------------