mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-04-28 09:15:46 +02:00
319 lines
8.7 KiB
C#
319 lines
8.7 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
using CefSharp;
|
|
using TweetDuck.Browser.Handling;
|
|
using TweetDuck.Controls;
|
|
using TweetDuck.Utils;
|
|
using TweetLib.Core.Features.Notifications;
|
|
|
|
namespace TweetDuck.Browser.Notification {
|
|
abstract partial class FormNotificationMain : FormNotificationBase, CustomKeyboardHandler.IBrowserKeyHandler {
|
|
protected sealed class NotificationInterfaceImpl : INotificationInterface {
|
|
public bool FreezeTimer {
|
|
get => notification.FreezeTimer;
|
|
set => notification.FreezeTimer = value;
|
|
}
|
|
|
|
public bool IsHovered => notification.IsCursorOverBrowser;
|
|
|
|
private readonly FormNotificationBase notification;
|
|
|
|
public NotificationInterfaceImpl(FormNotificationBase notification) {
|
|
this.notification = notification;
|
|
}
|
|
|
|
public void DisplayTooltip(string text) {
|
|
notification.InvokeAsyncSafe(() => notification.DisplayTooltip(text));
|
|
}
|
|
|
|
public void FinishCurrentNotification() {
|
|
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
|
}
|
|
|
|
public void ShowTweetDetail() {
|
|
notification.InvokeAsyncSafe(notification.ShowTweetDetail);
|
|
}
|
|
}
|
|
|
|
private static int FontSizeLevel {
|
|
get => NotificationBrowser.FontSize switch {
|
|
"largest" => 4,
|
|
"large" => 3,
|
|
"small" => 1,
|
|
"smallest" => 0,
|
|
_ => 2
|
|
};
|
|
}
|
|
|
|
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))
|
|
};
|
|
}
|
|
|
|
public Size BrowserSize => Config.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height - timerBarHeight) : ClientSize;
|
|
|
|
protected FormNotificationMain(FormBrowser owner, CreateBrowserImplFunc createBrowserImpl) : base(owner, createBrowserImpl) {
|
|
InitializeComponent();
|
|
|
|
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
|
|
|
|
browser.KeyboardHandler = new CustomKeyboardHandler(this);
|
|
|
|
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
|
|
|
mouseHookDelegate = MouseHookProc;
|
|
Disposed += (sender, args) => StopMouseHook(true);
|
|
}
|
|
|
|
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.BrowserCore.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 != NotificationBrowser.BlankURL) {
|
|
this.InvokeSafe(() => {
|
|
Visible = true; // ensures repaint before moving the window to a visible location
|
|
timerDisplayDelay.Start();
|
|
});
|
|
}
|
|
}
|
|
|
|
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 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();
|
|
}
|
|
|
|
bool CustomKeyboardHandler.IBrowserKeyHandler.HandleBrowserKey(Keys key) {
|
|
switch (key) {
|
|
case Keys.Enter:
|
|
this.InvokeAsyncSafe(FinishCurrentNotification);
|
|
return true;
|
|
|
|
case Keys.Escape:
|
|
this.InvokeAsyncSafe(HideNotification);
|
|
return true;
|
|
|
|
case Keys.Space:
|
|
this.InvokeAsyncSafe(() => FreezeTimer = !FreezeTimer);
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|