Custom NSSliderCell knob doesn't follow mouse when tracking
I've implemented a custom NSSliderCell that uses a very different knob in size than the default one (this is for an interactive exhibit - I cannot use any default Mac OS X controls).
While the slider appears and behaves correctly (the knob goes from end to end, etc), w开发者_如何学编程hen you look carefully you see a weird behaviour: Moving the mouse say, 20 pixels, will result in the knob to move by 30 pixels. This means that the knob might reach the end of the slider (and the slider will have the maximum value) before the mouse will reach the end.
This looks very weird and goes against all expectations. I wonder what do I have to change to ensure that the knob follows the mouse and doesn't move faster.
OK, as always, the solution was the simplest one.
Here is the simplest code you need to have a very custom slider:
#import "CSSSliderCell.h"
#define KNOB_WIDTH 20
#define KNOB_HEIGHT 126
#define SLIDER_WIDTH 13
@implementation CSSSliderCell
- (void)drawKnob:(NSRect)rect
{
// knobImage is an NSImage
[knobImage drawInRect:rect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
- (void)drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped
{
NSRect slideRect = cellFrame;
NSColor *backColor = [NSColor redColor];
if ([(NSSlider*) [self controlView] isVertical] == YES)
{
slideRect.size.width = SLIDER_WIDTH;
slideRect.origin.x += (cellFrame.size.width - SLIDER_WIDTH) * 0.5;
} else {
slideRect.size.height = SLIDER_WIDTH;
slideRect.origin.y += (cellFrame.size.height - SLIDER_WIDTH) * 0.5;
}
NSBezierPath *bezierPath = [NSBezierPath bezierPathWithRoundedRect:slideRect xRadius:SLIDER_WIDTH * 0.5 yRadius:SLIDER_WIDTH * 0.5];
[backColor setFill];
[bezierPath fill];
}
- (NSRect)knobRectFlipped:(BOOL)flipped{
CGFloat value = ([self doubleValue] - [self minValue])/ ([self maxValue] - [self minValue]);
NSRect defaultRect = [super knobRectFlipped:flipped];
NSRect myRect = NSMakeRect(0, 0, 0, 0);
if ([(NSSlider*) [self controlView] isVertical] == YES)
{
myRect.size.width = KNOB_WIDTH;
myRect.size.height = KNOB_HEIGHT;
if (!flipped) {
myRect.origin.y = value * ([[self controlView] frame].size.height - KNOB_HEIGHT);
} else {
myRect.origin.y = (1.0 - value) * ([[self controlView] frame].size.height - KNOB_HEIGHT);
}
myRect.origin.x = defaultRect.origin.x;
} else {
myRect.size.width = KNOB_HEIGHT;
myRect.size.height = KNOB_WIDTH;
myRect.origin.x = value * ([[self controlView] frame].size.width - KNOB_HEIGHT);
myRect.origin.y = defaultRect.origin.y;
}
return myRect;
}
- (BOOL)_usesCustomTrackImage
{
return YES;
}
@end
This might have problems, but so far both on horizontal and vertical orientations it works well.
orestis, your code helps me much. Thank you!
However, the knob's position (myRect.origin) is not correct. I modified the code (and also removed the hardcoded macros).
- (NSRect)knobRectFlipped:(BOOL)flipped {
float KNOB_WIDTH = [knobImage size].width;
float KNOB_HEIGHT = [knobImage size].height;
CGFloat value = ([self doubleValue] - [self minValue])/ ([self maxValue] - [self minValue]);
NSRect defaultRect = [super knobRectFlipped:flipped];
NSRect myRect = NSMakeRect(0, 0, 0, 0);
if ([(NSSlider*) [self controlView] isVertical] == YES)
{
//...
} else {
myRect.size.width = KNOB_WIDTH;
myRect.size.height = KNOB_HEIGHT;
myRect.origin.x = value * ([[self controlView] frame].size.width - KNOB_WIDTH);
myRect.origin.y = defaultRect.origin.y + defaultRect.size.height/2.0 - myRect.size.height/2.0; // Fixed the position
}
return myRect;
};
精彩评论