From b630e8885e4deab7fd205351d6f2b1ae02b4e83a Mon Sep 17 00:00:00 2001 From: Jacob Christie <19879272+jjatie@users.noreply.github.com> Date: Fri, 8 Nov 2019 02:22:37 -0400 Subject: [PATCH] Platform separation (#4178) * Added system dark mode support for primary label colors in the framework Moved Color to its own Platform file * Relocated color file * Reverted demo change * Separated `Platform` into multiple files for better organization * Updated from master --- Charts.xcodeproj/project.pbxproj | 16 + .../PiePolylineChartViewController.swift | 4 +- Source/Charts/Charts/ChartViewBase.swift | 2 +- .../Implementations/ChartBaseDataSet.swift | 4 +- Source/Charts/Utils/Platform+Color.swift | 53 +++ Source/Charts/Utils/Platform+Gestures.swift | 168 +++++++ Source/Charts/Utils/Platform+Graphics.swift | 144 ++++++ .../Utils/Platform+Touch Handling.swift | 130 ++++++ Source/Charts/Utils/Platform.swift | 424 +----------------- 9 files changed, 516 insertions(+), 429 deletions(-) create mode 100644 Source/Charts/Utils/Platform+Color.swift create mode 100644 Source/Charts/Utils/Platform+Gestures.swift create mode 100644 Source/Charts/Utils/Platform+Graphics.swift create mode 100644 Source/Charts/Utils/Platform+Touch Handling.swift diff --git a/Charts.xcodeproj/project.pbxproj b/Charts.xcodeproj/project.pbxproj index 6d11013875..8c0fbfb186 100644 --- a/Charts.xcodeproj/project.pbxproj +++ b/Charts.xcodeproj/project.pbxproj @@ -124,6 +124,10 @@ C33E1AF5471A60BA42DAF52E /* RadarHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F368CF209744D8F3B85B1028 /* RadarHighlighter.swift */; }; C3F0DDB7F0A922F0BB7EDB8A /* IBarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A53A9E42FC07FFDACA937C1 /* IBarChartDataSet.swift */; }; C7B150D740255670DEB9F455 /* Charts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65AD9E95D9ED4DC0BD73A743 /* Charts.framework */; }; + C9AA360A2355F01F00C97D93 /* Platform+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9AA36092355F01F00C97D93 /* Platform+Color.swift */; }; + C9F3DC262355F791000C3215 /* Platform+Graphics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F3DC242355F791000C3215 /* Platform+Graphics.swift */; }; + C9F3DC272355F791000C3215 /* Platform+Gestures.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F3DC252355F791000C3215 /* Platform+Gestures.swift */; }; + C9F3DC292355FA2F000C3215 /* Platform+Touch Handling.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F3DC282355FA2F000C3215 /* Platform+Touch Handling.swift */; }; CB785FE9B6B312408D17BC3B /* ChartUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FDA09EF973925A110506799 /* ChartUtils.swift */; }; CC7F8198A13249B5DEBBF25E /* AnimatedViewPortJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 710D7C9B2F1DB4A331EE405A /* AnimatedViewPortJob.swift */; }; CEF68F42A5390A73113F3663 /* Renderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F66B32AD8A878CBD6DB6ED2 /* Renderer.swift */; }; @@ -289,6 +293,10 @@ C75935E899183DDFA181E2CC /* AxisRendererBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AxisRendererBase.swift; path = Source/Charts/Renderers/AxisRendererBase.swift; sourceTree = ""; }; C8C9A105A7DB64F39DDA648B /* ComponentBase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ComponentBase.swift; path = Source/Charts/Components/ComponentBase.swift; sourceTree = ""; }; C8FB6219B143F8F7DA762950 /* TriangleShapeRenderer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TriangleShapeRenderer.swift; path = Source/Charts/Renderers/Scatter/TriangleShapeRenderer.swift; sourceTree = ""; }; + C9AA36092355F01F00C97D93 /* Platform+Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Platform+Color.swift"; path = "Source/Charts/Utils/Platform+Color.swift"; sourceTree = ""; }; + C9F3DC242355F791000C3215 /* Platform+Graphics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Platform+Graphics.swift"; path = "Source/Charts/Utils/Platform+Graphics.swift"; sourceTree = ""; }; + C9F3DC252355F791000C3215 /* Platform+Gestures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Platform+Gestures.swift"; path = "Source/Charts/Utils/Platform+Gestures.swift"; sourceTree = ""; }; + C9F3DC282355FA2F000C3215 /* Platform+Touch Handling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Platform+Touch Handling.swift"; path = "Source/Charts/Utils/Platform+Touch Handling.swift"; sourceTree = ""; }; C9FE42E868A225C116537368 /* ChartBaseDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ChartBaseDataSet.swift; path = Source/Charts/Data/Implementations/ChartBaseDataSet.swift; sourceTree = ""; }; CB1DD1A0F64266A10EE94194 /* ScatterChartDataSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ScatterChartDataSet.swift; path = Source/Charts/Data/Implementations/Standard/ScatterChartDataSet.swift; sourceTree = ""; }; D2E1819D72CD7B6C4A4E8048 /* LineChartTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LineChartTests.swift; path = Tests/Charts/LineChartTests.swift; sourceTree = ""; }; @@ -590,6 +598,10 @@ 5A4CFFFB65819121595F06F1 /* Fill.swift */, 3ED23C354AFE81818D78E645 /* Platform.swift */, 97AD2D4520AF917100F9C24A /* Platform+Accessibility.swift */, + C9AA36092355F01F00C97D93 /* Platform+Color.swift */, + C9F3DC252355F791000C3215 /* Platform+Gestures.swift */, + C9F3DC242355F791000C3215 /* Platform+Graphics.swift */, + C9F3DC282355FA2F000C3215 /* Platform+Touch Handling.swift */, FF475B9593B9898853814340 /* Transformer.swift */, 324C9127B53A8D39C8B49277 /* TransformerHorizontalBarChart.swift */, 72EAEBB7CF73E33565FC2896 /* ViewPortHandler.swift */, @@ -839,6 +851,7 @@ DBC9DB402CC9BB84B76968C4 /* Description.swift in Sources */, 03960E8148C6AEDACE4B77CC /* IMarker.swift in Sources */, ECE7EAE7179A7F57CE9BBD8F /* Legend.swift in Sources */, + C9F3DC272355F791000C3215 /* Platform+Gestures.swift in Sources */, C20A62D8CB9120523D5FB650 /* LegendEntry.swift in Sources */, 369DEB23452CB436A3A1A644 /* MarkerImage.swift in Sources */, 9360348A04723E653FBC8B18 /* MarkerView.swift in Sources */, @@ -848,6 +861,7 @@ 3395682A1E27756651FF6F4D /* BarChartData.swift in Sources */, 45C459FA25DFCBE62FA6A06C /* BarChartDataEntry.swift in Sources */, 17E994DA88777AA1D8CCFC58 /* BarChartDataSet.swift in Sources */, + C9AA360A2355F01F00C97D93 /* Platform+Color.swift in Sources */, 7C9CE6718D18859A35146098 /* BarLineScatterCandleBubbleChartData.swift in Sources */, 53A91F6F86740E26FE733639 /* BarLineScatterCandleBubbleChartDataSet.swift in Sources */, E68CA3DC66EB638C956E09B8 /* BubbleChartData.swift in Sources */, @@ -893,6 +907,7 @@ B6C9F450D937B87224D29D5C /* IFillFormatter.swift in Sources */, 967EE2EDDE3337C5C4337C59 /* IndexAxisValueFormatter.swift in Sources */, A40ACF0CCE96EEE104B0463D /* IValueFormatter.swift in Sources */, + C9F3DC262355F791000C3215 /* Platform+Graphics.swift in Sources */, 3CBE95F1E9394FA08CDCF31E /* BarHighlighter.swift in Sources */, D326491E8BCDE54A0921E137 /* ChartHighlighter.swift in Sources */, 64FA1EDB4DC1F65727D52D10 /* CombinedHighlighter.swift in Sources */, @@ -908,6 +923,7 @@ 4272DA5D44AF7DA05A5A8287 /* BubbleChartDataProvider.swift in Sources */, 30DCC4BAA5601B154ABADA13 /* CandleChartDataProvider.swift in Sources */, FD37AAC0270F390FFC470A65 /* ChartDataProvider.swift in Sources */, + C9F3DC292355FA2F000C3215 /* Platform+Touch Handling.swift in Sources */, 65EA404AE098EBCE8D5DE04B /* CombinedChartDataProvider.swift in Sources */, 2BA03CEC36BADCF682F1328B /* LineChartDataProvider.swift in Sources */, 0511E43EF3FD2CDE7F7F15DB /* ScatterChartDataProvider.swift in Sources */, diff --git a/ChartsDemo-iOS/Swift/Demos/PiePolylineChartViewController.swift b/ChartsDemo-iOS/Swift/Demos/PiePolylineChartViewController.swift index 75b5d99faf..a25000739b 100644 --- a/ChartsDemo-iOS/Swift/Demos/PiePolylineChartViewController.swift +++ b/ChartsDemo-iOS/Swift/Demos/PiePolylineChartViewController.swift @@ -6,9 +6,7 @@ // Copyright © 2017 jc. All rights reserved. // -#if canImport(UIKit) - import UIKit -#endif +import UIKit import Charts class PiePolylineChartViewController: DemoBaseViewController { diff --git a/Source/Charts/Charts/ChartViewBase.swift b/Source/Charts/Charts/ChartViewBase.swift index 0491d93c15..c453aced0c 100644 --- a/Source/Charts/Charts/ChartViewBase.swift +++ b/Source/Charts/Charts/ChartViewBase.swift @@ -98,7 +98,7 @@ open class ChartViewBase: NSUIView, ChartDataProvider, AnimatorDelegate @objc open var noDataFont = NSUIFont.systemFont(ofSize: 12) /// color of the no data text - @objc open var noDataTextColor: NSUIColor = NSUIColor.labelOrBlack + @objc open var noDataTextColor: NSUIColor = .labelOrBlack /// alignment of the no data text @objc open var noDataTextAlignment: NSTextAlignment = .left diff --git a/Source/Charts/Data/Implementations/ChartBaseDataSet.swift b/Source/Charts/Data/Implementations/ChartBaseDataSet.swift index 6387ee605d..5692e539d1 100644 --- a/Source/Charts/Data/Implementations/ChartBaseDataSet.swift +++ b/Source/Charts/Data/Implementations/ChartBaseDataSet.swift @@ -21,7 +21,7 @@ open class ChartBaseDataSet: NSObject, IChartDataSet, NSCopying // default color colors.append(NSUIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)) - valueColors.append(NSUIColor.labelOrBlack) + valueColors.append(.labelOrBlack) } @objc public init(label: String?) @@ -30,7 +30,7 @@ open class ChartBaseDataSet: NSObject, IChartDataSet, NSCopying // default color colors.append(NSUIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)) - valueColors.append(NSUIColor.labelOrBlack) + valueColors.append(.labelOrBlack) self.label = label } diff --git a/Source/Charts/Utils/Platform+Color.swift b/Source/Charts/Utils/Platform+Color.swift new file mode 100644 index 0000000000..fafc325536 --- /dev/null +++ b/Source/Charts/Utils/Platform+Color.swift @@ -0,0 +1,53 @@ +// +// Platform+Color.swift +// Charts +// +// Created by Jacob Christie on 2019-10-15. +// + +#if canImport(UIKit) +import UIKit + +public typealias NSUIColor = UIColor +private func fetchLabelColor() -> UIColor +{ + if #available(iOS 13, tvOS 13, *) + { + return .label + } + else + { + return .black + } +} +private let labelColor: UIColor = fetchLabelColor() + +extension UIColor +{ + static var labelOrBlack: UIColor { labelColor } +} +#endif + +#if canImport(AppKit) + +import AppKit + +public typealias NSUIColor = NSColor +private func fetchLabelColor() -> NSColor +{ + if #available(macOS 10.14, *) + { + return .labelColor + } + else + { + return .black + } +} +private let labelColor: NSColor = fetchLabelColor() + +extension NSColor +{ + static var labelOrBlack: NSColor { labelColor } +} +#endif diff --git a/Source/Charts/Utils/Platform+Gestures.swift b/Source/Charts/Utils/Platform+Gestures.swift new file mode 100644 index 0000000000..652c25ed0c --- /dev/null +++ b/Source/Charts/Utils/Platform+Gestures.swift @@ -0,0 +1,168 @@ +// +// Platform+Gestures.swift +// +// +// Created by Jacob Christie on 2019-10-15. +// + +// MARK: - UIKit +#if canImport(UIKit) +import UIKit + +public typealias NSUIGestureRecognizer = UIGestureRecognizer +public typealias NSUIGestureRecognizerState = UIGestureRecognizer.State +public typealias NSUIGestureRecognizerDelegate = UIGestureRecognizerDelegate +public typealias NSUITapGestureRecognizer = UITapGestureRecognizer +public typealias NSUIPanGestureRecognizer = UIPanGestureRecognizer + +extension NSUITapGestureRecognizer +{ + @objc final func nsuiNumberOfTouches() -> Int + { + return numberOfTouches + } + + @objc final var nsuiNumberOfTapsRequired: Int + { + get + { + return self.numberOfTapsRequired + } + set + { + self.numberOfTapsRequired = newValue + } + } +} + +extension NSUIPanGestureRecognizer +{ + @objc final func nsuiNumberOfTouches() -> Int + { + return numberOfTouches + } + + @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint + { + return super.location(ofTouch: touch, in: inView) + } +} + +#if !os(tvOS) +public typealias NSUIPinchGestureRecognizer = UIPinchGestureRecognizer +public typealias NSUIRotationGestureRecognizer = UIRotationGestureRecognizer + +extension NSUIRotationGestureRecognizer +{ + @objc final var nsuiRotation: CGFloat + { + get { return rotation } + set { rotation = newValue } + } +} + +extension NSUIPinchGestureRecognizer +{ + @objc final var nsuiScale: CGFloat + { + get + { + return scale + } + set + { + scale = newValue + } + } + + @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint + { + return super.location(ofTouch: touch, in: inView) + } +} +#endif +#endif + +// MARK: - AppKit +#if canImport(AppKit) +import AppKit + +public typealias NSUIGestureRecognizer = NSGestureRecognizer +public typealias NSUIGestureRecognizerState = NSGestureRecognizer.State +public typealias NSUIGestureRecognizerDelegate = NSGestureRecognizerDelegate +public typealias NSUITapGestureRecognizer = NSClickGestureRecognizer +public typealias NSUIPanGestureRecognizer = NSPanGestureRecognizer +public typealias NSUIPinchGestureRecognizer = NSMagnificationGestureRecognizer +public typealias NSUIRotationGestureRecognizer = NSRotationGestureRecognizer + +/** The 'tap' gesture is mapped to clicks. */ +extension NSUITapGestureRecognizer +{ + final func nsuiNumberOfTouches() -> Int + { + return 1 + } + + final var nsuiNumberOfTapsRequired: Int + { + get + { + return self.numberOfClicksRequired + } + set + { + self.numberOfClicksRequired = newValue + } + } +} + +extension NSUIPanGestureRecognizer +{ + final func nsuiNumberOfTouches() -> Int + { + return 1 + } + + /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures. + final func nsuiLocationOfTouch(_ touch: Int, inView: NSView?) -> NSPoint + { + return super.location(in: inView) + } +} + +extension NSUIRotationGestureRecognizer +{ + /// FIXME: Currently there are no velocities in OSX gestures, and not way to create custom touch gestures. + final var velocity: CGFloat + { + return 0.1 + } + + final var nsuiRotation: CGFloat + { + get { return -rotation } + set { rotation = -newValue } + } +} + +extension NSUIPinchGestureRecognizer +{ + final var nsuiScale: CGFloat + { + get + { + return magnification + 1.0 + } + set + { + magnification = newValue - 1.0 + } + } + + /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures. + final func nsuiLocationOfTouch(_ touch: Int, inView view: NSView?) -> NSPoint + { + return super.location(in: view) + } +} +#endif diff --git a/Source/Charts/Utils/Platform+Graphics.swift b/Source/Charts/Utils/Platform+Graphics.swift new file mode 100644 index 0000000000..6abb21e896 --- /dev/null +++ b/Source/Charts/Utils/Platform+Graphics.swift @@ -0,0 +1,144 @@ +// +// Platform+Graphics.swift +// +// +// Created by Jacob Christie on 2019-10-15. +// + +// MARK: - UIKit +#if canImport(UIKit) +import UIKit + +func NSUIGraphicsGetCurrentContext() -> CGContext? +{ + return UIGraphicsGetCurrentContext() +} + +func NSUIGraphicsGetImageFromCurrentImageContext() -> NSUIImage! +{ + return UIGraphicsGetImageFromCurrentImageContext() +} + +func NSUIGraphicsPushContext(_ context: CGContext) +{ + UIGraphicsPushContext(context) +} + +func NSUIGraphicsPopContext() +{ + UIGraphicsPopContext() +} + +func NSUIGraphicsEndImageContext() +{ + UIGraphicsEndImageContext() +} + +func NSUIImagePNGRepresentation(_ image: NSUIImage) -> Data? +{ + return image.pngData() +} + +func NSUIImageJPEGRepresentation(_ image: NSUIImage, _ quality: CGFloat = 0.8) -> Data? +{ + return image.jpegData(compressionQuality: quality) +} + +func NSUIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) +{ + UIGraphicsBeginImageContextWithOptions(size, opaque, scale) +} +#endif + +// MARK: - AppKit +#if canImport(AppKit) +import AppKit + +func NSUIGraphicsGetCurrentContext() -> CGContext? +{ + return NSGraphicsContext.current?.cgContext +} + +func NSUIGraphicsPushContext(_ context: CGContext) +{ + let cx = NSGraphicsContext(cgContext: context, flipped: true) + NSGraphicsContext.saveGraphicsState() + NSGraphicsContext.current = cx +} + +func NSUIGraphicsPopContext() +{ + NSGraphicsContext.restoreGraphicsState() +} + +func NSUIImagePNGRepresentation(_ image: NSUIImage) -> Data? +{ + image.lockFocus() + let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height)) + image.unlockFocus() + return rep?.representation(using: .png, properties: [:]) +} + +func NSUIImageJPEGRepresentation(_ image: NSUIImage, _ quality: CGFloat = 0.9) -> Data? +{ + image.lockFocus() + let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height)) + image.unlockFocus() + return rep?.representation(using: .jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: quality]) +} + +private var imageContextStack: [CGFloat] = [] + +func NSUIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) +{ + var scale = scale + if scale == 0.0 + { + scale = NSScreen.main?.backingScaleFactor ?? 1.0 + } + + let width = Int(size.width * scale) + let height = Int(size.height * scale) + + if width > 0 && height > 0 + { + imageContextStack.append(scale) + + let colorSpace = CGColorSpaceCreateDeviceRGB() + + guard let ctx = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4*width, space: colorSpace, bitmapInfo: (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue)) + else { return } + + ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(height))) + ctx.scaleBy(x: scale, y: scale) + NSUIGraphicsPushContext(ctx) + } +} + +func NSUIGraphicsGetImageFromCurrentImageContext() -> NSUIImage? +{ + if !imageContextStack.isEmpty + { + guard let ctx = NSUIGraphicsGetCurrentContext() + else { return nil } + + let scale = imageContextStack.last! + if let theCGImage = ctx.makeImage() + { + let size = CGSize(width: CGFloat(ctx.width) / scale, height: CGFloat(ctx.height) / scale) + let image = NSImage(cgImage: theCGImage, size: size) + return image + } + } + return nil +} + +func NSUIGraphicsEndImageContext() +{ + if imageContextStack.last != nil + { + imageContextStack.removeLast() + NSUIGraphicsPopContext() + } +} +#endif diff --git a/Source/Charts/Utils/Platform+Touch Handling.swift b/Source/Charts/Utils/Platform+Touch Handling.swift new file mode 100644 index 0000000000..afcfdd85ba --- /dev/null +++ b/Source/Charts/Utils/Platform+Touch Handling.swift @@ -0,0 +1,130 @@ +// +// Platform+Touch Handling.swift +// Charts +// +// Created by Jacob Christie on 2019-10-15. +// + +#if canImport(UIKit) +import UIKit + +public typealias NSUIEvent = UIEvent +public typealias NSUITouch = UITouch + +@objc +extension NSUIView { + public final override func touchesBegan(_ touches: Set, with event: NSUIEvent?) + { + self.nsuiTouchesBegan(touches, withEvent: event) + } + + public final override func touchesMoved(_ touches: Set, with event: NSUIEvent?) + { + self.nsuiTouchesMoved(touches, withEvent: event) + } + + public final override func touchesEnded(_ touches: Set, with event: NSUIEvent?) + { + self.nsuiTouchesEnded(touches, withEvent: event) + } + + public final override func touchesCancelled(_ touches: Set, with event: NSUIEvent?) + { + self.nsuiTouchesCancelled(touches, withEvent: event) + } + + open func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) + { + super.touchesBegan(touches, with: event!) + } + + open func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) + { + super.touchesMoved(touches, with: event!) + } + + open func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) + { + super.touchesEnded(touches, with: event!) + } + + open func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) + { + super.touchesCancelled(touches!, with: event!) + } +} + +extension UIView +{ + @objc final var nsuiGestureRecognizers: [NSUIGestureRecognizer]? + { + return self.gestureRecognizers + } +} +#endif + + +#if canImport(AppKit) +import AppKit + +public typealias NSUIEvent = NSEvent +public typealias NSUITouch = NSTouch + +@objc +extension NSUIView +{ + public final override func touchesBegan(with event: NSEvent) + { + self.nsuiTouchesBegan(event.touches(matching: .any, in: self), withEvent: event) + } + + public final override func touchesEnded(with event: NSEvent) + { + self.nsuiTouchesEnded(event.touches(matching: .any, in: self), withEvent: event) + } + + public final override func touchesMoved(with event: NSEvent) + { + self.nsuiTouchesMoved(event.touches(matching: .any, in: self), withEvent: event) + } + + open override func touchesCancelled(with event: NSEvent) + { + self.nsuiTouchesCancelled(event.touches(matching: .any, in: self), withEvent: event) + } + + open func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) + { + super.touchesBegan(with: event!) + } + + open func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) + { + super.touchesMoved(with: event!) + } + + open func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) + { + super.touchesEnded(with: event!) + } + + open func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) + { + super.touchesCancelled(with: event!) + } +} + +extension NSTouch +{ + /** Touch locations on OS X are relative to the trackpad, whereas on iOS they are actually *on* the view. */ + func locationInView(view: NSView) -> NSPoint + { + let n = self.normalizedPosition + let b = view.bounds + return NSPoint( + x: b.origin.x + b.size.width * n.x, + y: b.origin.y + b.size.height * n.y + ) + } +} +#endif diff --git a/Source/Charts/Utils/Platform.swift b/Source/Charts/Utils/Platform.swift index 38a6e2c598..19fdbf7eec 100644 --- a/Source/Charts/Utils/Platform.swift +++ b/Source/Charts/Utils/Platform.swift @@ -9,164 +9,19 @@ import Foundation #endif public typealias NSUIFont = UIFont -public typealias NSUIColor = UIColor -public typealias NSUIEvent = UIEvent -public typealias NSUITouch = UITouch public typealias NSUIImage = UIImage public typealias NSUIScrollView = UIScrollView -public typealias NSUIGestureRecognizer = UIGestureRecognizer -public typealias NSUIGestureRecognizerState = UIGestureRecognizer.State -public typealias NSUIGestureRecognizerDelegate = UIGestureRecognizerDelegate -public typealias NSUITapGestureRecognizer = UITapGestureRecognizer -public typealias NSUIPanGestureRecognizer = UIPanGestureRecognizer -#if !os(tvOS) -public typealias NSUIPinchGestureRecognizer = UIPinchGestureRecognizer -public typealias NSUIRotationGestureRecognizer = UIRotationGestureRecognizer -#endif public typealias NSUIScreen = UIScreen - public typealias NSUIDisplayLink = CADisplayLink -private func fetchLabelColor() -> UIColor -{ - if #available(iOS 13, tvOS 13, *) - { - return .label - } - else - { - return .black - } -} -private let labelColor: UIColor = fetchLabelColor() - -extension UIColor -{ - static var labelOrBlack: UIColor { labelColor } -} - -extension NSUITapGestureRecognizer -{ - @objc final func nsuiNumberOfTouches() -> Int - { - return numberOfTouches - } - - @objc final var nsuiNumberOfTapsRequired: Int - { - get - { - return self.numberOfTapsRequired - } - set - { - self.numberOfTapsRequired = newValue - } - } -} - -extension NSUIPanGestureRecognizer -{ - @objc final func nsuiNumberOfTouches() -> Int - { - return numberOfTouches - } - - @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint - { - return super.location(ofTouch: touch, in: inView) - } -} - -#if !os(tvOS) -extension NSUIRotationGestureRecognizer -{ - @objc final var nsuiRotation: CGFloat - { - get { return rotation } - set { rotation = newValue } - } -} -#endif - -#if !os(tvOS) -extension NSUIPinchGestureRecognizer -{ - @objc final var nsuiScale: CGFloat - { - get - { - return scale - } - set - { - scale = newValue - } - } - - @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint - { - return super.location(ofTouch: touch, in: inView) - } -} -#endif - open class NSUIView: UIView { - public final override func touchesBegan(_ touches: Set, with event: NSUIEvent?) - { - self.nsuiTouchesBegan(touches, withEvent: event) - } - - public final override func touchesMoved(_ touches: Set, with event: NSUIEvent?) - { - self.nsuiTouchesMoved(touches, withEvent: event) - } - - public final override func touchesEnded(_ touches: Set, with event: NSUIEvent?) - { - self.nsuiTouchesEnded(touches, withEvent: event) - } - - public final override func touchesCancelled(_ touches: Set, with event: NSUIEvent?) - { - self.nsuiTouchesCancelled(touches, withEvent: event) - } - - @objc open func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) - { - super.touchesBegan(touches, with: event!) - } - - @objc open func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) - { - super.touchesMoved(touches, with: event!) - } - - @objc open func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) - { - super.touchesEnded(touches, with: event!) - } - - @objc open func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) - { - super.touchesCancelled(touches!, with: event!) - } - @objc var nsuiLayer: CALayer? { return self.layer } } -extension UIView -{ - @objc final var nsuiGestureRecognizers: [NSUIGestureRecognizer]? - { - return self.gestureRecognizers - } -} - extension UIScrollView { @objc var nsuiIsScrollEnabled: Bool @@ -184,51 +39,11 @@ extension UIScreen } } -func NSUIGraphicsGetCurrentContext() -> CGContext? -{ - return UIGraphicsGetCurrentContext() -} - -func NSUIGraphicsGetImageFromCurrentImageContext() -> NSUIImage! -{ - return UIGraphicsGetImageFromCurrentImageContext() -} - -func NSUIGraphicsPushContext(_ context: CGContext) -{ - UIGraphicsPushContext(context) -} - -func NSUIGraphicsPopContext() -{ - UIGraphicsPopContext() -} - -func NSUIGraphicsEndImageContext() -{ - UIGraphicsEndImageContext() -} - -func NSUIImagePNGRepresentation(_ image: NSUIImage) -> Data? -{ - return image.pngData() -} - -func NSUIImageJPEGRepresentation(_ image: NSUIImage, _ quality: CGFloat = 0.8) -> Data? -{ - return image.jpegData(compressionQuality: quality) -} - func NSUIMainScreen() -> NSUIScreen? { return NSUIScreen.main } -func NSUIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) -{ - UIGraphicsBeginImageContextWithOptions(size, opaque, scale) -} - #endif #if os(OSX) @@ -236,18 +51,8 @@ import Cocoa import Quartz public typealias NSUIFont = NSFont -public typealias NSUIColor = NSColor -public typealias NSUIEvent = NSEvent -public typealias NSUITouch = NSTouch public typealias NSUIImage = NSImage public typealias NSUIScrollView = NSScrollView -public typealias NSUIGestureRecognizer = NSGestureRecognizer -public typealias NSUIGestureRecognizerState = NSGestureRecognizer.State -public typealias NSUIGestureRecognizerDelegate = NSGestureRecognizerDelegate -public typealias NSUITapGestureRecognizer = NSClickGestureRecognizer -public typealias NSUIPanGestureRecognizer = NSPanGestureRecognizer -public typealias NSUIPinchGestureRecognizer = NSMagnificationGestureRecognizer -public typealias NSUIRotationGestureRecognizer = NSRotationGestureRecognizer public typealias NSUIScreen = NSScreen /** On OS X there is no CADisplayLink. Use a 60 fps timer to render the animations. */ @@ -324,77 +129,6 @@ public class NSUIDisplayLink } } -/** The 'tap' gesture is mapped to clicks. */ -extension NSUITapGestureRecognizer -{ - final func nsuiNumberOfTouches() -> Int - { - return 1 - } - - final var nsuiNumberOfTapsRequired: Int - { - get - { - return self.numberOfClicksRequired - } - set - { - self.numberOfClicksRequired = newValue - } - } -} - -extension NSUIPanGestureRecognizer -{ - final func nsuiNumberOfTouches() -> Int - { - return 1 - } - - /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures. - final func nsuiLocationOfTouch(_ touch: Int, inView: NSView?) -> NSPoint - { - return super.location(in: inView) - } -} - -extension NSUIRotationGestureRecognizer -{ - /// FIXME: Currently there are no velocities in OSX gestures, and not way to create custom touch gestures. - final var velocity: CGFloat - { - return 0.1 - } - - final var nsuiRotation: CGFloat - { - get { return -rotation } - set { rotation = -newValue } - } -} - -extension NSUIPinchGestureRecognizer -{ - final var nsuiScale: CGFloat - { - get - { - return magnification + 1.0 - } - set - { - magnification = newValue - 1.0 - } - } - - /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures. - final func nsuiLocationOfTouch(_ touch: Int, inView view: NSView?) -> NSPoint - { - return super.location(in: view) - } -} - extension NSView { final var nsuiGestureRecognizers: [NSGestureRecognizer]? @@ -441,45 +175,6 @@ open class NSUIView: NSView self.setNeedsDisplay(self.bounds) } - public final override func touchesBegan(with event: NSEvent) - { - self.nsuiTouchesBegan(event.touches(matching: .any, in: self), withEvent: event) - } - - public final override func touchesEnded(with event: NSEvent) - { - self.nsuiTouchesEnded(event.touches(matching: .any, in: self), withEvent: event) - } - - public final override func touchesMoved(with event: NSEvent) - { - self.nsuiTouchesMoved(event.touches(matching: .any, in: self), withEvent: event) - } - - open override func touchesCancelled(with event: NSEvent) - { - self.nsuiTouchesCancelled(event.touches(matching: .any, in: self), withEvent: event) - } - - open func nsuiTouchesBegan(_ touches: Set, withEvent event: NSUIEvent?) - { - super.touchesBegan(with: event!) - } - - open func nsuiTouchesMoved(_ touches: Set, withEvent event: NSUIEvent?) - { - super.touchesMoved(with: event!) - } - - open func nsuiTouchesEnded(_ touches: Set, withEvent event: NSUIEvent?) - { - super.touchesEnded(with: event!) - } - - open func nsuiTouchesCancelled(_ touches: Set?, withEvent event: NSUIEvent?) - { - super.touchesCancelled(with: event!) - } open var backgroundColor: NSUIColor? { @@ -527,37 +222,9 @@ extension NSImage } } -extension NSTouch -{ - /** Touch locations on OS X are relative to the trackpad, whereas on iOS they are actually *on* the view. */ - func locationInView(view: NSView) -> NSPoint - { - let n = self.normalizedPosition - let b = view.bounds - return NSPoint(x: b.origin.x + b.size.width * n.x, y: b.origin.y + b.size.height * n.y) - } -} - -private func fetchLabelColor() -> NSColor -{ - if #available(macOS 10.14, *) - { - return .labelColor - } - else - { - return .black - } -} -private let labelColor: NSColor = fetchLabelColor() - -extension NSColor -{ - static var labelOrBlack: NSColor { labelColor } -} - extension NSScrollView { + /// NOTE: Unable to disable scrolling in macOS var scrollEnabled: Bool { get @@ -566,97 +233,8 @@ extension NSScrollView } set { - // FIXME: We can't disable scrolling it on OSX - } - } -} - -func NSUIGraphicsGetCurrentContext() -> CGContext? -{ - return NSGraphicsContext.current?.cgContext -} - -func NSUIGraphicsPushContext(_ context: CGContext) -{ - let cx = NSGraphicsContext(cgContext: context, flipped: true) - NSGraphicsContext.saveGraphicsState() - NSGraphicsContext.current = cx -} - -func NSUIGraphicsPopContext() -{ - NSGraphicsContext.restoreGraphicsState() -} - -func NSUIImagePNGRepresentation(_ image: NSUIImage) -> Data? -{ - image.lockFocus() - let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height)) - image.unlockFocus() - return rep?.representation(using: .png, properties: [:]) -} - -func NSUIImageJPEGRepresentation(_ image: NSUIImage, _ quality: CGFloat = 0.9) -> Data? -{ - image.lockFocus() - let rep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0, 0, image.size.width, image.size.height)) - image.unlockFocus() - return rep?.representation(using: .jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: quality]) -} - -private var imageContextStack: [CGFloat] = [] - -func NSUIGraphicsBeginImageContextWithOptions(_ size: CGSize, _ opaque: Bool, _ scale: CGFloat) -{ - var scale = scale - if scale == 0.0 - { - scale = NSScreen.main?.backingScaleFactor ?? 1.0 - } - - let width = Int(size.width * scale) - let height = Int(size.height * scale) - - if width > 0 && height > 0 - { - imageContextStack.append(scale) - - let colorSpace = CGColorSpaceCreateDeviceRGB() - - guard let ctx = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 4*width, space: colorSpace, bitmapInfo: (opaque ? CGImageAlphaInfo.noneSkipFirst.rawValue : CGImageAlphaInfo.premultipliedFirst.rawValue)) - else { return } - - ctx.concatenate(CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: CGFloat(height))) - ctx.scaleBy(x: scale, y: scale) - NSUIGraphicsPushContext(ctx) - } -} - -func NSUIGraphicsGetImageFromCurrentImageContext() -> NSUIImage? -{ - if !imageContextStack.isEmpty - { - guard let ctx = NSUIGraphicsGetCurrentContext() - else { return nil } - - let scale = imageContextStack.last! - if let theCGImage = ctx.makeImage() - { - let size = CGSize(width: CGFloat(ctx.width) / scale, height: CGFloat(ctx.height) / scale) - let image = NSImage(cgImage: theCGImage, size: size) - return image } } - return nil -} - -func NSUIGraphicsEndImageContext() -{ - if imageContextStack.last != nil - { - imageContextStack.removeLast() - NSUIGraphicsPopContext() - } } func NSUIMainScreen() -> NSUIScreen?