mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 07:15:31 +03:00
Separate notification models and views
This fixes language switching while notifications are visible
This commit is contained in:
parent
e871c1f99d
commit
fbbdeb3a39
@ -1,17 +1,9 @@
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class DonateNotification : LinkNotification
|
||||
public class DonateNotification : NotificationModel
|
||||
{
|
||||
private const string DONATE_URL = "https://www.naps2.com/donate";
|
||||
|
||||
public DonateNotification() : base(MiscResources.DonatePrompt, MiscResources.Donate, DONATE_URL, null)
|
||||
public override NotificationView CreateView()
|
||||
{
|
||||
HideTimeout = HIDE_LONG;
|
||||
}
|
||||
|
||||
protected override void LinkClick()
|
||||
{
|
||||
base.LinkClick();
|
||||
Manager!.Hide(this);
|
||||
return new DonateNotificationView(this);
|
||||
}
|
||||
}
|
18
NAPS2.Lib/EtoForms/Notifications/DonateNotificationView.cs
Normal file
18
NAPS2.Lib/EtoForms/Notifications/DonateNotificationView.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class DonateNotificationView : LinkNotificationView
|
||||
{
|
||||
private const string DONATE_URL = "https://www.naps2.com/donate";
|
||||
|
||||
public DonateNotificationView(DonateNotification model)
|
||||
: base(model, MiscResources.DonatePrompt, MiscResources.Donate, DONATE_URL, null)
|
||||
{
|
||||
HideTimeout = HIDE_LONG;
|
||||
}
|
||||
|
||||
protected override void LinkClick()
|
||||
{
|
||||
base.LinkClick();
|
||||
Manager!.Hide(Model);
|
||||
}
|
||||
}
|
@ -4,16 +4,18 @@ using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class LinkNotification : Notification
|
||||
public class LinkNotificationView : NotificationView
|
||||
{
|
||||
private readonly string? _linkTarget;
|
||||
private readonly string? _folderTarget;
|
||||
|
||||
private readonly Label _label = new() { BackgroundColor = BackgroundColor };
|
||||
private readonly LinkButton _link = new() { BackgroundColor = BackgroundColor };
|
||||
private readonly LinkButton _link = new() { BackgroundColor = BackgroundColor };
|
||||
private readonly ContextMenu _contextMenu = new();
|
||||
|
||||
protected LinkNotification(string title, string linkLabel, string? linkTarget, string? folderTarget)
|
||||
protected LinkNotificationView(
|
||||
NotificationModel model, string title, string linkLabel, string? linkTarget, string? folderTarget)
|
||||
: base(model)
|
||||
{
|
||||
_label.Text = title;
|
||||
_label.Font = new Font(_label.Font.Family, _label.Font.Size, FontStyle.Bold);
|
49
NAPS2.Lib/EtoForms/Notifications/NotificationArea.cs
Normal file
49
NAPS2.Lib/EtoForms/Notifications/NotificationArea.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class NotificationArea : IDisposable
|
||||
{
|
||||
private readonly NotificationManager _manager;
|
||||
private readonly LayoutController _layoutController;
|
||||
private readonly Dictionary<NotificationModel, (NotificationView, LayoutElement)> _items = new();
|
||||
|
||||
public NotificationArea(NotificationManager manager, LayoutController layoutController)
|
||||
{
|
||||
_manager = manager;
|
||||
_layoutController = layoutController;
|
||||
_manager.Updated += NotificationsUpdated;
|
||||
UpdateViews();
|
||||
}
|
||||
|
||||
public LayoutColumn Content { get; } = L.Column(C.Filler()).Spacing(20).Padding(15);
|
||||
|
||||
private void NotificationsUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
UpdateViews();
|
||||
_layoutController.Invalidate();
|
||||
}
|
||||
|
||||
private void UpdateViews()
|
||||
{
|
||||
foreach (var added in _manager.Notifications.Except(_items.Keys).ToList())
|
||||
{
|
||||
var view = added.CreateView();
|
||||
view.Manager = _manager;
|
||||
var content = view.CreateContent();
|
||||
Content.Children.Add(content);
|
||||
_items.Add(added, (view, content));
|
||||
}
|
||||
foreach (var removed in _items.Keys.Except(_manager.Notifications).ToList())
|
||||
{
|
||||
var (view, content) = _items[removed];
|
||||
Content.Children.Remove(content);
|
||||
view.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_manager.Updated -= NotificationsUpdated;
|
||||
}
|
||||
}
|
@ -1,32 +1,25 @@
|
||||
using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class NotificationManager
|
||||
{
|
||||
private readonly Dictionary<Notification, LayoutElement> _items = new();
|
||||
|
||||
public LayoutColumn Column { get; } = L.Column(C.Filler()).Spacing(20).Padding(15);
|
||||
public List<NotificationModel> Notifications { get; } = new();
|
||||
|
||||
public event EventHandler? Updated;
|
||||
|
||||
public event EventHandler? TimersStarting;
|
||||
|
||||
public void Show(Notification notification)
|
||||
public void Show(NotificationModel notification)
|
||||
{
|
||||
notification.Manager = this;
|
||||
var item = notification.CreateView();
|
||||
_items[notification] = item;
|
||||
Column.Children.Add(item);
|
||||
Notifications.Add(notification);
|
||||
Updated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Hide(Notification notification)
|
||||
public void Hide(NotificationModel notification)
|
||||
{
|
||||
if (!_items.ContainsKey(notification)) return;
|
||||
Column.Children.Remove(_items[notification]);
|
||||
Updated?.Invoke(this, EventArgs.Empty);
|
||||
notification.Dispose();
|
||||
if (Notifications.Remove(notification))
|
||||
{
|
||||
Updated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartTimers()
|
||||
|
6
NAPS2.Lib/EtoForms/Notifications/NotificationModel.cs
Normal file
6
NAPS2.Lib/EtoForms/Notifications/NotificationModel.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public abstract class NotificationModel
|
||||
{
|
||||
public abstract NotificationView CreateView();
|
||||
}
|
@ -5,7 +5,7 @@ using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public abstract class Notification : IDisposable
|
||||
public abstract class NotificationView : IDisposable
|
||||
{
|
||||
// TODO: Get from color scheme
|
||||
protected static readonly Color BackgroundColor = new(0.95f, 0.95f, 0.95f);
|
||||
@ -16,6 +16,13 @@ public abstract class Notification : IDisposable
|
||||
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;
|
||||
@ -26,12 +33,12 @@ public abstract class Notification : IDisposable
|
||||
|
||||
protected abstract LayoutElement SecondaryContent { get; }
|
||||
|
||||
public LayoutElement CreateView()
|
||||
public LayoutElement CreateContent()
|
||||
{
|
||||
var drawable = new Drawable();
|
||||
drawable.Paint += DrawableOnPaint;
|
||||
var closeButton = new CloseButton();
|
||||
closeButton.Click += (_, _) => Manager!.Hide(this);
|
||||
closeButton.Click += (_, _) => Manager!.Hide(Model);
|
||||
drawable.MouseUp += (_, _) => NotificationClicked();
|
||||
drawable.Load += (_, _) => SetUpHideTimeout(drawable);
|
||||
return L.Overlay(
|
||||
@ -59,7 +66,7 @@ public abstract class Notification : IDisposable
|
||||
{
|
||||
return new Timer(_ =>
|
||||
{
|
||||
Manager!.Hide(this);
|
||||
Manager!.Hide(Model);
|
||||
}, null, HideTimeout, -1);
|
||||
}
|
||||
|
@ -1,79 +1,18 @@
|
||||
using Eto.Forms;
|
||||
using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class ProgressNotification : Notification
|
||||
public class ProgressNotification : NotificationModel
|
||||
{
|
||||
private readonly OperationProgress _operationProgress;
|
||||
private readonly IOperation _op;
|
||||
|
||||
private readonly Label _textLabel = new();
|
||||
private readonly Label _numberLabel = new();
|
||||
private readonly ProgressBar _progressBar = new();
|
||||
private readonly LayoutVisibility _numberVis = new(true);
|
||||
|
||||
public ProgressNotification(OperationProgress operationProgress, IOperation op)
|
||||
public ProgressNotification(OperationProgress progress, IOperation op)
|
||||
{
|
||||
_operationProgress = operationProgress;
|
||||
_op = op;
|
||||
|
||||
ShowClose = false;
|
||||
op.StatusChanged += OnStatusChanged;
|
||||
op.Finished += OnFinished;
|
||||
if (op.IsFinished)
|
||||
{
|
||||
Invoker.Current.Invoke(() => Manager?.Hide(this));
|
||||
}
|
||||
_textLabel.MouseUp += (_, _) => NotificationClicked();
|
||||
_numberLabel.MouseUp += (_, _) => NotificationClicked();
|
||||
_progressBar.MouseUp += (_, _) => NotificationClicked();
|
||||
UpdateStatus();
|
||||
Progress = progress;
|
||||
Op = op;
|
||||
}
|
||||
|
||||
private void OnStatusChanged(object? sender, EventArgs eventArgs)
|
||||
public OperationProgress Progress { get; }
|
||||
public IOperation Op { get; }
|
||||
|
||||
public override NotificationView CreateView()
|
||||
{
|
||||
Invoker.Current.Invoke(UpdateStatus);
|
||||
return new ProgressNotificationView(this);
|
||||
}
|
||||
|
||||
private void OnFinished(object? sender, EventArgs e)
|
||||
{
|
||||
Invoker.Current.Invoke(() => Manager?.Hide(this));
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
var text1 = (_textLabel.Text, _numberLabel.Text);
|
||||
EtoOperationProgress.RenderStatus(_op, _textLabel, _numberLabel, _progressBar);
|
||||
var text2 = (_textLabel.Text, _numberLabel.Text);
|
||||
if (text1 != text2)
|
||||
{
|
||||
// The text width may have changed, so the notification size could change
|
||||
Manager?.InvokeUpdated();
|
||||
}
|
||||
// Don't display the number if the progress bar is precise
|
||||
// Otherwise, the widget will be too cluttered
|
||||
// The number is only shown for OcrOperation at the moment
|
||||
_numberVis.IsVisible = _op.Status?.IndeterminateProgress == true;
|
||||
}
|
||||
|
||||
protected override void NotificationClicked()
|
||||
{
|
||||
Manager!.Hide(this);
|
||||
_operationProgress.ShowModalProgress(_op);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
_op.StatusChanged -= OnStatusChanged;
|
||||
_op.Finished -= OnFinished;
|
||||
}
|
||||
|
||||
protected override LayoutElement PrimaryContent => L.Row(
|
||||
_textLabel,
|
||||
C.Filler().Visible(_numberVis),
|
||||
_numberLabel.Visible(_numberVis));
|
||||
|
||||
protected override LayoutElement SecondaryContent => _progressBar.MaxHeight(10);
|
||||
}
|
80
NAPS2.Lib/EtoForms/Notifications/ProgressNotificationView.cs
Normal file
80
NAPS2.Lib/EtoForms/Notifications/ProgressNotificationView.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using Eto.Forms;
|
||||
using NAPS2.EtoForms.Layout;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class ProgressNotificationView : NotificationView
|
||||
{
|
||||
private readonly OperationProgress _operationProgress;
|
||||
private readonly IOperation _op;
|
||||
|
||||
private readonly Label _textLabel = new();
|
||||
private readonly Label _numberLabel = new();
|
||||
private readonly ProgressBar _progressBar = new();
|
||||
private readonly LayoutVisibility _numberVis = new(true);
|
||||
|
||||
public ProgressNotificationView(ProgressNotification model)
|
||||
: base(model)
|
||||
{
|
||||
_operationProgress = model.Progress;
|
||||
_op = model.Op;
|
||||
|
||||
ShowClose = false;
|
||||
_op.StatusChanged += OnStatusChanged;
|
||||
_op.Finished += OnFinished;
|
||||
if (_op.IsFinished)
|
||||
{
|
||||
Invoker.Current.Invoke(() => Manager?.Hide(Model));
|
||||
}
|
||||
_textLabel.MouseUp += (_, _) => NotificationClicked();
|
||||
_numberLabel.MouseUp += (_, _) => NotificationClicked();
|
||||
_progressBar.MouseUp += (_, _) => NotificationClicked();
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
private void OnStatusChanged(object? sender, EventArgs eventArgs)
|
||||
{
|
||||
Invoker.Current.Invoke(UpdateStatus);
|
||||
}
|
||||
|
||||
private void OnFinished(object? sender, EventArgs e)
|
||||
{
|
||||
Invoker.Current.Invoke(() => Manager?.Hide(Model));
|
||||
}
|
||||
|
||||
private void UpdateStatus()
|
||||
{
|
||||
var text1 = (_textLabel.Text, _numberLabel.Text);
|
||||
EtoOperationProgress.RenderStatus(_op, _textLabel, _numberLabel, _progressBar);
|
||||
var text2 = (_textLabel.Text, _numberLabel.Text);
|
||||
if (text1 != text2)
|
||||
{
|
||||
// The text width may have changed, so the notification size could change
|
||||
Manager?.InvokeUpdated();
|
||||
}
|
||||
// Don't display the number if the progress bar is precise
|
||||
// Otherwise, the widget will be too cluttered
|
||||
// The number is only shown for OcrOperation at the moment
|
||||
_numberVis.IsVisible = _op.Status?.IndeterminateProgress == true;
|
||||
}
|
||||
|
||||
protected override void NotificationClicked()
|
||||
{
|
||||
Manager!.Hide(Model);
|
||||
_operationProgress.ShowModalProgress(_op);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
_op.StatusChanged -= OnStatusChanged;
|
||||
_op.Finished -= OnFinished;
|
||||
}
|
||||
|
||||
protected override LayoutElement PrimaryContent => L.Row(
|
||||
_textLabel,
|
||||
C.Filler().Visible(_numberVis),
|
||||
_numberLabel.Visible(_numberVis));
|
||||
|
||||
protected override LayoutElement SecondaryContent => _progressBar.MaxHeight(10);
|
||||
}
|
@ -1,10 +1,18 @@
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class SaveNotification : LinkNotification
|
||||
public class SaveNotification : NotificationModel
|
||||
{
|
||||
public SaveNotification(string message, string path)
|
||||
: base(message, Path.GetFileName(path), path, Path.GetDirectoryName(path))
|
||||
public SaveNotification(string title, string path)
|
||||
{
|
||||
HideTimeout = HIDE_SHORT;
|
||||
Title = title;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public string Title { get; }
|
||||
public string Path { get; }
|
||||
|
||||
public override NotificationView CreateView()
|
||||
{
|
||||
return new SaveNotificationView(this);
|
||||
}
|
||||
}
|
10
NAPS2.Lib/EtoForms/Notifications/SaveNotificationView.cs
Normal file
10
NAPS2.Lib/EtoForms/Notifications/SaveNotificationView.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class SaveNotificationView : LinkNotificationView
|
||||
{
|
||||
public SaveNotificationView(SaveNotification model)
|
||||
: base(model, model.Title, Path.GetFileName(model.Path), model.Path, Path.GetDirectoryName(model.Path))
|
||||
{
|
||||
HideTimeout = HIDE_SHORT;
|
||||
}
|
||||
}
|
@ -2,25 +2,19 @@ using NAPS2.Update;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class UpdateNotification : LinkNotification
|
||||
public class UpdateNotification : NotificationModel
|
||||
{
|
||||
private readonly IUpdateChecker _updateChecker;
|
||||
private readonly UpdateInfo _update;
|
||||
|
||||
public UpdateNotification(IUpdateChecker updateChecker, UpdateInfo update)
|
||||
: base(
|
||||
MiscResources.UpdateAvailable,
|
||||
string.Format(MiscResources.Install, update.Name),
|
||||
null, null)
|
||||
{
|
||||
_updateChecker = updateChecker;
|
||||
_update = update;
|
||||
HideTimeout = HIDE_LONG;
|
||||
UpdateChecker = updateChecker;
|
||||
Update = update;
|
||||
}
|
||||
|
||||
protected override void LinkClick()
|
||||
public IUpdateChecker UpdateChecker { get; }
|
||||
public UpdateInfo Update { get; }
|
||||
|
||||
public override NotificationView CreateView()
|
||||
{
|
||||
_updateChecker.StartUpdate(_update);
|
||||
Manager!.Hide(this);
|
||||
return new UpdateNotificationView(this);
|
||||
}
|
||||
}
|
27
NAPS2.Lib/EtoForms/Notifications/UpdateNotificationView.cs
Normal file
27
NAPS2.Lib/EtoForms/Notifications/UpdateNotificationView.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using NAPS2.Update;
|
||||
|
||||
namespace NAPS2.EtoForms.Notifications;
|
||||
|
||||
public class UpdateNotificationView : LinkNotificationView
|
||||
{
|
||||
private readonly IUpdateChecker _updateChecker;
|
||||
private readonly UpdateInfo _update;
|
||||
|
||||
public UpdateNotificationView(UpdateNotification model)
|
||||
: base(
|
||||
model,
|
||||
MiscResources.UpdateAvailable,
|
||||
string.Format(MiscResources.Install, model.Update.Name),
|
||||
null, null)
|
||||
{
|
||||
_updateChecker = model.UpdateChecker;
|
||||
_update = model.Update;
|
||||
HideTimeout = HIDE_LONG;
|
||||
}
|
||||
|
||||
protected override void LinkClick()
|
||||
{
|
||||
_updateChecker.StartUpdate(_update);
|
||||
Manager!.Hide(Model);
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
private readonly ListProvider<Command> _languageMenuCommands = new();
|
||||
private readonly ContextMenu _contextMenu = new();
|
||||
|
||||
private readonly NotificationArea _notificationArea;
|
||||
protected IListView<UiImage> _listView;
|
||||
private ImageListSyncer? _imageListSyncer;
|
||||
|
||||
@ -109,7 +110,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
ImageList.SelectionChanged += ImageList_SelectionChanged;
|
||||
ImageList.ImagesUpdated += ImageList_ImagesUpdated;
|
||||
_profileManager.ProfilesUpdated += ProfileManager_ProfilesUpdated;
|
||||
_notificationManager.Updated += (_, _) => LayoutController.Invalidate();
|
||||
_notificationArea = new NotificationArea(_notificationManager, LayoutController);
|
||||
}
|
||||
|
||||
protected override void BuildLayout()
|
||||
@ -127,7 +128,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
L.Row(
|
||||
GetZoomButtons(),
|
||||
C.Filler(),
|
||||
_notificationManager.Column)
|
||||
_notificationArea.Content)
|
||||
).Padding(10)
|
||||
);
|
||||
|
||||
@ -232,6 +233,7 @@ public abstract class DesktopForm : EtoFormBase
|
||||
ImageList.SelectionChanged -= ImageList_SelectionChanged;
|
||||
ImageList.ImagesUpdated -= ImageList_ImagesUpdated;
|
||||
_profileManager.ProfilesUpdated -= ProfileManager_ProfilesUpdated;
|
||||
_notificationArea.Dispose();
|
||||
_imageListSyncer?.Dispose();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user