Skip to content

Commit

Permalink
High-dpi fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
cyanfish committed Sep 6, 2024
1 parent 1ab56c9 commit 262b53c
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 29 deletions.
8 changes: 7 additions & 1 deletion NAPS2.Lib.Mac/EtoForms/Mac/MacEtoPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,18 @@ public override IListView<T> CreateListView<T>(ListViewBehavior<T> behavior) =>

public override void ConfigureImageButton(Button button, ButtonFlags flags)
{
var nsButton = (NSButton) button.ToNative();
if (button.ImagePosition == ButtonImagePosition.Above)
{
var nsButton = (NSButton) button.ToNative();
nsButton.ImageHugsTitle = true;
nsButton.Title = Environment.NewLine + nsButton.Title;
}
var image = nsButton.Image;
if (image.Representations() is [NSBitmapImageRep rep, ..])

Check warning on line 59 in NAPS2.Lib.Mac/EtoForms/Mac/MacEtoPlatform.cs

View workflow job for this annotation

GitHub Actions / build (macos-14)

Dereference of a possibly null reference.
{
image.Size = new CGSize(rep.PixelsWide / 2f, rep.PixelsHigh / 2f);
nsButton.Image = image;
}
}

public override Bitmap ToBitmap(IMemoryImage image)
Expand Down
3 changes: 2 additions & 1 deletion NAPS2.Lib/EtoForms/EtoPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ public virtual void ConfigureZoomButton(Button button, string icon)
{
}

public virtual void AttachDpiDependency(Control control, Action<float> callback) => callback(1f);
public virtual void AttachDpiDependency(Control control, Action<float> callback) =>
callback(GetScaleFactor(control.ParentWindow));

public virtual SizeF GetWrappedSize(Control control, int defaultWidth)
{
Expand Down
8 changes: 5 additions & 3 deletions NAPS2.Lib/EtoForms/Layout/C.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Windows.Input;
using Eto.Drawing;
using Eto.Forms;
using NAPS2.Scan;

namespace NAPS2.EtoForms.Layout;

Expand Down Expand Up @@ -93,7 +92,8 @@ public static Button Button(ActionCommand command, ButtonImagePosition imagePosi
return Button(command, command.IconName, imagePosition, flags);
}

public static Button Button(ActionCommand command, string? iconName, ButtonImagePosition imagePosition = default, ButtonFlags flags = default)
public static Button Button(ActionCommand command, string? iconName, ButtonImagePosition imagePosition = default,
ButtonFlags flags = default)
{
var button = Button(command);
if (command.Image != null)
Expand All @@ -114,8 +114,10 @@ public static Button Button(ActionCommand command, string? iconName, ButtonImage
button.ImagePosition = imagePosition;
if (flags.HasFlag(ButtonFlags.LargeText))
{
var baseFontSize = button.Font.Size;
EtoPlatform.Current.AttachDpiDependency(button,
scale => button.Font = new Font(button.Font.Family, 12 * scale));
_ => button.Font = new Font(button.Font.Family,
baseFontSize * 4 / 3 * EtoPlatform.Current.GetLayoutScaleFactor(button.ParentWindow)));
}
EtoPlatform.Current.ConfigureImageButton(button, flags);
return button;
Expand Down
25 changes: 18 additions & 7 deletions NAPS2.Lib/EtoForms/Ui/ChooseDeviceForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class ChooseDeviceForm : EtoDialogBase

private CancellationTokenSource? _getDevicesCts;
private Driver? _activeQuery;
private string? _statusIconName;

public ChooseDeviceForm(Naps2Config config, IIconProvider iconProvider,
DeviceListViewBehavior deviceListViewBehavior, ScanningContext scanningContext,
Expand All @@ -47,8 +48,10 @@ public ChooseDeviceForm(Naps2Config config, IIconProvider iconProvider,
_selectDevice = C.OkButton(this, SelectDevice, UiStrings.Select);
_deviceIconList = EtoPlatform.Current.CreateListView(deviceListViewBehavior);
_deviceIconList.ImageSize = new Size(48, 32);
deviceListViewBehavior.SetImage(AlwaysAskMarker, iconProvider.GetIcon("ask")!);
deviceListViewBehavior.SetImage(ManualIpMarker, iconProvider.GetIcon("network_ip")!);
deviceListViewBehavior.SetIconName(AlwaysAskMarker, "ask");
deviceListViewBehavior.SetIconName(ManualIpMarker, "network_ip");

EtoPlatform.Current.AttachDpiDependency(this, _ => UpdateStatusIcon());

_deviceTextList.Activated += (_, _) => _selectDevice.PerformClick();
_deviceIconList.ItemClicked += (_, _) => _selectDevice.PerformClick();
Expand All @@ -62,6 +65,15 @@ public ChooseDeviceForm(Naps2Config config, IIconProvider iconProvider,
_textListVis.IsVisible = config.Get(c => c.DeviceListAsTextOnly);
}

private void UpdateStatusIcon()
{
if (_statusIconName != null)
{
_statusIcon.Image = _iconProvider.GetIcon(_statusIconName, EtoPlatform.Current.GetScaleFactor(this));
_statusIcon.Size = Size.Round(new SizeF(16, 16) * EtoPlatform.Current.GetLayoutScaleFactor(this));
}
}

private void Driver_MouseUp(object? sender, EventArgs e)
{
QueryForDevices();
Expand Down Expand Up @@ -274,10 +286,8 @@ private void QueryForDevices()
if (!cts.IsCancellationRequested)
{
_spinnerVis.IsVisible = false;
_statusIcon.Image =
DeviceList.Count > 0
? _iconProvider.GetIcon("accept_small")
: _iconProvider.GetIcon("exclamation_small");
_statusIconName = DeviceList.Count > 0 ? "accept_small" : "exclamation_small";
UpdateStatusIcon();
_statusLabel.Text = DeviceList.Count switch
{
> 1 => string.Format(UiStrings.DevicesFound, DeviceList.Count),
Expand All @@ -294,7 +304,8 @@ private void QueryForDevices()
if (!cts.IsCancellationRequested)
{
_spinnerVis.IsVisible = false;
_statusIcon.Image = _iconProvider.GetIcon("exclamation_small");
_statusIconName = "exclamation_small";
UpdateStatusIcon();
_statusLabel.Text = ex.Message;
}
});
Expand Down
3 changes: 2 additions & 1 deletion NAPS2.Lib/EtoForms/Ui/DesktopCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public DesktopCommands(DesktopController desktopController, DesktopScanControlle
};
SaveAll = new ActionCommand(imageListActions.SaveAllAsPdfOrImages)
{
Text = UiStrings.SaveAll
Text = UiStrings.SaveAll,
IconName = "diskette"
};
SaveSelected = new ActionCommand(imageListActions.SaveSelectedAsPdfOrImages)
{
Expand Down
12 changes: 11 additions & 1 deletion NAPS2.Lib/EtoForms/Widgets/DeviceListViewBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace NAPS2.EtoForms.Widgets;
public class DeviceListViewBehavior : ListViewBehavior<ScanDevice>
{
private readonly Dictionary<ScanDevice, Image> _imageMap = new();
private readonly Dictionary<ScanDevice, string> _iconNameMap = new();

public DeviceListViewBehavior(ColorScheme colorScheme) : base(colorScheme)
{
Expand All @@ -16,10 +17,19 @@ public DeviceListViewBehavior(ColorScheme colorScheme) : base(colorScheme)

public void SetImage(ScanDevice item, Image image) => _imageMap[item] = image;

public void SetIconName(ScanDevice item, string iconName) => _iconNameMap[item] = iconName;

public override string GetLabel(ScanDevice item) => item.Name;

public override Image GetImage(IListView<ScanDevice> listView, ScanDevice item)
{
return (_imageMap.Get(item)?.Clone() ?? Icons.device.ToEtoImage()).PadTo(listView.ImageSize);
float scale = EtoPlatform.Current.GetScaleFactor(listView.Control.ParentWindow);
if (_imageMap.Get(item) is { } image)
{
int scaledSize = (int) Math.Round(48 * scale);
return image.Clone().ResizeTo(scaledSize).PadTo(listView.ImageSize);
}
string iconName = _iconNameMap.Get(item) ?? "device";
return EtoPlatform.Current.IconProvider.GetIcon(iconName, scale)!.PadTo(listView.ImageSize);
}
}
33 changes: 21 additions & 12 deletions NAPS2.Lib/EtoForms/Widgets/DeviceSelectorWidget.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading;
using Eto.Drawing;
using Eto.Forms;
using NAPS2.EtoForms.Layout;
using NAPS2.Scan;
Expand All @@ -19,6 +20,8 @@ public class DeviceSelectorWidget
private readonly Button _chooseDevice = new() { Text = UiStrings.ChooseDevice };

private DeviceChoice _choice = DeviceChoice.None;
private Image? _deviceIconImage;
private string _deviceIconName = "device";
private CancellationTokenSource? _loadIconCts;

public DeviceSelectorWidget(IScanPerformer scanPerformer, DeviceCapsCache deviceCapsCache,
Expand All @@ -29,6 +32,7 @@ public DeviceSelectorWidget(IScanPerformer scanPerformer, DeviceCapsCache device
_iconProvider = iconProvider;
_parentWindow = parentWindow;
_chooseDevice.Click += ChooseDevice;
EtoPlatform.Current.AttachDpiDependency(_deviceIcon, _ => UpdateDeviceIconImage());
}

public required Func<ScanProfile> ProfileFunc { get; init; }
Expand Down Expand Up @@ -76,7 +80,6 @@ public bool Enabled

private async void ChooseDevice(object? sender, EventArgs args)
{
;
var choice = await _scanPerformer.PromptForDevice(ProfileFunc(), AllowAlwaysAsk, _parentWindow.NativeHandle);
if (choice.Device != null || choice.AlwaysAsk)
{
Expand All @@ -90,15 +93,15 @@ private async void ChooseDevice(object? sender, EventArgs args)

public void SetDeviceIcon(string? iconUri)
{
var cachedIcon = _deviceCapsCache.GetCachedIcon(iconUri);
EtoPlatform.Current.AttachDpiDependency(_deviceIcon, scale =>
_deviceIcon.Image =
cachedIcon ?? (_choice.AlwaysAsk ? _iconProvider.GetIcon("ask", scale) : _iconProvider.GetIcon("device", scale)));
_deviceIconImage = _deviceCapsCache.GetCachedIcon(iconUri);
_deviceIconName = _choice.AlwaysAsk ? "ask" : "device";
UpdateDeviceIconImage();

if (((Window) _parentWindow).Loaded)
{
_parentWindow.LayoutController.Invalidate();
}
if (cachedIcon == null && iconUri != null)
if (_deviceIconImage == null && iconUri != null)
{
ReloadDeviceIcon(iconUri);
}
Expand All @@ -118,14 +121,23 @@ private void ReloadDeviceIcon(string iconUri)
{
if (!cts.IsCancellationRequested)
{
_deviceIcon.Image = icon;
_deviceIconImage = icon;
UpdateDeviceIconImage();
_parentWindow.LayoutController.Invalidate();
}
});
}
});
}

private void UpdateDeviceIconImage()
{
float scale = EtoPlatform.Current.GetScaleFactor(_deviceIcon.ParentWindow);
_deviceIcon.Image = _deviceIconImage ?? _iconProvider.GetIcon(_deviceIconName, scale);
var size = _deviceIconImage != null ? new SizeF(48, 48) : new SizeF(32, 32);
_deviceIcon.Size = Size.Round(size * EtoPlatform.Current.GetLayoutScaleFactor(_deviceIcon.ParentWindow));
}

public static implicit operator LayoutElement(DeviceSelectorWidget control)
{
return control.AsControl();
Expand All @@ -142,11 +154,8 @@ public LayoutElement AsControl()
_deviceDriver,
C.Filler()
).Spacing(5).Visible(_deviceVis).Scale(),
// TODO: We should probably have a compact choose-device button for the sidebar.
// It should also change the name of the profile if it matches the device name.
// i.e. for users that are naming their own profiles, its their responsibility to keep the devices
// matched up. For a "basic" user that might only create one profile, its name should keep matched
// with the device.
// TODO: We can consider a compact choose-device button for the sidebar, but maybe simpler to force
// creation of separate profiles
ShowChooseDevice ? _chooseDevice.AlignCenter() : C.None()
)
);
Expand Down
4 changes: 1 addition & 3 deletions NAPS2.Lib/Scan/DeviceCapsCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ namespace NAPS2.Scan;

public class DeviceCapsCache
{
private const int ICON_SIZE = 48;

private readonly Dictionary<DeviceKey, ScanCaps> _capsCache = new();
private readonly Dictionary<string, Image> _iconCache = new();

Expand Down Expand Up @@ -120,7 +118,7 @@ private async Task<Image> DoLoadIcon(string iconUri)
var imageBytes = await client.GetByteArrayAsync(iconUri);
image = _imageContext.Load(imageBytes);
}
return image.PerformTransform(new ThumbnailTransform(ICON_SIZE)).ToEtoImage();
return image.ToEtoImage();
}

private DeviceKey GetDeviceKey(ScanProfile profile)
Expand Down

0 comments on commit 262b53c

Please sign in to comment.