0
votes

I'm trying to create an OS X application that allows an external application (i.e. textEdit) to float on top of the running application without keeping focus, but never disappearing. Basically what I'm trying to do is mimic the ForceQuit Application (press option-command-esc to see it), where it's always on top but doesn't always have focus.

What I'm doing now with limited success is this:

Creating a NSTimer to keep putting it on top:

_timer = [NSTimer scheduleTimerWithTimeInterval:0.1 target:self selector:@selector(keepTextEditOnTop) userInfo:nil repeats:YES];

...

Bring textEdit to the front:

- (void)keepTextEditOnTop {
    AXUIElementRef appRef = AXUIElementCreateApplication(_textEditProcessID);
    AXUIElementSetAttributeValue(appRef, kAXFrontmostAttribute, kCFBooleanTrue);
}

The textEdit window will stay on top if you click off it (make it lose focus). But there are two problems. 1.) It will flash briefly putting it back on top 2.) It will essentially always keep focus so you can't interact with the underlying app.

From the appRef, you can get the window:

CFArrayRef windowList;
AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList);

AXUIElementRef windowRef = (AXUIElementRef) CFArrayGetValueAtIndex(windowList, 0);

But I don't think it will be possible to convert it into a NSWindow. If I could do that, I imagine I could make it a child window of the main window or maybe just a floating window.

Any ideas on how this could be done?

Edit- Adding suggestions from @TheDarkKnight

I'm trying this:

- (void)testing{

    CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
    for (NSMutableDictionary* entry in (__bridge NSArray*)windowList)
    {
        NSString* ownerName = [entry objectForKey:(__bridge id)kCGWindowOwnerName];
        NSString *windowNum = [entry objectForKey:(__bridge id)kCGWindowNumber];
    if([ownerName isEqualToString:@"Sublime Text"]){
       [self holdToTheFront:[windowNum integerValue]];
    }
}
CFRelease(windowList);

}

Getting a EXC_BAD_ACCESS error as noted in the comment below:

- (void)holdToTheFront:(NSInteger)winNum {
     objc_object*  nsviewObject = reinterpret_cast<objc_object *>(winNum);

    //Getting a "Thread 1:EXC_BAD_ACCESS (code=1, address=0x18)" at the line below:
    NSWindow* nsWindowObject = ((id (*)(id, SEL))objc_msgSend)((__bridge NSView *)nsviewObject, sel_registerName("window"));

    int NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0;
    ((id (*)(id, SEL, NSUInteger))objc_msgSend)(nsWindowObject, sel_registerName("setCollectionBehavior:"), NSWindowCollectionBehaviorCanJoinAllSpaces);
}
1
Do you have any update on this?sliwinski.lukas

1 Answers

0
votes

In order for an application window to be visible on all spaces, you can do something like this: -

objc_object* nsviewObject = reinterpret_cast<objc_object *>(windowObject);
objc_object* nsWindowObject = objc_msgSend(nsviewObject, sel_registerName("window"));
int NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0;
objc_msgSend(nsWindowObject, sel_registerName("setCollectionBehavior:"), NSWindowCollectionBehaviorCanJoinAllSpaces);

Where windowObject is the windowNumber of the application window you want to remain on top.