Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
267 lines (210 sloc) 8.5 KB
Created on 28/02/2009
Copyright 2009 Max Howell <>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <>.
#define KNOB_WIDTH 38
#define HEIGHT 26
#define WIDTH 89
#define KNOB_MIN_X 0
@interface MBKnobAnimation : NSAnimation
int start, range;
id delegate;
@implementation MBKnobAnimation
-(id)initWithStart:(int)begin end:(int)end
[super init];
start = begin;
range = end - begin;
return self;
int x = start+progress*range;
[super setCurrentProgress:progress];
[delegate performSelector:@selector(setPosition:) withObject:[NSNumber numberWithInteger:x]];
delegate = d;
#import "MBSliderButton.h"
@implementation MBSliderButton
surround = [[NSImage alloc] initByReferencingFile:[[prefpane bundle] pathForResource:@"button_surround" ofType:@"png"]];
knob = [[NSImage alloc] initByReferencingFile:[[prefpane bundle] pathForResource:@"button_knob" ofType:@"png"]];
state = false;
NSColor* darkGreen = [NSColor colorWithDeviceRed:0.431 green:0.639 blue:0.118 alpha:1.0];
NSColor* lightGreen = [NSColor colorWithDeviceRed:0.647 green:0.835 blue:0.247 alpha:1.0];
NSColor* darkGray = [NSColor colorWithDeviceRed:0.5 green:0.5 blue:0.5 alpha:1.0];
NSColor* lightGray = [NSColor colorWithDeviceRed:0.7 green:0.7 blue:0.7 alpha:1.0];
NSGradient* green_gradient = [[NSGradient alloc] initWithStartingColor:darkGreen endingColor:lightGreen];
NSGradient* gray_gradient = [[NSGradient alloc] initWithStartingColor:darkGray endingColor:lightGray];
[green_gradient drawInRect:NSMakeRect(0, location.y, location.x+10, HEIGHT) angle:270];
NSString* s = @"ON";
NSMutableDictionary* attr = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSFont boldSystemFontOfSize:15.0], NSFontAttributeName,
[NSColor colorWithDeviceRed:0.2 green:0.333 blue:0.027 alpha:1.0], NSForegroundColorAttributeName,
NSSize sz = [s sizeWithAttributes:attr];
NSPoint pt;
pt.x = (KNOB_MAX_X-sz.width)/2 - (KNOB_MAX_X-location.x);
pt.y = HEIGHT/2 - sz.height/2;
[s drawAtPoint:pt withAttributes:attr];
int x = location.x+KNOB_WIDTH-2;
[gray_gradient drawInRect:NSMakeRect(x, location.y, WIDTH-x, HEIGHT) angle:270];
s = @"OFF";
[attr setObject:[NSColor colorWithDeviceWhite:0.2 alpha:0.66] forKey:NSForegroundColorAttributeName];
sz = [s sizeWithAttributes:attr];
pt.x = location.x+KNOB_WIDTH+(KNOB_MAX_X-sz.width)/2;
[s drawAtPoint:pt withAttributes:attr];
[surround drawAtPoint:NSMakePoint(0,0) fromRect:NSZeroRect
pt = location;
pt.x -= 2;
[knob drawAtPoint:pt fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
return YES;
return state ? NSOnState : NSOffState;
MBKnobAnimation* a = [[MBKnobAnimation alloc] initWithStart:location.x end:x];
[a setDelegate:self];
if (location.x == 0 || location.x == KNOB_MAX_X){
[a setDuration:0.20];
[a setAnimationCurve:NSAnimationEaseInOut];
[a setDuration:0.35 * ((fabs(location.x-x))/KNOB_MAX_X)];
[a setAnimationCurve:NSAnimationLinear];
[a setAnimationBlockingMode:NSAnimationBlocking];
[a startAnimation];
[a release];
location.x = [x intValue];
[self display];
[self setState:newstate animate:true];
-(void)setState:(NSInteger)newstate animate:(bool)animate
if(newstate == [self state])
int x = newstate == NSOnState ? KNOB_MAX_X : 0;
//TODO animate if we are visible and otherwise don't
[self animateTo:x];
[self setNeedsDisplay:YES];
state = newstate == NSOnState ? true : false;
location.x = x;
location.x = location.x + x;
if (location.x < KNOB_MIN_X) location.x = KNOB_MIN_X;
if (location.x > KNOB_MAX_X) location.x = KNOB_MAX_X;
[self setNeedsDisplay:YES];
-(void)mouseDown:(NSEvent *)event
BOOL loop = YES;
// convert the initial click location into the view coords
NSPoint clickLocation = [self convertPoint:[event locationInWindow] fromView:nil];
// did the click occur in the draggable item?
if (NSPointInRect(clickLocation, [self bounds])) {
NSPoint newDragLocation;
// the tight event loop pattern doesn't require the use
// of any instance variables, so we'll use a local
// variable localLastDragLocation instead.
NSPoint localLastDragLocation;
// save the starting location as the first relative point
while (loop) {
// get the next event that is a mouse-up or mouse-dragged event
NSEvent *localEvent;
localEvent= [[self window] nextEventMatchingMask:NSLeftMouseUpMask | NSLeftMouseDraggedMask];
switch ([localEvent type]) {
case NSLeftMouseDragged:
// convert the new drag location into the view coords
newDragLocation = [self convertPoint:[localEvent locationInWindow]
// offset the item and update the display
[self offsetLocationByX:(newDragLocation.x-localLastDragLocation.x)];
// update the relative drag location;
localLastDragLocation = newDragLocation;
// support automatic scrolling during a drag
// by calling NSView's autoscroll: method
[self autoscroll:localEvent];
case NSLeftMouseUp:
// mouse up has been detected,
// we can exit the loop
loop = NO;
if (memcmp(&clickLocation, &localLastDragLocation, sizeof(NSPoint)) == 0)
[self animateTo:state ? 0 : KNOB_MAX_X];
else if (location.x > 0 && location.x < KNOB_MAX_X)
[self animateTo:state ? KNOB_MAX_X : 0];
//TODO if let go of it halfway then slide to non destructive side
if(location.x == 0 && state || location.x == KNOB_MAX_X && !state){
state = !state;
// wanted to use self.action and but both are null
// even though I set them up in IB! :(
[prefpane performSelector:@selector(onEnable:) withObject:self];
// the rectangle has moved, we need to reset our cursor
// rectangle
[[self window] invalidateCursorRectsForView:self];
// Ignore any other kind of event.
return YES;
[self offsetLocationByX:-10.0];
[[self window] invalidateCursorRectsForView:self];
[self offsetLocationByX:10.0];
[[self window] invalidateCursorRectsForView:self];