Skip to content

Commit

Permalink
Add a custom menu to the console’s menu
Browse files Browse the repository at this point in the history
  • Loading branch information
duraidabdul committed Nov 22, 2022
1 parent 1dcb6e3 commit 6413eb8
Showing 1 changed file with 78 additions and 18 deletions.
96 changes: 78 additions & 18 deletions Sources/LocalConsole/LCManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
lazy var consoleTextView = InvertedTextView()

/// Button that reveals menu.
lazy var menuButton = UIButton()
lazy var menuButton = ConsoleMenuButton()

/// Tracks whether the PiP console is in text view scroll mode or pan mode.
var scrollLocked = true
Expand Down Expand Up @@ -319,10 +319,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let diameter = CGFloat(30)

// 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*/))
menuButton.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*/
)
menuButton.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin]

let circleFrame = CGRect(
Expand All @@ -336,13 +338,17 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
circle.isUserInteractionEnabled = false
menuButton.addSubview(circle)

let ellipsisImage = UIImageView(image: UIImage(systemName: "ellipsis",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .medium)))
let ellipsisImage = UIImageView(
image: UIImage(
systemName: "ellipsis",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .medium)
)
)
ellipsisImage.frame.size = circle.bounds.size
ellipsisImage.contentMode = .center
circle.addSubview(ellipsisImage)

menuButton.tintColor = UIColor(white: 1, alpha: 0.75)
menuButton.tintColor = UIColor(white: 1, alpha: 0.8)
menuButton.menu = makeMenu()
menuButton.showsMenuAsPrimaryAction = true
consoleView.addSubview(menuButton)
Expand Down Expand Up @@ -391,16 +397,24 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}

func addConsoleToWindow(window: UIWindow) {
SwizzleTool().swizzleContextMenuReverseOrder()

window.addSubview(consoleViewController.view)
window.rootViewController?.addChild(consoleViewController)

consoleViewController.view = PassthroughView()
consoleViewController.view.addSubview(consoleView)

window.addSubview(consoleViewController.view)
consoleViewController.view.frame = window.bounds
consoleViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

updateConsoleOrigin()

SwizzleTool().swizzleContextMenuReverseOrder()

// Ensure console view always stays above other views.
SwizzleTool().swizzleDidAddSubview {
window.bringSubviewToFront(self.consoleViewController.view)
}
}

/// Ensures the window is configured (i.e. scene has been found). If not, delay and wait for a scene to prepare itself, then try again.
Expand All @@ -426,8 +440,10 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}

func snapToCachedEndpoint() {
let cachedConsolePosition = CGPoint(x: UserDefaults.standard.object(forKey: "LocalConsole.X") as? CGFloat ?? possibleEndpoints.first!.x,
y: UserDefaults.standard.object(forKey: "LocalConsole.Y") as? CGFloat ?? possibleEndpoints.first!.y)
let cachedConsolePosition = CGPoint(
x: UserDefaults.standard.object(forKey: "LocalConsole.X") as? CGFloat ?? possibleEndpoints.first!.x,
y: UserDefaults.standard.object(forKey: "LocalConsole.Y") as? CGFloat ?? possibleEndpoints.first!.y
)

consoleView.center = cachedConsolePosition // Update console center so possibleEndpoints are calculated correctly.
consoleView.center = nearestTargetTo(cachedConsolePosition, possibleTargets: possibleEndpoints)
Expand Down Expand Up @@ -478,8 +494,14 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}

// This menu is included in the console's main menu.
public var menu: UIMenuElement? = nil {
didSet {
menuButton.menu = makeMenu()
}
}

var grabberMode: Bool = false {

didSet {
guard oldValue != grabberMode else { return }

Expand Down Expand Up @@ -882,9 +904,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}()

if keys.isEmpty {
actions.append(UIAction(title: "No Entries",
image: nil, attributes: .disabled, handler: { _ in }
))
actions.append(
UIAction(title: "No Entries", attributes: .disabled, handler: { _ in })
)
} else {
for key in keys.sorted(by: { $0.lowercased() < $1.lowercased() }) {

Expand Down Expand Up @@ -1057,7 +1079,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
} else {
menuContent.append(UIMenu(title: "", options: .displayInline, children: [resize]))
}

menuContent.append(debugMenu)
if let customMenu = menu {
menuContent.append(customMenu)
}

if consoleTextView.text != "" {
menuContent.append(UIMenu(title: "", options: .displayInline, children: [clear]))
}
Expand Down Expand Up @@ -1238,6 +1265,20 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}

/// Custom button that pauses console window swizzling to allow the console menu's presenting view controller to remain the top view controller.
class ConsoleMenuButton: UIButton {
override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
super.contextMenuInteraction(interaction, willDisplayMenuFor: configuration, animator: animator)

SwizzleTool.pauseDidAddSubviewSwizzledClosure = true
}

override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {

SwizzleTool.pauseDidAddSubviewSwizzledClosure = false
}
}

// Custom view that is passes touches .
class PassthroughView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
Expand Down Expand Up @@ -1290,7 +1331,6 @@ class SwizzleTool: NSObject {
}

@objc func swizzled_reverses_Action_Order() -> Bool {

if let menu = self.value(forKey: "displayed" + "Menu") as? UIMenu,
menu.title == "Debug" || menu.title == "User" + "Defaults" {
return false
Expand All @@ -1302,8 +1342,28 @@ class SwizzleTool: NSObject {

return false
}
}

static var swizzledDidAddSubviewClosure: (() -> Void)?
static var pauseDidAddSubviewSwizzledClosure: Bool = false

func swizzleDidAddSubview(_ closure: @escaping () -> Void) {
guard let originalMethod = class_getInstanceMethod(UIWindow.self, #selector(UIWindow.didAddSubview(_:))),
let swizzledMethod = class_getInstanceMethod(SwizzleTool.self, #selector(swizzled_did_add_subview(_:)))
else { Swift.print("Swizzle Error Occurred"); return }

method_exchangeImplementations(originalMethod, swizzledMethod)

Self.swizzledDidAddSubviewClosure = closure
}

@objc func swizzled_did_add_subview(_ subview: UIView) {
guard !Self.pauseDidAddSubviewSwizzledClosure else { return }

if let closure = Self.swizzledDidAddSubviewClosure {
closure()
}
}
}

class LumaView: UIView {
lazy var visualEffectView: UIView = {
Expand Down

0 comments on commit 6413eb8

Please sign in to comment.