我有一个相当简单的问题。我将如何以编程方式添加/删除任务控制中找到的工作空间。我已经看过这篇文章here关于以编程方式更改到另一个空格,我认为它可能与答案类似,使用CGSPrivate.h
。我不需要担心私有框架,因为它不在应用程序商店中。
com.apple.spaces.plist
和添加工作区的帖子,但我不知道如何添加它,因为dict有UUID和其他东西。
答案 0 :(得分:2)
在Mission Control中,这是Dock的辅助功能层次结构(在我的Mac上,OS X 10.10):
Role Position Title Value Description
AXList 632.000000, 1136.000000 (null) (null) (null)
AXDockItem 636.300049, 1138.000000 Finder (null) (null)
AXDockItem 688.300049, 1138.000000 Firefox (null) (null)
…
AXDockItem 1231.699951, 1138.000000 Trash (null) (null)
AXGroup 0.000000, 0.000000 (null) (null) (null)
AXGroup 20.000000, 227.000000 (null) (null) exposéd windows
AXList 0.000000, -2.000000 (null) (null) (null)
AXButton 592.000000, 20.000000 Desktop 1 (null) select Desktop 1
AXButton 864.000000, 20.000000 Desktop 2 (null) select Desktop 2
AXButton 1136.000000, 20.000000 Desktop 3 (null) select Desktop 3
AXButton 1824.000000, 20.000000 (null) (null) add desktop
工作区按钮的位置是删除按钮的中间位置。
我的测试应用:
- (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++;
CFRelease(aRole);
}
}
else
anIndex++;
if (anIndex == theIndex) {
aResultElement = (AXUIElementRef)CFRetain((__bridge CFTypeRef)(anElement));
break;
}
}
CFRelease(aChildren);
}
return aResultElement;
}
- (IBAction)addWorkspace:(id)sender {
if (!AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt:@YES}))
return;
// type control-arrow-up
CGEventRef anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, true);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, false);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// click on the + button
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
CFTypeRef aGroup = [self copyAXUIElementFrom:anAXDockApp role:kAXGroupRole atIndex:0];
CFTypeRef aButton = [self copyAXUIElementFrom:aGroup role:kAXButtonRole atIndex:0];
CFRelease(aGroup);
if (aButton) {
AXError anAXError = AXUIElementPerformAction(aButton, kAXPressAction);
CFRelease(aButton);
}
// option up
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, false);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// type escape
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, true);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, false);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
}
- (IBAction)removeWorkspace:(id)sender {
if (!AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt:@YES}))
return;
// type control-arrow-up
CGEventRef anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, true);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, false);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// move mouse to the top of the screen
CGPoint aPoint;
aPoint.x = 10.0;
aPoint.y = 10.0;
anEvent = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, aPoint, 0);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// click at the location of the workspace
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
CFTypeRef aGroup = [self copyAXUIElementFrom:anAXDockApp role:kAXGroupRole atIndex:0];
CFTypeRef aList = [self copyAXUIElementFrom:aGroup role:kAXListRole atIndex:0];
CFRelease(aGroup);
CFTypeRef aButton = [self copyAXUIElementFrom:aList role:kAXButtonRole atIndex:1]; // index of the workspace
CFRelease(aList);
if (aButton) {
CFTypeRef aPosition;
AXError anAXError = AXUIElementCopyAttributeValue(aButton, kAXPositionAttribute, &aPosition);
if (anAXError == kAXErrorSuccess) {
AXValueGetValue(aPosition, kAXValueCGPointType, &aPoint);
CFRelease(aPosition);
// click
anEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, aPoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, aPoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
CFRelease(aButton);
}
}
// option up
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, false);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// type escape
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, true);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, false);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
}
答案 1 :(得分:1)
与Willeke
类似,经过数小时的代码后我才能完成此任务。这是我的代码,然后我将解释它对未来遇到此问题的人的作用。
In .h
My code is in AppDelegate (it is a menubar app).
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
...
// Workspace mutations vars
NSInteger workspacesToRemove; // Used in removing workspaces (as
loop)
}
// Define constants for sizes
#define kWORKSPACE_WIDTH 145
#define kWORKSPACE_HEIGHT 90
#define kWORKSPACE_SPACING 30
In .m
- (void)removeAllWorkspaces
{
NSDictionary *spacesPlist = [NSDictionary dictionaryWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.spaces.plist"]];
NSDictionary *spacesDisplayConfig = [spacesPlist objectForKey:[[spacesPlist allKeys] objectAtIndex:0]];
NSArray *spaceProperties = [spacesDisplayConfig objectForKey:@"Space Properties"];
NSInteger numberOfWorkspaces = [spaceProperties count];
NSLog(@"Number of workspaces: %ld", (long)numberOfWorkspaces);
// Set counter
workspacesToRemove = numberOfWorkspaces;
[self openMissionControl];
}
#pragma mark Open/Close step methods
- (void)openMissionControl
{
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
/*
*/
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
[self performSelector:@selector(moveMouseToUpdateMissionControl) withObject:nil afterDelay:1];
}
- (void)moveMouseToUpdateMissionControl
{
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
[self performSelector:@selector(moveMouseToCloseRightmostWorkspace) withObject:nil afterDelay:1];
}
- (void)moveMouseToCloseRightmostWorkspace
{
NSRect workspaceRect = [self rectForWorkspaces];
NSInteger closeX = (workspaceRect.origin.x + workspaceRect.size.width) - kWORKSPACE_WIDTH;
CGPoint closePoint = CGPointMake(closeX, workspaceRect.origin.y);
// Move mouse to point
CGEventRef mouseMove = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, closePoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouseMove);
CFRelease(mouseMove);
// Click
[self performSelector:@selector(clickMouseAtPoint:) withObject:[NSValue valueWithPoint:closePoint] afterDelay:2]; // Must be equal or greater 1.5
}
- (void)clickMouseAtPoint:(NSValue *)pointValue
{
CGPoint clickPoint = [pointValue pointValue];
// Click
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, clickPoint, kCGMouseButtonLeft));
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, clickPoint, kCGMouseButtonLeft));
workspacesToRemove--;
NSLog(@"%ld", (long)workspacesToRemove);
if (workspacesToRemove > 1) {
[self performSelector:@selector(moveMouseToCloseRightmostWorkspace) withObject:nil afterDelay:2];
} else {
[self performSelector:@selector(closeMissionControl) withObject:nil afterDelay:1];
}
}
- (void)closeMissionControl
{
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
}
#pragma mark
#pragma mark Adding Workspaces
- (void)openWorkspaces:(NSInteger)numberToOpen
{
// Open Mission control
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
/*
*/
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
[NSThread sleepForTimeInterval:2];
// Move mouse to point
CGEventRef mouseMove = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouseMove);
CFRelease(mouseMove);
for (NSInteger i = 0; i < numberToOpen; i++) {
// Add as many times as needed
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
[NSThread sleepForTimeInterval:1];
}
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
}
- (NSRect)rectForWorkspaces
{
NSDictionary *spacesPlist = [NSDictionary dictionaryWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.spaces.plist"]];
NSDictionary *spacesDisplayConfig = [spacesPlist objectForKey:[[spacesPlist allKeys] objectAtIndex:0]];
NSArray *spaceProperties = [spacesDisplayConfig objectForKey:@"Space Properties"];
NSInteger numberOfWorkspaces = [spaceProperties count];
NSInteger totalSpacing = (numberOfWorkspaces - 1) * kWORKSPACE_SPACING;
NSInteger totalLengthOfWorkspaces = numberOfWorkspaces * kWORKSPACE_WIDTH;
NSInteger totalRectWidth = totalSpacing + totalLengthOfWorkspaces;
NSRect workspaceRect = NSMakeRect(0, 0, totalRectWidth, kWORKSPACE_HEIGHT);
// Calculate center x or screen
NSInteger screenCenter = [[NSScreen mainScreen] frame].size.width / 2;
workspaceRect.origin.x = screenCenter - (workspaceRect.size.width / 2);
workspaceRect.origin.y = kWORKSPACE_SPACING;
return workspaceRect;
}
对于删除工作空间,第一种方法removeAllWorkspaces
是起点。
此代码从com.apple.spaces.plist
文件中获取打开的工作空间数,然后设置变量workspacesToRemove
。这个变量对于循环很重要,因为当有方法链(我称之为方法链)时很难做for-loop
。
接下来,我通过CGEvents
调用一种方法来打开任务控制。然后我将鼠标移动到屏幕的顶角,以确保工作区图标正确居中。
接下来,代码使用rectForWorkspaces
方法确定最右边工作区的关闭按钮的位置。
这是一个非常简单的方法,但它是发生的事情的主要部分。
它计算了工作空间在任务控制中所处位置的矩形。这是一个代表它计算的图像:
然后我拿这个矩形,减去145(工作区图标宽度),然后在弹出时单击关闭按钮。
此部分循环,直到关闭所有工作空间(1除外)。
FWI:它被分成许多方法的原因是我可以循环回到特定的方法并在延迟后执行方法而不会阻塞线程。
添加工作区要容易得多。
它只有一种方法(openWorkspaces:(NSInteger)numberToOpen
),它打开任务控制,将鼠标移动到位置,并点击次数,直到添加了所有工作空间。非常简单。
答案 2 :(得分:0)
最近我能找到一个解决方案就是通过苹果脚本实现这一点 - 请参阅ѺȐeallү的答案,链接如下: