Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: keyboard language component #514

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions GlazeWM.Bar/BarViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ private List<ComponentViewModel> CreateComponentViewModels(
MemoryComponentConfig rampc => new MemoryComponentViewModel(this, rampc),
TextFileComponentConfig stc => new TextFileComponentViewModel(this, stc),
MusicComponentConfig mcc => new MusicComponentViewModel(this, mcc),
InputLanguageComponentConfig ilcc => new InputLanguageComponentViewModel(this, ilcc),
_ => throw new ArgumentOutOfRangeException(nameof(config)),
});
}
Expand Down
5 changes: 5 additions & 0 deletions GlazeWM.Bar/Components/ComponentPortal.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@
Padding="{Binding Padding}"
Background="{Binding Background}" />
</DataTemplate>
<DataTemplate DataType="{x:Type components:InputLanguageComponentViewModel}">
<components:InputLanguageComponent
Padding="{Binding Padding}"
Background="{Binding Background}" />
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
Expand Down
10 changes: 10 additions & 0 deletions GlazeWM.Bar/Components/InputLanguageComponent.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<UserControl
x:Class="GlazeWM.Bar.Components.InputLanguageComponent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:components="clr-namespace:GlazeWM.Bar.Components"
mc:Ignorable="d">
<components:LabelComponent DataContext="{Binding Label}" />
</UserControl>
12 changes: 12 additions & 0 deletions GlazeWM.Bar/Components/InputLanguageComponent.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Windows.Controls;

namespace GlazeWM.Bar.Components
{
/// <summary>
/// Interaction logic for InputLanguageComponent.xaml
/// </summary>
public partial class InputLanguageComponent : UserControl
{
public InputLanguageComponent() => InitializeComponent();
}
}
73 changes: 73 additions & 0 deletions GlazeWM.Bar/Components/InputLanguageComponentViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using GlazeWM.Domain.UserConfigs;
using static GlazeWM.Infrastructure.WindowsApi.WindowsApiService;

namespace GlazeWM.Bar.Components
{
public class InputLanguageComponentViewModel : ComponentViewModel
{
private const uint LOCALE_ALLOW_NEUTRAL_NAMES = 0x08000000;

private readonly InputLanguageComponentConfig _config;

private LabelViewModel _label;
public LabelViewModel Label
{
get => _label;
protected set => SetField(ref _label, value);
}

public InputLanguageComponentViewModel(
BarViewModel parentViewModel,
InputLanguageComponentConfig config) : base(parentViewModel, config)
{
_config = config;

var updateInterval = TimeSpan.FromMilliseconds(_config.RefreshIntervalMs);

Observable
.Interval(updateInterval)
.TakeUntil(_parentViewModel.WindowClosing)
.Subscribe(_ => Label = CreateLabel());
}

private LabelViewModel CreateLabel()
{
var variableDictionary = new Dictionary<string, Func<string>>()
{
{
"input_language",
GetInputLanguage
}
};

return XamlHelper.ParseLabel(_config.Label, variableDictionary, this);
}

private static string GetInputLanguage()
{
var layout = GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero));

// If the layout is larger than this, need different handling.
const ulong big = 0xffffffff;

uint layoutId;
if ((ulong)layout > big)
{
layoutId = (uint)layout & 0xffff;
}
else
{
layoutId = (uint)layout >> 16;
}

var sb = new StringBuilder();
_ = LCIDToLocaleName(layoutId, sb, sb.Capacity, LOCALE_ALLOW_NEUTRAL_NAMES);

return sb.ToString();
}
}
}
4 changes: 4 additions & 0 deletions GlazeWM.Domain/UserConfigs/BarComponentConfigConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ public override BarComponentConfig Read(
jsonObject.RootElement.ToString(),
options
),
"input language" => JsonSerializer.Deserialize<InputLanguageComponentConfig>(
jsonObject.RootElement.ToString(),
options
),
_ => throw new ArgumentException($"Invalid component type '{typeDiscriminator}'."),
};
}
Expand Down
8 changes: 8 additions & 0 deletions GlazeWM.Domain/UserConfigs/InputLanguageComponentConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace GlazeWM.Domain.UserConfigs
{
public class InputLanguageComponentConfig : BarComponentConfig
{
public string Label { get; set; } = "Input: {input_language}";
public int RefreshIntervalMs { get; set; } = 1000;
}
}
9 changes: 9 additions & 0 deletions GlazeWM.Infrastructure/WindowsApi/WindowsApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,12 @@ public struct LowLevelKeyboardInputEvent
public IntPtr AdditionalInformation;
}

[DllImport("user32.dll")]
public static extern IntPtr GetKeyboardLayout(uint idThread);

[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);

[DllImport("user32.dll")]
public static extern IntPtr SetWindowsHookEx(HookType hookType, [MarshalAs(UnmanagedType.FunctionPtr)] HookProc lpfn, IntPtr hMod, int dwThreadId);

Expand Down Expand Up @@ -448,6 +454,9 @@ public enum MonitorFromPointFlags : uint
[DllImport("User32.dll")]
public static extern IntPtr MonitorFromPoint(Point pt, MonitorFromPointFlags dwFlags);

[DllImport("kernel32.dll")]
public static extern int LCIDToLocaleName(uint locale, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpName, int cchName, uint dwFlags);

[DllImport("kernel32.dll")]
public static extern bool GetSystemPowerStatus(out SystemPowerStatus lpSystemPowerStatus);

Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,17 @@ Displays currently playing music.
max_artist_length: 20
```

### Bar Component: Input language

Displays current input language (keyboard layout).

```yaml
- type: "input language"
label: "Input: {input_language}"
# Optional: How often this counter is refreshed. Default 1000
refresh_interval_ms: 1000
```

## Mixing font properties within a label

Font family, font weight, font size, and foreground color can be changed within parts of a label. This means that icons and text fonts can be used together in a label. To customize a part of the label, wrap it in an <attr> tag:
Expand Down
Loading