VMConfigNetworkPortForwardView.swift 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. //
  2. // Copyright © 2020 osy. All rights reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. import SwiftUI
  17. @available(macOS 12, *)
  18. struct VMConfigNetworkPortForwardView: View {
  19. @Binding var config: UTMQemuConfigurationNetwork
  20. @State private var isEditingNewPort = false
  21. @State private var isEditingExistingPort = false
  22. @State private var selectedId: UUID?
  23. @State private var editPortForward: UTMQemuConfigurationPortForward?
  24. var body: some View {
  25. VStack {
  26. Table(config.portForward, selection: $selectedId) {
  27. TableColumn("Protocol") { row in
  28. Text(row.protocol.prettyValue)
  29. }
  30. TableColumn("Guest Address") { row in
  31. Text(row.guestAddress ?? "")
  32. }
  33. TableColumn("Guest Port") { row in
  34. Text(String(row.guestPort))
  35. }
  36. TableColumn("Host Address") { row in
  37. Text(row.hostAddress ?? "")
  38. }
  39. TableColumn("Host Port") { row in
  40. Text(String(row.hostPort))
  41. }
  42. }.onDoubleClick {
  43. editPortForward = config.portForward.first(where: { $0.id == selectedId })
  44. }
  45. HStack {
  46. Spacer()
  47. if let selectedId = selectedId {
  48. Button("Delete") {
  49. config.portForward.removeAll(where: { $0.id == selectedId })
  50. self.selectedId = nil
  51. }
  52. Button("Edit…") {
  53. editPortForward = config.portForward.first(where: { $0.id == selectedId })
  54. }.popover(item: $editPortForward, arrowEdge: .top) { item in
  55. PortForwardEdit(config: $config, forward: item).padding()
  56. .frame(width: 250)
  57. }
  58. }
  59. Button("New…") {
  60. isEditingNewPort.toggle()
  61. }.popover(isPresented: $isEditingNewPort, arrowEdge: .top) {
  62. PortForwardEdit(config: $config, forward: .init()).padding()
  63. .frame(width: 250)
  64. }
  65. }.padding()
  66. }
  67. }
  68. }
  69. @available(macOS 11, *)
  70. struct VMConfigNetworkPortForwardLegacyView: View {
  71. @Binding var config: UTMQemuConfigurationNetwork
  72. @State private var editingNewPort = false
  73. @State private var selectedPortForward: UTMQemuConfigurationPortForward?
  74. var body: some View {
  75. Section(header: HStack {
  76. Text("Port Forward")
  77. Spacer()
  78. Button(action: { editingNewPort = true }, label: {
  79. Text("New…")
  80. }).popover(isPresented: $editingNewPort, arrowEdge: .bottom) {
  81. PortForwardEdit(config: $config, forward: .init()).padding()
  82. .frame(width: 250)
  83. }
  84. }) {
  85. VStack {
  86. ForEach(config.portForward) { forward in
  87. let isPopoverShown = Binding<Bool> {
  88. selectedPortForward == forward
  89. } set: { value in
  90. if value {
  91. selectedPortForward = forward
  92. } else {
  93. selectedPortForward = nil
  94. }
  95. }
  96. Button(action: { isPopoverShown.wrappedValue = true }, label: {
  97. let guest = "\(forward.guestAddress ?? ""):\(forward.guestPort)"
  98. let host = "\(forward.hostAddress ?? ""):\(forward.hostPort)"
  99. Text("\(guest) ➡️ \(host)")
  100. }).buttonStyle(.bordered)
  101. .popover(isPresented: isPopoverShown, arrowEdge: .bottom) {
  102. PortForwardEdit(config: $config, forward: forward).padding()
  103. .frame(width: 250)
  104. }
  105. }
  106. }
  107. }
  108. }
  109. }
  110. @available(macOS 11, *)
  111. struct PortForwardEdit: View {
  112. @Binding var config: UTMQemuConfigurationNetwork
  113. @State var forward: UTMQemuConfigurationPortForward
  114. @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
  115. var body: some View {
  116. VStack {
  117. VMConfigPortForwardForm(forward: $forward).multilineTextAlignment(.trailing)
  118. HStack {
  119. Spacer()
  120. let index = config.portForward.firstIndex(where: { $0.id == forward.id })
  121. if let index = index {
  122. Button(action: {
  123. config.portForward.remove(at: index)
  124. closePopup()
  125. }, label: {
  126. Text("Delete")
  127. })
  128. }
  129. Button(action: {
  130. if let index = index {
  131. config.portForward[index] = forward
  132. } else {
  133. config.portForward.append(forward)
  134. }
  135. closePopup()
  136. }, label: {
  137. Text("Save")
  138. }).disabled(forward.guestPort == 0 || forward.hostPort == 0)
  139. }
  140. }
  141. }
  142. private func closePopup() {
  143. self.presentationMode.wrappedValue.dismiss()
  144. }
  145. }
  146. @available(macOS 11, *)
  147. struct VMConfigNetworkPortForwardView_Previews: PreviewProvider {
  148. @State static private var config = UTMQemuConfigurationNetwork()
  149. static var previews: some View {
  150. Group {
  151. Form {
  152. if #available(macOS 12, *) {
  153. VMConfigNetworkPortForwardView(config: $config)
  154. } else {
  155. VMConfigNetworkPortForwardLegacyView(config: $config)
  156. }
  157. }.onAppear {
  158. if config.portForward.count == 0 {
  159. var newConfigPort = UTMQemuConfigurationPortForward()
  160. newConfigPort.protocol = .tcp
  161. newConfigPort.guestAddress = "1.2.3.4"
  162. newConfigPort.guestPort = 1234
  163. newConfigPort.hostAddress = "4.3.2.1"
  164. newConfigPort.hostPort = 4321
  165. config.portForward.append(newConfigPort)
  166. newConfigPort.protocol = .udp
  167. newConfigPort.guestAddress = ""
  168. newConfigPort.guestPort = 2222
  169. newConfigPort.hostAddress = ""
  170. newConfigPort.hostPort = 3333
  171. config.portForward.append(newConfigPort)
  172. }
  173. }
  174. }
  175. }
  176. }