diff --git a/PlantUML4iPad.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/PlantUML4iPad.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/PlantUML4iPad.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/PlantUML4iPad.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PlantUML4iPad.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1bbde48..18c8679 100644 --- a/PlantUML4iPad.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/PlantUML4iPad.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,7 +6,7 @@ "location" : "https://github.com/bsorrentino/PlantUML4iPad.git", "state" : { "branch" : "line_editor", - "revision" : "c90880eceaa26b08870ab34caf356dbbfa6cabc8" + "revision" : "502b2d37fa46ab0fc24936860d1edabf0342ce89" } } ], diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/DebounceObject.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/DebounceObject.swift new file mode 100644 index 0000000..883c73f --- /dev/null +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/DebounceObject.swift @@ -0,0 +1,40 @@ +// +// File.swift +// +// +// Created by Bartolomeo Sorrentino on 06/01/23. +// + +import Foundation +import Combine + +class DebounceUpdate where T : Equatable { + + private var updateSubject = PassthroughSubject() + + private var cancellabe:Cancellable? + + func subscribe( debounceInSeconds seconds: Double, onUpdate update: @escaping ( T ) -> Void ) { + + if self.cancellabe == nil { + + self.cancellabe = updateSubject + .removeDuplicates() + .debounce(for: .seconds(seconds), scheduler: RunLoop.main) + .print() + .sink( receiveValue: update ) + + } + + } + + func request( for element:T ) { + updateSubject.send( element ) + } +} + +class DebounceUpdateObject : ObservableObject where T : Equatable { + + let update = DebounceUpdate() + +} diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard+Color.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard+Color.swift new file mode 100644 index 0000000..3558b31 --- /dev/null +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard+Color.swift @@ -0,0 +1,217 @@ +// +// PlantUMLKeyboard+Color.swift +// +// +// Created by Bartolomeo Sorrentino on 05/01/23. +// + +import SwiftUI +import Combine + +typealias RGBA = (R:Int, G:Int, B:Int, A:Int ) + +// MARK: CGColor extension + +extension CGColor { + + func rgbValue() -> RGBA? { + var output:RGBA? = nil + + if let values = self.components { + + switch values.count { + case 1: + output = ( Int(values[0] * 255), Int(values[0] * 255), Int(values[0] * 255),1) + break + case 2: + output = ( Int(values[0] * 255), Int(values[0] * 255), Int(values[0] * 255),Int(values[1] * 255)) + break + case 3: + output = ( Int(values[0] * 255), Int(values[1] * 255), Int(values[2] * 255),1) + case 4: + output = ( Int(values[0] * 255), Int(values[1] * 255), Int(values[2] * 255),Int(values[3] * 255)) + default: + break + } + } + + return output + } + + func hexValue() -> String? { + var output:String? = nil + + if let rgba:RGBA = self.rgbValue() { + output = "#\(String(format:"%02X", rgba.R))\(String(format:"%02X", rgba.G))\(String(format:"%02X", rgba.B))\( String(format:"%02X", rgba.A))" + } + + return output + } + +} + +// MARK: Color extension +extension Color { + + func hexValue() -> String? { + self.cgColor?.hexValue() + } +} + + +struct ColorKeyButton : UIViewRepresentable { + + var symbol:Symbol + var onPressSymbol: (Symbol) -> Void + + init(symbol: Symbol, onPressSymbol: @escaping (Symbol) -> Void) { + self.symbol = symbol + self.onPressSymbol = onPressSymbol + } + + func makeCoordinator() -> Coordinator { + Coordinator( self ) + } + + func makeUIView(context: Context) -> UIButton { + let button = UIButton() + + + // + // title + // + button.setTitle(symbol.id, for: .normal) + button.setTitleColor(UIColor.black, for: .normal) + if let label = button.titleLabel { + label.font = UIFont.systemFont(ofSize: 16, weight: .bold) + } + + // + // Image + // +// if let image = UIImage(named: "paintpalette", in: .module, compatibleWith: nil) { +// result.setImage(image, for: .normal) +// } + if let image = UIImage(systemName: "paintbrush.fill") { + button.setImage(image, for: .normal) + } + + // + // Border + // + button.layer.borderColor = UIColor.black.cgColor + button.layer.borderWidth = 1 + button.layer.cornerRadius = 5 + + button.frame.size = CGSize(width: 100, height: 30) + + // + // constraints + // +// button.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true +// button.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true + + return button + } + + func updateUIView(_ button: UIButton, context: Context) { + // + // action + // + let action = UIAction(title: symbol.id ) { _ in + + let colorPicker = UIColorPickerViewController() + + colorPicker.delegate = context.coordinator + + getRootViewController()?.presentedViewController?.present( colorPicker, animated: true, completion: nil ) + } + + button.addAction( action, for: .touchDown ) + + } + + + +} + +extension ColorKeyButton { + + class Coordinator: NSObject, UIColorPickerViewControllerDelegate { + + private let update = DebounceUpdate() + private var owner: ColorKeyButton + + init( _ owner: ColorKeyButton ) { + self.owner = owner + } + + func colorPickerViewController(_ viewController: UIColorPickerViewController, didSelect color: UIColor, continuously: Bool) { + + guard let hexColor = color.cgColor.hexValue() else { + return + } + + update.subscribe( debounceInSeconds: 0 ) { [self] value in + + viewController.dismiss(animated: true, completion: nil) + + let symbol = Symbol( id: owner.symbol.id, value: value ) + + owner.onPressSymbol( symbol ) + + } + + let value = String(format: owner.symbol.value, hexColor ) + + update.request(for: value) + } + + } +} + +/* +struct ColorKeyView: View { + @StateObject private var updateColor = DebounceUpdateObject() + + @State private var selectedColor = Color.blue.opacity(0.5) + + var symbol:Symbol + var onPressSymbol: (Symbol) -> Void + + var body: some View { + VStack { + + ColorPicker( selection: $selectedColor, label: { + Text(symbol.id).font(.system(size: 16).bold()) + + }) + .frame( maxWidth: 110 ) + .border(.black) + .padding() + .onChange(of: selectedColor ) { color in + updateColor.update.subscribe( debounceInSeconds: 0 ) { + onPressSymbol( makeSymbol( from: $0 ) ) + } + updateColor.update.request(for: color.hexValue() ?? "") + + } + } + + } + + private func makeSymbol( from hexColor: String ) -> Symbol { + + let value = String(format: symbol.value, hexColor ) + return Symbol( id: symbol.id, value: value ) + } +} + +*/ + +// MARK: Preview +struct ColorKeyButton_Previews: PreviewProvider { + static var previews: some View { + ColorKeyButton( symbol:Symbol( id: "test" ), onPressSymbol: { _ in } ) + } +} diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard.swift index bd4c599..aded960 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard.swift +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard.swift @@ -4,6 +4,7 @@ import LineEditor public struct PlantUMLKeyboardView: LineEditorKeyboard { + var onHide:() -> Void var onPressSymbol: (Symbol) -> Void @@ -11,6 +12,7 @@ public struct PlantUMLKeyboardView: LineEditorKeyboard { self.onHide = onHide self.onPressSymbol = onPressSymbol } + public var body : some View{ ZStack(alignment: .topLeading) { @@ -41,6 +43,7 @@ public struct PlantUMLKeyboardView: LineEditorKeyboard { .padding() } + func ContentView( _ group: PlantUMLSymbolGroup ) -> some View { ScrollView(.vertical, showsIndicators: false) { @@ -52,39 +55,52 @@ public struct PlantUMLKeyboardView: LineEditorKeyboard { ForEach( Array(i.enumerated()), id: \.offset ) { cellIndex, symbol in - Button { - - onPressSymbol(symbol) - - } label: { - - ButtonLabel( for: group, row: rowIndex, cell: cellIndex, symbol: symbol ) - + VStack { + if symbol.type == "color" { +// ColorKeyView( symbol: symbol, onPressSymbol: onPressSymbol ) + ColorKeyButton( symbol: symbol, onPressSymbol: onPressSymbol ) + .frame( maxWidth: 100) + + } + else { + Button { + onPressSymbol(symbol) + } label: { + ButtonLabel( for: group, row: rowIndex, cell: cellIndex, symbol: symbol ) + } + .buttonStyle( KeyButtonStyle() ) + } } - .buttonStyle( KeyButtonStyle() ) + } + } } } .padding(.top) - + } } -} - -fileprivate struct KeyButtonStyle: ButtonStyle { - func makeBody(configuration: Configuration) -> some View { - configuration.label - .padding(5) - .border( .black, width: 1) - .background( .white ) - } } - +// MARK: Plain Button Extension extension PlantUMLKeyboardView { - + + fileprivate struct KeyButtonStyle: ButtonStyle { + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding(5) + .background( .white ) +// .border( .black, width: 1) + .overlay { + RoundedRectangle(cornerRadius: 5) + .stroke(.black, lineWidth: 1) + } + } + } + func ButtonLabel( for group: PlantUMLSymbolGroup, row: Int, cell: Int, symbol: Symbol ) -> some View { Text(symbol.id).font(.system(size: 16).bold()) @@ -106,6 +122,7 @@ extension PlantUMLKeyboardView { } } + struct PlantUMLKeyboardView_Previews: PreviewProvider { static var previews: some View { diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift index fb23b92..1263533 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift @@ -12,27 +12,29 @@ struct Symbol : Decodable, Identifiable, CustomStringConvertible, LineEditorKeyb enum CodingKeys: String, CodingKey { case id - case value = "v0" - case additionalValues = "v1" + case value + case additionalValues = "additional" + case type } var description: String { value } var id:String private var _value:String? - private var _additionalValues:[String]? + private(set) var additionalValues:[String]? + var type = "string" var value: String { get { _value ?? id } } - var additionalValues: [String]? { - get { _additionalValues } - } +// var additionalValues: [String]? { +// get { _additionalValues } +// } - init( _ id:String, _ value:String? = nil, _ additionalValues: [String]? = nil) { + init( id:String, value:String? = nil, additionalValues: [String]? = nil) { self.id = id self._value = value - self._additionalValues = additionalValues + self.additionalValues = additionalValues } init(from decoder: Decoder) throws { @@ -41,7 +43,10 @@ struct Symbol : Decodable, Identifiable, CustomStringConvertible, LineEditorKeyb self.id = try container.decode(String.self, forKey: CodingKeys.id) self._value = try container.decodeIfPresent(String.self, forKey: CodingKeys.value) - self._additionalValues = try container.decodeIfPresent([String].self, forKey: CodingKeys.additionalValues) + self.additionalValues = try container.decodeIfPresent([String].self, forKey: CodingKeys.additionalValues) + if let type = try container.decodeIfPresent(String.self, forKey: CodingKeys.type) { + self.type = type + } } diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbolsBuilder.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbolsBuilder.swift index 900697f..ac94903 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbolsBuilder.swift +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbolsBuilder.swift @@ -23,13 +23,13 @@ extension Symbol { return sym } else if let str = elem as? String { - return Symbol(str) + return Symbol(id: str) } else if let part2 = elem as? PART2 { - return Symbol(part2.0, part2.1) + return Symbol(id: part2.0, value: part2.1) } else if let part3 = elem as? PART3 { - return Symbol(part3.0, part3.1, part3.2) + return Symbol( id: part3.0, value: part3.1, additionalValues: part3.2) } return nil diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/Window+Utils.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/Window+Utils.swift index 03e1cec..511d9ec 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/Window+Utils.swift +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/Window+Utils.swift @@ -34,6 +34,10 @@ public func getWindows() -> [UIWindow]? { } +func getRootViewController() -> UIViewController? { + getWindows()?.first?.rootViewController +} + func getFirstTextFieldResponder() -> UITextField? { guard let firstWindow = getWindows()?.first, let firstResponder = firstWindow.firstResponder else { diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json index e633e2d..146713c 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json @@ -4,20 +4,21 @@ "name": "general", "rows": [ [ - { "id": "title", "v0": "title my title" }, - { "id": "header", "v0": "header my header" }, - { "id": "footer", "v0": "footer my footer" } + { "id": "title", "value": "title my title" }, + { "id": "header", "value": "header my header" }, + { "id": "footer", "value": "footer my footer" } ], [ - { "id": "allow mixing", "v0": "allow_mixing" }, + { "id": "allow mixing", "value": "allow_mixing" }, { "id": "hide empty members" }, - { "id": "shadowing false", "v0": "skinparam shadowing false" }, - { "id": "linetype ortho", "v0": "skinparam linetype ortho" }, - { "id": "left to right", "v0": "left to right direction" }, - { "id": "top to bottom", "v0": "top to bottom direction" } + { "id": "shadowing false", "value": "skinparam shadowing false" }, + { "id": "linetype ortho", "value": "skinparam linetype ortho" }, + { "id": "left to right", "value": "left to right direction" }, + { "id": "top to bottom", "value": "top to bottom direction" } ], [ - { "id": "[#red]" }, + { "id": "#color", "value": "%@", "type": "color" }, + { "id": "[#color]", "value": "[%@]", "type": "color" }, { "id": "#line.dashed" } ] ] @@ -28,19 +29,19 @@ [ { "id": "autonumber" }, { "id": "hide footbox" }, - { "id": "teoz", "v0": "!pragma teoz true" } + { "id": "teoz", "value": "!pragma teoz true" } ], [ - {"id": "box", "v0": "box \"\\nmy box\\n", "v1": ["end box"]}, - {"id": "actor", "v0": "actor \"my actor\" as a1"}, - {"id": "participant", "v0": "participant \"my participant\" as p1"}, - {"id": "boundary", "v0": "boundary \"my boundary\" as b1"}, - {"id": "control", "v0": "control \"my control\" as c1"}, - {"id": "entity", "v0": "entity \"my entity\" as e1"}, - {"id": "database", "v0": "database \"my database\" as db1"}, - {"id": "collections","v0": "collections \"my collections\" as cc1" }, - {"id": "queue", "v0": "queue \"my queue\" as q1"} + {"id": "box", "value": "box \"\\nmy box\\n", "additional": ["end box"]}, + {"id": "actor", "value": "actor \"my actor\" as a1"}, + {"id": "participant", "value": "participant \"my participant\" as p1"}, + {"id": "boundary", "value": "boundary \"my boundary\" as b1"}, + {"id": "control", "value": "control \"my control\" as c1"}, + {"id": "entity", "value": "entity \"my entity\" as e1"}, + {"id": "database", "value": "database \"my database\" as db1"}, + {"id": "collections","value": "collections \"my collections\" as cc1" }, + {"id": "queue", "value": "queue \"my queue\" as q1"} ], [ { "id": "->x" }, @@ -55,13 +56,13 @@ { "id": "<->o" } ], [ - { "id": "{start}", "v0": "{start} " }, { "id": "{end}", "v0": "{end} " }, - { "id": "group", "v0": "group My own label", "v1": ["end"] }, - { "id": "loop", "v0": "loop N times", "v1": ["end"] }, - { "id": "alt", "v0": "alt successful case", "v1": [ "else some kind of failure", "end"] }, - { "id": "note left", "v0": "note left /' of p1 '/", "v1": ["this note is displayed left", "end note"]}, - { "id": "note right", "v0": "note right /' of p1 '/", "v1": ["this note is displayed right", "end note"]}, - { "id": "note over", "v0": "note over p1 /', p2 '/", "v1": ["this note is displayed over participant1", "end note"]} + { "id": "{start}", "value": "{start} " }, { "id": "{end}", "value": "{end} " }, + { "id": "group", "value": "group My own label", "additional": ["end"] }, + { "id": "loop", "value": "loop N times", "additional": ["end"] }, + { "id": "alt", "value": "alt successful case", "additional": [ "else some kind of failure", "end"] }, + { "id": "note left", "value": "note left /' of p1 '/", "additional": ["this note is displayed left", "end note"]}, + { "id": "note right", "value": "note right /' of p1 '/", "additional": ["this note is displayed right", "end note"]}, + { "id": "note over", "value": "note over p1 /', p2 '/", "additional": ["this note is displayed over participant1", "end note"]} ] ] @@ -70,34 +71,34 @@ "name": "deployment", "rows": [ [ - { "id": "actor", "v0": "actor \"my actor\" as ac1"}, - { "id": "agent", "v0": "agent \"my agent\" as ag1"}, - { "id": "artifact", "v0": "artifact \"my artifact\" as ar1"}, - { "id": "boundary", "v0": "boundary \"my boundary\" as bn1"}, - { "id": "card", "v0": "card \"my card\" as cd1"}, - { "id": "circle", "v0": "circle \"my circle\" as cr1"}, - { "id": "cloud", "v0": "cloud \"my cloud\" as cd1"}, - { "id": "collections", "v0": "collections \"my collections\" as cl1"}, - { "id": "component", "v0": "component \"my component\" as cp1"}, - { "id": "control", "v0": "control \"my control\" as cn1"}, - { "id": "person", "v0": "person \"my person\" as pr1"}, - { "id": "queue", "v0": "queue \"my queue\" as qq1"}, - { "id": "rectangle", "v0": "rectangle \"my rect\\n\" as rc1 {", "v1": [ "}" ] } + { "id": "actor", "value": "actor \"my actor\" as ac1"}, + { "id": "agent", "value": "agent \"my agent\" as ag1"}, + { "id": "artifact", "value": "artifact \"my artifact\" as ar1"}, + { "id": "boundary", "value": "boundary \"my boundary\" as bn1"}, + { "id": "card", "value": "card \"my card\" as cd1"}, + { "id": "circle", "value": "circle \"my circle\" as cr1"}, + { "id": "cloud", "value": "cloud \"my cloud\" as cd1"}, + { "id": "collections", "value": "collections \"my collections\" as cl1"}, + { "id": "component", "value": "component \"my component\" as cp1"}, + { "id": "control", "value": "control \"my control\" as cn1"}, + { "id": "person", "value": "person \"my person\" as pr1"}, + { "id": "queue", "value": "queue \"my queue\" as qq1"}, + { "id": "rectangle", "value": "rectangle \"my rect\\n\" as rc1 {", "additional": [ "}" ] } ], [ - { "id": "database", "v0": "database \"my database\" as db1" }, - { "id": "entity", "v0": "entity \"my entity\" as ee1"}, - { "id": "file", "v0": "file \"my file\" as ff1"}, - { "id": "folder", "v0": "folder \"my folder\" as fl1"}, - { "id": "frame", "v0": "frame \"my frame\" as fr1"}, - { "id": "hexagon", "v0": "hexagon \"my hexagon\" as hx1"}, - { "id": "interface", "v0": "interface \"my interface\" as if1"}, - { "id": "label", "v0": "label \"my label\" as lb1"}, - { "id": "node", "v0": "node \"my node\" as nd1"}, - { "id": "package", "v0": "package \"my package\" as pc1"}, - { "id": "stack", "v0": "stack \"my stack\" as sk1"}, - { "id": "storage", "v0": "storage \"my storage\" as st1"}, - { "id": "usecase", "v0": "usecase \"my usecase\" as uc1"} + { "id": "database", "value": "database \"my database\" as db1" }, + { "id": "entity", "value": "entity \"my entity\" as ee1"}, + { "id": "file", "value": "file \"my file\" as ff1"}, + { "id": "folder", "value": "folder \"my folder\" as fl1"}, + { "id": "frame", "value": "frame \"my frame\" as fr1"}, + { "id": "hexagon", "value": "hexagon \"my hexagon\" as hx1"}, + { "id": "interface", "value": "interface \"my interface\" as if1"}, + { "id": "label", "value": "label \"my label\" as lb1"}, + { "id": "node", "value": "node \"my node\" as nd1"}, + { "id": "package", "value": "package \"my package\" as pc1"}, + { "id": "stack", "value": "stack \"my stack\" as sk1"}, + { "id": "storage", "value": "storage \"my storage\" as st1"}, + { "id": "usecase", "value": "usecase \"my usecase\" as uc1"} ], [ diff --git a/~/Library/Microsoft/PowerAppsCli/usersettings.json b/~/Library/Microsoft/PowerAppsCli/usersettings.json new file mode 100644 index 0000000..d4260c0 --- /dev/null +++ b/~/Library/Microsoft/PowerAppsCli/usersettings.json @@ -0,0 +1 @@ +{"uniqueId":"d67c2bb3-ce99-4e95-becd-f5d782fdaecb","settingVersion":"1.0","telemetryEnabled":true} \ No newline at end of file