1from Cocoa import *
2from Quartz import *
3import objc
4
5import math
6
7ShadowOffsetChanged = "ShadowOffsetChanged"
8
9class ShadowOffsetView (NSView):
10    _offset = objc.ivar(type=CGSize.__typestr__)
11    _scale = objc.ivar(type=objc._C_FLT)
12
13
14    def scale(self):
15        return self._scale
16
17    def setScale_(self, scale):
18        self._scale = scale
19
20    def offset(self):
21        return CGSizeMake(self._offset.width * self._scale, self._offset.height * self._scale)
22
23    def setOffset_(self, offset):
24        offset = CGSizeMake(offset.width / self._scale, offset.height / self._scale);
25        if self._offset != offset:
26            self._offset = offset;
27            self.setNeedsDisplay_(True)
28
29    def isOpaque(self):
30        return False
31
32    def setOffsetFromPoint_(self, point):
33        bounds = self.bounds()
34        offset = CGSize(
35            width = (point.x - NSMidX(bounds)) / (NSWidth(bounds) / 2),
36            height = (point.y - NSMidY(bounds)) / (NSHeight(bounds) / 2))
37        radius = math.sqrt(offset.width * offset.width + offset.height * offset.height)
38        if radius > 1:
39            offset.width /= radius;
40            offset.height /= radius;
41
42        if self._offset != offset:
43            self._offset = offset;
44            self.setNeedsDisplay_(True)
45            NSNotificationCenter.defaultCenter().postNotificationName_object_(ShadowOffsetChanged, self)
46
47    def mouseDown_(self, event):
48        point = self.convertPoint_fromView_(event.locationInWindow(), None)
49        self.setOffsetFromPoint_(point)
50
51    def mouseDragged_(self, event):
52        point = self.convertPoint_fromView_(event.locationInWindow(), None)
53        self.setOffsetFromPoint_(point)
54
55    def drawRect_(self, rect):
56        bounds = self.bounds()
57        x = NSMinX(bounds)
58        y = NSMinY(bounds)
59        w = NSWidth(bounds)
60        h = NSHeight(bounds)
61        r = min(w / 2, h / 2)
62
63        context = NSGraphicsContext.currentContext().graphicsPort()
64
65        CGContextTranslateCTM(context, x + w/2, y + h/2)
66
67        CGContextAddArc(context, 0, 0, r, 0, math.pi, True)
68        CGContextClip(context)
69
70        CGContextSetGrayFillColor(context, 0.910, 1)
71        CGContextFillRect(context, CGRectMake(-w/2, -h/2, w, h))
72
73        CGContextAddArc(context, 0, 0, r, 0, 2*math.pi, True)
74        CGContextSetGrayStrokeColor(context, 0.616, 1)
75        CGContextStrokePath(context)
76
77        CGContextAddArc(context, 0, -2, r, 0, 2*math.pi, True)
78        CGContextSetGrayStrokeColor(context, 0.784, 1)
79        CGContextStrokePath(context)
80
81        CGContextMoveToPoint(context, 0, 0)
82        CGContextAddLineToPoint(context, r * self._offset.width, r * self._offset.height)
83
84        CGContextSetLineWidth(context, 2)
85        CGContextSetGrayStrokeColor(context, 0.33, 1)
86        CGContextStrokePath(context)
87