I'm trying to use setContentBorderThickness:forEdge: to create a bottom bar in a Cocoa application.
mipadi was on to something, but in testing it out, I think maybe this is a slightly different problem:
-(void) adjustContentBorderBasedOnArrayControllerSelection{
if(([[self.resultsArrayController selectionIndexes] count] == 0)){
[[self window] setContentBorderThickness:40.0f forEdge:CGRectMinYEdge];
NSLog(#"%f", [[self window] contentBorderThicknessForEdge:CGRectMinYEdge]);
} else {
[[self window] setContentBorderThickness:60.0f forEdge:CGRectMinYEdge];
NSLog(#"%f", [[self window] contentBorderThicknessForEdge:CGRectMinYEdge]);
}
}
Both of those NSLog() messages show the thickness value is 0.0 even after I explicitly set it. Anyone know what's up with that?
You can use CGRectMinYEdge. (On 64-bit systems, NSMinYEdge is #define'd as CGRectMinYEdge anyway).
By any chance have you checked to make sure [self window] isn't nil? Do you have your outlet setup right? I get that behavior if the outlet to the window isn't set.
Related
I was wondering if there's a way you can programatically change the position of the splitter.
You can change the position of the divider using setPosition:ofDividerAtIndex:
Thanks for the quick reply...
And for interest sake, I found this on the way (might be useful for others)
Thanks...And for interest sake, I found this on the way (might be useful for others) which I found here: on Github
- (CGFloat)positionOfDividerAtIndex:(NSInteger)dividerIndex {
while (dividerIndex >= 0 && [self isSubviewCollapsed:[[self subviews] objectAtIndex:dividerIndex]])
dividerIndex--;
if (dividerIndex < 0)
return 0.0f;
NSRect priorViewFrame = [[[self subviews] objectAtIndex:dividerIndex] frame];
return [self isVertical] ? NSMaxX(priorViewFrame) : NSMaxY(priorViewFrame);
}
I personally used -(float), so use whichever "floats" your boat :D
I'm trying to figure out how to get the frame of all visible windows.
I tried the following code, but it only works for the app itself other windows report {0,0,0,0}
NSArray *windowArray = [NSWindow windowNumbersWithOptions:NSWindowNumberListAllApplications | NSWindowNumberListAllSpaces];
for(NSNumber *number in windowArray){
NSLog(#"Window number: %#", number);
NSWindow *window = [[NSApplication sharedApplication] windowWithWindowNumber:[number intValue]];
NSLog(#"Window: %#", NSStringFromRect( [[window contentView] frame]));
}
Sample code is appreciated.
I figured it out:
NSMutableArray *windows = (__bridge NSMutableArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
for (NSDictionary *window in windows) {
NSString *name = [window objectForKey:#"kCGWindowName" ];
CGRect bounds;
CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[window objectForKey:#"kCGWindowBounds"], &bounds);
NSLog(#"%#: %#",name,NSStringFromRect(bounds));
}
You can't create an NSWindow for a window of another application. In general, you can't access the objects of other applications except through an interface that they cooperate with, like scripting.
You can get what you're looking for using the Quartz Window Services (a.k.a. CGWindowList) API.
I'm not at all sure that the window numbers returned by Cocoa are the same as the window numbers used by that API. In fact, the docs for -[NSWindow windowNumber] specifically say "note that this isn’t the same as the global window number assigned by the window server". I'm note sure to what use you can put the window numbers returned by +[NSWindow windowNumbersWithOptions:] which are not for your application's windows.
I have created a window using -[NSWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces].
It only does half of what I want, though: when I switch spaces, the window also switches spaces (as expected), but my window moves to the back behind all other windows in that space. This is especially bad because my app is active but its window is below all other apps' windows. I tried changing the level to NSFloatingWindowLevel, and that does keep it on top, but then it loses key status (focus) when switching spaces.
I tried NSWindowCollectionBehaviorMoveToActiveSpace for the collection behaviour but it's definitely not what I'm looking for.
Is there hope? I know there are almost no other APIs that relate to Spaces.
Spaces is a pain. My solution was to register for a change notification like so:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(activeSpaceDidChange:)
name:NSWorkspaceActiveSpaceDidChangeNotification
object:nil];
Then in my WindowController class:
- (void) activeSpaceDidChange:(NSNotification *)aNotification {
if ([NSApp isActive]) [[self window] orderFront:self];
}
For borderless windows (created with NSBorderlessWindowMask), I banged my head until I came up with the following modification to Francis':
- (void) activeSpaceDidChange:(NSNotification *)aNotification {
if ([NSApp isActive])
{
NSRect windowRect = [[self window] frame];
[[self window] setStyleMask:NSTitledWindowMask];
[[self window] setStyleMask:NSBorderlessWindowMask];
[[self window] setFrame:windowRect display:YES];
[[NSApplication sharedApplication] activateIgnoringOtherApps : YES];
}
}
I saw others stating that borderless windows have issues which led me to the idea to trick it momentarily into not seeing it as a borderless window. First I had setting the style mask to "Titled" followed by "activateIgnoringOtherApps" and then setting the "borderless" style back, which seemed to be a more logical solution. Yet, just to see what minimal solution was required for it to function, I ended up seeing the above works. Be great if somebody could fill in what is exactly happening that allows this to work.
Swift 5 solution of #francis-mcgrew's answer ⤵︎
// Quick Solution: Place this in the AppDelegate in applicationDidFinishLaunching
NSWorkspace.shared.notificationCenter.addObserver(
self,
selector: #selector(notifySpaceChanged),
name: NSWorkspace.activeSpaceDidChangeNotification,
object: nil
)
// and this in the same class
#objc func notifySpaceChanged() {
window.orderFrontRegardless()
}
In my "InitWithFrame" method of a view I'm setting a tracking area for which I want to capture mouse enter/exit events.
My problems are two fold:
Without NSTrackingInVisibleRect the events won't be called at all.
No matter what "rect" I put it, one that covers the entire view's frame or one that occupies just a small portion of it - the mouse enter/exited events are called for the entire view, regardless of where the mouse cursor is on the view.
this is how I initialize the tracking area:
trackingArea = [[NSTrackingArea alloc] initWithRect:rect
options: (NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect | NSTrackingActiveAlways )
owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
Any clues why this happens? I want the mouse enter/exit events to be called only for a small portion (the bottom part) of my view.
Mike Abdullah's answer explains point 2.
Here is a guess about why you don't receive events at all when not using the NSTrackingInVisibleRect flag:
Probably the variable rect you provide is not within the view's coordinate system. You could use the following code as the designated initializer of your NSView subclass to receive mouseEntered: and mouseExited: events for the whole area of your view:
- (id)initWithFrame:(NSRect)frame
{
if ((self = [super initWithFrame:frame]))
{
//by using [self bounds] we get our internal origin (0, 0)
NSTrackingArea* trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:(NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways) owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
[trackingArea release];
}
return self;
}
Apple's documentation says:
When creating a tracking-area object,
you specify a rectangle (in the view’s
coordinate system), ...
Straight from the docs for NSTrackingInVisibleRect:
The NSTrackingArea object is automatically synchronized with changes in the view’s visible area (visibleRect) and the value returned from rect is ignored.
I'm trying to install a NSTrackingArea into a fullscreen view in order to get mouse moved events.
However, whenever I do, I get an assertion error. I've searched the web, but have not been able to find any leads.
*** Assertion failure in -[_NSFullScreenWindow _setTrackingRect:inside:owner:userData:useTrackingNum:install:], /SourceCache/AppKit/AppKit-1038.25/AppKit.subproj/NSWindow.m:3944
Here's the code that sets up the tracking area (x=1024, y=768):
cocoaWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0.0, 0.0, x,y)
styleMask: NSTitledWindowMask
backing: NSBackingStoreBuffered
defer:NO];
glView = [[WLMacGLView alloc] initWithFrame:NSMakeRect(0.0, 0.0, x,y) pixelFormat:[WLMacGLView defaultPixelFormat]];
[glView setCocoaController:self];
//add the glView as a subview of the window's content view
[[cocoaWindow contentView] addSubview:glView];
NSRect r = [glView frame];
NSTrackingArea *track = [[NSTrackingArea alloc] initWithRect:r options: NSTrackingMouseMoved | NSTrackingActiveWhenFirstResponder | NSTrackingActiveInKeyWindow
owner:self userInfo:nil];
[glView addTrackingArea:track];
[glView enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
[glView createContext];
The assertion happens right after the call to enterFullScreenMode: withOptions:
Anyone got any ideas? Is this not the approach I should be taking to get mouse moved events in a fullscreen window?
If you want to track mouse in the whole view, I think is will be easier to implement the mouseDown:, mouseMoved: and mouseUp: methods in order to get the mouse events.
So the answer to this question turned out to be a bug in my own code.
When initializing the NSTrackingArea, I was passing in the wrong object for owner. The proper thing to pass was the NSView. With that corrected, all works as expected.