From 77cfb815458fd48239ce9b3ad5821db0ce0e9c79 Mon Sep 17 00:00:00 2001 From: jp64k <122999544+jp64k@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:51:36 +0100 Subject: [PATCH] Feat: Rookie now flashes in taskbar when FlexibleMessageBox is thrown and window is inactive, Fix: FlexibleMessageBox positioning for inactive/minimized window Added taskbar flashing to notify users when a task is complete or requires user input (= when FlexibleMessageBox is thrown) while the application is inactive or minimized. Enhanced FlexibleMessageBox to better handle when the application is minimized by centering the dialog on the screen and repositioning it when the form is restored. --- FlexibleMessageBox.cs | 57 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/FlexibleMessageBox.cs b/FlexibleMessageBox.cs index e34a2fb..bbf8c4a 100644 --- a/FlexibleMessageBox.cs +++ b/FlexibleMessageBox.cs @@ -83,7 +83,18 @@ namespace JR.Utils.GUI.Forms private const int CS_DROPSHADOW = 0x00020000; private const int WM_NCLBUTTONDOWN = 0xA1; private const int HT_CAPTION = 0x2; - private const int BORDER_RADIUS = 12; + private const uint FLASHW_TRAY = 0x00000002; + private const uint FLASHW_TIMERNOFG = 0x0000000C; + + [StructLayout(LayoutKind.Sequential)] + private struct FLASHWINFO + { + public uint cbSize; + public IntPtr hwnd; + public uint dwFlags; + public uint uCount; + public uint dwTimeout; + } [DllImport("user32.dll")] private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); @@ -91,6 +102,9 @@ namespace JR.Utils.GUI.Forms [DllImport("user32.dll")] private static extern bool ReleaseCapture(); + [DllImport("user32.dll")] + private static extern bool FlashWindowEx(ref FLASHWINFO pwfi); + protected override CreateParams CreateParams { get @@ -604,7 +618,7 @@ namespace JR.Utils.GUI.Forms ownerForm = Form.ActiveForm; } - if (ownerForm != null && ownerForm.Visible) + if (ownerForm != null && ownerForm.Visible && ownerForm.WindowState != FormWindowState.Minimized) { // Center relative to owner window int x = ownerForm.Left + (ownerForm.Width - flexibleMessageBoxForm.Width) / 2; @@ -620,7 +634,7 @@ namespace JR.Utils.GUI.Forms } else { - // No owner found: center on current screen + // No owner found or minimized: center on current screen CenterOnScreen(flexibleMessageBoxForm); } } @@ -848,8 +862,45 @@ namespace JR.Utils.GUI.Forms int contentWidth = flexibleMessageBoxForm.ClientSize.Width - 16; // 8px padding flexibleMessageBoxForm.titlePanel.Width = contentWidth; + // Get owner form + Form ownerForm = owner as Form ?? Form.ActiveForm; + bool ownerWasMinimized = ownerForm != null && ownerForm.WindowState == FormWindowState.Minimized; + SetDialogStartPosition(flexibleMessageBoxForm, owner); + // If owner was minimized, reposition dialog when owner is restored + if (ownerWasMinimized && ownerForm != null) + { + EventHandler resizeHandler = null; + resizeHandler = (s, e) => + { + if (ownerForm.WindowState != FormWindowState.Minimized && flexibleMessageBoxForm.Visible && !flexibleMessageBoxForm.IsDisposed) + { + SetDialogStartPosition(flexibleMessageBoxForm, owner); + ownerForm.Resize -= resizeHandler; + } + }; + ownerForm.Resize += resizeHandler; + } + + // Flash taskbar if application is inactive or minimized + if (Form.ActiveForm == null || ownerWasMinimized) + { + Form targetForm = ownerForm ?? Application.OpenForms.Cast