2016-03-06 4 views
1

Для моего кода я пытаюсь получить массив AXMenuItems от AXMenu (AXUIElementRef). В меню входит успешно, и вот мой код:Как получить массив 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?

Скриншот Accessibility инспектора:

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.

На вопрос "Почему это не работает?": Меню не является меню. Когда вы проверяете меню в Inspectibility Inspector, оно отображает предупреждение «Parent не сообщает элемент как один из его дочерних элементов». Приложение Dock имеет один ребенок, список элементов док-станции.

+0

Я использую список детей, чтобы проверить, есть ли у него определенный заголовок. Затем действие – Minebomber

+0

Я отредактировал свой ответ. – Willeke

+0

Хммм. Благодарю. Я все еще очень новичок в доступности. Я постараюсь выяснить это – Minebomber

 Смежные вопросы

  • Нет связанных вопросов^_^