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 */,