From 381a8a0fc3516d4a79246e7cee52780ff0c6561c Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Thu, 5 Aug 2021 09:01:42 +1000 Subject: [PATCH 01/33] Added NSTextAlignment and NSMutableParagraphStyle --- Sources/UXKit/Common/UXTextView.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/UXKit/Common/UXTextView.swift b/Sources/UXKit/Common/UXTextView.swift index 3b99bd3..d433585 100644 --- a/Sources/UXKit/Common/UXTextView.swift +++ b/Sources/UXKit/Common/UXTextView.swift @@ -10,16 +10,24 @@ import class UIKit.NSTextStorage import class UIKit.NSLayoutManager import class UIKit.NSTextContainer + import enum UIKit.NSTextAlignment + import class UIKit.NSMutableParagraphStyle public typealias NSTextStorage = UIKit.NSTextStorage public typealias NSLayoutManager = UIKit.NSLayoutManager public typealias NSTextContainer = UIKit.NSTextContainer + public typealias NSTextAlignment = UIKit.NSTextAlignment + public typealias NSMutableParagraphStyle = UIKit.NSMutableParagraphStyle #else // macOS import class AppKit.NSTextStorage import class AppKit.NSLayoutManager import class AppKit.NSTextContainer + import enum AppKit.NSTextAlignment + import class AppKit.NSMutableParagraphStyle public typealias NSTextStorage = AppKit.NSTextStorage public typealias NSLayoutManager = AppKit.NSLayoutManager public typealias NSTextContainer = AppKit.NSTextContainer + public typealias NSTextAlignment = AppKit.NSTextAlignment + public typealias NSMutableParagraphStyle = AppKit.NSMutableParagraphStyle #endif // macOS From 2302bbe90f86d554aaeb4342c87c7530082bf1b9 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Thu, 5 Aug 2021 23:05:55 +1000 Subject: [PATCH 02/33] Added NSStringDrawingContext Added UXRectCorner (UIKit only because macOS doesn't support it) Added static image() function to UXImage to allow an image with arbitrary content provided by a draw block to be generated. --- Sources/UXKit/AppKit/UXGraphics-AppKit.swift | 15 +++++++++++++++ Sources/UXKit/Common/UXTextView.swift | 4 ++++ Sources/UXKit/UIKit/UXGraphics-UIKit.swift | 12 ++++++++++++ 3 files changed, 31 insertions(+) diff --git a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift index 2b59623..bcc5043 100644 --- a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift +++ b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift @@ -50,5 +50,20 @@ static var applicationIconImage: UXImage? { return UXImage(named: NSImage.applicationIconName) } + + static func image(withSize size: CGSize, andContent drawContent: () -> Void) -> UXImage { + let textImage = NSImage(size: size) + if let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0) { + textImage.addRepresentation(rep) + NSGraphicsContext.saveGraphicsState() + NSGraphicsContext.current = NSGraphicsContext.init(bitmapImageRep: rep) + } + + drawContent() + + NSGraphicsContext.restoreGraphicsState() + + return textImage + } } #endif // os(macOS) diff --git a/Sources/UXKit/Common/UXTextView.swift b/Sources/UXKit/Common/UXTextView.swift index d433585..c3a37e9 100644 --- a/Sources/UXKit/Common/UXTextView.swift +++ b/Sources/UXKit/Common/UXTextView.swift @@ -12,22 +12,26 @@ import class UIKit.NSTextContainer import enum UIKit.NSTextAlignment import class UIKit.NSMutableParagraphStyle + import class UIKit.NSStringDrawingContext public typealias NSTextStorage = UIKit.NSTextStorage public typealias NSLayoutManager = UIKit.NSLayoutManager public typealias NSTextContainer = UIKit.NSTextContainer public typealias NSTextAlignment = UIKit.NSTextAlignment public typealias NSMutableParagraphStyle = UIKit.NSMutableParagraphStyle + public typealias NSStringDrawingContext = UIKit.NSStringDrawingContext #else // macOS import class AppKit.NSTextStorage import class AppKit.NSLayoutManager import class AppKit.NSTextContainer import enum AppKit.NSTextAlignment import class AppKit.NSMutableParagraphStyle + import class AppKit.NSStringDrawingContext public typealias NSTextStorage = AppKit.NSTextStorage public typealias NSLayoutManager = AppKit.NSLayoutManager public typealias NSTextContainer = AppKit.NSTextContainer public typealias NSTextAlignment = AppKit.NSTextAlignment public typealias NSMutableParagraphStyle = AppKit.NSMutableParagraphStyle + public typealias NSStringDrawingContext = AppKit.NSStringDrawingContext #endif // macOS diff --git a/Sources/UXKit/UIKit/UXGraphics-UIKit.swift b/Sources/UXKit/UIKit/UXGraphics-UIKit.swift index e359ec3..7fec0e7 100644 --- a/Sources/UXKit/UIKit/UXGraphics-UIKit.swift +++ b/Sources/UXKit/UIKit/UXGraphics-UIKit.swift @@ -15,6 +15,7 @@ public typealias UXRect = CGRect public typealias UXPoint = CGPoint public typealias UXSize = CGSize + public typealias UXRectCorner = UIRectCorner public typealias UXImage = UIImage @@ -47,6 +48,17 @@ as? [ String ])?.first else { return nil } return UXImage(named: icon) } + + static func image(withSize size: CGSize, andContent drawContent: () -> Void) -> UXImage { + UIGraphicsBeginImageContextWithOptions(size, false, 1.0); + + drawContent() + + let result = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return result! + } } public extension Bundle { From 7a8440c869207d6dbf6c93580c5dc573eada6289 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Sun, 15 Aug 2021 14:21:55 +1000 Subject: [PATCH 03/33] * Lots of changes to broaden the compatibility between UIKit and AppKit. * Some of the changes to NSTableViewCell and UXTableView-* are placeholders to allow compilation to occur. They don't actually work yet. The target is to get the AppKit version of the table view to be a reasonably accurate and code-compatible representation of the UIKit table view. (i.e. a single column of table cells as views that provide their own layouts) * On AppKit there is no "Swipe" gesture recogniser however there is a mechanism for sensing a swipe. I've added an easy to access method for this. * A number of UXBezierPath enhancements to make it useful across both platforms. * Early attempt to build a UIAlert style code-compatibility layer for AppKit. Incomplete and untested. --- Package@swift-5.1.swift | 2 +- Package@swift-5.swift | 19 ++-- Sources/UXKit/AppKit/NSTableViewCell.swift | 48 ++++++++- Sources/UXKit/AppKit/UXGestures-AppKit.swift | 28 +++++ Sources/UXKit/AppKit/UXGraphics-AppKit.swift | 76 +++++++++++-- Sources/UXKit/AppKit/UXTableView-AppKit.swift | 15 ++- Sources/UXKit/AppKit/UXView-AppKit.swift | 100 +++++++++++++++++- Sources/UXKit/Common/UXAlert.swift | 58 ++++++++++ Sources/UXKit/Common/UXIndexPath.swift | 15 +++ Sources/UXKit/UIKit/UXGestures-UIKit.swift | 1 + Sources/UXKit/UIKit/UXGraphics-UIKit.swift | 7 ++ Sources/UXKit/UIKit/UXTableView-UIKit.swift | 3 + Sources/UXKit/UIKit/UXView-UIKit.swift | 4 +- UXKit.xcodeproj/project.pbxproj | 2 + 14 files changed, 353 insertions(+), 25 deletions(-) diff --git a/Package@swift-5.1.swift b/Package@swift-5.1.swift index 2a2e82a..b4bd09a 100644 --- a/Package@swift-5.1.swift +++ b/Package@swift-5.1.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "UXKit", platforms: [ - .macOS(.v10_12), .iOS(.v10) + .macOS(.v10_13), .iOS(.v10) ], products: [ .library(name: "UXKit", targets: ["UXKit"]), diff --git a/Package@swift-5.swift b/Package@swift-5.swift index 679dc5d..a3fa59c 100644 --- a/Package@swift-5.swift +++ b/Package@swift-5.swift @@ -3,12 +3,15 @@ import PackageDescription let package = Package( - name: "UXKit", - products: [ - .library(name: "UXKit", targets: ["UXKit"]), - ], - dependencies: [], - targets: [ - .target(name:"UXKit") - ] + name: "UXKit", + platforms: [ + .macOS(.v10_13), .iOS(.v10) + ], + products: [ + .library(name: "UXKit", targets: ["UXKit"]), + ], + dependencies: [], + targets: [ + .target(name:"UXKit") + ] ) diff --git a/Sources/UXKit/AppKit/NSTableViewCell.swift b/Sources/UXKit/AppKit/NSTableViewCell.swift index 56c4642..f0a3ea3 100644 --- a/Sources/UXKit/AppKit/NSTableViewCell.swift +++ b/Sources/UXKit/AppKit/NSTableViewCell.swift @@ -56,6 +56,53 @@ } private let style = Style() + // provided for compatibility, though not really used. + public enum SelectionStyle : Int { + case none = 0 + case blue = 1 + case gray = 2 + @available(iOS 7.0, *) + case `default` = 3 + } + + public var selectionStyle : SelectionStyle = .default + + // provided for compatibility, though not really used. + public enum AccessoryType : Int { + case none = 0 // don't show any accessory view + case disclosureIndicator = 1 // regular chevron. doesn't track + case detailDisclosureButton = 2 // info button w/ chevron. tracks + case checkmark = 3 // checkmark. doesn't track + + @available(iOS 7.0, *) + case detailButton = 4 // info button. tracks + } + + public var accessoryType : AccessoryType = .none + + + // provided for compatibility, though not really used. + public enum EditingStyle : Int { + case none = 0 + case delete = 1 + case insert = 2 + } + + // These are provided for UIKit compatibility. Not actively used (yet). + public var backgroundColor : UXColor { + get { + if let col = self.layer?.backgroundColor { + return UXColor(cgColor: col)! + } + + return UXColor.textBackgroundColor + } + + set { + self.layer?.backgroundColor = newValue.cgColor + } + } + public init(style: UXTableViewCellStyle, reuseIdentifier id: String?) { /* TODO: SETUP: default: just label, no detail @@ -224,7 +271,6 @@ } } - // MARK: - Separator line (TBD: should we draw this?) open var dividerColor : UXColor { return UXColor.lightGray } diff --git a/Sources/UXKit/AppKit/UXGestures-AppKit.swift b/Sources/UXKit/AppKit/UXGestures-AppKit.swift index 8237592..057e7f3 100644 --- a/Sources/UXKit/AppKit/UXGestures-AppKit.swift +++ b/Sources/UXKit/AppKit/UXGestures-AppKit.swift @@ -7,6 +7,7 @@ import Cocoa public typealias UXGestureRecognizer = NSGestureRecognizer + public typealias UXGestureRecognizerDelegate = NSGestureRecognizerDelegate public typealias UXRotationGestureRecognizer = NSRotationGestureRecognizer public typealias UXPanGestureRecognizer = NSPanGestureRecognizer public typealias UXTapGestureRecognizer = NSClickGestureRecognizer @@ -16,6 +17,14 @@ public extension NSView { + enum SwipeDirection { + case none + case left + case right + case up + case down + } + @discardableResult func on(gesture gr: UXGestureRecognizer, target: AnyObject, action: Selector) -> Self @@ -26,6 +35,25 @@ return self } + // This is how macOS handles Swipe gestures. + override func swipe(with event: NSEvent) { + let x : CGFloat = event.deltaX + let y : CGFloat = event.deltaY + var direction : SwipeDirection = .none + + if (x != 0) { + direction = (x > 0) ? .left : .right + } else if (y != 0) { + direction = (y > 0) ? .up : .down + } + + self.swipeGestureRecognized(inDirection: direction) + } + + // Override this if you want to receive swipe gestures on your NSView. + func swipeGestureRecognized(inDirection direction: SwipeDirection) { + } + } public extension UXTapGestureRecognizer { diff --git a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift index bcc5043..ba86268 100644 --- a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift +++ b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift @@ -17,6 +17,8 @@ public let UXEdgeInsetsMake = NSEdgeInsetsMake public typealias UXImage = NSImage + public typealias UXEvent = NSEvent + public typealias UXTouch = NSTouch #if swift(>=4.0) public typealias UXEdgeInsets = NSEdgeInsets @@ -51,19 +53,77 @@ return UXImage(named: NSImage.applicationIconName) } + /// Returns an image of the specified size, allowing the caller to provide a draw function that draws into the "current" graphics context. + /// - Parameters: + /// - size: The size of the image + /// - drawContent: A block responsible for drawing into the image. + /// - Returns: The image (which may be blank). static func image(withSize size: CGSize, andContent drawContent: () -> Void) -> UXImage { - let textImage = NSImage(size: size) + let resultImage = NSImage(size: size) if let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0) { - textImage.addRepresentation(rep) + resultImage.addRepresentation(rep) NSGraphicsContext.saveGraphicsState() - NSGraphicsContext.current = NSGraphicsContext.init(bitmapImageRep: rep) + NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep) + + drawContent() + + NSGraphicsContext.restoreGraphicsState() } - - drawContent() - - NSGraphicsContext.restoreGraphicsState() - return textImage + return resultImage } } + +import class SpriteKit.SKScene + +public extension UXTouch { + + func location(inScene: SKScene) -> UXPoint { + return self.location(in: inScene.view) + } +} + +public extension NSBezierPath { + + /// CREDIT: https://gist.github.com/juliensagot/9749c3a1df28c38fb9f9 + /// A `CGPath` object representing the current `NSBezierPath`. + var cgPath: CGPath { + let path = CGMutablePath() + let points = UnsafeMutablePointer.allocate(capacity: 3) + + if elementCount > 0 { + var didClosePath = true + + for index in 0.. Int { + return self.numberOfRows + } } public extension NSTableView { @@ -91,10 +98,10 @@ /// UIKit compat method for `makeView(withIdentifier:owner:)`. This one /// passes `nil` as the owner. func dequeueReusableCell(withIdentifier identifier: String) - -> UXView? + -> UXTableViewCell? { return makeView(withIdentifier: UXUserInterfaceItemIdentifier(identifier), - owner: nil) + owner: nil) as? UXTableViewCell } /// UIKit compat method for `makeView(withIdentifier:owner:)`. This one @@ -102,7 +109,7 @@ /// on AppKit. /// Note: Raises a fatalError if the cell could not be constructed! func dequeueReusableCell(withIdentifier identifier: String, - for indexPath: IndexPath) -> UXView + for indexPath: IndexPath) -> UXTableViewCell { guard let v = dequeueReusableCell(withIdentifier: identifier) else { fatalError("could not construct cell for \(identifier)") diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index f9d680b..06840e6 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -24,8 +24,10 @@ public typealias UXCheckBox = NSButton public typealias UXImageView = NSImageView public typealias UXSlider = NSSlider - - + public typealias UXAccessibility = NSAccessibility + public typealias UXAccessibilityElement = NSAccessibilityElement + public typealias UXTextFieldDelegate = NSTextFieldDelegate + // MARK: - UXUserInterfaceItemIdentification #if os(macOS) && swift(>=4.0) @@ -66,6 +68,64 @@ // maybe?) return CGPoint(x: NSMidX(frame), y: NSMidY(frame)) } + + var alpha: CGFloat { + get { + return self.alphaValue + } + set { + self.alphaValue = newValue + } + } + + func setSubViews(enabled: Bool) { + self.subviews.forEach { subView in + if subView.isKind(of: NSControl.self) { + (subView as! NSControl).isEnabled = enabled + } + + subView.setSubViews(enabled: enabled) + + subView.display() + } + } + + func isAnySubViewEnabled() -> Bool { + var result : Bool = false + + var iterator = self.subviews.enumerated().makeIterator() + + var current = iterator.next()?.element + + while current != nil && !result { + if current != nil { + if current!.isKind(of: NSControl.self) { + result = (current as! NSControl).isEnabled + } + + if !result { + result = current!.isAnySubViewEnabled() + + if !result { + current = iterator.next()?.element + } + } + } + } + + return result + } + + var isUserInteractionEnabled : Bool { + get { + return isAnySubViewEnabled() + } + + set { + setSubViews(enabled: newValue) + } + } + } public extension NSProgressIndicator { @@ -111,6 +171,31 @@ get { return alignment } } + var text : String? { + get { + return self.stringValue + } + set { + if let nv = newValue { + self.stringValue = nv + } else { + self.stringValue = "" + } + } + } + + var placeholder : String? { + get { + return self.placeholderString + } + set { + if let nv = newValue { + self.placeholderString = nv + } else { + self.placeholderString = "" + } + } + } } public extension UXSpinner { @@ -139,4 +224,15 @@ } } + +public extension UXAccessibility { + static func post(notification: UXAccessibility.Notification, + argument: Any?) { + if let arg = argument { + self.post(element: arg, notification: notification) + } else { + self.post(element: self, notification: notification) + } + } +} #endif // os(macOS) diff --git a/Sources/UXKit/Common/UXAlert.swift b/Sources/UXKit/Common/UXAlert.swift index c049e90..0cf8784 100644 --- a/Sources/UXKit/Common/UXAlert.swift +++ b/Sources/UXKit/Common/UXAlert.swift @@ -11,10 +11,68 @@ public typealias UXAlert = NSAlert + +public extension UXAlert.Style { + + static let alert : UXAlert.Style = .warning +} + +public class UXAlertAction { + public enum Style : Int { + case `default` = 0 + case cancel + case destructive + } + + var style : Style + var title : String? + var handler : ((UXAlertAction) -> Void)? + + public init(title: String?, + style: UXAlertAction.Style, + handler: ((UXAlertAction) -> Void)? = nil) { + self.style = style + self.title = title + self.handler = handler + } +} + +public extension UXAlert { + convenience init(title: String?, + message: String?, + preferredStyle: UXAlert.Style) { + self.init() + + if title != nil { + self.messageText = title! + } + + if message != nil { + self.informativeText = message! + } + + self.alertStyle = preferredStyle + } + + func addAction(_ action: UXAlertAction) { + // The idea here was to tune the visualisation of the buttons in a manner similar to UIKit, + // but at this time, I don't know how to. + switch action.style { + case .default: + self.addButton(withTitle: action.title!) + case .cancel: + self.addButton(withTitle: action.title!) + case .destructive: + self.addButton(withTitle: action.title!) + } + } +} + #elseif os(iOS) import UIKit public typealias UXAlert = UIAlertController + public typealias UXAlertAction = UIAlertAction public extension UIAlertController { diff --git a/Sources/UXKit/Common/UXIndexPath.swift b/Sources/UXKit/Common/UXIndexPath.swift index f6bc0fb..b72a3b0 100644 --- a/Sources/UXKit/Common/UXIndexPath.swift +++ b/Sources/UXKit/Common/UXIndexPath.swift @@ -9,6 +9,21 @@ import Foundation #if swift(>=4.0) // Hm. It does seem to have it (produces ambiguities). But maybe not. import AppKit + public extension IndexPath { + var section : Int { + get { + return 0 + } + } + var row : Int { + get { + return self.startIndex + } + } + init(row: Int, section: Int) { + self.init(index: row) + } + } #else public extension IndexPath { diff --git a/Sources/UXKit/UIKit/UXGestures-UIKit.swift b/Sources/UXKit/UIKit/UXGestures-UIKit.swift index a8ef8cc..05ed914 100644 --- a/Sources/UXKit/UIKit/UXGestures-UIKit.swift +++ b/Sources/UXKit/UIKit/UXGestures-UIKit.swift @@ -7,6 +7,7 @@ import UIKit public typealias UXGestureRecognizer = UIGestureRecognizer + public typealias UXGestureRecognizerDelegate = UIGestureRecognizerDelegate public typealias UXRotationGestureRecognizer = UIRotationGestureRecognizer public typealias UXPanGestureRecognizer = UIPanGestureRecognizer public typealias UXTapGestureRecognizer = UITapGestureRecognizer diff --git a/Sources/UXKit/UIKit/UXGraphics-UIKit.swift b/Sources/UXKit/UIKit/UXGraphics-UIKit.swift index 7fec0e7..2b0567c 100644 --- a/Sources/UXKit/UIKit/UXGraphics-UIKit.swift +++ b/Sources/UXKit/UIKit/UXGraphics-UIKit.swift @@ -16,6 +16,8 @@ public typealias UXPoint = CGPoint public typealias UXSize = CGSize public typealias UXRectCorner = UIRectCorner + public typealias UXEvent = UIEvent + public typealias UXTouch = UITouch public typealias UXImage = UIImage @@ -49,6 +51,11 @@ return UXImage(named: icon) } + /// Returns an image of the specified size, allowing the caller to provide a draw function that draws into the "current" graphics context. + /// - Parameters: + /// - size: The size of the image + /// - drawContent: A block responsible for drawing into the image. + /// - Returns: The image (which may be blank). static func image(withSize size: CGSize, andContent drawContent: () -> Void) -> UXImage { UIGraphicsBeginImageContextWithOptions(size, false, 1.0); diff --git a/Sources/UXKit/UIKit/UXTableView-UIKit.swift b/Sources/UXKit/UIKit/UXTableView-UIKit.swift index 12d7f89..8a50e4d 100644 --- a/Sources/UXKit/UIKit/UXTableView-UIKit.swift +++ b/Sources/UXKit/UIKit/UXTableView-UIKit.swift @@ -33,6 +33,9 @@ */ public typealias UXTableViewCell = UITableViewCell // same on iOS + public typealias UXTableViewDelegate = UITableViewDelegate + public typealias UXTableViewDataSource = UITableViewDataSource + public typealias UXTableViewCellStyle = UITableViewCell.CellStyle public extension UITableView.RowAnimation { static var effectFade = UITableView.RowAnimation.fade diff --git a/Sources/UXKit/UIKit/UXView-UIKit.swift b/Sources/UXKit/UIKit/UXView-UIKit.swift index 2566a2e..caf9f28 100644 --- a/Sources/UXKit/UIKit/UXView-UIKit.swift +++ b/Sources/UXKit/UIKit/UXView-UIKit.swift @@ -24,7 +24,9 @@ public typealias UXCheckBox = UISwitch public typealias UXImageView = UIImageView public typealias UXSlider = UISlider - + public typealias UXAccessibility = UIAccessibility + public typealias UXAccessibilityElement = UIAccessibilityElement + public typealias UXTextFieldDelegate = UITextFieldDelegate // MARK: - UXUserInterfaceItemIdentification diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index 0949048..27c51e0 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -442,6 +442,7 @@ baseConfigurationReference = E8460D781FBF12D2002CDC28 /* MacStaticLib.xcconfig */; buildSettings = { EXECUTABLE_PREFIX = lib; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -452,6 +453,7 @@ baseConfigurationReference = E8460D781FBF12D2002CDC28 /* MacStaticLib.xcconfig */; buildSettings = { EXECUTABLE_PREFIX = lib; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; }; From c1df5a612cfad8e5d1c07bee93fa3fa7f9c6dbee Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Sun, 15 Aug 2021 23:52:23 +1000 Subject: [PATCH 04/33] Prevent bezier path always being closed. --- Sources/UXKit/AppKit/UXGraphics-AppKit.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift index ba86268..70dbe96 100644 --- a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift +++ b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift @@ -114,7 +114,7 @@ public extension NSBezierPath { } } - if !didClosePath { path.closeSubpath() } + //if !didClosePath { path.closeSubpath() } } points.deallocate() From ae2cc28386484122d544d76a8d557d5e96614340 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Sun, 22 Aug 2021 23:46:01 +1000 Subject: [PATCH 05/33] Working on getting UXTableView working properly. --- Sources/UXKit/AppKit/NSTableViewCell.swift | 8 +++++--- Sources/UXKit/AppKit/UXGraphics-AppKit.swift | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Sources/UXKit/AppKit/NSTableViewCell.swift b/Sources/UXKit/AppKit/NSTableViewCell.swift index f0a3ea3..949e892 100644 --- a/Sources/UXKit/AppKit/NSTableViewCell.swift +++ b/Sources/UXKit/AppKit/NSTableViewCell.swift @@ -127,7 +127,7 @@ } private var installedConstraintSetup = ViewSetup.none - private var requiredContraintSetup : ViewSetup { + private var requiredConstraintSetup : ViewSetup { switch ( _textLabel, _detailTextLabel ) { case ( .none, .none ): return .none case ( .some, .none ): return .label @@ -140,7 +140,7 @@ override open func updateConstraints() { super.updateConstraints() - let required = requiredContraintSetup + let required = requiredConstraintSetup guard installedConstraintSetup != required else { return } // Swift.print("from \(installedConstraintSetup) to \(required)") @@ -217,6 +217,8 @@ label.action = #selector(didEditTableRow(_:)) #endif + label.sizeToFit() + addSubview(label) #if false // this is funny, setting this overrides the font self.textField = label // this is weak! @@ -295,7 +297,7 @@ // MARK: - Label Factory open func makeLabel() -> UXLabel { - let v = NSTextField(frame: UXRect()) + let v = UXLabel(frame: UXRect()) v.translatesAutoresizingMaskIntoConstraints = false /* configure as label */ diff --git a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift index 70dbe96..f0a3770 100644 --- a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift +++ b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift @@ -92,7 +92,7 @@ public extension NSBezierPath { let points = UnsafeMutablePointer.allocate(capacity: 3) if elementCount > 0 { - var didClosePath = true +// var didClosePath = true for index in 0.. Date: Mon, 23 Aug 2021 23:41:43 +1000 Subject: [PATCH 06/33] Got the table view cells centered properly on macOS. --- Sources/UXKit/AppKit/NSTableViewCell.swift | 29 +++++++++++----------- Sources/UXKit/AppKit/UXView-AppKit.swift | 8 ++++++ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Sources/UXKit/AppKit/NSTableViewCell.swift b/Sources/UXKit/AppKit/NSTableViewCell.swift index 949e892..c621eaf 100644 --- a/Sources/UXKit/AppKit/NSTableViewCell.swift +++ b/Sources/UXKit/AppKit/NSTableViewCell.swift @@ -217,8 +217,6 @@ label.action = #selector(didEditTableRow(_:)) #endif - label.sizeToFit() - addSubview(label) #if false // this is funny, setting this overrides the font self.textField = label // this is weak! @@ -297,19 +295,20 @@ // MARK: - Label Factory open func makeLabel() -> UXLabel { - let v = UXLabel(frame: UXRect()) - v.translatesAutoresizingMaskIntoConstraints = false - - /* configure as label */ - v.isEditable = false - v.isBezeled = false - v.drawsBackground = false - v.isSelectable = false // not for raw labels - - /* common */ - v.alignment = .left - - return v + let v = UXLabel(frame: UXRect()) + v.cell = VerticallyCenteredTextFieldCell() + v.translatesAutoresizingMaskIntoConstraints = false + + /* configure as label */ + v.isEditable = false + v.isBezeled = false + v.drawsBackground = false + v.isSelectable = false // not for raw labels + + /* common */ + v.alignment = .center + + return v } } diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index 06840e6..4f0024e 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -198,6 +198,14 @@ } } +open class VerticallyCenteredTextFieldCell: NSTextFieldCell { + open override func drawingRect(forBounds rect: NSRect) -> NSRect { + let rect = super.drawingRect(forBounds: rect) + let size = cellSize(forBounds: rect) + return NSRect(x: rect.minX, y: rect.minY + (rect.height - size.height) / 2, width: rect.width, height: size.height) + } +} + public extension UXSpinner { /// Use this instead of `isDisplayedWhenStopped` for UIKit compatibility. var hidesWhenStopped : Bool { From bf01731f44de98c59d6f47f9078d56c235b27617 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Wed, 25 Aug 2021 00:13:38 +1000 Subject: [PATCH 07/33] Added UXCustomRowView class to allow control over the background drawing of a table row. Fixed UXIndexPath so that "row" works as expected. --- Sources/UXKit/AppKit/UXTableView-AppKit.swift | 20 +++++++++++++++++++ Sources/UXKit/Common/UXIndexPath.swift | 9 ++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Sources/UXKit/AppKit/UXTableView-AppKit.swift b/Sources/UXKit/AppKit/UXTableView-AppKit.swift index 2fbff8c..9e86771 100644 --- a/Sources/UXKit/AppKit/UXTableView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXTableView-AppKit.swift @@ -118,4 +118,24 @@ } } + +public class UXCustomRowView: NSTableRowView { + + public override func drawSelection(in dirtyRect: NSRect) { + + if self.isSelected { + if #available(macOS 10.14, *) { + NSColor.selectedContentBackgroundColor.set() + } else { + // Fallback on earlier versions + NSColor.selectedMenuItemColor.set() + } + } else { + NSColor.clear.set() + } + + dirtyRect.fill() + } +} + #endif // os(macOS) diff --git a/Sources/UXKit/Common/UXIndexPath.swift b/Sources/UXKit/Common/UXIndexPath.swift index b72a3b0..71984a0 100644 --- a/Sources/UXKit/Common/UXIndexPath.swift +++ b/Sources/UXKit/Common/UXIndexPath.swift @@ -10,18 +10,13 @@ import Foundation // Hm. It does seem to have it (produces ambiguities). But maybe not. import AppKit public extension IndexPath { - var section : Int { - get { - return 0 - } - } var row : Int { get { - return self.startIndex + return self.item } } init(row: Int, section: Int) { - self.init(index: row) + self.init(item: row, section: section) } } #else From 593fce12dae12c9d9114b12fb8ffa53c4337517a Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Fri, 27 Aug 2021 22:20:42 +1000 Subject: [PATCH 08/33] Getting cell/row editing working within a table. --- Sources/UXKit/AppKit/NSTableViewCell.swift | 60 ++++++++++++++++++- Sources/UXKit/AppKit/UXTableView-AppKit.swift | 6 ++ Sources/UXKit/AppKit/UXView-AppKit.swift | 27 ++++++++- 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/Sources/UXKit/AppKit/NSTableViewCell.swift b/Sources/UXKit/AppKit/NSTableViewCell.swift index c621eaf..b79a249 100644 --- a/Sources/UXKit/AppKit/NSTableViewCell.swift +++ b/Sources/UXKit/AppKit/NSTableViewCell.swift @@ -21,7 +21,7 @@ * * NOTE: This one doesn't actually support an image :-/ */ - open class NSTableViewCell : NSTableCellView { + open class NSTableViewCell : NSTableCellView, NSTextFieldDelegate { // Note: UITableViewCell has many more views to show selection state, // regular background, etc. @@ -110,7 +110,8 @@ value1: blue lable on the left 1/4, black detail on the right (same row) subtitle: label, below gray detail (two rows) */ - + editing = false + super.init(frame: UXRect()) if let id = id { @@ -203,7 +204,7 @@ let label = makeLabel() label.font = UXFont.systemFont(ofSize: style.labelSize) label.cell?.lineBreakMode = .byTruncatingTail - + #if false label.verticalAlignment = .middleAlignment // TBD: do we want this? // I'm still not quite sure why we even need this. The height of the @@ -226,6 +227,59 @@ return label } } + + public override var acceptsFirstResponder: Bool { + get { + return true + } + } + + public override func becomeFirstResponder() -> Bool { + if !self.editing { + self.editing = true + + if #available(macOS 10.14, *) { + _textLabel?.backgroundColor = .selectedContentBackgroundColor + } else { + // Fallback on earlier versions + _textLabel?.backgroundColor = .selectedMenuItemColor + } + + _textLabel?.drawsBackground = true + _textLabel?.isEditable = true + _textLabel?.target = self + _textLabel?.action = #selector(didEditTableRow(_ :)) + _textLabel?.becomeFirstResponder() + _textLabel?.delegate = self + + return true + } + + return false + } + + public override func resignFirstResponder() -> Bool { + if self.editing { + self.editing = false + _textLabel?.drawsBackground = false + _textLabel?.abortEditing() + _textLabel?.isEditable = false + } + + return true + } + + var editing : Bool + + @objc func didEditTableRow(_ editor: Any) { + NSLog("row was edited") + _ = self.resignFirstResponder() + } + + public func controlTextDidEndEditing(_ obj: Notification) { + NSLog("row edit aborted") + _ = self.resignFirstResponder() + } var _detailTextLabel : UXLabel? = nil open var detailTextLabel : UXLabel? { diff --git a/Sources/UXKit/AppKit/UXTableView-AppKit.swift b/Sources/UXKit/AppKit/UXTableView-AppKit.swift index 9e86771..128f37f 100644 --- a/Sources/UXKit/AppKit/UXTableView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXTableView-AppKit.swift @@ -93,6 +93,12 @@ } } + + extension NSTableView { + open override func edit(withFrame rect: NSRect, editor textObj: NSText, delegate: Any?, event: NSEvent) { + textObj.becomeFirstResponder() + } +} public extension NSTableView { /// UIKit compat method for `makeView(withIdentifier:owner:)`. This one diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index 4f0024e..a8533d0 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -199,11 +199,34 @@ } open class VerticallyCenteredTextFieldCell: NSTextFieldCell { + + fileprivate var isEditingOrSelecting : Bool = false + open override func drawingRect(forBounds rect: NSRect) -> NSRect { let rect = super.drawingRect(forBounds: rect) - let size = cellSize(forBounds: rect) - return NSRect(x: rect.minX, y: rect.minY + (rect.height - size.height) / 2, width: rect.width, height: size.height) + + if !isEditingOrSelecting { + let size = cellSize(forBounds: rect) + return NSRect(x: rect.minX, y: rect.minY + (rect.height - size.height) / 2, width: rect.width, height: size.height) + } + + return rect } + + open override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) { + let aRect = self.drawingRect(forBounds: rect) + isEditingOrSelecting = true + super.select(withFrame: aRect, in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength) + isEditingOrSelecting = false + } + + open override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) { + let aRect = self.drawingRect(forBounds: rect) + isEditingOrSelecting = true + super.edit(withFrame: aRect, in: controlView, editor: textObj, delegate: delegate, event: event) + isEditingOrSelecting = false + } + } public extension UXSpinner { From b1c3f1ffa6a2b9e5cd67426622fccf13eb2c1698 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 31 Aug 2021 00:07:08 +1000 Subject: [PATCH 09/33] Added a UIKit style set of methods to the TextFieldDelegate protocol so that text fields can use that, providing delegate transparency to the application. --- Sources/UXKit/AppKit/NSTableViewCell.swift | 67 +++++++++++++++------- Sources/UXKit/AppKit/UXView-AppKit.swift | 15 ++++- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Sources/UXKit/AppKit/NSTableViewCell.swift b/Sources/UXKit/AppKit/NSTableViewCell.swift index b79a249..7ee3281 100644 --- a/Sources/UXKit/AppKit/NSTableViewCell.swift +++ b/Sources/UXKit/AppKit/NSTableViewCell.swift @@ -238,20 +238,29 @@ if !self.editing { self.editing = true - if #available(macOS 10.14, *) { - _textLabel?.backgroundColor = .selectedContentBackgroundColor - } else { - // Fallback on earlier versions - _textLabel?.backgroundColor = .selectedMenuItemColor - } - - _textLabel?.drawsBackground = true - _textLabel?.isEditable = true - _textLabel?.target = self - _textLabel?.action = #selector(didEditTableRow(_ :)) - _textLabel?.becomeFirstResponder() - _textLabel?.delegate = self + if let label = _textLabel { + if #available(macOS 10.14, *) { + label.backgroundColor = .selectedContentBackgroundColor + } else { + // Fallback on earlier versions + label.backgroundColor = .selectedMenuItemColor + } + label.drawsBackground = true + label.isEditable = true + label.target = self + label.action = #selector(didEditTableRow(_ :)) + label.becomeFirstResponder() + + if label.delegate == nil { + label.delegate = self + } + + if let del = label.delegate as? UXTextFieldDelegate { + del.textFieldDidBeginEditing(label) + } + } + return true } @@ -261,9 +270,16 @@ public override func resignFirstResponder() -> Bool { if self.editing { self.editing = false - _textLabel?.drawsBackground = false - _textLabel?.abortEditing() - _textLabel?.isEditable = false + + if let label = _textLabel { + label.drawsBackground = false + label.abortEditing() + label.isEditable = false + + if let del = label.delegate as? UXTextFieldDelegate { + del.textFieldDidEndEditing(label) + } + } } return true @@ -273,13 +289,22 @@ @objc func didEditTableRow(_ editor: Any) { NSLog("row was edited") - _ = self.resignFirstResponder() + if let label = _textLabel { + if let del = label.delegate as? UXTextFieldDelegate { + if del.textFieldShouldReturn(label) { + // Only actually end the editing if the delegate says to. + _ = self.resignFirstResponder() + } + } else { + // if the delegate is not an instance of UXTextFieldDelegate, meaning it + // doesn't support the UIKit enhancements, then just treat this as an end + // to editing. + // + _ = self.resignFirstResponder() + } + } } - public func controlTextDidEndEditing(_ obj: Notification) { - NSLog("row edit aborted") - _ = self.resignFirstResponder() - } var _detailTextLabel : UXLabel? = nil open var detailTextLabel : UXLabel? { diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index a8533d0..b04aa58 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -26,7 +26,6 @@ public typealias UXSlider = NSSlider public typealias UXAccessibility = NSAccessibility public typealias UXAccessibilityElement = NSAccessibilityElement - public typealias UXTextFieldDelegate = NSTextFieldDelegate // MARK: - UXUserInterfaceItemIdentification @@ -170,7 +169,7 @@ set { alignment = newValue } get { return alignment } } - + var text : String? { get { return self.stringValue @@ -196,6 +195,18 @@ } } } + + } + + // provides a cross-compatible delegate protocol. +public protocol UXTextFieldDelegate : NSTextFieldDelegate { + func textField(_ textField: UXTextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool + + func textFieldDidBeginEditing(_ textField: UXTextField) + + func textFieldDidEndEditing(_ textField: UXTextField) + + func textFieldShouldReturn(_ textField: UXTextField) -> Bool } open class VerticallyCenteredTextFieldCell: NSTextFieldCell { From f81047e57ce8e7262d19ab32a2b11eaa845257fb Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Wed, 1 Sep 2021 20:01:01 +1000 Subject: [PATCH 10/33] Fleshed out the UXAlertAction, and UXAlert and got it working. --- Sources/UXKit/Common/UXAlert.swift | 90 ++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/Sources/UXKit/Common/UXAlert.swift b/Sources/UXKit/Common/UXAlert.swift index 0cf8784..c85662e 100644 --- a/Sources/UXKit/Common/UXAlert.swift +++ b/Sources/UXKit/Common/UXAlert.swift @@ -17,17 +17,39 @@ public extension UXAlert.Style { static let alert : UXAlert.Style = .warning } +/// A UIKit style AlertAction to provide code compatibility to the enclosing application. +/// Use this to add an 'action' to a UXAlert. +/// public class UXAlertAction { + + /// The style of the action / button public enum Style : Int { case `default` = 0 case cancel case destructive } + /// Action style var style : Style + + /// Action title. var title : String? + + /// The handler block for the action. var handler : ((UXAlertAction) -> Void)? + /// These properties are used to allow the action to insert itself into the responder chain for when the + /// action is triggered. + /// + private var chainingButton : NSButton? + private var chainedTarget : AnyObject? + private var chainedSelector : Selector? + + /// Initialise the action with the supplied parameters. + /// - Parameters: + /// - title: the title for the action; this will be the text of the button (pre-localized) + /// - style: the style of the action; used to change the L&F of the button. + /// - handler: a block that will be called when the action is triggered by the alert.. public init(title: String?, style: UXAlertAction.Style, handler: ((UXAlertAction) -> Void)? = nil) { @@ -35,9 +57,53 @@ public class UXAlertAction { self.title = title self.handler = handler } + + /// A method that can be called by the alert when the associated button is pressed. + @objc func action(sender: AnyObject?) { + // If we actually have a handler, then call it. + if let handlerBlock = self.handler { + handlerBlock(self) + } + + // If there is a chained button, then call whatever action it was given by + // AppKit when the button was created. + // + if chainingButton != nil && + chainedTarget != nil && + chainedSelector != nil { + if let target = chainedTarget as? NSObject { + if target.responds(to: chainedSelector) { + target.perform(chainedSelector, with: sender) + } + } + } + } + + /// Inserts the action into the responder chain by first preserving whatever action AppKit + /// gave to the button when it was created, and then inserting this action. + /// - Parameter button: the button to be chained to. + func chainAction(toButton button: NSButton) { + // first preserve a reference to the button and it's current action. + self.chainingButton = button + self.chainedTarget = button.target + self.chainedSelector = button.action + + // now override the button action so that it triggers this action. + // + button.target = self + button.action = #selector(action) + } } +/// An extension to NSAlert that adds a layer of code compatibility for apps that need to work on both UIKit and AppKit. public extension UXAlert { + + + /// Initialise a new UXAlert with the provided parameters. + /// - Parameters: + /// - title: a pre-localized title for the alert + /// - message: a pre-localized message for the alert + /// - preferredStyle: the preferred style of the alert convenience init(title: String?, message: String?, preferredStyle: UXAlert.Style) { @@ -54,16 +120,30 @@ public extension UXAlert { self.alertStyle = preferredStyle } + /// Adds a single UXAlertAction to the alert, presenting it as a button. Due to the way NSAlert works in + /// AppKit, it is recommended that the "default" action be added first. Destructive buttons should never + /// be added first as AppKit ignores some of the styling that would normally happen for destructive buttons + /// when they are also the "default" button. + /// - Parameter action: the action to be added. func addAction(_ action: UXAlertAction) { - // The idea here was to tune the visualisation of the buttons in a manner similar to UIKit, - // but at this time, I don't know how to. switch action.style { case .default: - self.addButton(withTitle: action.title!) + let button = self.addButton(withTitle: action.title!) + action.chainAction(toButton: button) case .cancel: - self.addButton(withTitle: action.title!) + let button = self.addButton(withTitle: action.title!) + action.chainAction(toButton: button) case .destructive: - self.addButton(withTitle: action.title!) + let button = self.addButton(withTitle: action.title!) + action.chainAction(toButton: button) + + if #available(macOS 11.0, *) { + button.hasDestructiveAction = true + } else { + if #available(macOS 10.14, *) { + button.contentTintColor = .red + } + } } } } From c4da749a798379af27b075fb687c3169910275a4 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Thu, 2 Sep 2021 23:08:09 +1000 Subject: [PATCH 11/33] Added initialiser for UXTableView that does some initialisation that I expect will be common to most UXTableViews regardless of the platform. --- Sources/UXKit/AppKit/UXTableView-AppKit.swift | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/Sources/UXKit/AppKit/UXTableView-AppKit.swift b/Sources/UXKit/AppKit/UXTableView-AppKit.swift index 128f37f..de8c8c0 100644 --- a/Sources/UXKit/AppKit/UXTableView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXTableView-AppKit.swift @@ -44,6 +44,16 @@ case subtitle } +// Not used for now. +public enum UXTableViewStyle { + /// A plain table view. + case plain + /// A table view where sections have distinct groups of rows. + case grouped + /// A table view where the grouped sections are inset with rounded corners. + case insetGrouped +} + public protocol UXTableViewCellInit : AnyObject { init(style: UXTableViewCellStyle, reuseIdentifier: String?) func prepareForReuse() @@ -64,8 +74,27 @@ static var automatic = slideDown } - public extension NSTableView { +public extension UXTableView { + convenience init(frame frameRect: UXRect, style: UXTableViewStyle, withSingleColumn: Bool = true) { + + self.init(frame: frameRect) + + self.allowsColumnResizing = false + self.allowsColumnReordering = false + self.allowsEmptySelection = false + self.headerView = nil + self.usesAlternatingRowBackgroundColors = false + self.intercellSpacing = .zero + + if withSingleColumn { + let col = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("tableColumn1")) + col.width = frameRect.width + col.isEditable = false + self.addTableColumn(col) + } + } + func insertRows(at indexes : [ IndexPath ], with ao : NSTableView.AnimationOptions? = nil) { @@ -94,12 +123,12 @@ } - extension NSTableView { + extension UXTableView { open override func edit(withFrame rect: NSRect, editor textObj: NSText, delegate: Any?, event: NSEvent) { textObj.becomeFirstResponder() } } - public extension NSTableView { + public extension UXTableView { /// UIKit compat method for `makeView(withIdentifier:owner:)`. This one /// passes `nil` as the owner. From d913e5bae9254b21559cda88c17246eb8f19387f Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Sun, 21 Nov 2021 17:31:04 +1100 Subject: [PATCH 12/33] Added valueChanged notification to UIKit. --- Sources/UXKit/UIKit/UXView-UIKit.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/UXKit/UIKit/UXView-UIKit.swift b/Sources/UXKit/UIKit/UXView-UIKit.swift index 41b10ef..486aa3a 100644 --- a/Sources/UXKit/UIKit/UXView-UIKit.swift +++ b/Sources/UXKit/UIKit/UXView-UIKit.swift @@ -42,7 +42,10 @@ var identifier: UXUserInterfaceItemIdentifier? { get set } } - + public extension UXAccessibility.Notification { + static let valueChanged: UXAccessibility.Notification = .layoutChanged + } + // MARK: - NSView Compatibility public extension UIActivityIndicatorView { From 442194882c25e8282e9b87b9b13d8d76f9408f02 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 7 Mar 2022 20:19:46 +1100 Subject: [PATCH 13/33] Set the minimum OS versions. --- UXKit.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index b41f6b8..0b8d351 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -427,6 +427,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D771FBF12D2002CDC28 /* Debug.xcconfig */; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; }; name = Debug; }; @@ -434,6 +436,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D7A1FBF12D2002CDC28 /* Release.xcconfig */; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MACOSX_DEPLOYMENT_TARGET = 10.11; }; name = Release; }; From 0c2b5d9ff31f1f7120ac2337153d0b0603aa6f6b Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 7 Mar 2022 22:46:56 +1100 Subject: [PATCH 14/33] Added a tvOS target. --- UXKit.xcodeproj/project.pbxproj | 228 ++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index 0b8d351..453fbf1 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -22,6 +22,24 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + D037E3F727D6278700892CF0 /* UXTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8BF4DC4247025F20055AA53 /* UXTextView.swift */; }; + D037E3F827D6278700892CF0 /* UXGestureActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A937061FD7322800F180F8 /* UXGestureActions.swift */; }; + D037E3F927D6278700892CF0 /* UXObjectPresentingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2511FBB326800C1063B /* UXObjectPresentingType.swift */; }; + D037E3FA27D6278700892CF0 /* UXObjectControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2501FBB326800C1063B /* UXObjectControl.swift */; }; + D037E3FB27D6278700892CF0 /* UXPasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2521FBB326800C1063B /* UXPasteboard.swift */; }; + D037E3FC27D6278700892CF0 /* UXViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2531FBB326800C1063B /* UXViewController.swift */; }; + D037E3FD27D6278700892CF0 /* UXViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82E9D41223A953A005A52AE /* UXViewType.swift */; }; + D037E3FE27D6278700892CF0 /* UXAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8080ECE234E43060022D71A /* UXAlert.swift */; }; + D037E3FF27D6278700892CF0 /* UXIndexPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B24F1FBB326800C1063B /* UXIndexPath.swift */; }; + D037E40027D6279200892CF0 /* UXCollectionView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2551FBB326800C1063B /* UXCollectionView-UIKit.swift */; }; + D037E40127D6279200892CF0 /* UXTargetAction-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B25B1FBB326800C1063B /* UXTargetAction-UIKit.swift */; }; + D037E40227D6279200892CF0 /* NSSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83935312698823E00272DA7 /* NSSound.swift */; }; + D037E40327D6279200892CF0 /* UXFont-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2561FBB326800C1063B /* UXFont-UIKit.swift */; }; + D037E40427D6279200892CF0 /* UXTableView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B25A1FBB326800C1063B /* UXTableView-UIKit.swift */; }; + D037E40527D6279200892CF0 /* UXView-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B25C1FBB326800C1063B /* UXView-UIKit.swift */; }; + D037E40627D6279200892CF0 /* UXGestures-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2571FBB326800C1063B /* UXGestures-UIKit.swift */; }; + D037E40727D6279200892CF0 /* UXLayout-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2591FBB326800C1063B /* UXLayout-UIKit.swift */; }; + D037E40827D6279200892CF0 /* UXGraphics-UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A9B2581FBB326800C1063B /* UXGraphics-UIKit.swift */; }; E8080ECF234E43060022D71A /* UXAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8080ECE234E43060022D71A /* UXAlert.swift */; }; E8080ED0234E43060022D71A /* UXAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8080ECE234E43060022D71A /* UXAlert.swift */; }; E82E9D42223A953A005A52AE /* UXViewType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E82E9D41223A953A005A52AE /* UXViewType.swift */; }; @@ -78,6 +96,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + D037E3EE27D6270B00892CF0 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; E8A9B27E1FBB351D00C1063B /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -90,6 +117,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + D037E3F027D6270B00892CF0 /* libUXKit-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libUXKit-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E8080ECE234E43060022D71A /* UXAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UXAlert.swift; sourceTree = ""; }; E82E9D41223A953A005A52AE /* UXViewType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UXViewType.swift; sourceTree = ""; }; E83935312698823E00272DA7 /* NSSound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSSound.swift; sourceTree = ""; }; @@ -131,6 +159,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + D037E3ED27D6270B00892CF0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; E8A9B2331FBB318700C1063B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -178,6 +213,7 @@ children = ( E8A9B2361FBB318800C1063B /* libUXKit-AppKit.a */, E8A9B2801FBB351D00C1063B /* libUXKit-UIKit.a */, + D037E3F027D6270B00892CF0 /* libUXKit-tvOS.a */, ); name = Products; sourceTree = ""; @@ -261,6 +297,23 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + D037E3EF27D6270B00892CF0 /* UXKit-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D037E3F627D6270B00892CF0 /* Build configuration list for PBXNativeTarget "UXKit-tvOS" */; + buildPhases = ( + D037E3EC27D6270B00892CF0 /* Sources */, + D037E3ED27D6270B00892CF0 /* Frameworks */, + D037E3EE27D6270B00892CF0 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "UXKit-tvOS"; + productName = "UXKit-tvOS"; + productReference = D037E3F027D6270B00892CF0 /* libUXKit-tvOS.a */; + productType = "com.apple.product-type.library.static"; + }; E8A9B2351FBB318700C1063B /* UXKit-AppKit */ = { isa = PBXNativeTarget; buildConfigurationList = E8A9B23F1FBB318800C1063B /* Build configuration list for PBXNativeTarget "UXKit-AppKit" */; @@ -301,9 +354,14 @@ E8A9B22E1FBB318700C1063B /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 1320; LastUpgradeCheck = 1310; ORGANIZATIONNAME = "ZeeZide GmbH"; TargetAttributes = { + D037E3EF27D6270B00892CF0 = { + CreatedOnToolsVersion = 13.2.1; + ProvisioningStyle = Automatic; + }; E85CBD30233F736A0065D1DB = { CreatedOnToolsVersion = 11.1; ProvisioningStyle = Automatic; @@ -335,12 +393,38 @@ targets = ( E8A9B2351FBB318700C1063B /* UXKit-AppKit */, E8A9B27F1FBB351D00C1063B /* UXKit-UIKit */, + D037E3EF27D6270B00892CF0 /* UXKit-tvOS */, E85CBD30233F736A0065D1DB /* UXKit-AllPlatforms */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ + D037E3EC27D6270B00892CF0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D037E3FF27D6278700892CF0 /* UXIndexPath.swift in Sources */, + D037E3FC27D6278700892CF0 /* UXViewController.swift in Sources */, + D037E40027D6279200892CF0 /* UXCollectionView-UIKit.swift in Sources */, + D037E3FA27D6278700892CF0 /* UXObjectControl.swift in Sources */, + D037E3F927D6278700892CF0 /* UXObjectPresentingType.swift in Sources */, + D037E3F727D6278700892CF0 /* UXTextView.swift in Sources */, + D037E40127D6279200892CF0 /* UXTargetAction-UIKit.swift in Sources */, + D037E40727D6279200892CF0 /* UXLayout-UIKit.swift in Sources */, + D037E3FB27D6278700892CF0 /* UXPasteboard.swift in Sources */, + D037E40527D6279200892CF0 /* UXView-UIKit.swift in Sources */, + D037E40227D6279200892CF0 /* NSSound.swift in Sources */, + D037E40827D6279200892CF0 /* UXGraphics-UIKit.swift in Sources */, + D037E3FD27D6278700892CF0 /* UXViewType.swift in Sources */, + D037E40427D6279200892CF0 /* UXTableView-UIKit.swift in Sources */, + D037E40327D6279200892CF0 /* UXFont-UIKit.swift in Sources */, + D037E3F827D6278700892CF0 /* UXGestureActions.swift in Sources */, + D037E3FE27D6278700892CF0 /* UXAlert.swift in Sources */, + D037E40627D6279200892CF0 /* UXGestures-UIKit.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E8A9B2321FBB318700C1063B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -407,19 +491,154 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + D037E3F427D6270B00892CF0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = YTQG85D588; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 15.2; + }; + name = Debug; + }; + D037E3F527D6270B00892CF0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = YTQG85D588; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 15.2; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; E85CBD31233F736A0065D1DB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CODE_SIGN_STYLE = Automatic; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "watchsimulator watchos macosx iphonesimulator iphoneos driverkit appletvsimulator appletvos"; + SUPPORTS_MACCATALYST = YES; }; name = Debug; }; E85CBD32233F736A0065D1DB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; CODE_SIGN_STYLE = Automatic; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "watchsimulator watchos macosx iphonesimulator iphoneos driverkit appletvsimulator appletvos"; + SUPPORTS_MACCATALYST = YES; }; name = Release; }; @@ -491,6 +710,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + D037E3F627D6270B00892CF0 /* Build configuration list for PBXNativeTarget "UXKit-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D037E3F427D6270B00892CF0 /* Debug */, + D037E3F527D6270B00892CF0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; E85CBD33233F736A0065D1DB /* Build configuration list for PBXAggregateTarget "UXKit-AllPlatforms" */ = { isa = XCConfigurationList; buildConfigurations = ( From 30c1ab55f15a684ecd30064477b740412a279834 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 7 Mar 2022 22:56:19 +1100 Subject: [PATCH 15/33] Made the deployment targets more consistent. --- UXKit.xcodeproj/project.pbxproj | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index 453fbf1..3748e14 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -555,7 +555,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 15.2; + TVOS_DEPLOYMENT_TARGET = 13.0; }; name = Debug; }; @@ -615,7 +615,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 15.2; + TVOS_DEPLOYMENT_TARGET = 13.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -646,8 +646,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D771FBF12D2002CDC28 /* Debug.xcconfig */; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.11; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; }; name = Debug; }; @@ -655,8 +655,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D7A1FBF12D2002CDC28 /* Release.xcconfig */; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.11; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; }; name = Release; }; @@ -668,6 +668,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; + TVOS_DEPLOYMENT_TARGET = 13.0; }; name = Debug; }; @@ -679,6 +680,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; + TVOS_DEPLOYMENT_TARGET = 13.0; }; name = Release; }; @@ -691,6 +693,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.0; }; name = Debug; }; @@ -703,6 +706,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.0; VALIDATE_PRODUCT = YES; }; name = Release; From 6d87c87e4e090c95406d374f7165b38864e48f6b Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 7 Mar 2022 23:17:19 +1100 Subject: [PATCH 16/33] Made the deployment targets more consistent. --- Package@swift-5.1.swift | 2 +- UXKit.xcodeproj/project.pbxproj | 4 ++++ xcconfig/AppKitBase.xcconfig | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Package@swift-5.1.swift b/Package@swift-5.1.swift index b4bd09a..f3e84e7 100644 --- a/Package@swift-5.1.swift +++ b/Package@swift-5.1.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "UXKit", platforms: [ - .macOS(.v10_13), .iOS(.v10) + .macOS(.v10_13), .iOS(.v10), .tvOS(.v13) ], products: [ .library(name: "UXKit", targets: ["UXKit"]), diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index 3748e14..54faded 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -648,6 +648,8 @@ buildSettings = { IPHONEOS_DEPLOYMENT_TARGET = 10.0; MACOSX_DEPLOYMENT_TARGET = 10.13; + TVOS_DEPLOYMENT_TARGET = 13.0; + WATCHOS_DEPLOYMENT_TARGET = 8.3; }; name = Debug; }; @@ -657,6 +659,8 @@ buildSettings = { IPHONEOS_DEPLOYMENT_TARGET = 10.0; MACOSX_DEPLOYMENT_TARGET = 10.13; + TVOS_DEPLOYMENT_TARGET = 13.0; + WATCHOS_DEPLOYMENT_TARGET = 8.3; }; name = Release; }; diff --git a/xcconfig/AppKitBase.xcconfig b/xcconfig/AppKitBase.xcconfig index 3bc31b5..405258b 100644 --- a/xcconfig/AppKitBase.xcconfig +++ b/xcconfig/AppKitBase.xcconfig @@ -8,7 +8,7 @@ // MACOSX_DEPLOYMENT_TARGET[config=Debug-AppKit] = 10.11 // is this OK? the SDKROOT is already being used in Base.xcconfig -MACOSX_DEPLOYMENT_TARGET = 10.12 +MACOSX_DEPLOYMENT_TARGET = 10.13 // SDKROOT = macosx10.11 SDKROOT = macosx From 2fbb22c451d8a8b651e12f767d7389b3c6cc1a9c Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 7 Mar 2022 23:49:21 +1100 Subject: [PATCH 17/33] Made the tvOS target have the same module name as the others. --- Sources/UXKit/UIKit/UXGraphics-UIKit.swift | 11 +++++++++-- UXKit.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Sources/UXKit/UIKit/UXGraphics-UIKit.swift b/Sources/UXKit/UIKit/UXGraphics-UIKit.swift index b0c091c..c5a9f7f 100644 --- a/Sources/UXKit/UIKit/UXGraphics-UIKit.swift +++ b/Sources/UXKit/UIKit/UXGraphics-UIKit.swift @@ -36,8 +36,15 @@ /// macOS compat, using `.label` on iOS 13+ (dynamic), `.black` before. @inlinable static var textColor : UXColor { - if #available(iOS 13, *) { return UXColor.label } - else { return UXColor.black } +#if os(iOS) + if #available(iOS 13, *) { return UXColor.label } + else { return UXColor.black } +#endif + +#if os(tvOS) + if #available(tvOS 13, *) { return UXColor.label } + else { return UXColor.black } +#endif } } diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index 54faded..f12d3bc 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -548,6 +548,7 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; @@ -609,6 +610,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = "-ObjC"; + PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; From 3f7d56fc9ba17fa9f7061751f5fba43b215b70c1 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 7 Mar 2022 23:59:24 +1100 Subject: [PATCH 18/33] More tvOS specificity. --- Sources/UXKit/UIKit/UXFont-UIKit.swift | 28 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Sources/UXKit/UIKit/UXFont-UIKit.swift b/Sources/UXKit/UIKit/UXFont-UIKit.swift index 65bb73c..e1e5acd 100644 --- a/Sources/UXKit/UIKit/UXFont-UIKit.swift +++ b/Sources/UXKit/UIKit/UXFont-UIKit.swift @@ -20,17 +20,23 @@ * `UIFont.monospacedSystemFont()`. */ static func userFixedPitchFont(ofSize size: CGFloat) -> UXFont? { - if #available(iOS 13.0, *) { - #if true - return UIFont(name: "Menlo-Regular", size: size) - ?? UIFont.monospacedSystemFont(ofSize: size, weight: .regular) - #else - return UIFont.monospacedSystemFont(ofSize: size, weight: .regular) - #endif - } - else { - return nil - } +#if os(iOS) + if #available(iOS 13.0, *) { + return UIFont(name: "Menlo-Regular", size: size) + ?? UIFont.monospacedSystemFont(ofSize: size, weight: .regular) + } else { + return UIFont.monospacedSystemFont(ofSize: size, weight: .regular) + } +#endif + +#if os(tvOS) + if #available(tvOS 13.0, *) { + return UIFont(name: "Menlo-Regular", size: size) + ?? UIFont.monospacedSystemFont(ofSize: size, weight: .regular) + } else { + return UIFont.monospacedSystemFont(ofSize: size, weight: .regular) + } +#endif } } #endif // !os(macOS) From b5a1ba45d3108fa608b7e42baee0ee4100da88a2 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 8 Mar 2022 09:24:05 +1100 Subject: [PATCH 19/33] iOS needs 13.0 for several of the classes. --- Package.swift | 22 +++++++++++++--------- UXKit.xcodeproj/project.pbxproj | 6 ++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Package.swift b/Package.swift index eb1eb6a..8adcc50 100644 --- a/Package.swift +++ b/Package.swift @@ -1,13 +1,17 @@ import PackageDescription let package = Package( - name: "UXKit", - - exclude: [ - "UXKit.xcodeproj", - "GNUmakefile", - "LICENSE", - "README.md", - "xcconfig" - ] + name: "UXKit", + + platforms: [ + .macOS(.v10_13), .iOS(.v10), .tvOS(.v13) + ], + + exclude: [ + "UXKit.xcodeproj", + "GNUmakefile", + "LICENSE", + "README.md", + "xcconfig" + ] ) diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index f12d3bc..f5097ee 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -544,6 +544,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -607,6 +608,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = "-ObjC"; @@ -671,6 +673,7 @@ baseConfigurationReference = E8460D781FBF12D2002CDC28 /* MacStaticLib.xcconfig */; buildSettings = { EXECUTABLE_PREFIX = lib; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -683,6 +686,7 @@ baseConfigurationReference = E8460D781FBF12D2002CDC28 /* MacStaticLib.xcconfig */; buildSettings = { EXECUTABLE_PREFIX = lib; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -694,6 +698,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D791FBF12D2002CDC28 /* MobileStaticLib.xcconfig */; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 13.0; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -707,6 +712,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D791FBF12D2002CDC28 /* MobileStaticLib.xcconfig */; buildSettings = { + IPHONEOS_DEPLOYMENT_TARGET = 13.0; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; From 2909593f6d7fbb7e34fdaf0b0fdd5677127fa289 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 8 Mar 2022 09:26:25 +1100 Subject: [PATCH 20/33] Don't try to use tvOS 13 stuff when it's not available. --- Sources/UXKit/UIKit/UXFont-UIKit.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/UXKit/UIKit/UXFont-UIKit.swift b/Sources/UXKit/UIKit/UXFont-UIKit.swift index e1e5acd..28423dd 100644 --- a/Sources/UXKit/UIKit/UXFont-UIKit.swift +++ b/Sources/UXKit/UIKit/UXFont-UIKit.swift @@ -34,7 +34,7 @@ return UIFont(name: "Menlo-Regular", size: size) ?? UIFont.monospacedSystemFont(ofSize: size, weight: .regular) } else { - return UIFont.monospacedSystemFont(ofSize: size, weight: .regular) + return UIFont(name: "Menlo-Regular", size: size) } #endif } From 1f59f5df605050165b8a1e143bd4c7283cc91da8 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 8 Mar 2022 10:00:33 +1100 Subject: [PATCH 21/33] Don't try to use iOS 13 stuff when it's not available. --- Sources/UXKit/UIKit/UXFont-UIKit.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/UXKit/UIKit/UXFont-UIKit.swift b/Sources/UXKit/UIKit/UXFont-UIKit.swift index 28423dd..7bb79c2 100644 --- a/Sources/UXKit/UIKit/UXFont-UIKit.swift +++ b/Sources/UXKit/UIKit/UXFont-UIKit.swift @@ -25,7 +25,7 @@ return UIFont(name: "Menlo-Regular", size: size) ?? UIFont.monospacedSystemFont(ofSize: size, weight: .regular) } else { - return UIFont.monospacedSystemFont(ofSize: size, weight: .regular) + return UIFont(name: "Menlo-Regular", size: size) } #endif From 0602b785cdfcced0a6143ad365e76ef285e899b9 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 8 Mar 2022 11:20:40 +1100 Subject: [PATCH 22/33] Moving back to iOS and tvOS 12.4 --- Package.swift | 2 +- Package@swift-5.1.swift | 2 +- Package@swift-5.swift | 2 +- UXKit.xcodeproj/project.pbxproj | 28 ++++++++++++++-------------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Package.swift b/Package.swift index 8adcc50..80514ac 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ let package = Package( name: "UXKit", platforms: [ - .macOS(.v10_13), .iOS(.v10), .tvOS(.v13) + .macOS(.v10_13), .iOS(.v12), .tvOS(.v12) ], exclude: [ diff --git a/Package@swift-5.1.swift b/Package@swift-5.1.swift index f3e84e7..52329a8 100644 --- a/Package@swift-5.1.swift +++ b/Package@swift-5.1.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "UXKit", platforms: [ - .macOS(.v10_13), .iOS(.v10), .tvOS(.v13) + .macOS(.v10_13), .iOS(.v12), .tvOS(.v12) ], products: [ .library(name: "UXKit", targets: ["UXKit"]), diff --git a/Package@swift-5.swift b/Package@swift-5.swift index a3fa59c..b0cbcdc 100644 --- a/Package@swift-5.swift +++ b/Package@swift-5.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "UXKit", platforms: [ - .macOS(.v10_13), .iOS(.v10) + .macOS(.v10_13), .iOS(.v12), .tvOS(.v12) ], products: [ .library(name: "UXKit", targets: ["UXKit"]), diff --git a/UXKit.xcodeproj/project.pbxproj b/UXKit.xcodeproj/project.pbxproj index f5097ee..cf85f9e 100644 --- a/UXKit.xcodeproj/project.pbxproj +++ b/UXKit.xcodeproj/project.pbxproj @@ -544,7 +544,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -557,7 +557,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -608,7 +608,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_LDFLAGS = "-ObjC"; @@ -619,7 +619,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 12.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -650,7 +650,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D771FBF12D2002CDC28 /* Debug.xcconfig */; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; MACOSX_DEPLOYMENT_TARGET = 10.13; TVOS_DEPLOYMENT_TARGET = 13.0; WATCHOS_DEPLOYMENT_TARGET = 8.3; @@ -661,7 +661,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D7A1FBF12D2002CDC28 /* Release.xcconfig */; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; MACOSX_DEPLOYMENT_TARGET = 10.13; TVOS_DEPLOYMENT_TARGET = 13.0; WATCHOS_DEPLOYMENT_TARGET = 8.3; @@ -673,11 +673,11 @@ baseConfigurationReference = E8460D781FBF12D2002CDC28 /* MacStaticLib.xcconfig */; buildSettings = { EXECUTABLE_PREFIX = lib; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -686,11 +686,11 @@ baseConfigurationReference = E8460D781FBF12D2002CDC28 /* MacStaticLib.xcconfig */; buildSettings = { EXECUTABLE_PREFIX = lib; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; @@ -698,13 +698,13 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D791FBF12D2002CDC28 /* MobileStaticLib.xcconfig */; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -712,13 +712,13 @@ isa = XCBuildConfiguration; baseConfigurationReference = E8460D791FBF12D2002CDC28 /* MobileStaticLib.xcconfig */; buildSettings = { - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; PRODUCT_MODULE_NAME = UXKit; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = "1,2"; - TVOS_DEPLOYMENT_TARGET = 13.0; + TVOS_DEPLOYMENT_TARGET = 12.0; VALIDATE_PRODUCT = YES; }; name = Release; From f5e196931ff87a26756633cf771d95c4363ca794 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 30 May 2023 13:01:41 +1000 Subject: [PATCH 23/33] Move to iOS/tvOS v13 --- Package.swift | 2 +- Package@swift-5.1.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 80514ac..b23f2aa 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ let package = Package( name: "UXKit", platforms: [ - .macOS(.v10_13), .iOS(.v12), .tvOS(.v12) + .macOS(.v10_15), .iOS(.v13), .tvOS(.v13) ], exclude: [ diff --git a/Package@swift-5.1.swift b/Package@swift-5.1.swift index 52329a8..da64e0a 100644 --- a/Package@swift-5.1.swift +++ b/Package@swift-5.1.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "UXKit", platforms: [ - .macOS(.v10_13), .iOS(.v12), .tvOS(.v12) + .macOS(.v10_13), .iOS(.v13), .tvOS(.v13) ], products: [ .library(name: "UXKit", targets: ["UXKit"]), From 14caee456a673b043be5b083e2b8283a9184718b Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Wed, 13 Sep 2023 08:37:20 +1000 Subject: [PATCH 24/33] Added a little more to UXViewController in support of GameKit --- Sources/UXKit/AppKit/UXGraphics-AppKit.swift | 108 +++++++++---------- Sources/UXKit/Common/UXViewController.swift | 10 ++ 2 files changed, 63 insertions(+), 55 deletions(-) diff --git a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift index f0a3770..edab591 100644 --- a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift +++ b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift @@ -4,53 +4,53 @@ // Copyright © 2016-2019 ZeeZide GmbH. All rights reserved. // #if os(macOS) - import Cocoa - - public typealias UXFloat = CGFloat - - public typealias UXColor = NSColor - - public typealias UXBezierPath = NSBezierPath - public typealias UXRect = NSRect - public typealias UXPoint = NSPoint - public typealias UXSize = NSSize - public let UXEdgeInsetsMake = NSEdgeInsetsMake +import Cocoa - public typealias UXImage = NSImage - public typealias UXEvent = NSEvent - public typealias UXTouch = NSTouch +public typealias UXFloat = CGFloat - #if swift(>=4.0) - public typealias UXEdgeInsets = NSEdgeInsets - #else - public typealias UXEdgeInsets = EdgeInsets - #endif +public typealias UXColor = NSColor - public extension CGColor { +public typealias UXBezierPath = NSBezierPath +public typealias UXRect = NSRect +public typealias UXPoint = NSPoint +public typealias UXSize = NSSize +public let UXEdgeInsetsMake = NSEdgeInsetsMake + +public typealias UXImage = NSImage +public typealias UXEvent = NSEvent +public typealias UXTouch = NSTouch + +#if swift(>=4.0) +public typealias UXEdgeInsets = NSEdgeInsets +#else +public typealias UXEdgeInsets = EdgeInsets +#endif + +public extension CGColor { // macOS has no CGColor(gray:alpha:) static func new(gray: CGFloat, alpha: CGFloat) -> CGColor { - return CGColor(gray: gray, alpha: alpha) + return CGColor(gray: gray, alpha: alpha) } - } - - public extension NSRect { +} + +public extension NSRect { func inset(by insets: NSEdgeInsets) -> NSRect { - var newRect = self - newRect.origin.x += insets.left - newRect.origin.y += insets.bottom - newRect.size.width -= (insets.left + insets.right) - newRect.size.height -= (insets.top + insets.bottom) - return newRect + var newRect = self + newRect.origin.x += insets.left + newRect.origin.y += insets.bottom + newRect.size.width -= (insets.left + insets.right) + newRect.size.height -= (insets.top + insets.bottom) + return newRect } - } +} - public extension UXImage { +public extension UXImage { static var applicationIconImage: UXImage? { - return UXImage(named: NSImage.applicationIconName) + return UXImage(named: NSImage.applicationIconName) } /// Returns an image of the specified size, allowing the caller to provide a draw function that draws into the "current" graphics context. @@ -69,10 +69,10 @@ NSGraphicsContext.restoreGraphicsState() } - + return resultImage } - } +} import class SpriteKit.SKScene @@ -90,33 +90,31 @@ public extension NSBezierPath { var cgPath: CGPath { let path = CGMutablePath() let points = UnsafeMutablePointer.allocate(capacity: 3) - + if elementCount > 0 { -// var didClosePath = true - for index in 0.. Void)? = nil) { + self.dismiss(self) + } + + func present(_ viewControllerToPresent: UXViewController, animated flag: Bool, completion: (() -> Void)? = nil) { + self.presentAsModalWindow(viewControllerToPresent) + } +#endif } From a5c279cafd54f0363254e02e91c13d701a2b257d Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Wed, 13 Sep 2023 08:42:39 +1000 Subject: [PATCH 25/33] New functions need to be public. --- Sources/UXKit/Common/UXViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/UXKit/Common/UXViewController.swift b/Sources/UXKit/Common/UXViewController.swift index 4cb931e..f608550 100644 --- a/Sources/UXKit/Common/UXViewController.swift +++ b/Sources/UXKit/Common/UXViewController.swift @@ -81,11 +81,11 @@ public var uxParent : UXViewController? { return parent } #if os(macOS) - func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + public func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { self.dismiss(self) } - func present(_ viewControllerToPresent: UXViewController, animated flag: Bool, completion: (() -> Void)? = nil) { + public func present(_ viewControllerToPresent: UXViewController, animated flag: Bool, completion: (() -> Void)? = nil) { self.presentAsModalWindow(viewControllerToPresent) } #endif From 29078de275fa3f9a680bd1fd437bcc9370e424a3 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 5 Feb 2024 23:22:35 +1100 Subject: [PATCH 26/33] Added UXBezierPath.addCurve so that macOS can use the same API as iOS/tvOS/visionOS. --- Sources/UXKit/AppKit/UXGraphics-AppKit.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift index cea2355..4226f66 100644 --- a/Sources/UXKit/AppKit/UXGraphics-AppKit.swift +++ b/Sources/UXKit/AppKit/UXGraphics-AppKit.swift @@ -138,5 +138,10 @@ public extension NSBezierPath { let macTransform = AffineTransform(m11: transform.a, m12: transform.b, m21: transform.c, m22: transform.d, tX: transform.tx, tY: transform.ty) self.transform(using: macTransform) } + + // this provides parity with all but macOS. + func addCurve(to: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint) { + curve(to: to, controlPoint1: controlPoint1, controlPoint2: controlPoint2) + } } #endif // os(macOS) From 95d8a430d3a3cdf704b4317cebca7fe20163ef97 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 1 Apr 2024 19:45:40 +1100 Subject: [PATCH 27/33] Accessibility notifications need sometimes to provide a userInfo dictionary. --- Sources/UXKit/AppKit/UXView-AppKit.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index b97030e..666fc11 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -273,7 +273,19 @@ public extension UXAccessibility { static func post(notification: UXAccessibility.Notification, argument: Any?) { if let arg = argument { - self.post(element: arg, notification: notification) + if notification == .layoutChanged { + let userInfo = [kAXUIElementsKey : arg] + + self.post(element: userInfo, notification: notification) + } else if notification == .announcementRequested { + let userInfo = [kAXAnnouncementKey : arg, kAXPriorityKey : NSAccessibilityPriorityLevel.medium] + + self.post(element: userInfo, notification: notification) + } else { + let userInfo = [kAXUIElementsKey : arg] + + self.post(element: userInfo, notification: notification) + } } else { self.post(element: self, notification: notification) } From a885abbe3cfde989c737339ce7301bc877673338 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 2 Apr 2024 19:27:22 +1100 Subject: [PATCH 28/33] Attempt to improve the notifications sent for accessibility. --- Sources/UXKit/AppKit/UXView-AppKit.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index 666fc11..cc5b3a4 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -273,16 +273,18 @@ public extension UXAccessibility { static func post(notification: UXAccessibility.Notification, argument: Any?) { if let arg = argument { - if notification == .layoutChanged { - let userInfo = [kAXUIElementsKey : arg] - - self.post(element: userInfo, notification: notification) - } else if notification == .announcementRequested { + if notification == .announcementRequested { let userInfo = [kAXAnnouncementKey : arg, kAXPriorityKey : NSAccessibilityPriorityLevel.medium] self.post(element: userInfo, notification: notification) } else { - let userInfo = [kAXUIElementsKey : arg] + let userInfo : [ String : Any ] + + if let children = argument as? Array { + userInfo = [kAXUIElementsKey : children] + } else { + userInfo = [kAXUIElementsKey : [arg]] + } self.post(element: userInfo, notification: notification) } From 24127b92593cead42752e11eba75de12492586d9 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Wed, 3 Apr 2024 08:49:19 +1100 Subject: [PATCH 29/33] Another attempt to get macOS accessibility notifications to actually do what is advertised. --- Sources/UXKit/AppKit/UXView-AppKit.swift | 35 ++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index cc5b3a4..846d3ea 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -270,26 +270,27 @@ open class VerticallyCenteredTextFieldCell: NSTextFieldCell { } public extension UXAccessibility { + static func post(notification: UXAccessibility.Notification, - argument: Any?) { - if let arg = argument { - if notification == .announcementRequested { - let userInfo = [kAXAnnouncementKey : arg, kAXPriorityKey : NSAccessibilityPriorityLevel.medium] - - self.post(element: userInfo, notification: notification) - } else { - let userInfo : [ String : Any ] - - if let children = argument as? Array { - userInfo = [kAXUIElementsKey : children] - } else { - userInfo = [kAXUIElementsKey : [arg]] - } - - self.post(element: userInfo, notification: notification) + argument: Any) { + if notification == .announcementRequested { + let userInfo = [ NSAccessibility.NotificationUserInfoKey.announcement : argument, NSAccessibility.NotificationUserInfoKey.priority : NSAccessibilityPriorityLevel.medium] + + if let window = NSApplication.shared.mainWindow { + NSAccessibility.post(element: window, notification: notification, userInfo: userInfo) } } else { - self.post(element: self, notification: notification) + let userInfo : [ NSAccessibility.NotificationUserInfoKey : Any ] + + if let children = argument as? Array { + userInfo = [.uiElements : children] + } else { + userInfo = [.uiElements : [argument]] + } + + if let window = NSApplication.shared.mainWindow { + NSAccessibility.post(element: window, notification: notification, userInfo: userInfo) + } } } } From 2bd004ba6f3873160572bda9a27cc031b727cfa3 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Thu, 4 Apr 2024 13:01:11 +1100 Subject: [PATCH 30/33] Provide a UIKit equivalent for the AppKit announcementRequested. --- Sources/UXKit/UIKit/UXView-UIKit.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/UXKit/UIKit/UXView-UIKit.swift b/Sources/UXKit/UIKit/UXView-UIKit.swift index 486aa3a..3a2a2a6 100644 --- a/Sources/UXKit/UIKit/UXView-UIKit.swift +++ b/Sources/UXKit/UIKit/UXView-UIKit.swift @@ -44,6 +44,7 @@ public extension UXAccessibility.Notification { static let valueChanged: UXAccessibility.Notification = .layoutChanged + static let announcementRequested: UXAccessibility.Notification = .announcement } // MARK: - NSView Compatibility From f4bc84abe5bc5c17fbaf9125f79b7a73428a966b Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 9 Apr 2024 17:18:40 +1000 Subject: [PATCH 31/33] Added accessibilitySpeechLanguage to appkit so that there is alignment for this between iOS and macOS. --- Sources/UXKit/UIKit/UXView-UIKit.swift | 148 +++++++++++++------------ 1 file changed, 77 insertions(+), 71 deletions(-) diff --git a/Sources/UXKit/UIKit/UXView-UIKit.swift b/Sources/UXKit/UIKit/UXView-UIKit.swift index 3a2a2a6..662be62 100644 --- a/Sources/UXKit/UIKit/UXView-UIKit.swift +++ b/Sources/UXKit/UIKit/UXView-UIKit.swift @@ -4,103 +4,109 @@ // Copyright © 2016-2021 ZeeZide GmbH. All rights reserved. // #if !os(macOS) - import UIKit +import UIKit - public typealias UXWindow = UIWindow - public typealias UXView = UIView - public typealias UXResponder = UIResponder - public typealias UXControl = UIControl - public typealias UXLabel = UILabel - public typealias UXTextField = UITextField - public typealias UXSecureTextField = UITextField - public typealias UXScrollView = UIScrollView - public typealias UXCollectionView = UICollectionView - public typealias UXSearchField = UISearchBar // TBD - public typealias UXSpinner = UIActivityIndicatorView - public typealias UXProgressBar = UIProgressView - public typealias UXButton = UIButton - public typealias UXTextView = UITextView - public typealias UXTextViewDelegate = UITextViewDelegate - // public typealias UXPopUp = TODO - public typealias UXStackView = UIStackView - public typealias UXImageView = UIImageView - #if !os(tvOS) - public typealias UXCheckBox = UISwitch - public typealias UXSlider = UISlider - #endif - - public typealias UXAccessibility = UIAccessibility - public typealias UXAccessibilityElement = UIAccessibilityElement - public typealias UXTextFieldDelegate = UITextFieldDelegate - - // MARK: - UXUserInterfaceItemIdentification - - public typealias UXUserInterfaceItemIdentifier = String - // FIXME: maybe we should change it to the new Raw type in AppKit Swift 4 - - @objc public protocol UXUserInterfaceItemIdentification { +public typealias UXWindow = UIWindow +public typealias UXView = UIView +public typealias UXResponder = UIResponder +public typealias UXControl = UIControl +public typealias UXLabel = UILabel +public typealias UXTextField = UITextField +public typealias UXSecureTextField = UITextField +public typealias UXScrollView = UIScrollView +public typealias UXCollectionView = UICollectionView +public typealias UXSearchField = UISearchBar // TBD +public typealias UXSpinner = UIActivityIndicatorView +public typealias UXProgressBar = UIProgressView +public typealias UXButton = UIButton +public typealias UXTextView = UITextView +public typealias UXTextViewDelegate = UITextViewDelegate +// public typealias UXPopUp = TODO +public typealias UXStackView = UIStackView +public typealias UXImageView = UIImageView +#if !os(tvOS) +public typealias UXCheckBox = UISwitch +public typealias UXSlider = UISlider +#endif + +public typealias UXAccessibility = UIAccessibility +public typealias UXAccessibilityElement = UIAccessibilityElement +public typealias UXTextFieldDelegate = UITextFieldDelegate + +// MARK: - UXUserInterfaceItemIdentification + +public typealias UXUserInterfaceItemIdentifier = String +// FIXME: maybe we should change it to the new Raw type in AppKit Swift 4 + +@objc public protocol UXUserInterfaceItemIdentification { var identifier: UXUserInterfaceItemIdentifier? { get set } - } - - public extension UXAccessibility.Notification { - static let valueChanged: UXAccessibility.Notification = .layoutChanged - static let announcementRequested: UXAccessibility.Notification = .announcement - } +} + +public extension UXAccessibility.Notification { + static let valueChanged: UXAccessibility.Notification = .layoutChanged + static let announcementRequested: UXAccessibility.Notification = .announcement +} - // MARK: - NSView Compatibility - - public extension UIActivityIndicatorView { +public extension NSAttributedString.Key { + static let accessibilitySpeechLanguage : NSAttributedString.Key = .accessibilitySpeechLanguage + +} + + +// MARK: - NSView Compatibility + +public extension UIActivityIndicatorView { /// Use this instead of `start[stop]Animating` for AppKit compatibility. var isSpinning : Bool { - set { - guard newValue != isAnimating else { return } - if newValue { startAnimating() } - else { stopAnimating() } - } - get { - return isAnimating - } + set { + guard newValue != isAnimating else { return } + if newValue { startAnimating() } + else { stopAnimating() } + } + get { + return isAnimating + } } /// AppKit compatibility. Prefer: `hidesWhenStopped`. /// Note: It's the other way around! var isDisplayedWhenStopped : Bool { - set { hidesWhenStopped = !newValue } - get { return !hidesWhenStopped } + set { hidesWhenStopped = !newValue } + get { return !hidesWhenStopped } } - } - - public extension UIButton { +} + +public extension UIButton { var title : String? { - set { setTitle(newValue, for: .normal) } - get { return title(for: .normal) } + set { setTitle(newValue, for: .normal) } + get { return title(for: .normal) } } - } +} #if !os(tvOS) - public extension UISlider { +public extension UISlider { // UISlider base value is a `Float` var intValue : Int32 { // yeah, it is Int32 in Cocoa :-) - set { value = Float(newValue) } - get { return Int32(value) } + set { value = Float(newValue) } + get { return Int32(value) } } - } +} #endif - public extension UXView { +public extension UXView { func inOwnContext(execute: () -> ()) { - let context = UIGraphicsGetCurrentContext() - context?.saveGState() - defer { context?.restoreGState() } - - execute() + let context = UIGraphicsGetCurrentContext() + context?.saveGState() + defer { context?.restoreGState() } + + execute() } - - } + +} #endif // !os(macOS) From fe29d16d5bf574153db085a042f54db2d10ab7af Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Tue, 9 Apr 2024 17:22:42 +1000 Subject: [PATCH 32/33] Moved the accessibilitySpeechLanguage to the right place. --- Sources/UXKit/AppKit/UXView-AppKit.swift | 6 ++++++ Sources/UXKit/UIKit/UXView-UIKit.swift | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index 846d3ea..cba2016 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -294,4 +294,10 @@ public extension UXAccessibility { } } } + +public extension NSAttributedString.Key { + static let accessibilitySpeechLanguage : NSAttributedString.Key = .accessibilityLanguage + +} + #endif // os(macOS) diff --git a/Sources/UXKit/UIKit/UXView-UIKit.swift b/Sources/UXKit/UIKit/UXView-UIKit.swift index 662be62..d8e0519 100644 --- a/Sources/UXKit/UIKit/UXView-UIKit.swift +++ b/Sources/UXKit/UIKit/UXView-UIKit.swift @@ -47,12 +47,6 @@ public extension UXAccessibility.Notification { static let announcementRequested: UXAccessibility.Notification = .announcement } -public extension NSAttributedString.Key { - static let accessibilitySpeechLanguage : NSAttributedString.Key = .accessibilitySpeechLanguage - -} - - // MARK: - NSView Compatibility public extension UIActivityIndicatorView { From 77e0db4f4cc8f3435850319738c68a8f90733940 Mon Sep 17 00:00:00 2001 From: Peter Easdown Date: Mon, 22 Jul 2024 23:06:24 +1000 Subject: [PATCH 33/33] On macOS, force notifications to be posted on the main thread because they need access to the mainWindow. --- Sources/UXKit/AppKit/UXView-AppKit.swift | 34 +++++++++++++----------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Sources/UXKit/AppKit/UXView-AppKit.swift b/Sources/UXKit/AppKit/UXView-AppKit.swift index cba2016..bce29f1 100644 --- a/Sources/UXKit/AppKit/UXView-AppKit.swift +++ b/Sources/UXKit/AppKit/UXView-AppKit.swift @@ -273,23 +273,25 @@ public extension UXAccessibility { static func post(notification: UXAccessibility.Notification, argument: Any) { - if notification == .announcementRequested { - let userInfo = [ NSAccessibility.NotificationUserInfoKey.announcement : argument, NSAccessibility.NotificationUserInfoKey.priority : NSAccessibilityPriorityLevel.medium] - - if let window = NSApplication.shared.mainWindow { - NSAccessibility.post(element: window, notification: notification, userInfo: userInfo) - } - } else { - let userInfo : [ NSAccessibility.NotificationUserInfoKey : Any ] - - if let children = argument as? Array { - userInfo = [.uiElements : children] + DispatchQueue.main.async { + if notification == .announcementRequested { + let userInfo = [ NSAccessibility.NotificationUserInfoKey.announcement : argument, NSAccessibility.NotificationUserInfoKey.priority : NSAccessibilityPriorityLevel.medium] + + if let window = NSApplication.shared.mainWindow { + NSAccessibility.post(element: window, notification: notification, userInfo: userInfo) + } } else { - userInfo = [.uiElements : [argument]] - } - - if let window = NSApplication.shared.mainWindow { - NSAccessibility.post(element: window, notification: notification, userInfo: userInfo) + let userInfo : [ NSAccessibility.NotificationUserInfoKey : Any ] + + if let children = argument as? Array { + userInfo = [.uiElements : children] + } else { + userInfo = [.uiElements : [argument]] + } + + if let window = NSApplication.shared.mainWindow { + NSAccessibility.post(element: window, notification: notification, userInfo: userInfo) + } } } }