1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-04-23 03:15:48 +02:00

Create an API for picking which screen notifications are displayed on (WIP)

This commit is contained in:
chylex 2021-08-08 14:03:33 +02:00
parent 095c23b472
commit c2b5cf23cf
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
13 changed files with 314 additions and 16 deletions

View File

@ -0,0 +1,28 @@
namespace TweetLib.Api.Data.Notification {
/// <summary>
/// Allows extensions to decide which screen to use for notifications.
///
/// Every registered provider becomes available in the Options dialog and has to be explicitly selected by the user. Only one provider
/// can be active at any given time.
/// </summary>
public interface IDesktopNotificationScreenProvider {
/// <summary>
/// A unique identifier of this provider. Only needs to be unique within the scope of this plugin.
/// </summary>
Resource Id { get; }
/// <summary>
/// Text displayed in the user interface.
/// </summary>
string DisplayName { get; }
/// <summary>
/// Returns a screen that will be used to display the next desktop notification.
///
/// If the return value is <c>null</c> or a screen that is not present in <see cref="IScreenLayout.AllScreens" />, desktop
/// notifications will be temporarily paused and this method will be called again after an unspecified amount of time (but
/// not sooner than 1 second since the last call).
/// </summary>
IScreen? PickScreen(IScreenLayout layout);
}
}

View File

@ -0,0 +1,7 @@
namespace TweetLib.Api.Data.Notification {
public interface IScreen {
ScreenBounds Bounds { get; }
string Name { get; }
bool IsPrimary { get; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace TweetLib.Api.Data.Notification {
public interface IScreenLayout {
IScreen PrimaryScreen { get; }
IScreen TweetDuckScreen { get; }
List<IScreen> AllScreens { get; }
}
}

View File

@ -0,0 +1,18 @@
namespace TweetLib.Api.Data.Notification {
public readonly struct ScreenBounds {
public int X1 { get; }
public int Y1 { get; }
public int Width { get; }
public int Height { get; }
public int X2 => X1 + Width;
public int Y2 => Y1 + Height;
public ScreenBounds(int x1, int y1, int width, int height) {
X1 = x1;
Y1 = y1;
Width = width;
Height = height;
}
}
}

View File

@ -0,0 +1,7 @@
using TweetLib.Api.Data.Notification;
namespace TweetLib.Api.Service {
public interface INotificationService : ITweetDuckService {
void RegisterDesktopNotificationScreenProvider(IDesktopNotificationScreenProvider provider);
}
}

View File

@ -10,7 +10,7 @@
namespace TweetLib.Core.Systems.Configuration {
public abstract class ConfigManager {
protected static TypeConverterRegistry ConverterRegistry { get; } = new ();
public static TypeConverterRegistry ConverterRegistry { get; } = new ();
static ConfigManager() {
ConverterRegistry.Register(typeof(WindowState), new BasicTypeConverter<WindowState> {

View File

@ -1,11 +1,16 @@
using System;
using TweetDuck.Application.Service;
using TweetLib.Api;
using TweetLib.Api.Data;
using TweetLib.Api.Service;
using TweetLib.Core;
namespace TweetDuck.Application {
static class ApiServices {
public static NotificationService Notifications { get; } = new NotificationService();
public static void Register() {
App.Api.RegisterService<INotificationService>(Notifications);
}
internal static NamespacedResource Namespace(Resource path) {

View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using TweetLib.Api.Data;
using TweetLib.Api.Data.Notification;
using TweetLib.Api.Service;
namespace TweetDuck.Application.Service {
sealed class NotificationService : INotificationService {
private readonly List<NamespacedProvider> desktopNotificationScreenProviders = new ();
public void RegisterDesktopNotificationScreenProvider(IDesktopNotificationScreenProvider provider) {
desktopNotificationScreenProviders.Add(new NamespacedProvider(ApiServices.Namespace(provider.Id), provider));
}
public List<NamespacedProvider> GetDesktopNotificationScreenProviders() {
return desktopNotificationScreenProviders;
}
public sealed class NamespacedProvider : IDesktopNotificationScreenProvider {
public NamespacedResource NamespacedId { get; }
private readonly IDesktopNotificationScreenProvider provider;
public NamespacedProvider(NamespacedResource id, IDesktopNotificationScreenProvider provider) {
this.NamespacedId = id;
this.provider = provider;
}
public Resource Id => provider.Id;
public string DisplayName => provider.DisplayName;
public IScreen? PickScreen(IScreenLayout layout) => provider.PickScreen(layout);
}
}
}

View File

@ -22,15 +22,7 @@ abstract partial class FormNotificationBase : Form {
protected virtual Point PrimaryLocation {
get {
Screen screen;
if (Config.NotificationDisplay > 0 && Config.NotificationDisplay <= Screen.AllScreens.Length) {
screen = Screen.AllScreens[Config.NotificationDisplay - 1];
}
else {
screen = Screen.FromControl(owner);
}
Screen screen = Config.NotificationDisplay.PickScreen(owner);
int edgeDist = Config.NotificationEdgeDistance;
switch (Config.NotificationPosition) {

View File

@ -0,0 +1,173 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using TweetDuck.Application;
using TweetDuck.Application.Service;
using TweetLib.Api.Data;
using TweetLib.Api.Data.Notification;
using TweetLib.Utils.Serialization.Converters;
using TweetLib.Utils.Static;
namespace TweetDuck.Browser.Notification {
abstract class NotificationScreen {
public static List<NotificationScreen> All {
get {
var list = new List<NotificationScreen> {
SameAsTweetDuck.Instance
};
for (int index = 1; index <= Screen.AllScreens.Length; index++) {
list.Add(new Static(index));
}
foreach (var provider in ApiServices.Notifications.GetDesktopNotificationScreenProviders()) {
list.Add(new Provided(provider.NamespacedId));
}
return list;
}
}
public abstract string DisplayName { get; }
private NotificationScreen() {}
public abstract Screen PickScreen(FormBrowser mainWindow);
protected abstract string Serialize();
public sealed class SameAsTweetDuck : NotificationScreen {
public static SameAsTweetDuck Instance { get; } = new SameAsTweetDuck();
public override string DisplayName => "(Same as TweetDuck)";
private SameAsTweetDuck() {}
public override Screen PickScreen(FormBrowser mainWindow) {
return Screen.FromControl(mainWindow);
}
protected override string Serialize() {
return "0";
}
public override bool Equals(object? obj) {
return obj is SameAsTweetDuck;
}
public override int GetHashCode() {
return 1828695039;
}
}
private sealed class Static : NotificationScreen {
public override string DisplayName {
get {
Screen? screen = Screen;
if (screen == null) {
return $"Unknown ({screenIndex})";
}
return screen.DeviceName.TrimStart('\\', '.') + $" ({screen.Bounds.Width}x{screen.Bounds.Height})";
}
}
private Screen? Screen => screenIndex >= 1 && screenIndex <= Screen.AllScreens.Length ? Screen.AllScreens[screenIndex - 1] : null;
private readonly int screenIndex;
public Static(int screenIndex) {
this.screenIndex = screenIndex;
}
public override Screen PickScreen(FormBrowser mainWindow) {
return Screen ?? SameAsTweetDuck.Instance.PickScreen(mainWindow);
}
protected override string Serialize() {
return screenIndex.ToString();
}
public override bool Equals(object? obj) {
return obj is Static other && screenIndex == other.screenIndex;
}
public override int GetHashCode() {
return 31 * screenIndex;
}
}
private sealed class Provided : NotificationScreen {
public override string DisplayName => Provider?.DisplayName ?? $"Unknown ({resource})";
private readonly NamespacedResource resource;
private NotificationService.NamespacedProvider? provider;
private NotificationService.NamespacedProvider? Provider => provider ??= ApiServices.Notifications.GetDesktopNotificationScreenProviders().Find(p => p.NamespacedId == resource);
public Provided(NamespacedResource resource) {
this.resource = resource;
}
public override Screen PickScreen(FormBrowser mainWindow) {
IScreen? pick = Provider?.PickScreen(new WindowsFormsScreenLayout(mainWindow));
return pick is WindowsFormsScreen screen ? screen.Screen : SameAsTweetDuck.Instance.PickScreen(mainWindow); // TODO
}
protected override string Serialize() {
return resource.Namespace + ":" + resource.Path;
}
public override bool Equals(object? obj) {
return obj is Provided other && resource == other.resource;
}
public override int GetHashCode() {
return resource.GetHashCode();
}
private sealed class WindowsFormsScreenLayout : IScreenLayout {
public IScreen PrimaryScreen => new WindowsFormsScreen(Screen.PrimaryScreen);
public IScreen TweetDuckScreen => new WindowsFormsScreen(Screen.FromControl(mainWindow));
public List<IScreen> AllScreens => Screen.AllScreens.Select(static screen => new WindowsFormsScreen(screen)).ToList<IScreen>();
private readonly FormBrowser mainWindow;
public WindowsFormsScreenLayout(FormBrowser mainWindow) {
this.mainWindow = mainWindow;
}
}
private sealed class WindowsFormsScreen : IScreen {
public Screen Screen { get; }
public ScreenBounds Bounds { get; }
public string Name => Screen.DeviceName;
public bool IsPrimary => Screen.Primary;
public WindowsFormsScreen(Screen screen) {
this.Screen = screen;
this.Bounds = new ScreenBounds(screen.Bounds.X, screen.Bounds.Y, screen.Bounds.Width, screen.Bounds.Height);
}
}
}
public static readonly BasicTypeConverter<NotificationScreen> Converter = new() {
ConvertToString = static value => value.Serialize(),
ConvertToObject = static value => {
if (value == "0") {
return SameAsTweetDuck.Instance;
}
else if (int.TryParse(value, out int index)) {
return new Static(index);
}
var resource = StringUtils.SplitInTwo(value, ':');
if (resource != null) {
return new Provided(new NamespacedResource(new Resource(resource.Value.before), new Resource(resource.Value.after)));
}
return SameAsTweetDuck.Instance;
}
};
}
}

View File

@ -2,6 +2,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using TweetDuck.Browser;
using TweetDuck.Browser.Notification;
using TweetDuck.Controls;
using TweetLib.Core;
using TweetLib.Core.Application;
@ -66,7 +67,7 @@ sealed class UserConfig : BaseConfig<UserConfig>, IAppUserConfiguration {
public DesktopNotification.Position NotificationPosition { get; set; } = DesktopNotification.Position.TopRight;
public Point CustomNotificationPosition { get; set; } = ControlExtensions.InvisibleLocation;
public int NotificationDisplay { get; set; } = 0;
public NotificationScreen NotificationDisplay { get; set; } = NotificationScreen.SameAsTweetDuck.Instance;
public int NotificationEdgeDistance { get; set; } = 8;
public int NotificationWindowOpacity { get; set; } = 100;

View File

@ -86,13 +86,21 @@ public TabSettingsNotifications(FormNotificationExample notification) {
}
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
comboBoxDisplay.Items.Add("(Same as TweetDuck)");
foreach (Screen screen in Screen.AllScreens) {
comboBoxDisplay.Items.Add($"{screen.DeviceName.TrimStart('\\', '.')} ({screen.Bounds.Width}x{screen.Bounds.Height})");
bool foundScreen = false;
foreach (var screen in NotificationScreen.All) {
comboBoxDisplay.Items.Add(new NotificationScreenItem(screen));
if (screen.Equals(Config.NotificationDisplay)) {
comboBoxDisplay.SelectedIndex = comboBoxDisplay.Items.Count - 1;
foundScreen = true;
}
}
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count - 1, Config.NotificationDisplay);
if (!foundScreen) {
comboBoxDisplay.Items.Add(new NotificationScreenItem(Config.NotificationDisplay));
comboBoxDisplay.SelectedIndex = comboBoxDisplay.Items.Count - 1;
}
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value + " px";
@ -279,7 +287,7 @@ private void radioLocCustom_Click(object? sender, EventArgs e) {
}
private void comboBoxDisplay_SelectedValueChanged(object? sender, EventArgs e) {
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
Config.NotificationDisplay = ((NotificationScreenItem) comboBoxDisplay.SelectedItem).Screen;
notification.ShowExampleNotification(false);
}
@ -289,6 +297,21 @@ private void trackBarEdgeDistance_ValueChanged(object? sender, EventArgs e) {
notification.ShowExampleNotification(false);
}
private class NotificationScreenItem {
public NotificationScreen Screen { get; }
private readonly string displayName;
public NotificationScreenItem(NotificationScreen screen) {
this.Screen = screen;
this.displayName = screen.DisplayName;
}
public override string ToString() {
return displayName;
}
}
#endregion
#region Size

View File

@ -6,6 +6,7 @@
using TweetDuck.Application;
using TweetDuck.Browser;
using TweetDuck.Browser.Base;
using TweetDuck.Browser.Notification;
using TweetDuck.Configuration;
using TweetDuck.Dialogs;
using TweetDuck.Management;
@ -91,6 +92,7 @@ private sealed class Setup : IAppSetup {
public string? ResourceRewriteRules => Arguments.GetValue(Arguments.ArgFreeze);
public ConfigManager CreateConfigManager(string storagePath) {
ConfigManager.ConverterRegistry.Register(typeof(NotificationScreen), NotificationScreen.Converter);
return new ConfigManager<UserConfig, SystemConfig>(storagePath, Config);
}