Skip to content
Open
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
8 changes: 8 additions & 0 deletions FluentFlyoutWPF/Classes/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ public static partial class NativeMethods
internal const int WM_KEYDOWN = 0x0100;
internal const int WM_KEYUP = 0x0101;
internal const int WM_SETTINGCHANGE = 0x001A;
internal const int WM_INPUTLANGCHANGE = 0x0051;

// Shell Hook Messages
internal const int HSHELL_APPCOMMAND = 12;
internal const int HSHELL_LANGUAGE = 0x0037;

// App Command Messages
internal const int APPCOMMAND_VOLUME_MUTE = 8;
Expand Down Expand Up @@ -261,6 +263,12 @@ internal struct WindowCompositionAttributeData
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool IsIconic(IntPtr hWnd);

[LibraryImport("user32.dll", SetLastError = true)]
internal static partial uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

[LibraryImport("user32.dll")]
internal static partial IntPtr GetKeyboardLayout(uint idThread);

[LibraryImport("user32.dll", EntryPoint = "SetWindowLongW")]
internal static partial int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

Expand Down
24 changes: 24 additions & 0 deletions FluentFlyoutWPF/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public partial class MainWindow : MicaWindow
private bool _isHiding = true;

private LockWindow? lockWindow;
private IntPtr _lastLanguageLayout = IntPtr.Zero;
private DateTime _lastSelfUpdateTimestamp = DateTime.MinValue;

internal TaskbarWindow? taskbarWindow;
Expand Down Expand Up @@ -177,6 +178,7 @@ public MainWindow()
RegisterShellHookWindow(new WindowInteropHelper(this).Handle);

_positionTimer = new Timer(SeekbarUpdateUi, null, Timeout.Infinite, Timeout.Infinite);

if (_seekBarEnabled && GetActiveMediaSession() is { } session)
{
UpdateSeekbarCurrentDuration(session.ControlSession.GetTimelineProperties().Position);
Expand Down Expand Up @@ -854,6 +856,19 @@ private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
lockWindow.ShowLockFlyout("Insert", Keyboard.IsKeyToggled(Key.Insert));
}
}

if (SettingsManager.Current.LanguageFlyoutEnabled && !FullscreenDetector.IsFullscreenApplicationRunning())
{
IntPtr fgWindow = NativeMethods.GetForegroundWindow();
IntPtr hkl = NativeMethods.GetKeyboardLayout(NativeMethods.GetWindowThreadProcessId(fgWindow, IntPtr.Zero));

if (hkl != IntPtr.Zero && _lastLanguageLayout != IntPtr.Zero && hkl != _lastLanguageLayout)
{
lockWindow ??= new LockWindow();
lockWindow.ShowLanguageFlyout();
}
if (hkl != IntPtr.Zero) _lastLanguageLayout = hkl;
}
}
return CallNextHookEx(_hookId, nCode, wParam, lParam);
}
Expand Down Expand Up @@ -1525,6 +1540,15 @@ private async Task<bool> WaitForExplorerReadyAsync(int timeoutMs = 60000)

private nint WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
{
if (msg == WM_INPUTLANGCHANGE || (msg == WM_SHELLHOOK && (int)wParam == HSHELL_LANGUAGE))
{
if (SettingsManager.Current.LanguageFlyoutEnabled)
{
lockWindow ??= new LockWindow();
lockWindow.ShowLanguageFlyout();
}
}

// detect key presses from both keyboard hook and shell hook to show flyouts
if (msg == WM_SHELLHOOK && wParam == HSHELL_APPCOMMAND)
{
Expand Down
53 changes: 52 additions & 1 deletion FluentFlyoutWPF/Pages/LockKeysPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="0,0,0,24">
<Border CornerRadius="10" ClipToBounds="True" Width="164" Height="98">
Expand Down Expand Up @@ -124,7 +128,7 @@
</ui:CardControl.Header>
<ui:ToggleSwitch Name="LockKeysNumSwitch" IsChecked="{Binding LockKeysNumEnabled, Mode=TwoWay}" />
</ui:CardControl>
<ui:CardControl x:Name="EnableScrollTitleCard" Tag="Indexable" Grid.Row="10" Icon="{ui:SymbolIcon ArrowBetweenDown24}" Margin="0,0,0,76">
<ui:CardControl x:Name="EnableScrollTitleCard" Tag="Indexable" Grid.Row="10" Icon="{ui:SymbolIcon ArrowBetweenDown24}" Margin="0,0,0,3">
<ui:CardControl.Header>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
Expand All @@ -136,6 +140,53 @@
</ui:CardControl.Header>
<ui:ToggleSwitch Name="LockKeysScrollSwitch" IsChecked="{Binding LockKeysScrollEnabled, Mode=TwoWay}" />
</ui:CardControl>

<ui:CardControl x:Name="LanguageFlyoutSwitchCard" Tag="Indexable" Grid.Row="11" Icon="{ui:SymbolIcon LocalLanguage24}" Margin="0,24,0,3">
<ui:CardControl.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="{DynamicResource LanguageFlyoutTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
<TextBlock Text="{DynamicResource LanguageFlyoutDescription}" FontSize="12" Opacity="0.5" />
</StackPanel>
</ui:CardControl.Header>
<ui:ToggleSwitch Name="LanguageFlyoutSwitch" IsChecked="{Binding LanguageFlyoutEnabled, Mode=TwoWay}" />
</ui:CardControl>

<ui:CardControl x:Name="LanguageFlyoutShowRegionCard" Tag="Indexable" Grid.Row="12" Icon="{ui:SymbolIcon Info24}" Margin="0,0,0,3">
<ui:CardControl.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="{DynamicResource LanguageFlyoutShowRegionTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
<TextBlock Text="{DynamicResource LanguageFlyoutShowRegionDescription}" FontSize="12" Opacity="0.5" />
</StackPanel>
</ui:CardControl.Header>
<ui:ToggleSwitch Name="LanguageFlyoutShowRegionSwitch" IsChecked="{Binding LanguageFlyoutShowRegion, Mode=TwoWay}" />
</ui:CardControl>

<ui:CardControl x:Name="LanguageFlyoutDurationCard" Tag="Indexable" Grid.Row="13" Icon="{ui:SymbolIcon Timer24}" Margin="0,0,0,3">
<ui:CardControl.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="{DynamicResource LanguageFlyoutDurationTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
<TextBlock Text="{DynamicResource LanguageFlyoutDurationDescription}" FontSize="12" Opacity="0.5" />
</StackPanel>
</ui:CardControl.Header>
<StackPanel Orientation="Horizontal">
<ui:TextBox Width="60" ClearButtonEnabled="False" Text="{Binding LanguageFlyoutDurationText, Mode=TwoWay}" />
<TextBlock Text="{DynamicResource MillisecondsUnit}" FontSize="14" FontWeight="Regular" Margin="10,0,0,0" VerticalAlignment="Center" />
</StackPanel>
</ui:CardControl>

<ui:CardControl x:Name="LanguageFlyoutWidthCard" Tag="Indexable" Grid.Row="14" Icon="{ui:SymbolIcon ArrowFit24}" Margin="0,0,0,76">
<ui:CardControl.Header>
<StackPanel Orientation="Vertical">
<TextBlock Text="{DynamicResource LanguageFlyoutWidthTitle}" FontSize="14" FontWeight="Regular" VerticalAlignment="Center" />
<TextBlock Text="{DynamicResource LanguageFlyoutWidthDescription}" FontSize="12" Opacity="0.5" />
</StackPanel>
</ui:CardControl.Header>
<StackPanel Orientation="Horizontal">
<Slider Minimum="160" Maximum="400" Value="{Binding LanguageFlyoutWidth, Mode=TwoWay}" TickFrequency="10" IsSnapToTickEnabled="True" Width="150" VerticalAlignment="Center" />
<ui:TextBlock Text="{Binding LanguageFlyoutWidth}" Width="30" TextAlignment="Center" Margin="12,0,0,0" VerticalAlignment="Center" />
<TextBlock Text="px" FontSize="14" FontWeight="Regular" Margin="4,0,0,0" VerticalAlignment="Center" Opacity="0.5" />
</StackPanel>
</ui:CardControl>
</Grid>
</StackPanel>
</Page>
8 changes: 8 additions & 0 deletions FluentFlyoutWPF/Resources/Localization/Dictionary-en-US.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ You can help contribute translations on</System:String>
<System:String x:Key="LockWindow_InsertPressed">Insert Pressed</System:String>
<System:String x:Key="LockWindow_LockOn">on</System:String>
<System:String x:Key="LockWindow_LockOff">off</System:String>
<System:String x:Key="LanguageFlyoutTitle">Language Flyout</System:String>
<System:String x:Key="LanguageFlyoutDescription">Show flyout when switching keyboard input layout</System:String>
<System:String x:Key="LanguageFlyoutShowRegionTitle">Show Country / Region</System:String>
<System:String x:Key="LanguageFlyoutShowRegionDescription">Include country or region in parentheses (e.g. English (United States))</System:String>
<System:String x:Key="LanguageFlyoutDurationTitle">Display Duration</System:String>
<System:String x:Key="LanguageFlyoutDurationDescription">Duration the language flyout stays on screen</System:String>
<System:String x:Key="LanguageFlyoutWidthTitle">Flyout Width</System:String>
<System:String x:Key="LanguageFlyoutWidthDescription">Custom width of the language flyout in pixels</System:String>
<System:String x:Key="NextUpWindow_UpNextText">Up next:</System:String>
<System:String x:Key="TrayIcon_SettingsOption">Settings</System:String>
<System:String x:Key="TrayIcon_GitHubRepositoryOption">Repository</System:String>
Expand Down
8 changes: 8 additions & 0 deletions FluentFlyoutWPF/Resources/Localization/Dictionary-ru.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,12 @@
<system:String x:Key="TaskbarWidgetScrollingTextSpeedTitle">Скорость прокрутки</system:String>
<system:String x:Key="Back">Назад</system:String>
<system:String x:Key="OnboardingStepsCounter">Шаг {0} из {1}</system:String>
<system:String x:Key="LanguageFlyoutTitle">Окно раскладки клавиатуры</system:String>
<system:String x:Key="LanguageFlyoutDescription">Отображать всплывающее окно при переключении языка ввода</system:String>
<system:String x:Key="LanguageFlyoutShowRegionTitle">Показывать страну / регион</system:String>
<system:String x:Key="LanguageFlyoutShowRegionDescription">Включать страну или регион в скобках (например, Русский (Россия))</system:String>
<system:String x:Key="LanguageFlyoutDurationTitle">Время отображения</system:String>
<system:String x:Key="LanguageFlyoutDurationDescription">Длительность отображения окна смены языка</system:String>
<system:String x:Key="LanguageFlyoutWidthTitle">Ширина окна</system:String>
<system:String x:Key="LanguageFlyoutWidthDescription">Настраиваемая ширина окна смены языка в пикселях</system:String>
</ResourceDictionary>
52 changes: 52 additions & 0 deletions FluentFlyoutWPF/ViewModels/UserSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,55 @@ public string LockKeysDurationText
}
}

/// <summary>
/// Enable language switcher flyout
/// </summary>
[ObservableProperty]
public partial bool LanguageFlyoutEnabled { get; set; }

/// <summary>
/// Show region/country in parentheses in language flyout
/// </summary>
[ObservableProperty]
public partial bool LanguageFlyoutShowRegion { get; set; }

/// <summary>
/// Language flyout display duration (milliseconds)
/// </summary>
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(LanguageFlyoutDurationText))]
public partial int LanguageFlyoutDuration { get; set; }

[XmlIgnore]
public string LanguageFlyoutDurationText
{
get => LanguageFlyoutDuration.ToString();
set
{
if (int.TryParse(value, out var result))
{
LanguageFlyoutDuration = result switch
{
> 10000 => 10000,
< 0 => 0,
_ => result
};
}
else
{
LanguageFlyoutDuration = 2000;
}

OnPropertyChanged();
}
}

/// <summary>
/// Language flyout width (pixels)
/// </summary>
[ObservableProperty]
public partial int LanguageFlyoutWidth { get; set; }

/// <summary>
/// App theme. 0 for default, 1 for light, 2 for dark.
/// </summary>
Expand Down Expand Up @@ -711,6 +760,9 @@ public UserSettings()
LockKeysNumEnabled = true;
LockKeysScrollEnabled = true;
LockKeysDuration = 2000;
LanguageFlyoutEnabled = true;
LanguageFlyoutDuration = 2000;
LanguageFlyoutWidth = 220;
AppTheme = 0;
MediaFlyoutEnabled = true;
MediaFlyoutAlwaysDisplay = false;
Expand Down
Loading