Browse Source

suggested api changes

pull/2453/head
Dan Walmsley 8 years ago
parent
commit
e45805aa50
  1. 4
      samples/ControlCatalog/MainWindow.xaml.cs
  2. 14
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  3. 5
      samples/ControlCatalog/ViewModels/NotificationViewModel.cs
  4. 22
      src/Avalonia.Controls/Notifications/INotificationManager.cs
  5. 2
      src/Avalonia.Controls/Notifications/Notification.cs
  6. 27
      src/Avalonia.Controls/Notifications/NotificationContent.cs
  7. 56
      src/Avalonia.Controls/Notifications/SimpleNotification.cs
  8. 55
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  9. 5
      src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
  10. 2
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  11. 12
      src/Avalonia.Themes.Default/WindowNotificationManager.xaml

4
samples/ControlCatalog/MainWindow.xaml.cs

@ -12,7 +12,7 @@ namespace ControlCatalog
{ {
public class MainWindow : Window public class MainWindow : Window
{ {
private NotificationArea _notificationArea; private WindowNotificationManager _notificationArea;
public MainWindow() public MainWindow()
{ {
@ -21,7 +21,7 @@ namespace ControlCatalog
//Renderer.DrawFps = true; //Renderer.DrawFps = true;
//Renderer.DrawDirtyRects = Renderer.DrawFps = true; //Renderer.DrawDirtyRects = Renderer.DrawFps = true;
_notificationArea = new NotificationArea _notificationArea = new WindowNotificationManager
{ {
Position = NotificationPosition.TopRight, Position = NotificationPosition.TopRight,
MaxItems = 3 MaxItems = 3

14
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@ -1,15 +1,15 @@
using System.Reactive; using System.Reactive;
using System.Threading.Tasks;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using Avalonia.Diagnostics.ViewModels; using Avalonia.Diagnostics.ViewModels;
using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
namespace ControlCatalog.ViewModels namespace ControlCatalog.ViewModels
{ {
class MainWindowViewModel : ViewModelBase class MainWindowViewModel : ViewModelBase
{ {
public MainWindowViewModel(INotificationManager notificationManager) private IManagedNotificationManager _notificationManager;
public MainWindowViewModel(IManagedNotificationManager notificationManager)
{ {
_notificationManager = notificationManager; _notificationManager = notificationManager;
@ -20,18 +20,16 @@ namespace ControlCatalog.ViewModels
ShowManagedNotificationCommand = ReactiveCommand.Create(() => ShowManagedNotificationCommand = ReactiveCommand.Create(() =>
{ {
NotificationManager.Show(new NotificationContent { Title = "Welcome", Message = "Avalonia now supports Notifications.", Type = NotificationType.Information }); NotificationManager.Show(SimpleNotification.Create("Welcome", "Avalonia now supports Notifications.", NotificationType.Information));
}); });
ShowNativeNotificationCommand = ReactiveCommand.Create(() => ShowNativeNotificationCommand = ReactiveCommand.Create(() =>
{ {
NotificationManager.Show(new NotificationContent { Title = "Error", Message = "Native Notifications are not quite ready. Coming soon.", Type = NotificationType.Error }); NotificationManager.Show(SimpleNotification.Create("Error", "Native Notifications are not quite ready. Coming soon.", NotificationType.Error));
}); });
} }
private INotificationManager _notificationManager; public IManagedNotificationManager NotificationManager
public INotificationManager NotificationManager
{ {
get { return _notificationManager; } get { return _notificationManager; }
set { this.RaiseAndSetIfChanged(ref _notificationManager, value); } set { this.RaiseAndSetIfChanged(ref _notificationManager, value); }

5
samples/ControlCatalog/ViewModels/NotificationViewModel.cs

@ -1,5 +1,4 @@
using System.Reactive; using System.Reactive;
using Avalonia;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using ReactiveUI; using ReactiveUI;
@ -11,12 +10,12 @@ namespace ControlCatalog.ViewModels
{ {
YesCommand = ReactiveCommand.Create(() => YesCommand = ReactiveCommand.Create(() =>
{ {
manager.Show(new NotificationContent { Title = "Avalonia Notifications", Message = "Start adding notifications to your app today." }); manager.Show(SimpleNotification.Create("Avalonia Notifications", "Start adding notifications to your app today."));
}); });
NoCommand = ReactiveCommand.Create(() => NoCommand = ReactiveCommand.Create(() =>
{ {
manager.Show(new NotificationContent { Title = "Avalonia Notifications", Message = "Start adding notifications to your app today. To find out more visit..." }); manager.Show(SimpleNotification.Create("Avalonia Notifications", "Start adding notifications to your app today. To find out more visit..."));
}); });
} }

22
src/Avalonia.Controls/Notifications/INotificationManager.cs

@ -2,10 +2,28 @@
namespace Avalonia.Controls.Notifications namespace Avalonia.Controls.Notifications
{ {
public interface INotification
{
string Title { get; }
string Message { get; }
NotificationType Type { get; }
TimeSpan Expiration { get; }
Action OnClick { get; }
Action OnClose { get; }
}
public interface INotificationManager public interface INotificationManager
{ {
void Show(object content, TimeSpan? expirationTime = null, Action onClick = null, Action onClose = null); void Show(INotification notification);
}
void Show(NotificationContent content, TimeSpan? expirationTime = null, Action onClick = null, Action onClose = null); public interface IManagedNotificationManager : INotificationManager
{
void Show(object content);
} }
} }

2
src/Avalonia.Controls/Notifications/Notification.cs

@ -20,7 +20,7 @@ namespace Avalonia.Controls.Notifications
public Notification() public Notification()
{ {
this.GetObservable(ContentProperty) this.GetObservable(ContentProperty)
.OfType<NotificationContent>() .OfType<SimpleNotification>()
.Subscribe(x => .Subscribe(x =>
{ {
switch (x.Type) switch (x.Type)

27
src/Avalonia.Controls/Notifications/NotificationContent.cs

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Defines content for a <see cref="Notification"/> control.
/// </summary>
/// <remarks>
/// This notification content type is compatible with native notifications.
/// </remarks>
public class NotificationContent
{
public string Title { get; set; }
public string Message { get; set; }
public NotificationType Type { get; set; }
}
public enum NotificationType
{
Information,
Success,
Warning,
Error
}
}

56
src/Avalonia.Controls/Notifications/SimpleNotification.cs

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Controls.Notifications
{
/// <summary>
/// Defines content for a <see cref="Notification"/> control.
/// </summary>
/// <remarks>
/// This notification content type is compatible with native notifications.
/// </remarks>
public class SimpleNotification : INotification
{
public static INotification Create (
string title,
string message,
NotificationType type = NotificationType.Information,
TimeSpan? expiration = null,
Action onClick = null,
Action onClose = null)
{
var result = new SimpleNotification
{
Title = title,
Message = message,
Type = type,
Expiration = expiration.HasValue ? expiration.Value : TimeSpan.FromSeconds(5),
OnClick = onClick,
OnClose = onClose
};
return result;
}
public string Title { get; private set; }
public string Message { get; private set; }
public NotificationType Type { get; private set; }
public TimeSpan Expiration { get; private set; }
public Action OnClick { get; private set; }
public Action OnClose { get; private set; }
}
public enum NotificationType
{
Information,
Success,
Warning,
Error
}
}

55
src/Avalonia.Controls/Notifications/NotificationArea.cs → src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -8,7 +8,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Controls.Notifications namespace Avalonia.Controls.Notifications
{ {
public class NotificationArea : TemplatedControl, INotificationManager public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager
{ {
private IList _items; private IList _items;
@ -23,7 +23,7 @@ namespace Avalonia.Controls.Notifications
} }
public static readonly AvaloniaProperty<NotificationPosition> PositionProperty = public static readonly AvaloniaProperty<NotificationPosition> PositionProperty =
AvaloniaProperty.RegisterAttached<NotificationArea, TopLevel, NotificationPosition>("Position", defaultValue: NotificationPosition.TopLeft, inherits: true); AvaloniaProperty.RegisterAttached<WindowNotificationManager, TopLevel, NotificationPosition>("Position", defaultValue: NotificationPosition.TopLeft, inherits: true);
public NotificationPosition Position public NotificationPosition Position
{ {
@ -42,7 +42,7 @@ namespace Avalonia.Controls.Notifications
} }
public static readonly AvaloniaProperty<int> MaxItemsProperty = public static readonly AvaloniaProperty<int> MaxItemsProperty =
AvaloniaProperty.RegisterAttached<NotificationArea, TopLevel, int>("Position", defaultValue: 5, inherits: true); AvaloniaProperty.RegisterAttached<WindowNotificationManager, TopLevel, int>("Position", defaultValue: 5, inherits: true);
public int MaxItems public int MaxItems
{ {
@ -50,15 +50,15 @@ namespace Avalonia.Controls.Notifications
set { SetValue(MaxItemsProperty, value); } set { SetValue(MaxItemsProperty, value); }
} }
static NotificationArea() static WindowNotificationManager()
{ {
PseudoClass<NotificationArea, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopLeft, ":topleft"); PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopLeft, ":topleft");
PseudoClass<NotificationArea, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopRight, ":topright"); PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.TopRight, ":topright");
PseudoClass<NotificationArea, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomLeft, ":bottomleft"); PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomLeft, ":bottomleft");
PseudoClass<NotificationArea, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomRight, ":bottomright"); PseudoClass<WindowNotificationManager, NotificationPosition>(PositionProperty, x => x == NotificationPosition.BottomRight, ":bottomright");
HorizontalAlignmentProperty.OverrideDefaultValue<NotificationArea>(Layout.HorizontalAlignment.Stretch); HorizontalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.HorizontalAlignment.Stretch);
VerticalAlignmentProperty.OverrideDefaultValue<NotificationArea>(Layout.VerticalAlignment.Stretch); VerticalAlignmentProperty.OverrideDefaultValue<WindowNotificationManager>(Layout.VerticalAlignment.Stretch);
} }
protected override void OnTemplateApplied(TemplateAppliedEventArgs e) protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
@ -69,32 +69,39 @@ namespace Avalonia.Controls.Notifications
_items = itemsControl?.Children; _items = itemsControl?.Children;
} }
public void Show(NotificationContent content, TimeSpan? expirationTime, Action onClick, Action onClose) public void Show(INotification content)
{ {
Show(content as object, expirationTime, onClick, onClose); Show(content as object);
} }
public async void Show(object content, TimeSpan? expirationTime, Action onClick, Action onClose) public async void Show(object content)
{ {
var notification = new Notification var notification = content as INotification;
var notificationControl = new Notification
{ {
Content = content Content = content
}; };
notification.PointerPressed += (sender, args) => if (notification != null)
{
notificationControl.NotificationClosed += (sender, args) => notification.OnClose?.Invoke();
}
notificationControl.NotificationClosed += OnNotificationClosed;
notificationControl.PointerPressed += (sender, args) =>
{ {
if (onClick != null) if (notification != null && notification.OnClick != null)
{ {
onClick.Invoke(); notification.OnClick.Invoke();
(sender as Notification)?.Close(); (sender as Notification)?.Close();
} }
}; };
notification.NotificationClosed += (sender, args) => onClose?.Invoke();
notification.NotificationClosed += OnNotificationClosed;
lock (_items) lock (_items)
{ {
_items.Add(notification); _items.Add(notificationControl);
if (_items.OfType<Notification>().Count(i => !i.IsClosing) > MaxItems) if (_items.OfType<Notification>().Count(i => !i.IsClosing) > MaxItems)
{ {
@ -102,14 +109,14 @@ namespace Avalonia.Controls.Notifications
} }
} }
if (expirationTime == TimeSpan.MaxValue) if (notification != null && notification.Expiration == TimeSpan.MaxValue)
{ {
return; return;
} }
await Task.Delay(expirationTime ?? TimeSpan.FromSeconds(5)); await Task.Delay(notification?.Expiration ?? TimeSpan.FromSeconds(5));
notification.Close(); notificationControl.Close();
} }
private void OnNotificationClosed(object sender, RoutedEventArgs routedEventArgs) private void OnNotificationClosed(object sender, RoutedEventArgs routedEventArgs)
@ -118,7 +125,7 @@ namespace Avalonia.Controls.Notifications
_items.Remove(notification); _items.Remove(notification);
} }
public void Install(TopLevel host) public void Install(Window host)
{ {
var adornerLayer = host.GetVisualDescendants() var adornerLayer = host.GetVisualDescendants()
.OfType<AdornerDecorator>() .OfType<AdornerDecorator>()

5
src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj

@ -26,4 +26,9 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="WindowNotificationManager.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
</ItemGroup>
</Project> </Project>

2
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -46,5 +46,5 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.ButtonSpinner.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.ButtonSpinner.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.NumericUpDown.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.NumericUpDown.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.AutoCompleteBox.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.AutoCompleteBox.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.NotificationArea.xaml?assembly=Avalonia.Themes.Default"/> <StyleInclude Source="resm:Avalonia.Themes.Default.WindowNotificationManager.xaml?assembly=Avalonia.Themes.Default"/>
</Styles> </Styles>

12
src/Avalonia.Themes.Default/NotificationArea.xaml → src/Avalonia.Themes.Default/WindowNotificationManager.xaml

@ -1,12 +1,12 @@
<Styles xmlns="https://github.com/avaloniaui" <Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="NotificationArea"> <Style Selector="WindowNotificationManager">
<Setter Property="Margin" Value="0 0 8 8"/> <Setter Property="Margin" Value="0 0 8 8"/>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<StackPanel Name="PART_Items"> <StackPanel Name="PART_Items">
<StackPanel.DataTemplates> <StackPanel.DataTemplates>
<DataTemplate DataType="NotificationContent"> <DataTemplate DataType="INotification">
<StackPanel Spacing="8" Margin="12"> <StackPanel Spacing="8" Margin="12">
<TextBlock DockPanel.Dock="Top" Text="{Binding Title}" FontWeight="Medium" /> <TextBlock DockPanel.Dock="Top" Text="{Binding Title}" FontWeight="Medium" />
<TextBlock MaxHeight="80" Text="{Binding Message}" TextWrapping="Wrap" Margin="0,0,12,0"/> <TextBlock MaxHeight="80" Text="{Binding Message}" TextWrapping="Wrap" Margin="0,0,12,0"/>
@ -119,23 +119,23 @@
<Setter Property="Background" Value="OrangeRed"/> <Setter Property="Background" Value="OrangeRed"/>
</Style> </Style>
<Style Selector="NotificationArea:topleft /template/ StackPanel#PART_Items"> <Style Selector="WindowNotificationManager:topleft /template/ StackPanel#PART_Items">
<Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="HorizontalAlignment" Value="Left"/>
</Style> </Style>
<Style Selector="NotificationArea:topright /template/ StackPanel#PART_Items"> <Style Selector="WindowNotificationManager:topright /template/ StackPanel#PART_Items">
<Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="HorizontalAlignment" Value="Right"/>
</Style> </Style>
<Style Selector="NotificationArea:bottomleft /template/ StackPanel#PART_Items"> <Style Selector="WindowNotificationManager:bottomleft /template/ StackPanel#PART_Items">
<Setter Property="ReverseOrder" Value="True"/> <Setter Property="ReverseOrder" Value="True"/>
<Setter Property="VerticalAlignment" Value="Bottom"/> <Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="HorizontalAlignment" Value="Left"/>
</Style> </Style>
<Style Selector="NotificationArea:bottomright /template/ StackPanel#PART_Items"> <Style Selector="WindowNotificationManager:bottomright /template/ StackPanel#PART_Items">
<Setter Property="ReverseOrder" Value="True"/> <Setter Property="ReverseOrder" Value="True"/>
<Setter Property="VerticalAlignment" Value="Bottom"/> <Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="HorizontalAlignment" Value="Right"/>
Loading…
Cancel
Save