mirror of
				https://github.com/chylex/TweetDuck.git
				synced 2025-11-04 01:40:16 +01:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			c5a42e74d9
			...
			extensions
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						c2b5cf23cf
	
				 | 
					
					
						|||
| 
						
						
							
						
						095c23b472
	
				 | 
					
					
						
@@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetImpl.CefSharp", "windo
 | 
				
			|||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.WinForms.Legacy", "windows\TweetLib.WinForms.Legacy\TweetLib.WinForms.Legacy.csproj", "{B54E732A-4090-4DAA-9ABD-311368C17B68}"
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.WinForms.Legacy", "windows\TweetLib.WinForms.Legacy\TweetLib.WinForms.Legacy.csproj", "{B54E732A-4090-4DAA-9ABD-311368C17B68}"
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Api", "lib\TweetLib.Api\TweetLib.Api.csproj", "{85596C10-F76E-4619-9CC6-6C1593880F83}"
 | 
				
			||||||
 | 
					EndProject
 | 
				
			||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
 | 
				
			||||||
EndProject
 | 
					EndProject
 | 
				
			||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Core", "lib\TweetLib.Core\TweetLib.Core.csproj", "{93BA3CB4-A812-4949-B07D-8D393FB38937}"
 | 
					Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Core", "lib\TweetLib.Core\TweetLib.Core.csproj", "{93BA3CB4-A812-4949-B07D-8D393FB38937}"
 | 
				
			||||||
@@ -54,6 +56,10 @@ Global
 | 
				
			|||||||
		{B54E732A-4090-4DAA-9ABD-311368C17B68}.Debug|x86.Build.0 = Debug|x86
 | 
							{B54E732A-4090-4DAA-9ABD-311368C17B68}.Debug|x86.Build.0 = Debug|x86
 | 
				
			||||||
		{B54E732A-4090-4DAA-9ABD-311368C17B68}.Release|x86.ActiveCfg = Release|x86
 | 
							{B54E732A-4090-4DAA-9ABD-311368C17B68}.Release|x86.ActiveCfg = Release|x86
 | 
				
			||||||
		{B54E732A-4090-4DAA-9ABD-311368C17B68}.Release|x86.Build.0 = Release|x86
 | 
							{B54E732A-4090-4DAA-9ABD-311368C17B68}.Release|x86.Build.0 = Release|x86
 | 
				
			||||||
 | 
							{85596C10-F76E-4619-9CC6-6C1593880F83}.Debug|x86.ActiveCfg = Debug|x86
 | 
				
			||||||
 | 
							{85596C10-F76E-4619-9CC6-6C1593880F83}.Debug|x86.Build.0 = Debug|x86
 | 
				
			||||||
 | 
							{85596C10-F76E-4619-9CC6-6C1593880F83}.Release|x86.ActiveCfg = Release|x86
 | 
				
			||||||
 | 
							{85596C10-F76E-4619-9CC6-6C1593880F83}.Release|x86.Build.0 = Release|x86
 | 
				
			||||||
		{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.ActiveCfg = Debug|x86
 | 
							{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.ActiveCfg = Debug|x86
 | 
				
			||||||
		{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
 | 
							{72473763-4B9D-4FB6-A923-9364B2680F06}.Debug|x86.Build.0 = Debug|x86
 | 
				
			||||||
		{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
 | 
							{72473763-4B9D-4FB6-A923-9364B2680F06}.Release|x86.ActiveCfg = Release|x86
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										37
									
								
								lib/TweetLib.Api/Data/NamespacedResource.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								lib/TweetLib.Api/Data/NamespacedResource.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					namespace TweetLib.Api.Data {
 | 
				
			||||||
 | 
						public readonly struct NamespacedResource {
 | 
				
			||||||
 | 
							public Resource Namespace { get; }
 | 
				
			||||||
 | 
							public Resource Path { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public NamespacedResource(Resource ns, Resource path) {
 | 
				
			||||||
 | 
								Namespace = ns;
 | 
				
			||||||
 | 
								Path = path;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private bool Equals(NamespacedResource other) {
 | 
				
			||||||
 | 
								return Namespace.Equals(other.Namespace) && Path.Equals(other.Path);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override bool Equals(object? obj) {
 | 
				
			||||||
 | 
								return obj is NamespacedResource other && Equals(other);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public static bool operator ==(NamespacedResource left, NamespacedResource right) {
 | 
				
			||||||
 | 
								return left.Equals(right);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public static bool operator !=(NamespacedResource left, NamespacedResource right) {
 | 
				
			||||||
 | 
								return !left.Equals(right);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override int GetHashCode() {
 | 
				
			||||||
 | 
								unchecked {
 | 
				
			||||||
 | 
									return (Namespace.GetHashCode() * 397) ^ Path.GetHashCode();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override string ToString() {
 | 
				
			||||||
 | 
								return $"{Namespace}:{Path}";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										43
									
								
								lib/TweetLib.Api/Data/Resource.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								lib/TweetLib.Api/Data/Resource.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace TweetLib.Api.Data {
 | 
				
			||||||
 | 
						public readonly struct Resource {
 | 
				
			||||||
 | 
							private const string ValidCharacterPattern = "^[a-z0-9_]+$";
 | 
				
			||||||
 | 
							private static readonly Regex ValidCharacterRegex = new Regex(ValidCharacterPattern, RegexOptions.Compiled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public string Name { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public Resource(string name) {
 | 
				
			||||||
 | 
								if (!ValidCharacterRegex.IsMatch(name)) {
 | 
				
			||||||
 | 
									throw new ArgumentException("Resource name must match the regex: " + ValidCharacterPattern);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Name = name;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private bool Equals(Resource other) {
 | 
				
			||||||
 | 
								return Name == other.Name;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override bool Equals(object? obj) {
 | 
				
			||||||
 | 
								return obj is Resource other && Equals(other);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public static bool operator ==(Resource left, Resource right) {
 | 
				
			||||||
 | 
								return left.Equals(right);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public static bool operator !=(Resource left, Resource right) {
 | 
				
			||||||
 | 
								return !left.Equals(right);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override int GetHashCode() {
 | 
				
			||||||
 | 
								return Name.GetHashCode();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override string ToString() {
 | 
				
			||||||
 | 
								return Name;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								lib/TweetLib.Api/ITweetDuckApi.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								lib/TweetLib.Api/ITweetDuckApi.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					namespace TweetLib.Api {
 | 
				
			||||||
 | 
						public interface ITweetDuckApi {
 | 
				
			||||||
 | 
							T? FindService<T>() where T : class, ITweetDuckService;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								lib/TweetLib.Api/ITweetDuckService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib/TweetLib.Api/ITweetDuckService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					namespace TweetLib.Api {
 | 
				
			||||||
 | 
						public interface ITweetDuckService {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										15
									
								
								lib/TweetLib.Api/TweetDuckExtension.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								lib/TweetLib.Api/TweetDuckExtension.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					using TweetLib.Api.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace TweetLib.Api {
 | 
				
			||||||
 | 
						public abstract class TweetDuckExtension {
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Unique identifier of the extension.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public abstract Resource Id { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Called when the extension is loaded on startup, or enabled at runtime.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public abstract void Enable(ITweetDuckApi api);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								lib/TweetLib.Api/TweetLib.Api.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/TweetLib.Api/TweetLib.Api.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  <PropertyGroup>
 | 
				
			||||||
 | 
					    <TargetFramework>net6.0</TargetFramework>
 | 
				
			||||||
 | 
					    <Configurations>Debug;Release</Configurations>
 | 
				
			||||||
 | 
					    <Platforms>x86;x64</Platforms>
 | 
				
			||||||
 | 
					    <LangVersion>10</LangVersion>
 | 
				
			||||||
 | 
					    <Nullable>enable</Nullable>
 | 
				
			||||||
 | 
					    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
 | 
				
			||||||
 | 
					  </PropertyGroup>
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <Compile Include="..\..\Version.cs" Link="Version.cs" />
 | 
				
			||||||
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</Project>
 | 
				
			||||||
@@ -5,6 +5,8 @@ using TweetLib.Browser.Request;
 | 
				
			|||||||
using TweetLib.Core.Application;
 | 
					using TweetLib.Core.Application;
 | 
				
			||||||
using TweetLib.Core.Features;
 | 
					using TweetLib.Core.Features;
 | 
				
			||||||
using TweetLib.Core.Features.Plugins;
 | 
					using TweetLib.Core.Features.Plugins;
 | 
				
			||||||
 | 
					using TweetLib.Core.Resources;
 | 
				
			||||||
 | 
					using TweetLib.Core.Systems.Api;
 | 
				
			||||||
using TweetLib.Core.Systems.Configuration;
 | 
					using TweetLib.Core.Systems.Configuration;
 | 
				
			||||||
using TweetLib.Core.Systems.Logging;
 | 
					using TweetLib.Core.Systems.Logging;
 | 
				
			||||||
using TweetLib.Utils.Static;
 | 
					using TweetLib.Utils.Static;
 | 
				
			||||||
@@ -21,8 +23,11 @@ namespace TweetLib.Core {
 | 
				
			|||||||
		internal static readonly string PluginPath    = Path.Combine(ProgramPath, "plugins");
 | 
							internal static readonly string PluginPath    = Path.Combine(ProgramPath, "plugins");
 | 
				
			||||||
		internal static readonly string GuidePath     = Path.Combine(ProgramPath, "guide");
 | 
							internal static readonly string GuidePath     = Path.Combine(ProgramPath, "guide");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataFolder();
 | 
							public static readonly string ExtensionPath = Path.Combine(ProgramPath, "extensions");
 | 
				
			||||||
		public static readonly string LogoPath    = Path.Combine(ResourcesPath, "images/logo.png");
 | 
							public static readonly string StoragePath   = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataFolder();
 | 
				
			||||||
 | 
							public static readonly string LogoPath      = Path.Combine(ResourcesPath, "images/logo.png");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public static ApiImplementation Api { get; } = new ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public static Logger Logger               { get; } = new (Path.Combine(StoragePath, "TD_Log.txt"), Setup.IsDebugLogging);
 | 
							public static Logger Logger               { get; } = new (Path.Combine(StoragePath, "TD_Log.txt"), Setup.IsDebugLogging);
 | 
				
			||||||
		public static ConfigManager ConfigManager { get; } = Setup.CreateConfigManager(StoragePath);
 | 
							public static ConfigManager ConfigManager { get; } = Setup.CreateConfigManager(StoragePath);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								lib/TweetLib.Core/Features/Extensions/ExtensionLoader.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/TweetLib.Core/Features/Extensions/ExtensionLoader.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using TweetLib.Api;
 | 
				
			||||||
 | 
					using TweetLib.Core.Systems.Api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace TweetLib.Core.Features.Extensions {
 | 
				
			||||||
 | 
						public static class ExtensionLoader {
 | 
				
			||||||
 | 
							public static void LoadAllInFolder(string extensionFolder) {
 | 
				
			||||||
 | 
								if (!Directory.Exists(extensionFolder)) {
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									foreach (string file in Directory.EnumerateFiles(extensionFolder, "*.dll", SearchOption.TopDirectoryOnly)) {
 | 
				
			||||||
 | 
										try {
 | 
				
			||||||
 | 
											Assembly assembly = Assembly.LoadFile(file);
 | 
				
			||||||
 | 
											foreach (Type type in assembly.GetTypes()) {
 | 
				
			||||||
 | 
												if (typeof(TweetDuckExtension).IsAssignableFrom(type) && Activator.CreateInstance(type) is TweetDuckExtension extension) {
 | 
				
			||||||
 | 
													EnableExtension(extension);
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} catch (Exception e) {
 | 
				
			||||||
 | 
											App.ErrorHandler.HandleException("Extension Error", "Could not load extension: " + Path.GetFileNameWithoutExtension(file), true, e);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} catch (DirectoryNotFoundException) {
 | 
				
			||||||
 | 
									// ignore
 | 
				
			||||||
 | 
								} catch (Exception e) {
 | 
				
			||||||
 | 
									App.ErrorHandler.HandleException("Extension Error", "Could not load extensions.", true, e);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private static void EnableExtension(TweetDuckExtension extension) {
 | 
				
			||||||
 | 
								ApiImplementation apiImplementation = App.Api;
 | 
				
			||||||
 | 
								apiImplementation.CurrentExtension = extension;
 | 
				
			||||||
 | 
								extension.Enable(apiImplementation);
 | 
				
			||||||
 | 
								apiImplementation.CurrentExtension = null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								lib/TweetLib.Core/Systems/Api/ApiImplementation.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/TweetLib.Core/Systems/Api/ApiImplementation.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using TweetLib.Api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace TweetLib.Core.Systems.Api {
 | 
				
			||||||
 | 
						public class ApiImplementation : ITweetDuckApi {
 | 
				
			||||||
 | 
							public TweetDuckExtension? CurrentExtension { get; internal set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private readonly Dictionary<Type, ITweetDuckService> services = new Dictionary<Type, ITweetDuckService>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							internal ApiImplementation() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void RegisterService<T>(T service) where T : class, ITweetDuckService {
 | 
				
			||||||
 | 
								if (!typeof(T).IsInterface) {
 | 
				
			||||||
 | 
									throw new ArgumentException("Api service implementation must be registered with its interface type.");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								services.Add(typeof(T), service);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public T? FindService<T>() where T : class, ITweetDuckService {
 | 
				
			||||||
 | 
								return services.TryGetValue(typeof(T), out ITweetDuckService? service) ? service as T : null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,7 +10,7 @@ using TweetLib.Utils.Static;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace TweetLib.Core.Systems.Configuration {
 | 
					namespace TweetLib.Core.Systems.Configuration {
 | 
				
			||||||
	public abstract class ConfigManager {
 | 
						public abstract class ConfigManager {
 | 
				
			||||||
		protected static TypeConverterRegistry ConverterRegistry { get; } = new ();
 | 
							public static TypeConverterRegistry ConverterRegistry { get; } = new ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		static ConfigManager() {
 | 
							static ConfigManager() {
 | 
				
			||||||
			ConverterRegistry.Register(typeof(WindowState), new BasicTypeConverter<WindowState> {
 | 
								ConverterRegistry.Register(typeof(WindowState), new BasicTypeConverter<WindowState> {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@
 | 
				
			|||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\TweetLib.Api\TweetLib.Api.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\TweetLib.Browser\TweetLib.Browser.csproj" />
 | 
					    <ProjectReference Include="..\TweetLib.Browser\TweetLib.Browser.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\TweetLib.Utils\TweetLib.Utils.csproj" />
 | 
					    <ProjectReference Include="..\TweetLib.Utils\TweetLib.Utils.csproj" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								windows/TweetDuck/Application/ApiServices.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								windows/TweetDuck/Application/ApiServices.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					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) {
 | 
				
			||||||
 | 
								TweetDuckExtension currentExtension = App.Api.CurrentExtension ?? throw new InvalidOperationException("Cannot use API services outside of designated method calls.");
 | 
				
			||||||
 | 
								return new NamespacedResource(currentExtension.Id, 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 @@ namespace TweetDuck.Browser.Notification {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		protected virtual Point PrimaryLocation {
 | 
							protected virtual Point PrimaryLocation {
 | 
				
			||||||
			get {
 | 
								get {
 | 
				
			||||||
				Screen screen;
 | 
									Screen screen = Config.NotificationDisplay.PickScreen(owner);
 | 
				
			||||||
 | 
					 | 
				
			||||||
				if (Config.NotificationDisplay > 0 && Config.NotificationDisplay <= Screen.AllScreens.Length) {
 | 
					 | 
				
			||||||
					screen = Screen.AllScreens[Config.NotificationDisplay - 1];
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				else {
 | 
					 | 
				
			||||||
					screen = Screen.FromControl(owner);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				int edgeDist = Config.NotificationEdgeDistance;
 | 
									int edgeDist = Config.NotificationEdgeDistance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				switch (Config.NotificationPosition) {
 | 
									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.Diagnostics.CodeAnalysis;
 | 
				
			||||||
using System.Drawing;
 | 
					using System.Drawing;
 | 
				
			||||||
using TweetDuck.Browser;
 | 
					using TweetDuck.Browser;
 | 
				
			||||||
 | 
					using TweetDuck.Browser.Notification;
 | 
				
			||||||
using TweetDuck.Controls;
 | 
					using TweetDuck.Controls;
 | 
				
			||||||
using TweetLib.Core;
 | 
					using TweetLib.Core;
 | 
				
			||||||
using TweetLib.Core.Application;
 | 
					using TweetLib.Core.Application;
 | 
				
			||||||
@@ -66,7 +67,7 @@ namespace TweetDuck.Configuration {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		public DesktopNotification.Position NotificationPosition { get; set; } = DesktopNotification.Position.TopRight;
 | 
							public DesktopNotification.Position NotificationPosition { get; set; } = DesktopNotification.Position.TopRight;
 | 
				
			||||||
		public Point CustomNotificationPosition                  { get; set; } = ControlExtensions.InvisibleLocation;
 | 
							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 NotificationEdgeDistance                      { get; set; } = 8;
 | 
				
			||||||
		public int NotificationWindowOpacity                     { get; set; } = 100;
 | 
							public int NotificationWindowOpacity                     { get; set; } = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,13 +86,21 @@ namespace TweetDuck.Dialogs.Settings {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
 | 
								comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
 | 
				
			||||||
			comboBoxDisplay.Items.Add("(Same as TweetDuck)");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			foreach (Screen screen in Screen.AllScreens) {
 | 
								bool foundScreen = false;
 | 
				
			||||||
				comboBoxDisplay.Items.Add($"{screen.DeviceName.TrimStart('\\', '.')} ({screen.Bounds.Width}x{screen.Bounds.Height})");
 | 
					
 | 
				
			||||||
 | 
								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);
 | 
								trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
 | 
				
			||||||
			labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value + " px";
 | 
								labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value + " px";
 | 
				
			||||||
@@ -279,7 +287,7 @@ namespace TweetDuck.Dialogs.Settings {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private void comboBoxDisplay_SelectedValueChanged(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);
 | 
								notification.ShowExampleNotification(false);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -289,6 +297,21 @@ namespace TweetDuck.Dialogs.Settings {
 | 
				
			|||||||
			notification.ShowExampleNotification(false);
 | 
								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
 | 
							#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		#region Size
 | 
							#region Size
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ using CefSharp.WinForms;
 | 
				
			|||||||
using TweetDuck.Application;
 | 
					using TweetDuck.Application;
 | 
				
			||||||
using TweetDuck.Browser;
 | 
					using TweetDuck.Browser;
 | 
				
			||||||
using TweetDuck.Browser.Base;
 | 
					using TweetDuck.Browser.Base;
 | 
				
			||||||
 | 
					using TweetDuck.Browser.Notification;
 | 
				
			||||||
using TweetDuck.Configuration;
 | 
					using TweetDuck.Configuration;
 | 
				
			||||||
using TweetDuck.Dialogs;
 | 
					using TweetDuck.Dialogs;
 | 
				
			||||||
using TweetDuck.Management;
 | 
					using TweetDuck.Management;
 | 
				
			||||||
@@ -16,6 +17,7 @@ using TweetLib.Browser.CEF.Utils;
 | 
				
			|||||||
using TweetLib.Browser.Request;
 | 
					using TweetLib.Browser.Request;
 | 
				
			||||||
using TweetLib.Core;
 | 
					using TweetLib.Core;
 | 
				
			||||||
using TweetLib.Core.Application;
 | 
					using TweetLib.Core.Application;
 | 
				
			||||||
 | 
					using TweetLib.Core.Features.Extensions;
 | 
				
			||||||
using TweetLib.Core.Features.Plugins;
 | 
					using TweetLib.Core.Features.Plugins;
 | 
				
			||||||
using TweetLib.Core.Features.Plugins.Config;
 | 
					using TweetLib.Core.Features.Plugins.Config;
 | 
				
			||||||
using TweetLib.Core.Features.TweetDeck;
 | 
					using TweetLib.Core.Features.TweetDeck;
 | 
				
			||||||
@@ -90,6 +92,7 @@ namespace TweetDuck {
 | 
				
			|||||||
			public string? ResourceRewriteRules => Arguments.GetValue(Arguments.ArgFreeze);
 | 
								public string? ResourceRewriteRules => Arguments.GetValue(Arguments.ArgFreeze);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			public ConfigManager CreateConfigManager(string storagePath) {
 | 
								public ConfigManager CreateConfigManager(string storagePath) {
 | 
				
			||||||
 | 
									ConfigManager.ConverterRegistry.Register(typeof(NotificationScreen), NotificationScreen.Converter);
 | 
				
			||||||
				return new ConfigManager<UserConfig, SystemConfig>(storagePath, Config);
 | 
									return new ConfigManager<UserConfig, SystemConfig>(storagePath, Config);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -137,6 +140,9 @@ namespace TweetDuck {
 | 
				
			|||||||
				Cef.Initialize(settings, false, new BrowserProcessHandler());
 | 
									Cef.Initialize(settings, false, new BrowserProcessHandler());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				Win.Application.ApplicationExit += static (_, _) => ExitCleanup();
 | 
									Win.Application.ApplicationExit += static (_, _) => ExitCleanup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ApiServices.Register();
 | 
				
			||||||
 | 
									ExtensionLoader.LoadAllInFolder(App.ExtensionPath);
 | 
				
			||||||
				var updateCheckClient = new UpdateCheckClient(Path.Combine(storagePath, InstallerFolder));
 | 
									var updateCheckClient = new UpdateCheckClient(Path.Combine(storagePath, InstallerFolder));
 | 
				
			||||||
				var mainForm = new FormBrowser(resourceCache, pluginManager, updateCheckClient, lockManager.WindowRestoreMessage);
 | 
									var mainForm = new FormBrowser(resourceCache, pluginManager, updateCheckClient, lockManager.WindowRestoreMessage);
 | 
				
			||||||
				Win.Application.Run(mainForm);
 | 
									Win.Application.Run(mainForm);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@
 | 
				
			|||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\..\lib\TweetLib.Api\TweetLib.Api.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\..\lib\TweetLib.Browser.CEF\TweetLib.Browser.CEF.csproj" />
 | 
					    <ProjectReference Include="..\..\lib\TweetLib.Browser.CEF\TweetLib.Browser.CEF.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\..\lib\TweetLib.Browser\TweetLib.Browser.csproj" />
 | 
					    <ProjectReference Include="..\..\lib\TweetLib.Browser\TweetLib.Browser.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\..\lib\TweetLib.Communication\TweetLib.Communication.csproj" />
 | 
					    <ProjectReference Include="..\..\lib\TweetLib.Communication\TweetLib.Communication.csproj" />
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user