From 2094bf4c9ef15bea195475a266ce578b6e2b25da Mon Sep 17 00:00:00 2001
From: Chris Harper <19harperc.alt@gmail.com>
Date: Sun, 21 Nov 2021 02:02:38 -0500
Subject: [PATCH] [ios] Initial layout of keyboard toolbars in SwiftUI (#75)
---
App/Supporting Files/Info.plist | 8 +-
App/UI/Keyboard/KeyboardKeyButtonStyle.swift | 80 +++++++
.../Keyboard/KeyboardPopupToolbarView.swift | 199 ++++++++++++++++++
App/UI/Keyboard/KeyboardToolbarView.swift | 155 ++++++++++++++
NewTerm.xcodeproj/project.pbxproj | 12 ++
5 files changed, 450 insertions(+), 4 deletions(-)
create mode 100644 App/UI/Keyboard/KeyboardKeyButtonStyle.swift
create mode 100644 App/UI/Keyboard/KeyboardPopupToolbarView.swift
create mode 100644 App/UI/Keyboard/KeyboardToolbarView.swift
diff --git a/App/Supporting Files/Info.plist b/App/Supporting Files/Info.plist
index 2c4ed06..e7e5358 100644
--- a/App/Supporting Files/Info.plist
+++ b/App/Supporting Files/Info.plist
@@ -4,6 +4,10 @@
ATSApplicationFontsPath
./
+ CADisableMinimumFrameDuration
+
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
@@ -82,9 +86,5 @@
UIUserInterfaceStyle
Dark
- CADisableMinimumFrameDuration
-
- CADisableMinimumFrameDurationOnPhone
-
diff --git a/App/UI/Keyboard/KeyboardKeyButtonStyle.swift b/App/UI/Keyboard/KeyboardKeyButtonStyle.swift
new file mode 100644
index 0000000..8fbc02c
--- /dev/null
+++ b/App/UI/Keyboard/KeyboardKeyButtonStyle.swift
@@ -0,0 +1,80 @@
+//
+// KeyboardButtonStyle.swift
+// NewTerm (iOS)
+//
+// Created by Chris Harper on 11/21/21.
+//
+
+import SwiftUI
+
+struct KeyboardKeyButtonStyle: ButtonStyle {
+ var selected: Bool = false
+ var shadow: Bool = false
+ var fixedWidth: CGFloat?
+
+ func makeBody(configuration: Configuration) -> some View {
+ if fixedWidth == nil {
+ configuration.label
+ .font(.system(size: isBigDevice ? 15 : 13))
+ .frame(height: (isBigDevice ? 35 : 30))
+ .padding(.horizontal, 8)
+ .background(configuration.isPressed ? Color(.keyBackgroundHighlighted) : (selected ? Color(.keyBackgroundSelected) : Color(.keyBackgroundNormal)))
+ .cornerRadius(isBigDevice ? 6 : 4)
+ .shadow(color: shadow ? Color.black.opacity(0.8) : .clear, radius: 0, x: 0, y: shadow ? 1 : 0)
+ .animation(nil)
+ } else {
+ configuration.label
+ .font(.system(size: isBigDevice ? 15 : 13))
+ .frame(width: fixedWidth!, height: (isBigDevice ? 35 : 30))
+ .background(configuration.isPressed ? Color(.keyBackgroundHighlighted) : (selected ? Color(.keyBackgroundSelected) : Color(.keyBackgroundNormal)))
+ .cornerRadius(isBigDevice ? 6 : 4)
+ .shadow(color: shadow ? Color.black.opacity(0.8) : .clear, radius: 0, x: 0, y: shadow ? 1 : 0)
+ .animation(nil)
+ }
+ }
+
+ init(selected: Bool = false, hasShadow shadow: Bool = false, fixedWidth: CGFloat? = nil) {
+ self.selected = selected
+ self.shadow = shadow
+ self.fixedWidth = fixedWidth
+ }
+}
+
+extension ButtonStyle where Self == KeyboardKeyButtonStyle {
+
+ ///A button style that mimicks the keys of the software keyboard.
+ static func keyboardKey(selected: Bool = false, hasShadow shadow: Bool = false, fixedWidth: CGFloat? = nil) -> KeyboardKeyButtonStyle {
+ return KeyboardKeyButtonStyle(selected: selected, hasShadow: shadow, fixedWidth: fixedWidth)
+ }
+}
+
+struct KeyboardKeyButtonStyleContainer: View {
+ var body: some View {
+ HStack(alignment: .center, spacing: 5) {
+ Button {
+
+ } label: {
+ Text("Ctrl")
+ }
+ .buttonStyle(.keyboardKey())
+
+ Button {
+
+ } label: {
+ Image(systemName: "arrow.down")
+ }
+ .buttonStyle(.keyboardKey(fixedWidth: 31))
+ }
+ .padding()
+ }
+}
+
+struct KeyboardKeyButtonStyleContainer_Previews: PreviewProvider {
+ static var previews: some View {
+ ForEach(ColorScheme.allCases, id: \.self) { scheme in
+ KeyboardKeyButtonStyleContainer()
+ .preferredColorScheme(scheme)
+ .previewLayout(.sizeThatFits)
+ }
+ }
+}
diff --git a/App/UI/Keyboard/KeyboardPopupToolbarView.swift b/App/UI/Keyboard/KeyboardPopupToolbarView.swift
new file mode 100644
index 0000000..a283cfa
--- /dev/null
+++ b/App/UI/Keyboard/KeyboardPopupToolbarView.swift
@@ -0,0 +1,199 @@
+//
+// KeyboardPopupToolbarView.swift
+// NewTerm (iOS)
+//
+// Created by Chris Harper on 11/21/21.
+//
+
+import SwiftUI
+import NewTermCommon
+
+struct KeyboardPopupToolbarView: View {
+
+ struct KeyType {
+ enum function: Int, Equatable, CaseIterable {
+ case f1
+ case f2
+ case f3
+ case f4
+ case f5
+ case f6
+ case f7
+ case f8
+ case f9
+ case f10
+ case f11
+ case f12
+
+ var text: String {
+ return "F\(self.rawValue)"
+ }
+ }
+
+ enum leading: Int, CaseIterable {
+ case home
+ case end
+
+ var text: String {
+ switch self {
+ case .home:
+ return "Home"
+ case .end:
+ return "End"
+ }
+ }
+ }
+
+ enum paging: Int, CaseIterable {
+ case pgUp
+ case pgDn
+
+ var text: String {
+ switch self {
+ case .pgUp:
+ return "PgUp"
+ case .pgDn:
+ return "PgDn"
+ }
+ }
+ }
+
+ enum trailing: Int, CaseIterable {
+ case frwdDel
+
+ var imageName: String {
+ switch self {
+ case .frwdDel:
+ return "delete.forward"
+ }
+ }
+
+ }
+ }
+
+ var functionButtonGroup: some View {
+ HStack(alignment: .center, spacing: 5) {
+ ForEach(KeyType.function.allCases, id: \.self) { button in
+ Button {
+ switch button {
+ case .f1:
+ break
+ case .f2:
+ break
+ case .f3:
+ break
+ case .f4:
+ break
+ case .f5:
+ break
+ case .f6:
+ break
+ case .f7:
+ break
+ case .f8:
+ break
+ case .f9:
+ break
+ case .f10:
+ break
+ case .f11:
+ break
+ case .f12:
+ break
+ }
+ } label: {
+ Text("\(button.text)")
+ }
+ .buttonStyle(
+ .keyboardKey(fixedWidth: 35)
+ )
+ }
+ }
+ }
+
+ var leadingButtonGroup: some View {
+ HStack(alignment: .center, spacing: 5) {
+ // 1: home; 2: end
+ ForEach(KeyType.leading.allCases, id: \.self) { button in
+ Button {
+ switch button {
+ case .home:
+ break
+ case .end:
+ break
+ }
+ } label: {
+ Text(button.text)
+ }
+ .buttonStyle(
+ .keyboardKey(fixedWidth: 50)
+ )
+ }
+ }
+ }
+
+ var pagingButtonGroup: some View {
+ HStack(alignment: .center, spacing: 5) {
+ ForEach(KeyType.paging.allCases, id: \.self) { button in
+ Button {
+ switch button {
+ case .pgUp:
+ break
+ case .pgDn:
+ break
+ }
+ } label: {
+ Text(button.text)
+ }
+ .buttonStyle(
+ .keyboardKey(fixedWidth: 50)
+ )
+ }
+ }
+ }
+
+ var trailingButtonGroup: some View {
+ HStack(alignment: .center, spacing: 5) {
+ ForEach(KeyType.trailing.allCases, id: \.self) { button in
+ Button {
+ switch button {
+ case .frwdDel:
+ break
+ }
+ } label: {
+ Image(systemName: button.imageName)
+ }
+ .buttonStyle(.keyboardKey())
+ }
+ }
+ }
+
+ var body: some View {
+ VStack(spacing: 5) {
+ ScrollView(.horizontal, showsIndicators: false) {
+ functionButtonGroup
+ .padding(.horizontal, 5)
+ }
+ HStack(alignment: .center, spacing: 10) {
+ leadingButtonGroup
+ pagingButtonGroup
+ Spacer()
+ trailingButtonGroup
+ }
+ .padding(.horizontal, 5)
+ }
+ .frame(maxWidth: .infinity, maxHeight: isBigDevice ? 96 : 80)
+ .background(Color(.keyboardToolbarBackground))
+ }
+
+}
+
+struct KeyboardPopupToolbarView_Previews: PreviewProvider {
+ static var previews: some View {
+ ForEach(ColorScheme.allCases, id: \.self) { scheme in
+ KeyboardPopupToolbarView()
+ .preferredColorScheme(scheme)
+ .previewLayout(.sizeThatFits)
+ }
+ }
+}
diff --git a/App/UI/Keyboard/KeyboardToolbarView.swift b/App/UI/Keyboard/KeyboardToolbarView.swift
new file mode 100644
index 0000000..df09b3e
--- /dev/null
+++ b/App/UI/Keyboard/KeyboardToolbarView.swift
@@ -0,0 +1,155 @@
+//
+// KeyboardToolbarView.swift
+// NewTerm (iOS)
+//
+// Created by Chris Harper on 11/21/21.
+//
+
+import SwiftUI
+import NewTermCommon
+
+struct KeyboardToolbarView: View {
+
+ struct KeyType {
+ enum leading: Int, CaseIterable {
+ case control
+ case escape
+ case tab
+ case more
+
+ var text: String {
+ switch self {
+ case .control:
+ return "Ctrl"
+ case .escape:
+ return "Esc"
+ case .tab:
+ return "Tab"
+ case .more:
+ return "More"
+ }
+ }
+
+ var imageName: String {
+ switch self {
+ case .control:
+ return "control"
+ case .escape:
+ return "escape"
+ case .tab:
+ return "arrow.right.to.line"
+ case .more:
+ return "ellipsis"
+ }
+ }
+ }
+
+ enum trailing: Int, CaseIterable {
+ case up
+ case down
+ case left
+ case right
+
+ var imageName: String {
+ switch self {
+ case .up:
+ return "arrow.up"
+ case .down:
+ return "arrow.down"
+ case .left:
+ return "arrow.left"
+ case .right:
+ return "arrow.right"
+ }
+ }
+ }
+ }
+
+ @State var ctrlKeySelected = false
+ @State var moreKeySelected = false
+
+ @ObservedObject var preferences = Preferences.shared
+
+ var leadingButtonGroup: some View {
+ HStack(alignment: .center, spacing: 5) {
+ ForEach(KeyType.leading.allCases, id: \.self) { button in
+ Button {
+ switch button {
+ case .control:
+ ctrlKeySelected.toggle()
+ case .escape:
+ break
+ case .tab:
+ break
+ case .more:
+ moreKeySelected.toggle()
+ }
+ } label: {
+ switch preferences.keyboardAccessoryStyle {
+ case .icons:
+ Image(systemName: button.imageName)
+ case .text:
+ Text(button.text)
+ }
+ }
+ .buttonStyle(
+ button == .control ? .keyboardKey(selected: ctrlKeySelected) : button == .more ? .keyboardKey(selected: moreKeySelected) : .keyboardKey()
+ )
+ }
+ }
+ }
+
+ var trailingButtonGroup: some View {
+ HStack(alignment: .center, spacing: 5) {
+ ForEach(KeyType.trailing.allCases, id: \.self) { button in
+ Button {
+ switch button {
+ case .up:
+ break
+ case .down:
+ break
+ case .left:
+ break
+ case .right:
+ break
+ }
+ } label: {
+ Image(systemName: button.imageName)
+ }
+ .buttonStyle(
+ .keyboardKey(fixedWidth: 30)
+ )
+ }
+ }
+ }
+
+ var body: some View {
+ VStack(spacing: 0) {
+ if moreKeySelected {
+ KeyboardPopupToolbarView()
+ }
+ HStack(alignment: .center, spacing: 5) {
+ leadingButtonGroup
+ Spacer()
+ trailingButtonGroup
+ }
+ .padding(.horizontal, 5)
+ .frame(maxWidth: .infinity, maxHeight: isBigDevice ? 48 : 40)
+ .background(Color(.keyboardToolbarBackground))
+ }
+ }
+}
+
+struct KeyboardToolbarView_Previews: PreviewProvider {
+ static var previews: some View {
+ ForEach(ColorScheme.allCases, id: \.self) { scheme in
+ VStack{
+ Spacer()
+ KeyboardToolbarView()
+ // .padding()
+ .preferredColorScheme(scheme)
+ .previewLayout(.sizeThatFits)
+ }
+ }
+ }
+}
diff --git a/NewTerm.xcodeproj/project.pbxproj b/NewTerm.xcodeproj/project.pbxproj
index d05e7bd..a4a25f5 100644
--- a/NewTerm.xcodeproj/project.pbxproj
+++ b/NewTerm.xcodeproj/project.pbxproj
@@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
+ 1E1B78AF274A1D4300F885CC /* KeyboardPopupToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1B78AE274A1D4300F885CC /* KeyboardPopupToolbarView.swift */; };
+ 1E4A7332274A0CED00211604 /* KeyboardToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4A7331274A0CED00211604 /* KeyboardToolbarView.swift */; };
+ 1E4A7334274A0D5F00211604 /* KeyboardKeyButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4A7333274A0D5F00211604 /* KeyboardKeyButtonStyle.swift */; };
1EEB4F66262CF591005E5B79 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EEB4F65262CF591005E5B79 /* SafariView.swift */; };
4E006D8C26F4AB71008D0BD2 /* SettingsGeneralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E006D8B26F4AB70008D0BD2 /* SettingsGeneralView.swift */; };
4E0A97A02685CDE30022F569 /* SettingsAcknowledgementsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E0A979F2685CDE30022F569 /* SettingsAcknowledgementsView.swift */; };
@@ -121,6 +124,9 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 1E1B78AE274A1D4300F885CC /* KeyboardPopupToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPopupToolbarView.swift; sourceTree = ""; };
+ 1E4A7331274A0CED00211604 /* KeyboardToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardToolbarView.swift; sourceTree = ""; };
+ 1E4A7333274A0D5F00211604 /* KeyboardKeyButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardKeyButtonStyle.swift; sourceTree = ""; };
1EEB4F65262CF591005E5B79 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; };
4E006D8B26F4AB70008D0BD2 /* SettingsGeneralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsGeneralView.swift; sourceTree = ""; };
4E0A979F2685CDE30022F569 /* SettingsAcknowledgementsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAcknowledgementsView.swift; sourceTree = ""; };
@@ -422,6 +428,9 @@
CFBB968A22B5481B00585BE6 /* TerminalTextView.swift */,
4E9B32EA261DCFC4006A1FBC /* TextInputBase.swift */,
4ECEE6EC25F9B87E00FAD26B /* TerminalPasswordInputView.swift */,
+ 1E4A7331274A0CED00211604 /* KeyboardToolbarView.swift */,
+ 1E4A7333274A0D5F00211604 /* KeyboardKeyButtonStyle.swift */,
+ 1E1B78AE274A1D4300F885CC /* KeyboardPopupToolbarView.swift */,
);
path = Keyboard;
sourceTree = "";
@@ -761,6 +770,7 @@
4ECFC23825FA3D2C007B0F51 /* LayoutGuide.swift in Sources */,
4E98081C261850E600E41883 /* KeyValueView.swift in Sources */,
4ECEE6ED25F9B87E00FAD26B /* TerminalPasswordInputView.swift in Sources */,
+ 1E1B78AF274A1D4300F885CC /* KeyboardPopupToolbarView.swift in Sources */,
CFBB96EE22B5481B00585BE6 /* TerminalSceneDelegate.swift in Sources */,
CFBB96EC22B5481B00585BE6 /* AppDelegate.swift in Sources */,
CF6B3BAA244C4538000A608B /* LogoHeaderView.swift in Sources */,
@@ -783,6 +793,8 @@
CFBB96D222B5481B00585BE6 /* TerminalTextView.swift in Sources */,
4E9B32EB261DCFC4006A1FBC /* TextInputBase.swift in Sources */,
CFBB96CF22B5481B00585BE6 /* KeyboardToolbar.swift in Sources */,
+ 1E4A7334274A0D5F00211604 /* KeyboardKeyButtonStyle.swift in Sources */,
+ 1E4A7332274A0CED00211604 /* KeyboardToolbarView.swift in Sources */,
4EDC998E26F4401B0030C1F9 /* SettingsInterfaceView.swift in Sources */,
4E460121261EC8C0004DBCC2 /* UpdateCheckManager.swift in Sources */,
4E0A97A02685CDE30022F569 /* SettingsAcknowledgementsView.swift in Sources */,