From 51ab649983e977fd378706e0037866851ba538b8 Mon Sep 17 00:00:00 2001 From: Xyct <87l46110@gmail.com> Date: Mon, 13 Feb 2023 01:14:54 +0800 Subject: [PATCH] Add new toast UI and operation hint message (#82) * add new hint toast * limit toast number to four --- PlayTools/Controls/ControlMode.swift | 12 ++++ PlayTools/Controls/PlayInput.swift | 15 ++++ PlayTools/Keymap/EditorController.swift | 7 +- PlayTools/Utils/Toast.swift | 95 +++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 2 deletions(-) diff --git a/PlayTools/Controls/ControlMode.swift b/PlayTools/Controls/ControlMode.swift index 35661efb..df298807 100644 --- a/PlayTools/Controls/ControlMode.swift +++ b/PlayTools/Controls/ControlMode.swift @@ -16,6 +16,8 @@ public class ControlMode { if !editor.editorMode { if show { if !visible { + NotificationCenter.default.post(name: NSNotification.Name.playtoolsKeymappingWillDisable, + object: nil, userInfo: [:]) if screen.fullscreen { screen.switchDock(true) } @@ -26,6 +28,8 @@ public class ControlMode { } } else { if visible { + NotificationCenter.default.post(name: NSNotification.Name.playtoolsKeymappingWillEnable, + object: nil, userInfo: [:]) if PlaySettings.shared.mouseMapping { AKInterface.shared!.hideCursor() } @@ -40,3 +44,11 @@ public class ControlMode { } } } + +extension NSNotification.Name { + public static let playtoolsKeymappingWillEnable: NSNotification.Name + = NSNotification.Name("playtools.keymappingWillEnable") + + public static let playtoolsKeymappingWillDisable: NSNotification.Name + = NSNotification.Name("playtools.keymappingWillDisable") +} diff --git a/PlayTools/Controls/PlayInput.swift b/PlayTools/Controls/PlayInput.swift index 0698c746..6b031afd 100644 --- a/PlayTools/Controls/PlayInput.swift +++ b/PlayTools/Controls/PlayInput.swift @@ -163,6 +163,21 @@ class PlayInput { } setupShortcuts() + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) { + if !settings.mouseMapping || !mode.visible { + return + } + Toast.showHint(title: "Keymapping Disabled", text: ["Press ", "option ⌥", " to enable keymapping"], + notification: NSNotification.Name.playtoolsKeymappingWillEnable) + let center = NotificationCenter.default + var token: NSObjectProtocol? + token = center.addObserver(forName: NSNotification.Name.playtoolsKeymappingWillEnable, + object: nil, queue: OperationQueue.main) { _ in + center.removeObserver(token!) + Toast.showHint(title: "Keymapping Enabled", text: ["Press ", "option ⌥", " to disable keymapping"], + notification: NSNotification.Name.playtoolsKeymappingWillDisable) + } + } // Fix beep sound AKInterface.shared! diff --git a/PlayTools/Keymap/EditorController.swift b/PlayTools/Keymap/EditorController.swift index 1fe47c66..c6f7075e 100644 --- a/PlayTools/Keymap/EditorController.swift +++ b/PlayTools/Keymap/EditorController.swift @@ -60,14 +60,17 @@ class EditorController { previousWindow?.makeKeyAndVisible() PlayInput.shared.toggleEditor(show: false) focusedControl = nil - Toast.showOver(msg: "Keymapping saved") + Toast.showHint(title: "Keymap Saved") } else { PlayInput.shared.toggleEditor(show: true) previousWindow = screen.keyWindow editorWindow = initWindow() editorWindow?.makeKeyAndVisible() showButtons() - Toast.showOver(msg: "Click to start keymmaping edit") + Toast.showHint(title: "Keymapping Editor", + text: ["Click a button to edit its position or key bind\n" + + "Click an empty area to open input menu"], + notification: NSNotification.Name.playtoolsKeymappingWillEnable) } // Toast.showOver(msg: "\(UIApplication.shared.windows.count)") lock.unlock() diff --git a/PlayTools/Utils/Toast.swift b/PlayTools/Utils/Toast.swift index 4bf90244..44fba46a 100644 --- a/PlayTools/Utils/Toast.swift +++ b/PlayTools/Utils/Toast.swift @@ -12,6 +12,101 @@ class Toast { Toast.show(message: msg, parent: parent) } } + static var hintView: [UIView] = [] + + private static let gap: CGFloat = 40 + + public static func hideHint(hint: UIView) { + guard let id = hintView.firstIndex(of: hint) else {return} + for index in 0.. id { + hintView[index-1] = hintView[index] + } + } + hintView.removeLast() + UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseOut, animations: { + hint.alpha = 0.0 + }, completion: {_ in + hint.removeFromSuperview() + }) + } + + private static func getAttributedString(title: String, text: [String]) -> NSMutableAttributedString { + var heading = title + if !text.isEmpty { + heading += "\n" + } + let txt = NSMutableAttributedString(string: text.reduce(into: heading, { result, string in + result += string + })) + var messageLength = 0 + var highlight = false + for msg in text { + txt.addAttribute(.foregroundColor, value: highlight ? UIColor.cyan: UIColor.white, + range: NSRange(location: heading.count + messageLength, length: msg.count)) + highlight = !highlight + messageLength += msg.count + } + let style = NSMutableParagraphStyle() + style.alignment = .center + txt.addAttribute(.paragraphStyle, value: style, + range: NSRange(location: 0, length: heading.count + messageLength)) + txt.addAttribute(.font, value: UIFont.systemFont(ofSize: 28, weight: .bold), + range: NSRange(location: 0, length: heading.count)) + txt.addAttribute(.foregroundColor, value: UIColor.white, + range: NSRange(location: 0, length: heading.count)) + txt.addAttribute(.font, value: UIFont.systemFont(ofSize: 28), + range: NSRange(location: heading.count, length: messageLength)) + return txt + } + + public static func showHint(title: String, text: [String] = [], timeout: Double = 3, + notification: NSNotification.Name? = nil) { + let parent = screen.keyWindow! + + // Width and height here serve as an upper limit. + // Text would fill width first, then wrap, then fill height, then scroll + let messageLabel = UITextView(frame: CGRect(x: 0, y: 0, width: 800, height: 800)) + messageLabel.attributedText = getAttributedString(title: title, text: text) + messageLabel.backgroundColor = UIColor.black.withAlphaComponent(0.5) + messageLabel.alpha = 1.0 + messageLabel.clipsToBounds = true + messageLabel.isUserInteractionEnabled = false + messageLabel.frame.size = messageLabel.sizeThatFits(messageLabel.frame.size) + messageLabel.layer.cornerCurve = CALayerCornerCurve.continuous + messageLabel.layer.cornerRadius = messageLabel.frame.size.height / 4 + messageLabel.frame.size.width += messageLabel.layer.cornerRadius * 2 + messageLabel.center.x = parent.center.x + messageLabel.center.y = -messageLabel.frame.size.height / 2 + + hintView.append(messageLabel) + parent.addSubview(messageLabel) + + if hintView.count > 4 { + hideHint(hint: hintView.first!) + } + if let note = notification { + let center = NotificationCenter.default + var token: NSObjectProtocol? + token = center.addObserver(forName: note, object: nil, queue: OperationQueue.main) { _ in + center.removeObserver(token!) + hideHint(hint: messageLabel) + } + } else { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5 + timeout) { + hideHint(hint: messageLabel) + } + } + for view in hintView { + UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseIn, animations: { + view.layer.position.y += messageLabel.frame.size.height + gap + }) + } + } // swiftlint:disable function_body_length