Page 1 of 1

Personalized information panel

Posted: Sun Sep 28, 2025 5:49 pm
by Jacobus
Hello everyone,
I'm sharing my personal panel used to display information with my apps. This may be useful if you also want to display something, such as an appointment, a photo, important information, or even a simple error or warning message.
You can adapt it to your needs, your colors, and your preferences. The outline is transparent, with no title, icon, or buttons. :)

Code: Select all

;-=====================================
; Personalized information panel
; By Jacobus on 09/28/2025
; PB 6.21 (x86) and (x64) and Windows 10 & 11

; Possibilities:
; (enable DPI in the compilation options)
; - Position the window on the screen, in the center or bottom right above the notification area for example
; - Transparent panel outline (adjustable transparency)
; - Icons taken from Shell32.dll, so no external file
; - Adjustable window width and height
; - Modify title and text (can be replaced with an image)
; - Modifiable text and background color
; - Panel display duration via timer with automatic closing if desired
; - Close or not in fade mode
; - Option: If the timer is enabled -> the close button is hidden 
;                                   -> And another timer is started To count down before closing the panel

;-=====================================

;-DPI
CompilerIf #PB_Compiler_DPIAware = 1
  
  Global dpix.d = DesktopResolutionX()
  Global dpiy.d = DesktopResolutionY()
  
CompilerElse
  
  Global dpix.d = 1
  Global dpiy.d = 1
  
CompilerEndIf

Enumeration
  #WINMAIN
  #TXTCOUNT
  #WINSECOND
  #CONTAINERMAIN
  #IMGTITLE
  #TXTTITLE
  #TXTINFO
  #BTNCLOSE
EndEnumeration

Procedure SetWinTransparency(hWnd, Visibility)
  SetWindowLongPtr_(hWnd, #GWL_EXSTYLE, GetWindowLongPtr_(hWnd, #GWL_EXSTYLE) | #WS_EX_LAYERED) 
  
  ; 1/ 0 .. 100 % 
  ; SetLayeredWindowAttributes_(hWnd, 0, (Visibility * 255) / 100, #LWA_ALPHA) 
  
  ; 2/ 1 .. 255
  SetLayeredWindowAttributes_(hWnd, 0, Visibility, #LWA_ALPHA) 
EndProcedure 

Global Font0 = LoadFont(0, "Bahnschrift Light SemiCondensed", 10, #PB_Font_Bold|#PB_Font_HighQuality)
Global Font1 = LoadFont(1, "Courier New", 10, #PB_Font_Bold|#PB_Font_HighQuality)
Global TTime.i = 0

Procedure Panneau_Info(PosX.i, PosY.i, WinWidth.i, WinHeight.i, TitleColor.i, TextBackColor.i, TextFrontColor.i, Title$, Textinfo$, FlagPos.i, Time = 0, Fade = 0)
  ;{ Explications:
; PosX            = Window x position
; PosY            = Window y position
; WinWidth        = Window width
; WinHeight       = Window height
; TitleColor      = Info container title color
; TextBackColor   = Info textgadget background color
; TextFrontColor  = Info textgadget text color
; Title$          = Info panel title
; Textinfo$       = Info text to be displayed
; FlagPos         = #PB_Window_BorderLess|#PB_Window_WindowCentered (default)
;                   #PB_Window_BorderLess (to display the panel somewhere other than the center of the screen by adjusting PosX and PosY)
; Time            = Specify a value in ms (e.g., 3000 for 3 seconds) if you want it to close automatically, otherwise leave it at 0
; Fade            = Set to 1 for a fade-in close, otherwise leave it blank.
;}
  
  ; Creation of the window which will serve as a border with transparency effect
  If OpenWindow(#WINMAIN, PosX, PosY, WinWidth*dpix, WinHeight*dpiy, "", FlagPos) 
    SetWindowColor(#WINMAIN, RGB(237, 242, 244))

   ; Choose method
    SetWinTransparency(WindowID(#WINMAIN), 40) ; Border transparency (1 to 255)
   ; SetWinTransparency(WindowID(#WINMAIN), 20) ; Border transparency  (0 -> 100 %)
    
;     ; a countdown to closing is displayed in the transparent border at the bottom if the timer is activated
;    ; To position it at the transparent bottom of the window, uncomment here and comment at the current position with the close button.
;     TextGadget(#TXTCOUNT, 20, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-25, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-40, 20, "", #PB_Text_Center)
;     SetGadgetFont(#TXTCOUNT,Font0)
;     SetGadgetColor(#TXTCOUNT, #PB_Gadget_BackColor, RGB(237, 242, 244))
;     SetGadgetColor(#TXTCOUNT, #PB_Gadget_FrontColor, TitleColor)
;     HideGadget(#TXTCOUNT, #True)
    
    ; Creating the secondary window that will receive the gadgets we want
    If OpenWindow(#WINSECOND, 0, 0, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-60, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-60, "Info", #PB_Window_BorderLess|#PB_Window_WindowCentered, WindowID(#WINMAIN)) ; must always be in the center of the main window
      ;{
      ContainerGadget(#CONTAINERMAIN, 0, 0, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-60, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-60)
      SetGadgetColor(#CONTAINERMAIN, #PB_Gadget_BackColor, RGB(81, 95, 99))
      
      ImageGadget(#IMGTITLE, 20, 10, 32, 32, ExtractIcon_(GetModuleHandle_(#Null), "Shell32.dll",221));<-- use of icon #221 found in Shell32.dll (Windows11)
      
      TextGadget(#TXTTITLE, 60, 20, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-140, 20, Title$) 
      SetGadgetFont(#TXTTITLE,Font0) 
      SetGadgetColor(#TXTTITLE, #PB_Gadget_BackColor, RGB(81, 95, 99))
      SetGadgetColor(#TXTTITLE, #PB_Gadget_FrontColor, TitleColor)
      
      TextGadget(#TXTINFO, 20, 50, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-100, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-150, Textinfo$)
      SetGadgetFont(#TXTINFO,Font1)
      SetGadgetColor(#TXTINFO, #PB_Gadget_BackColor, TextBackColor)
      SetGadgetColor(#TXTINFO, #PB_Gadget_FrontColor, TextFrontColor)
      
      ;===============================================
      ;-Button of your choice: Text or image or image+text
      
      ;ButtonGadget(#BTNCLOSE, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-180, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-95, 100, 25, "Fermer")                                                       ; text button 
      
      ;ButtonImageGadget(#BTNCLOSE, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-100, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-95, 32, 32, ExtractIcon_(GetModuleHandle_(#Null), "Shell32.dll", 215))  ; image button
      
      ButtonGadget(#BTNCLOSE,WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-180, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-95, 100, 27,Space(2)+"Fermer")                                                 ; image button + text (all 3 lines)   
      SetWindowLongPtr_(GadgetID(#BTNCLOSE), #GWL_STYLE, GetWindowLongPtr_(GadgetID(#BTNCLOSE), #GWL_STYLE) |#BS_LEFT)
      SendMessage_(GadgetID(#BTNCLOSE), #BM_SETIMAGE, #IMAGE_ICON, ExtractIcon_(GetModuleHandle_(#Null), "Shell32.dll", 215)) ;<-- use of icon #215 found in Shell32.dll (Windows11)
      
      GadgetToolTip(#BTNCLOSE,"Close the information panel") 
      
      ; a countdown to closing is displayed in the transparent border at the bottom if the timer is activated
      TextGadget(#TXTCOUNT, 20, WindowHeight(#WINMAIN, #PB_Window_FrameCoordinate)-85, WindowWidth(#WINMAIN, #PB_Window_FrameCoordinate)-100, 20, "", #PB_Text_Right)
      SetGadgetFont(#TXTCOUNT,Font0)
      SetGadgetColor(#TXTCOUNT, #PB_Gadget_BackColor, TextBackColor)
      SetGadgetColor(#TXTCOUNT, #PB_Gadget_FrontColor, TitleColor)
      HideGadget(#TXTCOUNT, #True)
  
      CloseGadgetList()
      
      ; If we want the panel to disappear by itself, we indicate the time in the procedure arguments.
      If Time > 0 ; si on veut que le panneau s'efface tout seul on indique le temps dans les arguments de la procédure
        AddWindowTimer(#WINMAIN, 120, Time)
        HideGadget(#BTNCLOSE, #True) ; we hide the button that is no longer needed/on cache le bouton qui n'est plus nécessaire
        ; Timer for the countdown
        TTime = Time/1000
        SetGadgetText(#TXTCOUNT, "This message will self-destruct in "+Str(TTime)+" seconds")
        HideGadget(#TXTCOUNT, #False); on affiche le compte à rebours du timer 
        AddWindowTimer(#WINMAIN, 121, 1000)
      EndIf
      
      Repeat 
        Event = WaitWindowEvent() 
        Select Event 
         ; if a timer was specified
          Case #PB_Event_Timer ;{
            Select EventTimer()
              Case 120
                quit = 1
                Event = #PB_Event_CloseWindow
                If Fade = 1
                  AnimateWindow_(WindowID(#WINSECOND),500,#AW_BLEND|#AW_HIDE)
                EndIf 
                
              Case 121               
                TTime = TTime - 1
                SetGadgetText(#TXTCOUNT, "This message will self-destruct in "+Str(TTime)+" seconds")
            EndSelect
            ;}
          Case #PB_Event_Gadget ;{         
            Select EventGadget()
              Case #BTNCLOSE
                quit = 1
                Event = #PB_Event_CloseWindow
                If Fade = 1
                  AnimateWindow_(WindowID(#WINSECOND),500,#AW_BLEND|#AW_HIDE) ; for a smooth close in fade mode
                EndIf 
                End 
            EndSelect
            ;}
        EndSelect       
      Until Event = #PB_Event_CloseWindow 
      ;}
    EndIf 
      
    Repeat 
      Event = WaitWindowEvent()      
    Until quit = 1
    If Fade = 1
      AnimateWindow_(WindowID(#WINMAIN),500,#AW_BLEND|#AW_HIDE) ; for a smooth close in fade mode
    EndIf 
    End 
  EndIf
EndProcedure

If ExamineDesktops()<>0
Global DW = DesktopWidth(0)
Global DH = DesktopHeight(0)

;-My screen resolution, check yours
;{
; Debug "DesktopWidth(0) = " +Str(DW) + "    DesktopScaledX(DW) = " +DesktopScaledX(DW)
; Debug "DesktopHeight(0) = "+Str(DH) + "    DesktopScaledY(DH) = " +DesktopScaledY(DH)
; Debug "========================="
; Debug "Divisé par le dpi = " +Str(DW/dpix) +" x "+Str(DH/dpiy)  ; 2560 x 1440
; Debug "========================="
; Debug "posx = "+ Str((DW/dpix)-(300*dpix)) ; 300 = PosX (example 2)
; Debug "posy = "+ Str((DH/dpiy)-(200*dpiy)) ; 200 = PosY (for a 150 window add 50 for taskbar height)

;Mes Résultats:
; DesktopWidth(0)  = 3840    DesktopScaledX(DW) = 5760
; DesktopHeight(0) = 2160    DesktopScaledY(DH) = 3240
; =========================
; Divisé par le dpi = 2560 x 1440 (Divided by dpi)
; =========================
; posx = 2110
; posy = 1140
;}

EndIf 

;-=====================================
;-EXAMPLES OF USE

Title$ = "DISPLAY TEST FOR THIS INFO PANEL..."
Textinfo$ = "The transparency level should be between 1 (complete transparency) and 255 (opaque) for the outline of the first window. Level at 50 for this example."

; Uncomment the panel you want to see

;Example 1 / to display the panel in the center of the screen (so we don't worry about the DPI for POSX and PosY) and click on the button to close
;Panneau_Info(0, 0, 800, 600, RGB(242, 175, 40), RGB(54, 58, 66), RGB(245, 219, 168), Title$, Textinfo$, #PB_Window_BorderLess|#PB_Window_ScreenCentered, 0, 1) ; grosse pancarte...

;Example 2 / for display at the bottom right (DPI of POsX and PosY to be taken into consideration for large screens) and click on the button to close
;Panneau_Info((DW/dpix)-(300*dpix), (DH/dpiy)-(200*dpiy), 300, 150, RGB(226, 236, 239), RGB(81, 95, 99), RGB(226, 236, 239), Title$, Textinfo$, #PB_Window_BorderLess)

;Example 3 / for display in the bottom right for a set duration
;with a timer, the panel closes after 5 seconds in fade mode
;Panneau_Info((DW/dpix)-(500*dpix), (DH/dpiy)-(350*dpiy), 500, 300, RGB(226, 236, 239), RGB(81, 95, 99), RGB(226, 236, 239), Title$, Textinfo$, #PB_Window_BorderLess, 5000, 1)

;Example 4 / to display the panel in the center of the screen with timer and 7-second countdown
Panneau_Info(0, 0, 500, 300, RGB(140, 215, 34), RGB(81, 95, 99), RGB(226, 236, 239), Title$, Textinfo$, #PB_Window_BorderLess|#PB_Window_ScreenCentered, 7000, 1)



Re: Personalized information panel

Posted: Mon Sep 29, 2025 12:41 am
by idle
That's good, thanks for sharing

Re: Personalized information panel

Posted: Mon Sep 29, 2025 7:50 am
by Jacobus
Thanks for the feedback :)
Moon phases in the northern hemisphere by PhM, in French for example : https://www.purebasic.fr/french/viewtopic.php?t=19408
Image

Small modification in the initial code where I added modifiable background colors, notably at the title level.

Re: Personalized information panel

Posted: Mon Sep 29, 2025 11:27 am
by Denis
Slt Jacobus,

i like that :D
(pas mal ...)

Re: Personalized information panel

Posted: Mon Sep 29, 2025 1:38 pm
by Jacobus
Hey, salut Denis! :D
Glad to know you're still active in the PB world.
Just a little tool for more fun and personal displays. :)

Re: Personalized information panel

Posted: Mon Sep 29, 2025 5:35 pm
by Jacobus
Added a second timer to display a countdown before the panel closes in case the user prefers automatic closing rather than a button, and also to know how long the panel will be displayed.
Code updated in the first post.

Re: Personalized information panel

Posted: Wed Oct 01, 2025 4:38 pm
by Axolotl
Nice, thanks for sharing.

Just a small hint: your reference links about Windows Transparency are very old.

Code: Select all

 
; acc. to M$ 
; To write code that is compatible with both 32-bit and 64-bit versions of Windows, use SetWindowLongPtr. 
; Replace   SetWindowLong_()  with  SetWindowLongPtr_() 
; and GetWindowLong_() with GetWindowLongPtr_() 

; Or use my favorite implementation instead: 
Procedure SetWinTransparency(hWnd, Visibility) ;: 0 .. 100 % 
  SetWindowLongPtr_(hWnd, #GWL_EXSTYLE, GetWindowLongPtr_(hWnd, #GWL_EXSTYLE) | #WS_EX_LAYERED) 
  SetLayeredWindowAttributes_(hWnd, 0, (Visibility * 255) / 100, #LWA_ALPHA) 
EndProcedure 

Re: Personalized information panel

Posted: Wed Oct 01, 2025 6:20 pm
by Jacobus
Yes indeed, old code in my archives but still functional. Thank you for your updated code, but your calculation changes the perception of transparency because it is based on the percentage. So this leaves us two possibilities to obtain transparency. We must adapt the value of "Visibility"
I modified the code in the first post accordingly. :)

Code: Select all

Procedure SetWinTransparency(hWnd, Visibility)
  SetWindowLongPtr_(hWnd, #GWL_EXSTYLE, GetWindowLongPtr_(hWnd, #GWL_EXSTYLE) | #WS_EX_LAYERED) 
  
  ; 1/ 0 .. 100 % 
  ; SetLayeredWindowAttributes_(hWnd, 0, (Visibility * 255) / 100, #LWA_ALPHA) 
  
  ; 2/ 1 .. 255
  SetLayeredWindowAttributes_(hWnd, 0, Visibility, #LWA_ALPHA) 
EndProcedure

Re: Personalized information panel

Posted: Fri Oct 03, 2025 11:15 am
by Axolotl
Well, I like the 0 to 100% value better than 0 to 255, but it is up to you of cause.

One more thing I forgot to mention: you should always check the data type (especially for Windows handles). Older codes often use the extension .l, which can cause problems on 64-bit systems.

BTW: Now your procedure has used a hard-coded 40 instead of the value passed as a parameter. :wink:

Re: Personalized information panel

Posted: Fri Oct 03, 2025 3:48 pm
by Jacobus
I also prefer percentages; it's easier to understand, but I also like having a choice, which is why I think it's good to offer two options. Depending on each person's taste.

I didn't use .l, Everything is in interger and it works in 64-bit.

Good point about the hardcoded value in the procedure; it's been fixed.

Re: Personalized information panel

Posted: Fri Oct 03, 2025 4:07 pm
by Axolotl
Jacobus wrote: Fri Oct 03, 2025 3:48 pm .....
I didn't use .l, Everything is in interger and it works in 64-bit.
.....
Honestly, you did in your inital code and you are right. Your updated code is 64-bit ready.