diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
index 7ead7459f..33072b53d 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
@@ -52,6 +52,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public double SettingWindowHeight { get; set; } = 700;
public double SettingWindowTop { get; set; }
public double SettingWindowLeft { get; set; }
+ public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal;
public int CustomExplorerIndex { get; set; } = 0;
diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index 813a44527..1e7f70b0a 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -98,6 +98,7 @@
+
@@ -120,7 +121,7 @@
-
+
diff --git a/Flow.Launcher/Resources/Dark.xaml b/Flow.Launcher/Resources/Dark.xaml
index 445e07c63..674e04deb 100644
--- a/Flow.Launcher/Resources/Dark.xaml
+++ b/Flow.Launcher/Resources/Dark.xaml
@@ -69,6 +69,8 @@
+ #272727
+
#202020
#2b2b2b
#1d1d1d
diff --git a/Flow.Launcher/Resources/Light.xaml b/Flow.Launcher/Resources/Light.xaml
index cee8b63cd..8b04196dd 100644
--- a/Flow.Launcher/Resources/Light.xaml
+++ b/Flow.Launcher/Resources/Light.xaml
@@ -62,6 +62,8 @@
+ #f6f6f6
+
#f3f3f3
#ffffff
#e5e5e5
diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml
index 3c0905aa7..950694db5 100644
--- a/Flow.Launcher/SettingWindow.xaml
+++ b/Flow.Launcher/SettingWindow.xaml
@@ -11,6 +11,7 @@
xmlns:ui="http://schemas.modernwpf.com/2019"
xmlns:userSettings="clr-namespace:Flow.Launcher.Infrastructure.UserSettings;assembly=Flow.Launcher.Infrastructure"
xmlns:vm="clr-namespace:Flow.Launcher.ViewModel"
+ xmlns:wpftk="clr-namespace:WpfToolkit.Controls;assembly=VirtualizingWrapPanel"
Title="{DynamicResource flowlauncher_settings}"
Width="{Binding SettingWindowWidth, Mode=TwoWay}"
Height="{Binding SettingWindowHeight, Mode=TwoWay}"
@@ -47,7 +48,25 @@
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
+
+
+
@@ -1763,7 +1683,9 @@
+ ScrollViewer.CanContentScroll="True"
+ VirtualizingStackPanel.IsVirtualizing="True"
+ VirtualizingStackPanel.ScrollUnit="Pixel">
@@ -2339,14 +2261,16 @@
+ ScrollViewer.CanContentScroll="True"
+ VirtualizingStackPanel.IsVirtualizing="True"
+ VirtualizingStackPanel.ScrollUnit="Pixel">
-
+
@@ -2510,7 +2434,7 @@
Click="OnAddCustomHotkeyClick"
Content="{DynamicResource add}" />
-
+
+ ScrollViewer.CanContentScroll="True"
+ VirtualizingStackPanel.IsVirtualizing="True"
+ VirtualizingStackPanel.ScrollUnit="Pixel">
@@ -2805,7 +2731,9 @@
+ ScrollViewer.CanContentScroll="True"
+ VirtualizingStackPanel.IsVirtualizing="True"
+ VirtualizingStackPanel.ScrollUnit="Pixel">
(DependencyObject child) where T : DependencyObject
{
- _ = viewModel.RefreshExternalPluginsAsync();
- }
+ //get parent item
+ DependencyObject parentObject = VisualTreeHelper.GetParent(child);
+ //we've reached the end of the tree
+ if (parentObject == null) return null;
+
+ //check if the parent matches the type we're looking for
+ T parent = parentObject as T;
+ if (parent != null)
+ return parent;
+ else
+ return FindParent(parentObject);
+ }
+
private void OnExternalPluginInstallClick(object sender, RoutedEventArgs e)
{
- if (sender is Button { DataContext: PluginStoreItemViewModel plugin })
+ if (sender is not Button { DataContext: PluginStoreItemViewModel plugin } button)
{
- viewModel.DisplayPluginQuery($"install {plugin.Name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
+ return;
}
+
+ if (storeClickedButton != null)
+ {
+ FlyoutService.GetFlyout(storeClickedButton).Hide();
+ }
+
+ viewModel.DisplayPluginQuery($"install {plugin.Name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
}
private void OnExternalPluginUninstallClick(object sender, MouseButtonEventArgs e)
@@ -317,18 +337,30 @@ namespace Flow.Launcher
viewModel.DisplayPluginQuery($"uninstall {name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
}
+
}
private void OnExternalPluginUninstallClick(object sender, RoutedEventArgs e)
{
+ if (storeClickedButton != null)
+ {
+ FlyoutService.GetFlyout(storeClickedButton).Hide();
+ }
+
if (sender is Button { DataContext: PluginStoreItemViewModel plugin })
viewModel.DisplayPluginQuery($"uninstall {plugin.Name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
+
}
private void OnExternalPluginUpdateClick(object sender, RoutedEventArgs e)
{
+ if (storeClickedButton != null)
+ {
+ FlyoutService.GetFlyout(storeClickedButton).Hide();
+ }
if (sender is Button { DataContext: PluginStoreItemViewModel plugin })
viewModel.DisplayPluginQuery($"update {plugin.Name}", PluginManager.GetPluginForId("9f8f9b14-2518-4907-b211-35ab6290dee7"));
+
}
private void window_MouseDown(object sender, MouseButtonEventArgs e) /* for close hotkey popup */
@@ -522,6 +554,7 @@ namespace Flow.Launcher
Top = WindowTop();
Left = WindowLeft();
}
+ WindowState = settings.SettingWindowState;
}
public double WindowLeft()
{
@@ -541,5 +574,21 @@ namespace Flow.Launcher
return top;
}
+ private Button storeClickedButton;
+
+ private void StoreListItem_Click(object sender, RoutedEventArgs e)
+ {
+ if (sender is not Button button)
+ return;
+
+ storeClickedButton = button;
+
+ var flyout = FlyoutService.GetFlyout(button);
+ flyout.Closed += (_, _) =>
+ {
+ storeClickedButton = null;
+ };
+
+ }
}
}
diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
index 63b9c29c9..fa6520523 100644
--- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
+++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
@@ -20,10 +20,11 @@ using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
using Flow.Launcher.Plugin.SharedModels;
using System.Collections.ObjectModel;
+using CommunityToolkit.Mvvm.Input;
namespace Flow.Launcher.ViewModel
{
- public class SettingWindowViewModel : BaseModel
+ public partial class SettingWindowViewModel : BaseModel
{
private readonly Updater _updater;
private readonly IPortable _portable;
@@ -330,7 +331,8 @@ namespace Flow.Launcher.ViewModel
}
}
- public async Task RefreshExternalPluginsAsync()
+ [RelayCommand]
+ private async Task RefreshExternalPluginsAsync()
{
await PluginsManifest.UpdateManifestAsync();
OnPropertyChanged(nameof(ExternalPlugins));
@@ -535,6 +537,8 @@ namespace Flow.Launcher.ViewModel
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = memStream;
+ bitmap.DecodePixelWidth = 800;
+ bitmap.DecodePixelHeight = 600;
bitmap.EndInit();
var brush = new ImageBrush(bitmap)
{
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs
index ad7387f10..316aaaac3 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/UWP.cs
@@ -601,92 +601,97 @@ namespace Flow.Launcher.Plugin.Program.Programs
// windows 8.1 https://msdn.microsoft.com/en-us/library/windows/apps/hh965372.aspx#target_size
// windows 8 https://msdn.microsoft.com/en-us/library/windows/apps/br211475.aspx
- string path;
- if (uri.Contains("\\"))
- {
- path = Path.Combine(Package.Location, uri);
- }
- else
+ string path = Path.Combine(Package.Location, uri);
+
+ var logoPath = TryToFindLogo(uri, path);
+ if (String.IsNullOrEmpty(logoPath))
{
+ // TODO: Don't know why, just keep it at the moment
+ // Maybe on older version of Windows 10?
// for C:\Windows\MiracastView etc
- path = Path.Combine(Package.Location, "Assets", uri);
+ return TryToFindLogo(uri, Path.Combine(Package.Location, "Assets", uri));
}
+ return logoPath;
- var extension = Path.GetExtension(path);
- if (extension != null)
+ string TryToFindLogo(string uri, string path)
{
- var end = path.Length - extension.Length;
- var prefix = path.Substring(0, end);
- var paths = new List
+ var extension = Path.GetExtension(path);
+ if (extension != null)
{
- path
- };
+ //if (File.Exists(path))
+ //{
+ // return path; // shortcut, avoid enumerating files
+ //}
- var scaleFactors = new Dictionary>
- {
- // scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
+ var logoNamePrefix = Path.GetFileNameWithoutExtension(uri); // e.g Square44x44
+ var logoDir = Path.GetDirectoryName(path); // e.g ..\..\Assets
+ if (String.IsNullOrEmpty(logoNamePrefix) || String.IsNullOrEmpty(logoDir) || !Directory.Exists(logoDir))
{
- PackageVersion.Windows10, new List
- {
- 100,
- 125,
- 150,
- 200,
- 400
- }
- },
+ // Known issue: Edge always triggers it since logo is not at uri
+ ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
+ $"|{UserModelId} can't find logo uri for {uri} in package location (logo name or directory not found): {Package.Location}", new FileNotFoundException());
+ return string.Empty;
+ }
+
+ var files = Directory.EnumerateFiles(logoDir);
+
+ // Currently we don't care which one to choose
+ // Just ignore all qualifiers
+ // select like logo.[xxx_yyy].png
+ // https://learn.microsoft.com/en-us/windows/uwp/app-resources/tailor-resources-lang-scale-contrast
+ var logos = files.Where(file =>
+ Path.GetFileName(file)?.StartsWith(logoNamePrefix, StringComparison.OrdinalIgnoreCase) ?? false
+ && extension.Equals(Path.GetExtension(file), StringComparison.OrdinalIgnoreCase)
+ );
+
+ var selected = logos.FirstOrDefault();
+ var closest = selected;
+ int min = int.MaxValue;
+ foreach(var logo in logos)
{
- PackageVersion.Windows81, new List
+
+ var imageStream = File.OpenRead(logo);
+ var decoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.None);
+ var height = decoder.Frames[0].PixelHeight;
+ var width = decoder.Frames[0].PixelWidth;
+ int pixelCountDiff = Math.Abs(height * width - 1936); // 44*44=1936
+ if(pixelCountDiff < min)
{
- 100,
- 120,
- 140,
- 160,
- 180
- }
- },
- {
- PackageVersion.Windows8, new List
- {
- 100
+ // try to find the closest to 44x44 logo
+ closest = logo;
+ if (pixelCountDiff == 0)
+ break; // found 44x44
+ min = pixelCountDiff;
}
}
- };
- if (scaleFactors.ContainsKey(Package.Version))
- {
- foreach (var factor in scaleFactors[Package.Version])
+ selected = closest;
+ if (!string.IsNullOrEmpty(selected))
{
- paths.Add($"{prefix}.scale-{factor}{extension}");
+ return selected;
+ }
+ else
+ {
+ ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
+ $"|{UserModelId} can't find logo uri for {uri} in package location (can't find specified logo): {Package.Location}", new FileNotFoundException());
+ return string.Empty;
}
- }
-
- var selected = paths.FirstOrDefault(File.Exists);
- if (!string.IsNullOrEmpty(selected))
- {
- return selected;
}
else
{
ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
- $"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
+ $"|Unable to find extension from {uri} for {UserModelId} " +
+ $"in package location {Package.Location}", new FileNotFoundException());
return string.Empty;
}
}
- else
- {
- ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
- $"|Unable to find extension from {uri} for {UserModelId} " +
- $"in package location {Package.Location}", new FileNotFoundException());
- return string.Empty;
- }
}
public ImageSource Logo()
{
var logo = ImageFromPath(LogoPath);
- var plated = PlatedImage(logo);
+ var plated = PlatedImage(logo); // TODO: maybe get plated directly from app package?
// todo magic! temp fix for cross thread object
plated.Freeze();
diff --git a/appveyor.yml b/appveyor.yml
index a71779fdc..d467792d7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -7,6 +7,10 @@ init:
- sc config WSearch start= auto # Starts Windows Search service- Needed for running ExplorerTest
- net start WSearch
+cache:
+ - '%USERPROFILE%\.nuget\packages -> **.sln, **.csproj' # preserve nuget folder (packages) unless the solution or projects change
+
+
assembly_info:
patch: true
file: SolutionAssemblyInfo.cs
@@ -28,7 +32,9 @@ before_build:
build:
project: Flow.Launcher.sln
verbosity: minimal
-after_build:
+test_script:
+ - dotnet test --no-build -c Release
+after_test:
- ps: .\Scripts\post_build.ps1
artifacts: