spiegel-keyman/ios/engine/KMEI/KeymanEngine/Classes/Keyboard/SubKeysView.swift

272 lines
10 KiB
Swift

//
// SubKeysView.swift
// KMEI
//
// Created by Gabriel Wong on 2017-09-28.
// Copyright © 2017 SIL International. All rights reserved.
//
import UIKit
class SubKeysView: UIView {
private let borderRadius: CGFloat = 5.0
private let strokeWidth: CGFloat = 1.0
private var baseKeyYDelta: CGFloat = 0.0
private var keyFrame = CGRect.zero
private var adjX: CGFloat = 0.0
private var adjY: CGFloat = 0.0
private var rows: Int = 0
private let scale = UIScreen.main.scale
private let bgColor = Colors.keyboardBackground
private let bgColor2 = Colors.keyboardBackground
private let borderColor = Colors.popupBorder
let containerView: UIView
init(keyFrame frame: CGRect, subKeys: [UIButton]) {
let isSystemKeyboard = Manager.shared.isSystemKeyboard
var isPortrait = true
if isSystemKeyboard {
isPortrait = InputViewController.isPortrait
} else {
isPortrait = UIDevice.current.orientation.isPortrait
}
let screenRect = UIScreen.main.bounds
let screenWidth = screenRect.width
let screenHeight = screenRect.height
keyFrame = frame.insetBy(dx: 0, dy: 0)
var buttonWidth: CGFloat
var buttonHeight: CGFloat
let marginX: CGFloat = 4.0
let marginY: CGFloat = 3.0
let maxButtonSize = CGSize(width: keyFrame.width * 0.9, height: keyFrame.height * 0.9)
let minButtonSize: CGSize
if UIDevice.current.userInterfaceIdiom == .pad {
if isPortrait {
minButtonSize = CGSize(width: keyFrame.width * 0.65, height: keyFrame.height * 0.65)
} else {
minButtonSize = CGSize(width: keyFrame.width * 0.55, height: keyFrame.height * 0.55)
}
} else {
if isPortrait {
minButtonSize = CGSize(width: keyFrame.width * 0.75, height: keyFrame.height * 0.75)
} else {
minButtonSize = CGSize(width: keyFrame.width * 0.675, height: keyFrame.height * 0.675)
}
}
let kbHeight = Manager.shared.inputViewController.kmwHeight
let maxContainerHeight = (screenHeight - kbHeight) + keyFrame.origin.y - strokeWidth
var columns = Int((screenWidth - marginX) / (maxButtonSize.width + marginX))
rows = subKeys.count / columns
if subKeys.count % columns != 0 {
rows += 1
}
if subKeys.count % rows == 0 {
columns = subKeys.count / rows
} else {
let s = (columns * rows - subKeys.count) / 2
columns -= s / (rows - 1)
}
buttonWidth = maxButtonSize.width
buttonHeight = (maxContainerHeight - CGFloat(rows + 1) * marginY) / CGFloat(rows)
buttonHeight = CGFloat.minimum(buttonHeight, maxButtonSize.height)
buttonHeight = CGFloat.maximum(buttonHeight, minButtonSize.height)
let cx: CGFloat = 1.5
let containerWidth = (columns < 2 ? cx : CGFloat(columns)) * (buttonWidth + marginX) + marginX
let containerHeight = (CGFloat(rows) * (buttonHeight + marginY)) + marginY
let containerFrame = CGRect(x: strokeWidth, y: strokeWidth, width: containerWidth, height: containerHeight)
let viewWidth = containerWidth + 2 * strokeWidth
let baseHeight = keyFrame.size.height + marginY
let viewHeight = baseHeight + containerHeight - marginY + strokeWidth
var viewPosX = keyFrame.origin.x - (viewWidth - keyFrame.size.width) / 2.0
var viewPosY = keyFrame.origin.y - (viewHeight - keyFrame.size.height + strokeWidth)
// Ensure that the system keyboard doesn't try to display off the top of the keyboard's view area.
if viewPosY < 0 && isSystemKeyboard {
baseKeyYDelta = viewPosY // We'll need this to adjust the visualization.
viewPosY = 0
}
adjX = 0
adjY = 0
if viewPosX < 0 {
if (keyFrame.origin.x - borderRadius * 1.25) < 0 {
adjX = keyFrame.origin.x - viewPosX
viewPosX = keyFrame.origin.x
} else {
adjX = -viewPosX
viewPosX = 0
}
} else if (viewPosX + viewWidth) > screenWidth {
if ((keyFrame.origin.x + keyFrame.size.width) + borderRadius * 1.25) > screenWidth {
adjX = viewPosX - keyFrame.origin.x
viewPosX = keyFrame.origin.x + 2 * adjX
} else {
adjX = (screenWidth - viewWidth) - viewPosX
viewPosX = adjX + viewPosX
}
}
containerView = UIView(frame: containerFrame)
containerView.backgroundColor = UIColor.clear
super.init(frame: CGRect(x: viewPosX, y: viewPosY, width: viewWidth, height: viewHeight))
super.backgroundColor = UIColor.clear
//isUserInteractionEnabled = false
addSubview(containerView)
let fontSize = buttonHeight * (UIDevice.current.userInterfaceIdiom == .pad ? 0.4 : 0.5)
for button in subKeys {
let i = button.tag
let buttonRow = CGFloat(i / columns)
let buttonFrame = CGRect(x: marginX + CGFloat(i % columns) * (buttonWidth + marginX),
y: (buttonRow * buttonHeight) + (buttonRow + 1) * marginY,
width: buttonWidth, height: buttonHeight)
button.frame = buttonFrame
if subKeys.count == 1 {
button.center = containerView.center
}
button.titleLabel?.minimumScaleFactor = 0.25
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.font = button.titleLabel?.font.withSize(fontSize)
containerView.addSubview(button)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
drawKeyPreviewUp(rect)
}
private func drawKeyPreviewUp(_ rect: CGRect) {
let keyWidth = keyFrame.size.width
let keyHeight = keyFrame.size.height + adjY
let viewWidth = rect.size.width
let viewHeight = rect.size.height
guard let context = UIGraphicsGetCurrentContext() else {
return
}
UIGraphicsPushContext(context)
context.setLineJoin(.round)
context.setLineWidth(strokeWidth * 2)
context.setStrokeColor(borderColor.cgColor)
context.setFillColor(bgColor.cgColor)
let keyLeftUnrounded = (viewWidth - keyWidth) / 2.0 + strokeWidth - adjX
let keyRightUnrounded = (viewWidth - keyWidth) / 2.0 + keyWidth - strokeWidth - adjX
var keyLeft = (keyLeftUnrounded * 10).rounded(.up) / 10
var keyRight = (keyRightUnrounded * 10).rounded(.down) / 10
let midY = viewHeight - keyHeight
var viewLeft = strokeWidth
var viewRight = viewWidth - strokeWidth
let viewTop = strokeWidth
var viewBottom = viewHeight - strokeWidth * 0.75 - (CGFloat(rows - 1) * 0.25)
viewBottom += baseKeyYDelta
let showBaseKeyPopup = (viewBottom > midY)
if scale == 3.0 {
viewBottom -= CGFloat(3 - rows) * 0.4
} else if scale == 2.0 {
viewBottom -= CGFloat(3 - rows) * 0.1
}
let viewMid = viewWidth / 2.0
let r0 = borderRadius - strokeWidth
let r1 = borderRadius - strokeWidth
let r2 = borderRadius * 1.25 - strokeWidth
if viewLeft.rounded() == keyLeft.rounded() || viewLeft.rounded(.down) == keyLeft.rounded(.down) {
keyLeft = CGFloat.minimum(viewLeft, keyLeft)
viewLeft = keyLeft
}
if viewRight.rounded() == keyRight.rounded() || viewRight.rounded(.down) == keyRight.rounded(.down) {
keyRight = CGFloat.maximum(viewRight, keyRight)
viewRight = keyRight
}
context.beginPath()
context.move(to: CGPoint(x: viewMid, y: viewTop))
context.addArc(tangent1End: CGPoint(x: viewLeft, y: viewTop),
tangent2End: CGPoint(x: viewLeft, y: midY), radius: r1)
if showBaseKeyPopup {
context.addArc(tangent1End: CGPoint(x: viewLeft, y: midY),
tangent2End: CGPoint(x: keyLeft, y: midY), radius: r1)
context.addArc(tangent1End: CGPoint(x: keyLeft, y: midY),
tangent2End: CGPoint(x: keyLeft, y: viewBottom), radius: r2)
context.addArc(tangent1End: CGPoint(x: keyLeft, y: viewBottom),
tangent2End: CGPoint(x: keyRight, y: viewBottom), radius: r0)
context.addArc(tangent1End: CGPoint(x: keyRight, y: viewBottom),
tangent2End: CGPoint(x: keyRight, y: midY), radius: r0)
context.addArc(tangent1End: CGPoint(x: keyRight, y: midY),
tangent2End: CGPoint(x: viewRight, y: midY), radius: r2)
} else {
context.addArc(tangent1End: CGPoint(x: viewLeft, y: midY),
tangent2End: CGPoint(x: viewRight, y: midY), radius: r0)
}
context.addArc(tangent1End: CGPoint(x: viewRight, y: midY),
tangent2End: CGPoint(x: viewRight, y: viewTop), radius: r1)
context.addArc(tangent1End: CGPoint(x: viewRight, y: viewTop),
tangent2End: CGPoint(x: viewMid, y: viewTop), radius: r1)
context.closePath()
context.drawPath(using: .stroke)
context.beginPath()
context.move(to: CGPoint(x: viewMid, y: viewTop))
context.addArc(tangent1End: CGPoint(x: viewLeft, y: viewTop),
tangent2End: CGPoint(x: viewLeft, y: midY), radius: r1)
if showBaseKeyPopup {
context.addArc(tangent1End: CGPoint(x: viewLeft, y: midY),
tangent2End: CGPoint(x: keyLeft, y: midY), radius: r1)
context.addArc(tangent1End: CGPoint(x: keyLeft, y: midY),
tangent2End: CGPoint(x: keyLeft, y: viewBottom), radius: r2)
context.addArc(tangent1End: CGPoint(x: keyLeft, y: viewBottom),
tangent2End: CGPoint(x: keyRight, y: viewBottom), radius: r0)
context.addArc(tangent1End: CGPoint(x: keyRight, y: viewBottom),
tangent2End: CGPoint(x: keyRight, y: midY), radius: r0)
context.addArc(tangent1End: CGPoint(x: keyRight, y: midY),
tangent2End: CGPoint(x: viewRight, y: midY), radius: r2)
} else {
context.addArc(tangent1End: CGPoint(x: viewLeft, y: midY),
tangent2End: CGPoint(x: viewRight, y: midY), radius: r0)
}
context.addArc(tangent1End: CGPoint(x: viewRight, y: midY),
tangent2End: CGPoint(x: viewRight, y: viewTop), radius: r1)
context.addArc(tangent1End: CGPoint(x: viewRight, y: viewTop),
tangent2End: CGPoint(x: viewMid, y: viewTop), radius: r1)
context.closePath()
context.clip()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradientColors = [bgColor.cgColor, bgColor2.cgColor] as CFArray
let gradientLocations: [CGFloat] = [0, 1]
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: gradientLocations)!
let startPoint = CGPoint(x: rect.midX, y: rect.minY)
let endPoint = CGPoint(x: rect.midX, y: rect.maxY)
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: [])
UIGraphicsPopContext()
}
}