1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-15 14:34:08 +02:00
TweetDuck/Browser/Notification/FormNotificationMain.cs
2021-12-17 20:27:48 +01:00

297 lines
8.6 KiB
C#

using System;
using System.Drawing;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Browser.Adapters;
using TweetDuck.Browser.Bridge;
using TweetDuck.Browser.Handling;
using TweetDuck.Controls;
using TweetDuck.Plugins;
using TweetDuck.Utils;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Browser.Notification {
abstract partial class FormNotificationMain : FormNotificationBase {
private readonly PluginManager plugins;
private readonly int timerBarHeight;
protected int timeLeft, totalTime;
protected bool pausedDuringNotification;
private readonly NativeMethods.HookProc mouseHookDelegate;
private IntPtr mouseHook;
private bool blockXButtonUp;
private int currentOpacity;
private bool? prevDisplayTimer;
private int? prevFontSize;
public virtual bool RequiresResize {
get { return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Config.DisplayNotificationTimer || prevFontSize != FontSizeLevel; }
set {
if (value) {
prevDisplayTimer = null;
prevFontSize = null;
}
else {
prevDisplayTimer = Config.DisplayNotificationTimer;
prevFontSize = FontSizeLevel;
}
}
}
private int BaseClientWidth {
get => Config.NotificationSize switch {
DesktopNotification.Size.Custom => Config.CustomNotificationSize.Width,
_ => BrowserUtils.Scale(284, SizeScale * (1.0 + 0.05 * FontSizeLevel))
};
}
private int BaseClientHeight {
get => Config.NotificationSize switch {
DesktopNotification.Size.Custom => Config.CustomNotificationSize.Height,
_ => BrowserUtils.Scale(122, SizeScale * (1.0 + 0.08 * FontSizeLevel))
};
}
protected virtual string BodyClasses => IsCursorOverBrowser ? "td-notification td-hover" : "td-notification";
public Size BrowserSize => Config.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height - timerBarHeight) : ClientSize;
protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu) {
InitializeComponent();
this.plugins = pluginManager;
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
browser.KeyboardHandler = new KeyboardHandlerNotification(this);
browser.RegisterJsBridge("$TD", new TweetDeckBridge.Notification(owner, this));
browser.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
plugins.Register(PluginEnvironment.Notification, new PluginDispatcher(browser, url => TwitterUrls.IsTweetDeck(url) && url != BlankURL));
mouseHookDelegate = MouseHookProc;
Disposed += (sender, args) => StopMouseHook(true);
}
// helpers
private void SetOpacity(int opacity) {
if (currentOpacity != opacity) {
currentOpacity = opacity;
Opacity = opacity / 100.0;
}
}
// mouse wheel hook
private void StartMouseHook() {
if (mouseHook == IntPtr.Zero) {
mouseHook = NativeMethods.SetWindowsHookEx(NativeMethods.WM_MOUSE_LL, mouseHookDelegate, IntPtr.Zero, 0);
}
}
private void StopMouseHook(bool force) {
if (mouseHook != IntPtr.Zero && (force || !blockXButtonUp)) {
NativeMethods.UnhookWindowsHookEx(mouseHook);
mouseHook = IntPtr.Zero;
blockXButtonUp = false;
}
}
private IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam) {
if (nCode == 0) {
int eventType = wParam.ToInt32();
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser) {
int delta = BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Config.NotificationScrollSpeed * 0.01);
if (Config.EnableSmoothScrolling) {
browser.ExecuteScriptAsync("window.TDGF_scrollSmoothly", (int) Math.Round(-delta / 0.6));
}
else {
browser.SendMouseWheelEvent(0, 0, 0, delta, CefEventFlags.None);
}
return NativeMethods.HOOK_HANDLED;
}
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)) {
int extraButton = NativeMethods.GetMouseHookData(lParam);
if (extraButton == 2) { // forward button
this.InvokeAsyncSafe(FinishCurrentNotification);
}
else if (extraButton == 1) { // back button
this.InvokeAsyncSafe(Close);
}
blockXButtonUp = true;
return NativeMethods.HOOK_HANDLED;
}
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp) {
blockXButtonUp = false;
if (!Visible) {
StopMouseHook(false);
}
return NativeMethods.HOOK_HANDLED;
}
}
return NativeMethods.CallNextHookEx(mouseHook, nCode, wParam, lParam);
}
// event handlers
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e) {
if (e.CloseReason == CloseReason.UserClosing) {
HideNotification();
e.Cancel = true;
}
}
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) {
if (!e.IsLoading && browser.Address != BlankURL) {
this.InvokeSafe(() => {
Visible = true; // ensures repaint before moving the window to a visible location
timerDisplayDelay.Start();
});
}
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) {
IFrame frame = e.Frame;
if (frame.IsMain && browser.Address != BlankURL) {
frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
CefScriptExecutor.RunFile(frame, "notification.js");
}
}
private void timerDisplayDelay_Tick(object sender, EventArgs e) {
OnNotificationReady();
timerDisplayDelay.Stop();
}
private void timerHideProgress_Tick(object sender, EventArgs e) {
bool isCursorInside = Bounds.Contains(Cursor.Position);
if (isCursorInside) {
StartMouseHook();
SetOpacity(100);
}
else {
StopMouseHook(false);
SetOpacity(Config.NotificationWindowOpacity);
}
if (isCursorInside || FreezeTimer || ContextMenuOpen) {
return;
}
timeLeft -= timerProgress.Interval;
int value = BrowserUtils.Scale(progressBarTimer.Maximum + 25, (totalTime - timeLeft) / (double) totalTime);
progressBarTimer.SetValueInstant(Config.NotificationTimerCountDown ? progressBarTimer.Maximum - value : value);
if (timeLeft <= 0) {
FinishCurrentNotification();
}
}
// notification methods
public virtual void ShowNotification(DesktopNotification notification) {
LoadTweet(notification);
}
public override void HideNotification() {
base.HideNotification();
progressBarTimer.Value = Config.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
timerProgress.Stop();
totalTime = 0;
StopMouseHook(false);
}
public override void FinishCurrentNotification() {
timerProgress.Stop();
}
public override void PauseNotification() {
if (!IsPaused) {
pausedDuringNotification = IsNotificationVisible;
timerProgress.Stop();
StopMouseHook(true);
}
base.PauseNotification();
}
public override void ResumeNotification() {
bool wasPaused = IsPaused;
base.ResumeNotification();
if (wasPaused && !IsPaused && pausedDuringNotification) {
OnNotificationReady();
}
}
protected override string GetTweetHTML(DesktopNotification tweet) {
string html = tweet.GenerateHtml(BodyClasses, HeadLayout, Config.CustomNotificationCSS);
foreach (InjectedHTML injection in plugins.NotificationInjections) {
html = injection.InjectInto(html);
}
return html;
}
protected override void LoadTweet(DesktopNotification tweet) {
timerProgress.Stop();
totalTime = timeLeft = tweet.GetDisplayDuration(Config.NotificationDurationValue);
progressBarTimer.Value = Config.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
base.LoadTweet(tweet);
}
protected override void SetNotificationSize(int width, int height) {
if (Config.DisplayNotificationTimer) {
ClientSize = new Size(width, height + timerBarHeight);
progressBarTimer.Visible = true;
}
else {
ClientSize = new Size(width, height);
progressBarTimer.Visible = false;
}
browser.ClientSize = new Size(width, height);
}
protected void PrepareAndDisplayWindow() {
if (RequiresResize) {
RequiresResize = false;
SetNotificationSize(BaseClientWidth, BaseClientHeight);
}
SetOpacity(IsCursorOverBrowser ? 100 : Config.NotificationWindowOpacity);
MoveToVisibleLocation();
}
protected virtual void OnNotificationReady() {
PrepareAndDisplayWindow();
timerProgress.Start();
}
}
}