naps2/NAPS2.Lib/EtoForms/Notifications/NotificationView.cs
Ben Olden-Cooligan 1f0e529ca8 Mac: Don't depend on ClipRectangle
Starting in macOS 14 there is a new clipsToBounds property that defaults to false, which is a behavior change. This causes incompatibilities with anything that uses ClipRectangle directly or indirectly.

https://developer.apple.com/documentation/macos-release-notes/appkit-release-notes-for-macos-14#NSView

#227
2023-11-19 10:09:30 -08:00

147 lines
5.0 KiB
C#

using System.Threading;
using Eto.Drawing;
using Eto.Forms;
using NAPS2.EtoForms.Layout;
namespace NAPS2.EtoForms.Notifications;
public abstract class NotificationView : IDisposable
{
private const int BORDER_RADIUS = 7;
private const int CLOSE_BUTTON_SIZE = 18;
protected const int HIDE_LONG = 60 * 1000;
protected const int HIDE_SHORT = 5 * 1000;
protected NotificationView(NotificationModel model)
{
Model = model;
}
public NotificationModel Model { get; }
protected int HideTimeout { get; set; }
protected bool ShowClose { get; set; } = true;
public NotificationManager? Manager { get; set; }
protected abstract LayoutElement PrimaryContent { get; }
protected abstract LayoutElement SecondaryContent { get; }
protected Color BackgroundColor => Manager!.ColorScheme.NotificationBackgroundColor;
protected Color BorderColor => Manager!.ColorScheme.NotificationBorderColor;
public LayoutElement CreateContent()
{
BeforeCreateContent();
var drawable = new Drawable();
drawable.Paint += DrawableOnPaint;
var closeButton = new CloseButton(Manager!.ColorScheme);
closeButton.Click += (_, _) => Manager!.Hide(Model);
drawable.MouseUp += (_, _) => NotificationClicked();
drawable.Load += (_, _) => SetUpHideTimeout(drawable);
return L.Row(
C.Filler(),
L.Overlay(
drawable.MinWidth(120),
L.Column(
L.Row(
PrimaryContent,
ShowClose ? C.Spacer().Width(CLOSE_BUTTON_SIZE) : C.None()),
SecondaryContent
).Padding(10, 8, 10, 8),
ShowClose
? L.Column(
L.Row(
C.Filler(),
closeButton.Size(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE)
),
C.Filler()
).Padding(5, 5, 5, 5)
: C.None()
)
);
}
private void SetUpHideTimeout(Control control)
{
Timer CreateTimer()
{
return new Timer(_ => Manager!.Hide(Model), null, HideTimeout, -1);
}
if (HideTimeout > 0)
{
// Don't start the timer until the user is interacting with the window
void StartTimer(object? sender, EventArgs e)
{
Manager!.TimersStarting -= StartTimer;
control.ParentWindow.MouseMove -= StartTimer;
var timer = CreateTimer();
control.MouseEnter += (_, _) => timer.Dispose();
control.MouseLeave += (_, _) => timer = CreateTimer();
}
Manager!.TimersStarting += StartTimer;
}
}
protected virtual void BeforeCreateContent()
{
}
protected virtual void NotificationClicked()
{
}
private void DrawableOnPaint(object? sender, PaintEventArgs e)
{
var drawable = (Drawable) sender!;
var w = drawable.Width;
var h = drawable.Height;
e.Graphics.FillRectangle(BackgroundColor, 0, 0, w, h);
e.Graphics.DrawRectangle(BorderColor, 0, 0, w - 1, h - 1);
}
private void DrawWithRoundedCorners(Drawable drawable, PaintEventArgs e)
{
// TODO: We're not using this as the few pixels on the edges aren't transparent, which is a problem if there's
// an image underneath. Not sure if there's a way to make that work but I don't care enough about rounded
// corners at the moment.
var w = drawable.Width;
var h = drawable.Height;
var r = BORDER_RADIUS;
var d = r * 2;
var q = r / 2;
e.Graphics.FillRectangle(Manager!.ColorScheme.BackgroundColor, 0, 0, w, h);
// Corners
e.Graphics.FillEllipse(BackgroundColor, -1, -1, d, d);
e.Graphics.FillEllipse(BackgroundColor, w - d, -1, d, d);
e.Graphics.FillEllipse(BackgroundColor, -1, h - d, d, d);
e.Graphics.FillEllipse(BackgroundColor, w - d, h - d, d, d);
// Corner borders
e.Graphics.DrawEllipse(BorderColor, -1, -1, d, d);
e.Graphics.DrawEllipse(BorderColor, w - d, -1, d, d);
e.Graphics.DrawEllipse(BorderColor, -1, h - d, d, d);
e.Graphics.DrawEllipse(BorderColor, w - d, h - d, d, d);
// Middle
e.Graphics.FillRectangle(BackgroundColor, r, r, w - d, h - d);
// Sides
e.Graphics.FillRectangle(BackgroundColor, 0, r, r, h - d);
e.Graphics.FillRectangle(BackgroundColor, r, 0, w - d, r);
e.Graphics.FillRectangle(BackgroundColor, w - r, r, r, h - d);
e.Graphics.FillRectangle(BackgroundColor, r, h - r, w - d, r);
// Side borders
e.Graphics.DrawLine(BorderColor, 0, q, 0, h - q);
e.Graphics.DrawLine(BorderColor, q, 0, w - q, 0);
e.Graphics.DrawLine(BorderColor, w - 1, q, w - 1, h - q);
e.Graphics.DrawLine(BorderColor, q, h - 1, w - q, h - 1);
}
public virtual void Dispose()
{
}
}