module Motion ; module Xray class XrayColorSwatch < UIControl def initWithFrame(frame) super.tap do gradient_view = XrayGradientView.alloc.initWithFrame(self.bounds).tap do |gradient_view| gradient_view.layer.borderWidth = 1 gradient_view.layer.borderColor = :gray.uicolor.CGColor gradient_view.userInteractionEnabled = false end self << gradient_view swatch_rect = gradient_view.bounds.shrink(3) gradient_view << XrayTriangleSwatch.alloc.initWithFrame(swatch_rect) @color_swatch = UIView.alloc.initWithFrame(swatch_rect).tap do |color_swatch| color_swatch.layer.borderWidth = 1 color_swatch.layer.borderColor = :dimgray.uicolor.CGColor end gradient_view << @color_swatch @pressed_shader = UIView.alloc.initWithFrame(self.bounds).tap do |pressed_shader| pressed_shader.backgroundColor = :black.uicolor(0.5) pressed_shader.hide end self << @pressed_shader self.on :touch_start do @pressed_shader.show end self.on :touch_stop do @pressed_shader.hide end end end def color=(value) @color_swatch.backgroundColor = value && value.uicolor end def color @color_swatch.backgroundColor end end class XrayTriangleSwatch < UIView def drawRect(rect) context = UIGraphicsGetCurrentContext() path = UIBezierPath.bezierPath path.moveToPoint(bounds.top_right) path.addLineToPoint(bounds.bottom_right) path.addLineToPoint(bounds.bottom_left) CGContextAddPath(context, path.CGPath) :black.uicolor.setFill CGContextFillPath(context) path = UIBezierPath.bezierPath path.moveToPoint(bounds.bottom_left) path.addLineToPoint(bounds.top_left) path.addLineToPoint(bounds.top_right) CGContextAddPath(context, path.CGPath) :white.uicolor.setFill CGContextFillPath(context) end end class XrayColorSliders < UIControl attr_reader :color def initWithFrame(frame) super.tap do self.backgroundColor = :black.uicolor @color = :clear.uicolor @triangle = XrayTriangleSwatch.alloc.initWithFrame(CGRect.empty) end end def color=(value) @color = value.uicolor setNeedsDisplay end def drawRect(rect) super r = color.red g = color.green b = color.blue a = color.alpha return unless r && g && b && a context = UIGraphicsGetCurrentContext() color_space = CGColorSpaceCreateDeviceRGB() slider_height = bounds.height / 5 slider_size = bounds.size slider_size.height = slider_height path = UIBezierPath.bezierPathWithRect([[0, 0], slider_size]) @triangle.frame = [[0, 0], slider_size] @triangle.drawRect(rect) CGContextSaveGState(context) CGContextTranslateCTM(context, 0, 0) color.setFill path.fill CGContextRestoreGState(context) big_oval_width = slider_height - 4 big_oval = [[-big_oval_width / 2, (slider_height - big_oval_width) / 2], [big_oval_width, big_oval_width]] big_oval_path = UIBezierPath.bezierPathWithOvalInRect(big_oval) small_oval_width = 4 small_oval = [[-small_oval_width / 2, (slider_height - small_oval_width) / 2], [small_oval_width, small_oval_width]] small_oval_path = UIBezierPath.bezierPathWithOvalInRect(small_oval) points = [0, 1].to_pointer(:float) cgcolors_red = [ UIColor.colorWithRed(0, green:g, blue:b, alpha:1).CGColor, UIColor.colorWithRed(1, green:g, blue:b, alpha:1).CGColor, ] gradient_red = CGGradientCreateWithColors(color_space, cgcolors_red, points) CGContextSaveGState(context) CGContextTranslateCTM(context, 0, 1 * slider_height) path.addClip CGContextDrawLinearGradient(context, gradient_red, self.bounds.top_left, self.bounds.top_right, 0) CGContextTranslateCTM(context, r * bounds.width, 0) UIColor.colorWithRed(1, green:g, blue:b, alpha:1).invert.setStroke big_oval_path.stroke small_oval_path.stroke CGContextRestoreGState(context) cgcolors_green = [ UIColor.colorWithRed(r, green:0, blue:b, alpha:1).CGColor, UIColor.colorWithRed(r, green:1, blue:b, alpha:1).CGColor, ] gradient_green = CGGradientCreateWithColors(color_space, cgcolors_green, points) CGContextSaveGState(context) CGContextTranslateCTM(context, 0, 2 * slider_height) path.addClip CGContextDrawLinearGradient(context, gradient_green, self.bounds.top_left, self.bounds.top_right, 0) CGContextTranslateCTM(context, g * bounds.width, 0) UIColor.colorWithRed(r, green:1, blue:b, alpha:1).invert.setStroke big_oval_path.stroke small_oval_path.stroke CGContextRestoreGState(context) cgcolors_blue = [ UIColor.colorWithRed(r, green:g, blue:0, alpha:1).CGColor, UIColor.colorWithRed(r, green:g, blue:1, alpha:1).CGColor, ] gradient_blue = CGGradientCreateWithColors(color_space, cgcolors_blue, points) CGContextSaveGState(context) CGContextTranslateCTM(context, 0, 3 * slider_height) path.addClip CGContextDrawLinearGradient(context, gradient_blue, self.bounds.top_left, self.bounds.top_right, 0) CGContextTranslateCTM(context, b * bounds.width, 0) UIColor.colorWithRed(r, green:g, blue:1, alpha:1).invert.setStroke big_oval_path.stroke small_oval_path.stroke CGContextRestoreGState(context) cgcolors_alpha = [ UIColor.colorWithRed(r, green:g, blue:b, alpha:0).CGColor, UIColor.colorWithRed(r, green:g, blue:b, alpha:1).CGColor, ] gradient_alpha = CGGradientCreateWithColors(color_space, cgcolors_alpha, points) CGContextSaveGState(context) CGContextTranslateCTM(context, 0, 4 * slider_height) path.addClip CGContextDrawLinearGradient(context, gradient_alpha, self.bounds.top_left, self.bounds.top_right, 0) CGContextTranslateCTM(context, a * bounds.width, 0) :white.uicolor.mix_with(color.uicolor(1).invert, a).setStroke big_oval_path.stroke small_oval_path.stroke CGContextRestoreGState(context) end def touchesBegan(touches, withEvent:event) point = touches.anyObject.locationInView(self) @touched_section = nil touched_color_at(point) end def touchesMoved(touches, withEvent:event) point = touches.anyObject.locationInView(self) touched_color_at(point) end ColorSection = 0 RedSection = 1 GreenSection = 2 BlueSection = 3 AlphaSection = 4 def touched_color_at(point) slider_height = bounds.height / 5 section = (point.y / slider_height).floor amount = [[point.x / bounds.width, 0].max, 1].min # assigns @touched_section only the first time (in `touchesBegan()`) @touched_section ||= section # makes sure we're still touching the same section section = @touched_section return if section == ColorSection r = color.red g = color.green b = color.blue a = color.alpha case section when RedSection r = amount when GreenSection g = amount when BlueSection b = amount when AlphaSection amount = (amount * 100).round / 100.0 a = amount end self.color = UIColor.colorWithRed(r, green:g, blue:b, alpha:a) self.sendActionsForControlEvents(:value_changed.uicontrolevent) end end end end