mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #3420 from Jack251970/theme_change_api
New API Function from Theme & Improve Theme Model
This commit is contained in:
commit
8cc9f2de69
6 changed files with 151 additions and 67 deletions
|
|
@ -16,6 +16,7 @@ using Flow.Launcher.Infrastructure;
|
|||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
|
|
@ -81,11 +82,6 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
#region Theme Resources
|
||||
|
||||
public string GetCurrentTheme()
|
||||
{
|
||||
return _settings.Theme;
|
||||
}
|
||||
|
||||
private void MakeSureThemeDirectoriesExist()
|
||||
{
|
||||
foreach (var dir in _themeDirectories.Where(dir => !Directory.Exists(dir)))
|
||||
|
|
@ -127,7 +123,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
try
|
||||
{
|
||||
// Load a ResourceDictionary for the specified theme.
|
||||
var themeName = GetCurrentTheme();
|
||||
var themeName = _settings.Theme;
|
||||
var dict = GetThemeResourceDictionary(themeName);
|
||||
|
||||
// Apply font settings to the theme resource.
|
||||
|
|
@ -330,7 +326,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
private ResourceDictionary GetCurrentResourceDictionary()
|
||||
{
|
||||
return GetResourceDictionary(GetCurrentTheme());
|
||||
return GetResourceDictionary(_settings.Theme);
|
||||
}
|
||||
|
||||
private ThemeData GetThemeDataFromPath(string path)
|
||||
|
|
@ -383,9 +379,20 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
#endregion
|
||||
|
||||
#region Load & Change
|
||||
#region Get & Change Theme
|
||||
|
||||
public List<ThemeData> LoadAvailableThemes()
|
||||
public ThemeData GetCurrentTheme()
|
||||
{
|
||||
var themes = GetAvailableThemes();
|
||||
var matchingTheme = themes.FirstOrDefault(t => t.FileNameWithoutExtension == _settings.Theme);
|
||||
if (matchingTheme == null)
|
||||
{
|
||||
Log.Warn($"No matching theme found for '{_settings.Theme}'. Falling back to the first available theme.");
|
||||
}
|
||||
return matchingTheme ?? themes.FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<ThemeData> GetAvailableThemes()
|
||||
{
|
||||
List<ThemeData> themes = new List<ThemeData>();
|
||||
foreach (var themeDirectory in _themeDirectories)
|
||||
|
|
@ -403,7 +410,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
public bool ChangeTheme(string theme = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(theme))
|
||||
theme = GetCurrentTheme();
|
||||
theme = _settings.Theme;
|
||||
|
||||
string path = GetThemePath(theme);
|
||||
try
|
||||
|
|
@ -426,7 +433,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
BlurEnabled = IsBlurTheme();
|
||||
|
||||
// Can only apply blur but here also apply drop shadow effect to avoid possible drop shadow effect issues
|
||||
// Apply blur and drop shadow effect so that we do not need to call it again
|
||||
_ = RefreshFrameAsync();
|
||||
|
||||
return true;
|
||||
|
|
@ -591,7 +598,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
AutoDropShadow(useDropShadowEffect);
|
||||
}
|
||||
SetBlurForWindow(GetCurrentTheme(), backdropType);
|
||||
SetBlurForWindow(_settings.Theme, backdropType);
|
||||
|
||||
if (!BlurEnabled)
|
||||
{
|
||||
|
|
@ -610,7 +617,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
// Get the actual backdrop type and drop shadow effect settings
|
||||
var (backdropType, _) = GetActualValue();
|
||||
|
||||
SetBlurForWindow(GetCurrentTheme(), backdropType);
|
||||
SetBlurForWindow(_settings.Theme, backdropType);
|
||||
}, DispatcherPriority.Render);
|
||||
}
|
||||
|
||||
|
|
@ -898,11 +905,5 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using JetBrains.Annotations;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
|
|
@ -9,6 +7,8 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
|
|
@ -352,6 +352,26 @@ namespace Flow.Launcher.Plugin
|
|||
public void StopLoadingBar();
|
||||
|
||||
/// <summary>
|
||||
/// Get all available themes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<ThemeData> GetAvailableThemes();
|
||||
|
||||
/// <summary>
|
||||
/// Get the current theme
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ThemeData GetCurrentTheme();
|
||||
|
||||
/// <summary>
|
||||
/// Set the current theme
|
||||
/// </summary>
|
||||
/// <param name="theme"></param>
|
||||
/// <returns>
|
||||
/// True if the theme is set successfully, false otherwise.
|
||||
/// </returns>
|
||||
public bool SetCurrentTheme(ThemeData theme);
|
||||
|
||||
/// Save all Flow's plugins caches
|
||||
/// </summary>
|
||||
void SavePluginCaches();
|
||||
|
|
|
|||
77
Flow.Launcher.Plugin/SharedModels/ThemeData.cs
Normal file
77
Flow.Launcher.Plugin/SharedModels/ThemeData.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
|
||||
namespace Flow.Launcher.Plugin.SharedModels;
|
||||
|
||||
/// <summary>
|
||||
/// Theme data model
|
||||
/// </summary>
|
||||
public class ThemeData
|
||||
{
|
||||
/// <summary>
|
||||
/// Theme file name without extension
|
||||
/// </summary>
|
||||
public string FileNameWithoutExtension { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Theme name
|
||||
/// </summary>
|
||||
public string Name { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the theme supports dark mode
|
||||
/// </summary>
|
||||
public bool? IsDark { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the theme supports blur effects
|
||||
/// </summary>
|
||||
public bool? HasBlur { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Theme data constructor
|
||||
/// </summary>
|
||||
public ThemeData(string fileNameWithoutExtension, string name, bool? isDark = null, bool? hasBlur = null)
|
||||
{
|
||||
FileNameWithoutExtension = fileNameWithoutExtension;
|
||||
Name = name;
|
||||
IsDark = isDark;
|
||||
HasBlur = hasBlur;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator ==(ThemeData left, ThemeData right)
|
||||
{
|
||||
if (left is null && right is null)
|
||||
return true;
|
||||
if (left is null || right is null)
|
||||
return false;
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator !=(ThemeData left, ThemeData right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is not ThemeData other)
|
||||
return false;
|
||||
return FileNameWithoutExtension == other.FileNameWithoutExtension &&
|
||||
Name == other.Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(FileNameWithoutExtension, Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,9 @@ namespace Flow.Launcher
|
|||
private readonly Internationalization _translater;
|
||||
private readonly MainViewModel _mainVM;
|
||||
|
||||
private Theme _theme;
|
||||
private Theme Theme => _theme ??= Ioc.Default.GetRequiredService<Theme>();
|
||||
|
||||
private readonly object _saveSettingsLock = new();
|
||||
|
||||
#region Constructor
|
||||
|
|
@ -360,6 +363,13 @@ namespace Flow.Launcher
|
|||
public Task ShowProgressBoxAsync(string caption, Func<Action<double>, Task> reportProgressAsync,
|
||||
Action cancelProgress = null) => ProgressBoxEx.ShowAsync(caption, reportProgressAsync, cancelProgress);
|
||||
|
||||
public List<ThemeData> GetAvailableThemes() => Theme.GetAvailableThemes();
|
||||
|
||||
public ThemeData GetCurrentTheme() => Theme.GetCurrentTheme();
|
||||
|
||||
public bool SetCurrentTheme(ThemeData theme) =>
|
||||
Theme.ChangeTheme(theme.FileNameWithoutExtension);
|
||||
|
||||
private readonly ConcurrentDictionary<(string, string, Type), object> _pluginBinaryStorages = new();
|
||||
|
||||
public void RemovePluginCaches(string cacheDirectory)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Flow.Launcher.Helper;
|
|||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using ModernWpf;
|
||||
using ThemeManagerForColorSchemeSwitch = ModernWpf.ThemeManager;
|
||||
|
|
@ -28,25 +29,23 @@ public partial class SettingsPaneThemeViewModel : BaseModel
|
|||
public static string LinkHowToCreateTheme => @"https://www.flowlauncher.com/theme-builder/";
|
||||
public static string LinkThemeGallery => "https://github.com/Flow-Launcher/Flow.Launcher/discussions/1438";
|
||||
|
||||
private List<Theme.ThemeData> _themes;
|
||||
public List<Theme.ThemeData> Themes => _themes ??= _theme.LoadAvailableThemes();
|
||||
private List<ThemeData> _themes;
|
||||
public List<ThemeData> Themes => _themes ??= App.API.GetAvailableThemes();
|
||||
|
||||
private Theme.ThemeData _selectedTheme;
|
||||
public Theme.ThemeData SelectedTheme
|
||||
private ThemeData _selectedTheme;
|
||||
public ThemeData SelectedTheme
|
||||
{
|
||||
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == _theme.GetCurrentTheme());
|
||||
get => _selectedTheme ??= Themes.Find(v => v == App.API.GetCurrentTheme());
|
||||
set
|
||||
{
|
||||
_selectedTheme = value;
|
||||
_theme.ChangeTheme(value.FileNameWithoutExtension);
|
||||
App.API.SetCurrentTheme(value);
|
||||
|
||||
// Update UI state
|
||||
OnPropertyChanged(nameof(BackdropType));
|
||||
OnPropertyChanged(nameof(IsBackdropEnabled));
|
||||
OnPropertyChanged(nameof(IsDropShadowEnabled));
|
||||
OnPropertyChanged(nameof(DropShadowEffect));
|
||||
|
||||
_ = _theme.RefreshFrameAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
|
||||
namespace Flow.Launcher.Plugin.Sys
|
||||
{
|
||||
|
|
@ -11,32 +10,6 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
|
||||
private readonly PluginInitContext _context;
|
||||
|
||||
// Do not initialize it in the constructor, because it will cause null reference in
|
||||
// var dicts = Application.Current.Resources.MergedDictionaries; line of Theme
|
||||
private Theme theme = null;
|
||||
private Theme Theme => theme ??= Ioc.Default.GetRequiredService<Theme>();
|
||||
|
||||
#region Theme Selection
|
||||
|
||||
// Theme select codes simplified from SettingsPaneThemeViewModel.cs
|
||||
|
||||
private Theme.ThemeData _selectedTheme;
|
||||
public Theme.ThemeData SelectedTheme
|
||||
{
|
||||
get => _selectedTheme ??= Themes.Find(v => v.FileNameWithoutExtension == Theme.GetCurrentTheme());
|
||||
set
|
||||
{
|
||||
_selectedTheme = value;
|
||||
Theme.ChangeTheme(value.FileNameWithoutExtension);
|
||||
|
||||
_ = Theme.RefreshFrameAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Theme.ThemeData> Themes => Theme.LoadAvailableThemes();
|
||||
|
||||
#endregion
|
||||
|
||||
public ThemeSelector(PluginInitContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
|
@ -44,28 +17,30 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
|
||||
public List<Result> Query(Query query)
|
||||
{
|
||||
var themes = _context.API.GetAvailableThemes();
|
||||
var selectedTheme = _context.API.GetCurrentTheme();
|
||||
|
||||
var search = query.SecondToEndSearch;
|
||||
if (string.IsNullOrWhiteSpace(search))
|
||||
{
|
||||
return Themes.Select(CreateThemeResult)
|
||||
return themes.Select(x => CreateThemeResult(x, selectedTheme))
|
||||
.OrderBy(x => x.Title)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return Themes.Select(theme => (theme, matchResult: _context.API.FuzzySearch(search, theme.Name)))
|
||||
return themes.Select(theme => (theme, matchResult: _context.API.FuzzySearch(search, theme.Name)))
|
||||
.Where(x => x.matchResult.IsSearchPrecisionScoreMet())
|
||||
.Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData))
|
||||
.Select(x => CreateThemeResult(x.theme, selectedTheme, x.matchResult.Score, x.matchResult.MatchData))
|
||||
.OrderBy(x => x.Title)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private Result CreateThemeResult(Theme.ThemeData theme) => CreateThemeResult(theme, 0, null);
|
||||
private Result CreateThemeResult(ThemeData theme, ThemeData selectedTheme) => CreateThemeResult(theme, selectedTheme, 0, null);
|
||||
|
||||
private Result CreateThemeResult(Theme.ThemeData theme, int score, IList<int> highlightData)
|
||||
private Result CreateThemeResult(ThemeData theme, ThemeData selectedTheme, int score, IList<int> highlightData)
|
||||
{
|
||||
string themeName = theme.Name;
|
||||
string title;
|
||||
if (theme == SelectedTheme)
|
||||
if (theme == selectedTheme)
|
||||
{
|
||||
title = $"{theme.Name} ★";
|
||||
// Set current theme to the top
|
||||
|
|
@ -101,8 +76,10 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
Score = score,
|
||||
Action = c =>
|
||||
{
|
||||
SelectedTheme = theme;
|
||||
_context.API.ReQuery();
|
||||
if (_context.API.SetCurrentTheme(theme))
|
||||
{
|
||||
_context.API.ReQuery();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue