<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>mikeash.com pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html comments</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>mikeash.com Recent Comments</description><lastBuildDate>Sun, 10 May 2026 05:45:48 GMT</lastBuildDate><generator>PyRSS2Gen-1.0.0</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>mikeash - 2010-06-13 18:34:42</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>It's interesting that Apple recommends this approach, since virtually every Apple control takes the inner event loop approach. And since we already have to code around that when thinking about when our timers and such fire (by e.g. adding them to &lt;code&gt;NSEventTrackingRunLoopMode&lt;/code&gt; if we want them to fire during control tracking) then having third-party controls do the same thing really doesn't add any difficulty.</description><guid isPermaLink="true">3c9acbc0f99fad2ce7fa05c4a292631d</guid><pubDate>Sun, 13 Jun 2010 18:34:42 GMT</pubDate></item><item><title>DavidPhillipOster - 2010-06-12 19:42:19</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>pardon me, should be: mouseDragged: above, not mouseMoved:
&lt;br /&gt;Here's the working code:
&lt;br /&gt;&lt;code&gt;
&lt;br /&gt;@interface DiagonalTracker : NSObject {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;DiagonalSlider *owner_; // WEAK
&lt;br /&gt;&amp;nbsp;&amp;nbsp;double valueOffset_;
&lt;br /&gt;}
&lt;br /&gt;- (id)initWithOwner:(DiagonalSlider *)owner offset:(double)offset;
&lt;br /&gt;
&lt;br /&gt;- (void)mouseDragged:(NSEvent *)theEvent;
&lt;br /&gt;
&lt;br /&gt;@end
&lt;br /&gt;...
&lt;br /&gt;#pragma mark Event Tracking
&lt;br /&gt;
&lt;br /&gt;- (void)_trackMouseWithStartPoint: (NSPoint)p {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;// compute the value offset: this makes the pointer stay on the
&lt;br /&gt;&amp;nbsp;&amp;nbsp;// same piece of the knob when dragging
&lt;br /&gt;&amp;nbsp;&amp;nbsp;double offset = [self _valueForPoint: p] - value_;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;[self setTracker:[[[DiagonalTracker alloc] initWithOwner:self offset:offset] autorelease]];
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;- (void)mouseDown: (NSEvent *)event {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;NSPoint p = [self convertPoint: [event locationInWindow] fromView: nil];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;if([[self _knobPath] containsPoint: p]) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[self _trackMouseWithStartPoint: p];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;} else if([self _sliderContainsPoint: p]) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[self setDoubleValue: [self _valueForPoint: p]];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[self sendAction: [self action] to: [self target]];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[self _trackMouseWithStartPoint: p];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;- (void)mouseDragged:(NSEvent *)event {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;[tracker_ mouseDragged:event];
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;- (void)mouseUp:(NSEvent *)event {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;[self setTracker:nil];
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;...
&lt;br /&gt;@implementation DiagonalTracker
&lt;br /&gt;
&lt;br /&gt;- (id)initWithOwner:(DiagonalSlider *)owner offset:(double)offset {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;self = [super init];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (self) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;owner_ = owner;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;valueOffset_ = offset;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;&amp;nbsp;&amp;nbsp;return self;
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;- (void)mouseDragged:(NSEvent *)event {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;NSPoint p = [owner_ convertPoint: [event locationInWindow] fromView: nil];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;double value = [owner_ _valueForPoint: p];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;[owner_ setDoubleValue: value - valueOffset_];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;[owner_ sendAction: [owner_ action] to: [owner_ target]];
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;@end
&lt;br /&gt;
&lt;br /&gt;&lt;/code&gt;</description><guid isPermaLink="true">5f5d53bd6052143c146a516f5937b852</guid><pubDate>Sat, 12 Jun 2010 19:42:19 GMT</pubDate></item><item><title>DavidPhillipOster - 2010-06-12 19:00:23</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>RE: mouseDown:/mouseUp: methods
&lt;br /&gt;
&lt;br /&gt;Apple recommends you use separate mouseDown: mouseMoved: mouseUp: methods rather than a while loop, since the while loop technique can block other event processing that this part of the program may not know about (events from other sources, timers firing.)
&lt;br /&gt;
&lt;br /&gt;I like to wrap my mouse handling up into a Gesture object, which becomes firstResponder, and is responsible for that gesture. Although for a simple slider, the extra complexity isn't worth it: just put the mouseDown: etc methods in the in the view.
&lt;br /&gt;
&lt;br /&gt;Now if it also had clickable named tick-marks, or had multiple thumbs for displaying an interval, or had a text readout that you could type at during the mouse track, then helper objects might make the code more comprehensible.
&lt;br /&gt;</description><guid isPermaLink="true">3b31a4e0f28701925fc6c94490f44b14</guid><pubDate>Sat, 12 Jun 2010 19:00:23 GMT</pubDate></item><item><title>Ben Cohen - 2010-05-20 10:48:30</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>Another great post, thanks!
&lt;br /&gt;
&lt;br /&gt;Could you give a bit more info regarding event handling? I've a always handled events with the mouseDown:/mouseUp: methods but the alternative approach you descibe seems much cleaner. This makes me think that there's more to event handling in general than I was aware of.</description><guid isPermaLink="true">228831c2ac6305f29e93ea96a15b43be</guid><pubDate>Thu, 20 May 2010 10:48:30 GMT</pubDate></item><item><title>steve - 2010-04-25 03:20:33</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>mikeash: I believe one of the most difficult skills to learn as a programmer is to be able to differentiate between the work the computer does and the work the programmer does.
&lt;br /&gt;
&lt;br /&gt;Damn, you nailed my problem right there!
&lt;br /&gt;</description><guid isPermaLink="true">4f63bf6165e390a3f0e42f8fc1938148</guid><pubDate>Sun, 25 Apr 2010 03:20:33 GMT</pubDate></item><item><title>mikeash - 2010-04-24 02:41:12</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>&lt;b&gt;nevyn:&lt;/b&gt; I believe one of the most difficult skills to learn as a programmer is to be able to differentiate between the work the computer does and the work the programmer does. It's so common to see someone reject an idea that makes the &lt;i&gt;computer&lt;/i&gt; work harder, as if they thought it would be more difficult for &lt;i&gt;them&lt;/i&gt; to write it.
&lt;br /&gt;
&lt;br /&gt;For something like a custom control, the easiest approach is to build a single drawing method which can draw the current state of the control, and when the state changes, redraw. This minimizes the amount of code you have to write, because all drawing is the same, and reflecting state changes in the GUI is a one-liner to invalidate the view. This approach is more work for the computer, but it rarely matters.
&lt;br /&gt;
&lt;br /&gt;Using CoreAnimation will be less work for the computer (no expensive redraws just for moving elements around) but more work for you, because you have to do a lot more setup, then go through a different code path to reflect state changes.
&lt;br /&gt;
&lt;br /&gt;(CoreAnimation could easily be a net win if you actually want, say, animations. But for a simple control like this, I can't see any reason to use it.)
&lt;br /&gt;
&lt;br /&gt;&lt;b&gt;Ben Mitchell:&lt;/b&gt; A great question! I'm not sure of the specifics, but you'd want to implement one or more of the methods in the NSAccessibility informal protocol. Probably &lt;code&gt;-accessibilityAttributeValue:&lt;/code&gt; to tell Accessibility what type of control it is and its current value, and then the corresponding setter to allow its value to be set.
&lt;br /&gt;
&lt;br /&gt;&lt;b&gt;foobaz:&lt;/b&gt; Yes, &lt;code&gt;hypot&lt;/code&gt; would be better here, I just forgot about it. The &lt;code&gt;math.h&lt;/code&gt; header is full of great little helpers like this. (For others reading this, helper functions like &lt;code&gt;hypot&lt;/code&gt; aren't just convenient, but they usually produce an answer with better precision than you get by writing the calculation out longhand.)</description><guid isPermaLink="true">a8bdaf7f02fdd60c9b82afac20a37548</guid><pubDate>Sat, 24 Apr 2010 02:41:12 GMT</pubDate></item><item><title>foobaz - 2010-04-23 19:09:05</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>In your len() function, you compute the magnitude of the vector with &lt;code&gt;sqrt(p.x * p.x + p.y * p.y);&lt;/code&gt;. I prefer to use the underappreciated hypot() function, like &lt;code&gt;hypot(p.x, p.y)&lt;/code&gt;.</description><guid isPermaLink="true">763e8c2699331e1c16e5ffc59ce4db4f</guid><pubDate>Fri, 23 Apr 2010 19:09:05 GMT</pubDate></item><item><title>Ben Mitchell - 2010-04-23 18:53:20</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>What additional work would be needed to make this control present itself as an AXSlider to Accessibility?</description><guid isPermaLink="true">8c742797ed5fb32a854a168f6c6b46c7</guid><pubDate>Fri, 23 Apr 2010 18:53:20 GMT</pubDate></item><item><title>nevyn - 2010-04-23 17:59:46</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2010-04-23-implementing-a-custom-slider.html#comments</link><description>Any opinion on using Core Animation instead of drawRect/CG? I usually do that when writing custom controls, but often find it tedious; feels like it's at least as much work adjusting existing state (layers) as just redrawing everything when there's a change.</description><guid isPermaLink="true">0f56025f38b6c6d7d529f15803f2d64e</guid><pubDate>Fri, 23 Apr 2010 17:59:46 GMT</pubDate></item></channel></rss>
