2011-09-14 18 views
26

Sto tentando di correggere an application che ridimensiona le finestre utilizzando l'API di accessibilità.Identificare in modo univoco la finestra attiva su OS X

Ho bisogno di mantenere un dizionario con le dimensioni precedenti di Windows. La chiave deve identificare la finestra attualmente attiva. Al momento, questa finestra attiva viene recuperata tramite NSAccessibilityFocusedWindowAttribute premendo un tasto di scelta rapida.

Tuttavia, ogni volta che viene chiamato questo metodo, il valore restituito AXUIElementRef che identifica la finestra è diverso! Questo ovviamente significa che non posso usarlo come chiave del dizionario - il dizionario non troverà la voce corrispondente.

Il seguente codice riproduce il 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 è stato inizializzato nel metodo init utilizzando una chiamata a AXUIElementCreateSystemWide().

La dichiarazione CFShow mostra chiaramente diversi ID ogni volta che il metodo viene chiamato (anche se la stessa finestra è attiva), che è inutile per me:

<AXUIElement 0x47e850> {pid=42463} 
<AXUIElement 0x47e890> {pid=42463} 
<AXUIElement 0x47e2c0> {pid=42463} 
… 

Il documentation on AXUIElement non mostra alcun metodo che recupera un attributo univoco per l'elemento UI, e nemmeno lo fa that of the NSAccessibility protocol. Il PID univoco è non abbastanza per me, poiché un processo può avere più finestre.

Come recuperare un identificatore univoco della finestra attiva in Cocoa?

(A proposito, il codice vero e proprio sta controllando i codici di ritorno delle chiamate di cui sopra, non c'è nessun errore, le chiamate riescono.)

+1

@JeremyBanks Il risponditore originale ha l'idea giusta qui. Puoi effettivamente usare Quartz per ottenere un 'CGWindowID' dopo aver determinato la finestra focalizzata, se [questa risposta] (http://stackoverflow.com/a/312099/517815) deve essere creduta. Questo _should_ ti dà l'identificatore di finestra univoco che stai sperando, che puoi passare impunemente nel contesto della tua attuale applicazione. Fammi sapere se desideri una versione più coerente e completa di questo come una risposta reale. – MrGomez

+0

@MrGomez Certo, una risposta del genere sarebbe grandiosa. :) –

+0

@JeremyBanks Lo farà. Oggi sono un po 'sovraccarico, ma cercherò di ottenere una risposta a questo qualche tempo dopo questa sera (PST). :) – MrGomez

risposta

16

Rob Keniger ha la strategia giusta con his answer here. L'unica cosa che manca a questa risposta (e in effetti, la ragione per il posizionamento di taglie) è un'implementazione praticabile che prende la finestra attiva corrente e la traduce in una chiave univoca adatta per l'indicizzazione nel contesto dell'applicazione corrente.

soluzione di Rob schizzi questo fuori attraverso l'uso del CGWindowID dato nel contesto di Quartz Window Services. Ovviamente, è implicito che il riferimento a questa finestra sia only useful for your current application.

Ottenere questo riferimento di finestra è complicato, perché non esistono forti garanzie tra Accessibility API e Quartz Window Services. Tuttavia, è possibile aggirare questo nei seguenti modi:

  1. Usa extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);, come documented here.Questo non è garantito per funzionare, ma funziona come un test del piano terra per avviare le operazioni se funziona nella versione di OSX.

  2. Ottenere CGWindowID direttamente, utilizzando, ad esempio, HIWindowGetCGWindowID(). Maggiori dettagli sulla selezione della finestra attiva e sull'estrazione dell'ID sono disponibili in the reference manual for the Carbon Window Manager (avviso: PDF di grandi dimensioni).

  3. Catalog il tuo CGWindowID impostato utilizzando qualcosa come CGWindowListCreateDescriptionFromArray, esattamente come suggerito Rob. L'obiettivo qui è quindi quello di trovare alcuni schemi per collegare l'Accessibility API e Quartz, ma ciò è concepibile utilizzando, ad esempio, una callback legata al contesto della finestra attiva corrente. Sinceramente non conosco un esempio ottimale di questo che sia correttamente aggiornato a prova di futuro.

Tra le opzioni, vi consiglio di andare con 2. per le vostre esigenze attuali, se non sei in grado di creare qualche altra decoratore per le finestre per loro identificare in modo univoco. È attualmente definito nel codice base legacy, ma farà ciò che desideri.

Buona fortuna con la vostra applicazione.

+0

Come rilevare che nessuna finestra è selezionata o tutte le finestre sono inattive (ad esempio il controllo è sul desktop)? –

+0

Scusami, come usare "extern" C "AXError _AXUIElementGetWindow (AXUIElementRef, CGWindowID * out);" in Swift? – allenlinli

8

Penso che potrebbe essere in grado di utilizzare le funzioni di quarzo finestra Servizi , in particolare CGWindowListCreateDescriptionFromArray per enumerare le finestre attive in una determinata app.

Questa chiamata è di livello inferiore rispetto a AppKit e non sta per indicare quale è la finestra attiva, ma fornirà ID di finestra che sono univoci per la sessione utente corrente. Non è un'ottima soluzione, ma puoi confrontare le informazioni sui limiti delle finestre con ciò che ricevi dalle API di accessibilità per associare le finestre ai loro ID reali.