I created a custom view to a button, as I need to implement some highlighting when the mouse is over. The class is very simple, and I already implemented mouseEntered: as well as mouseExited:. The view was registered for tracking in the init method (not sure if it's the best place).
The problem is drawing. I keep an ivar mouseOver, set to YES on mouse enter and NO on mouse exited. The other ivar is for the image, called image. The difference between mouse over or not when it comes to drawing, is the transparency. Here is my drawRect::
- (void)drawRect:(NSRect)dirtyRect
{
[image drawAtPoint:NSMakePoint(0.0,0.0)
fromRect:dirtyRect
operation:NSCompositeCopy
fraction:((mouseOver) ? 1.0 : 0.0)];
}
It works nicely, but only when the mouse first entered, apparently. I guess the problem is that the view is not cleared before drawing the other image. I tried adding:
[[NSColor clearColor] set];
NSRectFillUsingOperation(dirtyRect, NSCompositeClear);
But without success. How can I fix this?
[NSColor clearColor] is a purely transparent color. You probably want to fill using a color with some opacity, like, say, [NSColor whiteColor].
Related
I have subclassed an NSImageView which carries an image set to NSImageScaleProportionallyDown so it will not always occupy the whole space of the view - this is working as it should. Now I don't know how to change the background color respectively the color of tha space that remains unused besides the image... it is by default gray.
I've tried overriding drawRect:
- (void)drawRect:(NSRect)dirtyRect
{
[[NSColor blackColor] setFill];
NSRectFill(dirtyRect);
[super drawRect:dirtyRect];
}
If I leave away the call to super it makes the whole ImageView go black, instead of only the background. If it stays there, there is no change at all - background stays gray...
Anyone has a hint on this, it'd be greatly appreciated.
EDIT:
I have the ImageView's styleMask set to NSImageFramePhoto - I need that, because it lets the image be resized the way it needs to be. If I leave it away, the background color works, but with it, it doesn't.
As of OSX 10.7, Apple suggests using drawFocusRingMask and focusRingMaskBounds methods as the way to draw a focus ring around NSView based objects instead of how it was done previously in 10.6 (using NSSetFocusRingStyle() in drawRect:). (see release notes).
Tested on OSX 10.10 :
I have an NSTextView descendent which implements basic focus ring drawing as suggested:
- (void)drawFocusRingMask {
NSRectFill([self bounds]);
}
- (NSRect)focusRingMaskBounds {
return [self bounds];
}
This NSTextView is programmatically added directly to the content view (without any scroll views).
This works well for the most part, however, when using scaleUnitSquareToSize: to scale the NSTextView, the focus ring drawn is completely incorrect.
So calling:
[textView scaleUnitSquareToSize:NSMakeSize(1.5, 1.5)];
Looks like this:
Scaling it further up or down results in even more skew of the focus ring rect. Reseting the scale of the NSTextView back to {1.0, 1.0} causes the focus ring to draw correctly again.
I assume this is some sort of a bug in the focus ring code. It seems to preform transformations on the graphics context used for drawing the focus ring before calling drawFocusRingMask / focusRingMaskBounds (internal class _NSAutomaticFocusRing is responsible for this).
Any idea how to fix this? Should I find a way to transform the context to the proper coordinates (so far without success)? Or is there any other way to force the focus ring to behave properly?
Overriding the drawFocusRingMask method (via a sub-class of NSTextField) allows the width of the focus ring to be corrected by "manually" applying the super-view's scale factor, but changing the height doesn't work. My guess is that the height of the focus ring is being pinned to the text size or original text field bounds before scaling.
This all on system 10.7.5. So I'm simply turning the focus ring off when the text field's super-view is in a zoomed state, until I can figure out a workaround or whatever the right thing to do is.
I have struggled with this bug for a long time. Here's a solution that seems to work:
Sub-class the NSTextField or NSTextView so that you are instantiating, e.g., a ZoomableTextField. Override its drawFocusRingMask: method, so that the override does nothing:
- (void)drawFocusRingMask
{
}
Then in the zoomable superview in which a ZoomableTextField is embedded, inside the superview's drawRect: method, call a new method of the ZoomableTextField, drawPossibleFocus, which looks like this:
- (void)drawPossibleFocus
{
NSWindow *win = [self window];
if ([self currentEditor]==[win firstResponder] && [win isKeyWindow]) {
[NSGraphicsContext saveGraphicsState];
NSSetFocusRingStyle(NSFocusRingOnly);
NSRectFill([self frame]);
[NSGraphicsContext restoreGraphicsState];
}
}
I'm trying to implement a custom slider in Cocoa with 5 values. See my demo project, which can be downloaded here: http://s000.tinyupload.com/index.php?file_id=07311576247413689572.
I've subclassed the NSSliderCell and implemented methods like drawKnob:(NSRect)knobRect and drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped etc.
I'm facing some issues:
I'm not able to position the knob correctly regarding to the background image. I know that I'm able to change the knob's frame, and I've tried doing some calculation to position the knob correctly, but I'm not able to make it work for my custom slider. Could someone please help me with this?
The height of my custom slider background is 41px. In the drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped I change the height of the frame to 41px as well, but the entire background is not visible. Why?
I've noticed that the included images (the background and knob) are flipped vertically. Why? Note that the border top is darker in the background compared to the bottom, but this is reversed when I draw the background.
I found a mistake in your calculation of the x position of the knob rectangle: You used the height of the image where you should have used the width.
The cell drawing is being clipped to the frame of the control. Maybe you could expand the control frame when your cell awakes.
You need to use the NSImage method drawInRect:fromRect:operation:fraction:respectFlipped:hints:, and pass YES for the respectFlipped: parameter. Apple's controls generally do use flipped coordinates.
Added: Expanding the frame in awakeFromNib doesn't seem to work, the frame gets set back. Here's something that does work. Instead of overriding drawBarInside:flipped:, add this override:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
NSRect controlFrame = [controlView frame];
float bgHeight = self.backgroundImage.size.height;
if (controlFrame.size.height < bgHeight)
{
controlFrame.size.height = bgHeight;
[controlView setFrame: controlFrame];
}
[self.backgroundImage
drawInRect: [controlView bounds]
fromRect: NSZeroRect
operation: NSCompositeSourceOver
fraction: 1.0
respectFlipped: YES
hints: NULL];
[self drawKnob];
}
Everywhere on the internet I can read that to change the background color of a NSView you can just override its method drawRect like this:
- (void)drawRect:(NSRect)rect
{
[[NSColor yellowColor] set];
NSRectFill(rect);
}
For example here.
However, in my case, the color is drawn on top of the view (I can't see anymore the content), which is quite logical to me. DrawRect is supposed to draw the view, not just its background.
what am I missing?
You should call [super drawRect:rect] after filling the background. Otherwise, you're simply replacing everything that would be drawn by the superclass's implementation.
The examples you refer to are displaying the subviews of the view above the background.
I'm doing some simple drawing in my NSView's drawRect with code like this
[[NSColor colorWithCalibratedWhite:0.250 alpha:0.700] set];
NSRectFill(dRect);
This drawing is being done in a nested view and I'm finding that the transparency goes all the way through the parent view and window showing some of the screen beneath it.
How do I get it so the transparency only goes through to the parent view (or any drawing that was done in that frame before it?
Thanks!
You really shouldn't be using NSRectFill to draw a transparent color. A call to
NSRectFill(rect);
is really just a shortcut to
NSRectFillUsingOperation(rect, NSCompositeCopy);
NSCompositeCopy doesn't perform compositing, so you can get unexpected results when a transparent NSColor is set.
Try using this instead:
[[NSColor colorWithCalibratedWhite:0.250 alpha:0.700] set];
[NSBezierPath fillRect:rect];
That should draw the transparency properly, with the underlying drawing visible.