Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mac: Fix some memory leaks #2560

Merged
merged 1 commit into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions src/Eto.Mac/Drawing/FormattedTextHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ namespace Eto.iOS.Drawing
{
public class EtoLayoutManager : NSLayoutManager
{
public Brush ForegroundBrush { get; set; } = new SolidBrush(SystemColors.ControlText);

internal GraphicsHandler CurrentGraphics { get; set; }
WeakReference WeakHandler { get; set; }

public FormattedTextHandler Handler
{
get => WeakHandler.Target as FormattedTextHandler;
set => WeakHandler = new WeakReference(value);
}

static IntPtr selShowCGGlyphs_Positions_Count_Font_Matrix_Attributes_InContext_Handle = Selector.GetHandle("showCGGlyphs:positions:count:font:matrix:attributes:inContext:");

Expand All @@ -29,13 +33,17 @@ public class EtoLayoutManager : NSLayoutManager
[Export("showCGGlyphs:positions:count:font:matrix:attributes:inContext:")]
protected void ShowGlyphs(IntPtr glyphs, IntPtr positions, nuint glyphCount, IntPtr font, CGAffineTransform textMatrix, IntPtr attributes, IntPtr graphicsContext)
{
if (ForegroundBrush is SolidBrush)
var h = Handler;
if (h == null)
return;
var foregroundBrush = h.ForegroundBrush;
if (foregroundBrush is SolidBrush)
{
// attributes can be null, Xamarin.Mac doesn't allow that when calling base. ugh.
Messaging.void_objc_msgSendSuper_IntPtr_IntPtr_nuint_IntPtr_CGAffineTransform_IntPtr_IntPtr(SuperHandle, selShowCGGlyphs_Positions_Count_Font_Matrix_Attributes_InContext_Handle, glyphs, positions, glyphCount, font, textMatrix, attributes, graphicsContext);
//base.ShowGlyphs(glyphs, positions, glyphCount, font, textMatrix, attributes, graphicsContext);
}
else if (glyphCount > 0)
else if (glyphCount > 0 && h.CurrentGraphics != null)
{
// draw manually so we can use a custom brush/fill.
var ctx = NSGraphicsContext.CurrentContext.GraphicsPort;
Expand All @@ -53,7 +61,7 @@ protected void ShowGlyphs(IntPtr glyphs, IntPtr positions, nuint glyphCount, Int
ctx.TextMatrix = m;
CTFontDrawGlyphs(font, glyphs, positions, glyphCount, ctx.Handle);

ForegroundBrush.Draw(CurrentGraphics, false, FillMode.Winding, false);
foregroundBrush.Draw(h.CurrentGraphics, false, FillMode.Winding, false);
ctx.RestoreState();
}
}
Expand All @@ -77,6 +85,9 @@ public class FormattedTextHandler : WidgetHandler<EtoLayoutManager, FormattedTex
FormattedTextWrapMode _wrap;
FormattedTextAlignment _alignment;
SizeF _maximumSize = SizeF.MaxValue;
Brush _foregroundBrush = new SolidBrush(SystemColors.ControlText);

internal GraphicsHandler CurrentGraphics { get; set; }

public FormattedTextAlignment Alignment
{
Expand Down Expand Up @@ -177,10 +188,10 @@ public Font Font

public Brush ForegroundBrush
{
get => Control.ForegroundBrush;
get => _foregroundBrush;
set
{
Control.ForegroundBrush = value;
_foregroundBrush = value;
Invalidate();
}
}
Expand Down Expand Up @@ -253,9 +264,9 @@ public FormattedTextHandler()
public void DrawText(GraphicsHandler graphics, PointF location)
{
EnsureString();
Control.CurrentGraphics = graphics;
var ctx = graphics.Control;
CurrentGraphics = graphics;
storage.DrawString(new CGRect(location.ToNS(), container.Size), NSStringDrawingOptions.UsesLineFragmentOrigin | NSStringDrawingOptions.TruncatesLastVisibleLine);
CurrentGraphics = null;
// Control.DrawGlyphs(new NSRange(0, (int)_text.Length), location.ToNS());
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/Eto.Mac/Drawing/GraphicsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ public GraphicsHandler(NSView view, CGContext context, nfloat height)

protected override void Dispose(bool disposing)
{
if (disposing)
{
_formattedText?.Dispose();
_formattedText = null;
}
base.Dispose(disposing);
}

Expand Down
5 changes: 0 additions & 5 deletions src/Eto.Mac/Forms/MacBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ public void AddToNotificationCenter()
if (!isNotification && c != null)
{
NSNotificationCenter.DefaultCenter.AddObserver(this, selPerformAction, KeyPath, c);
c.DangerousRetain();
isNotification = true;
}
}
Expand All @@ -69,12 +68,10 @@ public void AddToControl()
{
//Console.WriteLine ("{0}: 3. Adding observer! {1}, {2}", ((IRef)this.Handler).WidgetID, this.GetType (), Control.GetHashCode ());
c.AddObserver(this, KeyPath, NSKeyValueObservingOptions.New, IntPtr.Zero);
c.DangerousRetain();
isControl = true;
}
}

static readonly IntPtr selRelease_Handle = Selector.GetHandle("release");
static readonly IntPtr selRemoveObserverForKeyPath_Handle = Selector.GetHandle("removeObserver:forKeyPath:");

public void Remove()
Expand All @@ -83,15 +80,13 @@ public void Remove()
if (isNotification)
{
NSNotificationCenter.DefaultCenter.RemoveObserver(this);
Messaging.void_objc_msgSend(ControlHandle, selRelease_Handle);

isNotification = false;
}
if (isControl)
{
//Console.WriteLine ("{0}: 4. Removing observer! {1}, {2}", ((IRef)this.Handler).WidgetID, Handler.GetType (), Control.GetHashCode ());
Messaging.void_objc_msgSend_IntPtr_IntPtr(ControlHandle, selRemoveObserverForKeyPath_Handle, Handle, KeyPath.Handle);
Messaging.void_objc_msgSend(ControlHandle, selRelease_Handle);
isControl = false;
}
}
Expand Down
31 changes: 20 additions & 11 deletions src/Eto.Mac/Forms/MacWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1071,15 +1071,21 @@ protected override void Initialize()
{
base.Initialize();

// need to send Got/LostFocus to child when window Got/LostFocus is called
HandleEvent(Window.LostFocusEvent);
HandleEvent(Window.GotFocusEvent);
if (!Widget.IsAttached)
{
// need to send Got/LostFocus to child when window Got/LostFocus is called
HandleEvent(Window.LostFocusEvent);
HandleEvent(Window.GotFocusEvent);
}
}

public override void OnLoad(EventArgs e)
{
PerformAutoSize();
PositionWindow();
if (!Widget.IsAttached)
{
PerformAutoSize();
PositionWindow();
}
base.OnLoad(e);
}

Expand All @@ -1097,14 +1103,17 @@ protected override void FireOnShown()
public override void OnLoadComplete(EventArgs e)
{
base.OnLoadComplete(e);
if (initialState != null)
if (!Widget.IsAttached)
{
WindowState = initialState.Value;
initialState = null;
Callback.OnSizeChanged(Widget, EventArgs.Empty);
if (initialState != null)
{
WindowState = initialState.Value;
initialState = null;
Callback.OnSizeChanged(Widget, EventArgs.Empty);
}
else if (!setInitialSize)
Callback.OnSizeChanged(Widget, EventArgs.Empty);
}
else if (!setInitialSize)
Callback.OnSizeChanged(Widget, EventArgs.Empty);
}

protected virtual void PositionWindow()
Expand Down
33 changes: 33 additions & 0 deletions src/Eto.Mac/Forms/NativeFormHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,39 @@ protected override void ConfigureWindow()
{
}

internal static Window CreateWrapper(NSWindowController windowController, NSWindow nswindow)
{
var handle = windowController?.Handle ?? nswindow.Handle;

s_cachedWindows ??= new Dictionary<NativeHandle, WeakReference>();

if (s_cachedWindows.TryGetValue(handle, out var windowReference) && windowReference.Target is Window window)
return window;

var handler = windowController != null ? new NativeFormHandler(windowController) : new NativeFormHandler(nswindow);
window = new Form(handler);
s_cachedWindows.Add(handle, new WeakReference(window));
List<NativeHandle> toRemove = null;
foreach (var entry in s_cachedWindows)
{
if (!entry.Value.IsAlive)
{
toRemove ??= new List<NativeHandle>();
toRemove.Add(entry.Key);
}
}
if (toRemove != null)
{
foreach (var entry in toRemove)
{
s_cachedWindows.Remove(entry);
}
}
return window;
}

static Dictionary<NativeHandle, WeakReference> s_cachedWindows;

public override Size Size
{
get => Control.Frame.Size.ToEtoSize();
Expand Down
4 changes: 2 additions & 2 deletions src/Eto.Mac/MacHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static Window ToEtoWindow(this NSWindow window)
if (window is IMacControl macControl && macControl.WeakHandler?.Target is IMacWindow macWindow)
return macWindow.Widget;

return new Form(new NativeFormHandler(window));
return NativeFormHandler.CreateWrapper(null, window);
}

/// <summary>
Expand All @@ -106,7 +106,7 @@ public static Window ToEtoWindow(this NSWindowController windowController)
{
if (windowController == null)
return null;
return new Form(new NativeFormHandler(windowController));
return NativeFormHandler.CreateWrapper(windowController, null);
}

/// <summary>
Expand Down
Loading