EventHoverGadget() - How to check if mouse is over a gadget!

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Here is a platform independent solution for mouseover / mouse hover,
what is missing however is returning false if the gadget is hidden or disabled.
Also performance wise it's a bit worse off than a platform implementation obviously.

Ideally the PureBasic team would implement this into PureBasic utilizing PureBasic's own internal states, since MouseX and MouseY is already tracked a native code could avoid those two calls and would also have the hidden and disabled states available as well as quick access to the gadget x,y,w,h stored info so performance wise a native feature could perform as good as or better than a platform specific implementation, and it would even work with imagegadgets on Linux GTK etc.

For us such a feature would simply be using either:
gadget=GetHoverGadget()
or:
truefalse=GetGadgetHoverState(gadget)
or:
SetGadgetHoverCallback(@procedure())
+
Procedure hover_callback(gadget,mousex,mousey)
;we do something here
EndProcedure

A callback would give best performance as there would be no need to update internal states if the callback is not used.
GetGadgetHoverState() would be very similar to the example below.
GetHoverGadget() has the potential of the lowest overhead next to the callback.

Anyway, here ya go:

Code: Select all

Macro GadgetHoverCheck(windowmousex,windowmousey,gadget)
 (((Not windowmousex<GadgetX(gadget))&(Not windowmousey<GadgetY(gadget)))&(Not windowmousex>=(GadgetX(gadget)+GadgetWidth(gadget)))&(Not windowmousey>=(GadgetY(gadget)+GadgetHeight(gadget))))
EndMacro

;Example

#Window1=1
#Gadget1=1
#Gadget2=2
Define x.l,y.l,oldx.l,oldy.l

OpenWindow(#Window1,0,0,400,100,"Crossplatform MouseOver example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ButtonGadget(#Gadget1,10,10,380,20,"Button 1")
ButtonGadget(#Gadget2,10,30,380,20,"Button 2")

Repeat
 event=WaitWindowEvent(1)
 If event
  x=WindowMouseX(#Window1)
  y=WindowMouseY(#Window1)
  If (oldx<>x) Or (oldy<>y)
   If GadgetHoverCheck(x,y,#Gadget1)
    SetGadgetText(#Gadget1,"Hover")
   Else
    SetGadgetText(#Gadget1,"Button 1")
   EndIf
   If GadgetHoverCheck(x,y,#Gadget2)
    SetGadgetText(#Gadget2,"Hover")
   Else
    SetGadgetText(#Gadget2,"Button 2")
   EndIf
   oldx=x
   oldy=y
  EndIf
 EndIf
Until event=#PB_Event_CloseWindow
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: EventHoverGadget() - How to check if mouse is over a gad

Post by Michael Vogel »

I have modified the procedure to do the hover function only in the active window by adding a simple If GetParent_(handle)=GetActiveWindow_(), which works fine. Now I am using also the SplitterGadget which needs a GetParent(GetParent(...)), as in the following code.

My question: is there a more effective way instead using the multiple GetParents?

Code: Select all

Procedure.l GetGadgetXY()

	Protected cursor.POINT
	Protected handle.l

	If GetCursorPos_(cursor.POINT)
		handle=WindowFromPoint_(PeekQ(@cursor))
		If handle
			Dummy=GetParent_(handle)
			If Dummy=GetActiveWindow_() Or GetParent_(Dummy)=GetActiveWindow_()
				handle=GetDlgCtrlID_(handle)
				If handle>0; And handle<#NoMoreGadgets
					ProcedureReturn handle
				EndIf
			EndIf
		EndIf
	EndIf

	ProcedureReturn -999;#Undefined

EndProcedure
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: EventHoverGadget() - How to check if mouse is over a gad

Post by RASHAD »

Hi MV

Code: Select all

OpenWindow(0, 0, 0, 600, 400, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TextGadget    (0,  10,  10, 100, 16, "ListIcon Standard", #PB_Text_Center)
ListIconGadget(1,  10,  35, 300, 70, "Column 1", 100)
TextGadget    (2,  10, 125, 120, 16, "ListIcon with Checkbox", #PB_Text_Center)
ListIconGadget(3,  10, 150, 300, 70, "Column 1", 100, #PB_ListIcon_CheckBoxes) 
ButtonGadget  (4, 10, 230, 100, 20, "Add Gadget")

ButtonGadget(5, 0, 0, 0, 0, "Button 5")
ButtonGadget(6, 0, 0, 0, 0, "Button 6")
SplitterGadget(7, 400,35, 160, 125, 5, 6, #PB_Splitter_Separator)

ContainerGadget(8,400,160,160,160)
  ButtonGadget(9, 0, 10, 80, 25, "Button 9")
  ButtonGadget(10,0, 45, 80, 25, "Button 10")
CloseGadgetList()
   
Repeat
  ev=WaitWindowEvent()
  Select ev
        Case #WM_MOUSEMOVE         
              GetCursorPos_ (@p.POINT) 
              ScreenToClient_ (WindowID(0), @p)              
              hGad = ChildWindowFromPoint_ (WindowID(0), p\y<< 32+p\x)
              If hGad = WindowID(0)
                  Debug "No Gadget"
              Else
                  gadget = GetDlgCtrlID_(hGad)
                  If GadgetType(gadget) = #PB_GadgetType_Splitter Or GadgetType(gadget) = #PB_GadgetType_Container
                     GetCursorPos_ (@p.POINT)
                     ScreenToClient_ (GadgetID(gadget), @p) 
                     hGad = ChildWindowFromPoint_ (GadgetID(gadget), p\y<< 32+p\x)
                  EndIf
                 gadget = GetDlgCtrlID_(hGad)                  
                 Debug "Gadget : " + Str(gadget)                  
              EndIf
                  
        Case #PB_Event_CloseWindow 
          Q = 1 
    
  EndSelect
 
Until Q = 1

Edit : I did not notice the SplitterGadget() case at first,Sorry
Egypt my love
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: EventHoverGadget() - How to check if mouse is over a gad

Post by Michael Vogel »

Rashad, thanks for your code, never had a look at ScreenToClient before, could be usable from time to time.

As usual, I did not find precise words to describe my problem, hope the code below will show what I've needed: to show the hovered gadget of the active windows only.

Parent_GetGadgetXY seems to work for me, even it needs the while loop because of the Splitter and Panel gadgets...

Code: Select all

Procedure.l Rashad_GetGadgetXY()

	Protected cursor.POINT
	Protected handle.l

	If GetCursorPos_(cursor.POINT)

		ScreenToClient_ (GetActiveWindow_(),@p)
		handle=WindowFromPoint_(PeekQ(@cursor))
		If handle
			If GetParent_(handle)=GetActiveWindow_()
				handle=GetDlgCtrlID_(handle)
				If handle>0 ;And handle<15
					ProcedureReturn handle
				EndIf
			EndIf
		EndIf
		;EndIf
	EndIf

	ProcedureReturn -999;#Undefined

EndProcedure
Procedure.l Parent_GetGadgetXY()

	Protected cursor.POINT
	Protected handle.i
	Protected parent.i

	If GetCursorPos_(cursor.POINT)
		handle=WindowFromPoint_(PeekQ(@cursor))
		parent=GetParent_(handle)

		While parent

			If parent=GetActiveWindow_()
				handle=GetDlgCtrlID_(handle)
				If handle>0; And handle<#NoMoreGadgets
					ProcedureReturn handle
				Else
					ProcedureReturn -999;#Undefined
				EndIf
			EndIf

			parent=GetParent_(parent)
		Wend

	EndIf

	ProcedureReturn -999;#Undefined

EndProcedure

#NotifyFlag=#SS_NOTIFY

OpenWindow(101, 0, 0, 270, 200, "Wndow 101", #PB_Window_SystemMenu)
TextGadget(0, 10,  10, 250, 25, "***",#PB_Text_Center|#NotifyFlag|#SS_CENTERIMAGE)
SetGadgetColor(0,#PB_Gadget_BackColor,#Yellow)
TextGadget(1, 10,  40, 250, 25, "1", #PB_Text_Center|#NotifyFlag)
TextGadget(2, 10,  70, 250, 25, "2", #PB_Text_Center|#NotifyFlag)
SplitterGadget(201,10,40,250,55,1,2)
TextGadget(3, 10, 100, 250, 25, "3", #PB_Text_Center|#PB_Text_Border|#NotifyFlag)
PanelGadget(301,10,130,250,50)
TextGadget(4, 10, 10, 230, 25, "4", #PB_Text_Center|#PB_Text_Border|#NotifyFlag)

OpenWindow(102, 300, 0, 270, 200, "Window 102", #PB_Window_SystemMenu)
TextGadget(10, 10,  10, 250, 25, "10",#PB_Text_Center|#NotifyFlag)
TextGadget(11, 10,  40, 250, 25, "11", #PB_Text_Center|#NotifyFlag)
TextGadget(12, 10,  70, 250, 25, "12", #PB_Text_Center|#NotifyFlag)
SplitterGadget(202,10,40,250,55,11,12)
TextGadget(13, 10, 100, 250, 25, "13", #PB_Text_Center|#PB_Text_Border|#NotifyFlag)
PanelGadget(302,10,130,250,50)
TextGadget(14, 10, 10, 230, 25, "14", #PB_Text_Center|#PB_Text_Border|#NotifyFlag)

AddWindowTimer(101,100,250)

Repeat
	Select WaitWindowEvent()
	Case #WM_CHAR,#PB_Event_CloseWindow
		End
	EndSelect

	m=Parent_GetGadgetXY()
	n=Rashad_GetGadgetXY()

	If n>0 Or m>0
		SetGadgetText(0,"Mouse over "+Str(m)+" / "+Str(n))
	EndIf

ForEver
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4946
Joined: Sun Apr 12, 2009 6:27 am

Re: EventHoverGadget() - How to check if mouse is over a gad

Post by RASHAD »

Hi MV
Hope that the next code will be a step forward

Code: Select all

OpenWindow(0, 0, 0, 570, 500, "SplitterGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TextGadget    (0,  10,  10, 100, 16, "ListIcon Standard", #PB_Text_Center)
ButtonGadget  (1, 210, 10, 100, 20, "Add Gadget")
ListIconGadget(2,  10,  35, 300, 70, "Column 1", 100)
StringGadget    (3,  10, 118, 200, 22, "ListIcon with Checkbox", #PB_Text_Center)
ExplorerListGadget(4,  10, 150, 300,120,"C:\") 

ButtonGadget(5, 0, 0, 0, 0, "Button 5")
ButtonGadget(6, 0, 0, 0, 0, "Button 6")
SplitterGadget(7, 400,10, 160, 70, 5, 6, #PB_Splitter_Separator)

ContainerGadget(8,400,100,160,68,#PB_Container_Flat)
  ButtonGadget(9, 40, 4, 80, 25, "Button 9")
  ButtonGadget(10,40, 36, 80, 25, "Button 10")
CloseGadgetList()

PanelGadget     (11, 10, 280, 306, 203)
  AddGadgetItem (11, 1, "Panel 1")
  SetGadgetItemData(0, 1, 1)
      PanelGadget (12, 5, 5, 290, 166)
      AddGadgetItem(12, 1, "Sub-Panel 1")
      SetGadgetItemData(0, 1, 11)
      AddGadgetItem(12, 2, "Sub-Panel 2")
      SetGadgetItemData(0, 1, 12)
      AddGadgetItem(12, 3, "Sub-Panel 3")
      SetGadgetItemData(0, 1, 13)
    CloseGadgetList()
  AddGadgetItem (11, 2,"Panel 2")
  SetGadgetItemData(0, 2, 20)
    ButtonGadget(20, 10, 15, 80, 24,"Button 1")
    ButtonGadget(30, 95, 15, 80, 24,"Button 2")
CloseGadgetList()

CanvasGadget(40, 400, 190, 160, 50,#PB_Canvas_Border)
ImageGadget(50, 400, 260, 160 ,50, 0,#PB_Image_Border)
CalendarGadget(60, 400, 330, 160,150 )

OpenWindow(1, 300, 0, 270, 200, "Window 102", #PB_Window_SystemMenu)
TextGadget(71, 10,  10, 250, 25, "10",#PB_Text_Center)
TextGadget(72, 10,  40, 250, 25, "11", #PB_Text_Center)
TextGadget(73, 10,  70, 250, 25, "12", #PB_Text_Center)
SplitterGadget(74,10,40,250,55,71,72)
TextGadget(75, 10, 100, 250, 25, "13", #PB_Text_Center|#PB_Text_Border)
PanelGadget(76,10,130,250,50)
TextGadget(77, 10, 10, 230, 25, "14", #PB_Text_Center|#PB_Text_Border)
   
Repeat
  ev=WaitWindowEvent()
  Select ev
         Case #WM_MOUSEMOVE
               hWnd = WindowID(GetActiveWindow())        
               GetCursorPos_ (@p.POINT)
               wGad = WindowFromPoint_(p\y << 32 + p\x)
               ScreenToClient_ (hWnd, @p)        
               cGad = ChildWindowFromPoint_ (hWnd, p\y<< 32+p\x)
                  wgadget = GetDlgCtrlID_(wGad)
                  cgadget = GetDlgCtrlID_(cGad)
                  If GadgetType(cgadget) = #PB_GadgetType_Text Or GadgetType(cgadget) = #PB_GadgetType_ListIcon Or GadgetType(cgadget) = #PB_GadgetType_ExplorerList
                      If cGad = WindowID(0)
                         Debug "No Gadget"
                      Else
                         Debug "Gadget : " + Str(cgadget)
                      EndIf
                   Else
                       Debug "Gadget : " + Str(wgadget)
                  EndIf 
                  
        Case #PB_Event_CloseWindow 
          Q = 1 
    
  EndSelect
 
Until Q = 1

Egypt my love
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: EventHoverGadget() - How to check if mouse is over a gad

Post by Michael Vogel »

"one small step for man..."

Your code does not show the Gadget #77 and you may get an error in the hWnd = WindowID(GetActiveWindow()) line :?

I will keep my code for the moment, as it seems to work fine even with your multiple gadget window. All I have to keep in mind to use the #SS_Notify flag for all static gadgets.
Post Reply