mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-08 12:18:50 +02:00
Display colored icons for packaged apps (#5923)
* Added wox code for tiling
* nit fixes in plating function
* Added feature to plate images based on theme
* Improve code readability
* added altform based paths
* Error handling in fetching logo
* Re add fix sequence empty exception on calling max in program plugin
* Move path and logo setting code to setIcon function
* Add corner radius to packaged apps icon
* Revert "Add corner radius to packaged apps icon"
This reverts commit 9be496e662.
This commit is contained in:
committed by
GitHub
parent
83e8799610
commit
9c6b2c3e81
@@ -12,6 +12,7 @@ using System.Reflection;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
@@ -53,10 +54,16 @@ namespace Microsoft.Plugin.Program.Programs
|
|||||||
|
|
||||||
public string LogoPath { get; set; }
|
public string LogoPath { get; set; }
|
||||||
|
|
||||||
|
public LogoType LogoType { get; set; }
|
||||||
|
|
||||||
public UWP Package { get; set; }
|
public UWP Package { get; set; }
|
||||||
|
|
||||||
private string logoUri;
|
private string logoUri;
|
||||||
|
|
||||||
|
private const string ContrastWhite = "contrast-white";
|
||||||
|
|
||||||
|
private const string ContrastBlack = "contrast-black";
|
||||||
|
|
||||||
// Function to calculate the score of a result
|
// Function to calculate the score of a result
|
||||||
private int Score(string query)
|
private int Score(string query)
|
||||||
{
|
{
|
||||||
@@ -352,23 +359,186 @@ namespace Microsoft.Plugin.Program.Programs
|
|||||||
|
|
||||||
public void UpdatePath(Theme theme)
|
public void UpdatePath(Theme theme)
|
||||||
{
|
{
|
||||||
if (theme == Theme.Light || theme == Theme.HighContrastWhite)
|
LogoPathFromUri(this.logoUri, theme);
|
||||||
{
|
|
||||||
LogoPath = LogoPathFromUri(logoUri, "contrast-white");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogoPath = LogoPathFromUri(logoUri, "contrast-black");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string LogoPathFromUri(string uri, string theme)
|
private bool SetScaleIcons(string path, string colorscheme, bool highContrast = false)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(path);
|
||||||
|
if (extension != null)
|
||||||
|
{
|
||||||
|
var end = path.Length - extension.Length;
|
||||||
|
var prefix = path.Substring(0, end);
|
||||||
|
var paths = new List<string> { };
|
||||||
|
var scaleFactors = new Dictionary<PackageVersion, List<int>>
|
||||||
|
{
|
||||||
|
// scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
|
||||||
|
{ PackageVersion.Windows10, new List<int> { 100, 125, 150, 200, 400 } },
|
||||||
|
{ PackageVersion.Windows81, new List<int> { 100, 120, 140, 160, 180 } },
|
||||||
|
{ PackageVersion.Windows8, new List<int> { 100 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!highContrast)
|
||||||
|
{
|
||||||
|
paths.Add(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scaleFactors.ContainsKey(Package.Version))
|
||||||
|
{
|
||||||
|
foreach (var factor in scaleFactors[Package.Version])
|
||||||
|
{
|
||||||
|
if (highContrast)
|
||||||
|
{
|
||||||
|
paths.Add($"{prefix}.scale-{factor}_{colorscheme}{extension}");
|
||||||
|
paths.Add($"{prefix}.{colorscheme}_scale-{factor}{extension}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
paths.Add($"{prefix}.scale-{factor}{extension}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedIconPath = paths.FirstOrDefault(File.Exists);
|
||||||
|
if (!string.IsNullOrEmpty(selectedIconPath))
|
||||||
|
{
|
||||||
|
LogoPath = selectedIconPath;
|
||||||
|
if (highContrast)
|
||||||
|
{
|
||||||
|
LogoType = LogoType.HighContrast;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogoType = LogoType.Colored;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetTargetSizeIcon(string path, string colorscheme, bool highContrast = false)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(path);
|
||||||
|
if (extension != null)
|
||||||
|
{
|
||||||
|
var end = path.Length - extension.Length;
|
||||||
|
var prefix = path.Substring(0, end);
|
||||||
|
var paths = new List<string> { };
|
||||||
|
int appIconSize = 36;
|
||||||
|
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel();
|
||||||
|
var pathFactorPairs = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
foreach (var factor in targetSizes)
|
||||||
|
{
|
||||||
|
if (highContrast)
|
||||||
|
{
|
||||||
|
string suffixThemePath = $"{prefix}.targetsize-{factor}_{colorscheme}{extension}";
|
||||||
|
string prefixThemePath = $"{prefix}.{colorscheme}_targetsize-{factor}{extension}";
|
||||||
|
paths.Add(suffixThemePath);
|
||||||
|
paths.Add(prefixThemePath);
|
||||||
|
pathFactorPairs.Add(suffixThemePath, factor);
|
||||||
|
pathFactorPairs.Add(prefixThemePath, factor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string simplePath = $"{prefix}.targetsize-{factor}{extension}";
|
||||||
|
string altformUnPlatedPath = $"{prefix}.targetsize-{factor}_altform-unplated{extension}";
|
||||||
|
paths.Add(simplePath);
|
||||||
|
paths.Add(altformUnPlatedPath);
|
||||||
|
pathFactorPairs.Add(simplePath, factor);
|
||||||
|
pathFactorPairs.Add(altformUnPlatedPath, factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedIconPath = paths.OrderBy(x => Math.Abs(pathFactorPairs.GetValueOrDefault(x) - appIconSize)).FirstOrDefault(File.Exists);
|
||||||
|
if (!string.IsNullOrEmpty(selectedIconPath))
|
||||||
|
{
|
||||||
|
LogoPath = selectedIconPath;
|
||||||
|
if (highContrast)
|
||||||
|
{
|
||||||
|
LogoType = LogoType.HighContrast;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogoType = LogoType.Colored;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetColoredIcon(string path, string colorscheme)
|
||||||
|
{
|
||||||
|
var isSetColoredScaleIcon = SetScaleIcons(path, colorscheme);
|
||||||
|
if (isSetColoredScaleIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSetColoredTargetIcon = SetTargetSizeIcon(path, colorscheme);
|
||||||
|
if (isSetColoredTargetIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSetHighContrastScaleIcon = SetScaleIcons(path, colorscheme, true);
|
||||||
|
if (isSetHighContrastScaleIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSetHighContrastTargetIcon = SetTargetSizeIcon(path, colorscheme, true);
|
||||||
|
if (isSetHighContrastTargetIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetHighContrastIcon(string path, string colorscheme)
|
||||||
|
{
|
||||||
|
var isSetHighContrastScaleIcon = SetScaleIcons(path, colorscheme, true);
|
||||||
|
if (isSetHighContrastScaleIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSetHighContrastTargetIcon = SetTargetSizeIcon(path, colorscheme, true);
|
||||||
|
if (isSetHighContrastTargetIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSetColoredScaleIcon = SetScaleIcons(path, colorscheme);
|
||||||
|
if (isSetColoredScaleIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSetColoredTargetIcon = SetTargetSizeIcon(path, colorscheme);
|
||||||
|
if (isSetColoredTargetIcon)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LogoPathFromUri(string uri, Theme theme)
|
||||||
{
|
{
|
||||||
// all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets
|
// all https://msdn.microsoft.com/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets
|
||||||
// windows 10 https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx
|
// windows 10 https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx
|
||||||
// windows 8.1 https://msdn.microsoft.com/en-us/library/windows/apps/hh965372.aspx#target_size
|
// 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
|
// windows 8 https://msdn.microsoft.com/en-us/library/windows/apps/br211475.aspx
|
||||||
string path;
|
string path;
|
||||||
|
bool isLogoUriSet;
|
||||||
if (uri.Contains("\\", StringComparison.Ordinal))
|
if (uri.Contains("\\", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
path = Path.Combine(Package.Location, uri);
|
path = Path.Combine(Package.Location, uri);
|
||||||
@@ -379,87 +549,112 @@ namespace Microsoft.Plugin.Program.Programs
|
|||||||
path = Path.Combine(Package.Location, "Assets", uri);
|
path = Path.Combine(Package.Location, "Assets", uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
var extension = Path.GetExtension(path);
|
if (theme == Theme.HighContrastBlack || theme == Theme.HighContrastOne || theme == Theme.HighContrastTwo)
|
||||||
if (extension != null)
|
|
||||||
{
|
{
|
||||||
var end = path.Length - extension.Length;
|
isLogoUriSet = SetHighContrastIcon(path, ContrastBlack);
|
||||||
var prefix = path.Substring(0, end);
|
}
|
||||||
var paths = new List<string> { path };
|
else if (theme == Theme.HighContrastWhite)
|
||||||
|
{
|
||||||
var scaleFactors = new Dictionary<PackageVersion, List<int>>
|
isLogoUriSet = SetHighContrastIcon(path, ContrastWhite);
|
||||||
{
|
}
|
||||||
// scale factors on win10: https://docs.microsoft.com/en-us/windows/uwp/controls-and-patterns/tiles-and-notifications-app-assets#asset-size-tables,
|
else if (theme == Theme.Light)
|
||||||
{ PackageVersion.Windows10, new List<int> { 100, 125, 150, 200, 400 } },
|
{
|
||||||
{ PackageVersion.Windows81, new List<int> { 100, 120, 140, 160, 180 } },
|
isLogoUriSet = SetColoredIcon(path, ContrastWhite);
|
||||||
{ PackageVersion.Windows8, new List<int> { 100 } },
|
|
||||||
};
|
|
||||||
|
|
||||||
if (scaleFactors.ContainsKey(Package.Version))
|
|
||||||
{
|
|
||||||
foreach (var factor in scaleFactors[Package.Version])
|
|
||||||
{
|
|
||||||
paths.Add($"{prefix}.scale-{factor}{extension}");
|
|
||||||
paths.Add($"{prefix}.scale-{factor}_{theme}{extension}");
|
|
||||||
paths.Add($"{prefix}.{theme}_scale-{factor}{extension}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paths = paths.OrderByDescending(x => x.Contains(theme, StringComparison.OrdinalIgnoreCase)).ToList();
|
|
||||||
var selected = paths.FirstOrDefault(File.Exists);
|
|
||||||
if (!string.IsNullOrEmpty(selected))
|
|
||||||
{
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int appIconSize = 36;
|
|
||||||
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel();
|
|
||||||
Dictionary<string, int> pathFactorPairs = new Dictionary<string, int>();
|
|
||||||
|
|
||||||
foreach (var factor in targetSizes)
|
|
||||||
{
|
|
||||||
string simplePath = $"{prefix}.targetsize-{factor}{extension}";
|
|
||||||
string suffixThemePath = $"{prefix}.targetsize-{factor}_{theme}{extension}";
|
|
||||||
string prefixThemePath = $"{prefix}.{theme}_targetsize-{factor}{extension}";
|
|
||||||
|
|
||||||
paths.Add(simplePath);
|
|
||||||
paths.Add(suffixThemePath);
|
|
||||||
paths.Add(prefixThemePath);
|
|
||||||
|
|
||||||
pathFactorPairs.Add(simplePath, factor);
|
|
||||||
pathFactorPairs.Add(suffixThemePath, factor);
|
|
||||||
pathFactorPairs.Add(prefixThemePath, factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
paths = paths.OrderByDescending(x => x.Contains(theme, StringComparison.OrdinalIgnoreCase)).ToList();
|
|
||||||
var selectedIconPath = paths.OrderBy(x => Math.Abs(pathFactorPairs.GetValueOrDefault(x) - appIconSize)).FirstOrDefault(File.Exists);
|
|
||||||
if (!string.IsNullOrEmpty(selectedIconPath))
|
|
||||||
{
|
|
||||||
return selectedIconPath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ProgramLogger.LogException(
|
|
||||||
$"|UWP|LogoPathFromUri|{Package.Location}" +
|
|
||||||
$"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
isLogoUriSet = SetColoredIcon(path, ContrastBlack);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLogoUriSet)
|
||||||
|
{
|
||||||
|
LogoPath = string.Empty;
|
||||||
|
LogoType = LogoType.Error;
|
||||||
ProgramLogger.LogException(
|
ProgramLogger.LogException(
|
||||||
$"|UWP|LogoPathFromUri|{Package.Location}" +
|
$"|UWP|LogoPathFromUri|{Package.Location}" +
|
||||||
$"|Unable to find extension from {uri} for {UserModelId} " +
|
$"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
|
||||||
$"in package location {Package.Location}", new FileNotFoundException());
|
|
||||||
return string.Empty;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageSource Logo()
|
public ImageSource Logo()
|
||||||
{
|
{
|
||||||
var logo = ImageFromPath(LogoPath);
|
if (LogoType == LogoType.Colored)
|
||||||
return logo;
|
{
|
||||||
|
var logo = ImageFromPath(LogoPath);
|
||||||
|
var platedImage = PlatedImage(logo);
|
||||||
|
return platedImage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ImageFromPath(LogoPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageSource PlatedImage(BitmapImage image)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(BackgroundColor))
|
||||||
|
{
|
||||||
|
string currentBackgroundColor;
|
||||||
|
if (BackgroundColor == "transparent")
|
||||||
|
{
|
||||||
|
currentBackgroundColor = SystemParameters.WindowGlassBrush.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentBackgroundColor = BackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
var padding = 8;
|
||||||
|
var width = image.Width + (2 * padding);
|
||||||
|
var height = image.Height + (2 * padding);
|
||||||
|
var x = 0;
|
||||||
|
var y = 0;
|
||||||
|
|
||||||
|
var group = new DrawingGroup();
|
||||||
|
var converted = ColorConverter.ConvertFromString(currentBackgroundColor);
|
||||||
|
if (converted != null)
|
||||||
|
{
|
||||||
|
var color = (Color)converted;
|
||||||
|
var brush = new SolidColorBrush(color);
|
||||||
|
var pen = new Pen(brush, 1);
|
||||||
|
var backgroundArea = new Rect(0, 0, width, height);
|
||||||
|
var rectangleGeometry = new RectangleGeometry(backgroundArea);
|
||||||
|
var rectDrawing = new GeometryDrawing(brush, pen, rectangleGeometry);
|
||||||
|
group.Children.Add(rectDrawing);
|
||||||
|
|
||||||
|
var imageArea = new Rect(x + padding, y + padding, image.Width, image.Height);
|
||||||
|
var imageDrawing = new ImageDrawing(image, imageArea);
|
||||||
|
group.Children.Add(imageDrawing);
|
||||||
|
|
||||||
|
// http://stackoverflow.com/questions/6676072/get-system-drawing-bitmap-of-a-wpf-area-using-visualbrush
|
||||||
|
var visual = new DrawingVisual();
|
||||||
|
var context = visual.RenderOpen();
|
||||||
|
context.DrawDrawing(group);
|
||||||
|
context.Close();
|
||||||
|
const int dpiScale100 = 96;
|
||||||
|
var bitmap = new RenderTargetBitmap(
|
||||||
|
Convert.ToInt32(width),
|
||||||
|
Convert.ToInt32(height),
|
||||||
|
dpiScale100,
|
||||||
|
dpiScale100,
|
||||||
|
PixelFormats.Pbgra32);
|
||||||
|
bitmap.Render(visual);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProgramLogger.LogException(
|
||||||
|
$"|UWP|PlatedImage|{Package.Location}|Unable to convert background string {BackgroundColor} " +
|
||||||
|
$"to color for {Package.Location}", new InvalidOperationException());
|
||||||
|
|
||||||
|
return new BitmapImage(new Uri(Constant.ErrorIcon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// todo use windows theme as background
|
||||||
|
return image;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BitmapImage ImageFromPath(string path)
|
private BitmapImage ImageFromPath(string path)
|
||||||
@@ -481,8 +676,7 @@ namespace Microsoft.Plugin.Program.Programs
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ProgramLogger.LogException(
|
ProgramLogger.LogException(
|
||||||
$"|UWP|ImageFromPath|{path}" +
|
$"|UWP|ImageFromPath|{path}|Unable to get logo for {UserModelId} from {path} and" +
|
||||||
$"|Unable to get logo for {UserModelId} from {path} and" +
|
|
||||||
$" located in {Package.Location}", new FileNotFoundException());
|
$" located in {Package.Location}", new FileNotFoundException());
|
||||||
return new BitmapImage(new Uri(ImageLoader.ErrorIconPath));
|
return new BitmapImage(new Uri(ImageLoader.ErrorIconPath));
|
||||||
}
|
}
|
||||||
@@ -493,4 +687,11 @@ namespace Microsoft.Plugin.Program.Programs
|
|||||||
return $"{DisplayName}: {Description}";
|
return $"{DisplayName}: {Description}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum LogoType
|
||||||
|
{
|
||||||
|
Error,
|
||||||
|
Colored,
|
||||||
|
HighContrast,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user