Skip to content

Commit

Permalink
Gtk: Scale image buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
cyanfish committed Aug 30, 2024
1 parent ac35a1e commit 0e5a442
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 2 deletions.
6 changes: 4 additions & 2 deletions NAPS2.Lib.Gtk/EtoForms/Gtk/GtkEtoPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public override IListView<T> CreateListView<T>(ListViewBehavior<T> behavior) =>

public override void ConfigureImageButton(Button button, ButtonFlags flags)
{
AttachDpiDependency(button, _ => button.ScaleImage());
}

public override Bitmap ToBitmap(IMemoryImage image)
Expand Down Expand Up @@ -269,10 +270,11 @@ public override Control AccessibleImageButton(Image image, string text, Action o

public override void ConfigureZoomButton(Button button, string icon)
{
var gtkButton = button.ToNative();
button.Text = "";
button.Image = IconProvider.GetIcon(icon);
button.Image = IconProvider.GetIcon(icon, gtkButton.ScaleFactor);
button.ScaleImage();
button.Size = Size.Empty;
var gtkButton = button.ToNative();
gtkButton.StyleContext.AddClass("zoom-button");
gtkButton.SetSizeRequest(0, 0);
}
Expand Down
37 changes: 37 additions & 0 deletions NAPS2.Lib.Gtk/Util/GtkEtoExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,50 @@
using System.Reflection;
using Eto.Forms;
using Eto.GtkSharp;
using Eto.GtkSharp.Forms.Controls;
using Gdk;
using Gtk;

namespace NAPS2.Util;

public static class GtkEtoExtensions
{
private static readonly FieldInfo GtkImageField =
typeof(ButtonHandler<Gtk.Button, Eto.Forms.Button, Eto.Forms.Button.ICallback>).GetField("gtkimage",
BindingFlags.Instance | BindingFlags.NonPublic)!;

private static readonly MethodInfo SetImagePositionMethod =
typeof(ButtonHandler<Gtk.Button, Eto.Forms.Button, Eto.Forms.Button.ICallback>).GetMethod(
"SetImagePosition", BindingFlags.Instance | BindingFlags.NonPublic)!;

public static Image ToScaledImage(this Pixbuf pixbuf, int scaleFactor)
{
// Creating a Gtk.Image directly from a Pixbuf doesn't work if we want to render at high dpi.
// For example, if we have a 32x32 logical image size that actually gets rendered at 64x64 pixels due to a 2x
// display scaling, naively using the 64x64 image will get displayed with 64x64 logical size and 128x128
// physical size, which will look too big and blurry. To get an actual crisp image rendered at 32x32 logical
// pixels and 64x64 physical pixels, we first create a surface with a 2x scale factor and then create the
// Gtk.Image from that.
var surface = Gdk.CairoHelper.SurfaceCreateFromPixbuf(pixbuf, scaleFactor, null);
return new Image(surface);
}

public static void SetImage(this Eto.Forms.Button button, Image image)
{
// Hack to inject a Gtk.Image directly into an Eto.Forms.Button. Normally Eto only takes an Eto.Drawing.Image
// (which wraps a Pixbuf) but to correctly scale images at high dpi we need a Gtk.Image constructed from a
// surface.
image.Show();
GtkImageField.SetValue(button.Handler, image);
SetImagePositionMethod.Invoke(button.Handler, []);
}

public static void ScaleImage(this Eto.Forms.Button button)
{
// Helper to read the Eto.Drawing.Image from the Eto.Forms.Button, scale it, then inject it back into the
// button.
int scaleFactor = button.ToNative().ScaleFactor;
var image = button.Image.ToGdk().ToScaledImage(scaleFactor);
button.SetImage(image);
}
}

0 comments on commit 0e5a442

Please sign in to comment.