66using System . Linq ;
77using System . Runtime . InteropServices ;
88using System . Windows . Forms ;
9+ using System . Media ;
10+ using Microsoft . Win32 ;
911using Rainmeter ;
1012
1113namespace PluginScreenshot
@@ -62,6 +64,7 @@ public class Settings
6264 public bool ShowCursor { get ; private set ; }
6365 public int JpegQuality { get ; private set ; }
6466 public Rectangle PredefinedRegion { get ; private set ; }
67+ public bool ShowNotification { get ; private set ; }
6568
6669 public Settings ( API api )
6770 {
@@ -70,6 +73,7 @@ public Settings(API api)
7073 FinishAction = api . ReadString ( "ScreenshotFinishAction" , "" ) ;
7174 ShowCursor = api . ReadInt ( "ShowCursor" , 0 ) > 0 ;
7275 JpegQuality = api . ReadInt ( "JpgQuality" , 70 ) ;
76+ ShowNotification = api . ReadInt ( "ShowNotification" , 0 ) > 0 ;
7377 int x = api . ReadInt ( "PredefX" , 0 ) ;
7478 int y = api . ReadInt ( "PredefY" , 0 ) ;
7579 int w = api . ReadInt ( "PredefWidth" , 0 ) ;
@@ -179,6 +183,12 @@ public static void TakeFullScreen(Settings settings)
179183 }
180184 } ) ;
181185
186+ if ( settings . ShowNotification )
187+ {
188+ Logger . Log ( "TakeFullScreen: ShowNotification is enabled" ) ;
189+ ShowNotificationWithImage ( settings . SavePath , "Full Screen" ) ;
190+ }
191+
182192 ExecuteFinishAction ( settings ) ;
183193 }
184194
@@ -200,6 +210,12 @@ public static void TakePredefined(Settings settings)
200210 }
201211 } ) ;
202212
213+ if ( settings . ShowNotification )
214+ {
215+ Logger . Log ( "TakePredefined: ShowNotification is enabled" ) ;
216+ ShowNotificationWithImage ( settings . SavePath , "Predefined Region" ) ;
217+ }
218+
203219 ExecuteFinishAction ( settings ) ;
204220 }
205221
@@ -267,6 +283,12 @@ public static void TakeWindowScreenshot(Settings settings, string windowTitle)
267283 }
268284 } ) ;
269285
286+ if ( settings . ShowNotification )
287+ {
288+ Logger . Log ( "TakeWindowScreenshot: ShowNotification is enabled" ) ;
289+ ShowNotificationWithImage ( settings . SavePath , $ "Window: { windowTitle } ") ;
290+ }
291+
270292 ExecuteFinishAction ( settings ) ;
271293 }
272294
@@ -303,6 +325,12 @@ public static void CompositeCapture(Rectangle rect, Settings settings)
303325
304326 SaveImageSafely ( finalBmp , settings ) ;
305327 }
328+
329+ if ( settings . ShowNotification )
330+ {
331+ Logger . Log ( "CompositeCapture: ShowNotification is enabled" ) ;
332+ ShowNotificationWithImage ( settings . SavePath , "Custom Region" ) ;
333+ }
306334 }
307335
308336
@@ -381,6 +409,54 @@ public static void ExecuteFinishAction(Settings settings)
381409 Logger . Log ( "Error running finish action: " + ex . Message ) ;
382410 }
383411 }
412+
413+ private static void ShowNotificationWithImage ( string imagePath , string captureType )
414+ {
415+ try
416+ {
417+ if ( ! File . Exists ( imagePath ) )
418+ {
419+ Logger . Log ( $ "ShowNotificationWithImage: Image file not found at '{ imagePath } '") ;
420+ return ;
421+ }
422+
423+ Logger . Log ( $ "ShowNotificationWithImage: Creating notification for '{ captureType } '") ;
424+
425+ // Play Windows notification sound
426+ try
427+ {
428+ SystemSounds . Asterisk . Play ( ) ;
429+ Logger . Log ( "Notification sound played" ) ;
430+ }
431+ catch ( Exception ex )
432+ {
433+ Logger . Log ( $ "Failed to play notification sound: { ex . Message } ") ;
434+ }
435+
436+ // Create notification form on a separate thread
437+ var notificationThread = new System . Threading . Thread ( ( ) =>
438+ {
439+ try
440+ {
441+ Logger . Log ( "Notification thread started" ) ;
442+ Application . Run ( new NotificationForm ( imagePath , captureType ) ) ;
443+ }
444+ catch ( Exception ex )
445+ {
446+ Logger . Log ( $ "Notification thread error: { ex . Message } ") ;
447+ }
448+ } ) ;
449+ notificationThread . SetApartmentState ( System . Threading . ApartmentState . STA ) ;
450+ notificationThread . IsBackground = true ;
451+ notificationThread . Start ( ) ;
452+
453+ Logger . Log ( $ "Notification thread started for '{ captureType } ' capture.") ;
454+ }
455+ catch ( Exception ex )
456+ {
457+ Logger . Log ( $ "Error showing notification: { ex . Message } ") ;
458+ }
459+ }
384460 }
385461
386462 public class CustomScreenshotForm : Form
@@ -470,6 +546,251 @@ private void OnPaint(object s, PaintEventArgs e)
470546 }
471547 }
472548
549+ public class NotificationForm : Form
550+ {
551+ private readonly Timer _autoCloseTimer ;
552+ private readonly Timer _fadeOutTimer ;
553+ private readonly string _imagePath ;
554+ private const int NotificationWidth = 380 ;
555+ private const int NotificationHeight = 120 ;
556+ private const int DisplayDuration = 4000 ; // 4 seconds
557+ private const int FadeOutDuration = 500 ; // 0.5 seconds
558+ private double _opacity = 1.0 ;
559+ private readonly bool _isDarkMode ;
560+
561+ public NotificationForm ( string imagePath , string captureType )
562+ {
563+ _imagePath = imagePath ;
564+ _isDarkMode = IsWindowsDarkMode ( ) ;
565+
566+ // Form settings
567+ FormBorderStyle = FormBorderStyle . None ;
568+ StartPosition = FormStartPosition . Manual ;
569+ TopMost = true ;
570+ ShowInTaskbar = false ;
571+ Width = NotificationWidth ;
572+ Height = NotificationHeight ;
573+ BackColor = _isDarkMode ? Color . FromArgb ( 30 , 30 , 30 ) : Color . FromArgb ( 240 , 240 , 240 ) ;
574+ Opacity = 0 ;
575+
576+ // Position at bottom-right of screen
577+ var workingArea = Screen . PrimaryScreen . WorkingArea ;
578+ Location = new Point (
579+ workingArea . Right - Width - 20 ,
580+ workingArea . Bottom - Height - 20
581+ ) ;
582+
583+ // Create UI
584+ CreateNotificationUI ( captureType ) ;
585+
586+ // Auto-close timer
587+ _autoCloseTimer = new Timer { Interval = DisplayDuration } ;
588+ _autoCloseTimer . Tick += ( s , e ) =>
589+ {
590+ _autoCloseTimer . Stop ( ) ;
591+ StartFadeOut ( ) ;
592+ } ;
593+
594+ // Fade-out timer
595+ _fadeOutTimer = new Timer { Interval = 20 } ;
596+ _fadeOutTimer . Tick += FadeOutTick ;
597+
598+ // Click to close
599+ Click += ( s , e ) => StartFadeOut ( ) ;
600+
601+ // Show with fade-in effect
602+ Load += ( s , e ) =>
603+ {
604+ FadeIn ( ) ;
605+ _autoCloseTimer . Start ( ) ;
606+ } ;
607+ }
608+
609+ private bool IsWindowsDarkMode ( )
610+ {
611+ try
612+ {
613+ using ( RegistryKey key = Registry . CurrentUser . OpenSubKey ( @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ) )
614+ {
615+ if ( key != null )
616+ {
617+ object value = key . GetValue ( "AppsUseLightTheme" ) ;
618+ if ( value != null )
619+ {
620+ return ( int ) value == 0 ; // 0 = Dark Mode, 1 = Light Mode
621+ }
622+ }
623+ }
624+ }
625+ catch ( Exception ex )
626+ {
627+ Logger . Log ( $ "Failed to detect Windows theme: { ex . Message } ") ;
628+ }
629+ return true ; // Default to dark mode if detection fails
630+ }
631+
632+ private void CreateNotificationUI ( string captureType )
633+ {
634+ // Color scheme based on theme
635+ Color panelBackColor = _isDarkMode ? Color . FromArgb ( 40 , 40 , 40 ) : Color . FromArgb ( 250 , 250 , 250 ) ;
636+ Color textColor = _isDarkMode ? Color . White : Color . FromArgb ( 30 , 30 , 30 ) ;
637+ Color subtitleColor = _isDarkMode ? Color . FromArgb ( 180 , 180 , 180 ) : Color . FromArgb ( 100 , 100 , 100 ) ;
638+ Color closeButtonColor = _isDarkMode ? Color . FromArgb ( 150 , 150 , 150 ) : Color . FromArgb ( 100 , 100 , 100 ) ;
639+ Color closeButtonHoverColor = _isDarkMode ? Color . White : Color . Black ;
640+ Color thumbnailBorderColor = _isDarkMode ? Color . FromArgb ( 60 , 60 , 60 ) : Color . FromArgb ( 200 , 200 , 200 ) ;
641+
642+ // Main panel
643+ var panel = new Panel
644+ {
645+ Dock = DockStyle . Fill ,
646+ Padding = new Padding ( 10 ) ,
647+ BackColor = panelBackColor
648+ } ;
649+
650+ // Thumbnail image
651+ var thumbnail = new PictureBox
652+ {
653+ Location = new Point ( 10 , 10 ) ,
654+ Size = new Size ( 100 , 100 ) ,
655+ SizeMode = PictureBoxSizeMode . Zoom ,
656+ BorderStyle = BorderStyle . FixedSingle ,
657+ BackColor = thumbnailBorderColor
658+ } ;
659+
660+ try
661+ {
662+ using ( var img = Image . FromFile ( _imagePath ) )
663+ {
664+ thumbnail . Image = new Bitmap ( img , thumbnail . Size ) ;
665+ }
666+ }
667+ catch
668+ {
669+ thumbnail . BackColor = thumbnailBorderColor ;
670+ }
671+
672+ // Success icon/text
673+ var successLabel = new Label
674+ {
675+ Text = "FinalShot" ,
676+ Font = new Font ( "Segoe UI" , 12 , FontStyle . Bold ) ,
677+ ForeColor = Color . FromArgb ( 0 , 200 , 100 ) ,
678+ Location = new Point ( 120 , 10 ) ,
679+ Size = new Size ( 200 , 30 ) ,
680+ TextAlign = ContentAlignment . MiddleLeft
681+ } ;
682+
683+ // Title
684+ var titleLabel = new Label
685+ {
686+ Text = "Screenshot Captured!" ,
687+ Font = new Font ( "Segoe UI" , 11 , FontStyle . Bold ) ,
688+ ForeColor = textColor ,
689+ Location = new Point ( 120 , 45 ) ,
690+ Size = new Size ( 250 , 25 ) ,
691+ TextAlign = ContentAlignment . MiddleLeft
692+ } ;
693+
694+ // Subtitle
695+ var subtitleLabel = new Label
696+ {
697+ Text = captureType ,
698+ Font = new Font ( "Segoe UI" , 9 , FontStyle . Regular ) ,
699+ ForeColor = subtitleColor ,
700+ Location = new Point ( 120 , 70 ) ,
701+ Size = new Size ( 250 , 20 ) ,
702+ TextAlign = ContentAlignment . MiddleLeft
703+ } ;
704+
705+ // Close button
706+ var closeButton = new Label
707+ {
708+ Text = "✕" ,
709+ Font = new Font ( "Segoe UI" , 12 , FontStyle . Bold ) ,
710+ ForeColor = closeButtonColor ,
711+ Location = new Point ( NotificationWidth - 35 , 5 ) ,
712+ Size = new Size ( 25 , 25 ) ,
713+ TextAlign = ContentAlignment . MiddleCenter ,
714+ Cursor = Cursors . Hand
715+ } ;
716+ closeButton . Click += ( s , e ) => StartFadeOut ( ) ;
717+ closeButton . MouseEnter += ( s , e ) => closeButton . ForeColor = closeButtonHoverColor ;
718+ closeButton . MouseLeave += ( s , e ) => closeButton . ForeColor = closeButtonColor ;
719+
720+ // Add all controls
721+ panel . Controls . Add ( thumbnail ) ;
722+ panel . Controls . Add ( successLabel ) ;
723+ panel . Controls . Add ( titleLabel ) ;
724+ panel . Controls . Add ( subtitleLabel ) ;
725+ panel . Controls . Add ( closeButton ) ;
726+ Controls . Add ( panel ) ;
727+
728+ // Make labels click-through to panel
729+ foreach ( Control ctrl in panel . Controls )
730+ {
731+ if ( ctrl is Label && ctrl != closeButton )
732+ {
733+ ctrl . Click += ( s , e ) => StartFadeOut ( ) ;
734+ }
735+ }
736+ }
737+
738+ private void FadeIn ( )
739+ {
740+ var fadeInTimer = new Timer { Interval = 10 } ;
741+ double targetOpacity = 0.95 ;
742+ double step = 0.05 ;
743+
744+ fadeInTimer . Tick += ( s , e ) =>
745+ {
746+ _opacity += step ;
747+ if ( _opacity >= targetOpacity )
748+ {
749+ _opacity = targetOpacity ;
750+ Opacity = _opacity ;
751+ fadeInTimer . Stop ( ) ;
752+ fadeInTimer . Dispose ( ) ;
753+ }
754+ else
755+ {
756+ Opacity = _opacity ;
757+ }
758+ } ;
759+ fadeInTimer . Start ( ) ;
760+ }
761+
762+ private void StartFadeOut ( )
763+ {
764+ if ( _fadeOutTimer . Enabled ) return ;
765+ _autoCloseTimer . Stop ( ) ;
766+ _fadeOutTimer . Start ( ) ;
767+ }
768+
769+ private void FadeOutTick ( object sender , EventArgs e )
770+ {
771+ _opacity -= 0.05 ;
772+ if ( _opacity <= 0 )
773+ {
774+ _fadeOutTimer . Stop ( ) ;
775+ Close ( ) ;
776+ }
777+ else
778+ {
779+ Opacity = _opacity ;
780+ }
781+ }
782+
783+ protected override void Dispose ( bool disposing )
784+ {
785+ if ( disposing )
786+ {
787+ _autoCloseTimer ? . Dispose ( ) ;
788+ _fadeOutTimer ? . Dispose ( ) ;
789+ }
790+ base . Dispose ( disposing ) ;
791+ }
792+ }
793+
473794 public static class Plugin
474795 {
475796 [ DllExport ]
0 commit comments