Page 1 of 3

Simple non-modal custom dialog box closing automatically

Posted: Mon Sep 21, 2015 6:59 pm
by Blue
:: coded in PB 5.10, updated for PB 5.31 ::
:: 99% cross-platform (1% : see line 109) ::

It is quite easy to create a
custom non-modal dialog box that closes automatically
when the user clicks outside of it

The secret is to simply watch for #WM_LBUTTONUP and #WM_RBUTTONUP events occuring in the parent window.

Below is a full example. Wordy, so things are clearly demontrated 8), but still short :D
Lines 109-118 of the source code is where the concept is applied.

This may be of interest to newcomers to PureBasic or to Windows programming.

Code: Select all

;- ***** ***** ***** ***** ***** ***** *****
;- Blue - September 2013
;- --- --- --- --- --- --- --- --- --- ---
;- Child Window that closes 
;- ... when click detected outside of it
;- ***** ***** ***** ***** ***** ***** *****
; the child (technically correct : secondary) window has its own event loop

EnableExplicit

;{ constants
;- constants
;- .... for convenience
#enAttente = " Just waiting..."

#text = #PB_Gadget_FrontColor
#back = #PB_Gadget_BackColor

#colourF = $F4F7F9  ; child window background
#colourB = $FFFFFF  ; text box background 
#colourT = $FF0000  ; text
#colourL = $0779F8  ; labels

Enumeration   ; fenĂȘtres
  #main_WINDOW
  #child_WINDOW
EndEnumeration

;- .... for gadgets
Enumeration
  #cmdOK
  #cmdANNULER
  #infoLABEL
  #infoBOX
  
  #F2_cmdOK
  #F2_cmd2
  #F2_cmd3

  #F2_cadre1
  #F2_cadre2
  #F2_cadreLABEL
EndEnumeration
;}

;- --- --- --- --- --- --- --- --- --- --- --- ---
Procedure Child_Window()
  Define winH = 120
  Define winW = WindowWidth(#main_WINDOW) * 0.66


  If 0 = OpenWindow(#child_WINDOW, 0, 0, winW, winH, "Busy Body", 
                    #PB_Window_BorderLess | #PB_Window_WindowCentered, 
                    windowID(#main_WINDOW))
    
    SetGadgetText(#infoBox, " Child window could not be created")
    ProcedureReturn 
  EndIf
  SetWindowColor(#child_WINDOW,#colourF)
  SetGadgetText(#infoBox, " Click anywhere outside the child window to close it. ")

  ;{ gadgets hiding here
  ;- .... gadgets
  Define gadget, gX,gY, gW,gH
  
  gX = 1
  gY = 1
  gW = winW - (gX * 2)
  gH = winH - (gY * 2)
  FrameGadget(#F2_cadre1, gX, gY, gW,gH, "", #PB_Frame_Flat)
  
  gX + 8
  gY + 8
  gW = winW - (gX * 2)
  gH = winH - (gY * 2)
  FrameGadget(#F2_cadre2, gX,gY, gW,gH, "")

  gadget = #F2_cadreLABEL
    gX + 8
    gW = 80
    gH = 18
    TextGadget(gadget, gX,gY, gW,gH, "Child window",#PB_Text_Center)
    SetGadgetColor(gadget, #back,#colourF)
    SetGadgetColor(gadget, #text, #colourL)

  #dX = 12
  gW = 100
  gX + #dX
  gY + 24
  gH = 25
  ButtonGadget(#F2_cmd2, gX,gY, gW,gH, "Something")

  gX + gW + #dX
  ButtonGadget(#F2_cmd3, gX,gY, gW,gH, "Something else")

  gW = 70
  gH = 32
  gX = GadgetWidth(#F2_cadre2) - gW
  gY = GadgetHeight(#F2_cadre2) - gH
  ButtonGadget(#F2_cmdOK, gX,gY, gW,gH, "OK", #PB_Button_Default)
  ;}

  ;- .... event loop
  Define event, gadget, window
  Repeat
    event = WaitWindowEvent()
    window = EventWindow()

    If window <> #child_WINDOW           ;- ...  [109] >> catching outside events
    ; events occuring OUTSIDE our dialog box are intercepted here, 
    ; therefore preempting actions tied to gadgets in the main window
      SetActiveWindow(#child_WINDOW)
      Select event
       ;Case #PB_Event_CloseWindow  : Break                      ; uncomment to have the main close button close the child window
        Case #WM_LBUTTONUP, #WM_RBUTTONUP : Break                ; mouse clicked : the loop's work is done. (Windows OS only)
       ;Case #PB_Event_LeftClick, #PB_Event_RightClick : Break   ; cross-platform : comment above line (won't catch clicks on buttons)
      EndSelect
     ;Debug "event = "+ Str(event) + "  gadget = "+ Str(gadget) 
      Continue     ; disregard everything
    EndIf

    Select event
      Case #PB_Event_Gadget
        gadget = EventGadget()
        Select gadget
          Case #F2_cmd2 : SetGadgetText(#infoBox,"The child window is busy doing something...")
          Case #F2_cmd3 : SetGadgetText(#infoBox,"The child window is doing something else...")
          Case #F2_cmdOK : Break
          Default        : SetGadgetText(#infoBox,"gadget #" + Str(gadget))
        EndSelect
    EndSelect
  ForEver

  CloseWindow(#child_WINDOW)
  SetGadgetText(#infoBox, #enAttente)
EndProcedure  ; _Child_Window_

Procedure Main_Window()
  Define winH = 280
  Define winW = 400
  If 0 = OpenWindow(#main_WINDOW, 0, 0, winW, winH, "Main Window", 
                    #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    End
  EndIf

  ;- .... gadgets
  Define gadget, gX,gY, gW,gH
  #marge = 10

  gX = #marge
  gY = #marge
  gW = 130
  gH = 25
  ButtonGadget(#cmdOK, gX,gY, gW,gH, "Open child window", #PB_Button_Default)

  gH = 36
  gW = 90
  gX = winW - gW - #marge
  gY = winH - gH - #marge
  ButtonGadget(#cmdANNULER, gX,gY, gW,gH, "Quit") 

  gadget = #infoLABEL
    gX = #marge
    gW = 90
    gH = 22
    gY - gH 
    TextGadget(gadget, gX,gY, gW,gH, "INFORMATION :")
    SetGadgetColor(gadget, #text,#colourL)

  gadget = #infoBOX
    gX + gW
    gW = winW - gX - #marge
    gH = 24
    gY - 5
    TextGadget(gadget, gX,gY, gW,gH, "",#PB_Text_Border)
    SetGadgetColor(gadget, #back, #colourB)
    SetGadgetColor(gadget, #text, #colourT)
EndProcedure  ; _Main_Window_

;- --- --- --- --- --- --- --- --- --- --- --- ---

;- main event loop
Define event, evType, gadget
Define fenetre

Main_Window()
SetGadgetText(#infoBox,#enAttente)

Repeat
  event = WaitWindowEvent()
  
  Select event
    Case #PB_Event_CloseWindow : Break
    Case #PB_Event_Gadget :
      gadget = EventGadget()
      Select gadget
        Case #cmdANNULER : Break
        Case #cmdOK      : Child_Window()
      EndSelect
  EndSelect
  
ForEver

End
I hope someone will find it useful.

[UpDate]
Changes made (colour constants and 2 events) to render code cross-platform

Re: Simple way to create custom non-modal dialog box

Posted: Mon Sep 21, 2015 7:06 pm
by ts-soft
Nice example, but
Blue wrote::: cross-platform (I think!) ::
No, no :wink:
Some WinAPI examples:
#WM_LBUTTONUP, #WM_RBUTTONUP, #Red and so on!

Re: Simple way to create custom non-modal dialog box

Posted: Mon Sep 21, 2015 7:24 pm
by Blue
ts-soft wrote:Nice example, but
Blue wrote::: cross-platform (I think!) ::
No, no :wink:
Some WinAPI examples:
#WM_LBUTTONUP, #WM_RBUTTONUP, #Red and so on!
Thank you very much, ts-soft, for catching this so quickly.

Gosh, darn it, I always thought of those as PB constants !
How foolish !

I think replacing #Red by its Hex value will make that one cross-platform (right ? :? ), but i have no idea what should replace the #WM_LBUTTONUP, #WM_RBUTTONUP constants. Have you ?

I know #PB_Event_LeftClick and #PB_Event_RightClick won't work, since those events won't fire on all gadgets (buttons, for instance).

Anyway, i'll try a few things and correct the presentation of my topic if I can't find a cross-platform solution.

Re: Simple non-modal custom dialog box closing automatically

Posted: Mon Sep 21, 2015 7:51 pm
by RASHAD
Hi Blue
Replace :

Code: Select all

Case #WM_LBUTTONUP, #WM_RBUTTONUP
 
With :

Code: Select all

Case #PB_Event_LeftClick,#PB_Event_RightClick
Replace #Red,#Yellow,#Blue with HEX eq. etc $0000FF
And please use Light Gray or Light Yellow instead :P

Re: Simple non-modal custom dialog box closing automatically

Posted: Mon Sep 21, 2015 8:12 pm
by Blue
RASHAD wrote:Hi Blue
Replace :

Code: Select all

Case #WM_LBUTTONUP, #WM_RBUTTONUP
 
With :

Code: Select all

Case #PB_Event_LeftClick,#PB_Event_RightClick
Replace #Red,#Yellow,#Blue with HEX eq. etc $0000FF
And please use Light Gray or Light Yellow instead :P
Well... hello Mr Windows Magic.
Exactly what i was planning to do.

And please be specific: which colour do you wish me to replace with Light Gray :( exactly ?
Not my beloved Ultra Bright Yellow, i hope. It matches the colour of my teeth so well.
Besides, people curious enough to try this code are free to choose whichever colour they please.
It's only a matter of changing a single constant.

As for #WM_LBUTTONUP and #WM_RBUTTONUP, i was using them because they reported a click on anything in the other window. #PB_Event_LeftClick and #PB_Event_RightClick don't report clicks on the the button gadgets from the main window.

Try the code with #PB_Event_LeftClick and #PB_Event_RightClick, Rashad, and you'll see.
Let me know if you think of an alternative.

Thanks for the useful input, as always.

Re: Simple non-modal custom dialog box closing automatically

Posted: Mon Sep 21, 2015 10:52 pm
by Blue
I changed the code to make it *** MOSTLY *** cross-platform compatible.

colour constants : Hex values to replace Microsoft's constants
events : replaced #WM_LBUTTONUP by #PB_Event_LeftClick, and #WM_RBUTTONUP by #PB_Event_RightClick

in Windows, using the PB constants reduces the functionality of the code, since clicks on ButtonGadgets are not detected. So, in Windows, it's better to keep using the #WM_xxx symbolic constants.

PS >> ... plus i changed the yellow background to a yucky beige to accomodate Rashad's poor eyesight. :cry:

Re: Simple non-modal custom dialog box closing automatically

Posted: Mon Sep 21, 2015 11:29 pm
by RASHAD
Hi Blue
I am not sure about all your aims but I just tried my best to get as much as I can :)
I hope I am around not far

Remember PureBasic rule #1
Only one event loop

Code: Select all

Procedure move()
 ResizeWindow(1,WindowX(0)+150,WindowY(0)+125,300,150)
EndProcedure

LoadFont(0,"Georgia",14)

OpenWindow(0,0,0,600,400,"Win #1",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ButtonGadget(0,10,10,100,20,"Open Window")
ButtonGadget(1,530,350,60,30,"QUIT")
TextGadget(2,10,360,200,30,"This is a test....",#PB_Text_Border)
SetGadgetFont(2,FontID(0))
OpenWindow(1,0,0,300,150,"Win #2",#PB_Window_BorderLess|#PB_Window_WindowCentered| #PB_Window_Invisible,WindowID(0))
SetWindowColor(1,$0000FF)
UseGadgetList(WindowID(1))
ContainerGadget(10,0,0,300,150,#PB_Container_Raised)
  SetGadgetColor(10,#PB_Gadget_BackColor,$DFFEFC)
  FrameGadget(11,15,15,260,110,"Child Window");,#PB_Frame_Single)
  ButtonGadget(12,210,90,60,30,"OK")
CloseGadgetList()

BindEvent(#PB_Event_MoveWindow,@Move())

Repeat
Select WaitWindowEvent()
;    Case #PB_Event_CloseWindow
;       Quit = 1
   Case #PB_Event_LeftClick,#PB_Event_RightClick
       If GetActiveWindow() = 0
          HideWindow(1,1)
          Flag = 0
       EndIf
          
   Case #PB_Event_Gadget
      Select EventGadget()
         Case 0
            If Flag = 0
              HideWindow(1,0)
              Flag = 1
            Else
              HideWindow(1,1)
              Flag = 0
            EndIf
            
         Case 1
            If Flag = 1
              HideWindow(1,1)
              Flag = 0
            Else
              Break
            EndIf
            
         Case 12
            HideWindow(1,1)
            Flag = 0
      EndSelect
EndSelect
Until Quit = 1

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 2:07 am
by Blue
RASHAD wrote:Hi Blue
I am not sure about all your aims but I just tried my best to get as much as I can :)
My aim is exactly as spelled out in the Topic title , Rashad : showing off a method to make a secondary dialog box (it can be a Help Sticky displayed temporarily, an input dialog, an 'About..." box, whatever...) that quietly goes away when the user clicks outside its real estate.

I believe my code does just that, although not, as i originally claimed, in a 100% cross-platform way. So that had to be set straight right away.
And I put this demo in this section of the forum, because it's called Tricks 'n' Tips.
That's all.

I didn't realize 'Only one event loop' was a golden rule. But, by breaking it to enclose my secondary window and all its attending code into a procedure, i have considerably lightened the main loop in my current 5 600 lines project. In particular, it has made debugging, and navigating the logic of the program, a lot easier. So i'm inclined to move in that direction for a lot of my self-vanishing secondary service windows.

Your method -- very clever BTW, and showing some good ideas definitely worth stealing :shock: -- obviously works too, but presents a very different concept, requiring that 2 windows be kept alive, which may or may not be viable in all circumstances. Plus, of course, there's the need to check which window is alive for EVERY single gadget in window #1. :!: This is leading into a very interesting discussion. However, i believe this particular section of the forum is not the proper venue for it. Perhaps the "Coding Questions' section ? :wink:

Anyway, I thought the golden rule was 'one loop if it's modal, separate loops if not', or is it the other way around ? :oops:
I'll have to ask Danilo or ts-soft or maybe Rashad again... :?

Cheers.

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 2:21 am
by RASHAD
I knew that you will get angry :D
But I could not help it because I scratched my head for a trick to come over the mouse event but no way (in case of cross platform)
Just forget about cross platform and you are safe :wink:

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 2:33 am
by Blue
RASHAD wrote:I knew that you will get angry :D
But I could not help it because I scratched my head for a trick to come over the mouse event but no way (in case of cross platform)
Just forget about cross platform and you are safe :wink:
Glad you shoved a smiley at the end of that first sentence, Rashad. 8)
'Cause angry i am NOT.
I always welcome your 2 pennies of Tips'N'Tricks and enjoy exchanging with you.
I like it when you start scratching your head : it's a sure sign of good things to come.

So keep scratching, Rashad, and if you need a scalp doctor, i know a good one in Luxor :lol:

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 2:43 am
by RASHAD
That doctor you mentioned I know him :P
I wish you meet him one day to get rid of you :mrgreen:

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 3:57 am
by TI-994A
Blue wrote:I didn't realize 'Only one event loop' was a golden rule.
For what it's worth, it's not; if structured correctly. :wink:

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 12:12 pm
by infratec
Hi,

Crossplatform (I hope)

Code: Select all

Procedure CloseAboutWindow()
  CloseWindow(EventWindow())
EndProcedure

Procedure About(Timeout.i=0)
  Win = OpenWindow(#PB_Any, 0, 0, 200, 100, "About", #PB_Window_Tool|#PB_Window_ScreenCentered)
  TextGadget(#PB_Any, 10, 10, 100, 20, "About")
  BindEvent(#PB_Event_DeactivateWindow, @CloseAboutWindow(), Win)
  If Timeout
    AddWindowTimer(Win, 1, Timeout)
    BindEvent(#PB_Event_Timer, @CloseAboutWindow(), Win)
  EndIf
EndProcedure


OpenWindow(0, 0, 0, 400, 300, "Test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)

ButtonGadget(0, 10, 10, 50, 20, "About")

Repeat
 
  Event = WaitWindowEvent()
 
  If Event = #PB_Event_Gadget
    About(3000)
  EndIf
 
Until Event = #PB_Event_CloseWindow
Look at page 3 for a version which works with #PB_Window_WindowCentered.

Bernd

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 1:50 pm
by Vera
Hello Blue,
Maybe let time decide ? ~ Image

Code: Select all

Global about, TimeRuns

Procedure About() 
  TimeRuns = OpenWindow(#PB_Any, 0, 0, 200, 100, "About", #PB_Window_BorderLess | #PB_Window_ScreenCentered)
  SetWindowColor(TimeRuns, $8A1517)
  tex=TextGadget(#PB_Any, 10, 20, 150, 22, " Time is on your side", #PB_Text_Border|#PB_Text_Center)
  SetGadgetColor(tex, #PB_Gadget_FrontColor, $00CCFF) : SetGadgetColor(tex, #PB_Gadget_BackColor, $682651)
  SetActiveWindow(TimeRuns)
  AddWindowTimer(TimeRuns, 123, 1000) 
  about = 1 
EndProcedure

OpenWindow(0, 0, 0, 400, 300, "Test", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ButtonGadget(0, 10, 20, 150, 22, "About Time")

Repeat
  Event = WaitWindowEvent()
  
  If Event = #PB_Event_Timer And about And GetActiveWindow() > -1 And GetActiveWindow() <> TimeRuns
     RemoveWindowTimer(TimeRuns, 123) : CloseWindow(TimeRuns) : about = 0
  EndIf
  
  If Event = #PB_Event_Gadget
    About()
  EndIf
 
Until Event = #PB_Event_CloseWindow

Re: Simple non-modal custom dialog box closing automatically

Posted: Tue Sep 22, 2015 3:02 pm
by RASHAD
@Bernd
@Vera
You are dead meat :mrgreen:
Just wait for Blue