Sign in to follow this  
Followers 0
PauloBuchsbaum

Left Click Event in a GUI

7 posts in this topic

#1 ·  Posted (edited)

A great trouble in a GUI environment in AutoIt is that most controls are not clickable.

It's a big trouble, because it's very normal the user needs to access a specific field/control.

There is no easy way to do this, because AutoIt doesn't launch a event when a user click

a mouse over most controls, except for few controls, like buttons.

I've found in this forum a nice solution Clickable Label in order to accept click on Label Control.

Fortunately, the same approach suits any control.

#include <guiconstants.au3>

Dim $Main = GUICreate("MyGui", 300, 100)
Dim $Name = EditLabel(20,"Name")   ; "Name" field
Dim $Address = EditLabel(50,"Address") ; "Address" field

GUISetState()   ; Shows GUI
While 1
    $imsg = GUIGetMsg()   ; get event
    Select
        Case $imsg = $GUI_EVENT_CLOSE  ; The user has clicked on X
            Exit  
        Case _IsPressed('01')   ; Other left click
          IF _CtrlClick($Main, "", $Name) = 1 Then  ; "Name" field's click
            GUICtrlSetState ($Name , $GUI_FOCUS)
          Elseif _CtrlClick($Main, "", $Address) = 1 Then  ; "Address" field's click
            GUICtrlSetState ($Address, $GUI_FOCUS)
          Endif
    EndSelect
WEnd 

;*****************************************************************
;                _ClickContr()
;  Is the left button click inside a Control $Contr in the GUI $GUI, returns 1
;***************************************************************
Func _ClickContr($GUI, $Text, $Contr)
    Local $Opt = Opt("MouseCoordMode", 2)              ; Relative coords
    Local $Pos = ControlGetPos($GUI, $Text, $Contr)    ; Control dimensions 
    Local $Mouse = MouseGetPos()                       ; Mouse position  
    If IsArray($Mouse) And IsArray($Pos) Then          ; Mouse is inside the control?  
        If $Mouse[0] >= $Pos[0] And $Mouse[0] <= $Pos[0] + $Pos[2] And $Mouse[1] >= $Pos[1] And $Mouse[1] <= $Pos[1] + $Pos[3] Then
            Return 1
        EndIf
    EndIf
    Return 0
EndFunc

;*************************************************
;                _IsPressed()
;  Is the left mouse was clicked, returns 1
;************************************************
Func _IsPressed($s_hexKey, $v_dll = 'user32.dll') 
    Local $a_R = DllCall($v_dll, "int", "GetAsyncKeyState", "int", '0x' & $s_hexKey)  
    If Not @error And BitAND($a_R[0], 0x8000) = 0x8000 Then Return 1
    Return 0
EndFunc
Edited by PauloBuc

Share this post


Link to post
Share on other sites



The style $SS_NOTIFY allows you to trigger an event on a static click (so pics, icons and labels... You could also simplify what you've got by using GUIGetCursorInfo.

Mat

Share this post


Link to post
Share on other sites

Thanks, Mat. You are right.

I've researched for a solution and I just find a complex one. Unfortunately.

Yours it's better and simple.

Now the code is shorter...

#include <guiconstants.au3>
#include <StaticConstants.au3>

Dim $Main = GUICreate("MyGui", 300, 100)
Dim $Name = EditLabel(20,"Name")       ; "Name" field
Dim $Address = EditLabel(50,"Address") ; "Address" field

GUISetState()   ; Shows GUI
While 1
    $imsg = GUIGetMsg()   ; get event
    Select
        Case $imsg = $GUI_EVENT_CLOSE  ; click on X
            Exit
        Case $imsg = $Name
            GUICtrlSetState ($Name , $GUI_FOCUS)
        Case $imsg = $Address
            GUICtrlSetState ($Address, $GUI_FOCUS)
    EndSelect
WEnd

Func EditLabel($Alt,$Lab)
Local $Aux
GUICtrlCreateLabel($Lab, 10, $Alt, 100, 20)
$Aux = GUICtrlCreateInput("",130, $Alt, 100,20,$SS_NOTIFY)
Return $Aux
EndFunc

Share this post


Link to post
Share on other sites

I was thinking you could also do this:

#include <GUIConstantsEx.au3>

Local $Main = GUICreate("MyGui", 300, 100)
Local $First = GUICtrlCreateLabel ("Test 1: Label", 2, 2, 100, 20)
Local $Second = GUICtrlCreateInput ("Test 2: Input", 2, 24, 100, 20)

GUISetState()

Local $iMsg, $aCur

While 1
    $aCur = GUIGetCursorInfo()
    $imsg = GUIGetMsg()
    If ($iMsg = $GUI_EVENT_PRIMARYUP) Then $iMsg = $aCur[4]
    Switch $iMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $First
            ConsoleWrite ("Label clciked" & @CRLF)
        Case $Second
            ConsoleWrite ("Input clicked" & @CRLF)
    EndSwitch
WEnd

But there is probably a more accurate method using windos messages.

Mat

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

Mat,

Thanks for your attention!

Your example works smoothly.

However, in my real program, with many edit, label, combo, buttons and checkboxes fields, this method is not trustable.

I need to click 2 or 3 times to the program notices the click. I've used two options, with or without $SS_NOTIFY in control creation. The click's behaviour is unpredictable.

My original way to do that (inspired in a old post cited) it's not pretty, but works flawless in real code.

My purpose is just to allow the user edit any field that he wants, without no forced order.

In my real program, I keep a global variable with the active field to force tight validation control.

I know how to set the focus in a control, but I don't know how to get the control that has focus.

So I've decided that the program knows all the time which control is active, updating a variable in all events.

PS: I don't have enough practice about Windows Messages management in order to I try by myself do the "right" way.

#include <guiconstants.au3>

Dim $Main = GUICreate("MyGui", 300, 100)
Dim $Name = EditLabel(20,"Name")   ; "Name" field
Dim $Address = EditLabel(50,"Address") ; "Address" field

GUISetState()   ; Shows GUI
While 1
    $imsg = GUIGetMsg()   ; get event
    Select
        Case $imsg = $GUI_EVENT_CLOSE  ; The user has clicked on X
            Exit  
        Case _IsPressed('01')   ; Other left click
          IF _CtrlClick($Main, "", $Name) = 1 Then  ; "Name" field's click
            GUICtrlSetState ($Name , $GUI_FOCUS)
          Elseif _CtrlClick($Main, "", $Address) = 1 Then  ; "Address" field's click
            GUICtrlSetState ($Address, $GUI_FOCUS)
          Endif
    EndSelect
WEnd 

;*****************************************************************
;                _ClickContr()
;  Is the left button click inside a Control $Contr in the GUI $GUI, returns 1
;***************************************************************
Func _ClickContr($GUI, $Text, $Contr)
    Local $Opt = Opt("MouseCoordMode", 2)              ; Relative coords
    Local $Pos = ControlGetPos($GUI, $Text, $Contr)    ; Control dimensions 
    Local $Mouse = MouseGetPos()                       ; Mouse position  
    If IsArray($Mouse) And IsArray($Pos) Then          ; Mouse is inside the control?  
        If $Mouse[0] >= $Pos[0] And $Mouse[0] <= $Pos[0] + $Pos[2] And $Mouse[1] >= $Pos[1] And $Mouse[1] <= $Pos[1] + $Pos[3] Then
            Return 1
        EndIf
    EndIf
    Return 0
EndFunc

;*************************************************
;                _IsPressed()
;  Is the left mouse was clicked, returns 1
;************************************************
Func _IsPressed($s_hexKey, $v_dll = 'user32.dll') 
    Local $a_R = DllCall($v_dll, "int", "GetAsyncKeyState", "int", '0x' & $s_hexKey)  
    If Not @error And BitAND($a_R[0], 0x8000) = 0x8000 Then Return 1
    Return 0
EndFunc
;*************************************************
;                EditLabel
;  Label + Edit
;************************************************
Func EditLabel($Alt,$Lab)
  Local $Aux
  GUICtrlCreateLabel($Lab, 10, $Alt, 100, 20)
  $Aux = GUICtrlCreateInput("",130, $Alt, 100,20)
  Return $Aux
EndFunc
Edited by PauloBuchsbaum

Share this post


Link to post
Share on other sites

There are many many ways to do this.

The problem with searching the forum is you come up with code that is 4 years old. _ispressed is now part of the misc udf and there are lots of other changes. Here are two ways to get guictrl focus. One is a hybrid of Mat and your approach, the other is the window message method you were talking about.

#include <guiconstants.au3>
#Include <Misc.au3>
Global $dll = DllOpen("user32.dll")
Dim $Main = GUICreate("MyGui", 300, 100)
Dim $Name = EditLabel(20,"Name")   ; "Name" field
Dim $Address = EditLabel(50,"Address") ; "Address" field

GUISetState()   ; Shows GUI
While 1
    $imsg = GUIGetMsg()   ; get event
    Select
        Case $imsg = $GUI_EVENT_CLOSE  ; The user has clicked on X
            Exit
        Case _IsPressed('01',$dll)   ; Other left click
            $hov=GUIGetCursorInfo($Main)
          IF $hov[4]=$name Then  ; "Name" field's click
            GUICtrlSetState ($Name , $GUI_FOCUS)
          Elseif $hov[4]= $Address Then  ; "Address" field's click
            GUICtrlSetState ($Address, $GUI_FOCUS)
          Endif
    EndSelect
WEnd

;*************************************************
;                EditLabel
;  Label + Edit
;************************************************
Func EditLabel($Alt,$Lab)
  Local $Aux
  GUICtrlCreateLabel($Lab, 10, $Alt, 100, 20)
  $Aux = GUICtrlCreateInput("",130, $Alt, 100,20)
  Return $Aux
EndFunc

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <EditConstants.au3>

Global $Main = GUICreate("MyGui", 300, 100)
Global $Name = EditLabel(20,"Name")   ; "Name" field
Global $Address = EditLabel(50,"Address") ; "Address" field

Global $hLabel = GUICtrlCreateLabel("", 10, 0, 200, 20)


GUISetState()

; Catch focus passing to $InputLDescrition
GUIRegisterMsg($WM_COMMAND, "ED_WM_COMMAND")

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch

WEnd

Func ED_WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $iCode = BitShift($wParam, 16)
    Switch $lParam
        Case GUICtrlGetHandle($Name)
            Switch $iCode
                Case $EN_SETFOCUS
                    GUICtrlSetData($hLabel, "Name edit has focus")
                Case $EN_KILLFOCUS
                 ;   GUICtrlSetData($hLabel, "Edit does not have focus")
            EndSwitch
        Case GUICtrlGetHandle($Address)
            Switch $iCode
                Case $EN_SETFOCUS
                    GUICtrlSetData($hLabel, "Address edit has focus")
                Case $EN_KILLFOCUS
                ;    GUICtrlSetData($hLabel, "Edit does not have focus")
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc  ;==>ED_WM_COMMAND

;*************************************************
;                EditLabel
;  Label + Edit
;************************************************
Func EditLabel($Alt,$Lab)
  Local $Aux
  GUICtrlCreateLabel($Lab, 10, $Alt, 100, 20)
  $Aux = GUICtrlCreateInput("",130, $Alt, 100,20)
  Return $Aux
EndFunc

Share this post


Link to post
Share on other sites

Picea892,

Very thanks for your post.

Both solutions works perfectly in my real program.

It can be posted before this feature, but it's not easy find it using search engine.

A proper post title it's always useful!

I've liked very much the first solution, because it's simpler than windows message handling solution.

PS: I didn't understand with AutoIt developers choose make the input and other controls not selectable by

default. It's the normal behaviour in any form design in most programming languages.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0