         ]
         
         // actual coordinates for each edge, including translation
-        self._segmentPoints = [] // TODO: is this declaration correct?
+        self.segmentPoints = [] // TODO: is this declaration correct?
         
         // actual coordinates for arc centers for each corner
-        self._arcCenters = []
-        
-        self._arcStartingAngles = []
-        
-        for i in 0 ..< self._startingPoints.count {
-            let currentPoint = self._startingPoints[i]
-            let nextPoint = self._startingPoints[(i + 1) % self._startingPoints.count]
+        self.arcCenters = []
+        
+        self.arcStartingAngles = []
+        
+        for i in 0 ..< self.startingPoints.count {
+            let currentPoint = self.startingPoints[i]
+            let nextPoint = self.startingPoints[(i + 1) % self.startingPoints.count]
             
             var xDir = 0.0
             var yDir = 0.0
             
             if (i == 1) {
                 xDir = 1.0
-                self._arcStartingAngles.append(CGFloat(M_PI))
+                self.arcStartingAngles.append(CGFloat(M_PI))
             }
             else if (i == 3) {
                 xDir = -1.0
-                self._arcStartingAngles.append(CGFloat(0))
+                self.arcStartingAngles.append(CGFloat(0))
             }
             
             if (i == 0) {
                 yDir = -1.0
-                self._arcStartingAngles.append(CGFloat(M_PI/2.0))
+                self.arcStartingAngles.append(CGFloat(M_PI/2.0))
             }
             else if (i == 2) {
                 yDir = 1.0
-                self._arcStartingAngles.append(CGFloat(-M_PI/2.0))
+                self.arcStartingAngles.append(CGFloat(-M_PI/2.0))
             }
             
             let p0 = CGPointMake(
                 currentPoint.x + CGFloat(xDir * cornerRadius),
-                currentPoint.y + CGFloat(shadowOffset) + CGFloat(yDir * cornerRadius))
+                currentPoint.y + CGFloat(underOffset) + CGFloat(yDir * cornerRadius))
             let p1 = CGPointMake(
                 nextPoint.x - CGFloat(xDir * cornerRadius),
-                nextPoint.y + CGFloat(shadowOffset) - CGFloat(yDir * cornerRadius))
-            
-            self._segmentPoints.append((p0, p1))
+                nextPoint.y + CGFloat(underOffset) - CGFloat(yDir * cornerRadius))
+            
+            self.segmentPoints.append((p0, p1))
             
             let c = CGPointMake(
                 p0.x - CGFloat(yDir * cornerRadius),
                 p0.y + CGFloat(xDir * cornerRadius))
             
-            self._arcCenters.append(c)
+            self.arcCenters.append(c)
         }
     }
     
     func attachmentPoints(direction: Direction) -> (CGPoint, CGPoint) {
         var returnValue = (
-            self._segmentPoints[direction.clockwise().toRaw()].0,
-            self._segmentPoints[direction.counterclockwise().toRaw()].1)
+            self.segmentPoints[direction.clockwise().toRaw()].0,
+            self.segmentPoints[direction.counterclockwise().toRaw()].1)
         
         // TODO: quick hack
-        returnValue.0.y -= CGFloat(self.shadowOffset)
-        returnValue.1.y -= CGFloat(self.shadowOffset)
+        returnValue.0.y -= CGFloat(self.underOffset)
+        returnValue.1.y -= CGFloat(self.underOffset)
         
         return returnValue
     }
     
     func attachmentDirection() -> Direction? {
-        return self._attached
+        return self.attached
     }
     
     func attach(direction: Direction?) {
-        self._attached = direction
+        self.attached = direction
     }
 }
-
-//class KeyboardKeyPopup: KeyboardKeyBackground {
-//    
-//    init(frame: CGRect, vertical: Bool) {
-//        super.init(frame: frame)
-//    }
-//    
-//    // if action is nil, the key is not selectable
-//    func addOption(option: String, action: String?) {
-//    }
-//}

        var firstEdge = false
        
        for i in 0..<4 {
            if self._attached != nil && self._attached!.toRaw() == i {
                continue
            }
            
            var edgePath = CGPathCreateMutable()
            
            CGPathMoveToPoint(edgePath, nil, self._segmentPoints[i].0.x, self._segmentPoints[i].0.y)
            CGPathAddLineToPoint(edgePath, nil, self._segmentPoints[i].1.x, self._segmentPoints[i].1.y)
            
            // TODO: figure out if this is ncessary
            if !firstEdge {
                CGPathMoveToPoint(fillPath, nil, self._segmentPoints[i].0.x, self._segmentPoints[i].0.y)
                firstEdge = true
            }
            else {
                CGPathAddLineToPoint(fillPath, nil, self._segmentPoints[i].0.x, self._segmentPoints[i].0.y)
            }
            CGPathAddLineToPoint(fillPath, nil, self._segmentPoints[i].1.x, self._segmentPoints[i].1.y)
            
            if (self._attached != nil && self._attached!.toRaw() == ((i + 1) % 4)) {
                // do nothing
            } else {
                CGPathAddRelativeArc(edgePath, nil, self._arcCenters[(i + 1) % 4].x, self._arcCenters[(i + 1) % 4].y, CGFloat(self.cornerRadius), self._arcStartingAngles[(i + 1) % 4], CGFloat(M_PI/2.0))
                CGPathAddRelativeArc(fillPath, nil, self._arcCenters[(i + 1) % 4].x, self._arcCenters[(i + 1) % 4].y, CGFloat(self.cornerRadius), self._arcStartingAngles[(i + 1) % 4], CGFloat(M_PI/2.0))
            }
            
            edgePaths.append(edgePath)
        }
        
        if self.drawUnder && self._attached != Direction.Down {
            // TODO: is this the right way to do this? either way, it works for now
            CGContextSaveGState(ctx)
                CGContextTranslateCTM(ctx, 0, -CGFloat(shadowOffset))
                CGContextAddPath(ctx, fillPath)
                CGContextTranslateCTM(ctx, 0, CGFloat(shadowOffset))
                CGContextAddPath(ctx, fillPath)
                CGContextEOClip(ctx)
            
                CGContextTranslateCTM(ctx, 0, CGFloat(shadowOffset))
                CGContextSetFillColorWithColor(ctx, self.underColor.CGColor)
                CGContextAddPath(ctx, fillPath)
                CGContextFillPath(ctx)
            CGContextRestoreGState(ctx)
        }
        
        if self.drawOver {
            CGContextSetFillColorWithColor(ctx, self.color.CGColor)
            CGContextSetStrokeColorWithColor(ctx, self.borderColor.CGColor)
            CGContextSetLineWidth(ctx, 1)
        }
        
        // TODO: border stroke outside, not inside
        CGContextTranslateCTM(ctx, 0, -CGFloat(shadowOffset))
        CGContextSaveGState(ctx)
        CGContextAddPath(ctx, fillPath)
        CGContextClip(ctx)
        CGContextAddPath(ctx, fillPath)
        CGContextFillPath(ctx)
        if self.drawBorder {
            for path in edgePaths {
                CGContextAddPath(ctx, path)
                CGContextStrokePath(ctx)
            }
        }
        CGContextRestoreGState(ctx)
        CGContextTranslateCTM(ctx, 0, CGFloat(shadowOffset))
        
        /////////////
        // cleanup //
        /////////////
        
        // TODO: apparently you don't need to call CFRelease for "annotated" CG APIs... does this apply to paths and color spaces?
    }
    
    func generatePointsForDrawing() {
        let segmentWidth = self.bounds.width
        let segmentHeight = self.bounds.height - CGFloat(shadowOffset)
        
        // base, untranslated corner points
        self._startingPoints = [
            CGPointMake(0, segmentHeight),
            CGPointMake(0, 0),
            CGPointMake(segmentWidth, 0),


import UIKit

class KeyboardKeyBackground: UIView, KeyboardView, Connectable {
    
    var underOffset: Double { didSet { self.setNeedsDisplay() }}
    var cornerRadius: Double {
        didSet {
            self.generatePointsForDrawing()
