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:
parent
095c23b472
commit
c2b5cf23cf
lib
TweetLib.Api
Data/Notification
Service
TweetLib.Core/Systems/Configuration
windows/TweetDuck
@ -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);
|
||||
}
|
||||
}
|
7
lib/TweetLib.Api/Data/Notification/IScreen.cs
Normal file
7
lib/TweetLib.Api/Data/Notification/IScreen.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace TweetLib.Api.Data.Notification {
|
||||
public interface IScreen {
|
||||
ScreenBounds Bounds { get; }
|
||||
string Name { get; }
|
||||
bool IsPrimary { get; }
|
||||
}
|
||||
}
|
9
lib/TweetLib.Api/Data/Notification/IScreenLayout.cs
Normal file
9
lib/TweetLib.Api/Data/Notification/IScreenLayout.cs
Normal 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; }
|
||||
}
|
||||
}
|
18
lib/TweetLib.Api/Data/Notification/ScreenBounds.cs
Normal file
18
lib/TweetLib.Api/Data/Notification/ScreenBounds.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
7
lib/TweetLib.Api/Service/INotificationService.cs
Normal file
7
lib/TweetLib.Api/Service/INotificationService.cs
Normal file
@ -0,0 +1,7 @@
|
||||
using TweetLib.Api.Data.Notification;
|
||||
|
||||
namespace TweetLib.Api.Service {
|
||||
public interface INotificationService : ITweetDuckService {
|
||||
void RegisterDesktopNotificationScreenProvider(IDesktopNotificationScreenProvider provider);
|
||||
}
|
||||
}
|
@ -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> {
|
||||
|
@ -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) {
|
||||
|
33
windows/TweetDuck/Application/Service/NotificationService.cs
Normal file
33
windows/TweetDuck/Application/Service/NotificationService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
173
windows/TweetDuck/Browser/Notification/NotificationScreen.cs
Normal file
173
windows/TweetDuck/Browser/Notification/NotificationScreen.cs
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user