diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs index 48900261730..e5980b62fa3 100644 --- a/Flow.Launcher.Core/Resource/Theme.cs +++ b/Flow.Launcher.Core/Resource/Theme.cs @@ -24,7 +24,7 @@ public class Theme { #region Properties & Fields - public bool BlurEnabled { get; set; } + public bool BlurEnabled { get; private set; } private const string ThemeMetadataNamePrefix = "Name:"; private const string ThemeMetadataIsDarkPrefix = "IsDark:"; @@ -42,6 +42,8 @@ public class Theme private static string DirectoryPath => Path.Combine(Constant.ProgramDirectory, Folder); private static string UserDirectoryPath => Path.Combine(DataLocation.DataDirectory(), Folder); + private Thickness _themeResizeBorderThickness; + #endregion #region Constructor @@ -463,7 +465,7 @@ public void AddDropShadowEffectToCurrentTheme() var effectSetter = new Setter { - Property = Border.EffectProperty, + Property = UIElement.EffectProperty, Value = new DropShadowEffect { Opacity = 0.3, @@ -473,12 +475,12 @@ public void AddDropShadowEffectToCurrentTheme() } }; - if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) is not Setter marginSetter) + if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is not Setter marginSetter) { var margin = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin); marginSetter = new Setter() { - Property = Border.MarginProperty, + Property = FrameworkElement.MarginProperty, Value = margin, }; windowBorderStyle.Setters.Add(marginSetter); @@ -508,12 +510,12 @@ public void RemoveDropShadowEffectFromCurrentTheme() var dict = GetCurrentResourceDictionary(); var windowBorderStyle = dict["WindowBorderStyle"] as Style; - if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) is Setter effectSetter) + if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == UIElement.EffectProperty) is Setter effectSetter) { windowBorderStyle.Setters.Remove(effectSetter); } - if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.MarginProperty) is Setter marginSetter) + if (windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == FrameworkElement.MarginProperty) is Setter marginSetter) { var currentMargin = (Thickness)marginSetter.Value; var newMargin = new Thickness( @@ -529,28 +531,41 @@ public void RemoveDropShadowEffectFromCurrentTheme() UpdateResourceDictionary(dict); } + public void SetResizeBorderThickness(WindowChrome windowChrome, bool fixedWindowSize) + { + if (fixedWindowSize) + { + windowChrome.ResizeBorderThickness = new Thickness(0); + } + else + { + windowChrome.ResizeBorderThickness = _themeResizeBorderThickness; + } + } + // because adding drop shadow effect will change the margin of the window, // we need to update the window chrome thickness to correct set the resize border - private static void SetResizeBoarderThickness(Thickness? effectMargin) + private void SetResizeBoarderThickness(Thickness? effectMargin) { var window = Application.Current.MainWindow; if (WindowChrome.GetWindowChrome(window) is WindowChrome windowChrome) { - Thickness thickness; + // Save the theme resize border thickness so that we can restore it if we change ResizeWindow setting if (effectMargin == null) { - thickness = SystemParameters.WindowResizeBorderThickness; + _themeResizeBorderThickness = SystemParameters.WindowResizeBorderThickness; } else { - thickness = new Thickness( + _themeResizeBorderThickness = new Thickness( effectMargin.Value.Left + SystemParameters.WindowResizeBorderThickness.Left, effectMargin.Value.Top + SystemParameters.WindowResizeBorderThickness.Top, effectMargin.Value.Right + SystemParameters.WindowResizeBorderThickness.Right, effectMargin.Value.Bottom + SystemParameters.WindowResizeBorderThickness.Bottom); } - windowChrome.ResizeBorderThickness = thickness; + // Apply the resize border thickness to the window chrome + SetResizeBorderThickness(windowChrome, _settings.KeepMaxResults); } } @@ -582,7 +597,7 @@ await Application.Current.Dispatcher.InvokeAsync(() => { AutoDropShadow(useDropShadowEffect); } - }, DispatcherPriority.Normal); + }, DispatcherPriority.Render); } /// @@ -596,7 +611,7 @@ await Application.Current.Dispatcher.InvokeAsync(() => var (backdropType, _) = GetActualValue(); SetBlurForWindow(GetCurrentTheme(), backdropType); - }, DispatcherPriority.Normal); + }, DispatcherPriority.Render); } /// diff --git a/Flow.Launcher.Core/Resource/ThemeManager.cs b/Flow.Launcher.Core/Resource/ThemeManager.cs deleted file mode 100644 index 3cbe8319ad3..00000000000 --- a/Flow.Launcher.Core/Resource/ThemeManager.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using CommunityToolkit.Mvvm.DependencyInjection; - -namespace Flow.Launcher.Core.Resource -{ - [Obsolete("ThemeManager.Instance is obsolete. Use Ioc.Default.GetRequiredService() instead.")] - public class ThemeManager - { - public static Theme Instance - => Ioc.Default.GetRequiredService(); - } -} diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index 63debfb474c..bd146f49a0b 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -68,11 +68,12 @@ public string Theme get => _theme; set { - if (value == _theme) - return; - _theme = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(MaxResultsToShow)); + if (value != _theme) + { + _theme = value; + OnPropertyChanged(); + OnPropertyChanged(nameof(MaxResultsToShow)); + } } } public bool UseDropShadowEffect { get; set; } = true; @@ -113,6 +114,33 @@ public string Theme public double? SettingWindowLeft { get; set; } = null; public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal; + bool _showPlaceholder { get; set; } = false; + public bool ShowPlaceholder + { + get => _showPlaceholder; + set + { + if (_showPlaceholder != value) + { + _showPlaceholder = value; + OnPropertyChanged(); + } + } + } + string _placeholderText { get; set; } = string.Empty; + public string PlaceholderText + { + get => _placeholderText; + set + { + if (_placeholderText != value) + { + _placeholderText = value; + OnPropertyChanged(); + } + } + } + public int CustomExplorerIndex { get; set; } = 0; [JsonIgnore] @@ -241,10 +269,26 @@ public SearchPrecisionScore QuerySearchPrecision /// public double CustomWindowTop { get; set; } = 0; - public bool KeepMaxResults { get; set; } = false; + /// + /// Fixed window size + /// + private bool _keepMaxResults { get; set; } = false; + public bool KeepMaxResults + { + get => _keepMaxResults; + set + { + if (_keepMaxResults != value) + { + _keepMaxResults = value; + OnPropertyChanged(); + } + } + } + public int MaxResultsToShow { get; set; } = 5; - public int ActivateTimes { get; set; } + public int ActivateTimes { get; set; } public ObservableCollection CustomPluginHotkeys { get; set; } = new ObservableCollection(); diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index 833c63ddff8..7b1d113fbdc 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -177,7 +177,6 @@ await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () => HotKeyMapper.Initialize(); // main windows needs initialized before theme change because of blur settings - // TODO: Clean ThemeManager.Instance in future Ioc.Default.GetRequiredService().ChangeTheme(); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index f0454b49633..c170c44e422 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -39,7 +39,7 @@ Game Mode Suspend the use of Hotkeys. Position Reset - Reset search window position + Type here to search Settings @@ -72,8 +72,6 @@ Empty last Query Preserve Last Action Keyword Select Last Action Keyword - Fixed Window Height - The window height is not adjustable by dragging. Maximum results shown You can also quickly adjust this by using CTRL+Plus and CTRL+Minus. Ignore hotkeys in fullscreen mode @@ -104,11 +102,6 @@ Always Preview Always open preview panel when Flow activates. Press {0} to toggle preview. Shadow effect is not allowed while current theme has blur effect enabled - Backdrop Type - None - Acrylic - Mica - Mica Alt Search Plugin @@ -200,8 +193,19 @@ Custom Clock Date + Backdrop Type + None + Acrylic + Mica + Mica Alt This theme supports two(light/dark) modes. This theme supports Blur Transparent Background. + Show placeholder + Display placeholder when query is empty + Placeholder text + Change placeholder text. Input empty will use: {0} + Fixed Window Size + The window size is not adjustable by dragging. Hotkey diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 533819d1730..82ac63b7da6 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -218,6 +218,14 @@ + ().RefreshFrameAsync(); + await _theme.RefreshFrameAsync(); + + // Initialize resize mode after refreshing frame + SetupResizeMode(); // Reset preview _viewModel.ResetPreview(); // Since the default main window visibility is visible, so we need set focus during startup QueryTextBox.Focus(); + // Set the initial state of the QueryTextBoxCursorMovedToEnd property // Without this part, when shown for the first time, switching the context menu does not move the cursor to the end. _viewModel.QueryTextCursorMovedToEnd = false; @@ -237,6 +246,15 @@ private async void OnLoaded(object sender, RoutedEventArgs _) case nameof(Settings.WindowTop): Top = _settings.WindowTop; break; + case nameof(Settings.ShowPlaceholder): + SetupPlaceholderText(); + break; + case nameof(Settings.PlaceholderText): + _viewModel.PlaceholderText = _settings.PlaceholderText; + break; + case nameof(Settings.KeepMaxResults): + SetupResizeMode(); + break; } }; @@ -437,23 +455,25 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b { _initialWidth = (int)Width; _initialHeight = (int)Height; + handled = true; } else if (msg == Win32Helper.WM_EXITSIZEMOVE) { if (_initialHeight != (int)Height) { - var shadowMargin = 0; - var (_, useDropShadowEffect) = _theme.GetActualValue(); - if (useDropShadowEffect) - { - shadowMargin = 32; - } - if (!_settings.KeepMaxResults) { - var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize; + // Get shadow margin + var shadowMargin = 0; + var (_, useDropShadowEffect) = _theme.GetActualValue(); + if (useDropShadowEffect) + { + shadowMargin = 32; + } + // Calculate max results to show + var itemCount = (Height - (_settings.WindowHeightSize + 14) - shadowMargin) / _settings.ItemHeightSize; if (itemCount < 2) { _settings.MaxResultsToShow = 2; @@ -465,11 +485,16 @@ private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref b } SizeToContent = SizeToContent.Height; - _viewModel.MainWindowWidth = Width; } if (_initialWidth != (int)Width) { + if (!_settings.KeepMaxResults) + { + // Update width + _viewModel.MainWindowWidth = Width; + } + SizeToContent = SizeToContent.Height; } @@ -1028,6 +1053,56 @@ private void QueryTextBox_OnPreviewDragOver(object sender, DragEventArgs e) #endregion + #region Placeholder + + private void SetupPlaceholderText() + { + if (_settings.ShowPlaceholder) + { + QueryTextBox.TextChanged += QueryTextBox_TextChanged; + QueryTextSuggestionBox.TextChanged += QueryTextSuggestionBox_TextChanged; + SetPlaceholderText(); + } + else + { + QueryTextBox.TextChanged -= QueryTextBox_TextChanged; + QueryTextSuggestionBox.TextChanged -= QueryTextSuggestionBox_TextChanged; + QueryTextPlaceholderBox.Visibility = Visibility.Collapsed; + } + } + + private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e) + { + SetPlaceholderText(); + } + + private void QueryTextSuggestionBox_TextChanged(object sender, TextChangedEventArgs e) + { + SetPlaceholderText(); + } + + private void SetPlaceholderText() + { + var queryText = QueryTextBox.Text; + var suggestionText = QueryTextSuggestionBox.Text; + QueryTextPlaceholderBox.Visibility = string.IsNullOrEmpty(queryText) && string.IsNullOrEmpty(suggestionText) ? Visibility.Visible : Visibility.Collapsed; + } + + #endregion + + #region Resize Mode + + private void SetupResizeMode() + { + ResizeMode = _settings.KeepMaxResults ? ResizeMode.NoResize : ResizeMode.CanResize; + if (WindowChrome.GetWindowChrome(this) is WindowChrome windowChrome) + { + _theme.SetResizeBorderThickness(windowChrome, _settings.KeepMaxResults); + } + } + + #endregion + #region IDisposable protected virtual void Dispose(bool disposing) diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs index 647a9bcfca0..e35c978ede7 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs @@ -259,6 +259,23 @@ public double SoundEffectVolume set => Settings.SoundVolume = value; } + public bool ShowPlaceholder + { + get => Settings.ShowPlaceholder; + set => Settings.ShowPlaceholder = value; + } + + public string PlaceholderTextTip + { + get => string.Format(App.API.GetTranslation("PlaceholderTextTip"), App.API.GetTranslation("queryTextBoxPlaceholder")); + } + + public string PlaceholderText + { + get => Settings.PlaceholderText; + set => Settings.PlaceholderText = value; + } + public bool UseClock { get => Settings.UseClock; diff --git a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml index 6142371469d..5e1ba24ea96 100644 --- a/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml +++ b/Flow.Launcher/SettingPages/Views/SettingsPaneTheme.xaml @@ -30,6 +30,7 @@ VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.ScrollUnit="Pixel"> + + @@ -68,6 +70,7 @@ + - + + - - - - + - + + + + + + + + + + + + diff --git a/Flow.Launcher/Themes/Discord Dark.xaml b/Flow.Launcher/Themes/Discord Dark.xaml index 9e39ee5bdaf..fb88da31353 100644 --- a/Flow.Launcher/Themes/Discord Dark.xaml +++ b/Flow.Launcher/Themes/Discord Dark.xaml @@ -28,7 +28,6 @@ BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/League.xaml b/Flow.Launcher/Themes/League.xaml index f1c8ba19222..ffecf3fcbb4 100644 --- a/Flow.Launcher/Themes/League.xaml +++ b/Flow.Launcher/Themes/League.xaml @@ -24,7 +24,6 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/Themes/Pink.xaml b/Flow.Launcher/Themes/Pink.xaml index d7de4e2467d..5bbfa26d6e8 100644 --- a/Flow.Launcher/Themes/Pink.xaml +++ b/Flow.Launcher/Themes/Pink.xaml @@ -22,7 +22,6 @@ x:Key="QuerySuggestionBoxStyle" BasedOn="{StaticResource BaseQuerySuggestionBoxStyle}" TargetType="{x:Type TextBox}"> - diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index c69fc4484a2..56de70b4785 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -584,7 +584,7 @@ public string QueryText [RelayCommand] private void IncreaseWidth() { - Settings.WindowSize += 100; + MainWindowWidth += 100; Settings.WindowLeft -= 50; OnPropertyChanged(nameof(MainWindowWidth)); } @@ -592,14 +592,14 @@ private void IncreaseWidth() [RelayCommand] private void DecreaseWidth() { - if (MainWindowWidth - 100 < 400 || Settings.WindowSize == 400) + if (MainWindowWidth - 100 < 400 || MainWindowWidth == 400) { - Settings.WindowSize = 400; + MainWindowWidth = 400; } else { + MainWindowWidth -= 100; Settings.WindowLeft += 50; - Settings.WindowSize -= 100; } OnPropertyChanged(nameof(MainWindowWidth)); @@ -765,6 +765,17 @@ private ResultsViewModel SelectedResults public double ClockPanelOpacity { get; set; } = 1; public double SearchIconOpacity { get; set; } = 1; + private string _placeholderText; + public string PlaceholderText + { + get => string.IsNullOrEmpty(_placeholderText) ? App.API.GetTranslation("queryTextBoxPlaceholder") : _placeholderText; + set + { + _placeholderText = value; + OnPropertyChanged(); + } + } + public double MainWindowWidth { get => Settings.WindowSize;