|
|
|
@@ -16,23 +16,39 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
|
|
|
|
|
public static let shared = LCManager()
|
|
|
|
|
|
|
|
|
|
/// Set the font size. The font can be set to a minimum value of 5.0 and a maximum value of 20.0. The default value is 7.5.
|
|
|
|
|
public var fontSize: CGFloat = 7.5 {
|
|
|
|
|
didSet {
|
|
|
|
|
guard fontSize >= 4 else { fontSize = 4; return }
|
|
|
|
|
guard fontSize <= 20 else { fontSize = 20; return }
|
|
|
|
|
|
|
|
|
|
setAttributedText(consoleTextView.text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The fixed size of the console view.
|
|
|
|
|
let consoleSize = CGSize(width: 212, height: 124)
|
|
|
|
|
|
|
|
|
|
// Strong reference needed to keep the window alive.
|
|
|
|
|
/// Strong reference keeps the window alive.
|
|
|
|
|
var consoleWindow: ConsoleWindow?
|
|
|
|
|
|
|
|
|
|
// The console needs a view controller to display context menus.
|
|
|
|
|
// The console needs a parent view controller in order to display context menus.
|
|
|
|
|
let viewController = UIViewController()
|
|
|
|
|
lazy var consoleView = viewController.view!
|
|
|
|
|
|
|
|
|
|
/// Text view that displays printed items.
|
|
|
|
|
let consoleTextView = UITextView()
|
|
|
|
|
|
|
|
|
|
/// Button that reveals menu.
|
|
|
|
|
var menuButton: UIButton!
|
|
|
|
|
|
|
|
|
|
/// Tracks whether the PiP console is in text view scroll mode or pan mode.
|
|
|
|
|
var scrollLocked = true
|
|
|
|
|
|
|
|
|
|
/// Feedback generator for the long press action.
|
|
|
|
|
let feedbackGenerator = UISelectionFeedbackGenerator()
|
|
|
|
|
|
|
|
|
|
/// Gesture endpoints. Each point represents a corner of the screen. TODO: Handle screen rotation.
|
|
|
|
|
lazy var possibleEndpoints = [CGPoint(x: consoleSize.width / 2 + 12,
|
|
|
|
|
y: UIApplication.shared.statusBarHeight + consoleSize.height / 2 + 5),
|
|
|
|
|
CGPoint(x: UIScreen.size.width - consoleSize.width / 2 - 12,
|
|
|
|
@@ -56,7 +72,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
if let windowScene = windowScene as? UIWindowScene {
|
|
|
|
|
consoleWindow = ConsoleWindow(windowScene: windowScene)
|
|
|
|
|
consoleWindow?.frame = UIScreen.main.bounds
|
|
|
|
|
consoleWindow?.windowLevel = UIWindow.Level.normal
|
|
|
|
|
consoleWindow?.windowLevel = UIWindow.Level.statusBar
|
|
|
|
|
consoleWindow?.isHidden = false
|
|
|
|
|
consoleWindow?.addSubview(consoleView)
|
|
|
|
|
|
|
|
|
@@ -73,12 +89,19 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
consoleView.center = possibleEndpoints.first!
|
|
|
|
|
consoleView.alpha = 0
|
|
|
|
|
|
|
|
|
|
consoleView.layer.borderWidth = 1
|
|
|
|
|
consoleView.layer.borderColor = UIColor(white: 1, alpha: 0.08).cgColor
|
|
|
|
|
|
|
|
|
|
consoleView.layer.cornerRadius = 19
|
|
|
|
|
consoleView.layer.cornerRadius = 20
|
|
|
|
|
consoleView.layer.cornerCurve = .continuous
|
|
|
|
|
|
|
|
|
|
let borderLayer = CALayer()
|
|
|
|
|
borderLayer.frame = CGRect(x: -1, y: -1,
|
|
|
|
|
width: consoleSize.width + 2,
|
|
|
|
|
height: consoleSize.height + 2)
|
|
|
|
|
borderLayer.borderWidth = 1
|
|
|
|
|
borderLayer.borderColor = UIColor(white: 1, alpha: 0.08).cgColor
|
|
|
|
|
borderLayer.cornerRadius = consoleView.layer.cornerRadius + 1
|
|
|
|
|
borderLayer.cornerCurve = .continuous
|
|
|
|
|
consoleView.layer.addSublayer(borderLayer)
|
|
|
|
|
|
|
|
|
|
// Configure text view.
|
|
|
|
|
consoleTextView.frame = CGRect(x: 0, y: 2, width: consoleSize.width, height: consoleSize.height - 4)
|
|
|
|
|
consoleTextView.isEditable = false
|
|
|
|
@@ -106,18 +129,29 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
consoleView.addGestureRecognizer(longPressRecognizer)
|
|
|
|
|
|
|
|
|
|
// Prepare menu button.
|
|
|
|
|
let diameter = CGFloat(25)
|
|
|
|
|
let diameter = CGFloat(26)
|
|
|
|
|
|
|
|
|
|
menuButton = UIButton(frame: CGRect(x: consoleView.bounds.width - diameter - (consoleView.layer.cornerRadius - diameter / 2),
|
|
|
|
|
y: consoleView.bounds.height - diameter - (consoleView.layer.cornerRadius - diameter / 2),
|
|
|
|
|
width: diameter, height: diameter))
|
|
|
|
|
menuButton.layer.cornerRadius = diameter / 2
|
|
|
|
|
menuButton.backgroundColor = UIColor(white: 1, alpha: 0.20)
|
|
|
|
|
// This tuned button frame is used to adjust where the menu appears.
|
|
|
|
|
menuButton = UIButton(frame: CGRect(x: consoleView.bounds.width - 44,
|
|
|
|
|
y: consoleView.bounds.height - 36,
|
|
|
|
|
width: 44,
|
|
|
|
|
height: 36 + 4 /*Offests the context menu by the desired amount*/))
|
|
|
|
|
|
|
|
|
|
let circleFrame = CGRect(
|
|
|
|
|
x: menuButton.bounds.width - diameter - (consoleView.layer.cornerRadius - diameter / 2),
|
|
|
|
|
y: menuButton.bounds.height - diameter - (consoleView.layer.cornerRadius - diameter / 2) - 4,
|
|
|
|
|
width: diameter, height: diameter)
|
|
|
|
|
|
|
|
|
|
let circle = UIView(frame: circleFrame)
|
|
|
|
|
circle.backgroundColor = UIColor(white: 0.2, alpha: 0.95)
|
|
|
|
|
circle.layer.cornerRadius = diameter / 2
|
|
|
|
|
circle.isUserInteractionEnabled = false
|
|
|
|
|
menuButton.addSubview(circle)
|
|
|
|
|
|
|
|
|
|
let ellipsisImage = UIImageView(image: UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 16)))
|
|
|
|
|
ellipsisImage.frame.size = menuButton!.bounds.size
|
|
|
|
|
ellipsisImage.frame.size = circle.bounds.size
|
|
|
|
|
ellipsisImage.contentMode = .center
|
|
|
|
|
menuButton.addSubview(ellipsisImage)
|
|
|
|
|
circle.addSubview(ellipsisImage)
|
|
|
|
|
|
|
|
|
|
menuButton.tintColor = UIColor(white: 1, alpha: 0.75)
|
|
|
|
|
menuButton.menu = makeMenu()
|
|
|
|
@@ -127,6 +161,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
UIView.swizzleDebugBehaviour
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Public
|
|
|
|
|
|
|
|
|
|
public var isVisible = false {
|
|
|
|
|
|
|
|
|
|
didSet {
|
|
|
|
@@ -149,6 +185,27 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Print items to the console view.
|
|
|
|
|
public func print(_ items: Any) {
|
|
|
|
|
|
|
|
|
|
let string: String = {
|
|
|
|
|
if consoleTextView.text == "" {
|
|
|
|
|
return "\(items)"
|
|
|
|
|
} else {
|
|
|
|
|
return "\(items)\n" + consoleTextView.text
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
setAttributedText(string)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Clear text in the console view.
|
|
|
|
|
public func clear() {
|
|
|
|
|
consoleTextView.text = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Private
|
|
|
|
|
|
|
|
|
|
private var debugBordersEnabled = false {
|
|
|
|
|
didSet {
|
|
|
|
|
GLOBAL_DEBUG_BORDERS = debugBordersEnabled
|
|
|
|
@@ -202,32 +259,19 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
consoleView.backgroundColor = .black
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func print(_ items: Any) {
|
|
|
|
|
|
|
|
|
|
func setAttributedText(_ string: String) {
|
|
|
|
|
let paragraphStyle = NSMutableParagraphStyle()
|
|
|
|
|
paragraphStyle.headIndent = 7
|
|
|
|
|
|
|
|
|
|
let attributes: [NSAttributedString.Key: Any] = [
|
|
|
|
|
.paragraphStyle: paragraphStyle,
|
|
|
|
|
.foregroundColor: UIColor.white,
|
|
|
|
|
.font: UIFont.systemFont(ofSize: 7, weight: .semibold, design: .monospaced)
|
|
|
|
|
.font: UIFont.systemFont(ofSize: fontSize, weight: .semibold, design: .monospaced)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
let string: String = {
|
|
|
|
|
if consoleTextView.attributedText.string == "" {
|
|
|
|
|
return "\(items)"
|
|
|
|
|
} else {
|
|
|
|
|
return "\(items)\n" + consoleTextView.text
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
consoleTextView.attributedText = NSAttributedString(string: string, attributes: attributes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public func clear() {
|
|
|
|
|
consoleTextView.text = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func makeMenu() -> UIMenu {
|
|
|
|
|
let viewFrames = UIAction(title: debugBordersEnabled ? "Hide View Frames" : "Show View Frames",
|
|
|
|
|
image: UIImage(systemName: "rectangle.3.offgrid"), handler: { _ in
|
|
|
|
@@ -253,8 +297,14 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}
|
|
|
|
|
animator.startAnimation()
|
|
|
|
|
})
|
|
|
|
|
let primarySection = UIMenu(title: "", options: .displayInline, children: [viewFrames, respring])
|
|
|
|
|
|
|
|
|
|
return UIMenu(title: "", children: [viewFrames, respring])
|
|
|
|
|
let clear = UIAction(title: "Clear Console",
|
|
|
|
|
image: UIImage(systemName: "xmark.square"), handler: { _ in
|
|
|
|
|
self.clear()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return UIMenu(title: "", children: [primarySection, clear])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc func longPressAction(recognizer: UILongPressGestureRecognizer) {
|
|
|
|
|