Función para comprobar si el foco está sobre un control

y programas personales para otros como tu, puede que te corrijan ;)
Responder
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Función para comprobar si el foco está sobre un control

Mensaje por Jonny »

Hola

Tras la consulta que hacía hace algunos días en:
[ul] http://www.emesn.com/autoitforum/viewto ... f=3&t=2264 [/ul]

Chefito colgó unas funciones muy interesantes para hacer lo que necesitaba entre otras cosas, pero no se por qué, no me funcionaron, pues me devolvían siempre un código de error (como que el foco no estaba en ningún control cuando realmente lo estaba), no se que haría mal...

En cualquier caso, me animé gracias a que Ximorro me dió alguna pista para hacer lo que necesitaba en el post anterior, y Me arranqué haciendo mi propia función[/b :), que siempre es interesante crear tu propio código :P.

así pues, aquí teneis la función ¡con control de errores! que a Chefito le daba pereza implementar :).

Código: Seleccionar todo

Func IsInControl($WinTitle, $ControlID, $PointerType=0, $ControlType="all")
	;----------
	; - Comprueba si el puntero del ratón o el foco del teclado están sobre el objeto indicado en la ventana especificada.
	; - Argumentos: $WinTitle: Título de la ventana del control a comprobar.
		;$ControlID: Handle del control a comprobar, devuelto por las funciones GuiCtrlCreate...().
		;$PointerType: Foco a comprobar si está sobre el control: 0 (por defecto: Cursor del ratón. 1: Foco del teclado.
		;$ControlType: Tipo del control que se chequea: "all" (por defecto): Cualquier tipo de control.
					;-Este parámetro permite simplificar la programación de eventos en controles de un tipo concreto ("Button" por ejemplo).
					;-Si se desea realizar una acción cuando el foco del teclado está sobre un botón, basta con pasarle el Handle de este a $ControlID y "button" a $ControlType.
					;-De esta forma, si el control indicado es de tipo "Button" y el foco del teclado está sobre él, la función devolverá true (1).
	; - Valores de retorno: Al fallar: Devuelve -1.
		;Con éxito:
		;Devuelve 1 si el cursor indicado en $PointerType está sobre el control indicado y si este coincide con el tipo de control (si se especifica en $ControlType).
		;Devuelve 0 si el cursor indicado en $PointerType no está sobre el control indicado o si lo está, pero el tipo del control no coincide con el indicado (si se especifica en $ControlType).
	;Autor: Jonny (Para el foro de AutoIt en Español).
	;----------
Local $CurrentPosMouse, $CurrentPosFocus, $GetCTRLType, $GetCTRLID
		If ($WinTitle=="" Or $ControlID=="" Or $PointerType=="" Or $ControlType=="") Then Return -1
	$PointerType=Int($PointerType)
		If ($PointerType<>0 And $PointerType<>1) Then Return -1
Switch($PointerType)
	Case 0
		$CurrentPosMouse=GuiGetCursorInfo($WinTitle)
		$GetCTRLType=_WinAPI_GetClassName($CurrentPosMouse[4])
		If ((Not IsArray($CurrentPosMouse)) Or ($CurrentPosMouse[4]==0) Or ($CurrentPosMouse[4]<>$ControlID) Or ($CurrentPosMouse[4]==$ControlID And (StringLower($ControlType)<>"all" And StringLower($ControlType)<>StringLower($GetCTRLType)))) Then Return 0
		If (($CurrentPosMouse[4]==$ControlID) And ((StringLower($ControlType)=="all") Or (StringLower($ControlType)<>"all" And StringLower($ControlType)==StringLower($GetCTRLType)))) Then Return 1
	Case 1
		$CurrentPosFocus=ControlGetHandle($WinTitle, "", ControlGetFocus($WinTitle, ""))
		$GetCTRLID=DllCall("User32.dll", "int", "GetDlgCtrlID", "hwnd", $CurrentPosFocus)
		$GetCTRLType=_WinAPI_GetClassName($GetCTRLID[0])
		If (($GetCTRLID[0]=="") Or (@Error==1) Or ($GetCTRLID[0]<>$ControlID) Or ($GetCTRLID[0]==$ControlID And (StringLower($ControlType)<>"all" And StringLower($ControlType)<>StringLower($GetCTRLType)))) Then Return 0
		If (($GetCTRLID[0]==$ControlID) And ((StringLower($ControlType)=="all") Or (StringLower($ControlType)<>"all" And StringLower($ControlType)==StringLower($GetCTRLType)))) Then Return 1
	Case Else
		Return -1
EndSwitch
EndFunc
(Solucionado pequeño lápsus en el bloque switch (antes Select))

Como podeis ver en la mini-documentación de la función, entre los parámetros que admite, está "$ControlType", que lo puse porque para el menú que quiero diseñar, quiero que al estar el foco del teclado o el cursor del ratón sobre un botón, se produzca un evento, pero sólo si se trata de un botón, y no de un label por ejemplo. Pensé entonces, que este parámetro ahorraría algo de código, ya que si se especifica el tipo de control (ver los de la ayuda de autoit) en este parámetro, la función podría hacer el trabajo de comprobar si el ratón o el teclado tienen seleccionado el control indicado, y este es del tipo que se necesita; de ser así, devuelve 1; en caso contrario devuelve 0 (aunque el teclado o el ratón tengan el foco sobre el control indicado).
También puede dejarse el valor por defecto de "$ControlType" ("all") para que no haga distinciones entre el tipo de control seleccionado y el especificado en "$ControlID". En cuyo caso, devuelve 1 si el control indicado en "$ControlID" está seleccionado por el teclado o el ratón, sin importar qué tipo de control sea.

Además, preferí unificar tanto la detección del ratón como del foco del teclado en una única función, que perfectamente podría haber hecho en dos, pero bueno, Cuestión de gustos :P.

He intentado hacerlo lo más simplificado posible, tanto que leerlo es un suplicio con esos algoritmos tan largos y complicados, con tantas precedencias de paréntesis ¡Pero queda más pofesional! :).

Se admiten sugerencias, bugs...

Espero que os guste.

Salu2!
Avatar de Usuario
Chefito
Profesional del Autoit
Mensajes: 2035
Registrado: 21 Feb 2008, 18:42
Ubicación: Albacete/Cuenca (España)

Re: Función para comprobar si el foco está sobre un control

Mensaje por Chefito »

Jejejeje.....bien hecho :smt023 . Me gusta que tengas iniciativa hombre.

Es raro lo de mis funciones. Como habrás podido comprobar, el ejemplo que adjunto con las funciones, funciona perfectamente, por lo menos a mí :smt017 . En mi ejemplo hago lo de la entrada y salida del foco en un objeto list.

Te recomiendo que pongas un ejemplo en el post junto a la función para que la gente no tenga que crear una gui para ver el funcionamiento de ésta. Yo me esperaré a esto que te digo para ver su funcionamiento :smt016 .

Así por encima, veo que sobran algunas cosas. Por ejemplo, quita todo lo referente a la variable $PointerType antes del switch. No hace falta. Ya lo compruebas con el switch. Además, la función int devuelve 0 si no le introduces algún dígito. No se si esto te interesa o prefieres que te de error.
Si quieres que sea más profesional puedes hacer tratamiento de errores. Que en vez de devolver solamente -1, utiliza seterror y consolewriteerror.
Meterme con las otras condiciones sin probarla es muy dificil :smt003 . Son muy largas.....puede que demasiado. Habría que estudiarlas detenidamente para ver si se pueden optimizar.

Saludos.
Cita vista en algún lugar de la red: En este mundo hay 10 tipos de personas, los que saben binario y los que no ;).
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Función para comprobar si el foco está sobre un control

Mensaje por Jonny »

Hola

Disculpas por no adjuntar la gui de ejemplo, aquí está El diseño no es lo mío... pero para ver que funciona, supongo que servirá.

Código: Seleccionar todo

#Include <Array.au3>
#Include <Constants.au3>
#Include <File.au3>
#Include <GUIConstants.au3>
#Include <Sound.au3>
#Include <StaticConstants.au3>
#Include <String.au3>
#Include <WinApi.au3>
#Include <WindowsConstants.au3>

		Global $WinHand ;Handle de la ventana.
		Global $Label1 ;Handle del Label1.
Global $Label2 ;Handle del Label2.
	Global $Button1 ;Handle del Botón 1.
	Global $Button2 ;Handle del Botón 2.
	Global $MsgEvent ;Recibir eventos de ventana.
		Global $BuffLastObjMouse ;Buffer del último botón seleccionado por el Mouse.
		Global $BuffLastObjFocus ;Buffer del último botón seleccionado por el Teclado.

	$WinHand=GuiCreate("Prueba de detección de foco", 600, 600, -1, -1, $WS_CAPTION) ;Crear interfaz.
		GuiSetBkColor("123104238", $WinHand) ;Color de fondo de la ventana.
		$Label1=GuiCtrlCreateLabel("Selecciona uno de los botones con el cursor del ratón o del teclado.", 120, 80, -1, -1, $SS_CENTER) ;Label 1.
		GuiCtrlSetFont($Label1, 11, 800, 0, "Century") ;Fuente del Label1.
		GuiCtrlSetColor($Label1, "16777215") ;Color del texto del Label 1.
		$Label2=GuiCtrlCreateLabel("", 120, 130, 300, -1, $SS_CENTER) ;Label 2.
		GuiCtrlSetFont($Label2, 10, 400, 0, "Verdana") ;Fuente del Label 2.
		GuiCtrlSetColor($Label2, "16777215") ;Color del texto del Label 2.
		GuiCtrlSetState($Label2, $Gui_Hide)
		$Button1=GuiCtrlCreateButton("Botón 1", 170, 210, 90, 90, $WS_TABSTOP) ;Botón 1.
		GuiCtrlSetState($Button1, $Gui_Focus)
		$Button2=GuiCtrlCreateButton("Botón 2", 260, 210, 90, 90, $WS_TABSTOP) ;Botón 2.
	GuiSetState(@SW_SHOW) ;Fin de la interfaz.

		While 1 ;Ejecución indefinida.
		Sleep(5) ;Pausar script 5 milisegundos.
		$MsgEvent=GuiGetMsg() ;Recepción de eventos de la GUI.
			Select
				Case $MsgEvent==$GUI_EVENT_Close
				Exit
				Case ($MsgEvent==$Button1 Or $MsgEvent==$Button2)
				Exit
			Case ((IsInControl($WinHand, $Button1, 0, "Button")==0 And IsInControl($WinHand, $Button2, 0, "Button")==0) And (IsInControl($WinHand, $Button1, 1, "Button")==0 And IsInControl($WinHand, $Button2, 1, "Button")==0))
				ContinueLoop
			Case (IsInControl($WinHand, $Button1, 0, "Button")==1 And $BuffLastObjMouse<>$Button1)
				$BuffLastObjMouse=$Button1
				GuiCtrlSetState($Label2, $Gui_Show)
				GuiCtrlSetData($Label2, "Has seleccionado el Botón 1.")
			Case (IsInControl($WinHand, $Button2, 0, "Button")==1 And $BuffLastObjMouse<>$Button2)
				$BuffLastObjMouse=$Button2
				GuiCtrlSetState($Label2, $Gui_Show)
				GuiCtrlSetData($Label2, "Has seleccionado el Botón 2.")
			Case (IsInControl($WinHand, $Button1, 1, "Button")==1 And $BuffLastObjFocus<>$Button1)
				$BuffLastObjFocus=$Button1
				GuiCtrlSetState($Label2, $Gui_Show)
				GuiCtrlSetData($Label2, "Has seleccionado el Botón 1.")
			Case (IsInControl($WinHand, $Button2, 1, "Button")==1 And $BuffLastObjFocus<>$Button2)
				$BuffLastObjFocus=$Button2
				GuiCtrlSetState($Label2, $Gui_Show)
				GuiCtrlSetData($Label2, "Has seleccionado el Botón 2.")
			EndSelect
		Wend

Func IsInControl($WinTitle, $ControlID, $PointerType=0, $ControlType="all")
   ;----------
   ; - Comprueba si el puntero del ratón o el foco del teclado están sobre el objeto indicado en la ventana especificada.
   ; - Argumentos: $WinTitle: Título de la ventana del control a comprobar.
      ;$ControlID: Handle del control a comprobar, devuelto por las funciones GuiCtrlCreate...().
      ;$PointerType: Foco a comprobar si está sobre el control: 0 (por defecto: Cursor del ratón. 1: Foco del teclado.
      ;$ControlType: Tipo del control que se chequea: "all" (por defecto): Cualquier tipo de control.
               ;-Este parámetro permite simplificar la programación de eventos en controles de un tipo concreto ("Button" por ejemplo).
               ;-Si se desea realizar una acción cuando el foco del teclado está sobre un botón, basta con pasarle el Handle de este a $ControlID y "button" a $ControlType.
               ;-De esta forma, si el control indicado es de tipo "Button" y el foco del teclado está sobre él, la función devolverá true (1).
   ; - Valores de retorno: Al fallar: Devuelve -1.
      ;Con éxito:
      ;Devuelve 1 si el cursor indicado en $PointerType está sobre el control indicado y si este coincide con el tipo de control (si se especifica en $ControlType).
      ;Devuelve 0 si el cursor indicado en $PointerType no está sobre el control indicado o si lo está, pero el tipo del control no coincide con el indicado (si se especifica en $ControlType).
   ;Autor: Jonny (Para el foro de AutoIt en Español).
   ;----------
Local $CurrentPosMouse, $CurrentPosFocus, $GetCTRLType, $GetCTRLID
      If ($WinTitle=="" Or $ControlID=="" Or $PointerType=="" Or $ControlType=="") Then Return -1
   $PointerType=Int($PointerType)
Switch($PointerType)
   Case 0
      $CurrentPosMouse=GuiGetCursorInfo($WinTitle)
      $GetCTRLType=_WinAPI_GetClassName($CurrentPosMouse[4])
      If ((Not IsArray($CurrentPosMouse)) Or ($CurrentPosMouse[4]==0) Or ($CurrentPosMouse[4]<>$ControlID) Or ($CurrentPosMouse[4]==$ControlID And (StringLower($ControlType)<>"all" And StringLower($ControlType)<>StringLower($GetCTRLType)))) Then Return 0
      If (($CurrentPosMouse[4]==$ControlID) And ((StringLower($ControlType)=="all") Or (StringLower($ControlType)<>"all" And StringLower($ControlType)==StringLower($GetCTRLType)))) Then Return 1
   Case 1
      $CurrentPosFocus=ControlGetHandle($WinTitle, "", ControlGetFocus($WinTitle, ""))
      $GetCTRLID=DllCall("User32.dll", "int", "GetDlgCtrlID", "hwnd", $CurrentPosFocus)
      $GetCTRLType=_WinAPI_GetClassName($GetCTRLID[0])
      If (($GetCTRLID[0]=="") Or (@Error==1) Or ($GetCTRLID[0]<>$ControlID) Or ($GetCTRLID[0]==$ControlID And (StringLower($ControlType)<>"all" And StringLower($ControlType)<>StringLower($GetCTRLType)))) Then Return 0
      If (($GetCTRLID[0]==$ControlID) And ((StringLower($ControlType)=="all") Or (StringLower($ControlType)<>"all" And StringLower($ControlType)==StringLower($GetCTRLType)))) Then Return 1
   Case Else
      Return -1
EndSwitch
EndFunc
No entiendo bien lo de la función Int() que comentas. Hice esto:

Código: Seleccionar todo

      If ($PointerType<>0 And $PointerType<>1) Then Return -1
para comprobar que se pasara un valor válido a $PointerType (0 para el ratón o 1 para el teclado), no para ver si devolvía error, que por otro lado es cierto, que el Case Else hace esa misma comprobación, así que lo quito.
Pasé $PointerType a entero, por eso de que si se pasara como string ("0") por ejemplo, no se si se cumpliría la condición en el switch.

la línea

Código: Seleccionar todo

      If ($WinTitle=="" Or $ControlID=="" Or $PointerType=="" Or $ControlType=="") Then Return -1

la puse, para que si algún argumento está vacío, se salga de la función, sin que llegue a ejecutarse más código.

Lo de los algoritmos... creo que en principio controlan bien los errores que pueden darse, y comprueban bien que se cumplan las condiciones establecidas en los argumentos, pero cuando son tan largos es posible que se escape algo... A mi en principio me funciona bien.

Por cierto, efectivamente, tus funciones, iban como la seda en tu ejemplo, no se que haría para que no me funcionaran.

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Función para comprobar si el foco está sobre un control

Mensaje por Ximorro »

Hombreeeeeeeeeeeeee, al fin vemos código tuyo más elaborado, ¡enhorabuena!

Muy bien, muy bien, te hago algunas sugerencias por si te ayudan a afinar algunas cosas:
.- No hace falta usar el operador "==" con números, eso está pensado para cadenas.
.- Toda la condición del ContinueLoop creo que es innecesaria, aunque no pongas ese Case tampoco va a entrar los demás si no se cumplen las condiciones...
.- Los textos de ambos label me salen cortados, deberías darles más tamaño...
.- Deberías usar el tipo de parámetro que se espera en las funciones, si es texto, pon cadenas, si son números, pon números. Por ejemplo al asignar los colores, al hacer GuiCtrlSetColor($Label1, "16777215"), AutoIT pasa esa cadena a número, pero si le das directamente GuiCtrlSetColor($Label1, 16777215) no tiene que hacer la conversión. Por cierto, 16777215 es el color blanco, que es el valor más alto posible, todo color por encima de ese es incorrecto, así que el color que pasas a GuiSetBkColor está fuera de rango...
Lo mismo con tu propia función, un handle es numérico, será extremadamente raro que alguien llame a tu función pasando "" como el handle del control, si ahí llega un control no inicializado $ControlID probablemente valdrá cero, no una cadena vacía, así que en el error deberías controlar $ControlID=0
.- ¡Permite cerrar la ventana de una forma clara! ¿Por qué has quitado el aspa de cerrar ventana en la barra de título? En una aplicación final te puede interesar por varias razones pero en una prueba así sólo es un poco molestia. Me he dado cuenta de que los botones cierran la ventana, pero no es evidente, al menos ponles como texto "Cerrar1" y "Cerrar2" o algo así...

Puedes simplificar las condiciones, por ejemplo esta:

Código: Seleccionar todo

If (($CurrentPosMouse[4]==$ControlID) And ((StringLower($ControlType)=="all") Or (StringLower($ControlType)<>"all" And StringLower($ControlType)==StringLower($GetCTRLType)))) Then Return 1
es equivalente a esta:

Código: Seleccionar todo

If ($CurrentPosMouse[4]=$ControlID) And ($ControlType="all" Or $ControlType=$GetCTRLType) Then Return 1
Ligeramente más corta... :smt005

He hecho:
- cambiar "==" por "=" para comparaciones numéricas
- cambiarlo también para comparaciones de cadena, porque precisamente quieres comparar independientemente del caso sensitivo
- al usar el operador no caso sensitivo, no tengo que hacer los StringLower
- Lo de ((StringLower($ControlType)=="all") Or (StringLower($ControlType)<>"all"... esa segunda condición es innecesaria, cuando entra en la segunda parte del OR seguro que es distinto a "all", pues ha pasado ya la primera parte del OR donde se ha mirado que es igual. Además, es que encima de eso miras si es $GetCTRLType, así que si eso es cierto lógicamente no es "all", con lo que es innecesario mirarlo, es decir:
$ControlType="all" Or $ControlType=$GetCTRLType
quiere decir que $ControlType vale "all" o $GetCTRLType, en la segunda parte del OR no tiene sentido asegurarse de que no vale "all", si vale $GetCTRLType seguro que no vale "all"...
- He quitado algunos paréntesis innecesarios, en realidad el primer par también es innecesario, pero lo he dejado porque clarifica la separación del AND y el OR.

Como ves ha quedado bastante corto...

Si cambias los otros ojo que no hay un operador "<>" no caso sensitivo, así que hay que hacerlo con un NOT =, así por ejemplo:
StringLower($ControlType)<>StringLower($GetCTRLType)
quedaría:
Not ($ControlType=$GetCTRLType)
A pesar de añadir el NOT, como con "=" podemos quitar los StringLower sigue quedando más corto.
Ojo que en la versión simplificada los paréntesis son necesarios porque NOT es el operador de mayor precedencia, si no lo ponemos estaríamos diciendo:
(Not $ControlType) = $GetCTRLType

¡MOLA LO DE MIRAR FOCO DE TECLADO Y FOCO DE RATÓN! :smt038
Veo un pequeño fallo, sospecho que no es cosa de la función sino del Select principal. Prueba esto: mueve el ratón sobre Botón2, sácalo y que no esté sobre ningún botón, haz TAB dos veces para que pase el foco del teclado a Botón2 y vuelva a Botón1, mueve el ratón sobre Botón2. A mí no me detecta el cambio de foco, a partir de ahí puedes mover el ratón fuera y dentro de Botón2 todas las veces que quieras (sin pasar por Botón1) que no lo detecta.

Una sugerencia de nota :smt003
¿para qué pasar el nombre de la ventana si un control pertenece únicamente a una ventana?
ya sé que lo haces para GuiGetCursorInfo, pero si averiguas la ventana a la que pertenece el control sin que tenga que ponerlo el usuario sería genial.
Supongo que lo puedes hacer con _WinAPI_GetParent.

¡Buen trabajo sigue así!
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Jonny
Profesional del Autoit
Mensajes: 1042
Registrado: 30 Jun 2008, 20:08

Re: Función para comprobar si el foco está sobre un control

Mensaje por Jonny »

Hola
.- No hace falta usar el operador "==" con números, eso está pensado para cadenas.
Vaya, otro de esos vicios, que he cogido al darme algún que otro quebradero de cabeza el "=", y no fallarme este nunca :).
.- Toda la condición del ContinueLoop creo que es innecesaria, aunque no pongas ese Case tampoco va a entrar los demás si no se cumplen las condiciones...
¿Te refieres a los ContinueLoop que hay en el select del While 1 ... Wend ¿Verdad??.
Supongo que el no ponerlos, equivaldría a un Case Else, pero siempre me ha gustado intentar controlar al máximo lo que hace el código, por eso de que a veces los ordenadores sacan su genio ... :).
.- Deberías usar el tipo de parámetro que se espera en las funciones, si es texto, pon cadenas, si son números, pon números.
Por ejemplo al asignar los colores, al hacer GuiCtrlSetColor($Label1, "16777215"), AutoIT pasa esa cadena a número, pero si le das directamente GuiCtrlSetColor($Label1, 16777215) no tiene que hacer la conversión.
Por cierto, 16777215 es el color blanco, que es el valor más alto posible, todo color por encima de ese es incorrecto, así que el color que pasas a GuiSetBkColor está fuera de rango...
¡Buena observación!.
Parece una tontería, pero seguro que ayuda a optimizar el rendimiento del programa.

Lo del color que comentas, es raro, yo los códigos los saco de
[ul]
http://cloford.com/resources/colours/500col.htm
[/ul]

Siempre supuse que estarían bien...
Lo mismo con tu propia función, un handle es numérico, será extremadamente raro que alguien llame a tu función pasando "" como el handle del control,
si ahí llega un control no inicializado $ControlID probablemente valdrá cero, no una cadena vacía, así que en el error deberías controlar $ControlID=0
Uf ¡nunca se sabe!, aunque lo de comprobar que $ControlID valga 0, tal como lo planteas es cierto que debería hacerse.
.- ¡Permite cerrar la ventana de una forma clara! ¿Por qué has quitado el aspa de cerrar ventana en la barra de título? En una aplicación final te puede interesar por varias razones pero en una prueba así sólo es un poco molestia.
Me he dado cuenta de que los botones cierran la ventana, pero no es evidente, al menos ponles como texto "Cerrar1" y "Cerrar2" o algo así...
Tienes razón. Avisar de que los botones cerraban la ventana (por darles una utilidad) y quitar la inclusión de la librería "Sound.au3", son dos detalles que se me pasaron.
La librería de sonido la incluí, porque en un principio pensaba hacer que al situar el foco del teclado o del ratón sobre uno de los botones, se reprodujera un sonido, pero al final, con las prisas de ir a trabajar opté por hacerlo así para ir más rápido.
Y justo por las prisas, olvidé deciros que la ventana se cerraba con ESC, Alt+F4 o pulsando sobre los botones, motivo por el que quité el aspa de la barra de título.
Veo un pequeño fallo, sospecho que no es cosa de la función sino del Select principal.
Prueba esto: mueve el ratón sobre Botón2, sácalo y que no esté sobre ningún botón, haz TAB dos veces para que pase el foco del teclado a Botón2 y vuelva a Botón1, mueve el ratón sobre Botón2.
A mí no me detecta el cambio de foco, a partir de ahí puedes mover el ratón fuera y dentro de Botón2 todas las veces que quieras (sin pasar por Botón1) que no lo detecta.
¿Te refieres al select del While no?

A mi me funciona bien. Lo que pasa, que ahora que lo dices, quizá la detección de cambios en el select del while no esté bien planteada (en realidad puede plantearse como se quiera).
Pero para detectar la ganancia de un foco en un control, podría afinarse más todavía.

Tal y como está planteado funciona así:

[li]
-Sitúas el foco del ratón o teclado sobre un botón.
-Quitas el foco del botón (no detecta cambio si no lo mueves a otro botón).
-Vuelves a situar el foco sobre el mismo botón (No detecta cambio si no estaba sobre un botón).
[/li]

Creo, que es este el fallo que has detectado...

Resulta, que si te fijas, en $BuffLastObjMouse se almacena el último botón donde se situó el foco del ratón (en realidad el actual), para tenerlo como referencia, y saber si se ha movido a un botón diferente.
Al moverlo a un label por ejemplo, como no estamos detectando que esté sobre ningún tipo de control distinto a "Button", no ocurre nada (uno de los continueloop que decías que no era necesario).
Al volver a situar el foco del ratón sobre el botón en el que estaba anteriormente, no lo detecta como cambio, porque en realidad no se ha movido a otro botón y $BuffLastObjMouse no ha cambiado de valor (se mantiene el del último botón seleccionado por el puntero del ratón). Esto sería tan sencillo como darle un valor vacío ("") a $BuffLastObjMouse cuando el foco del ratón esté en un control distinto a "Button", para que forzosamente no tenga el mismo valor que el botón sobre el que estaba. De esta manera, al situar nuevamente el foco sobre el botón que estaba, sí detectará el cambio, pero esto depende de como quiera hacerse...
Personalmente, creo que lo modificaré, para que sea de esta última manera en el menú que estoy haciendo, porque la idea realmente era que se produjera un evento si se ponía el foco sobre un botón, y aunque lo quites y lo pongas en el mismo, no deja de estar seleccionándose el botón, y debería responder al evento.
No entiendo de todas formas, que te pase en esa situación concreta que describes; si pruebas como te he dicho, verás que ocurre lo mismo que como tú dices, y que te ocurrirá con ambos botones.
Además, también ocurriría con el foco del teclado, pues se comprueba de la misma manera, en caso de haber controles edit, listview, etc.
¿para qué pasar el nombre de la ventana si un control pertenece únicamente a una ventana?
Pues sinceramente, porque como bien dices, es necesario para algunas funciones que utilizo. NO tenía muy claro, si los handles que genera Windows para un control son únicos o sólo únicos en una ventana...
Si puede hacerse como dices, pues sí estaría bien ahorrar un parámetro.

Por cierto, que no digo nada sobre los apuntes que haces sobre las condiciones, porque es cierto que pueden simplificarse, pero analizar eso tan a fondo ¡te deja cao! :).

Salu2!
Avatar de Usuario
Ximorro
Profesional del Autoit
Mensajes: 1500
Registrado: 10 Jul 2009, 12:35
Ubicación: Castellón, España

Re: Función para comprobar si el foco está sobre un control

Mensaje por Ximorro »

Vaya, otro de esos vicios, que he cogido al darme algún que otro quebradero de cabeza el "=", y no fallarme este nunca :).
Pues no lo uses porque es incorrecto, más comprobaciones para AutoIT, al darle un operador para cadenas cuando estás usando números.
Eso incluye también cosas como $MsgEvent==$GUI_EVENT_Close, pues esos son valores numéricos...
¿Te refieres a los ContinueLoop que hay en el select del While 1 ... Wend ¿Verdad??.
Supongo que el no ponerlos, equivaldría a un Case Else, pero siempre me ha gustado intentar controlar al máximo lo que hace el código, por eso de que a veces los ordenadores sacan su genio ... :).
¿Es que hay otro ContinueLoop u otro Select? :smt003
Ese Case yo lo quitaría entero, en un Select (o un Switch) sólo se entra en un Case, no tienes que forzar que no entre en los demás con el ContinueLoop, si no se les cumple la condición seguro que no entra.

Lo de pasar cadenas cuando se espera cadenas y número cuando se espera números ciertamente algo optimizará el rendimiento del programa, pero además seguramente evitarás errores potenciales en las conversiones a que se ve forzado AutoIt, o que no interprete bien lo que le estás pasando.

Interesante lo del página de colores, entonces no sé de dónde has sacado el color 123104238 que pones en GuiSetBkColor, porque en esa página no está. La verdad es que en el vistazo que he echado en esa página parecen estar todos dentro de rango...
Tienes razón. Avisar de que los botones cerraban la ventana (por darles una utilidad) y quitar la inclusión de la librería "Sound.au3", son dos detalles que se me pasaron.
Ejem, pues si es por quitar librerías que no se usan puedes quitar Sound.au3, Array.au3, Constants.au3, File.au3 y String.au3, es decir, más de la mitad :smt005 :smt005
Veo un pequeño fallo, sospecho que no es cosa de la función sino del Select principal.
Prueba esto: mueve el ratón sobre Botón2, sácalo y que no esté sobre ningún botón, haz TAB dos veces para que pase el foco del teclado a Botón2 y vuelva a Botón1, mueve el ratón sobre Botón2.
A mí no me detecta el cambio de foco, a partir de ahí puedes mover el ratón fuera y dentro de Botón2 todas las veces que quieras (sin pasar por Botón1) que no lo detecta.
¿Te refieres al select del While no?
¿¿Es que hay otro Select??
No, no es ese fallo que comentas el que digo, eso que dices efectivamente no es un fallo pues realmente no se cambia foco, yo lo que digo es que cambio el foco al OTRO botón, pero no lo detecta. Haz esto:
.- Abre el programa (que el ratón no esté casualmente sobre un botón). El foco de teclado empieza en Botón1.
.- mueve el ratón sobre Botón2, (efectivamente el label dice que el foco está en Botón2)
.- mueve el ratón fuera de Botón2 y que no esté sobre Botón1, (la etiqueta indica foco en Botón2, pues no ha cambiado, OK)
.- haz TAB dos veces para que pase el foco del teclado a Botón2 y vuelva a Botón1 (aquí ya cambia la etiqueta diciendo foco en Botón1, OK)
.- mueve el ratón sobre Botón2 ==> LA ETIQUETA SE QUEDA CON FOCO EN Botón1, y de hecho ahora mueves el ratón fuera y dentro de Botón2 y aunque va ganando y perdiendo foco la etiqueta no cambia, hasta que vuelves a pasar por Botón1.
"¿Y no será que en este mundo hay cada vez más gente y menos personas?". Mafalda (Quino)
Responder