Browse Source
Refresh icons when window DPI changes Provide ICON_SMALL icons Fixed Win32Icon.Size being incorrect when the exact size couldn't be found in the iconpull/14621/head
committed by
GitHub
10 changed files with 376 additions and 137 deletions
@ -1,27 +1,88 @@ |
|||||
using System; |
using System; |
||||
using System.Drawing; |
|
||||
using System.IO; |
using System.IO; |
||||
using Avalonia.Platform; |
using Avalonia.Platform; |
||||
using Avalonia.Win32.Interop; |
using Avalonia.Win32.Interop; |
||||
|
|
||||
namespace Avalonia.Win32 |
namespace Avalonia.Win32 |
||||
{ |
{ |
||||
class IconImpl : IWindowIconImpl |
internal class IconImpl : IWindowIconImpl, IDisposable |
||||
{ |
{ |
||||
private readonly Win32Icon _icon; |
private readonly Win32Icon _smallIcon; |
||||
private readonly byte[] _iconData; |
private readonly Win32Icon _bigIcon; |
||||
|
|
||||
public IconImpl(Win32Icon icon, byte[] iconData) |
private static readonly int s_taskbarIconSize = Win32Platform.WindowsVersion < PlatformConstants.Windows10 ? 32 : 24; |
||||
|
|
||||
|
public IconImpl(Stream smallIcon, Stream bigIcon) |
||||
|
{ |
||||
|
_smallIcon = CreateIconImpl(smallIcon); |
||||
|
_bigIcon = CreateIconImpl(bigIcon); |
||||
|
} |
||||
|
|
||||
|
public IconImpl(Stream icon) |
||||
|
{ |
||||
|
_smallIcon = _bigIcon = CreateIconImpl(icon); |
||||
|
} |
||||
|
|
||||
|
private static Win32Icon CreateIconImpl(Stream stream) |
||||
|
{ |
||||
|
if (stream.CanSeek) |
||||
|
{ |
||||
|
stream.Position = 0; |
||||
|
} |
||||
|
|
||||
|
if (stream is MemoryStream memoryStream) |
||||
|
{ |
||||
|
var iconData = memoryStream.ToArray(); |
||||
|
|
||||
|
return new Win32Icon(iconData); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
using var ms = new MemoryStream(); |
||||
|
stream.CopyTo(ms); |
||||
|
|
||||
|
ms.Position = 0; |
||||
|
|
||||
|
var iconData = ms.ToArray(); |
||||
|
|
||||
|
return new Win32Icon(iconData); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// GetSystemMetrics returns values scaled for the primary monitor, as of the time at which the process started.
|
||||
|
// This is no good for a per-monitor DPI aware application. GetSystemMetricsForDpi would solve the problem,
|
||||
|
// but is only available in Windows 10 version 1607 and later. So instead, we just hard-code the 96dpi icon sizes.
|
||||
|
|
||||
|
public Win32Icon LoadSmallIcon(double scaleFactor) => new(_smallIcon, GetScaledSize(16, scaleFactor)); |
||||
|
|
||||
|
public Win32Icon LoadBigIcon(double scaleFactor) |
||||
|
{ |
||||
|
var targetSize = GetScaledSize(s_taskbarIconSize, scaleFactor); |
||||
|
var icon = new Win32Icon(_bigIcon, targetSize); |
||||
|
|
||||
|
// The exact size of a taskbar icon in Windows 10 and later is 24px @ 96dpi. But if an ICO file doesn't have
|
||||
|
// that size, 16px can be selected instead. If this happens, fall back to a 32 pixel icon. Windows will downscale it.
|
||||
|
if (s_taskbarIconSize == 24 && icon.Size.Width < targetSize.Width) |
||||
|
{ |
||||
|
icon.Dispose(); |
||||
|
icon = new(_bigIcon, GetScaledSize(32, scaleFactor)); |
||||
|
} |
||||
|
|
||||
|
return icon; |
||||
|
} |
||||
|
|
||||
|
private static PixelSize GetScaledSize(int baseSize, double factor) |
||||
{ |
{ |
||||
_icon = icon; |
var scaled = (int)Math.Ceiling(baseSize * factor); |
||||
_iconData = iconData; |
return new(scaled, scaled); |
||||
} |
} |
||||
|
|
||||
public IntPtr HIcon => _icon.Handle; |
public void Save(Stream outputStream) => _bigIcon.CopyTo(outputStream); |
||||
|
|
||||
public void Save(Stream outputStream) |
public void Dispose() |
||||
{ |
{ |
||||
outputStream.Write(_iconData, 0, _iconData.Length); |
_smallIcon.Dispose(); |
||||
|
_bigIcon.Dispose(); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue