1# This is an example of an combination object: model and view. A number of these are 2# created and added to the superview (see Controller). 3 4require 'osx/cocoa' 5 6include OSX 7 8SQUARE_SIZE = 50 9STROKE_WIDTH = 10 10SHADOW_OFFSET = 5 11SHADOW_BLUR = 3 12 13class ViewModel < NSView 14 15 attr_reader :speed 16 17 def initWithFrame(frame) 18 super_initWithFrame(frame) 19 20 @stroke = STROKE_WIDTH 21 @color = getRandomColor 22 @speed = 0 23 setupShadows 24 setupDrawRect 25 26 return self 27 end 28 29 def setNum(n) 30 @num = n 31 end 32 33 def setupShadows 34 # The shadow will automagically "mask" any white part in a drawn image. 35 # Shadows also work with drawn paths, but leave room for the shadow in the frame 36 # (if object drawing fills frame, then shadow won't be visible). 37 38 @onShadow = NSShadow.alloc.init 39 offset = NSSize.new(SHADOW_OFFSET, -SHADOW_OFFSET) 40 @onShadow.setShadowOffset(offset) # required to get a shadow. 41 @onShadow.setShadowBlurRadius(SHADOW_BLUR) # default is 0. 42 # The following is not needed since it matches the default. Change if desired. 43 @onShadow.setShadowColor(NSColor.blackColor.colorWithAlphaComponent(0.33)) 44 45 # For the off shadow, all that is needed is to create it. It defaults to "no shadow" settings. 46 @offShadow = NSShadow.alloc.init 47 end 48 49 def setupDrawRect 50 # The draw rect is smaller than the frame, to leave room for the shadow and the stroke width. 51 @drawRect = NSRect.new(@stroke/2, SHADOW_OFFSET + @stroke/2, bounds.width - SHADOW_OFFSET - @stroke, bounds.height - SHADOW_OFFSET - @stroke) 52 end 53 54 def getRandomColor 55 red = rand(256)/256.0 #forces float 56 green = rand(256)/256.0 57 blue = rand(256)/256.0 58 NSColor.colorWithCalibratedRed_green_blue_alpha(red, green, blue, 1.0) 59 end 60 61 def drawRect(rect) 62 if superview.shadowSwitch.state == 1 63 @onShadow.set 64 else 65 @offShadow.set 66 end 67 68 @color.set 69 NSBezierPath.setDefaultLineWidth(@stroke) 70 NSBezierPath.strokeRect(@drawRect) 71 end 72 73 def mouseDown(event) 74 puts "mouseDown for obj #{@num}" 75 76 # It can be useful to keep this point so that the relationship (distance) between 77 # the object and the pointer can be maintained during a mouseDragged event. 78 # Object-local coordinates. 79 @mouseDownPoint = convertPoint_fromView(event.locationInWindow, nil) 80 81 # Put this subview on top, if control is set. 82 if superview.moveToTopSwitch.state == 1 83 superview.moveSubviewToTop(self) 84 # or, to move it behind the others: 85 #superview.moveSubviewToIndex(self, 0) 86 end 87 end 88 89 def mouseDragged(event) 90 myPoint = NSPoint.new(event.locationInWindow.x - superview.frame.origin.x, 91 event.locationInWindow.y - superview.frame.origin.y) 92 myPoint.x -= @mouseDownPoint.x 93 myPoint.y -= @mouseDownPoint.y 94 95 if @speed > 0 # was in the process of animating to a new location 96 @speed = 0 # stop the movement 97 end 98 99 # There is no need to redraw all the objects during a drag. 100 # This method keeps dragging very smooth, even with lots of objects on the screen. 101 # Mark the old rect as needing an update... 102 superview.setNeedsDisplayInRect(frame) 103 # ...update the position... 104 setFrameOrigin(myPoint) 105 # ...and mark the new one too. 106 superview.setNeedsDisplayInRect(frame) 107 end 108 109 def setMoveDestination(destPt) 110 if destPt.x != frame.x or destPt.y != frame.y 111 @destPt = destPt 112 end 113 end 114 115 def setSpeed(speed) 116 @speed = speed 117 @angle = getAngleInRadians(frame.origin, @destPt) 118 @xDelta = Math.sin(@angle) * @speed 119 @yDelta = Math.cos(@angle) * @speed 120 end 121 122 def startMovement 123 # Set the global so the app knows that an object is moving 124 $objMoving = true 125 # start the timer (superview checks to see if it is already going) 126 superview.startTimer 127 end 128 129 def moveToDestination 130 return 0 if speed == 0 131 currPt = frame.origin 132 if currPt == @destPt # reached destination 133 @speed = 0 134 return 0 135 end 136 137 if (currPt.x - @destPt.x).abs < @speed 138 currPt.x = @destPt.x 139 else 140 currPt.x += @xDelta 141 end 142 143 if (currPt.y - @destPt.y).abs < @speed 144 currPt.y = @destPt.y 145 else 146 currPt.y += @yDelta 147 end 148 149 # Unlike with dragging, during animation it is inefficient for each object 150 # to call "setNeedsDisplay" as it moves. It is much better to just update the 151 # object's position and let the superview call setNeedsDisplay once. 152 setFrameOrigin(currPt) 153 154 return 1 # yes, this object is still moving 155 end 156 157 def getAngleInRadians(p1, p2) # points 158 x = p2.x - p1.x 159 y = p2.y - p1.y 160 Math.atan2(x,y) 161 end 162end 163 164