I was wondering if it is possible to make my NSWindow show above absolutely everything. I current set the window level to CGShieldingWindowLevel using the code...
[self setLevel:CGShieldingWindowLevel()];
...and this does a great job for the most part, but it isn't quite enough. The window is not drawn when mission control or expose is active, and it is drawn below the mouse. I was wondering if it was possible to crearte a truly "top level" which would be drawn above all these elements.
Any help would be greatly appreciated. Thanks!
In regards to your question about Expose, you probably just haven't set your collection behavior correctly. Make a subclass of NSWindow, and somewhere in your initialization do something along the following:
self.collectionBehavior = (NSWindowCollectionBehaviorStationary | NSWindowCollectionBehaviorIgnoresCycle);
If you want your window on all spaces (which you probably do) don't forget to also add NSWindowCollectionBehaviorCanJoinAllSpaces.
In terms of your question about the mouse, unfortunately I do not think there's a way to draw above it.
Related
A Tale of Two Subclasses
By Ben Stock
Prologue
I'm in the process of making a really nice looking set of controls which automatically change their appearance depending on the type of window they're used in (e.g. If you drop a button in a normal window, it looks like any other standard Aqua button. If you drop it on an NSPanel with a window mask of NSHUDWindowMask, however, it'll automatically switch its style to look good on a HUD background. So far, I've subclassed NSButton, NSTextField, NSSlider, and NSSearchField. Last night I started on NSTabView, only to be slammed down by its lack of customizability. It's a real pain in the ass, but I'm a developer, so I'm used to finding my own way. The first thing I think to do is add an instance of NSSegmentedControl in place of the private tabs used by NSTabView. So far, so good. I've got the buttons selectable, they automatically update when new NSTabViewItem's are added, and they work just like the real thing.
And the Pain Begins …
Finally, I start to style my segments, and … WTF have I gotten myself into‽ I should've just gone into acting or something. Objective-C development is slowly taking years off my life. No matter what I do, the "tracking areas" used by NSSegmentedCell don't seem to be updating when my segment widths change. So when my widths change, my artwork does, too. However, the actual tracking area doesn't update (even when I override -updateTrackingAreas. It's really hard to explain, so I decided to draw my segment rectangles behind and in front of the ones drawn by super in -drawSegment:inFrame:withView. Here's a screenshot with my art drawn on top of the underlying tracking areas:
And here's super's implementation above my segment rects:
I've tried overriding everything I can think of. Here are a few of the methods I've overridden (and un-overridden):
-cellSize (NSSegmentedCell)
-cellSizeForBounds: (NSSegmentedCell)
-sizeToFit (NSSegmentedControl)
-intrinsicContentSize (NSSegmentedControl)
-setWidth:forSegment: (NSSegmentedControl/Cell)
-startTrackingAt:inView: (NSSegmentedCell)
-continueTracking:at:inView: (NSSegmentedCell)
-stopTracking:at:inView:mouseIsUp: (NSSegmentedCell)
At this point, some of those methods in the above list are still using my overrides and some aren't. I've mixed and matched, deleted, simplified, rewrote, and refactored, and no matter what I do, the underlying rectangles don't change. I love Apple as much as the next guy, but their view of customization needs to change. I can't stand not being able to understand what's going on in the implementation of all these stupid controls. Not to mention the fact that I still can't fully wrap my head around Auto Layout (which is about the most un-"auto" thing I've ever dealt with), but that's a post for another day. Anyway, if anybody could help a brotha out, I'd be super grateful. Sorry for ranting and thanks for reading!
P.S. None of these things are finished, so please don't be too hard on a few pixel imperfections. ;-)
Generally, when editing text in a text field/input/area/editor, pressing ⌘A will select all of the current text in said field/input/area/editor. I've subclassed NSTextField and NSTextFieldCell, and no matter what I try, I can't seem to get basic "Select All" functionality working. I've tried implementing delegate protocols, intercepting events, manipulating commands made by selectors, and every other thing I can think of. What gives? (I can already hear the "Ever heard of Google?" refrains because of how simple this probably is, but I haven't found a single answer out there. I guess I can thank iOS for that.)
And before I forget to mention it, I also dragged a standard NSTextField into my nib to see if a non-subclassed NSTextField implements Select All behavior by default, and to my shock, it doesn't. Am I going crazy here, or am I completely overlooking something? Isn't Select All almost a requirement when implementing a text field? Apple's First Responder proxy handles everything under the sun (including two versions of selectAll (selectAll and selectAll:), but the n00b is strong with me, and I can't seem to make sense of any of this.
Any help/ideas would be immensely appreciated. Cheers!
The application menu handles sending the keyboard shortcut actions to the application's current first responder. The missing connection would explain why your regular NSTextField objects are missing this functionality as well.
I'm learning Objective-c and made a little practice app to take input from an NSSlider and set the level to it. However, I would like to know if there is any way to make the level indicator update with the dragging of the slider. Currently, it only updates when I let go of the slider. I saw a couple of references to a setContinuous method, but it didn't seem to do anything. If that method is completely unrelated, please constrain your laughter. Also, it would be awesome if you could add code snippets to show me where to put the method.
-setContinuous: should do the trick, if you're sending it to the right object. Or, if you've set up your interface in a .xib file, check the 'continuous' box (I don't remember the actual label) for the slider.
UPDATE: With the blush of shame I discovered that the order had nothing to do with the speed of tapping. I was calling the visual code before the super touchesEnded:withEvent call, which was why if you tapped really fast, the display never got a chance to draw the highlighted state before being dismissed again. Because the code that was actually causing the main thread to block just a few milliseconds, the highlighted state would stay visible until the main thread unblocked again, where as if you tapped really fast, it looked like nothing happened at all. Moving the super call up to the top of the overridden method fixed it all. Sorry, if any moderator sees this post it can be deleted. shame
This problem must have been asked a 1000 times at SO, yet I can't find the explanation to match my specific issue.
I have a UIButton subclass with a custom design. Of course the design is custom enough that I can't just use the regular setSomething:forControlState: methods. I need a different backgroundcolor on touch, for one, and some icons that need to flash.
To implement these view changes, I (counter-intuitively) put the display code in (A) touchesBegan:withEvent and (Z) touchesEnded:withEvent:, before calling their respective super methods. Feels weird, but it works as intended, or so it seemed at first.
After implementing addTarget:action:forControlEvents was used to bind the UIControlEventTouchUpInside to the method (X) itemTapped:, I would expect these methods to always fire in the order (A)(X)(Z). However, if you tap the screen real fast (or the mouse in simulator), they fire in the order (A)(Z)(X). Where (A) and (Z) follow each other in such rapid succession, that the whole visual feedback for tapping is invisible. This is unwanted behavior. This also can't be the way to go, for so many apps need similar behavior, right?
So my question to you is: What am I doing wrong? One thing I'm guessing is that the visual appearance of the buttons shouldn't be manipulated in the touchesBegan:withEvent and touchesEnded:withEvent, but then where? Or am I missing some other well known fact?
Thanks for the nudge,
Eric-Paul.
I don't know why the order is different, but here's 2 suggestions to help deal with it.
What visual changes are you making to the button? If it's things like changing title/image/background image, you can do all this by modifying the highlighted state of the button. You can set a few properties like title and background image per-state. When the user's finger is down on the button, the highlighted state is turned on, so any changes you make to this state will be visible at this time. Do note that if you're making use of the selected state on the button, then you'll need to also set up the visual appearance for UIControlStateHighlighted|UIControlStateSelected, otherwise it will default back to inheriting from Normal when both highlighted & selected are on.
The other suggestion is to ditch touchesBegan:withEvent: and touchesEnded:withEvent: and switch over to using the methods inherited from UIControl, namely beginTrackingWithTouch:withEvent: and endTrackingWithTouch:withEvent:. You may also want to implement continueTrackingWithTouch:withEvent: and use the touchInside property to turn off your visual tweaks if the touch leaves the control.
I'm slowly learning Objective-C and Cocoa, and the only way I see so far to capture key events in Text Views is to use delegation, but I'm having trouble finding useful documentation and examples on how to implement such a solution. Can anyone point me in the right direction or supply some first-hand help?
Generally, the way you implement it is simply to add the required function to your view's controller, and set its delegate. For example, if you want code to run when the view loads, you just delegate your view to the controller, and implement the awakeFromNib function.
So, to detect a key press in a text view, make sure your controller is the text view's delegate, and then implement this:
- (void)keyUp:(NSEvent *)theEvent
Note that this is an inherited NSResponder method, not a NSTextView method.
Just a tip for syntax highlighting:
Don't highlight the whole text view at once - it's very slow. Also don't highlight the last edited text using -editedRange - it's very slow too if the user pastes a large body of text into the text view.
Instead you need to highlight the visible text which is done like this:
NSRect visibleRect = [[[textView enclosingScrollView] contentView] documentVisibleRect];
NSRange visibleRange = [[textView layoutManager] glyphRangeForBoundingRect:visibleRect inTextContainer:[textView textContainer]];
Then you feed visibleRange to your highlighting code.
It's important to tell us what you're really trying to accomplish — the higher-level goal that you think capturing key events in an NSTextView will address.
For example, when someone asks me how to capture key events in an NSTextField what they really want to know is how to validate input in the field. That's done by setting the field's formatter to an instance of NSFormatter (whether one of the formatters included in Cocoa or a custom one), not by processing keystrokes directly.
So given that example, what are you really trying to accomplish?
I've done some hard digging, and I did find an answer to my own question. I'll get at it below, but thanks to the two fellas who replied. I think that Stack Overflow is a fantastic site already--I hope more Mac developers find their way in once the beta is over--this could be a great resource for other developers looking to transition to the platform.
So, I did, as suggested by Danny, find my answer in delegation. What I didn't understand from Danny's post was that there are a set of delegate-enabled methods in the delegating object, and that the delegate must implement said events. And so for a TextView, I was able to find the method textDidChange, which accomplished what I wanted in an even better way than simply capturing key presses would have done. So if I implement this in my controller:
- (void)textDidChange:(NSNotification *)aNotification;
I can respond to the text being edited. There are, of course, other methods available, and I'm excited to play with them, because I know I'll learn a whole lot as I do. Thanks again, guys.