Personalized information panel

Share your advanced PureBasic knowledge/code with the community.
User avatar
Jacobus
Enthusiast
Enthusiast
Posts: 146
Joined: Wed Nov 16, 2005 7:51 pm
Location: France
Contact:

Personalized information panel

Post 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)


Last edited by Jacobus on Fri Oct 03, 2025 3:39 pm, edited 7 times in total.
PureBasicien tu es, PureBasicien tu resteras.
User avatar
idle
Always Here
Always Here
Posts: 5965
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Personalized information panel

Post by idle »

That's good, thanks for sharing
User avatar
Jacobus
Enthusiast
Enthusiast
Posts: 146
Joined: Wed Nov 16, 2005 7:51 pm
Location: France
Contact:

Re: Personalized information panel

Post 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.
PureBasicien tu es, PureBasicien tu resteras.
Denis
Enthusiast
Enthusiast
Posts: 779
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: Personalized information panel

Post by Denis »

Slt Jacobus,

i like that :D
(pas mal ...)
A+
Denis
User avatar
Jacobus
Enthusiast
Enthusiast
Posts: 146
Joined: Wed Nov 16, 2005 7:51 pm
Location: France
Contact:

Re: Personalized information panel

Post 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. :)
PureBasicien tu es, PureBasicien tu resteras.
User avatar
Jacobus
Enthusiast
Enthusiast
Posts: 146
Joined: Wed Nov 16, 2005 7:51 pm
Location: France
Contact:

Re: Personalized information panel

Post 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.
PureBasicien tu es, PureBasicien tu resteras.
Axolotl
Addict
Addict
Posts: 865
Joined: Wed Dec 31, 2008 3:36 pm

Re: Personalized information panel

Post 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 
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
Jacobus
Enthusiast
Enthusiast
Posts: 146
Joined: Wed Nov 16, 2005 7:51 pm
Location: France
Contact:

Re: Personalized information panel

Post 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
Last edited by Jacobus on Fri Oct 03, 2025 3:40 pm, edited 1 time in total.
PureBasicien tu es, PureBasicien tu resteras.
Axolotl
Addict
Addict
Posts: 865
Joined: Wed Dec 31, 2008 3:36 pm

Re: Personalized information panel

Post 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:
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
Jacobus
Enthusiast
Enthusiast
Posts: 146
Joined: Wed Nov 16, 2005 7:51 pm
Location: France
Contact:

Re: Personalized information panel

Post 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.
PureBasicien tu es, PureBasicien tu resteras.
Axolotl
Addict
Addict
Posts: 865
Joined: Wed Dec 31, 2008 3:36 pm

Re: Personalized information panel

Post 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.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
Post Reply