2016-03-06 24 views
1

對於我的代碼,我試圖得到AXMenuItemsAXMenuAXUIElementRef)的陣列。菜單成功登錄了,這裏是我的代碼:如何獲得AXMenuItems從AXMenu數組?

NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]; 
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]); 

CFTypeRef aChildren; 
AXUIElementCopyAttributeValue(anAXDockApp, kAXChildrenAttribute, &aChildren); 

SafeCFRelease(anAXDockApp); 

CFTypeRef aMenu = CFArrayGetValueAtIndex(aChildren, 0); 

NSLog(@"aMenu: %@", aMenu); 

        // Get menu items 
CFTypeRef aMenuChildren; 
AXUIElementCopyAttributeValue(aMenu, kAXVisibleChildrenAttribute, &aMenuChildren); 

for (NSInteger i = 0; i < CFArrayGetCount(aMenuChildren); i++) { 

    AXUIElementRef aMenuItem = [self copyAXUIElementFrom:aMenu role:kAXMenuItemRole atIndex:i]; 

    NSLog(@"aMenuItem: %@", aMenuItem); // logs (null) 

    CFTypeRef aTitle; 
    AXUIElementCopyAttributeValue(aMenuItem, kAXTitleAttribute, &aTitle); 


    if ([(__bridge NSString *)aTitle isEqualToString:@"New Window"] || [(__bridge NSString *)aTitle isEqualToString:@"New Finder Window"]) /* Crashes here (i can see why)*/{ 

     AXUIElementPerformAction(aMenuItem, kAXPressAction); 

     [NSThread sleepForTimeInterval:1]; 

     break; 

    } 

} 

什麼是得到AXMenuItems列表中選擇正確的方式?

截圖輔助功能檢查的:

inspector

回答

0

我已經想出了一個答案,使用@Willeke答案使用AXUIElementCopyElementAtPosition()來獲取菜單。由於有多個碼頭方向和隱藏,我在.h文件中創建枚舉,因爲它會更容易閱讀比0,1,或2

// .h 
typedef enum { 
    kDockPositionBottom, 
    kDockPositionLeft, 
    kDockPositionRight, 
    kDockPositionUnknown 
} DockPosition; 

typedef enum { 
    kDockAutohideOn, 
    kDockAutohideOff 
} DockAutoHideState; 

然後,我添加方法來獲取這些在.m

// .m 
- (DockPosition)dockPosition 
{ 
    NSRect screenRect = [[NSScreen mainScreen] frame]; 

    NSRect visibleRect = [[NSScreen mainScreen] visibleFrame]; 

    // Dont need to remove menubar height 
    visibleRect.origin.y = 0; 

    if (visibleRect.origin.x > screenRect.origin.x) { 
     return kDockPositionLeft; 
    } else if (visibleRect.size.width < screenRect.size.width) { 
     return kDockPositionRight; 
    } else if (visibleRect.size.height < screenRect.size.height) { 
     return kDockPositionBottom; 
    } 
    return kDockPositionUnknown; 
} 

- (DockAutoHideState)dockHidden 
{ 
    NSString *plistPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.dock.plist"]; 
    NSDictionary *dockDict = [NSDictionary dictionaryWithContentsOfFile:plistPath]; 

    CFBooleanRef autohide = CFDictionaryGetValue((__bridge CFDictionaryRef)dockDict, @"autohide"); 
    if (CFBooleanGetValue(autohide) == true) { 
     return kDockAutohideOn; 
    } 
    return kDockAutohideOff; 
} 

對於未知的碼頭位置的狀態,我的情況下添加它無法從屏幕位置計算。

然後,我用一個方法來獲得從菜單欄碼頭項目:

- (AXUIElementRef)getDockItemWithName:(NSString *)name 
{ 
    NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]; 
    AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]); 

    AXUIElementRef aList = [self copyAXUIElementFrom:anAXDockApp role:kAXListRole atIndex:0]; 

    CFTypeRef aChildren; 
    AXUIElementCopyAttributeValue(aList, kAXChildrenAttribute, &aChildren); 
    NSInteger itemIndex = -1; 

    for (NSInteger i = 0; i < CFArrayGetCount(aChildren); i++) { 

     AXUIElementRef anElement = CFArrayGetValueAtIndex(aChildren, i); 

     CFTypeRef aResult; 

     AXUIElementCopyAttributeValue(anElement, kAXTitleAttribute, &aResult); 

     if ([(__bridge NSString *)aResult isEqualToString:name]) { 

      itemIndex = i; 
     } 
    } 
    SafeCFRelease(aChildren); 

    if (itemIndex == -1) return nil; 

    // We have index now do something with it 

    AXUIElementRef aReturnItem = [self copyAXUIElementFrom:aList role:kAXDockItemRole atIndex:itemIndex]; 
    SafeCFRelease(aList); 
    return aReturnItem; 
} 

SafeCFRelease()方法來檢查,如果傳遞的值不爲零,然後釋放一個非常簡單的方法(有一些問題更早)。

void SafeCFRelease(CFTypeRef cf) 
{ 
    if (cf) CFRelease(cf); 
} 

這種方法[copyAXUIElementFrom: role: atIndex:]@Willeke回答我的問題另外一個方法:

- (AXUIElementRef)copyAXUIElementFrom:(AXUIElementRef)theContainer role:(CFStringRef)theRole atIndex:(NSInteger)theIndex { 
    AXUIElementRef aResultElement = NULL; 
    CFTypeRef aChildren; 
    AXError anAXError = AXUIElementCopyAttributeValue(theContainer, kAXChildrenAttribute, &aChildren); 
    if (anAXError == kAXErrorSuccess) { 
     NSUInteger anIndex = -1; 
     for (id anElement in (__bridge NSArray *)aChildren) { 
      if (theRole) { 
       CFTypeRef aRole; 
       anAXError = AXUIElementCopyAttributeValue((__bridge AXUIElementRef)anElement, kAXRoleAttribute, &aRole); 
       if (anAXError == kAXErrorSuccess) { 
        if (CFStringCompare(aRole, theRole, 0) == kCFCompareEqualTo) 
         anIndex++; 
        SafeCFRelease(aRole); 
       } 
      } 
      else 
       anIndex++; 
      if (anIndex == theIndex) { 
       aResultElement = (AXUIElementRef)CFRetain((__bridge CFTypeRef)(anElement)); 
       break; 
      } 
     } 
     SafeCFRelease(aChildren); 
    } 
    return aResultElement; 
} 

考慮這些代碼,我把它放到我的方法之一:

//檢查是否在碼頭(否則不能這樣做)

   if ([self isAppOfNameInDock:[appDict objectForKey:@"AppName"]]) { 

        // Get dock item 

        AXUIElementRef aDockItem = [self getDockItemWithName:[appDict objectForKey:@"AppName"]]; 

        AXUIElementPerformAction(aDockItem, kAXShowMenuAction); 

        [NSThread sleepForTimeInterval:0.5]; 

        CGRect aRect; 

        CFTypeRef aPosition; 
        AXUIElementCopyAttributeValue(aDockItem, kAXPositionAttribute, &aPosition); 
        AXValueGetValue(aPosition, kAXValueCGPointType, &aRect.origin); 
        SafeCFRelease(aPosition); 

        CFTypeRef aSize; 
        AXUIElementCopyAttributeValue(aDockItem, kAXSizeAttribute, &aSize); 
        AXValueGetValue(aSize, kAXValueCGSizeType, &aRect.size); 
        SafeCFRelease(aSize); 

        SafeCFRelease(aDockItem); 

        CGPoint aMenuPoint; 

        if ([self dockHidden] == kDockAutohideOff) { 
         switch ([self dockPosition]) { 
          case kDockPositionRight: 
           aMenuPoint = CGPointMake(aRect.origin.x - 18, aRect.origin.y + (aRect.size.height/2)); 
           break; 
          case kDockPositionLeft: 
           aMenuPoint = CGPointMake(aRect.origin.x + aRect.size.width + 18, aRect.origin.y + (aRect.size.height/2)); 
           break; 
          case kDockPositionBottom: 
           aMenuPoint = CGPointMake(aRect.origin.x + (aRect.size.width/2), aRect.origin.y - 18); 
           break; 
          case kDockPositionUnknown: 
           aMenuPoint = CGPointMake(0, 0); 
           break; 
         } 
        } else { 

         NSRect screenFrame = [[NSScreen mainScreen] frame]; 

         switch ([self dockPosition]) { 
          case kDockPositionRight: 
           aMenuPoint = CGPointMake(screenFrame.size.width - 18, aRect.origin.y + (aRect.size.height/2)); 
           break; 
          case kDockPositionLeft: 
           aMenuPoint = CGPointMake(screenFrame.origin.x + 18, aRect.origin.y + (aRect.size.height/2)); 
           break; 
          case kDockPositionBottom: 
           aMenuPoint = CGPointMake(aRect.origin.x + (aRect.size.width/2), screenFrame.size.height - 18); 
           break; 
          case kDockPositionUnknown: 
           aMenuPoint = CGPointMake(0, 0); 
           break; 
         } 
        } 

        if ((aMenuPoint.x != 0) && (aMenuPoint.y != 0)) { 

         AXUIElementRef _systemWideElement = AXUIElementCreateSystemWide(); 

         AXUIElementRef aMenu; 
         AXUIElementCopyElementAtPosition(_systemWideElement, aMenuPoint.x, aMenuPoint.y, &aMenu); 

         SafeCFRelease(_systemWideElement); 

         // Get menu items 
         CFTypeRef aMenuChildren; 
         AXUIElementCopyAttributeValue(aMenu, kAXVisibleChildrenAttribute, &aMenuChildren); 

         NSRunningApplication *app = [[NSRunningApplication runningApplicationsWithBundleIdentifier:[appDict objectForKey:@"BundleID"]] objectAtIndex:0]; 

         for (NSInteger i = 0; i < CFArrayGetCount(aMenuChildren); i++) { 

          AXUIElementRef aMenuItem = [self copyAXUIElementFrom:aMenu role:kAXMenuItemRole atIndex:i]; 

          CFTypeRef aTitle; 
          AXUIElementCopyAttributeValue(aMenuItem, kAXTitleAttribute, &aTitle); 

          // Supports chrome, safari, and finder 
          if ([(__bridge NSString *)aTitle isEqualToString:@"New Window"] || [(__bridge NSString *)aTitle isEqualToString:@"New Finder Window"]) { 

           AXUIElementPerformAction(aMenuItem, kAXPressAction); 

           NSInteger numberOfWindows = [self numberOfWindowsOpenFromApplicationWithPID:[app processIdentifier]]; 
           // Wait until open 
           while ([self numberOfWindowsOpenFromApplicationWithPID:[app processIdentifier]] <= numberOfWindows) { 
           } 
           break; 
          } 
         } 
         SafeCFRelease(aMenu); 
         SafeCFRelease(aMenuChildren); 
        } 
       } 

這是非常複雜的,但它的作品。我可能無法解釋它,但我壓力測試了這個代碼,它工作得很好。

0

回答這個問題:「如何獲得AXMenuItems從AXMenu數組?」:aMenuChildren是菜單項列表。爲了確保您可以通過角色kAXMenuItemRole過濾。

回答這個問題:「爲什麼它不工作?」:菜單不是菜單。當您在Axccessibility督察檢查菜單,它會顯示一個警告「父不報告元素作爲它的一個子」。 Dock應用程序有一個孩子,一個Dock項目列表。

+0

我使用兒童列表來檢查它是否有某個標題。然後行動 – Minebomber

+0

我編輯了我的答案。 – Willeke

+0

嗯。謝謝。我對可訪問性仍然很陌生。我會試圖找出它 – Minebomber