Skip to content

Commit

Permalink
Build Menu
Browse files Browse the repository at this point in the history
- Substituted keyCommands with Menu Bar.
- Menu Items are associated to their corresponding shortcut actions. Note that
on iPad, as of iOS15 they won't show up if there is no key associated to them.

- Refactored code, taking into account that now these shortcuts are defined
externally.
  • Loading branch information
Carlos Cabanero committed Sep 11, 2021
1 parent 01e3232 commit 58083ed
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 36 deletions.
4 changes: 4 additions & 0 deletions Blink.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
B7D450361DD3A87200CE0DBE /* BKiCloudSyncHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = B7D450351DD3A87200CE0DBE /* BKiCloudSyncHandler.m */; };
B7D4503C1DD4706000CE0DBE /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = B7D4503B1DD4706000CE0DBE /* Reachability.m */; };
B7D6A6291E2D43A800EDF7B0 /* BKSmartKeysConfigViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B7D6A6281E2D43A800EDF7B0 /* BKSmartKeysConfigViewController.m */; };
BD1758AC26EA8C5400AEC545 /* MenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1758AB26EA8C5400AEC545 /* MenuController.swift */; };
BD44DCE626D6BEAC00054338 /* BlinkItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD44DCE526D6BEAC00054338 /* BlinkItemIdentifier.swift */; };
BD44DCF126D9802900054338 /* TranslatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD44DCED26D9802900054338 /* TranslatorFactory.swift */; };
BD7810A52640C36100114700 /* NWConnection+WriterTo.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7810A42640C36100114700 /* NWConnection+WriterTo.swift */; };
Expand Down Expand Up @@ -655,6 +656,7 @@
B7D4503B1DD4706000CE0DBE /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
B7D6A6271E2D43A800EDF7B0 /* BKSmartKeysConfigViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BKSmartKeysConfigViewController.h; sourceTree = "<group>"; };
B7D6A6281E2D43A800EDF7B0 /* BKSmartKeysConfigViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BKSmartKeysConfigViewController.m; sourceTree = "<group>"; };
BD1758AB26EA8C5400AEC545 /* MenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuController.swift; sourceTree = "<group>"; };
BD44DCE526D6BEAC00054338 /* BlinkItemIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlinkItemIdentifier.swift; sourceTree = "<group>"; };
BD44DCED26D9802900054338 /* TranslatorFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TranslatorFactory.swift; sourceTree = "<group>"; };
BD7810A42640C36100114700 /* NWConnection+WriterTo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWConnection+WriterTo.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1087,6 +1089,7 @@
D2BF5F7B265953090070F839 /* Intents.intentdefinition */,
D2673734265BD77B002036FF /* BuildCLIConfig.swift */,
D275492C26A033CC0039CC95 /* FaceCam.swift */,
BD1758AB26EA8C5400AEC545 /* MenuController.swift */,
);
path = Blink;
sourceTree = "<group>";
Expand Down Expand Up @@ -2489,6 +2492,7 @@
D2F330DA20A7127B0074ADD7 /* open.m in Sources */,
C9B2E0321D6B612400B89F69 /* BKFont.m in Sources */,
D2C24412238E44AB0082C69C /* KeyModifier.swift in Sources */,
BD1758AC26EA8C5400AEC545 /* MenuController.swift in Sources */,
074F30791D062A2800A73445 /* main.m in Sources */,
D2A5221E230D279B0010AC04 /* SmarterTermInput.swift in Sources */,
D23D258823438B15002850CA /* UIColor.swift in Sources */,
Expand Down
8 changes: 8 additions & 0 deletions Blink/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -350,4 +350,12 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti
completionHandler();
}

#pragma mark - Menu Building

- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder {
if (builder.system == UIMenuSystem.mainSystem) {
[MenuController buildMenuWith:builder];
}
}

@end
133 changes: 133 additions & 0 deletions Blink/MenuController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//////////////////////////////////////////////////////////////////////////////////
//
// B L I N K
//
// Copyright (C) 2016-2019 Blink Mobile Shell Project
//
// This file is part of Blink.
//
// Blink is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Blink is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Blink. If not, see <http://www.gnu.org/licenses/>.
//
// In addition, Blink is also subject to certain additional terms under
// GNU GPL version 3 section 7.
//
// You should have received a copy of these additional terms immediately
// following the terms and conditions of the GNU General Public License
// which accompanied the Blink Source Code. If not, see
// <http://www.github.com/blinksh/blink>.
//
////////////////////////////////////////////////////////////////////////////////


import Foundation
import UIKit


//To rebuild a menu, call the setNeedsRebuild method. Call setNeedsRevalidate when you need the menu system to revalidate a menu.
@objc public class MenuController: NSObject {
enum ShellMenu: String, CaseIterable {
case windowNew
case windowClose
case tabNew
case tabClose
case configShow
}

enum EditMenu: String, CaseIterable {
case clipboardCopy
case clipboardPaste
case selectionGoogle
case selectionStackOverflow
case selectionShare
}

enum ViewMenu: String, CaseIterable {
case toggleKeyCast
case zoomIn
case zoomOut
case zoomReset
}

enum WindowMenu: String, CaseIterable {
case windowFocusOther
case tabMoveToOtherWindow
case tabNext
case tabPrev
case tabLast
}

override private init() {}

@objc public class func buildMenu(with builder: UIMenuBuilder) {
// We will embed our own textSize inside View, so just remove to avoid collisions.
builder.remove(menu: .textSize)

let shellMenuCommands: [UICommand] = ShellMenu.allCases.map { generate(Command(rawValue: $0.rawValue)!) }
let editMenuCommands: [UICommand] = EditMenu.allCases.map { generate(Command(rawValue: $0.rawValue)!) }
let viewMenuCommands: [UICommand] = ViewMenu.allCases.map { generate(Command(rawValue: $0.rawValue)!) }
let windowMenuCommands: [UICommand] = WindowMenu.allCases.map { generate(Command(rawValue: $0.rawValue)!) }

builder.insertSibling(UIMenu(title: "Shell",
image: nil,
identifier: UIMenu.Identifier("com.example.apple-samplecode.menus.shellMenu"),
options: [],
children: shellMenuCommands), beforeMenu: .edit)

// Note we are assuming that shortcuts here are already unique.
// The same shortcut, or the same action, will make this crash with a
// 'NSInternalInconsistencyException', reason: 'replacement menu has duplicate submenu,
// command or key command, or a key command is missing input or action'.
builder.replaceChildren(ofMenu: .standardEdit) { _ in editMenuCommands }
builder.replaceChildren(ofMenu: .view) { _ in viewMenuCommands }
builder.replaceChildren(ofMenu: .window) { _ in windowMenuCommands }

// TODO 'NSInternalInconsistencyException', reason: 'replacement menu has duplicate submenu, command or key command, or a key command is missing input or action'
// The action also must be different, or at least have propertyList that identifies as different.
// We need to take into account that the stored shortcuts must not collide, otherwise things will break.

// There is an additional problem, which is that maybe a user by mistake may have that same
// combination. We should clean it up. We could remove the dupes at the storage,
// and then use that same function to make sure there are none during creation.
}

private class func generate(_ command: Command) -> UICommand {
let kbConfig = KBTracker.shared.loadConfig()

// For the action to be different, we are passing it as part of the PropertyList.
if let shortcut = kbConfig.shortcuts.first(
where: { s in // s.triggers(command)
if case .command(let cmd) = s.action,
case command = cmd
{
return true
}
return false
})
{
return UIKeyCommand(title: command.title,
action: #selector(SpaceController._onShortcut(_:)),
input: shortcut.input,
modifierFlags: shortcut.modifiers,
propertyList: ["Command": command.rawValue])
} else {
return UICommand(
title: command.title,
image: nil,
action: #selector(SpaceController._onShortcut(_:)),
propertyList: ["Command": command.rawValue]
)
}
}
}

29 changes: 12 additions & 17 deletions Blink/SpaceController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -530,21 +530,6 @@ extension SpaceController {
view.window?.windowScene?.activationState == UIScene.ActivationState.foregroundActive
}

public override var keyCommands: [UIKeyCommand]? {
guard
let input = KBTracker.shared.input,
foregroundActive
else {
return []
}

if let keyCode = stuckKeyCode {
return [UIKeyCommand(input: "", modifierFlags: keyCode.modifierFlags, action: #selector(onStuckOpCommand))]
}

return input.blinkKeyCommands
}

@objc func onStuckOpCommand() {
stuckKeyCode = nil
presentedViewController?.dismiss(animated: true)
Expand All @@ -565,13 +550,23 @@ extension SpaceController {
case .press(let keyCode, mods: let mods):
input.reportPress(UIKeyModifierFlags(rawValue: mods), keyId: keyCode.id)
break;
case .command(let c):
_onCommand(c)
// case .command(let c):
// _onCommand(c)
default:
break;
}
}

@objc func _onShortcut(_ event: UICommand) {
guard
let propertyList = event.propertyList as? [String:String],
let cmd = Command(rawValue: propertyList["Command"]!)
else {
return
}
_onCommand(cmd)
}

func _onCommand(_ cmd: Command) {
guard foregroundActive else {
return
Expand Down
21 changes: 2 additions & 19 deletions KB/Native/Views/KBWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ class KBWebView: KBWebViewBase {

private var _loaded = false
private(set) var webViewReady = false
private(set) var blinkKeyCommands: [BlinkCommand] = []
private var _grabsCtrlSpace = false

func configure(_ cfg: KBConfig) {
_buildCommands(cfg)
_grabsCtrlSpace = matchCommand(input: " ", flags: [UIKeyModifierFlags.control]) != nil

guard
let data = try? JSONEncoder().encode(cfg),
let json = String(data: data, encoding: .utf8)
Expand All @@ -54,23 +54,6 @@ class KBWebView: KBWebViewBase {
report("config", arg: json as NSString)
}

func _buildCommands(_ cfg: KBConfig) {
blinkKeyCommands = cfg.shortcuts.map { shortcut in
let cmd = BlinkCommand(
title: shortcut.action.isCommand ? shortcut.title : "", // Show only commands in cmd help view
image: nil,
action: #selector(SpaceController._onBlinkCommand(_:)),
input: shortcut.input,
modifierFlags: shortcut.modifiers,
propertyList: nil
)
cmd.bindingAction = shortcut.action
return cmd
}

_grabsCtrlSpace = matchCommand(input: " ", flags: [UIKeyModifierFlags.control]) != nil
}

func matchCommand(input: String, flags: UIKeyModifierFlags) -> (UIKeyCommand, UIResponder)? {
var result: (UIKeyCommand, UIResponder)? = nil

Expand Down

0 comments on commit 58083ed

Please sign in to comment.