objective-c - todas - teclado mac funciones
Identificar de forma Ășnica la ventana activa en OS X (2)
Estoy tratando de parchar una aplicación que cambia el tamaño de las ventanas usando la API de accesibilidad.
Necesito mantener un diccionario con los tamaños anteriores de ventanas. La clave debe identificar la ventana activa actualmente. En este momento, esta ventana activa se recupera a través de NSAccessibilityFocusedWindowAttribute
al presionar una tecla de NSAccessibilityFocusedWindowAttribute
rápido.
Sin embargo, cada vez que se llama a este método, el AXUIElementRef
devuelto que identifica la ventana es diferente . Esto, por supuesto, significa que no puedo usarlo como una clave de diccionario; el diccionario no encontrará la entrada correspondiente.
El siguiente código reproduce el problema:
-(IBAction)testWindowIdentification:(id)sender{
AXUIElementRef focusedApp;
AXUIElementRef focusedWindow;
AXUIElementCopyAttributeValue(_systemWideElement,
(CFStringRef) kAXFocusedApplicationAttribute,
(CFTypeRef*) &focusedApp);
AXUIElementCopyAttributeValue((AXUIElementRef) focusedApp,
(CFStringRef) NSAccessibilityFocusedWindowAttribute,
(CFTypeRef*) &focusedWindow);
CFShow(focusedWindow);
}
_systemWideElement
se ha inicializado en el método init
mediante una llamada a AXUIElementCreateSystemWide()
.
La declaración de CFShow
muestra claramente diferentes ID cada vez que se llama al método (aunque la misma ventana está activa), lo que no me sirve de nada:
<AXUIElement 0x47e850> {pid=42463}
<AXUIElement 0x47e890> {pid=42463}
<AXUIElement 0x47e2c0> {pid=42463}
…
La documentación sobre AXUIElement
no muestra ningún método que recupere un atributo único para el elemento UI, y tampoco lo hace el protocolo NSAccessibility
. El PID único no es suficiente para mí, ya que un proceso puede tener varias ventanas.
¿Cómo puedo recuperar algún identificador único de la ventana activa en Cocoa?
(Por cierto, el código real está verificando los códigos de retorno en las llamadas anteriores; no hay error, las llamadas tienen éxito).
Creo que podría usar las funciones de los Servicios de ventanas de Quartz, específicamente CGWindowListCreateDescriptionFromArray
para enumerar las ventanas actualmente activas en una aplicación en particular.
Esta llamada es de nivel inferior a AppKit y no le dirá cuál es la ventana activa, pero le proporcionará ID de ventana que son únicas para la sesión de usuario actual. No es una gran solución, pero puede comparar la información de los límites de las ventanas con lo que recibe de las API de accesibilidad para asociar las ventanas con sus ID reales.
Rob Keniger tiene la estrategia correcta con su respuesta aquí . Lo único que falta en esta respuesta (y, de hecho, el motivo de la colocación de la recompensa) es una implementación viable que tome la ventana activa actual y la traduzca en una clave única adecuada para la indexación en el contexto de la aplicación actual.
La solución de Rob dibuja esto mediante el uso del CGWindowID
dado en el contexto de los servicios de ventana de cuarzo. Por supuesto, está fuertemente implícito que esta referencia de ventana solo es útil para su aplicación actual .
Obtener esta referencia de ventana es complicado, ya que no existen garantías sólidas entre la API de accesibilidad y los servicios de ventana de cuarzo. Sin embargo, puede solucionar esto de las siguientes maneras:
Use
extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);
, como se documenta aquí . No se garantiza que funcione, pero funciona como una prueba en la planta baja para comenzar si funciona en su versión de OSX.Obtenga el
CGWindowID
directamente, utilizando, por ejemplo,HIWindowGetCGWindowID()
. Puede encontrar más detalles sobre la selección de la ventana activa y la extracción de la ID en el manual de referencia de Carbon Window Manager (advertencia: PDF grande).Catalogue su conjunto de
CGWindowID
usando algo comoCGWindowListCreateDescriptionFromArray
, exactamente como Rob sugirió. El objetivo aquí es encontrar algún esquema para enlazar la API de accesibilidad y Quartz, pero esto es posible utilizando, por ejemplo, una devolución de llamada vinculada al contexto de su ventana activa actual. Sin embargo, honestamente no conozco un ejemplo óptimo de esto que esté debidamente preparado para el futuro .
De las opciones, recomiendo ir con 2.
para sus necesidades actuales, si no puede crear algún otro decorador para sus ventanas para identificarlas de manera única. Actualmente está definido en la base de código heredado, pero hará lo que desees.
Mucha suerte con tu aplicación.