From 9f9821a8aca410d36181ad534e7572444532771a Mon Sep 17 00:00:00 2001 From: Mark Goldin <> Date: Thu, 28 Feb 2019 18:07:41 +0700 Subject: [PATCH 1/6] wip on Fix 387 - add "system" color support for SVG parser --- Source/svg/SVGParser.swift | 107 ++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 3d9d80df..36293a6d 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -595,8 +595,111 @@ open class SVGParser { let red = CGFloat((rgbValue >> 16) & 0xff) let green = CGFloat((rgbValue >> 08) & 0xff) let blue = CGFloat((rgbValue >> 00) & 0xff) - - return Color.rgba(r: Int(red), g: Int(green), b: Int(blue), a: opacity) + + switch cleanedHexString { + case "AppWorkspace": + #if os(iOS) + return Color.white + #elseif os(OSX) + return Color.rgba(r: Int(170), g: Int(170), b: Int(170), a: opacity) + #endif + + case "ActiveBorder": + #if os(iOS) + return Color.white + #elseif os(OSX) + return Color.rgba(r: Int(42), g: Int(108), b: Int(205), a: 0.36) + #endif + + case "ActiveCaption": + #if os(iOS) + return Color.rgba(r: Int(204), g: Int(204), b: Int(204), a: opacity) + #elseif os(OSX) + return Color.rgba(r: Int(36), g: Int(36), b: Int(36), a: opacity) + #endif + + case "Background": + return Color.rgba(r: Int(99), g: Int(99), b: Int(206), a: opacity) + + case "ButtonFace": + return Color.silver + + case "ButtonHighlight": + #if os(iOS) + return Color.rgba(r: Int(204), g: Int(204), b: Int(204), a: opacity) + #elseif os(OSX) + return Color.white + #endif + + case "ButtonShadow": + #if os(iOS) + return Color.rgba(r: Int(136), g: Int(136), b: Int(136), a: opacity) + + #elseif os(OSX) + return Color.rgba(r: Int(141), g: Int(141), b: Int(141), a: opacity) + #endif + + case "CaptionText": + return Color.black + + case "HighlightText": + return Color.black + + case "Menu": + #if os(iOS) + return Color.silver + #elseif os(OSX) + return Color.rgba(r: Int(246), g: Int(246), b: Int(246), a: opacity) + #endif + + case "MenuText": + #if os(iOS) + return Color.black + #elseif os(OSX) + return Color.white + #endif + + case "ThreeDFace": + return Color.silver + + case "ThreeDDarkShadow": + #if os(iOS) + return Color.rgba(r: Int(102), g: Int(102), b: Int(102), a: opacity) + #elseif os(OSX) + return Color.black + #endif + + case "ThreeDLightShadow": + #if os(iOS) + return Color.silver + #elseif os(OSX) + return Color.white + #endif + + case "Window": + #if os(iOS) + return Color.white + #elseif os(OSX) + return Color.rgba(r: Int(236), g: Int(236), b: Int(236), a: opacity) + #endif + + case "WindowFrame": + #if os(iOS) + return Color.rgba(r: Int(204), g: Int(204), b: Int(204), a: opacity) + #elseif os(OSX) + return Color.rgba(r: Int(170), g: Int(170), b: Int(170), a: opacity) + #endif + + case "WindowText": + #if os(iOS) + return Color.black + #elseif os(OSX) + return Color.rgba(r: Int(36), g: Int(36), b: Int(36), a: opacity) + #endif + + default: + return Color.rgba(r: Int(red), g: Int(green), b: Int(blue), a: opacity) + } } fileprivate func createColor(_ colorString: String, opacity: Double = 1) -> Color? { From 9da0e017b74be66ff638c06df7a23498042215b3 Mon Sep 17 00:00:00 2001 From: Mark Goldin Date: Mon, 11 Mar 2019 00:46:12 +0700 Subject: [PATCH 2/6] Fix #387: refactor "system" color support for SVG parser --- Source/svg/SVGConstants.swift | 44 +++++++++++++- Source/svg/SVGParser.swift | 109 ++-------------------------------- 2 files changed, 48 insertions(+), 105 deletions(-) diff --git a/Source/svg/SVGConstants.swift b/Source/svg/SVGConstants.swift index 7c1991ae..4ebe0822 100644 --- a/Source/svg/SVGConstants.swift +++ b/Source/svg/SVGConstants.swift @@ -292,7 +292,49 @@ open class SVGConstants { "yellow": 0xffff00, "yellowgreen": 0x9acd32 ] - + + #if os(iOS) + public static let systemColorList = [ + "AppWorkspace": 0xffffff, + "ActiveBorder": 0xffffff, + "ActiveCaption": 0xcccccc, + "Background": 0x6363ce, + "ButtonFace": 0xc0c0c0, + "ButtonHighlight": 0xcccccc, + "ButtonShadow": 0x888888, + "CaptionText": 0x000000, + "HighlightText": 0x000000, + "Menu": 0xc0c0c0, + "MenuText": 0x000000, + "ThreeDFace": 0xc0c0c0, + "ThreeDDarkShadow": 0x666666, + "ThreeDLightShadow": 0xc0c0c0, + "Window": 0xffffff, + "WindowFrame": 0xcccccc, + "WindowText": 0x000000 + ] + #elseif os(OSX) + public static let systemColorList = [ + "AppWorkspace": 0xaaaaaa, + "ActiveBorder": 0x992a6ccd, + "ActiveCaption": 0x242424, + "Background": 0x6363ce, + "ButtonFace": 0xc0c0c0, + "ButtonHighlight": 0xffffff, + "ButtonShadow": 0x8d8d8d, + "CaptionText": 0x000000, + "HighlightText": 0x000000, + "Menu": 0xf6f6f6, + "MenuText": 0xffffff, + "ThreeDFace": 0xc0c0c0, + "ThreeDDarkShadow": 0x000000, + "ThreeDLightShadow": 0xffffff, + "Window": 0xececec, + "WindowFrame": 0xaaaaaa, + "WindowText": 0x242424 + ] + #endif + public static func valueToColor(_ color: Int) -> String? { return SVGConstants.colorList.filter { _, v -> Bool in v == color }.map { k, _ -> String in k }.first } diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index 36293a6d..f3ce658d 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -596,110 +596,7 @@ open class SVGParser { let green = CGFloat((rgbValue >> 08) & 0xff) let blue = CGFloat((rgbValue >> 00) & 0xff) - switch cleanedHexString { - case "AppWorkspace": - #if os(iOS) - return Color.white - #elseif os(OSX) - return Color.rgba(r: Int(170), g: Int(170), b: Int(170), a: opacity) - #endif - - case "ActiveBorder": - #if os(iOS) - return Color.white - #elseif os(OSX) - return Color.rgba(r: Int(42), g: Int(108), b: Int(205), a: 0.36) - #endif - - case "ActiveCaption": - #if os(iOS) - return Color.rgba(r: Int(204), g: Int(204), b: Int(204), a: opacity) - #elseif os(OSX) - return Color.rgba(r: Int(36), g: Int(36), b: Int(36), a: opacity) - #endif - - case "Background": - return Color.rgba(r: Int(99), g: Int(99), b: Int(206), a: opacity) - - case "ButtonFace": - return Color.silver - - case "ButtonHighlight": - #if os(iOS) - return Color.rgba(r: Int(204), g: Int(204), b: Int(204), a: opacity) - #elseif os(OSX) - return Color.white - #endif - - case "ButtonShadow": - #if os(iOS) - return Color.rgba(r: Int(136), g: Int(136), b: Int(136), a: opacity) - - #elseif os(OSX) - return Color.rgba(r: Int(141), g: Int(141), b: Int(141), a: opacity) - #endif - - case "CaptionText": - return Color.black - - case "HighlightText": - return Color.black - - case "Menu": - #if os(iOS) - return Color.silver - #elseif os(OSX) - return Color.rgba(r: Int(246), g: Int(246), b: Int(246), a: opacity) - #endif - - case "MenuText": - #if os(iOS) - return Color.black - #elseif os(OSX) - return Color.white - #endif - - case "ThreeDFace": - return Color.silver - - case "ThreeDDarkShadow": - #if os(iOS) - return Color.rgba(r: Int(102), g: Int(102), b: Int(102), a: opacity) - #elseif os(OSX) - return Color.black - #endif - - case "ThreeDLightShadow": - #if os(iOS) - return Color.silver - #elseif os(OSX) - return Color.white - #endif - - case "Window": - #if os(iOS) - return Color.white - #elseif os(OSX) - return Color.rgba(r: Int(236), g: Int(236), b: Int(236), a: opacity) - #endif - - case "WindowFrame": - #if os(iOS) - return Color.rgba(r: Int(204), g: Int(204), b: Int(204), a: opacity) - #elseif os(OSX) - return Color.rgba(r: Int(170), g: Int(170), b: Int(170), a: opacity) - #endif - - case "WindowText": - #if os(iOS) - return Color.black - #elseif os(OSX) - return Color.rgba(r: Int(36), g: Int(36), b: Int(36), a: opacity) - #endif - - default: - return Color.rgba(r: Int(red), g: Int(green), b: Int(blue), a: opacity) - } + return Color.rgba(r: Int(red), g: Int(green), b: Int(blue), a: opacity) } fileprivate func createColor(_ colorString: String, opacity: Double = 1) -> Color? { @@ -711,6 +608,10 @@ open class SVGParser { let color = Color(val: defaultColor) return opacity != 1 ? color.with(a: opacity) : color } + if let systemColor = SVGConstants.systemColorList[colorString] { + let color = Color(val: systemColor) + return color + } if colorString.hasPrefix("rgb") { let color = parseRGBNotation(colorString: colorString) return opacity != 1 ? color.with(a: opacity) : color From f2f9edabbd4671fe182e59c8af29eb4c87cf040f Mon Sep 17 00:00:00 2001 From: Mark Goldin Date: Fri, 5 Apr 2019 13:06:32 +0700 Subject: [PATCH 3/6] Fix #387: add color-prop-04-t-manual svg & reference files and testColorProp04 to MacawSVGTests --- Macaw.xcodeproj/project.pbxproj | 22 +- MacawTests/MacawSVGTests.swift | 4 + .../color-prop-04-t-manual.reference | 642 ++++++++++++++++++ .../w3cSVGTests/color-prop-04-t-manual.svg | 96 +++ 4 files changed, 757 insertions(+), 7 deletions(-) create mode 100644 MacawTests/w3cSVGTests/color-prop-04-t-manual.reference create mode 100644 MacawTests/w3cSVGTests/color-prop-04-t-manual.svg diff --git a/Macaw.xcodeproj/project.pbxproj b/Macaw.xcodeproj/project.pbxproj index 8779046e..219c8198 100644 --- a/Macaw.xcodeproj/project.pbxproj +++ b/Macaw.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ 30FF496D215CF27E00FF653C /* MCAShapeLayerLineCap_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FF496C215CF27E00FF653C /* MCAShapeLayerLineCap_macOS.swift */; }; 30FF496F215CF3B000FF653C /* MCAMediaTimingFunctionName_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FF496E215CF3B000FF653C /* MCAMediaTimingFunctionName_iOS.swift */; }; 30FF4971215CF4CE00FF653C /* MCAMediaTimingFunctionName_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FF4970215CF4CE00FF653C /* MCAMediaTimingFunctionName_macOS.swift */; }; + 421C66502225196900DD73F5 /* color-prop-04-t-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 421C664F2225196900DD73F5 /* color-prop-04-t-manual.svg */; }; + 4269F43E2257265800D91393 /* color-prop-04-t-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 4269F43D2257265700D91393 /* color-prop-04-t-manual.reference */; }; 5713C4E21E51EC8F00BBA4D9 /* TouchEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5713C4E11E51EC8F00BBA4D9 /* TouchEvent.swift */; }; 5713C4F31E5AD46800BBA4D9 /* ControlStatesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5713C4F21E5AD46800BBA4D9 /* ControlStatesTests.swift */; }; 5713C4F51E5AE2C300BBA4D9 /* CombineAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5713C4F41E5AE2C300BBA4D9 /* CombineAnimationTests.swift */; }; @@ -460,14 +462,14 @@ 5B6E194420AC58F900454E7E /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6E192020AC58F900454E7E /* Gradient.swift */; }; 5B7D7ED321300D4A00B5ED00 /* TimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */; }; 5B7D7ED421300D4A00B5ED00 /* TimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */; }; - 5B7E79CE20CBE69700C50BCF /* masking-path-02-b-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79CC20CBE69600C50BCF /* masking-path-02-b-manual.reference */; }; - 5B7E79CF20CBE69700C50BCF /* masking-path-02-b-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79CD20CBE69700C50BCF /* masking-path-02-b-manual.svg */; }; - 5B7E79DE20D2781A00C50BCF /* masking-intro-01-f-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79DC20D2781A00C50BCF /* masking-intro-01-f-manual.reference */; }; - 5B7E79DF20D2781A00C50BCF /* masking-intro-01-f-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79DD20D2781A00C50BCF /* masking-intro-01-f-manual.svg */; }; 5B7E79C020CA7E9300C50BCF /* Pattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7E79BF20CA7E9300C50BCF /* Pattern.swift */; }; 5B7E79C120CA7E9300C50BCF /* Pattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7E79BF20CA7E9300C50BCF /* Pattern.swift */; }; 5B7E79C420CA7F1B00C50BCF /* pservers-grad-03-b-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79C220CA7F1A00C50BCF /* pservers-grad-03-b-manual.svg */; }; 5B7E79C520CA7F1B00C50BCF /* pservers-grad-03-b-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79C320CA7F1B00C50BCF /* pservers-grad-03-b-manual.reference */; }; + 5B7E79CE20CBE69700C50BCF /* masking-path-02-b-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79CC20CBE69600C50BCF /* masking-path-02-b-manual.reference */; }; + 5B7E79CF20CBE69700C50BCF /* masking-path-02-b-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79CD20CBE69700C50BCF /* masking-path-02-b-manual.svg */; }; + 5B7E79DE20D2781A00C50BCF /* masking-intro-01-f-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79DC20D2781A00C50BCF /* masking-intro-01-f-manual.reference */; }; + 5B7E79DF20D2781A00C50BCF /* masking-intro-01-f-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79DD20D2781A00C50BCF /* masking-intro-01-f-manual.svg */; }; 5BAE201F208E1211006BF277 /* SVGCanvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAE201E208E1211006BF277 /* SVGCanvas.swift */; }; 5BAE2038208E163D006BF277 /* polyline.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5BAE2022208E1637006BF277 /* polyline.reference */; }; 5BAE2039208E163D006BF277 /* polygon.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5BAE2023208E1637006BF277 /* polygon.reference */; }; @@ -557,6 +559,8 @@ 30FF496C215CF27E00FF653C /* MCAShapeLayerLineCap_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCAShapeLayerLineCap_macOS.swift; sourceTree = ""; }; 30FF496E215CF3B000FF653C /* MCAMediaTimingFunctionName_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCAMediaTimingFunctionName_iOS.swift; sourceTree = ""; }; 30FF4970215CF4CE00FF653C /* MCAMediaTimingFunctionName_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCAMediaTimingFunctionName_macOS.swift; sourceTree = ""; }; + 421C664F2225196900DD73F5 /* color-prop-04-t-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual.svg"; sourceTree = ""; }; + 4269F43D2257265700D91393 /* color-prop-04-t-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "color-prop-04-t-manual.reference"; sourceTree = ""; }; 5713C4E11E51EC8F00BBA4D9 /* TouchEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchEvent.swift; sourceTree = ""; }; 5713C4F21E5AD46800BBA4D9 /* ControlStatesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlStatesTests.swift; sourceTree = ""; }; 5713C4F41E5AE2C300BBA4D9 /* CombineAnimationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineAnimationTests.swift; sourceTree = ""; }; @@ -878,13 +882,13 @@ 5B6E191F20AC58F900454E7E /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; 5B6E192020AC58F900454E7E /* Gradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gradient.swift; sourceTree = ""; }; 5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingFunction.swift; sourceTree = ""; }; + 5B7E79BF20CA7E9300C50BCF /* Pattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pattern.swift; sourceTree = ""; }; + 5B7E79C220CA7F1A00C50BCF /* pservers-grad-03-b-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "pservers-grad-03-b-manual.svg"; sourceTree = ""; }; + 5B7E79C320CA7F1B00C50BCF /* pservers-grad-03-b-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "pservers-grad-03-b-manual.reference"; sourceTree = ""; }; 5B7E79CC20CBE69600C50BCF /* masking-path-02-b-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-path-02-b-manual.reference"; sourceTree = ""; }; 5B7E79CD20CBE69700C50BCF /* masking-path-02-b-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-path-02-b-manual.svg"; sourceTree = ""; }; 5B7E79DC20D2781A00C50BCF /* masking-intro-01-f-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-intro-01-f-manual.reference"; sourceTree = ""; }; 5B7E79DD20D2781A00C50BCF /* masking-intro-01-f-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-intro-01-f-manual.svg"; sourceTree = ""; }; - 5B7E79BF20CA7E9300C50BCF /* Pattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pattern.swift; sourceTree = ""; }; - 5B7E79C220CA7F1A00C50BCF /* pservers-grad-03-b-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "pservers-grad-03-b-manual.svg"; sourceTree = ""; }; - 5B7E79C320CA7F1B00C50BCF /* pservers-grad-03-b-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "pservers-grad-03-b-manual.reference"; sourceTree = ""; }; 5BAE201E208E1211006BF277 /* SVGCanvas.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGCanvas.swift; sourceTree = ""; }; 5BAE2022208E1637006BF277 /* polyline.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = polyline.reference; sourceTree = ""; }; 5BAE2023208E1637006BF277 /* polygon.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = polygon.reference; sourceTree = ""; }; @@ -1360,6 +1364,8 @@ 5B1AE18320B6A669007EECCB /* w3cSVGTests */ = { isa = PBXGroup; children = ( + 421C664F2225196900DD73F5 /* color-prop-04-t-manual.svg */, + 4269F43D2257265700D91393 /* color-prop-04-t-manual.reference */, 5B1AE22D20B6A669007EECCB /* color-prop-01-b-manual.reference */, 5B1AE18A20B6A669007EECCB /* color-prop-01-b-manual.svg */, 5B1AE22C20B6A669007EECCB /* color-prop-02-f-manual.reference */, @@ -1768,6 +1774,7 @@ 5B1AE26920B6A669007EECCB /* struct-use-03-t-manual.reference in Resources */, 5B1AE27820B6A669007EECCB /* struct-group-01-t-manual.reference in Resources */, 5B1AE2B820B6A669007EECCB /* painting-stroke-03-t-manual.reference in Resources */, + 4269F43E2257265800D91393 /* color-prop-04-t-manual.reference in Resources */, 5B1AE2C720B6A669007EECCB /* paths-data-02-t-manual.svg in Resources */, 5B1AE27120B6A669007EECCB /* painting-stroke-04-t-manual.reference in Resources */, 5B1AE2C220B6A669007EECCB /* coords-trans-05-t-manual.svg in Resources */, @@ -1854,6 +1861,7 @@ 5B1AE23C20B6A669007EECCB /* paths-data-15-t-manual.svg in Resources */, 5BAE2039208E163D006BF277 /* polygon.reference in Resources */, 5B1AE27220B6A669007EECCB /* struct-frag-06-t-manual.svg in Resources */, + 421C66502225196900DD73F5 /* color-prop-04-t-manual.svg in Resources */, 5B1AE2A020B6A669007EECCB /* coords-trans-08-t-manual.svg in Resources */, 5B1AE23A20B6A669007EECCB /* painting-fill-03-t-manual.reference in Resources */, 5BAE2043208E163D006BF277 /* textBasicTransform.reference in Resources */, diff --git a/MacawTests/MacawSVGTests.swift b/MacawTests/MacawSVGTests.swift index af5ec5f9..c3bc656b 100644 --- a/MacawTests/MacawSVGTests.swift +++ b/MacawTests/MacawSVGTests.swift @@ -485,6 +485,10 @@ class MacawSVGTests: XCTestCase { validateJSON("color-prop-03-t-manual") } + func testColorProp04() { + validateJSON("color-prop-04-t-manual") + } + func testTypesBasic01() { validateJSON("types-basic-01-f-manual") } diff --git a/MacawTests/w3cSVGTests/color-prop-04-t-manual.reference b/MacawTests/w3cSVGTests/color-prop-04-t-manual.reference new file mode 100644 index 00000000..e29b0b97 --- /dev/null +++ b/MacawTests/w3cSVGTests/color-prop-04-t-manual.reference @@ -0,0 +1,642 @@ +{ + "contents" : [ + { + "contents" : [ + { + "contents" : [ + { + "fill" : { + "type" : "Color", + "val" : 6513614 + }, + "form" : { + "h" : 300, + "type" : "Rect", + "w" : 460, + "x" : -230, + "y" : -170 + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 16777215 + }, + "form" : { + "h" : 280, + "type" : "Rect", + "w" : 440, + "x" : -220, + "y" : -160 + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 16777215 + }, + "form" : { + "h" : 221, + "type" : "Rect", + "w" : 317, + "x" : -152, + "y" : -108 + }, + "node" : "Shape" + }, + { + "form" : { + "h" : 221, + "type" : "Rect", + "w" : 317, + "x" : -152, + "y" : -108 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 13421772 + }, + "join" : "miter", + "width" : 3 + } + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 12, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -148, 0", + "text" : "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. " + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 12, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -148, 20", + "text" : "Vestibulum pulvinar. Duis laoreet, nunc vitae facilisis " + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 12, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -148, 40", + "text" : "tristique, pede sem iaculis mi, non consectetuer lorem " + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 12, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -148, 60", + "text" : "libero et est. Donec imperdiet purus sed odio. Duis " + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 12, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -148, 80", + "text" : "venenatis tortor eu lectus. Suspendisse sed metus at " + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 12, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -148, 100", + "text" : "metus viverra ultricies. Mauris porttitor, justo a vulputate" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "form" : { + "h" : 190, + "type" : "Rect", + "w" : 118, + "x" : -150, + "y" : -107 + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "form" : { + "h" : 34, + "type" : "Rect", + "w" : 102, + "x" : -143, + "y" : 0 + }, + "node" : "Shape" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 20, + "weight" : "bold" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -138, 24", + "text" : "Load" + }, + { + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "form" : { + "h" : 34, + "type" : "Rect", + "w" : 102, + "x" : -143, + "y" : 40 + }, + "node" : "Shape" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 20, + "weight" : "bold" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -138, 64", + "text" : "Save" + }, + { + "form" : { + "segments" : [ + { + "data" : [ + -149, + 83 + ], + "type" : "M" + }, + { + "data" : [ + 114 + ], + "type" : "h" + }, + { + "data" : [ + -94 + ], + "type" : "v" + } + ], + "type" : "Path" + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 6710886 + }, + "join" : "miter", + "width" : 4 + } + }, + { + "form" : { + "segments" : [ + { + "data" : [ + -149, + 83 + ], + "type" : "M" + }, + { + "data" : [ + -94 + ], + "type" : "v" + }, + { + "data" : [ + 114 + ], + "type" : "h" + } + ], + "type" : "Path" + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "join" : "miter", + "width" : 4 + } + } + ], + "node" : "Group", + "place" : "1, 0, 0, 1, 2, 0" + }, + { + "contents" : [ + { + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "form" : { + "h" : 46, + "type" : "Rect", + "w" : 310, + "x" : -148, + "y" : -62 + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 6710886 + }, + "form" : { + "segments" : [ + { + "data" : [ + 152, + -52 + ], + "type" : "M" + }, + { + "data" : [ + 10, + -10 + ], + "type" : "l" + }, + { + "data" : [ + 46 + ], + "type" : "v" + }, + { + "data" : [ + -311 + ], + "type" : "h" + }, + { + "data" : [ + 10, + -10 + ], + "type" : "l" + }, + { + "data" : [ + + ], + "type" : "z" + } + ], + "type" : "Path" + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "form" : { + "h" : 37, + "type" : "Rect", + "w" : 302, + "x" : -144, + "y" : -58 + }, + "node" : "Shape" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 20, + "weight" : "bold" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -141, -32", + "text" : "File" + }, + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 20, + "weight" : "bold" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, -90, -32", + "text" : "Edit" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "fill" : { + "type" : "Color", + "val" : 13421772 + }, + "form" : { + "h" : 42, + "type" : "Rect", + "w" : 311, + "x" : -149, + "y" : -106 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 16777215 + }, + "join" : "miter", + "width" : 4 + } + }, + { + "align" : "mid", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 24, + "weight" : "bold" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 5, -78", + "text" : "Lorem" + }, + { + "contents" : [ + { + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "form" : { + "rect" : { + "h" : 26, + "type" : "Rect", + "w" : 26, + "x" : 120, + "y" : -99 + }, + "rx" : 8, + "ry" : 8, + "type" : "RoundRect" + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 13421772 + }, + "form" : { + "rect" : { + "h" : 24, + "type" : "Rect", + "w" : 24, + "x" : 120, + "y" : -99 + }, + "rx" : 8, + "ry" : 8, + "type" : "RoundRect" + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 8947848 + }, + "form" : { + "rect" : { + "h" : 24, + "type" : "Rect", + "w" : 24, + "x" : 122, + "y" : -97 + }, + "rx" : 8, + "ry" : 8, + "type" : "RoundRect" + }, + "node" : "Shape" + }, + { + "fill" : { + "type" : "Color", + "val" : 12632256 + }, + "form" : { + "rect" : { + "h" : 22, + "type" : "Rect", + "w" : 22, + "x" : 122, + "y" : -97 + }, + "rx" : 8, + "ry" : 8, + "type" : "RoundRect" + }, + "node" : "Shape" + } + ], + "node" : "Group" + } + ], + "node" : "Group" + } + ], + "node" : "Group", + "place" : "1, 0, 0, 1, 240, 180" + } + ], + "node" : "Group" + }, + { + "contents" : [ + { + "align" : "min", + "baseline" : "bottom", + "fill" : { + "type" : "Color", + "val" : 0 + }, + "font" : { + "name" : "SVGFreeSansASCII,sans-serif", + "size" : 32, + "weight" : "normal" + }, + "node" : "Text", + "place" : "1, 0, 0, 1, 10, 340", + "text" : "$Revision: 1.7 $" + } + ], + "node" : "Group" + }, + { + "form" : { + "h" : 358, + "type" : "Rect", + "w" : 478, + "x" : 1, + "y" : 1 + }, + "node" : "Shape", + "stroke" : { + "cap" : "butt", + "dashes" : [ + + ], + "fill" : { + "type" : "Color", + "val" : 0 + }, + "join" : "miter", + "width" : 1 + } + } + ], + "layout" : { + "scalingMode" : "meet", + "svgSize" : { + "height" : "100.0%", + "width" : "100.0%" + }, + "viewBox" : { + "h" : 360, + "type" : "Rect", + "w" : 480, + "x" : 0, + "y" : 0 + }, + "xAligningMode" : "mid", + "yAligningMode" : "mid" + }, + "node" : "Canvas" +} \ No newline at end of file diff --git a/MacawTests/w3cSVGTests/color-prop-04-t-manual.svg b/MacawTests/w3cSVGTests/color-prop-04-t-manual.svg new file mode 100644 index 00000000..34521761 --- /dev/null +++ b/MacawTests/w3cSVGTests/color-prop-04-t-manual.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + +

+ This tests the 'system' colors. +

+ + +

Run the test. No interaction required.

+
+ +

+ This test has no specific pass criteria, except that no error must be indicated. +

+

+ The colors on your screen might not match the reference + image at all, but they should at minimum be legible and should + preferably resemble the colors used on menus and other user interface + elements on your computer, pda or phone. +

+
+ + $RCSfile: color-prop-04-t.svg,v $ + + + + + + + + + + + + + + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + Vestibulum pulvinar. Duis laoreet, nunc vitae facilisis + tristique, pede sem iaculis mi, non consectetuer lorem + libero et est. Donec imperdiet purus sed odio. Duis + venenatis tortor eu lectus. Suspendisse sed metus at + metus viverra ultricies. Mauris porttitor, justo a vulputate + + + + + Load + + Save + + + + + + + + File + Edit + + + + Lorem + + + + + + + + + + + $Revision: 1.7 $ + + + + + From 6f1d0b102b56add16cb54df3f6c07c37e524dbbd Mon Sep 17 00:00:00 2001 From: Mark Goldin Date: Mon, 8 Apr 2019 16:30:36 +0700 Subject: [PATCH 4/6] Fix #387: use opacity for system colors --- Source/svg/SVGParser.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index f3ce658d..d92b6ead 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -610,7 +610,7 @@ open class SVGParser { } if let systemColor = SVGConstants.systemColorList[colorString] { let color = Color(val: systemColor) - return color + return opacity != 1 ? color.with(a: opacity) : color } if colorString.hasPrefix("rgb") { let color = parseRGBNotation(colorString: colorString) From 84602c48a0f37ccb2dcff63a604d0870377a2d20 Mon Sep 17 00:00:00 2001 From: Yuri Strot Date: Mon, 8 Apr 2019 17:29:41 +0700 Subject: [PATCH 5/6] WIP #561: Built-in zoom support --- Example/Example.xcodeproj/project.pbxproj | 10 +- Macaw.xcodeproj/project.pbxproj | 6 + Source/animation/AnimationProducer.swift | 4 +- Source/model/geom2d/Arc.swift | 2 +- Source/model/geom2d/Point.swift | 29 ++-- Source/model/geom2d/Size.swift | 20 ++- Source/model/geom2d/Transform.swift | 9 +- Source/views/MacawView.swift | 89 ++++++++++-- Source/views/MacawZoom.swift | 162 ++++++++++++++++++++++ 9 files changed, 306 insertions(+), 25 deletions(-) create mode 100644 Source/views/MacawZoom.swift diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 5a10c70d..b3207ffd 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -258,8 +258,9 @@ TargetAttributes = { B02E75EC1C16104900D1971D = { CreatedOnToolsVersion = 7.1.1; + DevelopmentTeam = FZXCM5CJ7P; LastSwiftMigration = 1020; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -502,7 +503,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = FZXCM5CJ7P; INFOPLIST_FILE = Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -521,13 +523,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = FZXCM5CJ7P; INFOPLIST_FILE = Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.exyte.Example.Example; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; name = Release; diff --git a/Macaw.xcodeproj/project.pbxproj b/Macaw.xcodeproj/project.pbxproj index a99ff41e..e9df761b 100644 --- a/Macaw.xcodeproj/project.pbxproj +++ b/Macaw.xcodeproj/project.pbxproj @@ -234,6 +234,8 @@ 5852891620B29D67003E51D1 /* TransformedLocus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5852891520B29D67003E51D1 /* TransformedLocus.swift */; }; 5852891720B29D67003E51D1 /* TransformedLocus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5852891520B29D67003E51D1 /* TransformedLocus.swift */; }; 5874CCB720DA8A860090DBD5 /* ColorMatrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5874CCB620DA8A860090DBD5 /* ColorMatrix.swift */; }; + 5876C63222572859000B31B6 /* MacawZoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5876C63122572859000B31B6 /* MacawZoom.swift */; }; + 5876C63322572859000B31B6 /* MacawZoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5876C63122572859000B31B6 /* MacawZoom.swift */; }; 58944BDA20AC8A9A00657640 /* logo_base64.txt in Resources */ = {isa = PBXBuildFile; fileRef = 57B7A4E01EE70DA5009D78D7 /* logo_base64.txt */; }; 58944BDB20AC8A9A00657640 /* clip.svg in Resources */ = {isa = PBXBuildFile; fileRef = C43B064C1F9738EF00787A35 /* clip.svg */; }; 58B0523920E10E7100D45008 /* ColorMatrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5874CCB620DA8A860090DBD5 /* ColorMatrix.swift */; }; @@ -676,6 +678,7 @@ 585288F320AD96A2003E51D1 /* ContentLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentLayout.swift; sourceTree = ""; }; 5852891520B29D67003E51D1 /* TransformedLocus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransformedLocus.swift; sourceTree = ""; }; 5874CCB620DA8A860090DBD5 /* ColorMatrix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorMatrix.swift; sourceTree = ""; }; + 5876C63122572859000B31B6 /* MacawZoom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacawZoom.swift; sourceTree = ""; }; 5B1A8C7520A15F7300E5FFAE /* SVGNodeLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVGNodeLayout.swift; sourceTree = ""; }; 5B1AE18420B6A669007EECCB /* text-align-01-b-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "text-align-01-b-manual.svg"; sourceTree = ""; }; 5B1AE18520B6A669007EECCB /* paths-data-06-t-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "paths-data-06-t-manual.reference"; sourceTree = ""; }; @@ -1295,6 +1298,7 @@ 57F108731F502A3600DC365B /* Touchable.swift */, 57E5E1501E3B393900D1CB28 /* MacawView.swift */, 57E5E1521E3B393900D1CB28 /* ShapeLayer.swift */, + 5876C63122572859000B31B6 /* MacawZoom.swift */, ); path = views; sourceTree = ""; @@ -2082,6 +2086,7 @@ 57614B3A1F83D15600875933 /* AnimationProducer.swift in Sources */, 585288F620AD96A2003E51D1 /* ContentLayout.swift in Sources */, 57614B3C1F83D15600875933 /* ShapeInterpolation.swift in Sources */, + 5876C63322572859000B31B6 /* MacawZoom.swift in Sources */, 57614B3D1F83D15600875933 /* Graphics_iOS.swift in Sources */, 57614BDB1F8739EE00875933 /* MacawView+PDF.swift in Sources */, 57614B411F83D15600875933 /* Text.swift in Sources */, @@ -2222,6 +2227,7 @@ 57A27BD51E44C5840057BD3A /* ShapeInterpolation.swift in Sources */, A718CD471F45C28700966E06 /* Graphics_iOS.swift in Sources */, 57614BDA1F8739EE00875933 /* MacawView+PDF.swift in Sources */, + 5876C63222572859000B31B6 /* MacawZoom.swift in Sources */, 57E5E1A21E3B393900D1CB28 /* Text.swift in Sources */, 57F1087C1F53CA7E00DC365B /* MDisplayLink_iOS.swift in Sources */, 57E5E1A61E3B393900D1CB28 /* RenderContext.swift in Sources */, diff --git a/Source/animation/AnimationProducer.swift b/Source/animation/AnimationProducer.swift index 244aac25..8ecac9a9 100644 --- a/Source/animation/AnimationProducer.swift +++ b/Source/animation/AnimationProducer.swift @@ -384,8 +384,8 @@ class AnimationContext { func getLayoutTransform(_ renderer: NodeRenderer?) -> Transform { if rootTransform == nil { - if let view = renderer?.view, let node = view.renderer?.node() { - rootTransform = LayoutHelper.calcTransform(node, view.contentLayout, view.bounds.size.toMacaw()) + if let view = renderer?.view { + rootTransform = view.place } } return rootTransform ?? Transform.identity diff --git a/Source/model/geom2d/Arc.swift b/Source/model/geom2d/Arc.swift index 5c728df5..c94035c9 100644 --- a/Source/model/geom2d/Arc.swift +++ b/Source/model/geom2d/Arc.swift @@ -1,4 +1,4 @@ -import Darwin +import Foundation open class Arc: Locus { diff --git a/Source/model/geom2d/Point.swift b/Source/model/geom2d/Point.swift index 97f3125f..cac4f508 100644 --- a/Source/model/geom2d/Point.swift +++ b/Source/model/geom2d/Point.swift @@ -1,3 +1,5 @@ +import Foundation + open class Point: Locus { public let x: Double @@ -11,24 +13,35 @@ open class Point: Locus { } override open func bounds() -> Rect { - return Rect( - x: x, - y: y, - w: 0.0, - h: 0.0) + return Rect(x: x, y: y, w: 0.0, h: 0.0) } open func add(_ point: Point) -> Point { - return Point( - x: self.x + point.x, - y: self.y + point.y) + return Point( x: x + point.x, y: y + point.y) } open func rect(size: Size) -> Rect { return Rect(point: self, size: size) } + open func distance(to point: Point) -> Double { + let dx = point.x - x + let dy = point.y - y + return sqrt(dx * dx + dy * dy) + } + override open func toPath() -> Path { return MoveTo(x: x, y: y).lineTo(x: x, y: y).build() } } + +extension Point: Equatable { + public static func == (lhs: Point, rhs: Point) -> Bool { + return lhs.x == rhs.x + && lhs.y == rhs.y + } + + public static func - (lhs: Point, rhs: Point) -> Size { + return Size(w: lhs.x - rhs.x, h: lhs.y - rhs.y) + } +} diff --git a/Source/model/geom2d/Size.swift b/Source/model/geom2d/Size.swift index 12df24c7..b4a09805 100644 --- a/Source/model/geom2d/Size.swift +++ b/Source/model/geom2d/Size.swift @@ -1,3 +1,5 @@ +import Foundation + open class Size { public let w: Double @@ -13,11 +15,25 @@ open class Size { open func rect(at point: Point = Point.origin) -> Rect { return Rect(point: point, size: self) } + + open func angle() -> Double { + return atan2(h, w) + } + } extension Size { + public static func == (lhs: Size, rhs: Size) -> Bool { - return lhs.w == rhs.w - && lhs.h == rhs.h + return lhs.w == rhs.w && lhs.h == rhs.h } + + public static func + (lhs: Size, rhs: Size) -> Size { + return Size(w: lhs.w + rhs.w, h: lhs.h + rhs.h) + } + + public static func - (lhs: Size, rhs: Size) -> Size { + return Size(w: lhs.w - rhs.w, h: lhs.h - rhs.h) + } + } diff --git a/Source/model/geom2d/Transform.swift b/Source/model/geom2d/Transform.swift index 808822ad..8f731b6c 100644 --- a/Source/model/geom2d/Transform.swift +++ b/Source/model/geom2d/Transform.swift @@ -75,12 +75,19 @@ public final class Transform { return Transform(m11: nm11, m12: nm12, m21: nm21, m22: nm22, dx: ndx, dy: ndy) } + public func apply(to: Point) -> Point { + let x2 = m11 * to.x + m12 * to.x + dx + let y2 = m21 * to.y + m22 * to.y + dy + return Point(x: x2, y: y2) + } + public func invert() -> Transform? { let det = self.m11 * self.m22 - self.m12 * self.m21 if det == 0 { return nil } - return Transform(m11: m22 / det, m12: -m12 / det, m21: -m21 / det, m22: m11 / det, + return Transform(m11: m22 / det, m12: -m12 / det, + m21: -m21 / det, m22: m11 / det, dx: (m21 * dy - m22 * dx) / det, dy: (m12 * dx - m11 * dy) / det) } diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 53061043..01823fc0 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -5,6 +5,7 @@ import UIKit #elseif os(OSX) import AppKit #endif + /// /// MacawView is a main class used to embed Macaw scene into your Cocoa UI. /// You could create your own view extended from MacawView with predefined scene. @@ -43,6 +44,18 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } + public let zoom = MacawZoom() + + public var place: Transform { + return placeManager.placeVar.value + } + + public var placeVar: Variable { + return placeManager.placeVar + } + + private let placeManager = RootPlaceManager() + override open var frame: CGRect { didSet { super.frame = frame @@ -106,6 +119,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { @objc public init?(node: Node, coder aDecoder: NSCoder) { super.init(coder: aDecoder) + zoom.initialize(view: self, onChange: onZoomChange) initializeView() @@ -128,6 +142,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { public override init(frame: CGRect) { super.init(frame: frame) + zoom.initialize(view: self, onChange: onZoomChange) initializeView() } @@ -136,6 +151,11 @@ open class MacawView: MView, MGestureRecognizerDelegate { self.init(node: Group(), coder: aDecoder) } + private func onZoomChange(t: Transform) { + placeManager.setZoom(place: t) + self.setNeedsDisplay() + } + func initializeView() { self.contentLayout = .none self.context = RenderContext(view: self) @@ -191,7 +211,11 @@ open class MacawView: MView, MGestureRecognizerDelegate { return } renderer.calculateZPositionRecursively() - ctx.concatenate(layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw())) + + // TODO: actually we should track all changes + placeManager.setLayout(place: layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw())) + + ctx.concatenate(self.place.toCG()) renderer.render(in: ctx, force: false, opacity: node.opacity) } @@ -210,7 +234,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { defer { ctx.restoreGState() } - let transform = layoutHelper.getTransform(renderer, contentLayout, bounds.size.toMacaw()) + let transform = place.toCG() ctx.concatenate(transform) let loc = location.applying(transform.inverted()) return renderer.findNodeAt(parentNodePath: NodePath(node: Node(), location: loc), ctx: ctx) @@ -225,10 +249,29 @@ open class MacawView: MView, MGestureRecognizerDelegate { return .none } + open override func touchesBegan(_ touches: Set, with event: MEvent?) { + super.touchesBegan(touches, with: event) + zoom.touchesBegan(touches) + } + + open override func touchesMoved(_ touches: Set, with event: MEvent?) { + super.touchesMoved(touches, with: event) + zoom.touchesMoved(touches) + } + + open override func touchesEnded(_ touches: Set, with event: MEvent?) { + super.touchesEnded(touches, with: event) + zoom.touchesEnded(touches) + } + + open override func touchesCancelled(_ touches: Set, with event: MEvent?) { + super.touchesCancelled(touches, with: event) + zoom.touchesEnded(touches) + } + // MARK: - Touches override func mTouchesBegan(_ touches: [MTouchEvent]) { - if !self.node.shouldCheckForPressed() && !self.node.shouldCheckForMoved() && !self.node.shouldCheckForReleased () { @@ -547,9 +590,9 @@ class LayoutHelper { private var prevSize: Size? private var prevRect: Rect? - private var prevTransform: CGAffineTransform? + private var prevTransform: Transform? - public func getTransform(_ nodeRenderer: NodeRenderer, _ layout: ContentLayout, _ size: Size) -> CGAffineTransform { + public func getTransform(_ nodeRenderer: NodeRenderer, _ layout: ContentLayout, _ size: Size) -> Transform { setSize(size: size) let node = nodeRenderer.node() var rect = node?.bounds @@ -566,9 +609,9 @@ class LayoutHelper { if let transform = prevTransform { return transform } - return setTransform(transform: layout.layout(rect: prevRect!, into: size).toCG()) + return setTransform(transform: layout.layout(rect: rect, into: size)) } - return CGAffineTransform.identity + return Transform.identity } public class func calcTransform(_ node: Node, _ layout: ContentLayout, _ size: Size) -> Transform { @@ -624,9 +667,39 @@ class LayoutHelper { prevTransform = nil } - private func setTransform(transform: CGAffineTransform) -> CGAffineTransform { + private func setTransform(transform: Transform) -> Transform { prevTransform = transform return transform } } + +class RootPlaceManager { + + var placeVar = Variable(Transform.identity) + private var places: [Transform] = [.identity, .identity] + + func setLayout(place: Transform) { + if places[1] !== place { + places[1] = place + placeVar.value = recalc() + } + } + + func setZoom(place: Transform) { + if places[0] !== place { + places[0] = place + placeVar.value = recalc() + } + } + + private func recalc() -> Transform { + if places[0] === Transform.identity { + return places[1] + } else if places[1] === Transform.identity { + return places[0] + } + return places[0].concat(with: places[1]) + } + +} diff --git a/Source/views/MacawZoom.swift b/Source/views/MacawZoom.swift new file mode 100644 index 00000000..a420b11e --- /dev/null +++ b/Source/views/MacawZoom.swift @@ -0,0 +1,162 @@ +// +// MacawZoom.swift +// Macaw +// +// Created by Yuri Strot on 4/5/19. +// Copyright © 2019 Exyte. All rights reserved. +// + +import Foundation + +#if os(iOS) +import UIKit +#elseif os(OSX) +import AppKit +#endif + +open class MacawZoom { + + private var view: MView! + private var onChange: ((Transform) -> Void)! + private var touches = [TouchData]() + private var zoomData = ZoomData() + + private var trackMove = false + private var trackScale = false + private var trackRotate = false + + open func enable(move: Bool = true, scale: Bool = true, rotate: Bool = false) { + trackMove = move + trackScale = scale + trackRotate = rotate + if scale || rotate { + view.isMultipleTouchEnabled = true + } + } + + open func disable() { + trackMove = false + trackScale = false + trackRotate = false + } + + open func set(offset: Size? = nil, scale: Double? = nil, angle: Double? = nil) { + let o = offset ?? zoomData.offset + let s = scale ?? zoomData.scale + let a = angle ?? zoomData.angle + zoomData = ZoomData(offset: o, scale: s, angle: a) + onChange(zoomData.transform()) + } + + func initialize(view: MView, onChange: @escaping ((Transform) -> Void)) { + self.view = view + self.onChange = onChange + } + + func touchesBegan(_ touches: Set) { + zoomData = getNewZoom() + self.touches = self.touches.map { TouchData(touch: $0.touch, in: view) } + self.touches.append(contentsOf: touches.map { TouchData(touch: $0, in: view) }) + } + + func touchesMoved(_ touches: Set) { + let zoom = cleanTouches() ?? getNewZoom() + onChange(zoom.transform()) + } + + func touchesEnded(_ touches: Set) { + cleanTouches() + if let touch = touches.first { + if touches.count == 1 && touch.tapCount == 2 && touch.timestamp + 0.3 >= CACurrentMediaTime() { + set(offset: .zero, scale: 1, angle: 0) + } + } + } + + @discardableResult private func cleanTouches() -> ZoomData? { + let newTouches = touches.filter { $0.touch.phase.rawValue < MTouch.Phase.ended.rawValue } + if newTouches.count != touches.count { + zoomData = getNewZoom() + touches = newTouches.map { TouchData(touch: $0.touch, in: view) } + return zoomData + } + return nil + } + + private func getNewZoom() -> ZoomData { + if touches.count == 0 || (touches.count == 1 && !trackMove) { + return zoomData + } + let s1 = touches[0].point + let e1 = touches[0].current(in: view) + if touches.count == 1 { + return zoomData.move(delta: e1 - s1) + } + let s2 = touches[1].point + let e2 = touches[1].current(in: view) + let scale = trackScale ? e1.distance(to: e2) / s1.distance(to: s2) : 1 + let a = trackRotate ? (e1 - e2).angle() - (s1 - s2).angle() : 0 + var offset = Size.zero + if trackMove { + let sina = sin(a) + let cosa = cos(a) + let w = e1.x - scale * (s1.x * cosa - s1.y * sina) + let h = e1.y - scale * (s1.x * sina + s1.y * cosa) + offset = Size(w: w, h: h) + } + return ZoomData(offset: offset, scale: scale, angle: a).combine(with: zoomData) + } + +} + +fileprivate class ZoomData { + + let offset: Size + let scale: Double + let angle: Double + + init(offset: Size = Size.zero, scale: Double = 1, angle: Double = 0) { + self.offset = offset + self.scale = scale + self.angle = angle + } + + func transform() -> Transform { + return Transform.move(dx: offset.w, dy: offset.h).scale(sx: scale, sy: scale).rotate(angle: angle) + } + + func move(delta: Size) -> ZoomData { + return ZoomData(offset: offset + delta, scale: scale, angle: angle) + } + + func combine(with: ZoomData) -> ZoomData { + let sina = sin(angle) + let cosa = cos(angle) + let w = offset.w + scale * (cosa * with.offset.w - sina * with.offset.h) + let h = offset.h + scale * (sina * with.offset.w + cosa * with.offset.h) + let s = scale * with.scale + let a = angle + with.angle + return ZoomData(offset: Size(w: w, h: h), scale: s, angle: a) + } + +} + +fileprivate class TouchData { + + let touch: MTouch + let point: Point + + convenience init(touch: MTouch, in view: MView) { + self.init(touch: touch, point: touch.location(in: view).toMacaw()) + } + + init(touch: MTouch, point: Point) { + self.touch = touch + self.point = point + } + + func current(in view: MView) -> Point { + return touch.location(in: view).toMacaw() + } + +} From 916240f118690cfdab278095c7f606358a6a000e Mon Sep 17 00:00:00 2001 From: Yuri Strot Date: Mon, 8 Apr 2019 19:27:57 +0700 Subject: [PATCH 6/6] #561: Built-in zoom support (Fix OSX build) --- Source/platform/iOS/MView_iOS.swift | 52 ++----------- Source/platform/macOS/MView_macOS.swift | 98 +++---------------------- Source/svg/SVGConstants.swift | 4 +- Source/svg/SVGParser.swift | 2 +- Source/views/MacawView.swift | 52 ++++++------- Source/views/MacawZoom.swift | 7 +- Source/views/Touchable.swift | 8 +- 7 files changed, 49 insertions(+), 174 deletions(-) diff --git a/Source/platform/iOS/MView_iOS.swift b/Source/platform/iOS/MView_iOS.swift index b97c7006..61d8d32e 100644 --- a/Source/platform/iOS/MView_iOS.swift +++ b/Source/platform/iOS/MView_iOS.swift @@ -26,70 +26,34 @@ open class MView: UIView, Touchable { open override func touchesBegan(_ touches: Set, with event: MEvent?) { super.touchesBegan(touches, with: event) - - let touchPoints = touches.map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - mTouchesBegan(touchPoints) + mTouchesBegan(touches, with: event) } open override func touchesMoved(_ touches: Set, with event: MEvent?) { super.touchesMoved(touches, with: event) - - let touchPoints = touches.map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - self.mTouchesMoved(touchPoints) + mTouchesMoved(touches, with: event) } open override func touchesEnded(_ touches: Set, with event: MEvent?) { super.touchesEnded(touches, with: event) - - let touchPoints = touches.map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - mTouchesEnded(touchPoints) + mTouchesEnded(touches, with: event) } override open func touchesCancelled(_ touches: Set, with event: MEvent?) { super.touchesCancelled(touches, with: event) - - let touchPoints = touches.map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - mTouchesCancelled(touchPoints) + mTouchesCancelled(touches, with: event) } - func mTouchesBegan(_ touches: [MTouchEvent]) { - + func mTouchesBegan(_ touches: Set, with event: MEvent?) { } - func mTouchesMoved(_ touches: [MTouchEvent]) { - + func mTouchesMoved(_ touches: Set, with event: MEvent?) { } - func mTouchesEnded(_ touches: [MTouchEvent]) { - + func mTouchesEnded(_ touches: Set, with event: MEvent?) { } - func mTouchesCancelled(_ touches: [MTouchEvent]) { - + func mTouchesCancelled(_ touches: Set, with event: MEvent?) { } } diff --git a/Source/platform/macOS/MView_macOS.swift b/Source/platform/macOS/MView_macOS.swift index b2bfd231..141346b7 100644 --- a/Source/platform/macOS/MView_macOS.swift +++ b/Source/platform/macOS/MView_macOS.swift @@ -38,7 +38,6 @@ open class MView: NSView, Touchable { super.init(coder: coder) self.wantsLayer = true - setupMouse() } open override var isFlipped: Bool { @@ -80,118 +79,39 @@ open class MView: NSView, Touchable { func layoutSubviews() { super.resizeSubviews(withOldSize: self.bounds.size) } - + // MARK: - Touch pad open override func touchesBegan(with event: NSEvent) { super.touchesBegan(with: event) - - let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - mTouchesBegan(touchPoints) + mTouchesBegan(event.touches(matching: .any, in: self), with: event) } open override func touchesEnded(with event: NSEvent) { super.touchesEnded(with: event) - - let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - mTouchesEnded(touchPoints) + mTouchesEnded(event.touches(matching: .any, in: self), with: event) } open override func touchesMoved(with event: NSEvent) { super.touchesMoved(with: event) - - let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - mTouchesMoved(touchPoints) + mTouchesMoved(event.touches(matching: .any, in: self), with: event) } open override func touchesCancelled(with event: NSEvent) { super.touchesCancelled(with: event) - - let touchPoints = event.touches(matching: .any, in: self).map { touch -> MTouchEvent in - let location = touch.location(in: self) - let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) - - return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) - } - - mTouchesCancelled(touchPoints) - } - - // MARK: - Mouse - private func setupMouse() { - subscribeForMouseDown() - subscribeForMouseUp() - subscribeForMouseDragged() - } - - private func subscribeForMouseDown() { - NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { [weak self] event -> NSEvent? in - self?.handleInput(event: event) { touches in - self?.mTouchesBegan(touches) - } - return event - } - } - - private func subscribeForMouseUp() { - NSEvent.addLocalMonitorForEvents(matching: .leftMouseUp) { [weak self] event -> NSEvent? in - self?.handleInput(event: event) { touches in - self?.mTouchesEnded(touches) - } - return event - } - } - - private func subscribeForMouseDragged() { - NSEvent.addLocalMonitorForEvents(matching: .leftMouseDragged) { [weak self] event -> NSEvent? in - self?.handleInput(event: event) { touches in - self?.mTouchesMoved(touches) - } - return event - } - } - - private func handleInput(event: NSEvent, handler: (_ touches: [MTouchEvent]) -> Void ) { - let location = self.convert(event.locationInWindow, to: .none) - let touchPoint = MTouchEvent(x: Double(location.x), y: Double(location.y), id: 0) - - handler([touchPoint]) - - return + mTouchesCancelled(event.touches(matching: .any, in: self), with: event) } // MARK: - Touchable - func mTouchesBegan(_ touches: [MTouchEvent]) { - + func mTouchesBegan(_ touches: Set, with event: MEvent?) { } - func mTouchesMoved(_ touches: [MTouchEvent]) { - + func mTouchesMoved(_ touches: Set, with event: MEvent?) { } - func mTouchesEnded(_ touches: [MTouchEvent]) { - + func mTouchesEnded(_ touches: Set, with event: MEvent?) { } - func mTouchesCancelled(_ touches: [MTouchEvent]) { - + func mTouchesCancelled(_ touches: Set, with event: MEvent?) { } } #endif diff --git a/Source/svg/SVGConstants.swift b/Source/svg/SVGConstants.swift index 4ebe0822..5111644d 100644 --- a/Source/svg/SVGConstants.swift +++ b/Source/svg/SVGConstants.swift @@ -292,7 +292,7 @@ open class SVGConstants { "yellow": 0xffff00, "yellowgreen": 0x9acd32 ] - + #if os(iOS) public static let systemColorList = [ "AppWorkspace": 0xffffff, @@ -334,7 +334,7 @@ open class SVGConstants { "WindowText": 0x242424 ] #endif - + public static func valueToColor(_ color: Int) -> String? { return SVGConstants.colorList.filter { _, v -> Bool in v == color }.map { k, _ -> String in k }.first } diff --git a/Source/svg/SVGParser.swift b/Source/svg/SVGParser.swift index d92b6ead..2b21bd02 100644 --- a/Source/svg/SVGParser.swift +++ b/Source/svg/SVGParser.swift @@ -595,7 +595,7 @@ open class SVGParser { let red = CGFloat((rgbValue >> 16) & 0xff) let green = CGFloat((rgbValue >> 08) & 0xff) let blue = CGFloat((rgbValue >> 00) & 0xff) - + return Color.rgba(r: Int(red), g: Int(green), b: Int(blue), a: opacity) } diff --git a/Source/views/MacawView.swift b/Source/views/MacawView.swift index 01823fc0..1c21593a 100644 --- a/Source/views/MacawView.swift +++ b/Source/views/MacawView.swift @@ -249,29 +249,11 @@ open class MacawView: MView, MGestureRecognizerDelegate { return .none } - open override func touchesBegan(_ touches: Set, with event: MEvent?) { - super.touchesBegan(touches, with: event) - zoom.touchesBegan(touches) - } - - open override func touchesMoved(_ touches: Set, with event: MEvent?) { - super.touchesMoved(touches, with: event) - zoom.touchesMoved(touches) - } - - open override func touchesEnded(_ touches: Set, with event: MEvent?) { - super.touchesEnded(touches, with: event) - zoom.touchesEnded(touches) - } - - open override func touchesCancelled(_ touches: Set, with event: MEvent?) { - super.touchesCancelled(touches, with: event) - zoom.touchesEnded(touches) - } - // MARK: - Touches + override func mTouchesBegan(_ touches: Set, with event: MEvent?) { + zoom.touchesBegan(touches) - override func mTouchesBegan(_ touches: [MTouchEvent]) { + let touchPoints = convert(touches: touches) if !self.node.shouldCheckForPressed() && !self.node.shouldCheckForMoved() && !self.node.shouldCheckForReleased () { @@ -282,7 +264,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { return } - for touch in touches { + for touch in touchPoints { let location = CGPoint(x: touch.x, y: touch.y) var nodePath = doFindNode(location: location) @@ -314,7 +296,8 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } - override func mTouchesMoved(_ touches: [MTouchEvent]) { + override func mTouchesMoved(_ touches: Set, with event: MEvent?) { + zoom.touchesMoved(touches) if !self.node.shouldCheckForMoved() { return } @@ -323,6 +306,7 @@ open class MacawView: MView, MGestureRecognizerDelegate { return } + let touchPoints = convert(touches: touches) touchesOfNode.keys.forEach { currentNode in guard let initialTouches = touchesOfNode[currentNode] else { return @@ -330,10 +314,10 @@ open class MacawView: MView, MGestureRecognizerDelegate { var points = [TouchPoint]() for initialTouch in initialTouches { - guard let currentIndex = touches.firstIndex(of: initialTouch) else { + guard let currentIndex = touchPoints.firstIndex(of: initialTouch) else { continue } - let currentTouch = touches[currentIndex] + let currentTouch = touchPoints[currentIndex] guard let nodePath = touchesMap[currentTouch]?.first else { continue } @@ -349,20 +333,30 @@ open class MacawView: MView, MGestureRecognizerDelegate { } } - override func mTouchesCancelled(_ touches: [MTouchEvent]) { + override func mTouchesCancelled(_ touches: Set, with event: MEvent?) { touchesEnded(touches: touches) } - override func mTouchesEnded(_ touches: [MTouchEvent]) { + override func mTouchesEnded(_ touches: Set, with event: MEvent?) { touchesEnded(touches: touches) } - private func touchesEnded(touches: [MTouchEvent]) { + private func convert(touches: Set) -> [MTouchEvent] { + return touches.map { touch -> MTouchEvent in + let location = touch.location(in: self) + let id = Int(bitPattern: Unmanaged.passUnretained(touch).toOpaque()) + return MTouchEvent(x: Double(location.x), y: Double(location.y), id: id) + } + } + + private func touchesEnded(touches: Set) { + zoom.touchesEnded(touches) guard let _ = renderer else { return } - for touch in touches { + let touchPoints = convert(touches: touches) + for touch in touchPoints { touchesMap[touch]?.forEach { nodePath in diff --git a/Source/views/MacawZoom.swift b/Source/views/MacawZoom.swift index a420b11e..1831593b 100644 --- a/Source/views/MacawZoom.swift +++ b/Source/views/MacawZoom.swift @@ -30,7 +30,9 @@ open class MacawZoom { trackScale = scale trackRotate = rotate if scale || rotate { + #if os(iOS) view.isMultipleTouchEnabled = true + #endif } } @@ -66,11 +68,6 @@ open class MacawZoom { func touchesEnded(_ touches: Set) { cleanTouches() - if let touch = touches.first { - if touches.count == 1 && touch.tapCount == 2 && touch.timestamp + 0.3 >= CACurrentMediaTime() { - set(offset: .zero, scale: 1, angle: 0) - } - } } @discardableResult private func cleanTouches() -> ZoomData? { diff --git a/Source/views/Touchable.swift b/Source/views/Touchable.swift index a830e63d..f5229470 100644 --- a/Source/views/Touchable.swift +++ b/Source/views/Touchable.swift @@ -19,8 +19,8 @@ class MTouchEvent: Hashable { } protocol Touchable { - func mTouchesBegan(_ touches: [MTouchEvent]) - func mTouchesMoved(_ touches: [MTouchEvent]) - func mTouchesEnded(_ touches: [MTouchEvent]) - func mTouchesCancelled(_ touches: [MTouchEvent]) + func mTouchesBegan(_ touches: Set, with event: MEvent?) + func mTouchesMoved(_ touches: Set, with event: MEvent?) + func mTouchesEnded(_ touches: Set, with event: MEvent?) + func mTouchesCancelled(_ touches: Set, with event: MEvent?) }