Skip to content

Commit

Permalink
Miscellaneous refactorings in GdkExtensions (#1157)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lehonti authored Nov 21, 2024
1 parent 7d7c01d commit de8f4f9
Showing 1 changed file with 86 additions and 58 deletions.
144 changes: 86 additions & 58 deletions Pinta.Core/Extensions/GdkExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Gdk;
using Cairo;

namespace Pinta.Core;

Expand All @@ -43,45 +43,45 @@ static GdkExtensions ()
osxLibraryName: "libgtk-4.1.dylib"
);
}
public static bool IsShiftPressed (this ModifierType m)
=> m.HasFlag (ModifierType.ShiftMask);
public static bool IsShiftPressed (this Gdk.ModifierType m)
=> m.HasFlag (Gdk.ModifierType.ShiftMask);

/// <summary>
/// Returns whether a Ctrl modifier is pressed (or the Cmd key on macOS).
/// </summary>
public static bool IsControlPressed (this ModifierType m)
public static bool IsControlPressed (this Gdk.ModifierType m)
{
if (PintaCore.System.OperatingSystem == OS.Mac) {
return m.HasFlag (ModifierType.MetaMask);
} else
return m.HasFlag (ModifierType.ControlMask);
if (PintaCore.System.OperatingSystem == OS.Mac)
return m.HasFlag (Gdk.ModifierType.MetaMask);
else
return m.HasFlag (Gdk.ModifierType.ControlMask);
}

public static bool IsAltPressed (this ModifierType m)
=> m.HasFlag (ModifierType.AltMask);
public static bool IsAltPressed (this Gdk.ModifierType m)
=> m.HasFlag (Gdk.ModifierType.AltMask);

public static bool IsLeftMousePressed (this ModifierType m)
=> m.HasFlag (ModifierType.Button1Mask);
public static bool IsLeftMousePressed (this Gdk.ModifierType m)
=> m.HasFlag (Gdk.ModifierType.Button1Mask);

public static bool IsRightMousePressed (this ModifierType m)
=> m.HasFlag (ModifierType.Button3Mask);
public static bool IsRightMousePressed (this Gdk.ModifierType m)
=> m.HasFlag (Gdk.ModifierType.Button3Mask);

/// <summary>
/// Returns whether this key is a Ctrl key (or the Cmd key on macOS).
/// </summary>
public static bool IsControlKey (this Key key)
public static bool IsControlKey (this Gdk.Key key)
{
if (PintaCore.System.OperatingSystem == OS.Mac)
return key == Key.Meta_L || key == Key.Meta_R;
return key == Gdk.Key.Meta_L || key == Gdk.Key.Meta_R;
else
return key == Key.Control_L || key == Key.Control_R;
return key == Gdk.Key.Control_L || key == Gdk.Key.Control_R;
}

/// <summary>
/// Returns whether any of the Ctrl/Cmd/Shift/Alt modifiers are active.
/// This prevents Caps Lock, Num Lock, etc from appearing as active modifier keys.
/// </summary>
public static bool HasModifierKey (this ModifierType current_state)
public static bool HasModifierKey (this Gdk.ModifierType current_state)
=> current_state.IsControlPressed () || current_state.IsShiftPressed () || current_state.IsAltPressed ();

/// <summary>
Expand All @@ -106,30 +106,32 @@ public static Gdk.Texture CreateIconWithShape (
{
Gdk.Texture img = PintaCore.Resources.GetIcon (imgName);

double zoom = 1d;
if (PintaCore.Workspace.HasOpenDocuments) {
zoom = Math.Min (30d, PintaCore.Workspace.ActiveDocument.Workspace.Scale);
}
double zoom =
(PintaCore.Workspace.HasOpenDocuments)
? Math.Min (30d, PintaCore.Workspace.ActiveDocument.Workspace.Scale)
: 1d;

shapeWidth = (int) Math.Min (800d, shapeWidth * zoom);
int halfOfShapeWidth = shapeWidth / 2;
int clampedWidth = (int) Math.Min (800d, shapeWidth * zoom);
int halfOfShapeWidth = clampedWidth / 2;

// Calculate bounding boxes around the both image and shape
// relative to the image top-left corner.
var imgBBox = new RectangleI (0, 0, img.Width, img.Height);
var shapeBBox = new RectangleI (

RectangleI imgBBox = new (0, 0, img.Width, img.Height);

RectangleI initialShapeBBox = new (
imgToShapeX - halfOfShapeWidth,
imgToShapeY - halfOfShapeWidth,
shapeWidth,
shapeWidth);
clampedWidth,
clampedWidth);

// Inflate shape bounding box to allow for anti-aliasing
shapeBBox = shapeBBox.Inflated (2, 2);
RectangleI inflatedBBox = initialShapeBBox.Inflated (2, 2);

// To determine required size of icon,
// find union of the image and shape bounding boxes
// (still relative to image top-left corner)
RectangleI iconBBox = imgBBox.Union (shapeBBox);
RectangleI iconBBox = imgBBox.Union (inflatedBBox);

// Image top-left corner in icon coordinates
int imgX = imgBBox.Left - iconBBox.Left;
Expand All @@ -139,19 +141,26 @@ public static Gdk.Texture CreateIconWithShape (
shapeX = imgToShapeX - iconBBox.Left;
shapeY = imgToShapeY - iconBBox.Top;

var i = CairoExtensions.CreateImageSurface (Cairo.Format.Argb32, iconBBox.Width, iconBBox.Height);
using Cairo.Context g = new (i);
ImageSurface i = CairoExtensions.CreateImageSurface (
Format.Argb32,
iconBBox.Width,
iconBBox.Height);

using Context g = new (i);

// Don't show shape if shapeWidth less than 3,
if (shapeWidth > 3) {
int diam = Math.Max (1, shapeWidth - 2);
var shapeRect = new RectangleD (
if (clampedWidth > 3) {

int diam = Math.Max (1, clampedWidth - 2);

RectangleD shapeRect = new (
shapeX - halfOfShapeWidth,
shapeY - halfOfShapeWidth,
diam,
diam);

Cairo.Color outerColor = new Cairo.Color (255, 255, 255, 0.75);
Cairo.Color innerColor = new Cairo.Color (0, 0, 0);
Color outerColor = new (255, 255, 255, 0.75);
Color innerColor = new (0, 0, 0);

switch (shape) {
case CursorShape.Ellipse:
Expand All @@ -168,36 +177,48 @@ public static Gdk.Texture CreateIconWithShape (
}

// Draw the image
var img_surf = img.ToSurface ();
ImageSurface img_surf = img.ToSurface ();

g.SetSourceSurface (img_surf, imgX, imgY);
g.Paint ();

return Texture.NewForPixbuf (i.ToPixbuf ());
return Gdk.Texture.NewForPixbuf (i.ToPixbuf ());
}

public static Key ToUpper (this Key k1)
public static Gdk.Key ToUpper (this Gdk.Key k1)
{
if (Enum.TryParse (k1.ToString ().ToUpperInvariant (), out Key result))
if (Enum.TryParse (k1.ToString ().ToUpperInvariant (), out Gdk.Key result))
return result;

return k1;
else
return k1;
}

// TODO-GTK4 (bindings, unsubmitted) - need gir.core async bindings for Gdk.Clipboard
public static Task<Texture?> ReadTextureAsync (this Gdk.Clipboard clipboard)
public static Task<Gdk.Texture?> ReadTextureAsync (this Gdk.Clipboard clipboard)
{
var tcs = new TaskCompletionSource<Texture?> ();
TaskCompletionSource<Gdk.Texture?> tcs = new ();

Gdk.Internal.Clipboard.ReadTextureAsync (
clipboard.Handle,
IntPtr.Zero,
new Gio.Internal.AsyncReadyCallbackAsyncHandler ((_, args, _) => {

Gdk.Internal.Clipboard.ReadTextureAsync (clipboard.Handle, IntPtr.Zero, new Gio.Internal.AsyncReadyCallbackAsyncHandler ((_, args, _) => {
IntPtr result = Gdk.Internal.Clipboard.ReadTextureFinish (clipboard.Handle, args.Handle, out var error);
IntPtr result = Gdk.Internal.Clipboard.ReadTextureFinish (
clipboard.Handle,
args.Handle,
out var error);

Texture? texture = texture = GObject.Internal.ObjectWrapper.WrapNullableHandle<Texture> (result, ownedRef: true);
Gdk.Texture? texture = GObject.Internal.ObjectWrapper.WrapNullableHandle<Gdk.Texture> (
result,
ownedRef: true);

if (!error.IsInvalid)
texture = null;
if (!error.IsInvalid)
texture = null;

tcs.SetResult (texture);
}).NativeCallback, IntPtr.Zero);
tcs.SetResult (texture);

}).NativeCallback,
IntPtr.Zero);

return tcs.Task;
}
Expand All @@ -217,15 +238,22 @@ public static Gdk.Clipboard GetDefaultClipboard ()
/// <summary>
/// Convert a texture to a Cairo surface.
/// </summary>
public static unsafe Cairo.ImageSurface ToSurface (this Gdk.Texture texture)
public static unsafe ImageSurface ToSurface (this Gdk.Texture texture)
{
var surf = CairoExtensions.CreateImageSurface (Cairo.Format.Argb32, texture.Width, texture.Height);
ImageSurface surf = CairoExtensions.CreateImageSurface (
Format.Argb32,
texture.Width,
texture.Height);

Span<byte> surf_data = surf.GetData ();

// TODO-GTK4 (bindings, unsubmitted) - needs support for primitive value arrays
var buffer = new byte[surf_data.Length];
fixed (byte* buffer_data = buffer)
Gdk.Internal.Texture.Download (texture.Handle, ref *buffer_data, (uint) surf.Stride);
Gdk.Internal.Texture.Download (
texture.Handle,
ref *buffer_data,
(uint) surf.Stride);

buffer.CopyTo (surf_data);
surf.MarkDirty ();
Expand All @@ -240,13 +268,13 @@ public static unsafe Cairo.ImageSurface ToSurface (this Gdk.Texture texture)
// TODO-GTK4 (bindings) - Gdk.FileList.GetFiles() is not generated
public static Gio.File[] GetFilesHelper (this Gdk.FileList file_list)
{
var slist = new GLib.SList (FileListGetFiles (file_list.Handle));
GLib.SList slist = new (FileListGetFiles (file_list.Handle));

uint n = GLib.SList.Length (slist);

var result = new Gio.File[n];
for (uint i = 0; i < n; ++i) {
for (uint i = 0; i < n; ++i)
result[i] = new Gio.FileHelper (GLib.SList.NthData (slist, i), ownedRef: false);
}

return result;
}
Expand Down

0 comments on commit de8f4f9

Please sign in to comment.