diff --git a/ntfysh_client/MainForm.cs b/ntfysh_client/MainForm.cs index cc2cdfa..10198ea 100644 --- a/ntfysh_client/MainForm.cs +++ b/ntfysh_client/MainForm.cs @@ -16,6 +16,7 @@ namespace ntfysh_client private readonly NotificationListener _notificationListener; private bool _startInTray; private bool _trueExit; + private NotificationDialog _notificationDialog; public MainForm(NotificationListener notificationListener, bool startInTray = false) { @@ -32,6 +33,8 @@ namespace ntfysh_client { LoadSettings(); LoadTopics(); + + _notificationDialog = new NotificationDialog(); } protected override void SetVisibleCore(bool value) @@ -69,8 +72,24 @@ namespace ntfysh_client }; string finalTitle = string.IsNullOrWhiteSpace(e.Title) ? $"{e.Sender.TopicId}@{e.Sender.ServerUrl}" : e.Title; - - notifyIcon.ShowBalloonTip((int)TimeSpan.FromSeconds((double)Program.Settings.Timeout).TotalMilliseconds, finalTitle, e.Message, priorityIcon); + + if (Program.Settings.NotificationsMethod == SettingsModel.NotificationsType.NativeWindows) + { + notifyIcon.ShowBalloonTip((int)TimeSpan.FromSeconds((double)Program.Settings.Timeout).TotalMilliseconds, finalTitle, e.Message, priorityIcon); + } + else + { + + _notificationDialog.ShowNotification( + title: finalTitle, + message: e.Message, + timeoutSeconds: (int)Program.Settings.Timeout, + icon: priorityIcon, + showTimeOutBar: Program.Settings.CustomTrayNotificationsShowTimeoutBar, + showInDarkMode: Program.Settings.CustomTrayNotificationsShowInDarkMode, + playNotificationSound: Program.Settings.CustomTrayNotificationsPlayDefaultWindowsSound + ); + } } private void OnConnectionMultiAttemptFailure(NotificationListener sender, SubscribedTopic topic) @@ -132,10 +151,15 @@ namespace ntfysh_client using SettingsDialog dialog = new(); //Load current settings into dialog - dialog.Timeout = Program.Settings.Timeout; dialog.ReconnectAttempts = Program.Settings.ReconnectAttempts; dialog.ReconnectAttemptDelay = Program.Settings.ReconnectAttemptDelay; - + dialog.UseNativeWindowsNotifications = Program.Settings.NotificationsMethod == SettingsModel.NotificationsType.NativeWindows; + dialog.UseCustomTrayNotifications = Program.Settings.NotificationsMethod == SettingsModel.NotificationsType.CustomTray; + dialog.CustomTrayNotificationsShowTimeoutBar = Program.Settings.CustomTrayNotificationsShowTimeoutBar; + dialog.CustomTrayNotificationsShowInDarkMode = Program.Settings.CustomTrayNotificationsShowInDarkMode; + dialog.CustomTrayNotificationsPlayDefaultWindowsSound = Program.Settings.CustomTrayNotificationsPlayDefaultWindowsSound; + dialog.Timeout = Program.Settings.Timeout; // set timeout last so bounds are setup before setting value + //Show dialog DialogResult result = dialog.ShowDialog(); @@ -146,7 +170,11 @@ namespace ntfysh_client Program.Settings.Timeout = dialog.Timeout; Program.Settings.ReconnectAttempts = dialog.ReconnectAttempts; Program.Settings.ReconnectAttemptDelay = dialog.ReconnectAttemptDelay; - + Program.Settings.NotificationsMethod = (dialog.UseNativeWindowsNotifications)? SettingsModel.NotificationsType.NativeWindows : SettingsModel.NotificationsType.CustomTray; + Program.Settings.CustomTrayNotificationsShowTimeoutBar = dialog.CustomTrayNotificationsShowTimeoutBar; + Program.Settings.CustomTrayNotificationsShowInDarkMode = dialog.CustomTrayNotificationsShowInDarkMode; + Program.Settings.CustomTrayNotificationsPlayDefaultWindowsSound = dialog.CustomTrayNotificationsPlayDefaultWindowsSound; + //Save new settings persistently SaveSettingsToFile(); } @@ -298,10 +326,14 @@ namespace ntfysh_client private SettingsModel GetDefaultSettings() => new() { - Revision = 1, + Revision = 2, Timeout = 5, ReconnectAttempts = 10, - ReconnectAttemptDelay = 3 + ReconnectAttemptDelay = 3, + NotificationsMethod = SettingsModel.NotificationsType.NativeWindows, + CustomTrayNotificationsShowTimeoutBar = true, + CustomTrayNotificationsShowInDarkMode = false, + CustomTrayNotificationsPlayDefaultWindowsSound = true, }; private void MergeSettingsRevisions(SettingsModel older, SettingsModel newer) @@ -313,6 +345,15 @@ namespace ntfysh_client older.ReconnectAttemptDelay = newer.ReconnectAttemptDelay; } + //Apply settings introduced in Revision 2 (Native vs custom notifications) + if (older.Revision < 2) + { + older.NotificationsMethod = newer.NotificationsMethod; + older.CustomTrayNotificationsShowTimeoutBar = newer.CustomTrayNotificationsShowTimeoutBar; + older.CustomTrayNotificationsShowInDarkMode = newer.CustomTrayNotificationsShowInDarkMode; + older.CustomTrayNotificationsPlayDefaultWindowsSound = newer.CustomTrayNotificationsPlayDefaultWindowsSound; + } + //Update the revision older.Revision = newer.Revision; } @@ -361,7 +402,7 @@ namespace ntfysh_client Program.Settings = settings; //Check the settings revision. If it is older than the current latest revision, apply the settings defaults missing from previous revision - if (Program.Settings.Revision < defaultSettings.ReconnectAttempts) + if (Program.Settings.Revision < defaultSettings.Revision) { MergeSettingsRevisions(Program.Settings, defaultSettings); SaveSettingsToFile(); diff --git a/ntfysh_client/NotificationDialog.Designer.cs b/ntfysh_client/NotificationDialog.Designer.cs new file mode 100644 index 0000000..40b8faa --- /dev/null +++ b/ntfysh_client/NotificationDialog.Designer.cs @@ -0,0 +1,146 @@ +namespace ntfysh_client +{ + partial class NotificationDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + TxBTitle = new System.Windows.Forms.TextBox(); + ButtonClose = new System.Windows.Forms.Button(); + TxBMessage = new System.Windows.Forms.RichTextBox(); + IconBox = new System.Windows.Forms.PictureBox(); + ProgressBar1 = new System.Windows.Forms.ProgressBar(); + LblTimeout = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)IconBox).BeginInit(); + SuspendLayout(); + // + // TxBTitle + // + TxBTitle.BackColor = System.Drawing.SystemColors.ControlDark; + TxBTitle.BorderStyle = System.Windows.Forms.BorderStyle.None; + TxBTitle.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + TxBTitle.ForeColor = System.Drawing.SystemColors.ControlLightLight; + TxBTitle.Location = new System.Drawing.Point(54, 13); + TxBTitle.Name = "TxBTitle"; + TxBTitle.ReadOnly = true; + TxBTitle.Size = new System.Drawing.Size(683, 32); + TxBTitle.TabIndex = 0; + TxBTitle.MouseDown += window_MouseDown; + // + // ButtonClose + // + ButtonClose.BackColor = System.Drawing.SystemColors.ActiveCaptionText; + ButtonClose.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; + ButtonClose.FlatAppearance.BorderSize = 0; + ButtonClose.FlatAppearance.MouseOverBackColor = System.Drawing.Color.DimGray; + ButtonClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + ButtonClose.ForeColor = System.Drawing.SystemColors.ButtonFace; + ButtonClose.Location = new System.Drawing.Point(759, 7); + ButtonClose.Name = "ButtonClose"; + ButtonClose.Size = new System.Drawing.Size(29, 38); + ButtonClose.TabIndex = 1; + ButtonClose.Text = "X"; + ButtonClose.UseVisualStyleBackColor = false; + ButtonClose.Click += ButtonClose_ClickHandler; + // + // TxBMessage + // + TxBMessage.BackColor = System.Drawing.SystemColors.ControlDark; + TxBMessage.BorderStyle = System.Windows.Forms.BorderStyle.None; + TxBMessage.Font = new System.Drawing.Font("Segoe UI", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + TxBMessage.ForeColor = System.Drawing.SystemColors.ControlLightLight; + TxBMessage.Location = new System.Drawing.Point(12, 57); + TxBMessage.Name = "TxBMessage"; + TxBMessage.ReadOnly = true; + TxBMessage.Size = new System.Drawing.Size(776, 191); + TxBMessage.TabIndex = 2; + TxBMessage.Text = ""; + TxBMessage.MouseDown += window_MouseDown; + // + // IconBox + // + IconBox.Location = new System.Drawing.Point(12, 12); + IconBox.Name = "IconBox"; + IconBox.Size = new System.Drawing.Size(36, 39); + IconBox.TabIndex = 3; + IconBox.TabStop = false; + // + // ProgressBar1 + // + ProgressBar1.BackColor = System.Drawing.SystemColors.ControlDarkDark; + ProgressBar1.Enabled = false; + ProgressBar1.ForeColor = System.Drawing.SystemColors.WindowFrame; + ProgressBar1.Location = new System.Drawing.Point(70, 254); + ProgressBar1.MarqueeAnimationSpeed = 1; + ProgressBar1.Name = "ProgressBar1"; + ProgressBar1.Size = new System.Drawing.Size(718, 23); + ProgressBar1.Step = 1; + ProgressBar1.Style = System.Windows.Forms.ProgressBarStyle.Continuous; + ProgressBar1.TabIndex = 4; + ProgressBar1.Value = 100; + // + // LblTimeout + // + LblTimeout.AutoSize = true; + LblTimeout.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + LblTimeout.Location = new System.Drawing.Point(21, 254); + LblTimeout.Name = "LblTimeout"; + LblTimeout.Size = new System.Drawing.Size(43, 17); + LblTimeout.TabIndex = 5; + LblTimeout.Text = "label1"; + LblTimeout.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // NotificationDialog + // + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + BackColor = System.Drawing.SystemColors.ControlDark; + ClientSize = new System.Drawing.Size(800, 289); + Controls.Add(LblTimeout); + Controls.Add(ProgressBar1); + Controls.Add(IconBox); + Controls.Add(TxBMessage); + Controls.Add(ButtonClose); + Controls.Add(TxBTitle); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + Name = "NotificationDialog"; + Text = "NotificationDialog"; + Click += window_MouseDown; + ((System.ComponentModel.ISupportInitialize)IconBox).EndInit(); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private System.Windows.Forms.TextBox TxBTitle; + private System.Windows.Forms.Button ButtonClose; + private System.Windows.Forms.RichTextBox TxBMessage; + private System.Windows.Forms.PictureBox IconBox; + private System.Windows.Forms.ProgressBar ProgressBar1; + private System.Windows.Forms.Label LblTimeout; + } +} \ No newline at end of file diff --git a/ntfysh_client/NotificationDialog.cs b/ntfysh_client/NotificationDialog.cs new file mode 100644 index 0000000..d856f2c --- /dev/null +++ b/ntfysh_client/NotificationDialog.cs @@ -0,0 +1,295 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using System.Diagnostics; +using ntfysh_client.Themes; +using Microsoft.Win32; +using System.Media; + + +namespace ntfysh_client +{ + public partial class NotificationDialog : Form + { + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern bool AnimateWindow(IntPtr hWnd, int time, int flags); + + private const int ScreenMargin = 20; + + private int _timeoutSeconds = 0; + private System.Timers.Timer? _displayTimeoutTimer = null; + private System.Windows.Forms.Timer? _updateTimer = null; + private Stopwatch? _shownStopwatch = null; + + private readonly BaseTheme _darkModeTheme = new DarkModeTheme(); + private readonly BaseTheme _defaultTheme = new DefaultTheme(); + private BaseTheme? _theme = null; + + public NotificationDialog() + { + ShowInTaskbar = false; + Visible = false; + TopMost = true; + InitializeComponent(); + InitializeWindowHidden(); + } + + public void ShowNotification(string title, string message, int timeoutSeconds = 0, ToolTipIcon? icon = null, bool showTimeOutBar = true, bool showInDarkMode = true, bool playNotificationSound = false) + { + if (Visible) + { + // close the current notification + HandleTimeout(null, null); + } + + _theme = showInDarkMode ? _darkModeTheme : _defaultTheme; + + ApplyTheme(); + + // setup data + IconBox.Image = (icon is null) ? null : ConvertToolTipIconToImage(icon.Value); + + TxBTitle.Text = title; + TxBMessage.Text = message; + + // setup timers + if (_displayTimeoutTimer != null) + { + _displayTimeoutTimer.Stop(); + _displayTimeoutTimer.Dispose(); + } + + if (_updateTimer != null) + { + _updateTimer.Stop(); + _updateTimer.Dispose(); + } + + if (timeoutSeconds > 0) + { + _displayTimeoutTimer = new System.Timers.Timer(timeoutSeconds * 1000); + _displayTimeoutTimer.Elapsed += HandleTimeout; + _displayTimeoutTimer.Start(); + + if (showTimeOutBar) + { + ProgressBar1.Value = 100; + _updateTimer = new System.Windows.Forms.Timer(); + _updateTimer.Interval = 100; + _updateTimer.Tick += UpdateProgress; + _updateTimer.Start(); + + _shownStopwatch = new Stopwatch(); + _shownStopwatch.Start(); + + ProgressBar1.Visible = true; + LblTimeout.Visible = true; + _timeoutSeconds = timeoutSeconds; + } + else + { + ProgressBar1.Visible = false; + LblTimeout.Visible = false; + } + } + else + { + ProgressBar1.Visible = false; + LblTimeout.Visible = false; + } + + // ok, show the window + Show(); + ButtonClose.Focus(); // make sure the window is focused, not the title box. + SetWindowPosition(); + + if (playNotificationSound) PlayNotificationSound(); + } + + private void ApplyTheme() + { + _theme ??= _defaultTheme; + + // back colors + BackColor = _theme.BackgroundColor; + TxBTitle.BackColor = _theme.BackgroundColor; + TxBMessage.BackColor = _theme.BackgroundColor; + LblTimeout.BackColor = _theme.BackgroundColor; + ProgressBar1.BackColor = _theme.BackgroundColor; + + // this one is not "hiding" + ButtonClose.BackColor = _theme.ControlBackGroundColor; + ButtonClose.ForeColor = _theme.BackgroundColor; + // handle mouse over + ButtonClose.FlatAppearance.MouseOverBackColor = _theme.ControlMouseOverBackgroundColor; + + // fore colors + ForeColor = _theme.ForegroundColor; + TxBTitle.ForeColor = _theme.ForegroundColor; + TxBMessage.ForeColor = _theme.ForegroundColor; + LblTimeout.ForeColor = _theme.ForegroundColor; + ProgressBar1.ForeColor = _theme.ForegroundColor; + } + + private void UpdateProgress(object? sender, EventArgs e) + { + if (_shownStopwatch is null) return; + + ProgressBar1.Value = (_timeoutSeconds - _shownStopwatch.Elapsed.Seconds) * 100 / _timeoutSeconds; + LblTimeout.Text = $@"{_timeoutSeconds - _shownStopwatch.Elapsed.Seconds}"; + } + + protected override void SetVisibleCore(bool value) + { + SetWindowPosition(); + + if (value) + { + BringToFront(); + + AnimateWindow( + Handle, + time: 250, + flags: NFWinUserAnimateWindowConstants.AW_SLIDE | NFWinUserAnimateWindowConstants.AW_VER_NEGATIVE + ); + } + + base.SetVisibleCore(value); + } + + private void SetWindowPosition() + { + var workingTop = Screen.PrimaryScreen.WorkingArea.Height - Height; + Top = workingTop - NotificationDialog.ScreenMargin; + + var workingLeft = Screen.PrimaryScreen.WorkingArea.Width - Width; + Left = workingLeft - NotificationDialog.ScreenMargin; + } + + private void UIThreadAnimatedHideWindow(object? sender, EventArgs? e) + { + + AnimateWindow( + Handle, + time: 250, + flags: NFWinUserAnimateWindowConstants.AW_SLIDE | NFWinUserAnimateWindowConstants.AW_VER_POSITIVE | NFWinUserAnimateWindowConstants.AW_HIDE + ); + + Visible = false; + } + + private void HandleTimeout(object? sender, EventArgs? e) + { + CancelTimer(); + + if (InvokeRequired) + { + // on a background thread, so invoke on the UI thread + Invoke(new Action(() => UIThreadAnimatedHideWindow(sender, e))); + } + else + { + // in the UI thread, invoke directly + UIThreadAnimatedHideWindow(sender, e); + } + } + + private Image? ConvertToolTipIconToImage(ToolTipIcon icon) => icon switch + { + ToolTipIcon.Info => SystemIcons.Information.ToBitmap(), + ToolTipIcon.Warning => SystemIcons.Warning.ToBitmap(), + ToolTipIcon.Error => SystemIcons.Error.ToBitmap(), + _ => null + }; + + private void InitializeWindowHidden() + { + Opacity = 0; + ShowNotification("Title", "Message"); + ButtonClose.Focus(); + Visible = false; + Opacity = 1; + } + + private void ButtonClose_ClickHandler(object sender, EventArgs e) + { + // don't animate, immediately "close" + Visible = false; + } + + private class NFWinUserAnimateWindowConstants + { + public const int AW_HOR_POSITIVE = 0x00000001; + public const int AW_HOR_NEGATIVE = 0x00000002; + public const int AW_VER_POSITIVE = 0x00000004; + public const int AW_VER_NEGATIVE = 0x00000008; + public const int AW_CENTER = 0x00000010; + public const int AW_HIDE = 0x00010000; + public const int AW_ACTIVATE = 0x00020000; + public const int AW_SLIDE = 0x00040000; + public const int AW_BLEND = 0x00080000; + } + + private void window_MouseDown(object sender, EventArgs e) => CancelTimer(); + + private void CancelTimer() + { + if (InvokeRequired) + { + // on a background thread, so invoke on the UI thread + Invoke(new Action(() => + { + LblTimeout.Visible = false; + ProgressBar1.Visible = false; + })); + } + else + { + // in the UI thread, invoke directly + LblTimeout.Visible = false; + ProgressBar1.Visible = false; + } + + if (_displayTimeoutTimer != null) // check if the timer has already been disposed + { + _displayTimeoutTimer.Stop(); + _displayTimeoutTimer.Dispose(); + _displayTimeoutTimer = null; + } + + if (_updateTimer != null) + { + _updateTimer.Stop(); + _updateTimer.Dispose(); + _updateTimer = null; + } + + if (_shownStopwatch != null) + { + _shownStopwatch.Stop(); + _shownStopwatch = null; + } + } + + private void PlayNotificationSound() + { + try + { + using RegistryKey? defaultSoundPathKey = Registry.CurrentUser.OpenSubKey(@"AppEvents\Schemes\Apps\.Default\Notification.Default\.Current"); + + if (defaultSoundPathKey is null) throw new Exception(); + + if (defaultSoundPathKey.GetValue(null) is not string defaultSoundPath) throw new Exception(); + + SoundPlayer loadedSound = new(defaultSoundPath); + + loadedSound.Play(); + } + catch + { + SystemSounds.Beep.Play(); // consolation prize + } + } + } +} diff --git a/ntfysh_client/NotificationDialog.resx b/ntfysh_client/NotificationDialog.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/ntfysh_client/NotificationDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ntfysh_client/SettingsDialog.Designer.cs b/ntfysh_client/SettingsDialog.Designer.cs index 8f240fe..428625a 100644 --- a/ntfysh_client/SettingsDialog.Designer.cs +++ b/ntfysh_client/SettingsDialog.Designer.cs @@ -38,10 +38,20 @@ namespace ntfysh_client reconnectAttemptsLabel = new System.Windows.Forms.Label(); reconnectAttemptDelay = new System.Windows.Forms.NumericUpDown(); reconnectAttemptDelayLabel = new System.Windows.Forms.Label(); + nativeVersusCustomNotificationsGroupBox = new System.Windows.Forms.GroupBox(); + useCustomTrayNotifications = new System.Windows.Forms.RadioButton(); + useNativeWindowsNotifications = new System.Windows.Forms.RadioButton(); + groupCustomNotificationSettings = new System.Windows.Forms.GroupBox(); + customNotificationsPlayWindowsNotificationAudio = new System.Windows.Forms.CheckBox(); + customNotificationsShowInDarkMode = new System.Windows.Forms.CheckBox(); + customNotificationsShowTimeoutBar = new System.Windows.Forms.CheckBox(); + label1 = new System.Windows.Forms.Label(); buttonPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)timeout).BeginInit(); ((System.ComponentModel.ISupportInitialize)reconnectAttempts).BeginInit(); ((System.ComponentModel.ISupportInitialize)reconnectAttemptDelay).BeginInit(); + nativeVersusCustomNotificationsGroupBox.SuspendLayout(); + groupCustomNotificationSettings.SuspendLayout(); SuspendLayout(); // // buttonPanel @@ -50,7 +60,7 @@ namespace ntfysh_client buttonPanel.Controls.Add(cancelButton); buttonPanel.Controls.Add(saveButton); buttonPanel.Dock = System.Windows.Forms.DockStyle.Bottom; - buttonPanel.Location = new System.Drawing.Point(0, 150); + buttonPanel.Location = new System.Drawing.Point(0, 336); buttonPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); buttonPanel.Name = "buttonPanel"; buttonPanel.Size = new System.Drawing.Size(531, 51); @@ -82,9 +92,9 @@ namespace ntfysh_client timeoutLabel.Location = new System.Drawing.Point(13, 9); timeoutLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); timeoutLabel.Name = "timeoutLabel"; - timeoutLabel.Size = new System.Drawing.Size(488, 15); + timeoutLabel.Size = new System.Drawing.Size(401, 15); timeoutLabel.TabIndex = 3; - timeoutLabel.Text = "Notification Toast Timeout (seconds, may be ignored by OS based on accessibility settings):"; + timeoutLabel.Text = "Notification Toast Timeout (seconds, use -1 to require closing notification):"; // // timeout // @@ -108,7 +118,7 @@ namespace ntfysh_client reconnectAttemptsLabel.Location = new System.Drawing.Point(12, 54); reconnectAttemptsLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); reconnectAttemptsLabel.Name = "reconnectAttemptsLabel"; - reconnectAttemptsLabel.Size = new System.Drawing.Size(198, 15); + reconnectAttemptsLabel.Size = new System.Drawing.Size(287, 15); reconnectAttemptsLabel.TabIndex = 5; reconnectAttemptsLabel.Text = "Maximum reconnect retry attempts (requires restart):"; // @@ -126,16 +136,102 @@ namespace ntfysh_client reconnectAttemptDelayLabel.Location = new System.Drawing.Point(12, 99); reconnectAttemptDelayLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); reconnectAttemptDelayLabel.Name = "reconnectAttemptDelayLabel"; - reconnectAttemptDelayLabel.Size = new System.Drawing.Size(191, 15); + reconnectAttemptDelayLabel.Size = new System.Drawing.Size(275, 15); reconnectAttemptDelayLabel.TabIndex = 7; reconnectAttemptDelayLabel.Text = "Delay between attempts (seconds, requires restart):"; // + // nativeVersusCustomNotificationsGroupBox + // + nativeVersusCustomNotificationsGroupBox.Controls.Add(useCustomTrayNotifications); + nativeVersusCustomNotificationsGroupBox.Controls.Add(useNativeWindowsNotifications); + nativeVersusCustomNotificationsGroupBox.Location = new System.Drawing.Point(12, 147); + nativeVersusCustomNotificationsGroupBox.Name = "nativeVersusCustomNotificationsGroupBox"; + nativeVersusCustomNotificationsGroupBox.Size = new System.Drawing.Size(506, 67); + nativeVersusCustomNotificationsGroupBox.TabIndex = 9; + nativeVersusCustomNotificationsGroupBox.TabStop = false; + // + // useCustomTrayNotifications + // + useCustomTrayNotifications.AutoSize = true; + useCustomTrayNotifications.Location = new System.Drawing.Point(6, 40); + useCustomTrayNotifications.Name = "useCustomTrayNotifications"; + useCustomTrayNotifications.Size = new System.Drawing.Size(267, 19); + useCustomTrayNotifications.TabIndex = 1; + useCustomTrayNotifications.TabStop = true; + useCustomTrayNotifications.Text = "Use ntfysh-windows custom tray notifications"; + useCustomTrayNotifications.UseVisualStyleBackColor = true; + useCustomTrayNotifications.CheckedChanged += UseCustomTrayNotifications_CheckedChanged; + // + // useNativeWindowsNotifications + // + useNativeWindowsNotifications.AutoSize = true; + useNativeWindowsNotifications.Location = new System.Drawing.Point(6, 15); + useNativeWindowsNotifications.Name = "useNativeWindowsNotifications"; + useNativeWindowsNotifications.Size = new System.Drawing.Size(203, 19); + useNativeWindowsNotifications.TabIndex = 0; + useNativeWindowsNotifications.TabStop = true; + useNativeWindowsNotifications.Text = "Use Windows' native notifications"; + useNativeWindowsNotifications.UseVisualStyleBackColor = true; + // + // groupCustomNotificationSettings + // + groupCustomNotificationSettings.Controls.Add(customNotificationsPlayWindowsNotificationAudio); + groupCustomNotificationSettings.Controls.Add(customNotificationsShowInDarkMode); + groupCustomNotificationSettings.Controls.Add(customNotificationsShowTimeoutBar); + groupCustomNotificationSettings.Location = new System.Drawing.Point(12, 243); + groupCustomNotificationSettings.Name = "groupCustomNotificationSettings"; + groupCustomNotificationSettings.Size = new System.Drawing.Size(504, 87); + groupCustomNotificationSettings.TabIndex = 10; + groupCustomNotificationSettings.TabStop = false; + // + // customNotificationsPlayWindowsNotificationAudio + // + customNotificationsPlayWindowsNotificationAudio.AutoSize = true; + customNotificationsPlayWindowsNotificationAudio.Location = new System.Drawing.Point(4, 59); + customNotificationsPlayWindowsNotificationAudio.Name = "customNotificationsPlayWindowsNotificationAudio"; + customNotificationsPlayWindowsNotificationAudio.Size = new System.Drawing.Size(200, 19); + customNotificationsPlayWindowsNotificationAudio.TabIndex = 2; + customNotificationsPlayWindowsNotificationAudio.Text = "Play Windows notification sound"; + customNotificationsPlayWindowsNotificationAudio.UseVisualStyleBackColor = true; + // + // customNotificationsShowInDarkMode + // + customNotificationsShowInDarkMode.AutoSize = true; + customNotificationsShowInDarkMode.Location = new System.Drawing.Point(6, 37); + customNotificationsShowInDarkMode.Name = "customNotificationsShowInDarkMode"; + customNotificationsShowInDarkMode.Size = new System.Drawing.Size(197, 19); + customNotificationsShowInDarkMode.TabIndex = 1; + customNotificationsShowInDarkMode.Text = "Show notifications in dark mode"; + customNotificationsShowInDarkMode.UseVisualStyleBackColor = true; + // + // customNotificationsShowTimeoutBar + // + customNotificationsShowTimeoutBar.AutoSize = true; + customNotificationsShowTimeoutBar.Location = new System.Drawing.Point(6, 14); + customNotificationsShowTimeoutBar.Name = "customNotificationsShowTimeoutBar"; + customNotificationsShowTimeoutBar.Size = new System.Drawing.Size(211, 19); + customNotificationsShowTimeoutBar.TabIndex = 0; + customNotificationsShowTimeoutBar.Text = "Show time-out bar on notifications"; + customNotificationsShowTimeoutBar.UseVisualStyleBackColor = true; + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(12, 227); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(180, 15); + label1.TabIndex = 11; + label1.Text = "Custom tray notification settings"; + // // SettingsDialog // AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; BackColor = System.Drawing.Color.White; - ClientSize = new System.Drawing.Size(531, 201); + ClientSize = new System.Drawing.Size(531, 387); + Controls.Add(label1); + Controls.Add(groupCustomNotificationSettings); + Controls.Add(nativeVersusCustomNotificationsGroupBox); Controls.Add(reconnectAttemptDelay); Controls.Add(reconnectAttemptDelayLabel); Controls.Add(reconnectAttempts); @@ -156,6 +252,10 @@ namespace ntfysh_client ((System.ComponentModel.ISupportInitialize)timeout).EndInit(); ((System.ComponentModel.ISupportInitialize)reconnectAttempts).EndInit(); ((System.ComponentModel.ISupportInitialize)reconnectAttemptDelay).EndInit(); + nativeVersusCustomNotificationsGroupBox.ResumeLayout(false); + nativeVersusCustomNotificationsGroupBox.PerformLayout(); + groupCustomNotificationSettings.ResumeLayout(false); + groupCustomNotificationSettings.PerformLayout(); ResumeLayout(false); PerformLayout(); } @@ -171,5 +271,13 @@ namespace ntfysh_client private System.Windows.Forms.Label reconnectAttemptsLabel; private System.Windows.Forms.NumericUpDown reconnectAttemptDelay; private System.Windows.Forms.Label reconnectAttemptDelayLabel; + private System.Windows.Forms.GroupBox nativeVersusCustomNotificationsGroupBox; + private System.Windows.Forms.RadioButton useCustomTrayNotifications; + private System.Windows.Forms.RadioButton useNativeWindowsNotifications; + private System.Windows.Forms.GroupBox groupCustomNotificationSettings; + private System.Windows.Forms.CheckBox customNotificationsShowTimeoutBar; + private System.Windows.Forms.CheckBox customNotificationsShowInDarkMode; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.CheckBox customNotificationsPlayWindowsNotificationAudio; } } \ No newline at end of file diff --git a/ntfysh_client/SettingsDialog.cs b/ntfysh_client/SettingsDialog.cs index 6568d9d..2d4d0ec 100644 --- a/ntfysh_client/SettingsDialog.cs +++ b/ntfysh_client/SettingsDialog.cs @@ -1,10 +1,13 @@ using System; using System.Windows.Forms; +using static ntfysh_client.SettingsModel; namespace ntfysh_client { public partial class SettingsDialog : Form { + public NotificationsType NotificationsMethod { get; set; } + public decimal Timeout { get => timeout.Value; @@ -23,9 +26,53 @@ namespace ntfysh_client set => reconnectAttemptDelay.Value = value; } + #region: Native vs custom notifications options. Because these are in a group box, these are mutualy exclusive. + public bool UseNativeWindowsNotifications + { + get => useNativeWindowsNotifications.Checked; + set + { + useNativeWindowsNotifications.Checked = value; + groupCustomNotificationSettings.Enabled = !value; + NotificationsMethod = (value) ? NotificationsType.NativeWindows : NotificationsType.CustomTray; + } + } + + public bool UseCustomTrayNotifications + { + get => useCustomTrayNotifications.Checked; + set { + useCustomTrayNotifications.Checked = value; + groupCustomNotificationSettings.Enabled = value; + NotificationsMethod = (value) ? NotificationsType.NativeWindows : NotificationsType.CustomTray; + } + } + #endregion + + #region: Custom tray notification options + public bool CustomTrayNotificationsShowTimeoutBar + { + get => customNotificationsShowTimeoutBar.Checked; + set => customNotificationsShowTimeoutBar.Checked = value; + } + + public bool CustomTrayNotificationsShowInDarkMode + { + get => customNotificationsShowInDarkMode.Checked; + set => customNotificationsShowInDarkMode.Checked = value; + } + + public bool CustomTrayNotificationsPlayDefaultWindowsSound + { + get => customNotificationsPlayWindowsNotificationAudio.Checked; + set => customNotificationsPlayWindowsNotificationAudio.Checked = value; + } + #endregion + public SettingsDialog() { InitializeComponent(); + SetNotificationsUiElements(); } private void saveButton_Click(object sender, EventArgs e) @@ -37,5 +84,19 @@ namespace ntfysh_client { DialogResult = DialogResult.Cancel; } + + private void SetNotificationsUiElements() + { + groupCustomNotificationSettings.Enabled = useCustomTrayNotifications.Checked; + timeoutLabel.Text = useCustomTrayNotifications.Checked ? _customNotificationsTimeout : _windowsNotificationsTimeout; + } + + private void UseCustomTrayNotifications_CheckedChanged(object sender, EventArgs e) + { + SetNotificationsUiElements(); + } + + private const string _windowsNotificationsTimeout = "Notification Toast Timeout (seconds, may be ignored by OS based on accessibility settings):"; + private const string _customNotificationsTimeout = "Notification Toast Timeout (seconds, use 0 to require closing notification):"; } } diff --git a/ntfysh_client/SettingsDialog.resx b/ntfysh_client/SettingsDialog.resx index af32865..8b2ff64 100644 --- a/ntfysh_client/SettingsDialog.resx +++ b/ntfysh_client/SettingsDialog.resx @@ -1,7 +1,7 @@