mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-06 00:45:32 +00:00
272 lines
10 KiB
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()
|
|
}
|
|
}
|