cog/Window/VolumeSlider.m

176 lines
5.3 KiB
Objective-C

//
// VolumeSlider.m
// Cog
//
// Created by Vincent Spader on 2/8/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
//
#import "VolumeSlider.h"
#import "CogAudio/Helper.h"
#import "PlaybackController.h"
static void *kVolumeSliderContext = &kVolumeSliderContext;
@implementation VolumeSlider {
NSTimer *currentTimer;
BOOL wasInsideSnapRange;
BOOL observersadded;
}
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
return self;
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
return self;
}
- (void)awakeFromNib {
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
wasInsideSnapRange = NO;
textView = [[NSText alloc] init];
[textView setFrame:NSMakeRect(0, 0, 50, 20)];
textView.drawsBackground = NO;
textView.editable = NO;
textView.alignment = NSTextAlignmentCenter;
NSViewController *viewController = [[NSViewController alloc] init];
viewController.view = textView;
popover = [[NSPopover alloc] init];
popover.contentViewController = viewController;
// Don't hide the popover automatically.
popover.behavior = NSPopoverBehaviorTransient;
popover.animates = NO;
[popover setContentSize:textView.bounds.size];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeLimit" options:0 context:kVolumeSliderContext];
observersadded = YES;
}
- (void)dealloc {
if(observersadded) {
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeLimit" context:kVolumeSliderContext];
}
}
- (void)updateToolTip {
double value = [self doubleValue];
double volume;
volume = linearToLogarithmic(value, MAX_VOLUME);
NSString *text = [NSString stringWithFormat:@"%0.lf%%", volume];
[textView setString:text];
}
- (void)showToolTip {
[self updateToolTip];
double progress = (self.maxValue - [self doubleValue]) / (self.maxValue - self.minValue);
CGFloat width = self.knobThickness - 1;
// Show tooltip to the left of the Slider Knob
CGFloat height = self.knobThickness / 2.f + (self.bounds.size.height - self.knobThickness) * progress - 1;
NSWindow *window = self.window;
NSPoint screenPoint = [window convertPointToScreen:NSMakePoint(width + 1, height + 1)];
if(window.screen.frame.size.width < screenPoint.x + textView.bounds.size.width + 64) // wing it
[popover showRelativeToRect:NSMakeRect(1, height, 2, 2) ofView:self preferredEdge:NSRectEdgeMinX];
else
[popover showRelativeToRect:NSMakeRect(width, height, 2, 2) ofView:self preferredEdge:NSRectEdgeMaxX];
[self.window.parentWindow makeKeyWindow];
}
- (void)showToolTipForDuration:(NSTimeInterval)duration {
[self showToolTip];
[self hideToolTipAfterDelay:duration];
}
- (void)showToolTipForView:(NSView *)view closeAfter:(NSTimeInterval)duration {
[self updateToolTip];
[popover showRelativeToRect:view.bounds ofView:view preferredEdge:NSRectEdgeMaxY];
[self hideToolTipAfterDelay:duration];
}
- (void)hideToolTip {
[popover close];
}
- (void)hideToolTipAfterDelay:(NSTimeInterval)duration {
if(currentTimer) {
[currentTimer invalidate];
currentTimer = nil;
}
if(duration > 0.0) {
currentTimer = [NSTimer scheduledTimerWithTimeInterval:duration
target:self
selector:@selector(hideToolTip)
userInfo:nil
repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:currentTimer forMode:NSRunLoopCommonModes];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(context != kVolumeSliderContext) {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
if([keyPath isEqualToString:@"values.volumeLimit"]) {
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double new_MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
if(MAX_VOLUME != new_MAX_VOLUME) {
double currentLevel = linearToLogarithmic([self doubleValue], MAX_VOLUME);
[self setDoubleValue:logarithmicToLinear(currentLevel, new_MAX_VOLUME)];
}
MAX_VOLUME = new_MAX_VOLUME;
}
}
- (BOOL)sendAction:(SEL)theAction to:(id)theTarget {
// Snap to 100% if value is close
double snapTarget = logarithmicToLinear(100.0, MAX_VOLUME);
double snapProgress = ([self doubleValue] - snapTarget) / (self.maxValue - self.minValue);
if(fabs(snapProgress) < 0.005) {
[self setDoubleValue:snapTarget];
if(!wasInsideSnapRange) {
if(@available(macOS 10.11, *)) {
[[NSHapticFeedbackManager defaultPerformer] performFeedbackPattern:NSHapticFeedbackPatternGeneric
performanceTime:NSHapticFeedbackPerformanceTimeDefault];
}
}
wasInsideSnapRange = YES;
} else {
wasInsideSnapRange = NO;
}
[self showToolTip];
return [super sendAction:theAction to:theTarget];
}
- (void)scrollWheel:(NSEvent *)theEvent {
double change = [theEvent deltaY];
[self setDoubleValue:[self doubleValue] + change];
[[self target] changeVolume:self];
[self showToolTipForDuration:1.0];
}
@end