diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index f9c548de863..54604a27118 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
+using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Windows;
@@ -517,5 +518,92 @@ public static bool IsNotificationSupported()
}
#endregion
+
+ #region Korean IME
+
+ public static bool IsWindows11()
+ {
+ return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
+ Environment.OSVersion.Version.Build >= 22000;
+ }
+
+ public static bool IsKoreanIMEExist()
+ {
+ return GetLegacyKoreanIMERegistryValue() != null;
+ }
+
+ public static bool IsLegacyKoreanIMEEnabled()
+ {
+ object value = GetLegacyKoreanIMERegistryValue();
+
+ if (value is int intValue)
+ {
+ return intValue == 1;
+ }
+ else if (value != null && int.TryParse(value.ToString(), out int parsedValue))
+ {
+ return parsedValue == 1;
+ }
+
+ return false;
+ }
+
+ public static bool SetLegacyKoreanIMEEnabled(bool enable)
+ {
+ const string subKeyPath = @"Software\Microsoft\input\tsf\tsf3override\{A028AE76-01B1-46C2-99C4-ACD9858AE02F}";
+ const string valueName = "NoTsf3Override5";
+
+ try
+ {
+ using RegistryKey key = Registry.CurrentUser.CreateSubKey(subKeyPath);
+ if (key != null)
+ {
+ int value = enable ? 1 : 0;
+ key.SetValue(valueName, value, RegistryValueKind.DWord);
+ return true;
+ }
+ }
+ catch (System.Exception)
+ {
+ // Ignored
+ }
+
+ return false;
+ }
+
+ public static object GetLegacyKoreanIMERegistryValue()
+ {
+ const string subKeyPath = @"Software\Microsoft\input\tsf\tsf3override\{A028AE76-01B1-46C2-99C4-ACD9858AE02F}";
+ const string valueName = "NoTsf3Override5";
+
+ try
+ {
+ using RegistryKey key = Registry.CurrentUser.OpenSubKey(subKeyPath);
+ if (key != null)
+ {
+ return key.GetValue(valueName);
+ }
+ }
+ catch (System.Exception)
+ {
+ // Ignored
+ }
+
+ return null;
+ }
+
+ public static void OpenImeSettings()
+ {
+ try
+ {
+ Process.Start(new ProcessStartInfo("ms-settings:regionlanguage") { UseShellExecute = true });
+ }
+ catch (System.Exception)
+ {
+ // Ignored
+ }
+ }
+
+ #endregion
}
}
diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index 609859d0dab..f1c0a67cbe6 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -111,6 +111,19 @@
Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average.
Default Search Delay Time
Wait time before showing results after typing stops. Higher values wait longer. (ms)
+ Information for Korean IME user
+
+ The Korean input method used in Windows 11 may cause some issues in Flow Launcher.
+ If you experience any problems, you may need to enable "Use previous version of Korean IME".
+ Open Setting in Windows 11 and go to:
+ Time & Language > Language & Region > Korean > Language Options > Keyboard - Microsoft IME > Compatibility,
+ and enable "Use previous version of Microsoft IME".
+
+ Open Language and Region System Settings
+ Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility
+ Open
+ Use Previous Korean IME
+ You can change the Previous Korean IME settings directly from here
Search Plugin
diff --git a/Flow.Launcher/Resources/Controls/InfoBar.xaml b/Flow.Launcher/Resources/Controls/InfoBar.xaml
new file mode 100644
index 00000000000..2ddcbdd0cc8
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/InfoBar.xaml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Flow.Launcher/Resources/Controls/InfoBar.xaml.cs b/Flow.Launcher/Resources/Controls/InfoBar.xaml.cs
new file mode 100644
index 00000000000..ebf763e22ab
--- /dev/null
+++ b/Flow.Launcher/Resources/Controls/InfoBar.xaml.cs
@@ -0,0 +1,222 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace Flow.Launcher.Resources.Controls
+{
+ public partial class InfoBar : UserControl
+ {
+ public InfoBar()
+ {
+ InitializeComponent();
+ Loaded += InfoBar_Loaded;
+ }
+
+ private void InfoBar_Loaded(object sender, RoutedEventArgs e)
+ {
+ UpdateStyle();
+ UpdateTitleVisibility();
+ UpdateMessageVisibility();
+ UpdateOrientation();
+ UpdateIconAlignmentAndMargin();
+ UpdateIconVisibility();
+ UpdateCloseButtonVisibility();
+ }
+
+ public static readonly DependencyProperty TypeProperty =
+ DependencyProperty.Register(nameof(Type), typeof(InfoBarType), typeof(InfoBar), new PropertyMetadata(InfoBarType.Info, OnTypeChanged));
+
+ public InfoBarType Type
+ {
+ get => (InfoBarType)GetValue(TypeProperty);
+ set => SetValue(TypeProperty, value);
+ }
+
+ private static void OnTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is InfoBar infoBar)
+ {
+ infoBar.UpdateStyle();
+ }
+ }
+
+ public static readonly DependencyProperty MessageProperty =
+ DependencyProperty.Register(nameof(Message), typeof(string), typeof(InfoBar), new PropertyMetadata(string.Empty, OnMessageChanged));
+
+ public string Message
+ {
+ get => (string)GetValue(MessageProperty);
+ set
+ {
+ SetValue(MessageProperty, value);
+ }
+ }
+
+ private static void OnMessageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is InfoBar infoBar)
+ {
+ infoBar.UpdateMessageVisibility();
+ }
+ }
+
+ private void UpdateMessageVisibility()
+ {
+ PART_Message.Visibility = string.IsNullOrEmpty(Message) ? Visibility.Collapsed : Visibility.Visible;
+ }
+
+ public static readonly DependencyProperty TitleProperty =
+ DependencyProperty.Register(nameof(Title), typeof(string), typeof(InfoBar), new PropertyMetadata(string.Empty, OnTitleChanged));
+
+ public string Title
+ {
+ get => (string)GetValue(TitleProperty);
+ set
+ {
+ SetValue(TitleProperty, value);
+ UpdateTitleVisibility(); // Visibility update when change Title
+ }
+ }
+
+ private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is InfoBar infoBar)
+ {
+ infoBar.UpdateTitleVisibility();
+ }
+ }
+
+ private void UpdateTitleVisibility()
+ {
+ PART_Title.Visibility = string.IsNullOrEmpty(Title) ? Visibility.Collapsed : Visibility.Visible;
+ }
+
+ public static readonly DependencyProperty IsIconVisibleProperty =
+ DependencyProperty.Register(nameof(IsIconVisible), typeof(bool), typeof(InfoBar), new PropertyMetadata(true, OnIsIconVisibleChanged));
+
+ public bool IsIconVisible
+ {
+ get => (bool)GetValue(IsIconVisibleProperty);
+ set => SetValue(IsIconVisibleProperty, value);
+ }
+
+ public static readonly DependencyProperty LengthProperty =
+ DependencyProperty.Register(nameof(Length), typeof(InfoBarLength), typeof(InfoBar), new PropertyMetadata(InfoBarLength.Short, OnLengthChanged));
+
+ public InfoBarLength Length
+ {
+ get { return (InfoBarLength)GetValue(LengthProperty); }
+ set { SetValue(LengthProperty, value); }
+ }
+
+ private static void OnLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is InfoBar infoBar)
+ {
+ infoBar.UpdateOrientation();
+ infoBar.UpdateIconAlignmentAndMargin();
+ }
+ }
+
+ private void UpdateOrientation()
+ {
+ PART_StackPanel.Orientation = Length == InfoBarLength.Long ? Orientation.Vertical : Orientation.Horizontal;
+ }
+
+ private void UpdateIconAlignmentAndMargin()
+ {
+ if (Length == InfoBarLength.Short)
+ {
+ PART_IconBorder.VerticalAlignment = VerticalAlignment.Center;
+ PART_IconBorder.Margin = new Thickness(0, 0, 12, 0);
+ }
+ else
+ {
+ PART_IconBorder.VerticalAlignment = VerticalAlignment.Top;
+ PART_IconBorder.Margin = new Thickness(0, 2, 12, 0);
+ }
+ }
+
+ public static readonly DependencyProperty ClosableProperty =
+ DependencyProperty.Register(nameof(Closable), typeof(bool), typeof(InfoBar), new PropertyMetadata(true, OnClosableChanged));
+
+ public bool Closable
+ {
+ get => (bool)GetValue(ClosableProperty);
+ set => SetValue(ClosableProperty, value);
+ }
+
+ private void PART_CloseButton_Click(object sender, RoutedEventArgs e)
+ {
+ Visibility = Visibility.Collapsed;
+ }
+
+ private void UpdateStyle()
+ {
+ switch (Type)
+ {
+ case InfoBarType.Info:
+ PART_Border.Background = (Brush)FindResource("InfoBarInfoBG");
+ PART_IconBorder.Background = (Brush)FindResource("InfoBarInfoIcon");
+ PART_Icon.Glyph = "\xF13F";
+ break;
+ case InfoBarType.Success:
+ PART_Border.Background = (Brush)FindResource("InfoBarSuccessBG");
+ PART_IconBorder.Background = (Brush)FindResource("InfoBarSuccessIcon");
+ PART_Icon.Glyph = "\xF13E";
+ break;
+ case InfoBarType.Warning:
+ PART_Border.Background = (Brush)FindResource("InfoBarWarningBG");
+ PART_IconBorder.Background = (Brush)FindResource("InfoBarWarningIcon");
+ PART_Icon.Glyph = "\xF13C";
+ break;
+ case InfoBarType.Error:
+ PART_Border.Background = (Brush)FindResource("InfoBarErrorBG");
+ PART_IconBorder.Background = (Brush)FindResource("InfoBarErrorIcon");
+ PART_Icon.Glyph = "\xF13D";
+ break;
+ default:
+ PART_Border.Background = (Brush)FindResource("InfoBarInfoBG");
+ PART_IconBorder.Background = (Brush)FindResource("InfoBarInfoIcon");
+ PART_Icon.Glyph = "\xF13F";
+ break;
+ }
+ }
+
+ private static void OnIsIconVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var infoBar = (InfoBar)d;
+ infoBar.UpdateIconVisibility();
+ }
+
+ private static void OnClosableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var infoBar = (InfoBar)d;
+ infoBar.UpdateCloseButtonVisibility();
+ }
+
+ private void UpdateIconVisibility()
+ {
+ PART_IconBorder.Visibility = IsIconVisible ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ private void UpdateCloseButtonVisibility()
+ {
+ PART_CloseButton.Visibility = Closable ? Visibility.Visible : Visibility.Collapsed;
+ }
+ }
+
+ public enum InfoBarType
+ {
+ Info,
+ Success,
+ Warning,
+ Error
+ }
+
+ public enum InfoBarLength
+ {
+ Short,
+ Long
+ }
+}
diff --git a/Flow.Launcher/Resources/Dark.xaml b/Flow.Launcher/Resources/Dark.xaml
index 498e96bffd3..594a92fbe11 100644
--- a/Flow.Launcher/Resources/Dark.xaml
+++ b/Flow.Launcher/Resources/Dark.xaml
@@ -106,7 +106,6 @@
#f5f5f5
#464646
#ffffff
-
#272727
@@ -115,10 +114,19 @@
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/Resources/Light.xaml b/Flow.Launcher/Resources/Light.xaml
index 0f1e98a6b95..aaa23c8e2d5 100644
--- a/Flow.Launcher/Resources/Light.xaml
+++ b/Flow.Launcher/Resources/Light.xaml
@@ -108,10 +108,20 @@
-
+
+
+
+
+
-
+
+
+
+
diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
index 069a7cb24d8..021c9d7fe61 100644
--- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
+++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneGeneralViewModel.cs
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Windows.Forms;
using CommunityToolkit.Mvvm.Input;
using Flow.Launcher.Core;
using Flow.Launcher.Core.Configuration;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
+using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
@@ -179,6 +181,70 @@ public string Language
}
}
+ #region Korean IME
+
+ // The new Korean IME used in Windows 11 has compatibility issues with WPF. This issue is difficult to resolve within
+ // WPF itself, but it can be avoided by having the user switch to the legacy IME at the system level. Therefore,
+ // we provide guidance and a direct button for users to make this change themselves. If the relevant registry key does
+ // not exist (i.e., the Korean IME is not installed), this setting will not be shown at all.
+
+ public bool LegacyKoreanIMEEnabled
+ {
+ get => Win32Helper.IsLegacyKoreanIMEEnabled();
+ set
+ {
+ if (Win32Helper.SetLegacyKoreanIMEEnabled(value))
+ {
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(KoreanIMERegistryValueIsZero));
+ }
+ else
+ {
+ //Since this is rarely seen text, language support is not provided.
+ App.API.ShowMsg("Failed to change Korean IME setting", "Please check your system registry access or contact support.");
+ }
+ }
+ }
+
+ public bool KoreanIMERegistryKeyExists
+ {
+ get
+ {
+ var registryKeyExists = Win32Helper.IsKoreanIMEExist();
+ var koreanLanguageInstalled = InputLanguage.InstalledInputLanguages.Cast().Any(lang => lang.Culture.Name.StartsWith("ko"));
+ var isWindows11 = Win32Helper.IsWindows11();
+
+ // Return true if Windows 11 with Korean IME installed, or if the registry key exists
+ return (isWindows11 && koreanLanguageInstalled) || registryKeyExists;
+ }
+ }
+
+ public bool KoreanIMERegistryValueIsZero
+ {
+ get
+ {
+ var value = Win32Helper.GetLegacyKoreanIMERegistryValue();
+ if (value is int intValue)
+ {
+ return intValue == 0;
+ }
+ else if (value != null && int.TryParse(value.ToString(), out var parsedValue))
+ {
+ return parsedValue == 0;
+ }
+
+ return false;
+ }
+ }
+
+ [RelayCommand]
+ private void OpenImeSettings()
+ {
+ Win32Helper.OpenImeSettings();
+ }
+
+ #endregion
+
public bool ShouldUsePinyin
{
get => Settings.ShouldUsePinyin;
diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
index 657e97f30a8..4782d356ef3 100644
--- a/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
+++ b/Flow.Launcher/SettingPages/Views/SettingsPaneGeneral.xaml
@@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cc="clr-namespace:Flow.Launcher.Resources.Controls"
+ xmlns:converters="clr-namespace:Flow.Launcher.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ext="clr-namespace:Flow.Launcher.Resources.MarkupExtensions"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -14,6 +15,9 @@
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
+
+
+
+
+ ValidationMode="InvalidInputOverwritten"
+ Value="{Binding SearchDelayTimeValue}" />
@@ -312,6 +317,35 @@
SelectedValue="{Binding Language}"
SelectedValuePath="LanguageCode" />
+
+
+
+
+
+
+
+
+
+
+