From f0e273fff22e69d19973e51f9969b0e5e68330a4 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Tue, 11 Feb 2020 13:04:19 -0800 Subject: [PATCH 01/17] [BIG] Add macOS cross-platform compatibility, macOS demo application + UI unit tests, & other unit tests. --- .../FontAwesomeDemo (macOS)/AppDelegate.swift | 36 + .../AppIcon.appiconset/Contents.json | 58 ++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/Main.storyboard | 931 ++++++++++++++++++ .../Extensions/NSToolbarItem.Identifier.swift | 17 + .../FontAwesomeDemo__macOS_.entitlements | 10 + Demo/FontAwesomeDemo (macOS)/Info.plist | 34 + .../ViewController.swift | 50 + .../FontAwesomeDemo.xcodeproj/project.pbxproj | 376 ++++++- .../Extensions/NSButton.BezelStyle.swift | 45 + .../Extensions/NSToolbar.SizeMode.swift | 39 + .../FontAwesomeMacOSDemoUnitTests.swift | 312 ++++++ .../Info.plist | 22 + FontAwesome.xcodeproj/project.pbxproj | 315 +++++- .../xcschemes/FontAwesome-macOS.xcscheme | 77 ++ .../xcschemes/FontAwesome-tvOS.xcscheme | 6 +- .../xcschemes/FontAwesome-watchOS.xcscheme | 6 +- .../xcschemes/FontAwesome.xcscheme | 24 +- .../AppKit/Cells/FontAwesomeButtonCell.swift | 123 +++ .../Enumerations/FontAwesomeIconSize.swift | 44 + FontAwesome/AppKit/Extensions/CGFloat.swift | 45 + FontAwesome/AppKit/Extensions/Font.swift | 57 ++ .../AppKit/Extensions/Image+Trim.swift | 156 +++ .../Extensions/NSButton.BezelStyle.swift | 55 ++ FontAwesome/AppKit/Extensions/NSSize.swift | 35 + FontAwesome/AppKit/FontAwesomeButton.swift | 263 +++++ FontAwesome/AppKit/FontAwesomeLabel.swift | 119 +++ .../AppKit/FontAwesomeLayoutManager.swift | 127 +++ FontAwesome/AppKit/FontAwesomeToolbar.swift | 37 + .../AppKit/FontAwesomeToolbarItem.swift | 265 +++++ .../FontAwesomeRawRepresentable.swift | 48 + .../Protocols/FontAwesomeRepresentable.swift | 68 ++ FontAwesome/FontAwesome.h | 2 +- FontAwesome/FontAwesome.swift | 105 +- .../FontAwesomeImageRepresentable.swift | 26 +- FontAwesome/FontAwesomeImageView.swift | 91 +- .../FontAwesomeTextRepresentable.swift | 57 ++ FontAwesome/FontAwesomeView.swift | 56 +- FontAwesome/Shims.swift | 56 ++ .../FontAwesomeBarButtonItem.swift | 0 .../FontAwesomeSegmentedControl.swift | 0 .../FontAwesomeStateRequirement.swift | 0 .../{ => UIKit}/FontAwesomeTabBarItem.swift | 0 FontAwesomeTests/FontAwesomeTests.swift | 67 +- 44 files changed, 4154 insertions(+), 112 deletions(-) create mode 100644 Demo/FontAwesomeDemo (macOS)/AppDelegate.swift create mode 100644 Demo/FontAwesomeDemo (macOS)/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Demo/FontAwesomeDemo (macOS)/Assets.xcassets/Contents.json create mode 100644 Demo/FontAwesomeDemo (macOS)/Base.lproj/Main.storyboard create mode 100644 Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift create mode 100644 Demo/FontAwesomeDemo (macOS)/FontAwesomeDemo__macOS_.entitlements create mode 100644 Demo/FontAwesomeDemo (macOS)/Info.plist create mode 100644 Demo/FontAwesomeDemo (macOS)/ViewController.swift create mode 100644 Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift create mode 100644 Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSToolbar.SizeMode.swift create mode 100644 Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift create mode 100644 Demo/FontAwesomeDemoUnitTests (macOS)/Info.plist create mode 100644 FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-macOS.xcscheme create mode 100644 FontAwesome/AppKit/Cells/FontAwesomeButtonCell.swift create mode 100644 FontAwesome/AppKit/Enumerations/FontAwesomeIconSize.swift create mode 100644 FontAwesome/AppKit/Extensions/CGFloat.swift create mode 100644 FontAwesome/AppKit/Extensions/Font.swift create mode 100644 FontAwesome/AppKit/Extensions/Image+Trim.swift create mode 100644 FontAwesome/AppKit/Extensions/NSButton.BezelStyle.swift create mode 100644 FontAwesome/AppKit/Extensions/NSSize.swift create mode 100644 FontAwesome/AppKit/FontAwesomeButton.swift create mode 100644 FontAwesome/AppKit/FontAwesomeLabel.swift create mode 100644 FontAwesome/AppKit/FontAwesomeLayoutManager.swift create mode 100644 FontAwesome/AppKit/FontAwesomeToolbar.swift create mode 100644 FontAwesome/AppKit/FontAwesomeToolbarItem.swift create mode 100644 FontAwesome/AppKit/Protocols/FontAwesomeRawRepresentable.swift create mode 100644 FontAwesome/AppKit/Protocols/FontAwesomeRepresentable.swift create mode 100644 FontAwesome/Shims.swift rename FontAwesome/{ => UIKit}/FontAwesomeBarButtonItem.swift (100%) rename FontAwesome/{ => UIKit}/FontAwesomeSegmentedControl.swift (100%) rename FontAwesome/{ => UIKit}/FontAwesomeStateRequirement.swift (100%) rename FontAwesome/{ => UIKit}/FontAwesomeTabBarItem.swift (100%) diff --git a/Demo/FontAwesomeDemo (macOS)/AppDelegate.swift b/Demo/FontAwesomeDemo (macOS)/AppDelegate.swift new file mode 100644 index 00000000..ce31b50f --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/AppDelegate.swift @@ -0,0 +1,36 @@ +// AppDelegate.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Cocoa + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate { + + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Insert code here to initialize your application + } + + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } + +} diff --git a/Demo/FontAwesomeDemo (macOS)/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demo/FontAwesomeDemo (macOS)/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..2db2b1c7 --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demo/FontAwesomeDemo (macOS)/Assets.xcassets/Contents.json b/Demo/FontAwesomeDemo (macOS)/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demo/FontAwesomeDemo (macOS)/Base.lproj/Main.storyboard b/Demo/FontAwesomeDemo (macOS)/Base.lproj/Main.storyboard new file mode 100644 index 00000000..81a666c7 --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/Base.lproj/Main.storyboard @@ -0,0 +1,931 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift b/Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift new file mode 100644 index 00000000..474afe8b --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift @@ -0,0 +1,17 @@ +// +// NSToolbarItem.Identifier.swift +// FontAwesomeDemo (macOS) +// +// Created by Chris Zielinski on 1/14/20. +// + +import AppKit + +extension NSToolbarItem.Identifier { + + static let defaultWidth = NSToolbarItem.Identifier("default-width") + static let fittedWidth = NSToolbarItem.Identifier("fitted-width") + static let fixedWidth = NSToolbarItem.Identifier("fixed-width") + static let fixedPointSize = NSToolbarItem.Identifier("fixed-point-size") + +} diff --git a/Demo/FontAwesomeDemo (macOS)/FontAwesomeDemo__macOS_.entitlements b/Demo/FontAwesomeDemo (macOS)/FontAwesomeDemo__macOS_.entitlements new file mode 100644 index 00000000..f2ef3ae0 --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/FontAwesomeDemo__macOS_.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/Demo/FontAwesomeDemo (macOS)/Info.plist b/Demo/FontAwesomeDemo (macOS)/Info.plist new file mode 100644 index 00000000..838bf648 --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + + diff --git a/Demo/FontAwesomeDemo (macOS)/ViewController.swift b/Demo/FontAwesomeDemo (macOS)/ViewController.swift new file mode 100644 index 00000000..a23360cd --- /dev/null +++ b/Demo/FontAwesomeDemo (macOS)/ViewController.swift @@ -0,0 +1,50 @@ +// ViewController.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Cocoa +import FontAwesome + +class ViewController: NSViewController { + + @IBOutlet + var fontAwesomeButton: FontAwesomeButton! + @IBOutlet + var fontAwesomeImageView: FontAwesomeImageView! + @IBOutlet + var fontAwesomeLabel: FontAwesomeLabel! + @IBOutlet + var fontAwesomeView: FontAwesomeView! + + override func viewDidLoad() { + super.viewDidLoad() + + // 🎡 Play around with the Font Awesome interface components. +// fontAwesomeView.icon = .ghost +// fontAwesomeView.style = .solid + } + + @IBAction + func toolbarItemAction(button: FontAwesomeButton) { + print("Clicked on the '\(button.toolbarItemIdentifier.rawValue)' toolbar item.") + } + +} diff --git a/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj b/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj index c12b7648..c5753c74 100644 --- a/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj +++ b/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj @@ -7,11 +7,21 @@ objects = { /* Begin PBXBuildFile section */ + 144D4A6523CEBD9D00D8FAA6 /* NSToolbarItem.Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 144D4A6423CEBD9D00D8FAA6 /* NSToolbarItem.Identifier.swift */; }; + 14578D292379464600304E74 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14578D282379464600304E74 /* AppDelegate.swift */; }; + 14578D2B2379464600304E74 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14578D2A2379464600304E74 /* ViewController.swift */; }; + 14578D2D2379464900304E74 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14578D2C2379464900304E74 /* Assets.xcassets */; }; + 14578D302379464900304E74 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 14578D2E2379464900304E74 /* Main.storyboard */; }; + 14578D3C237946CF00304E74 /* FontAwesome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14578D3A2379464900304E74 /* FontAwesome.framework */; }; + 14578D3D237946CF00304E74 /* FontAwesome.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 14578D3A2379464900304E74 /* FontAwesome.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 145AE9A023F1FD7C006803D5 /* NSButton.BezelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1466724D23EC04DC0000389E /* NSButton.BezelStyle.swift */; }; + 145AE9A223F1FDCD006803D5 /* NSToolbar.SizeMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 145AE9A123F1FDCD006803D5 /* NSToolbar.SizeMode.swift */; }; 8401A9541B398B5600269D14 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401A9531B398B5600269D14 /* AppDelegate.swift */; }; 8401A9561B398B5600269D14 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401A9551B398B5600269D14 /* ViewController.swift */; }; 8401A9591B398B5600269D14 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8401A9571B398B5600269D14 /* Main.storyboard */; }; 8401A95B1B398B5600269D14 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8401A95A1B398B5600269D14 /* Images.xcassets */; }; 8401A95E1B398B5600269D14 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8401A95C1B398B5600269D14 /* LaunchScreen.xib */; }; + 84344C6423CE9FFE00F2F6A6 /* FontAwesomeMacOSDemoUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84344C6323CE9FFE00F2F6A6 /* FontAwesomeMacOSDemoUnitTests.swift */; }; CF090C471FBB7A7200BB8B4C /* FontAwesome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841575D61B39AB71001092B6 /* FontAwesome.framework */; }; /* End PBXBuildFile section */ @@ -23,6 +33,20 @@ remoteGlobalIDString = 8401A8D11B39601400269D14; remoteInfo = FontAwesome; }; + 14578D392379464900304E74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 841575D01B39AB71001092B6 /* FontAwesome.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 144772A0236DF81D00597F27; + remoteInfo = "FontAwesome-macOS"; + }; + 14578D3F2379470200304E74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 841575D01B39AB71001092B6 /* FontAwesome.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 1447729F236DF81D00597F27; + remoteInfo = "FontAwesome-macOS"; + }; 841575D51B39AB71001092B6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 841575D01B39AB71001092B6 /* FontAwesome.xcodeproj */; @@ -37,6 +61,13 @@ remoteGlobalIDString = 8401A8DD1B39601400269D14; remoteInfo = FontAwesomeTests; }; + 84344C6623CE9FFE00F2F6A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8401A9461B398B5600269D14 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 14578D252379464600304E74; + remoteInfo = "FontAwesomeDemo (macOS)"; + }; 84669FA41F3D4C0A00EFCA99 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 841575D01B39AB71001092B6 /* FontAwesome.xcodeproj */; @@ -53,7 +84,31 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 14578D3E237946CF00304E74 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 14578D3D237946CF00304E74 /* FontAwesome.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 144D4A6423CEBD9D00D8FAA6 /* NSToolbarItem.Identifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSToolbarItem.Identifier.swift; sourceTree = ""; }; + 14578D262379464600304E74 /* FontAwesomeDemo (macOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "FontAwesomeDemo (macOS).app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 14578D282379464600304E74 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 14578D2A2379464600304E74 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 14578D2C2379464900304E74 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 14578D2F2379464900304E74 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 14578D312379464900304E74 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 14578D322379464900304E74 /* FontAwesomeDemo__macOS_.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FontAwesomeDemo__macOS_.entitlements; sourceTree = ""; }; + 145AE9A123F1FDCD006803D5 /* NSToolbar.SizeMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSToolbar.SizeMode.swift; sourceTree = ""; }; + 1466724D23EC04DC0000389E /* NSButton.BezelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSButton.BezelStyle.swift; sourceTree = ""; }; 8401A94E1B398B5600269D14 /* FontAwesomeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FontAwesomeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8401A9521B398B5600269D14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8401A9531B398B5600269D14 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -62,9 +117,20 @@ 8401A95A1B398B5600269D14 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 8401A95D1B398B5600269D14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 841575D01B39AB71001092B6 /* FontAwesome.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FontAwesome.xcodeproj; path = ../FontAwesome.xcodeproj; sourceTree = ""; }; + 84344C6123CE9FFE00F2F6A6 /* FontAwesomeDemoUnitTests (macOS).xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "FontAwesomeDemoUnitTests (macOS).xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 84344C6323CE9FFE00F2F6A6 /* FontAwesomeMacOSDemoUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeMacOSDemoUnitTests.swift; sourceTree = ""; }; + 84344C6523CE9FFE00F2F6A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 14578D232379464600304E74 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 14578D3C237946CF00304E74 /* FontAwesome.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8401A94B1B398B5600269D14 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -73,13 +139,61 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84344C5E23CE9FFE00F2F6A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 144D4A6923CEBDAB00D8FAA6 /* Extensions */ = { + isa = PBXGroup; + children = ( + 144D4A6423CEBD9D00D8FAA6 /* NSToolbarItem.Identifier.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 14578D272379464600304E74 /* FontAwesomeDemo (macOS) */ = { + isa = PBXGroup; + children = ( + 14578D282379464600304E74 /* AppDelegate.swift */, + 14578D2C2379464900304E74 /* Assets.xcassets */, + 144D4A6923CEBDAB00D8FAA6 /* Extensions */, + 14578D2E2379464900304E74 /* Main.storyboard */, + 14C445C323F2364200E438BC /* Supporting Files */, + 14578D2A2379464600304E74 /* ViewController.swift */, + ); + path = "FontAwesomeDemo (macOS)"; + sourceTree = ""; + }; + 1466724923EC04CE0000389E /* Extensions */ = { + isa = PBXGroup; + children = ( + 1466724D23EC04DC0000389E /* NSButton.BezelStyle.swift */, + 145AE9A123F1FDCD006803D5 /* NSToolbar.SizeMode.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 14C445C323F2364200E438BC /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 14578D322379464900304E74 /* FontAwesomeDemo__macOS_.entitlements */, + 14578D312379464900304E74 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; 8401A9451B398B5600269D14 = { isa = PBXGroup; children = ( 8401A9501B398B5600269D14 /* FontAwesomeDemo */, + 14578D272379464600304E74 /* FontAwesomeDemo (macOS) */, + 84344C6223CE9FFE00F2F6A6 /* FontAwesomeDemoUnitTests (macOS) */, 8401A94F1B398B5600269D14 /* Products */, 841575D01B39AB71001092B6 /* FontAwesome.xcodeproj */, CF090C461FBB7A7200BB8B4C /* Frameworks */, @@ -90,6 +204,8 @@ isa = PBXGroup; children = ( 8401A94E1B398B5600269D14 /* FontAwesomeDemo.app */, + 14578D262379464600304E74 /* FontAwesomeDemo (macOS).app */, + 84344C6123CE9FFE00F2F6A6 /* FontAwesomeDemoUnitTests (macOS).xctest */, ); name = Products; sourceTree = ""; @@ -122,10 +238,21 @@ 841575D81B39AB71001092B6 /* FontAwesomeTests.xctest */, 84DBFFAB1D8A7E5C002C4517 /* FontAwesome.framework */, 84669FA51F3D4C0A00EFCA99 /* FontAwesome.framework */, + 14578D3A2379464900304E74 /* FontAwesome.framework */, ); name = Products; sourceTree = ""; }; + 84344C6223CE9FFE00F2F6A6 /* FontAwesomeDemoUnitTests (macOS) */ = { + isa = PBXGroup; + children = ( + 1466724923EC04CE0000389E /* Extensions */, + 84344C6323CE9FFE00F2F6A6 /* FontAwesomeMacOSDemoUnitTests.swift */, + 84344C6523CE9FFE00F2F6A6 /* Info.plist */, + ); + path = "FontAwesomeDemoUnitTests (macOS)"; + sourceTree = ""; + }; CF090C461FBB7A7200BB8B4C /* Frameworks */ = { isa = PBXGroup; children = ( @@ -136,6 +263,25 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 14578D252379464600304E74 /* FontAwesomeDemo (macOS) */ = { + isa = PBXNativeTarget; + buildConfigurationList = 14578D3B2379464900304E74 /* Build configuration list for PBXNativeTarget "FontAwesomeDemo (macOS)" */; + buildPhases = ( + 14578D222379464600304E74 /* Sources */, + 14578D232379464600304E74 /* Frameworks */, + 14578D242379464600304E74 /* Resources */, + 14578D3E237946CF00304E74 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 14578D402379470200304E74 /* PBXTargetDependency */, + ); + name = "FontAwesomeDemo (macOS)"; + productName = "FontAwesomeDemo (macOS)"; + productReference = 14578D262379464600304E74 /* FontAwesomeDemo (macOS).app */; + productType = "com.apple.product-type.application"; + }; 8401A94D1B398B5600269D14 /* FontAwesomeDemo */ = { isa = PBXNativeTarget; buildConfigurationList = 8401A96D1B398B5600269D14 /* Build configuration list for PBXNativeTarget "FontAwesomeDemo" */; @@ -154,19 +300,48 @@ productReference = 8401A94E1B398B5600269D14 /* FontAwesomeDemo.app */; productType = "com.apple.product-type.application"; }; + 84344C6023CE9FFE00F2F6A6 /* FontAwesomeDemoUnitTests (macOS) */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84344C6D23CE9FFE00F2F6A6 /* Build configuration list for PBXNativeTarget "FontAwesomeDemoUnitTests (macOS)" */; + buildPhases = ( + 84344C5D23CE9FFE00F2F6A6 /* Sources */, + 84344C5E23CE9FFE00F2F6A6 /* Frameworks */, + 84344C5F23CE9FFE00F2F6A6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 84344C6723CE9FFE00F2F6A6 /* PBXTargetDependency */, + ); + name = "FontAwesomeDemoUnitTests (macOS)"; + productName = "FontAwesomeDemoUnitTests (macOS)"; + productReference = 84344C6123CE9FFE00F2F6A6 /* FontAwesomeDemoUnitTests (macOS).xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 8401A9461B398B5600269D14 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 1000; + LastSwiftUpdateCheck = 1010; + LastUpgradeCheck = 1120; TargetAttributes = { + 14578D252379464600304E74 = { + CreatedOnToolsVersion = 11.2; + LastSwiftMigration = 1130; + ProvisioningStyle = Manual; + }; 8401A94D1B398B5600269D14 = { CreatedOnToolsVersion = 6.3.2; LastSwiftMigration = 1020; }; + 84344C6023CE9FFE00F2F6A6 = { + CreatedOnToolsVersion = 10.1; + LastSwiftMigration = 1130; + ProvisioningStyle = Automatic; + TestTargetID = 14578D252379464600304E74; + }; }; }; buildConfigurationList = 8401A9491B398B5600269D14 /* Build configuration list for PBXProject "FontAwesomeDemo" */; @@ -189,11 +364,20 @@ projectRoot = ""; targets = ( 8401A94D1B398B5600269D14 /* FontAwesomeDemo */, + 14578D252379464600304E74 /* FontAwesomeDemo (macOS) */, + 84344C6023CE9FFE00F2F6A6 /* FontAwesomeDemoUnitTests (macOS) */, ); }; /* End PBXProject section */ /* Begin PBXReferenceProxy section */ + 14578D3A2379464900304E74 /* FontAwesome.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = FontAwesome.framework; + remoteRef = 14578D392379464900304E74 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 841575D61B39AB71001092B6 /* FontAwesome.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -225,6 +409,15 @@ /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ + 14578D242379464600304E74 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 14578D2D2379464900304E74 /* Assets.xcassets in Resources */, + 14578D302379464900304E74 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8401A94C1B398B5600269D14 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -235,9 +428,26 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84344C5F23CE9FFE00F2F6A6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 14578D222379464600304E74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 14578D2B2379464600304E74 /* ViewController.swift in Sources */, + 14578D292379464600304E74 /* AppDelegate.swift in Sources */, + 144D4A6523CEBD9D00D8FAA6 /* NSToolbarItem.Identifier.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8401A94A1B398B5600269D14 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -247,6 +457,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84344C5D23CE9FFE00F2F6A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 145AE9A023F1FD7C006803D5 /* NSButton.BezelStyle.swift in Sources */, + 84344C6423CE9FFE00F2F6A6 /* FontAwesomeMacOSDemoUnitTests.swift in Sources */, + 145AE9A223F1FDCD006803D5 /* NSToolbar.SizeMode.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -255,9 +475,27 @@ name = FontAwesome; targetProxy = 1025818D1C2B187200146E20 /* PBXContainerItemProxy */; }; + 14578D402379470200304E74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FontAwesome-macOS"; + targetProxy = 14578D3F2379470200304E74 /* PBXContainerItemProxy */; + }; + 84344C6723CE9FFE00F2F6A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 14578D252379464600304E74 /* FontAwesomeDemo (macOS) */; + targetProxy = 84344C6623CE9FFE00F2F6A6 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + 14578D2E2379464900304E74 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 14578D2F2379464900304E74 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; 8401A9571B398B5600269D14 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -277,6 +515,67 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 14578D332379464900304E74 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "FontAwesomeDemo (macOS)/FontAwesomeDemo__macOS_.entitlements"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "FontAwesomeDemo (macOS)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "im.thi.FontAwesomeDemo-macOS"; + PRODUCT_MODULE_NAME = FontAwesomeDemoMacOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 14578D342379464900304E74 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "FontAwesomeDemo (macOS)/FontAwesomeDemo__macOS_.entitlements"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "FontAwesomeDemo (macOS)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "im.thi.FontAwesomeDemo-macOS"; + PRODUCT_MODULE_NAME = FontAwesomeDemoMacOS; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 8401A96B1B398B5600269D14 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -404,9 +703,73 @@ }; name = Release; }; + 84344C6823CE9FFE00F2F6A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "FontAwesomeDemoUnitTests (macOS)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "im.thii.FontAwesomeDemoUnitTests--macOS-"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FontAwesomeDemo (macOS).app/Contents/MacOS/FontAwesomeDemo (macOS)"; + }; + name = Debug; + }; + 84344C6923CE9FFE00F2F6A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = "FontAwesomeDemoUnitTests (macOS)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "im.thii.FontAwesomeDemoUnitTests--macOS-"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FontAwesomeDemo (macOS).app/Contents/MacOS/FontAwesomeDemo (macOS)"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 14578D3B2379464900304E74 /* Build configuration list for PBXNativeTarget "FontAwesomeDemo (macOS)" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 14578D332379464900304E74 /* Debug */, + 14578D342379464900304E74 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 8401A9491B398B5600269D14 /* Build configuration list for PBXProject "FontAwesomeDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -425,6 +788,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 84344C6D23CE9FFE00F2F6A6 /* Build configuration list for PBXNativeTarget "FontAwesomeDemoUnitTests (macOS)" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84344C6823CE9FFE00F2F6A6 /* Debug */, + 84344C6923CE9FFE00F2F6A6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 8401A9461B398B5600269D14 /* Project object */; diff --git a/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift b/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift new file mode 100644 index 00000000..1760de95 --- /dev/null +++ b/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift @@ -0,0 +1,45 @@ +// NSButton.BezelStyle.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +extension NSButton.BezelStyle: CaseIterable { + + public static var allCases: [NSButton.BezelStyle] { + return [ + .rounded, + .regularSquare, + .disclosure, + .shadowlessSquare, + .circular, + .texturedSquare, + .helpButton, + .smallSquare, + .texturedRounded, + .roundRect, + .recessed, + .roundedDisclosure, + .inline + ] + } + +} diff --git a/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSToolbar.SizeMode.swift b/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSToolbar.SizeMode.swift new file mode 100644 index 00000000..08a993e2 --- /dev/null +++ b/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSToolbar.SizeMode.swift @@ -0,0 +1,39 @@ +// NSToolbar.SizeMode.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +extension NSToolbar.SizeMode: Equatable { + + public static func == (lhs: NSToolbar.SizeMode, rhs: NSToolbar.SizeMode) -> Bool { + switch lhs { + case .default, .regular: + return rhs.rawValue == NSToolbar.SizeMode.default.rawValue + || rhs.rawValue == NSToolbar.SizeMode.regular.rawValue + case .small: + return rhs.rawValue == NSToolbar.SizeMode.small.rawValue + @unknown default: + return false + } + } + +} diff --git a/Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift b/Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift new file mode 100644 index 00000000..51179320 --- /dev/null +++ b/Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift @@ -0,0 +1,312 @@ +// FontAwesomeMacOSDemoUnitTests.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import XCTest +@testable import FontAwesome +@testable import FontAwesomeDemoMacOS + +class FontAwesomeMacOSDemoUnitTests: XCTestCase { + + override func setUp() { + continueAfterFailure = false + + if FontAwesomeConfig.defaultButtonIconSize != .medium { + FontAwesomeConfig.defaultButtonIconSize = .medium + // Force re-layout. + toolbar?.sizeMode = .regular + RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.5)) + } + } + + func testButton() { + let size = isHighResolutionDisplay + ? NSSize(width: 14, height: 13.5) + : NSSize(width: 13, height: 13) + let button: FontAwesomeButton = viewController.fontAwesomeButton + + XCTAssertFalse(button.isInToolbar) + XCTAssertEqual(button.image?.size, size) + } + + func testLabel() { + let label: FontAwesomeLabel = viewController.fontAwesomeLabel + + XCTAssertEqual(label.iconPointSize, 50) + XCTAssertEqual(label.stringValue, FontAwesome.github.unicode) + XCTAssertEqual(label.font?.pointSize, 50) + XCTAssertEqual(label.bounds.size, NSSize(width: isHighResolutionDisplay ? 52.5 : 53, + height: 61)) + } + + func testView() { + let size = isHighResolutionDisplay + ? NSSize(width: 138.5, height: 167) + : NSSize(width: 141, height: 170) + let labelSubview = viewController.fontAwesomeView.subviews.first as? FontAwesomeLabel + + XCTAssertNotNil(labelSubview) + XCTAssertEqual(labelSubview!.stringValue, FontAwesome.github.unicode) + XCTAssertEqual(labelSubview!.font?.pointSize, size.width) + XCTAssertEqual(labelSubview!.bounds.size, size) + } + + func testRegularToolbarItems() { + setToolbar(sizeMode: .regular) + AssertSizesOfToolbarItems() + } + + func testSmallToolbarItems() { + setToolbar(sizeMode: .small) + AssertSizesOfToolbarItems() + } + + func testRegularFirstToolbarItemCycle() { + setToolbar(sizeMode: .regular) + AssertSizesOfToolbarItems() + + setToolbar(sizeMode: .small) + AssertSizesOfToolbarItems() + + setToolbar(sizeMode: .regular) + AssertSizesOfToolbarItems() + + setToolbar(sizeMode: .small) + AssertSizesOfToolbarItems() + } + + func testSmallFirstToolbarItemCycle() { + setToolbar(sizeMode: .small) + AssertSizesOfToolbarItems() + + setToolbar(sizeMode: .regular) + AssertSizesOfToolbarItems() + + setToolbar(sizeMode: .small) + AssertSizesOfToolbarItems() + + setToolbar(sizeMode: .regular) + AssertSizesOfToolbarItems() + } + + func testIconHeight() { + let button = FontAwesomeButton(icon: .fontAwesomeFlag) + + button.controlSize = .regular + XCTAssertEqual(button.iconHeight, 13) + + button.controlSize = .small + XCTAssertEqual(button.iconHeight, 12) + + button.controlSize = .mini + XCTAssertEqual(button.iconHeight, 11) + } + + func testEquatable() { + XCTAssertEqual(NSToolbar.SizeMode.regular, NSToolbar.SizeMode.default) + XCTAssertTrue(NSToolbar.SizeMode.regular == NSToolbar.SizeMode.default) + XCTAssertEqual(NSToolbar.SizeMode.small, NSToolbar.SizeMode.small) + XCTAssertTrue(NSToolbar.SizeMode.small == NSToolbar.SizeMode.small) + XCTAssertNotEqual(NSToolbar.SizeMode.small, NSToolbar.SizeMode.default) + XCTAssertTrue(NSToolbar.SizeMode.small != NSToolbar.SizeMode.default) + XCTAssertNotEqual(NSToolbar.SizeMode.small, NSToolbar.SizeMode.regular) + XCTAssertTrue(NSToolbar.SizeMode.small != NSToolbar.SizeMode.regular) + } + + func testImageVerticalAdjustmentForAllBezels() { + let button = FontAwesomeButton() + + for bezelStyle in NSButton.BezelStyle.allCases { + button.bezelStyle = bezelStyle + + let value = button.cell?.value(forKey: "_imageVerticalAdjustmentForBezel") as? CGFloat + XCTAssertEqual(button.buttonCell?.verticalImageAdjustmentForBezel, + value, + "`imageVerticalAdjustment(for:)` does not return the expected " + + "value of \(value ?? -1) for the '\(bezelStyle)' bezel style") + } + } + + // MARK: - Test Misc. + + var toolbar: NSToolbar? { + return window?.toolbar + } + var viewController: ViewController { + return NSApp.windows[0].contentViewController as! ViewController + } + var window: NSWindow? { + return NSApp.windows.first + } + + var backingScaleFactor: CGFloat { + return window?.backingScaleFactor ?? 0 + } + var pixelPointValue: CGFloat { + return 1 / backingScaleFactor + } + var isHighResolutionDisplay: Bool { + return backingScaleFactor == 2 + } + + func setToolbar(sizeMode: NSToolbar.SizeMode) { + guard toolbar?.sizeMode != sizeMode + else { return } + + window?.perform(Selector(("toggleUsingSmallToolbarIcons:"))) + XCTAssertEqual(toolbar?.sizeMode, sizeMode) + + RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.5)) + } + + func toggleUsingSmallToolbarIcons() { + setToolbar(sizeMode: toolbar?.sizeMode == .small ? .regular : .small) + } + + func expectedItemWidth(for item: FontAwesomeToolbarItem) -> CGFloat { + switch item.itemIdentifier { + case .defaultWidth: + if isHighResolutionDisplay { + return toolbar?.sizeMode == .small + ? 38 - floor(2.5 * backingScaleFactor) + : 38 + } else { + return toolbar?.sizeMode == .small + ? 38 - floor(2.5 * backingScaleFactor) + : 38 + 1 + } + case .fittedWidth: + if isHighResolutionDisplay { + return toolbar?.sizeMode == .small ? 22 : 25 + } else { + return toolbar?.sizeMode == .small ? 22 : 25 + } + case .fixedWidth: + if isHighResolutionDisplay { + return toolbar?.sizeMode == .small + ? 40 - floor(2.5 * backingScaleFactor) + : 40 + } else { + return toolbar?.sizeMode == .small + ? 40 - floor(2.5 * backingScaleFactor) + : 40 + 1 + } + case .fixedPointSize: + if isHighResolutionDisplay { + return toolbar?.sizeMode == .small + ? 38 - floor(2.5 * backingScaleFactor) + 0.5 + : 38 + 0.5 + } else { + return toolbar?.sizeMode == .small + ? 38 - floor(2.5 * backingScaleFactor) + 1 + : 38 + } + default: + return 0 + } + } + + func expectedImageSize(for item: FontAwesomeToolbarItem) -> NSSize { + var size: NSSize + if toolbar?.sizeMode == .small { + size = isHighResolutionDisplay + ? NSSize(width: 15, height: 14.5) + : NSSize(width: 14, height: 14) + } else { + size = isHighResolutionDisplay + ? NSSize(width: 16, height: 15) + : NSSize(width: 15, height: 14) + } + + switch item.itemIdentifier { + case .fixedPointSize: + size.width += pixelPointValue + + if !(toolbar?.sizeMode == .small) { + size.height += pixelPointValue + } + default: () + } + + return size + } + + func expectedImageOrigin(for item: FontAwesomeToolbarItem) -> NSPoint { + var point: NSPoint = .zero + if toolbar?.sizeMode == .small { + point.y = isHighResolutionDisplay ? 3.5 : 4 + } else { + point.y = isHighResolutionDisplay ? 6 : 7 + } + + point.x = ((expectedItemWidth(for: item) - expectedImageSize(for: item).width) / 2) + + (item.bezelWidthAdjustment / 2) + + return point + } + + // swiftlint:disable:next identifier_name + func AssertSizesOfToolbarItems(file: StaticString = #file, line: UInt = #line) { + XCTAssertNotNil(toolbar, "Could not find toolbar", file: file, line: line) + + for item in toolbar!.items.compactMap({ $0 as? FontAwesomeToolbarItem }) { + XCTAssertNotNil(item.button, + "the item \"\(item.itemIdentifier.rawValue)\" does not have" + + " a valid `button` value", + file: file, + line: line) + XCTAssertTrue(item.button!.isInToolbar, + "the item \"\(item.itemIdentifier.rawValue)\"'s button does not have " + + "the correct `isInToolbar` value") + + let expectedWidth = expectedItemWidth(for: item) + XCTAssertEqual(item.button!.frame.width - item.bezelWidthAdjustment, + expectedWidth, + "the item \"\(item.itemIdentifier.rawValue)\" does not have" + + " the expected button width \(expectedWidth)", + file: file, + line: line) + XCTAssertEqual(item.minSize.width - item.bezelWidthAdjustment, + expectedWidth, + "the item \"\(item.itemIdentifier.rawValue)\" does not have" + + " the expected `minSize.width` \(expectedWidth)", + file: file, + line: line) + + let expectedSize = expectedImageSize(for: item) + XCTAssertEqual(item.button!.image?.size, + expectedSize, + "the item \"\(item.itemIdentifier.rawValue)\" does not have" + + " the expected image size \(expectedSize)", + file: file, + line: line) + + let expectedOrigin = expectedImageOrigin(for: item) + XCTAssertEqual(item.button!.buttonCell?.imageRect(forBounds: item.button!.bounds).origin, + expectedOrigin, + "the item \"\(item.itemIdentifier.rawValue)\" does not have" + + " the expected image origin \(expectedOrigin)", + file: file, + line: line) + } + } + +} diff --git a/Demo/FontAwesomeDemoUnitTests (macOS)/Info.plist b/Demo/FontAwesomeDemoUnitTests (macOS)/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/Demo/FontAwesomeDemoUnitTests (macOS)/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/FontAwesome.xcodeproj/project.pbxproj b/FontAwesome.xcodeproj/project.pbxproj index ff7c8199..9d890b56 100644 --- a/FontAwesome.xcodeproj/project.pbxproj +++ b/FontAwesome.xcodeproj/project.pbxproj @@ -7,6 +7,37 @@ objects = { /* Begin PBXBuildFile section */ + 140152C6237B6EA00051049B /* FontAwesomeRawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140152C5237B6EA00051049B /* FontAwesomeRawRepresentable.swift */; }; + 1403403723E6513F00B22878 /* FontAwesomeToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1403403623E6513F00B22878 /* FontAwesomeToolbar.swift */; }; + 1416E4FA2370F574002CE7CC /* FontAwesomeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1416E4F92370F574002CE7CC /* FontAwesomeButton.swift */; }; + 141B0EA62374983F00A019FD /* FontAwesomeRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141B0EA52374983F00A019FD /* FontAwesomeRepresentable.swift */; }; + 141B0EAB2374F57A00A019FD /* FontAwesomeButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 141B0EAA2374F57A00A019FD /* FontAwesomeButtonCell.swift */; }; + 144635DD23E3AB6800464BB8 /* FontAwesomeIconSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 144635DC23E3AB6800464BB8 /* FontAwesomeIconSize.swift */; }; + 144772A8236DF8B100597F27 /* FontAwesome.h in Headers */ = {isa = PBXBuildFile; fileRef = 8401A8D71B39601400269D14 /* FontAwesome.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 144772A9236DF8CF00597F27 /* Enum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841575D91B3A490A001092B6 /* Enum.swift */; }; + 144772AA236DF8CF00597F27 /* FontAwesome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401A8F01B39610F00269D14 /* FontAwesome.swift */; }; + 144772AC236DF8CF00597F27 /* FontAwesomeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E07764210C36B600CF2885 /* FontAwesomeExtension.swift */; }; + 144772B4236DF8E300597F27 /* Font Awesome 5 Brands-Regular-400.otf in Resources */ = {isa = PBXBuildFile; fileRef = 84E07768210C47DD00CF2885 /* Font Awesome 5 Brands-Regular-400.otf */; }; + 144772B5236DF8E300597F27 /* Font Awesome 5 Free-Regular-400.otf in Resources */ = {isa = PBXBuildFile; fileRef = 84E0776A210C47DD00CF2885 /* Font Awesome 5 Free-Regular-400.otf */; }; + 144772B6236DF8E300597F27 /* Font Awesome 5 Free-Solid-900.otf in Resources */ = {isa = PBXBuildFile; fileRef = 84E07769210C47DD00CF2885 /* Font Awesome 5 Free-Solid-900.otf */; }; + 144772BA236DF9B500597F27 /* Shims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 144772B9236DF9B500597F27 /* Shims.swift */; }; + 144772BB236DF9B500597F27 /* Shims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 144772B9236DF9B500597F27 /* Shims.swift */; }; + 144772BC236DF9B500597F27 /* Shims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 144772B9236DF9B500597F27 /* Shims.swift */; }; + 144772BD236DF9B500597F27 /* Shims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 144772B9236DF9B500597F27 /* Shims.swift */; }; + 144772BF236E05E700597F27 /* FontAwesomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DBFFAC1D8A7E76002C4517 /* FontAwesomeView.swift */; }; + 145FF26E23ECC9F5000A7FD5 /* NSSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 145FF26D23ECC9F5000A7FD5 /* NSSize.swift */; }; + 146609CE23CFAE2300DB1F4D /* Image+Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 146609CD23CFAE2300DB1F4D /* Image+Trim.swift */; }; + 1466724823EBE03C0000389E /* NSButton.BezelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1466724723EBE03C0000389E /* NSButton.BezelStyle.swift */; }; + 149644E223D998B500045A58 /* FontAwesomeLayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 149644E123D998B500045A58 /* FontAwesomeLayoutManager.swift */; }; + 14971E9923D7FBEE00C1868E /* FontAwesome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8401A8D21B39601400269D14 /* FontAwesome.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 14971E9A23D7FBEE00C1868E /* FontAwesome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 144772A0236DF81D00597F27 /* FontAwesome.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 14A04526238DA90700E309DE /* CGFloat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A04525238DA90700E309DE /* CGFloat.swift */; }; + 14BD4FFB2377900F009C2F3B /* FontAwesomeTextRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A3C1ECC816A0010B0BA /* FontAwesomeTextRepresentable.swift */; }; + 14C856AE236F732A00C6409A /* FontAwesomeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14C856AD236F732A00C6409A /* FontAwesomeLabel.swift */; }; + 14C856B1236F8A6B00C6409A /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14C856AF236F8A6300C6409A /* Font.swift */; }; + 14C856B5236FC74600C6409A /* FontAwesomeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A381ECC816A0010B0BA /* FontAwesomeImageView.swift */; }; + 14C856B6236FC75E00C6409A /* FontAwesomeImageRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A371ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift */; }; + 14C856B9236FC93F00C6409A /* FontAwesomeToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14C856B8236FC93F00C6409A /* FontAwesomeToolbarItem.swift */; }; 484F4A3D1ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A361ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift */; }; 484F4A3E1ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A371ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift */; }; 484F4A3F1ECC816A0010B0BA /* FontAwesomeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A381ECC816A0010B0BA /* FontAwesomeImageView.swift */; }; @@ -19,7 +50,6 @@ 4DF4FB981E94582E00F03B22 /* FontAwesomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DBFFAC1D8A7E76002C4517 /* FontAwesomeView.swift */; }; 4DF4FB991E94583100F03B22 /* Enum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841575D91B3A490A001092B6 /* Enum.swift */; }; 8401A8D81B39601400269D14 /* FontAwesome.h in Headers */ = {isa = PBXBuildFile; fileRef = 8401A8D71B39601400269D14 /* FontAwesome.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8401A8DE1B39601400269D14 /* FontAwesome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8401A8D21B39601400269D14 /* FontAwesome.framework */; }; 8401A8E51B39601400269D14 /* FontAwesomeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401A8E41B39601400269D14 /* FontAwesomeTests.swift */; }; 8401A8F11B39610F00269D14 /* FontAwesome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8401A8F01B39610F00269D14 /* FontAwesome.swift */; }; 841575DA1B3A490A001092B6 /* Enum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841575D91B3A490A001092B6 /* Enum.swift */; }; @@ -41,17 +71,23 @@ B104D3C21E9530560025BA9B /* Enum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841575D91B3A490A001092B6 /* Enum.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 8401A8DF1B39601400269D14 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 8401A8C91B39601400269D14 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 8401A8D11B39601400269D14; - remoteInfo = FontAwesome; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ + 140152C5237B6EA00051049B /* FontAwesomeRawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeRawRepresentable.swift; sourceTree = ""; }; + 1403403623E6513F00B22878 /* FontAwesomeToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeToolbar.swift; sourceTree = ""; }; + 1416E4F92370F574002CE7CC /* FontAwesomeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeButton.swift; sourceTree = ""; }; + 141B0EA52374983F00A019FD /* FontAwesomeRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeRepresentable.swift; sourceTree = ""; }; + 141B0EAA2374F57A00A019FD /* FontAwesomeButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeButtonCell.swift; sourceTree = ""; }; + 144635DC23E3AB6800464BB8 /* FontAwesomeIconSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeIconSize.swift; sourceTree = ""; }; + 144772A0236DF81D00597F27 /* FontAwesome.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FontAwesome.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 144772B9236DF9B500597F27 /* Shims.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shims.swift; sourceTree = ""; }; + 145FF26D23ECC9F5000A7FD5 /* NSSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSSize.swift; sourceTree = ""; }; + 146609CD23CFAE2300DB1F4D /* Image+Trim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image+Trim.swift"; sourceTree = ""; }; + 1466724723EBE03C0000389E /* NSButton.BezelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSButton.BezelStyle.swift; sourceTree = ""; }; + 149644E123D998B500045A58 /* FontAwesomeLayoutManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeLayoutManager.swift; sourceTree = ""; }; + 14A04525238DA90700E309DE /* CGFloat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGFloat.swift; sourceTree = ""; }; + 14C856AD236F732A00C6409A /* FontAwesomeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeLabel.swift; sourceTree = ""; }; + 14C856AF236F8A6300C6409A /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; + 14C856B8236FC93F00C6409A /* FontAwesomeToolbarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeToolbarItem.swift; sourceTree = ""; }; 484F4A361ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontAwesomeBarButtonItem.swift; sourceTree = ""; }; 484F4A371ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontAwesomeImageRepresentable.swift; sourceTree = ""; }; 484F4A381ECC816A0010B0BA /* FontAwesomeImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontAwesomeImageView.swift; sourceTree = ""; }; @@ -77,6 +113,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 1447729D236DF81D00597F27 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4DF4FB8A1E94581300F03B22 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -95,7 +138,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8401A8DE1B39601400269D14 /* FontAwesome.framework in Frameworks */, + 14971E9923D7FBEE00C1868E /* FontAwesome.framework in Frameworks */, + 14971E9A23D7FBEE00C1868E /* FontAwesome.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -109,12 +153,84 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 141B0EA42374982C00A019FD /* Protocols */ = { + isa = PBXGroup; + children = ( + 140152C5237B6EA00051049B /* FontAwesomeRawRepresentable.swift */, + 141B0EA52374983F00A019FD /* FontAwesomeRepresentable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + 141B0EA72374F50F00A019FD /* Cells */ = { + isa = PBXGroup; + children = ( + 141B0EAA2374F57A00A019FD /* FontAwesomeButtonCell.swift */, + ); + path = Cells; + sourceTree = ""; + }; + 144635DB23E3AB4300464BB8 /* Enumerations */ = { + isa = PBXGroup; + children = ( + 144635DC23E3AB6800464BB8 /* FontAwesomeIconSize.swift */, + ); + path = Enumerations; + sourceTree = ""; + }; + 148665662379246E003D87F0 /* UIKit */ = { + isa = PBXGroup; + children = ( + 484F4A361ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift */, + 484F4A391ECC816A0010B0BA /* FontAwesomeSegmentedControl.swift */, + 484F4A3A1ECC816A0010B0BA /* FontAwesomeStateRequirement.swift */, + 484F4A3B1ECC816A0010B0BA /* FontAwesomeTabBarItem.swift */, + ); + path = UIKit; + sourceTree = ""; + }; + 14971E9823D7FBEE00C1868E /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 14AEE9EC23847745004F5838 /* Extensions */ = { + isa = PBXGroup; + children = ( + 14A04525238DA90700E309DE /* CGFloat.swift */, + 14C856AF236F8A6300C6409A /* Font.swift */, + 146609CD23CFAE2300DB1F4D /* Image+Trim.swift */, + 1466724723EBE03C0000389E /* NSButton.BezelStyle.swift */, + 145FF26D23ECC9F5000A7FD5 /* NSSize.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 14C856B7236FC85C00C6409A /* AppKit */ = { + isa = PBXGroup; + children = ( + 141B0EA72374F50F00A019FD /* Cells */, + 144635DB23E3AB4300464BB8 /* Enumerations */, + 14AEE9EC23847745004F5838 /* Extensions */, + 1416E4F92370F574002CE7CC /* FontAwesomeButton.swift */, + 14C856AD236F732A00C6409A /* FontAwesomeLabel.swift */, + 149644E123D998B500045A58 /* FontAwesomeLayoutManager.swift */, + 1403403623E6513F00B22878 /* FontAwesomeToolbar.swift */, + 14C856B8236FC93F00C6409A /* FontAwesomeToolbarItem.swift */, + 141B0EA42374982C00A019FD /* Protocols */, + ); + path = AppKit; + sourceTree = ""; + }; 8401A8C81B39601400269D14 = { isa = PBXGroup; children = ( 8401A8D41B39601400269D14 /* FontAwesome */, 8401A8E11B39601400269D14 /* FontAwesomeTests */, 8401A8D31B39601400269D14 /* Products */, + 14971E9823D7FBEE00C1868E /* Frameworks */, ); sourceTree = ""; }; @@ -125,6 +241,7 @@ 8401A8DD1B39601400269D14 /* FontAwesomeTests.xctest */, 848937F91C6AF0660085F6D1 /* FontAwesome.framework */, 4DF4FB8E1E94581300F03B22 /* FontAwesome.framework */, + 144772A0236DF81D00597F27 /* FontAwesome.framework */, ); name = Products; sourceTree = ""; @@ -132,20 +249,19 @@ 8401A8D41B39601400269D14 /* FontAwesome */ = { isa = PBXGroup; children = ( + 14C856B7236FC85C00C6409A /* AppKit */, 841575D91B3A490A001092B6 /* Enum.swift */, 8401A8D71B39601400269D14 /* FontAwesome.h */, 8401A8F01B39610F00269D14 /* FontAwesome.swift */, - 484F4A361ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift */, 84E07764210C36B600CF2885 /* FontAwesomeExtension.swift */, 484F4A371ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift */, 484F4A381ECC816A0010B0BA /* FontAwesomeImageView.swift */, - 484F4A391ECC816A0010B0BA /* FontAwesomeSegmentedControl.swift */, - 484F4A3A1ECC816A0010B0BA /* FontAwesomeStateRequirement.swift */, - 484F4A3B1ECC816A0010B0BA /* FontAwesomeTabBarItem.swift */, 484F4A3C1ECC816A0010B0BA /* FontAwesomeTextRepresentable.swift */, 84DBFFAC1D8A7E76002C4517 /* FontAwesomeView.swift */, 84E07774210CA54300CF2885 /* Resources */, + 144772B9236DF9B500597F27 /* Shims.swift */, 8401A8D51B39601400269D14 /* Supporting Files */, + 148665662379246E003D87F0 /* UIKit */, ); path = FontAwesome; sourceTree = ""; @@ -188,6 +304,14 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 1447729B236DF81D00597F27 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 144772A8236DF8B100597F27 /* FontAwesome.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4DF4FB8B1E94581300F03B22 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -215,6 +339,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 1447729F236DF81D00597F27 /* FontAwesome-macOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 144772A7236DF81D00597F27 /* Build configuration list for PBXNativeTarget "FontAwesome-macOS" */; + buildPhases = ( + 1447729B236DF81D00597F27 /* Headers */, + 1447729C236DF81D00597F27 /* Sources */, + 1447729D236DF81D00597F27 /* Frameworks */, + 1447729E236DF81D00597F27 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "FontAwesome-macOS"; + productName = "FontAwesome-macOS"; + productReference = 144772A0236DF81D00597F27 /* FontAwesome.framework */; + productType = "com.apple.product-type.framework"; + }; 4DF4FB8D1E94581300F03B22 /* FontAwesone-tvOS */ = { isa = PBXNativeTarget; buildConfigurationList = 4DF4FB931E94581300F03B22 /* Build configuration list for PBXNativeTarget "FontAwesone-tvOS" */; @@ -262,7 +404,6 @@ buildRules = ( ); dependencies = ( - 8401A8E01B39601400269D14 /* PBXTargetDependency */, ); name = FontAwesomeTests; productName = FontAwesomeTests; @@ -293,9 +434,14 @@ 8401A8C91B39601400269D14 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0940; + LastSwiftUpdateCheck = 1010; LastUpgradeCheck = 1000; TargetAttributes = { + 1447729F236DF81D00597F27 = { + CreatedOnToolsVersion = 11.2; + LastSwiftMigration = 1130; + ProvisioningStyle = Automatic; + }; 4DF4FB8D1E94581300F03B22 = { CreatedOnToolsVersion = 8.3; LastSwiftMigration = 1020; @@ -307,7 +453,7 @@ }; 8401A8DC1B39601400269D14 = { CreatedOnToolsVersion = 6.3.2; - LastSwiftMigration = 1020; + LastSwiftMigration = 1130; }; 848937F81C6AF0660085F6D1 = { CreatedOnToolsVersion = 7.2.1; @@ -332,11 +478,22 @@ 8401A8DC1B39601400269D14 /* FontAwesomeTests */, 848937F81C6AF0660085F6D1 /* FontAwesome-watchOS */, 4DF4FB8D1E94581300F03B22 /* FontAwesone-tvOS */, + 1447729F236DF81D00597F27 /* FontAwesome-macOS */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 1447729E236DF81D00597F27 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 144772B4236DF8E300597F27 /* Font Awesome 5 Brands-Regular-400.otf in Resources */, + 144772B5236DF8E300597F27 /* Font Awesome 5 Free-Regular-400.otf in Resources */, + 144772B6236DF8E300597F27 /* Font Awesome 5 Free-Solid-900.otf in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4DF4FB8C1E94581300F03B22 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -377,6 +534,35 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 1447729C236DF81D00597F27 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1466724823EBE03C0000389E /* NSButton.BezelStyle.swift in Sources */, + 14A04526238DA90700E309DE /* CGFloat.swift in Sources */, + 14C856AE236F732A00C6409A /* FontAwesomeLabel.swift in Sources */, + 14C856B5236FC74600C6409A /* FontAwesomeImageView.swift in Sources */, + 14C856B6236FC75E00C6409A /* FontAwesomeImageRepresentable.swift in Sources */, + 146609CE23CFAE2300DB1F4D /* Image+Trim.swift in Sources */, + 140152C6237B6EA00051049B /* FontAwesomeRawRepresentable.swift in Sources */, + 145FF26E23ECC9F5000A7FD5 /* NSSize.swift in Sources */, + 144772A9236DF8CF00597F27 /* Enum.swift in Sources */, + 144772AA236DF8CF00597F27 /* FontAwesome.swift in Sources */, + 149644E223D998B500045A58 /* FontAwesomeLayoutManager.swift in Sources */, + 141B0EA62374983F00A019FD /* FontAwesomeRepresentable.swift in Sources */, + 144772AC236DF8CF00597F27 /* FontAwesomeExtension.swift in Sources */, + 1403403723E6513F00B22878 /* FontAwesomeToolbar.swift in Sources */, + 144635DD23E3AB6800464BB8 /* FontAwesomeIconSize.swift in Sources */, + 14C856B1236F8A6B00C6409A /* Font.swift in Sources */, + 144772BF236E05E700597F27 /* FontAwesomeView.swift in Sources */, + 144772BD236DF9B500597F27 /* Shims.swift in Sources */, + 141B0EAB2374F57A00A019FD /* FontAwesomeButtonCell.swift in Sources */, + 14C856B9236FC93F00C6409A /* FontAwesomeToolbarItem.swift in Sources */, + 14BD4FFB2377900F009C2F3B /* FontAwesomeTextRepresentable.swift in Sources */, + 1416E4FA2370F574002CE7CC /* FontAwesomeButton.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 4DF4FB891E94581300F03B22 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -384,6 +570,7 @@ 4DF4FB991E94583100F03B22 /* Enum.swift in Sources */, 4DF4FB981E94582E00F03B22 /* FontAwesomeView.swift in Sources */, 84E07767210C36B600CF2885 /* FontAwesomeExtension.swift in Sources */, + 144772BC236DF9B500597F27 /* Shims.swift in Sources */, 4DF4FB971E94582B00F03B22 /* FontAwesome.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -402,6 +589,7 @@ 84E07765210C36B600CF2885 /* FontAwesomeExtension.swift in Sources */, 484F4A3F1ECC816A0010B0BA /* FontAwesomeImageView.swift in Sources */, 8401A8F11B39610F00269D14 /* FontAwesome.swift in Sources */, + 144772BA236DF9B500597F27 /* Shims.swift in Sources */, 84DBFFAD1D8A7E76002C4517 /* FontAwesomeView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -420,21 +608,75 @@ files = ( 84E07766210C36B600CF2885 /* FontAwesomeExtension.swift in Sources */, B104D3C21E9530560025BA9B /* Enum.swift in Sources */, + 144772BB236DF9B500597F27 /* Shims.swift in Sources */, B104D3C01E95304D0025BA9B /* FontAwesome.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 8401A8E01B39601400269D14 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 8401A8D11B39601400269D14 /* FontAwesome */; - targetProxy = 8401A8DF1B39601400269D14 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ + 144772A5236DF81D00597F27 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FontAwesome/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.thi.FontAwesome; + PRODUCT_NAME = FontAwesome; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 144772A6236DF81D00597F27 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FontAwesome/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = im.thi.FontAwesome; + PRODUCT_NAME = FontAwesome; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 4DF4FB941E94581300F03B22 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -619,7 +861,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -639,7 +881,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "im.thi.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -652,9 +894,10 @@ "$(inherited)", ); INFOPLIST_FILE = FontAwesomeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "im.thi.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; SWIFT_VERSION = 5.0; }; name = Debug; @@ -664,9 +907,10 @@ buildSettings = { FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = FontAwesomeTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "im.thi.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; SWIFT_VERSION = 5.0; }; name = Release; @@ -722,6 +966,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 144772A7236DF81D00597F27 /* Build configuration list for PBXNativeTarget "FontAwesome-macOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 144772A5236DF81D00597F27 /* Debug */, + 144772A6236DF81D00597F27 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 4DF4FB931E94581300F03B22 /* Build configuration list for PBXNativeTarget "FontAwesone-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-macOS.xcscheme b/FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-macOS.xcscheme new file mode 100644 index 00000000..934fff8c --- /dev/null +++ b/FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-macOS.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-tvOS.xcscheme b/FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-tvOS.xcscheme index 335b61fe..7005e202 100644 --- a/FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-tvOS.xcscheme +++ b/FontAwesome.xcodeproj/xcshareddata/xcschemes/FontAwesome-tvOS.xcscheme @@ -1,6 +1,6 @@ - - - - - - - - + + + + @@ -53,17 +62,6 @@ - - - - - - - - NSSize { + return fontAwesomeButton?.adjustingSize(size) ?? size + } + + open func bezelRect(forBounds rect: NSRect) -> NSRect { + return fontAwesomeButton?.bezelRect(forBounds: rect) ?? rect + } + + open override func imageRect(forBounds rect: NSRect) -> NSRect { + guard let image = image + else { return super.imageRect(forBounds: rect) } + + var imageRect = bezelRect(forBounds: rect) + imageRect.origin.x += (iconOffset.x + + ((imageRect.width - image.size.width) / 2)) + .roundUpToPixel() + imageRect.origin.y += (iconOffset.y + + ((imageRect.height - image.size.height) / 2) + + verticalImageAdjustmentForBezel) + .roundUpToPixel() + imageRect.size = image.size + return imageRect + } + +} diff --git a/FontAwesome/AppKit/Enumerations/FontAwesomeIconSize.swift b/FontAwesome/AppKit/Enumerations/FontAwesomeIconSize.swift new file mode 100644 index 00000000..5715e017 --- /dev/null +++ b/FontAwesome/AppKit/Enumerations/FontAwesomeIconSize.swift @@ -0,0 +1,44 @@ +// FontAwesomeIconSize.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +public enum FontAwesomeIconSize: String, Codable { + case small + case medium + case large + + public init?(stringValue: String) { + let value = stringValue.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + if let size = FontAwesomeIconSize(rawValue: value) { + self = size + } else { + switch value.first { + case "s": + self = .small + case "l": + self = .large + default: + self = .medium + } + } + } + +} diff --git a/FontAwesome/AppKit/Extensions/CGFloat.swift b/FontAwesome/AppKit/Extensions/CGFloat.swift new file mode 100644 index 00000000..8bf54f04 --- /dev/null +++ b/FontAwesome/AppKit/Extensions/CGFloat.swift @@ -0,0 +1,45 @@ +// CGFloat.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +internal extension CGFloat { + + func floor(nearest: CGFloat) -> CGFloat { + return Darwin.floor(self / nearest) * nearest + } + + func ceil(nearest: CGFloat) -> CGFloat { + return Darwin.ceil(self / nearest) * nearest + } + + /// Returns the closest pixel point greater than or equal to the receiver. + func roundUpToPixel() -> CGFloat { + return ceil(nearest: FALayoutManager.pixelPointValue) + } + + /// Returns the closest pixel point less than or equal to the receiver. + func roundDownToPixel() -> CGFloat { + return floor(nearest: FALayoutManager.pixelPointValue) + } + +} diff --git a/FontAwesome/AppKit/Extensions/Font.swift b/FontAwesome/AppKit/Extensions/Font.swift new file mode 100644 index 00000000..b26f3368 --- /dev/null +++ b/FontAwesome/AppKit/Extensions/Font.swift @@ -0,0 +1,57 @@ +// Font.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +extension Font { + + /// Returns the font point size that fits a Font Awesome icon within a bounding size. + /// + /// - Parameters: + /// - icon: The Unicode Font Awesome icon. + /// - style: The font style. + /// - maxSize: The maximum size. + /// - usingTypographicBounds: Whether to use the glyph's typographic bounds instead of the image + /// bounds. When true, the icon will be typeset by the system. + /// + /// - Returns: The font point size that fits `icon` with `style` inside `maxSize`. + static public func fontAwesomePointSize(of icon: String, + with style: FontAwesomeStyle, + fitting maxSize: CGSize, + usingTypographicBounds: Bool = false) -> CGFloat { + let arbitraryPointSize: CGFloat = 100 + let font = Font.fontAwesome(ofSize: arbitraryPointSize, style: style) + let attributedString = NSMutableAttributedString(string: icon, attributes: [.font: font]) + + let iconSize = usingTypographicBounds + ? FALayoutManager.typographicBounds(of: attributedString).size + : FALayoutManager.imageBounds(of: attributedString).size + + let iconAspectRatio = iconSize.width / iconSize.height + let heightRatio = iconSize.height / arbitraryPointSize + let heightMaxPointSize = maxSize.height / heightRatio + let widthMaxPointSize = (maxSize.width / iconAspectRatio) / heightRatio + let minimumPointSize = min(heightMaxPointSize, widthMaxPointSize).roundDownToPixel() + return max(minimumPointSize, 0.1) + } + +} diff --git a/FontAwesome/AppKit/Extensions/Image+Trim.swift b/FontAwesome/AppKit/Extensions/Image+Trim.swift new file mode 100644 index 00000000..50053bd3 --- /dev/null +++ b/FontAwesome/AppKit/Extensions/Image+Trim.swift @@ -0,0 +1,156 @@ +// Image+Trim.swift +// +// Copyright © 2020 Christopher Zielinski. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if canImport(AppKit) +import AppKit + +typealias EdgeInsets = NSEdgeInsets +#endif + +import CoreGraphics + +#if canImport(AppKit) +extension NSImage { + + /// Crops the insets of transparency around the image. + /// + /// - Parameters: + /// - maximumAlphaChannel: The maximum alpha channel value to consider _transparent_ and thus crop. Any alpha value + /// strictly greater than `maximumAlphaChannel` will be considered opaque. + func trimmingTransparentPixels(maximumAlphaChannel: UInt8 = 0) -> NSImage? { + guard size.height > 1 && size.width > 1 + else { return self } + + guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil)? + .trimmingTransparentPixels(maximumAlphaChannel: maximumAlphaChannel) + else { return nil } + + let scale = recommendedLayerContentsScale(0) + let scaledSize = CGSize(width: CGFloat(cgImage.width) / scale, + height: CGFloat(cgImage.height) / scale) + let image = NSImage(cgImage: cgImage, size: scaledSize) + image.isTemplate = isTemplate + return image + } + +} +#endif + +extension CGImage { + + /// Crops the insets of transparency around the image. + /// + /// - Parameters: + /// - maximumAlphaChannel: The maximum alpha channel value to consider _transparent_ and thus crop. Any alpha value + /// strictly greater than `maximumAlphaChannel` will be considered opaque. + func trimmingTransparentPixels(maximumAlphaChannel: UInt8 = 0) -> CGImage? { + return _CGImageTransparencyTrimmer(image: self, maximumAlphaChannel: maximumAlphaChannel)?.trim() + } + +} + +private struct _CGImageTransparencyTrimmer { + + let image: CGImage + let maximumAlphaChannel: UInt8 + let cgContext: CGContext + let zeroByteBlock: UnsafeMutableRawPointer + let pixelRowRange: Range + let pixelColumnRange: Range + + init?(image: CGImage, maximumAlphaChannel: UInt8) { + guard let cgContext = CGContext(data: nil, + width: image.width, + height: image.height, + bitsPerComponent: 8, + bytesPerRow: 0, + space: CGColorSpaceCreateDeviceGray(), + bitmapInfo: CGImageAlphaInfo.alphaOnly.rawValue), + cgContext.data != nil + else { return nil } + + cgContext.draw(image, + in: CGRect(origin: .zero, + size: CGSize(width: image.width, + height: image.height))) + + guard let zeroByteBlock = calloc(image.width, MemoryLayout.size) + else { return nil } + + self.image = image + self.maximumAlphaChannel = maximumAlphaChannel + self.cgContext = cgContext + self.zeroByteBlock = zeroByteBlock + + pixelRowRange = 0.. CGImage? { + guard let topInset = firstOpaquePixelRow(in: pixelRowRange), + let bottomOpaqueRow = firstOpaquePixelRow(in: pixelRowRange.reversed()), + let leftInset = firstOpaquePixelColumn(in: pixelColumnRange), + let rightOpaqueColumn = firstOpaquePixelColumn(in: pixelColumnRange.reversed()) + else { return nil } + + let bottomInset = (image.height - 1) - bottomOpaqueRow + let rightInset = (image.width - 1) - rightOpaqueColumn + + guard !(topInset == 0 && bottomInset == 0 && leftInset == 0 && rightInset == 0) + else { return image } + + return image.cropping(to: CGRect(origin: CGPoint(x: leftInset, y: topInset), + size: CGSize(width: image.width - (leftInset + rightInset), + height: image.height - (topInset + bottomInset)))) + } + + @inlinable + func isPixelOpaque(column: Int, row: Int) -> Bool { + // Sanity check: It is safe to get the data pointer in iOS 4.0+ and macOS 10.6+ only. + assert(cgContext.data != nil) + return cgContext.data!.load(fromByteOffset: (row * cgContext.bytesPerRow) + column, as: UInt8.self) + > maximumAlphaChannel + } + + @inlinable + func isPixelRowTransparent(_ row: Int) -> Bool { + assert(cgContext.data != nil) + // `memcmp` will efficiently check if the entire pixel row has zero alpha values + return memcmp(cgContext.data! + (row * cgContext.bytesPerRow), zeroByteBlock, image.width) == 0 + // When the entire row is NOT zeroed, we proceed to check each pixel's alpha + // value individually until we locate the first "opaque" pixel (very ~not~ efficient). + || !pixelColumnRange.contains(where: { isPixelOpaque(column: $0, row: row) }) + } + + @inlinable + func firstOpaquePixelRow(in rowRange: T) -> Int? where T.Element == Int { + return rowRange.first(where: { !isPixelRowTransparent($0) }) + } + + @inlinable + func firstOpaquePixelColumn(in columnRange: T) -> Int? where T.Element == Int { + return columnRange.first(where: { column in + pixelRowRange.contains(where: { isPixelOpaque(column: column, row: $0) }) + }) + } + +} diff --git a/FontAwesome/AppKit/Extensions/NSButton.BezelStyle.swift b/FontAwesome/AppKit/Extensions/NSButton.BezelStyle.swift new file mode 100644 index 00000000..fe7f16f1 --- /dev/null +++ b/FontAwesome/AppKit/Extensions/NSButton.BezelStyle.swift @@ -0,0 +1,55 @@ +// NSButton.BezelStyle.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +internal extension NSButton.BezelStyle { + + var hasFixedHeight: Bool { + switch self { + case .rounded, + .roundRect, + .texturedRounded, + .recessed, + .disclosure, + .helpButton, + .roundedDisclosure, + .circular: + return true + default: + return false + } + } + + var hasFixedWidth: Bool { + switch self { + case .disclosure, + .helpButton, + .roundedDisclosure, + .circular: + return true + default: + return false + } + } + +} diff --git a/FontAwesome/AppKit/Extensions/NSSize.swift b/FontAwesome/AppKit/Extensions/NSSize.swift new file mode 100644 index 00000000..b1bc117f --- /dev/null +++ b/FontAwesome/AppKit/Extensions/NSSize.swift @@ -0,0 +1,35 @@ +// NSSize.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +internal extension NSSize { + + func adding(size: NSSize) -> NSSize { + return NSSize(width: width + size.width, height: height + size.height) + } + + func subtracting(width: CGFloat, height: CGFloat) -> NSSize { + return NSSize(width: self.width - width, height: self.height - height) + } + +} diff --git a/FontAwesome/AppKit/FontAwesomeButton.swift b/FontAwesome/AppKit/FontAwesomeButton.swift new file mode 100644 index 00000000..e7c71a6a --- /dev/null +++ b/FontAwesome/AppKit/FontAwesomeButton.swift @@ -0,0 +1,263 @@ +// FontAwesomeButton.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +@IBDesignable +open class FontAwesomeButton: NSButton { + + public typealias ButtonCell = FontAwesomeButtonCell + + @IBInspectable + open var iconCode: String { + get { return buttonCell?.iconCode ?? FontAwesome.fontAwesomeFlag.rawValue } + set { + assert(buttonCell != nil) + buttonCell?.iconCode = newValue + needsLayout = true + } + } + @IBInspectable + open var styleName: String { + get { return buttonCell?.styleName ?? FontAwesomeStyle.brands.rawValue } + set { + assert(buttonCell != nil) + buttonCell?.styleName = newValue + needsLayout = true + } + } + /// The extra padding applied to the button's size, when applicable. + /// + /// This property has a default value of `.zero`. + @IBInspectable + open var extraPadding: NSSize { + get { return buttonCell?.extraPadding ?? .zero } + set { + assert(buttonCell != nil) + buttonCell?.extraPadding = newValue + needsLayout = true + } + } + /// The point offset for the icon position. + /// + /// A positive `y` value indicates a shift above the text baseline, and a negative value indicates a shift + /// below the text baseline. Similarly, a positive `x` value indicates a horizontal shift towards the trailing + /// edge, and a negative value indicates a shift towards the leading edge. + /// + /// This property has a default value of `.zero`. + @IBInspectable + open var iconOffset: NSPoint { + get { return buttonCell?.iconOffset ?? .zero } + set { + assert(buttonCell != nil) + buttonCell?.iconOffset = newValue + needsLayout = true + } + } + @IBInspectable + open var iconPointSize: CGFloat { + get { return buttonCell?.iconPointSize ?? 0 } + set { + assert(buttonCell != nil) + buttonCell?.iconPointSize = newValue + needsLayout = true + } + } + /// The size of the Font Awesome icon. + /// + /// A value of `nil` implies the receiver assumes the value of `FontAwesomeConfig.defaultButtonIconSize`. + /// + /// This property has a default value of `nil`. + /// + /// - Note: The icon sizes corresponding to the values of `FontAwesomeIconSize` may not be aesthetically pleasing for + /// certain icons. Want better control? Your options include using `iconPointSize` or overriding + /// `FontAwesomeButton.iconHeight`. + open var iconSize: ButtonCell.IconSize? { + get { return buttonCell?.iconSize } + set { + assert(buttonCell != nil) + buttonCell?.iconSize = newValue + needsLayout = true + } + } + /// A convience setter for `iconSize` accessible through Interface Builder. + /// + /// Possible values are `"small"` or `"s"`, `"medium"` or `"m"`, and `"large"` or `"l"`. + /// + /// - Important: The usage of `iconSize` should be preferred for programmatic consumption. + @IBInspectable + open var iconSizeStringValue: String { + get { return (iconSize ?? FontAwesomeConfig.defaultButtonIconSize).rawValue } + set { iconSize = ButtonCell.IconSize(stringValue: newValue) } + } + /// Whether the receiver is inside of a toolbar. + /// + /// This property has a default value of `false`. + open var isInToolbar: Bool { + get { return buttonCell?.isInToolbar ?? false } + set { + assert(buttonCell != nil) + buttonCell?.isInToolbar = newValue + } + } + + open var buttonCell: ButtonCell? { + return cell as? ButtonCell + } + /// Returns the height of the Font Awesome icon used by the button. + /// + /// In computing the height, the default implementation depends on the values of the `controlSize`, `iconSize`, and + /// `isInToolbar` properties. + /// + /// - Note: The heights returned may not be aesthetically pleasing for certain icons. Want better control? Your options include + /// using `iconPointSize` or overriding `FontAwesomeButton.iconHeight`. + open var iconHeight: CGFloat { + // Note: `NSFont.systemFontSize` will typically be 13. The raw value of + // `NSControl.ControlSize`'s are 0, 1, and 2 for `.regular`, `.small`, + // and `.mini`, respectively. In all, this translates to 13, 12, and + // 11 for `.regular`, `.small`, and `.mini`, respectively. + var height = NSFont.systemFontSize - CGFloat(controlSize.rawValue) + + switch iconSize ?? FontAwesomeConfig.defaultButtonIconSize { + case .large: + height += 2 + case .small: + height -= 2 + default: () + } + + return height + (isInToolbar ? 2 : 0) + } + /// Returns the toolbar item's identifier that the receiver belongs to. + /// + /// - Important: The receiver must be in a toolbar item; otherwise, the return value is invalid. + open var toolbarItemIdentifier: NSToolbarItem.Identifier { + assert(isInToolbar) + return NSToolbarItem.Identifier(identifier?.rawValue ?? "") + } + + public override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + + commonInit() + } + + public init(icon: FontAwesome, style: FontAwesomeStyle = .brands) { + super.init(frame: .zero) + + commonInit() + + self.icon = icon + self.style = style + } + + open func commonInit() { + guard !(cell is ButtonCell) + else { return } + + cell = createCell() + setButtonType(.momentaryPushIn) + bezelStyle = .rounded + } + + open func createCell() -> ButtonCell { + return ButtonCell(imageCell: nil) + } + + open func adjustingSize(_ size: NSSize) -> NSSize { + guard let imageSize = image?.size + else { return size } + + var mutableSize = size + let insets = alignmentRectInsets + let pixelPoint = FALayoutManager.pixelPointValue + let estimatedSize = size.subtracting(width: insets.left + insets.right + imageSize.width, + height: insets.top + insets.bottom + imageSize.height) + + if !bezelStyle.hasFixedWidth + && estimatedSize.width.truncatingRemainder(dividingBy: 2 * pixelPoint) != 0 { + // Equidistant horizontal centering. + mutableSize.width += pixelPoint + } + + if !bezelStyle.hasFixedHeight + && estimatedSize.height.truncatingRemainder(dividingBy: 2 * pixelPoint) != 0 { + // Equidistant vertical centering. + mutableSize.height += pixelPoint + } + + return mutableSize + } + + open func bezelRect(forBounds rect: NSRect) -> NSRect { + if let bezelView = subviews.first, bezelView.className.hasSuffix("BezelView") { + return bezelView.alignmentRect(forFrame: rect) + } + + var bezelFrame = alignmentRect(forFrame: rect) + + if isFlipped { + bezelFrame.origin.y = rect.height - bezelFrame.maxY + } + + return bezelFrame + } + + open override func layout() { + updateFontIfNeeded() + + super.layout() + } + +} + +extension FontAwesomeButton: FontAwesomeTextRepresentable { + + open var maxIconSize: NSSize { + return iconPointSize > 0 + ? NSSize(width: 0, height: iconPointSize) + : NSSize(width: 1000, height: iconHeight) + } + + open func didUpdateFont() { + guard let icon = icon, let font = font + else { return } + + image = Image.fontAwesomeIcon(name: icon, + font: font, + textColor: .black, // Note: The image will be a template + size: maxIconSize) // image, so the color is largely + assert(image!.isTemplate) // irrelevant here. + + // Condition necessary for proper behavior of buttons in toolbar items in macOS 10.13. + if !isInToolbar { + sizeToFit() + } + } + +} diff --git a/FontAwesome/AppKit/FontAwesomeLabel.swift b/FontAwesome/AppKit/FontAwesomeLabel.swift new file mode 100644 index 00000000..29d0a9bd --- /dev/null +++ b/FontAwesome/AppKit/FontAwesomeLabel.swift @@ -0,0 +1,119 @@ +// FontAwesomeLabel.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +@IBDesignable +open class FontAwesomeLabel: NSTextField { + + @IBInspectable + open var iconCode: String = FontAwesome.fontAwesomeFlag.rawValue { + didSet { + stringValue = fontAwesomeIcon ?? "" + needsLayout = true + } + } + @IBInspectable + open var styleName: String = FontAwesomeStyle.brands.rawValue { + didSet { needsLayout = true } + } + @IBInspectable + open var iconPointSize: CGFloat = 0 { + didSet { needsLayout = true } + } + + public var textFieldCell: NSTextFieldCell? { + return cell as? NSTextFieldCell + } + + public override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + + // The cell will be created in `commonInit()`. + cell = nil + commonInit() + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + + public init(icon: FontAwesome, style: FontAwesomeStyle = .brands) { + super.init(frame: .zero) + + // The cell will be created in `commonInit()`. + cell = nil + commonInit() + + self.icon = icon + self.style = style + } + + open override func awakeFromNib() { + super.awakeFromNib() + + commonInit() + } + + open override func prepareForInterfaceBuilder() { + super.prepareForInterfaceBuilder() + + commonInit() + layout() + } + + open func commonInit() { + if cell == nil { + cell = NSTextFieldCell(textCell: "") + isSelectable = false + isBezeled = false + drawsBackground = false + } + + stringValue = fontAwesomeIcon ?? "" + } + + open override func layout() { + updateFontIfNeeded() + + super.layout() + } + +} + +extension FontAwesomeLabel: FontAwesomeTextRepresentable { + + open var maxIconSize: NSSize { + return superview is FontAwesomeView + ? superview!.bounds.size + : bounds.size + } + + open var usesTypographicBounds: Bool { + return true + } + + open func didUpdateFont() { + sizeToFit() + } + +} diff --git a/FontAwesome/AppKit/FontAwesomeLayoutManager.swift b/FontAwesome/AppKit/FontAwesomeLayoutManager.swift new file mode 100644 index 00000000..99f991e5 --- /dev/null +++ b/FontAwesome/AppKit/FontAwesomeLayoutManager.swift @@ -0,0 +1,127 @@ +// FontAwesomeLayoutManager.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +/// The convenience variable for the shared layout manager instance. +/// +/// - Note: This property is equivalent to calling `FontAwesomeLayoutManager.shared`. +@inlinable +internal var FALayoutManager: FontAwesomeLayoutManager { + return FontAwesomeLayoutManager.shared +} + +/// An object that coordinates the layout and display of Font Awesome icons. +public class FontAwesomeLayoutManager { + + /// Returns the shared instance of the layout manager. + public static var shared = FontAwesomeLayoutManager() + + public let textStorage: NSTextStorage + + private let textStorageLock: NSLock = NSLock() + + public lazy var sizingButton: NSButton = { NSButton() }() + public lazy var sizingTextField: NSTextField = { + if #available(OSX 10.12, *) { + return NSTextField(labelWithAttributedString: NSAttributedString()) + } else { + // Gotta do it ourselves. 🙄 + let textField = NSTextField() + textField.isBezeled = false + textField.drawsBackground = false + textField.isEditable = false + textField.isSelectable = false + return textField + } + }() + + /// The backing store pixel scale factor. + open var backingScaleFactor: CGFloat { + return NSApp?.windows.first?.backingScaleFactor ?? NSScreen.main?.backingScaleFactor ?? 2 + } + /// Returns the point value of a single pixel. + open var pixelPointValue: CGFloat { + return 1 / backingScaleFactor + } + /// Returns whether the application's initial window (or main screen) uses a high-resolution scaled display mode. + open var isHighResolutionDisplay: Bool { + return backingScaleFactor == 2 + } + + public var layoutManager: NSLayoutManager { + return textStorage.layoutManagers[0] + } + public var textContainer: NSTextContainer { + return layoutManager.textContainers[0] + } + + public init() { + let textContainer = NSTextContainer() + textContainer.lineFragmentPadding = 0 + + let layoutManager = NSLayoutManager() + layoutManager.typesetterBehavior = .behavior_10_2_WithCompatibility + layoutManager.addTextContainer(textContainer) + + textStorage = NSTextStorage() + textStorage.addLayoutManager(layoutManager) + } + + open func draw(_ attributedString: NSAttributedString, at point: NSPoint) { + textStorageLock.lock() + defer { textStorageLock.unlock() } + + textStorage.setAttributedString(attributedString) + let iconGlyphRange = layoutManager.glyphRange(for: textContainer) + layoutManager.drawBackground(forGlyphRange: iconGlyphRange, at: point) + layoutManager.drawGlyphs(forGlyphRange: iconGlyphRange, at: point) + } + + open func fittingSize(for button: NSButton) -> NSSize { + if button.needsLayout { + button.layoutSubtreeIfNeeded() + } + + sizingButton.controlSize = button.controlSize + sizingButton.bezelStyle = button.bezelStyle + sizingButton.image = button.image + + sizingButton.sizeToFit() + + return sizingButton.frame.size + } + + open func imageBounds(of attributedString: NSAttributedString) -> Rect { + return CTLineGetImageBounds(CTLineCreateWithAttributedString(attributedString), nil) + } + + open func lineHeight(for font: Font) -> CGFloat { + return layoutManager.defaultLineHeight(for: font) + } + + open func typographicBounds(of attributedString: NSAttributedString) -> Rect { + sizingTextField.attributedStringValue = attributedString + return CGRect(origin: .zero, size: sizingTextField.intrinsicContentSize) + } + +} diff --git a/FontAwesome/AppKit/FontAwesomeToolbar.swift b/FontAwesome/AppKit/FontAwesomeToolbar.swift new file mode 100644 index 00000000..0bdf94e6 --- /dev/null +++ b/FontAwesome/AppKit/FontAwesomeToolbar.swift @@ -0,0 +1,37 @@ +// FontAwesomeToolbar.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +open class FontAwesomeToolbar: NSToolbar { + + open override var sizeMode: NSToolbar.SizeMode { + get { return super.sizeMode } + set { + super.sizeMode = newValue + + visibleItems?.compactMap({ $0 as? FontAwesomeToolbarItem }) + .forEach({ $0.updateSize() }) + } + } + +} diff --git a/FontAwesome/AppKit/FontAwesomeToolbarItem.swift b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift new file mode 100644 index 00000000..e3bfbfc1 --- /dev/null +++ b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift @@ -0,0 +1,265 @@ +// FontAwesomeToolbarItem.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +open class FontAwesomeToolbarItem: NSToolbarItem { + + @IBInspectable + open var iconCode: String { + get { return button?.iconCode ?? "" } + set { + assert(button != nil) + button?.iconCode = newValue + } + } + @IBInspectable + open var styleName: String { + get { return button?.styleName ?? "" } + set { + assert(button != nil) + button?.styleName = newValue + } + } + /// The extra padding applied to the button's size, when applicable. + /// + /// This property has a default value of `.zero`. + @IBInspectable + open var extraPadding: NSSize { + get { return button?.extraPadding ?? .zero } + set { + assert(button != nil) + button?.extraPadding = newValue + } + } + /// The point offset for the icon position. + /// + /// A positive `y` value indicates a shift above the text baseline, and a negative value indicates a shift + /// below the text baseline. Similarly, a positive `x` value indicates a horizontal shift towards the trailing + /// edge, and a negative value indicates a shift towards the leading edge. + /// + /// This property has a default value of `.zero`. + @IBInspectable + open var iconOffset: NSPoint { + get { return button?.iconOffset ?? .zero } + set { + assert(button != nil) + button?.iconOffset = newValue + } + } + /// The font point size of the icon when the toolbar is in default/regular size mode (i.e. `sizeMode` has a value of `.default` + /// or `.regular`). + @IBInspectable + open var iconPointSize: CGFloat = 0 { + didSet { + if !(toolbar?.sizeMode == .small) { + updateSize() + } + } + } + /// A convience setter for `iconSize` accessible through Interface Builder. + /// + /// Possible values are `"small"` or `"s"`, `"medium"` or `"m"`, and `"large"` or `"l"`. + /// + /// - Important: The usage of `iconSize` should be preferred for programmatic consumption. + @IBInspectable + open var iconSizeStringValue: String { + get { return button?.iconSizeStringValue ?? "" } + set { + assert(button != nil) + button?.iconSizeStringValue = newValue + } + } + /// The font point size of the icon when the toolbar is in small size mode (i.e. `sizeMode` has a value of `.small`). + /// + /// When zero, the `iconPointSize` will be used. + /// + /// This property has a default value of `0`. + @IBInspectable + open var smallIconPointSize: CGFloat = 0 { + didSet { + if toolbar?.sizeMode == .small { + updateSize() + } + } + } + /// The pixel-perfect point width of the toolbar item's button. + /// + /// When zero, the width of the button will be sized to the minimum needed to contain the icon. + /// + /// This property has a default value of `38`. + /// + /// - Note: An extra _pixel_ may be added to the width when necessary to properly center the image within the button. + @IBInspectable + open var width: CGFloat = 38 { + didSet { updateSize() } + } + + /// The size of the Font Awesome icon. + /// + /// A value of `nil` implies the receiver assumes the value of `FontAwesomeConfig.defaultButtonIconSize`. + /// + /// This property has a default value of `nil`. + /// + /// - Note: The icon sizes corresponding to the values of `FontAwesomeIconSize` may not be aesthetically pleasing for + /// certain icons. Want better control? Your options include using `iconPointSize`/`smallIconPointSize` or + /// overriding `FontAwesomeButton.iconHeight`. + open var iconSize: FontAwesomeIconSize? { + get { return button?.iconSize } + set { + assert(button != nil) + button?.iconSize = newValue + } + } + + private var buttonImageKeyValueObservation: NSKeyValueObservation? + + /// Returns a point value that is added to the item's minimum and maximum width, when appropriate. + /// + /// This property is used to atone for any impurities in AppKit's geometry in order to attain pixel perfect button width. + open var bezelWidthAdjustment: CGFloat { + return FALayoutManager.isHighResolutionDisplay ? 1 : 0 + } + /// Whether the receiver has been configured. + open var isConfigured: Bool { + return button?.isInToolbar ?? false + } + /// Returns a value that is subtracted from `width` to obtain the button width for use when the toolbar is in _small_ size mode. + /// + /// This property has a default return value of `5` for high-resolution scaled display modes and `2` for all others. This is + /// consistent with the width change in native toolbar buttons. + open var smallSizeModeAdjustment: CGFloat { + return floor(2.5 * FALayoutManager.backingScaleFactor) + } + + public var button: FontAwesomeButton? { + return view as? FontAwesomeButton + } + + public init(itemIdentifier: NSToolbarItem.Identifier, + icon: FontAwesome, + style: FontAwesomeStyle = .brands) { + super.init(itemIdentifier: itemIdentifier) + + commonInit() + + self.icon = icon + self.style = style + } + + private override init(itemIdentifier: NSToolbarItem.Identifier) { + super.init(itemIdentifier: itemIdentifier) + + view = FontAwesomeButton() + } + + open override func awakeFromNib() { + super.awakeFromNib() + + commonInit() + } + + open override func prepareForInterfaceBuilder() { + super.prepareForInterfaceBuilder() + + commonInit() + } + + open func commonInit() { + if button == nil { + view = FontAwesomeButton() + } + + guard let button = button + else { return } + + button.isInToolbar = true + button.identifier = NSUserInterfaceItemIdentifier(itemIdentifier.rawValue) + button.bezelStyle = .texturedRounded + button.isEnabled = isEnabled + button.action = action + button.target = target + + updateSize() + + // Note: We invoke `updateSize()` in response to any observed change to the + // button's `image` property, which will ensure the image is horizontally + // centered in the button equidistantly. + buttonImageKeyValueObservation = button.buttonCell?.observe(\.image) { [weak self] (_, _) in + DispatchQueue.main.async { + self?.updateSize() + } + } + } + + open func fittingSize() -> NSSize { + guard let button = button + else { return .zero } + + if button.needsLayout { + button.layoutSubtreeIfNeeded() + } + + var size = NSSize(width: extraPadding.width, + height: button.fittingSize.height + extraPadding.height) + if width > 0 { + size.width += width + bezelWidthAdjustment + + if toolbar?.sizeMode == .small { + size.width -= smallSizeModeAdjustment + } + } else { + size.width += FALayoutManager.fittingSize(for: button).width + } + + return button.adjustingSize(size) + } + + open func updateSize() { + guard isConfigured + else { return } + + button?.iconPointSize = (toolbar?.sizeMode == .small && smallIconPointSize > 0) + ? smallIconPointSize + : iconPointSize + + minSize = fittingSize() + maxSize = minSize + } + + open override func validate() { + guard isConfigured + else { return } + + if let action = action, + let aTarget = NSApp.target(forAction: action, to: target, from: self) { + isEnabled = (aTarget as? NSToolbarItemValidation)?.validateToolbarItem(self) ?? true + } else { + isEnabled = false + } + + super.validate() + } + +} + +extension FontAwesomeToolbarItem: FontAwesomeRepresentable { } diff --git a/FontAwesome/AppKit/Protocols/FontAwesomeRawRepresentable.swift b/FontAwesome/AppKit/Protocols/FontAwesomeRawRepresentable.swift new file mode 100644 index 00000000..8f7137ed --- /dev/null +++ b/FontAwesome/AppKit/Protocols/FontAwesomeRawRepresentable.swift @@ -0,0 +1,48 @@ +// FontAwesomeRawRepresentable.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +public protocol FontAwesomeRawRepresentable: RawRepresentable where Self.RawValue == String { + + /// Creates a new instance with the specified icon name or raw value. + /// + /// If there is no icon that corresponds with the specified name or raw value, this initializer returns nil. + /// + /// - Parameter name: The icon name (e.g. `"camera"`) or raw value (e.g. `"fa-camera"`). + init?(name: String) + +} + +public extension FontAwesomeRawRepresentable { + + init?(name: String) { + if name.hasPrefix(.fontAwesomeIconNamePrefix) { + self.init(rawValue: name.lowercased()) + } else { + self.init(rawValue: .fontAwesomeIconNamePrefix + name.lowercased()) + } + } + +} + +extension FontAwesome: FontAwesomeRawRepresentable { } + +extension FontAwesomeBrands: FontAwesomeRawRepresentable { } diff --git a/FontAwesome/AppKit/Protocols/FontAwesomeRepresentable.swift b/FontAwesome/AppKit/Protocols/FontAwesomeRepresentable.swift new file mode 100644 index 00000000..3956aea9 --- /dev/null +++ b/FontAwesome/AppKit/Protocols/FontAwesomeRepresentable.swift @@ -0,0 +1,68 @@ +// FontAwesomeRepresentable.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +public protocol FontAwesomeRepresentable: class { + + /// The name of the [Font Awesome icon](https://fontawesome.com/icons?d=gallery). + /// + /// This property has a default value of `"fa-font-awesome-flag"` (`FontAwesome.fontAwesomeFlag`). + /// + /// - Note: The _fa-_ prefix is optional. For example, `"fa-camera"` and `"camera"` are equivalent. + /// + /// - Important: The usage of `icon` should be preferred. + var iconCode: String { get set } + /// The Font Awesome font style. + /// + /// This property has a default value of `"brands"` (`FontAwesomeStyle.brands`). + /// + /// - Important: The usage of `style` should be preferred. + var styleName: String { get set } + + /// The Unicode Font Awesome icon. + var fontAwesomeIcon: String? { get } + /// The [Font Awesome icon](https://fontawesome.com/icons?d=gallery). + var icon: FontAwesome? { get set } + /// The Font Awesome font style. + var style: FontAwesomeStyle { get set } + +} + +public extension FontAwesomeRepresentable { + + var fontAwesomeIcon: String? { + guard let icon = self.icon + else { return nil } + + return String.fontAwesomeIcon(name: icon) + } + + var icon: FontAwesome? { + get { return FontAwesome(name: iconCode) } + set { iconCode = newValue?.rawValue ?? "" } + } + + var style: FontAwesomeStyle { + get { return FontAwesomeStyle(rawValue: styleName.lowercased()) ?? .brands } + set { styleName = newValue.rawValue } + } + +} diff --git a/FontAwesome/FontAwesome.h b/FontAwesome/FontAwesome.h index b516f76f..817b3b4b 100644 --- a/FontAwesome/FontAwesome.h +++ b/FontAwesome/FontAwesome.h @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#import +#import FOUNDATION_EXPORT double FontAwesomeVersionNumber; FOUNDATION_EXPORT const unsigned char FontAwesomeVersionString[]; diff --git a/FontAwesome/FontAwesome.swift b/FontAwesome/FontAwesome.swift index c5e15551..7db7a762 100644 --- a/FontAwesome/FontAwesome.swift +++ b/FontAwesome/FontAwesome.swift @@ -20,7 +20,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#if canImport(UIKit) import UIKit +#else +import AppKit +#endif + import CoreText // MARK: - Public @@ -39,6 +44,18 @@ public struct FontAwesomeConfig { /// To use Font Awesome Pro fonts, you should add these to your main project and /// make sure they are added to the target and are included in the Info.plist file. public static var usesProFonts: Bool = false + + #if canImport(AppKit) + /// The default size of the Font Awesome icon in `FontAwesomeButton`s. + /// + /// This property has a default value of `.medium`. + /// + /// - Note: The icon sizes corresponding to the values of `FontAwesomeIconSize` may not be aesthetically pleasing for + /// certain icons. Want better control? Your options include using `FontAwesomeButton.iconPointSize` or + /// overriding `FontAwesomeButton.iconHeight`. + public static var defaultButtonIconSize: FontAwesomeIconSize = .medium + #endif + } public enum FontAwesomeStyle: String { @@ -86,28 +103,29 @@ public enum FontAwesomeStyle: String { } } -/// A FontAwesome extension to UIFont. -public extension UIFont { +/// A FontAwesome extension to UIFont/NSFont. +public extension Font { - /// Get a UIFont object of FontAwesome. + /// Get a font object of FontAwesome. /// /// - parameter ofSize: The preferred font size. - /// - returns: A UIFont object of FontAwesome. - class func fontAwesome(ofSize fontSize: CGFloat, style: FontAwesomeStyle) -> UIFont { + /// - returns: A font object of FontAwesome. + class func fontAwesome(ofSize fontSize: CGFloat, style: FontAwesomeStyle) -> Font { loadFontAwesome(ofStyle: style) - return UIFont(name: style.fontName(), size: fontSize)! + return Font(name: style.fontName(), size: fontSize)! } /// Loads the FontAwesome font in to memory. /// This method should be called when setting icons without using code. class func loadFontAwesome(ofStyle style: FontAwesomeStyle) { - if UIFont.fontNames(forFamilyName: style.fontFamilyName()).contains(style.fontName()) { + if Font.fontNames(forFamilyName: style.fontFamilyName()).contains(style.fontName()) { return } FontLoader.loadFont(style.fontFilename()) } - + + #if canImport(UIKit) /// Get a UIFont object of FontAwesome for a given text style /// /// - parameter forTextStyle: The preferred text style @@ -118,7 +136,6 @@ public extension UIFont { let pointSize = userFont.pointSize loadFontAwesome(ofStyle: style) let awesomeFont = UIFont(name: style.fontName(), size: pointSize)! - if #available(iOS 11.0, *), #available(watchOSApplicationExtension 4.0, *), #available(tvOS 11.0, *) { return UIFontMetrics.default.scaledFont(for: awesomeFont) } else { @@ -126,11 +143,15 @@ public extension UIFont { return awesomeFont.withSize(scale * awesomeFont.pointSize) } } + #endif } /// A FontAwesome extension to String. public extension String { + /// The FontAwesome icon name prefix (`"fa-"`). + static let fontAwesomeIconNamePrefix: String = "fa-" + /// Get a FontAwesome icon string with the given icon name. /// /// - parameter name: The preferred icon name. @@ -161,18 +182,18 @@ public extension String { } } -/// A FontAwesome extension to UIImage. -public extension UIImage { +/// A FontAwesome extension to UIImage/NSImage. +public extension Image { - /// Get a FontAwesome image with the given icon name, text color, size and an optional background color. + /// Get a FontAwesome image with the given icon, font, text color, size and an optional background color. /// /// - parameter name: The preferred icon name. - /// - parameter style: The font style. Either .solid, .regular or .brands. + /// - parameter font: The Font Awesome font. /// - parameter textColor: The text color. /// - parameter size: The image size. /// - parameter backgroundColor: The background color (optional). /// - returns: A string that will appear as icon with FontAwesome - static func fontAwesomeIcon(name: FontAwesome, style: FontAwesomeStyle, textColor: UIColor, size: CGSize, backgroundColor: UIColor = UIColor.clear, borderWidth: CGFloat = 0, borderColor: UIColor = UIColor.clear) -> UIImage { + static func fontAwesomeIcon(name: FontAwesome, font: Font, textColor: Color, size: CGSize, backgroundColor: Color = Color.clear, borderWidth: CGFloat = 0, borderColor: Color = Color.clear) -> Image { // Prevent application crash when passing size where width or height is set equal to or less than zero, by clipping width and height to a minimum of 1 pixel. var size = size @@ -182,25 +203,65 @@ public extension UIImage { let paragraph = NSMutableParagraphStyle() paragraph.alignment = NSTextAlignment.center - let fontSize = min(size.width / FontAwesomeConfig.fontAspectRatio, size.height) - // stroke width expects a whole number percentage of the font size - let strokeWidth: CGFloat = fontSize == 0 ? 0 : (-100 * borderWidth / fontSize) + let strokeWidth: CGFloat = font.pointSize == 0 ? 0 : (-100 * borderWidth / font.pointSize) - let attributedString = NSAttributedString(string: String.fontAwesomeIcon(name: name), attributes: [ - NSAttributedString.Key.font: UIFont.fontAwesome(ofSize: fontSize, style: style), + let icon = String.fontAwesomeIcon(name: name) + let attributedString = NSAttributedString(string: icon, attributes: [ + NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: textColor, NSAttributedString.Key.backgroundColor: backgroundColor, NSAttributedString.Key.paragraphStyle: paragraph, NSAttributedString.Key.strokeWidth: strokeWidth, NSAttributedString.Key.strokeColor: borderColor - ]) + ]) + #if canImport(UIKit) UIGraphicsBeginImageContextWithOptions(size, false, 0.0) - attributedString.draw(in: CGRect(x: 0, y: (size.height - fontSize) / 2, width: size.width, height: fontSize)) + attributedString.draw(in: CGRect(x: 0, y: (size.height - font.pointSize) / 2, width: size.width, height: font.pointSize)) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image! + #else + let pixelPoint = FALayoutManager.pixelPointValue + let iconRect = FALayoutManager.imageBounds(of: attributedString) + let origin = NSPoint(x: pixelPoint - iconRect.origin.x.roundUpToPixel(), + y: pixelPoint) + let imageSize = NSSize(width: iconRect.width.roundUpToPixel() + (2 * pixelPoint), + height: FALayoutManager.lineHeight(for: font) + (2 * pixelPoint)) + + let image = NSImage(size: imageSize) + image.isTemplate = backgroundColor == .clear && textColor == .black + + image.lockFocusFlipped(true) + FALayoutManager.draw(attributedString, at: origin) + image.unlockFocus() + + return image.trimmingTransparentPixels(maximumAlphaChannel: 35) ?? image + #endif + } + + /// Get a FontAwesome image with the given icon name, text color, size and an optional background color. + /// + /// - parameter name: The preferred icon name. + /// - parameter style: The font style. Either .solid, .regular or .brands. + /// - parameter textColor: The text color. + /// - parameter size: The image size. + /// - parameter backgroundColor: The background color (optional). + /// - returns: A string that will appear as icon with FontAwesome + static func fontAwesomeIcon(name: FontAwesome, style: FontAwesomeStyle, textColor: Color, size: CGSize, backgroundColor: Color = Color.clear, borderWidth: CGFloat = 0, borderColor: Color = Color.clear) -> Image { + let icon = String.fontAwesomeIcon(name: name) + #if canImport(UIKit) + let fontSize = min(size.width / FontAwesomeConfig.fontAspectRatio, size.height) + #else + let fontSize = size.width == 0 + // When `size.width` equals zero, then `size.height` specifies the font point size. + ? size.height + : Font.fontAwesomePointSize(of: icon, with: style, fitting: size) + #endif + let font = Font.fontAwesome(ofSize: fontSize, style: style) + + return fontAwesomeIcon(name: name, font: font, textColor: textColor, size: size, backgroundColor: backgroundColor, borderWidth: borderWidth, borderColor: borderColor) } /// Get a FontAwesome image with the given icon css code, text color, size and an optional background color. @@ -211,7 +272,7 @@ public extension UIImage { /// - parameter size: The image size. /// - parameter backgroundColor: The background color (optional). /// - returns: A string that will appear as icon with FontAwesome - static func fontAwesomeIcon(code: String, style: FontAwesomeStyle, textColor: UIColor, size: CGSize, backgroundColor: UIColor = UIColor.clear, borderWidth: CGFloat = 0, borderColor: UIColor = UIColor.clear) -> UIImage? { + static func fontAwesomeIcon(code: String, style: FontAwesomeStyle, textColor: Color, size: CGSize, backgroundColor: Color = Color.clear, borderWidth: CGFloat = 0, borderColor: Color = Color.clear) -> Image? { guard let name = String.fontAwesome(code: code) else { return nil } return fontAwesomeIcon(name: name, style: style, textColor: textColor, size: size, backgroundColor: backgroundColor, borderWidth: borderWidth, borderColor: borderColor) } diff --git a/FontAwesome/FontAwesomeImageRepresentable.swift b/FontAwesome/FontAwesomeImageRepresentable.swift index 0a814aa1..3639df3a 100644 --- a/FontAwesome/FontAwesomeImageRepresentable.swift +++ b/FontAwesome/FontAwesomeImageRepresentable.swift @@ -20,21 +20,21 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import UIKit +import CoreGraphics protocol FontAwesomeImageRepresentable: class { - typealias ImageConfig = (cssIconName: String, style: FontAwesomeStyle, color: UIColor?, backgroundColor: UIColor?) + typealias ImageConfig = (cssIconName: String, style: FontAwesomeStyle, color: Color?, backgroundColor: Color?) var imageWidth: CGFloat { get } var imageConfigs: [ImageConfig] { get } - func createImages(configurationHandler: (_ image: UIImage?, _ index: Int) -> Void) + func createImages(configurationHandler: (_ image: Image?, _ index: Int) -> Void) } extension FontAwesomeImageRepresentable { - func createImages(configurationHandler: (_ image: UIImage?, _ index: Int) -> Void) { + func createImages(configurationHandler: (_ image: Image?, _ index: Int) -> Void) { let imgSize = imageSizeForAspectRatio() for (index, config) in imageConfigs.enumerated() { let img = createImage(config: config, size: imgSize) @@ -42,15 +42,21 @@ extension FontAwesomeImageRepresentable { } } - private func createImage(config: ImageConfig, size: CGSize) -> UIImage? { - return UIImage.fontAwesomeIcon(code: config.cssIconName, - style: config.style, - textColor: config.color ?? .black, - size: size, - backgroundColor: config.backgroundColor ?? .clear) + private func createImage(config: ImageConfig, size: CGSize) -> Image? { + return Image.fontAwesomeIcon(code: config.cssIconName, + style: config.style, + textColor: config.color ?? .black, + size: size, + backgroundColor: config.backgroundColor ?? .clear) } private func imageSizeForAspectRatio() -> CGSize { + #if canImport(AppKit) + if let view = self as? View { + return view.bounds.size + } + #endif + return CGSize(width: imageWidth, height: imageWidth / FontAwesomeConfig.fontAspectRatio) } } diff --git a/FontAwesome/FontAwesomeImageView.swift b/FontAwesome/FontAwesomeImageView.swift index 0d51376d..6926c68d 100644 --- a/FontAwesome/FontAwesomeImageView.swift +++ b/FontAwesome/FontAwesomeImageView.swift @@ -20,22 +20,85 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#if canImport(UIKit) import UIKit +#else +import AppKit +#endif -@IBDesignable public class FontAwesomeImageView: UIImageView { +@IBDesignable public class FontAwesomeImageView: ImageView { @IBInspectable public var cssCode: String = "fa-font-awesome-flag" + // Note: Using the `Color` type alias breaks the IBDesignable functionality. + // Hence, the need for explicit definitions. + #if canImport(UIKit) @IBInspectable public var imageColor: UIColor = .black @IBInspectable public var imageBackgroundColor: UIColor = .clear + #else + @IBInspectable public var imageColor: NSColor = .black + @IBInspectable public var imageBackgroundColor: NSColor = .clear + #endif @IBInspectable public var styleName: String = "Brands" + #if canImport(AppKit) + /// The (exclusive) total horizontal and vertical change in points tolerated before the resolution of the backing + /// image (of the Font Awesome icon) is updated. + /// + /// Generating the icon image is a computationally intensive operation with a time complexity of _O(n²)_. Such + /// facets behoove us to minimize image generation, which can be accomplished by increasing the tolerance. On + /// the other hand, setting a value of `0` will result in image generation upon each invocation of `layout()`. + /// + /// This property has a default value of `10`. + @IBInspectable public var sizeChangeTolerance: CGFloat = 10 + #endif + + #if canImport(AppKit) + /// The size that was last used to generate the Font Awesome icon image. + private var lastBoundsUsedForImage: NSSize = .zero + + /// Returns the total point change to the size of the receiver since the last image was generated. + open var grossSizeChangeSinceImageGeneration: CGFloat { + return abs(bounds.width - lastBoundsUsedForImage.width) + + abs(bounds.height - lastBoundsUsedForImage.height) + } + #endif + + #if canImport(AppKit) + override public init(frame frameRect: NSRect) { + super.init(frame: frameRect) + commonInit() + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + } + + convenience public init(icon: FontAwesome, style: FontAwesomeStyle = .brands) { + self.init() + cssCode = icon.rawValue + styleName = style.rawValue + } + + public func commonInit() { + if cell == nil { + cell = NSImageCell(imageCell: nil) + imageScaling = .scaleProportionallyUpOrDown + } + } + #endif public override func awakeFromNib() { super.awakeFromNib() + #if canImport(AppKit) + commonInit() + #endif useFontAwesomeImage() } public override func prepareForInterfaceBuilder() { super.prepareForInterfaceBuilder() + #if canImport(AppKit) + commonInit() + #endif useFontAwesomeImage() } @@ -43,7 +106,33 @@ import UIKit createImages { (img, _) in image = img } + + #if canImport(AppKit) + lastBoundsUsedForImage = bounds.size + #endif + } + + #if canImport(AppKit) + open override func viewDidEndLiveResize() { + super.viewDidEndLiveResize() + + useFontAwesomeImage() + } + + open override func viewDidChangeBackingProperties() { + super.viewDidChangeBackingProperties() + + useFontAwesomeImage() + } + + open override func layout() { + if grossSizeChangeSinceImageGeneration >= sizeChangeTolerance { + useFontAwesomeImage() + } + + super.layout() } + #endif } diff --git a/FontAwesome/FontAwesomeTextRepresentable.swift b/FontAwesome/FontAwesomeTextRepresentable.swift index bff138af..0ed8c760 100644 --- a/FontAwesome/FontAwesomeTextRepresentable.swift +++ b/FontAwesome/FontAwesomeTextRepresentable.swift @@ -20,8 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#if canImport(UIKit) import UIKit +#endif +#if canImport(UIKit) protocol FontAwesomeTextRepresentable: FontAwesomeStateRequirement { var textSize: CGFloat { get } @@ -52,3 +55,57 @@ extension FontAwesomeTextRepresentable { } } } +#else +public protocol FontAwesomeTextRepresentable: FontAwesomeRepresentable { + + /// The font point size of the icon. + /// + /// When zero, the icon is sized to fit within the receiver's bounds. This property has a default value of `0`. + var iconPointSize: CGFloat { get } + /// The maximum size of the Font Awesome icon. + /// + /// This value is used along with `usesTypographicBounds` to compute a fitting font point size. + var maxIconSize: NSSize { get } + /// Whether to use the glyph's typographic bounds instead of the image bounds. + /// + /// When true, the icon will be typeset by the system. This includes various additional metrics, which results in a font point size + /// that can be drawn wholly within `maxIconSize`. + var usesTypographicBounds: Bool { get } + var font: Font? { get set } + + /// Updates the receiver's Font Awesome font, if needed. + func updateFontIfNeeded() + /// Called when the receiver's Font Awesome font is updated as a result of invoking `updateFontIfNeeded()`. + func didUpdateFont() + +} + +public extension FontAwesomeTextRepresentable { + + var usesTypographicBounds: Bool { + return false + } + + func updateFontIfNeeded() { + guard let stringValue = fontAwesomeIcon + else { return } + + let fontAwesomeStyle = style + let fontSize = iconPointSize > 0 + ? iconPointSize + : Font.fontAwesomePointSize(of: stringValue, + with: fontAwesomeStyle, + fitting: maxIconSize, + usingTypographicBounds: usesTypographicBounds) + let newFont = Font.fontAwesome(ofSize: fontSize, style: fontAwesomeStyle) + guard newFont != font + else { return } + + font = newFont + didUpdateFont() + } + + func didUpdateFont() { } + +} +#endif diff --git a/FontAwesome/FontAwesomeView.swift b/FontAwesome/FontAwesomeView.swift index efb5411b..67d906e8 100644 --- a/FontAwesome/FontAwesomeView.swift +++ b/FontAwesome/FontAwesomeView.swift @@ -20,22 +20,36 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#if canImport(UIKit) import UIKit +#else +import AppKit +#endif /// A view for FontAwesome icons. -@IBDesignable public class FontAwesomeView: UIView { +@IBDesignable public class FontAwesomeView: View { @IBInspectable public var iconCode: String = "" { didSet { + #if canImport(UIKit) self.iconView.text = String.fontAwesomeIcon(code: iconCode) + #else + self.iconView.iconCode = iconCode + #endif } } @IBInspectable - public var styleName: String = "Brands" + public var styleName: String = "Brands" { + didSet { + #if canImport(AppKit) + self.iconView.styleName = styleName + #endif + } + } - private var iconView = UILabel() + private var iconView = Label() override init(frame: CGRect) { super.init(frame: frame) @@ -52,25 +66,53 @@ import UIKit setupViews() } - /// Add a UILabel subview containing FontAwesome icon + /// Add a label subview containing FontAwesome icon func setupViews() { + #if canImport(UIKit) // Fits icon in the view self.iconView.textAlignment = NSTextAlignment.center self.iconView.text = String.fontAwesomeIcon(code: self.iconCode) self.iconView.textColor = self.tintColor + #else + self.iconView.iconCode = self.iconCode + self.iconView.styleName = self.styleName + #endif self.addSubview(iconView) + + #if canImport(AppKit) + self.iconView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + self.iconView.centerYAnchor.constraint(equalTo: self.centerYAnchor), + self.iconView.centerXAnchor.constraint(equalTo: self.centerXAnchor) + ]) + #endif } + #if canImport(UIKit) override public func tintColorDidChange() { self.iconView.textColor = self.tintColor } + #endif + #if canImport(UIKit) override public func layoutSubviews() { super.layoutSubviews() self.clipsToBounds = true let size = bounds.size.width < bounds.size.height ? bounds.size.width : bounds.size.height - let style = FontAwesomeStyle(rawValue: styleName) ?? .solid - self.iconView.font = UIFont.fontAwesome(ofSize: size, style: style) - self.iconView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: bounds.size.width, height: bounds.size.height)) + let style = FontAwesomeStyle(rawValue: styleName.lowercased()) ?? .solid + self.iconView.font = Font.fontAwesome(ofSize: size, style: style) + self.iconView.frame = CGRect(origin: .zero, size: CGSize(width: bounds.size.width, height: bounds.size.height)) + } + #else + override open func layout() { + self.iconView.needsLayout = true + super.layout() } + #endif + } + +#if canImport(AppKit) +extension FontAwesomeView: FontAwesomeRepresentable { } +#endif diff --git a/FontAwesome/Shims.swift b/FontAwesome/Shims.swift new file mode 100644 index 00000000..e1f6168b --- /dev/null +++ b/FontAwesome/Shims.swift @@ -0,0 +1,56 @@ +// Shims.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#if canImport(UIKit) +import UIKit + +public typealias Font = UIFont +public typealias Color = UIColor +public typealias Image = UIImage +public typealias Rect = CGRect +#if !os(watchOS) +public typealias ImageView = UIImageView +public typealias View = UIView +public typealias Label = UILabel +#endif /* !os(watchOS) */ +#else +import AppKit + +public typealias Font = NSFont +public typealias Color = NSColor +public typealias Image = NSImage +public typealias Rect = NSRect +public typealias ImageView = NSImageView +public typealias View = NSView +public typealias Label = FontAwesomeLabel + +extension Font { + + static func fontNames(forFamilyName familyName: String) -> [String] { + return NSFontManager.shared.availableMembers(ofFontFamily: familyName)? + .compactMap({ $0[0] as? String }) + ?? [] + } + +} + +#endif diff --git a/FontAwesome/FontAwesomeBarButtonItem.swift b/FontAwesome/UIKit/FontAwesomeBarButtonItem.swift similarity index 100% rename from FontAwesome/FontAwesomeBarButtonItem.swift rename to FontAwesome/UIKit/FontAwesomeBarButtonItem.swift diff --git a/FontAwesome/FontAwesomeSegmentedControl.swift b/FontAwesome/UIKit/FontAwesomeSegmentedControl.swift similarity index 100% rename from FontAwesome/FontAwesomeSegmentedControl.swift rename to FontAwesome/UIKit/FontAwesomeSegmentedControl.swift diff --git a/FontAwesome/FontAwesomeStateRequirement.swift b/FontAwesome/UIKit/FontAwesomeStateRequirement.swift similarity index 100% rename from FontAwesome/FontAwesomeStateRequirement.swift rename to FontAwesome/UIKit/FontAwesomeStateRequirement.swift diff --git a/FontAwesome/FontAwesomeTabBarItem.swift b/FontAwesome/UIKit/FontAwesomeTabBarItem.swift similarity index 100% rename from FontAwesome/FontAwesomeTabBarItem.swift rename to FontAwesome/UIKit/FontAwesomeTabBarItem.swift diff --git a/FontAwesomeTests/FontAwesomeTests.swift b/FontAwesomeTests/FontAwesomeTests.swift index 715dae23..a4c69d92 100644 --- a/FontAwesomeTests/FontAwesomeTests.swift +++ b/FontAwesomeTests/FontAwesomeTests.swift @@ -20,41 +20,64 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import UIKit import XCTest + +#if canImport(UIKit) +import UIKit +#else +import AppKit +#endif + +#if COCOAPODS +@testable import FontAwesome_swift +#else @testable import FontAwesome +#endif class FontAwesomeTests: XCTestCase { + #if canImport(AppKit) + func testFontAwesomeInit() { + XCTAssertEqual(FontAwesome(name: "camera"), .camera) + XCTAssertEqual(FontAwesome(name: "fa-camera"), .camera) + } + + func testFontAwesomeBrands() { + XCTAssertEqual(FontAwesomeBrands(name: "apple"), .apple) + XCTAssertEqual(FontAwesomeBrands(name: "fa-apple"), .apple) + } + #endif + func testFontsShouldBeLoaded() { - let solidFont = UIFont.fontAwesome(ofSize: 200, style: .solid) - let regularFont = UIFont.fontAwesome(ofSize: 200, style: .regular) - let brandsFont = UIFont.fontAwesome(ofSize: 200, style: .brands) + let solidFont = Font.fontAwesome(ofSize: 200, style: .solid) + let regularFont = Font.fontAwesome(ofSize: 200, style: .regular) + let brandsFont = Font.fontAwesome(ofSize: 200, style: .brands) XCTAssertNotNil(solidFont, "Solid font should be loaded.") XCTAssertNotNil(regularFont, "Regular font should be loaded.") XCTAssertNotNil(brandsFont, "Brands font should be loaded.") } func testIconFontShouldBeRegistered() { - let label = UILabel() - label.font = UIFont.fontAwesome(ofSize: 200, style: .brands) + let label = Label() + label.font = Font.fontAwesome(ofSize: 200, style: .brands) XCTAssertNotNil(label.font, "Icon font should not be nil.") } func testLabelText() { - let label = UILabel() - label.font = UIFont.fontAwesome(ofSize: 200, style: .brands) + let label = Label() + label.font = Font.fontAwesome(ofSize: 200, style: .brands) label.text = String.fontAwesomeIcon(name: FontAwesome.github) XCTAssertEqual(label.text, "\u{f09b}") } func testLabelTextFromCode() { - let label = UILabel() - label.font = UIFont.fontAwesome(ofSize: 200, style: .brands) + let label = Label() + label.font = Font.fontAwesome(ofSize: 200, style: .brands) label.text = String.fontAwesomeIcon(code: "fa-github") XCTAssertEqual(label.text, "\u{f09b}") } + #if canImport(UIKit) func testButtonTitle() { let button = UIButton() button.titleLabel?.font = UIFont.fontAwesome(ofSize: 30, style: .brands) @@ -95,7 +118,7 @@ class FontAwesomeTests: XCTestCase { let footnoteFont = UIFont.fontAwesome(forTextStyle: .footnote, style: .brands) let headlineFont = UIFont.fontAwesome(forTextStyle: .headline, style: .brands) let subheadlineFont = UIFont.fontAwesome(forTextStyle: .subheadline, style: .brands) - + XCTAssertNotNil(bodyFont, "Body font should be loaded.") XCTAssertNotNil(caption1Font, "Caption1 font should be loaded.") XCTAssertNotNil(caption2Font, "Caption2 font should be loaded.") @@ -108,18 +131,20 @@ class FontAwesomeTests: XCTestCase { let title1Font = UIFont.fontAwesome(forTextStyle: .title1, style: .brands) let title2Font = UIFont.fontAwesome(forTextStyle: .title2, style: .brands) let title3Font = UIFont.fontAwesome(forTextStyle: .title3, style: .brands) - + XCTAssertNotNil(calloutFont, "Callout font should be loaded.") XCTAssertNotNil(title1Font, "Title1 font should be loaded.") XCTAssertNotNil(title2Font, "Title2 font should be loaded.") XCTAssertNotNil(title3Font, "Title3 font should be loaded.") } - + + #if os(iOS) if #available(iOS 11.0, *) { let largeTitleFont = UIFont.fontAwesome(forTextStyle: .largeTitle, style: .brands) - + XCTAssertNotNil(largeTitleFont, "LargeTitle font should be loaded.") } + #endif } func testLabelTextUsingDynamicType() { @@ -128,4 +153,18 @@ class FontAwesomeTests: XCTestCase { label.text = String.fontAwesomeIcon(name: FontAwesome.github) XCTAssertEqual(label.text, "\u{f09b}") } + #endif +} + +// MARK: - Test Shims + +#if canImport(AppKit) +extension NSTextField { + + var text: String! { + get { return stringValue } + set { stringValue = newValue ?? "" } + } + } +#endif From 37f4771861be069198eb235cac36d69c05eda28e Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Tue, 11 Feb 2020 13:19:49 -0800 Subject: [PATCH 02/17] Add macOS targets to travis, fastlane, & cocoapods. --- .travis.yml | 3 +++ FontAwesome.swift.podspec | 15 ++++++++++++--- fastlane/Fastfile | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5a671fcc..96e456c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,11 @@ script: - brew tap thii/xcbeautify https://github.com/thii/xcbeautify.git - brew install xcbeautify - xcodebuild test -project FontAwesome.xcodeproj -scheme FontAwesome -destination 'platform=iOS Simulator,name=iPhone XS' | xcbeautify +- xcodebuild test -project FontAwesome.xcodeproj -scheme FontAwesome-macOS -destination 'platform=macOS' | xcbeautify +- xcodebuild test -project Demo/FontAwesomeDemo.xcodeproj -scheme 'FontAwesomeDemo (macOS)' | xcbeautify - xcodebuild -project FontAwesome.xcodeproj -scheme FontAwesome-watchOS -destination 'platform=watchOS Simulator,name=Apple Watch Series 4 - 44mm' | xcbeautify - xcodebuild -project FontAwesome.xcodeproj -scheme FontAwesome-tvOS -destination 'platform=tvOS Simulator,name=Apple TV' | xcbeautify +- pod lib lint --allow-warnings before_deploy: - carthage build --no-skip-current - carthage archive FontAwesome diff --git a/FontAwesome.swift.podspec b/FontAwesome.swift.podspec index 716f38c5..19a7da6d 100644 --- a/FontAwesome.swift.podspec +++ b/FontAwesome.swift.podspec @@ -9,10 +9,19 @@ Pod::Spec.new do |s| s.ios.deployment_target = '8.0' s.tvos.deployment_target = '9.0' + s.osx.deployment_target = '10.11' s.requires_arc = true - s.source_files = 'FontAwesome/*.{swift}' - s.resource_bundle = { 'FontAwesome.swift' => 'FontAwesome/*.otf' } - s.frameworks = 'UIKit', 'CoreText' + s.source_files = 'FontAwesome/*.{swift}', 'FontAwesome/Extensions/*.{swift}' + s.ios.source_files = s.tvos.source_files = 'FontAwesome/UIKit/*.{swift}' + s.osx.source_files = 'FontAwesome/AppKit/**/*.{swift}' + s.resources = 'FontAwesome/*.otf' + s.frameworks = 'CoreText' + s.ios.frameworks = s.tvos.frameworks = 'UIKit' + s.osx.frameworks = 'AppKit' s.swift_version = "5.0" + + s.test_spec do |test_spec| + test_spec.source_files = "FontAwesomeTests/**/*.swift" + end end diff --git a/fastlane/Fastfile b/fastlane/Fastfile index be921bfb..f8ec9c0a 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -2,6 +2,8 @@ desc "Runs all the tests" lane :test do Dir.chdir("..") do sh("xcodebuild test -project FontAwesome.xcodeproj -scheme FontAwesome -destination 'platform=iOS Simulator,name=iPhone XS' | xcbeautify") + sh("xcodebuild test -project FontAwesome.xcodeproj -scheme FontAwesome-macOS -destination 'platform=macOS' | xcbeautify") + sh("xcodebuild test -project Demo/FontAwesomeDemo.xcodeproj -scheme 'FontAwesomeDemo (macOS)' | xcbeautify") sh("xcodebuild -project FontAwesome.xcodeproj -scheme FontAwesome-watchOS -destination 'OS=5.1,name=Apple Watch Series 4 - 44mm' | xcbeautify") end end From e7c24e05c9a0c7611b2f850736dbaae1b232317c Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Tue, 11 Feb 2020 14:05:40 -0800 Subject: [PATCH 03/17] Update typesetter behavior. --- FontAwesome/AppKit/FontAwesomeLayoutManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FontAwesome/AppKit/FontAwesomeLayoutManager.swift b/FontAwesome/AppKit/FontAwesomeLayoutManager.swift index 99f991e5..8e3ca264 100644 --- a/FontAwesome/AppKit/FontAwesomeLayoutManager.swift +++ b/FontAwesome/AppKit/FontAwesomeLayoutManager.swift @@ -80,7 +80,7 @@ public class FontAwesomeLayoutManager { textContainer.lineFragmentPadding = 0 let layoutManager = NSLayoutManager() - layoutManager.typesetterBehavior = .behavior_10_2_WithCompatibility + layoutManager.typesetterBehavior = .behavior_10_4 layoutManager.addTextContainer(textContainer) textStorage = NSTextStorage() From b6750b19ee531f47e8b11090a94e777be08470f9 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Tue, 11 Feb 2020 16:00:58 -0800 Subject: [PATCH 04/17] Update README with macOS platform compatibility & add 'Features' section --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 64c7ad8c..9818ed27 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,19 @@ Use Font Awesome in your Swift projects To see the complete set of 3,978 icons in Font Awesome 5, please check the [FontAwesome.com](http://fontawesome.com/icons/) site. +## Features + +### iOS + +* Ready to use subclasses of `UIView`, `UIBarButtonItem`, `UIImageView`, `FontAwesomeSegmentedControl`, & more. + +### macOS + +* Ready to use subclasses of `NSImageView`, `NSButton`, `NSToolbarItem`, `NSTextField`, & more. +* Dark mode ready. +* Pixel perfect horizontal & vertical image alignment. +* Consistent icon layout across major macOS releases. + ## Examples ![](./.github/examples.png) @@ -31,7 +44,8 @@ To see the complete set of 3,978 icons in Font Awesome 5, please check the [Font ## Requirements -iOS 8 or later. +* iOS 8+ +* macOS 10.11+ ## Development To update this project to include all the latest icons from the new verison of From de72e5f1a1fe2b18502c3568b48464638a9c2061 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Tue, 11 Feb 2020 16:19:19 -0800 Subject: [PATCH 05/17] Fix file header in NSToolbarItem.Identifier.swift --- .../Extensions/NSToolbarItem.Identifier.swift | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift b/Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift index 474afe8b..98c4680a 100644 --- a/Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift +++ b/Demo/FontAwesomeDemo (macOS)/Extensions/NSToolbarItem.Identifier.swift @@ -1,9 +1,24 @@ +// NSToolbarItem.Identifier.swift // -// NSToolbarItem.Identifier.swift -// FontAwesomeDemo (macOS) +// Copyright (c) 2014-present FontAwesome.swift contributors // -// Created by Chris Zielinski on 1/14/20. +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: // +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. import AppKit From 67d3e69fa351b1dde7d62971b66ecce87eb1149e Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Tue, 11 Feb 2020 16:27:33 -0800 Subject: [PATCH 06/17] Downgrade macOS demo deployment target to 10.11 --- Demo/FontAwesomeDemo.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj b/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj index 19a290b4..b94de657 100644 --- a/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj +++ b/Demo/FontAwesomeDemo.xcodeproj/project.pbxproj @@ -542,7 +542,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "FontAwesomeDemo (macOS)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.thi.FontAwesomeDemo-macOS"; @@ -573,7 +573,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "FontAwesomeDemo (macOS)/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "im.thi.FontAwesomeDemo-macOS"; PRODUCT_MODULE_NAME = FontAwesomeDemoMacOS; From 1e97efa58b3dbbc22cdc5efedf0a1c66e3b10352 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Thu, 13 Feb 2020 18:55:30 -0800 Subject: [PATCH 07/17] Fix macOS version compatibility. --- .../Extensions/NSButton.BezelStyle.swift | 35 ++++++++- .../FontAwesomeMacOSDemoUnitTests.swift | 63 +++++++++++----- FontAwesome.xcodeproj/project.pbxproj | 8 ++ .../AppKit/Cells/FontAwesomeButtonCell.swift | 2 +- .../Cells/FontAwesomeTextFieldCell.swift | 74 +++++++++++++++++++ FontAwesome/AppKit/Extensions/Font.swift | 8 +- .../AppKit/Extensions/NSAppKitVersion.swift | 33 +++++++++ FontAwesome/AppKit/FontAwesomeLabel.swift | 59 +++++++++------ .../AppKit/FontAwesomeLayoutManager.swift | 22 ++---- 9 files changed, 242 insertions(+), 62 deletions(-) create mode 100644 FontAwesome/AppKit/Cells/FontAwesomeTextFieldCell.swift create mode 100644 FontAwesome/AppKit/Extensions/NSAppKitVersion.swift diff --git a/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift b/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift index 1760de95..59d97c6d 100644 --- a/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift +++ b/Demo/FontAwesomeDemoUnitTests (macOS)/Extensions/NSButton.BezelStyle.swift @@ -22,7 +22,7 @@ import AppKit -extension NSButton.BezelStyle: CaseIterable { +extension NSButton.BezelStyle: CaseIterable, CustomDebugStringConvertible { public static var allCases: [NSButton.BezelStyle] { return [ @@ -42,4 +42,37 @@ extension NSButton.BezelStyle: CaseIterable { ] } + public var debugDescription: String { + switch self { + case .rounded: + return ".rounded" + case .regularSquare: + return ".regularSquare" + case .disclosure: + return ".disclosure" + case .shadowlessSquare: + return ".shadowlessSquare" + case .circular: + return ".circular" + case .texturedSquare: + return ".texturedSquare" + case .helpButton: + return ".helpButton" + case .smallSquare: + return ".smallSquare" + case .texturedRounded: + return ".texturedRounded" + case .roundRect: + return ".roundRect" + case .recessed: + return ".recessed" + case .roundedDisclosure: + return ".roundedDisclosure" + case .inline: + return ".inline" + @unknown default: + return "" + } + } + } diff --git a/Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift b/Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift index 51179320..e545e46c 100644 --- a/Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift +++ b/Demo/FontAwesomeDemoUnitTests (macOS)/FontAwesomeMacOSDemoUnitTests.swift @@ -48,24 +48,32 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { } func testLabel() { + continueAfterFailure = true + let label: FontAwesomeLabel = viewController.fontAwesomeLabel XCTAssertEqual(label.iconPointSize, 50) XCTAssertEqual(label.stringValue, FontAwesome.github.unicode) XCTAssertEqual(label.font?.pointSize, 50) - XCTAssertEqual(label.bounds.size, NSSize(width: isHighResolutionDisplay ? 52.5 : 53, - height: 61)) + XCTAssertEqual(label.bounds.size, + NSSize(width: isHighResolutionDisplay ? 53.5 : 55, height: 50)) } func testView() { - let size = isHighResolutionDisplay - ? NSSize(width: 138.5, height: 167) - : NSSize(width: 141, height: 170) + let fontPointSize = NSAppKitVersion.isMacOSMojaveOrNewer + ? CGFloat(137.5).roundDownToPixel() + : 139 + let size = NSAppKitVersion.isMacOSMojaveOrNewer + ? NSSize(width: CGFloat(138.5).roundUpToPixel(), height: 137) + : NSSize(width: 141, height: 139) let labelSubview = viewController.fontAwesomeView.subviews.first as? FontAwesomeLabel XCTAssertNotNil(labelSubview) + + continueAfterFailure = true + XCTAssertEqual(labelSubview!.stringValue, FontAwesome.github.unicode) - XCTAssertEqual(labelSubview!.font?.pointSize, size.width) + XCTAssertEqual(labelSubview!.font?.pointSize, fontPointSize) XCTAssertEqual(labelSubview!.bounds.size, size) } @@ -108,6 +116,8 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { } func testIconHeight() { + continueAfterFailure = true + let button = FontAwesomeButton(icon: .fontAwesomeFlag) button.controlSize = .regular @@ -121,6 +131,8 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { } func testEquatable() { + continueAfterFailure = true + XCTAssertEqual(NSToolbar.SizeMode.regular, NSToolbar.SizeMode.default) XCTAssertTrue(NSToolbar.SizeMode.regular == NSToolbar.SizeMode.default) XCTAssertEqual(NSToolbar.SizeMode.small, NSToolbar.SizeMode.small) @@ -132,6 +144,8 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { } func testImageVerticalAdjustmentForAllBezels() { + continueAfterFailure = true + let button = FontAwesomeButton() for bezelStyle in NSButton.BezelStyle.allCases { @@ -226,7 +240,15 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { func expectedImageSize(for item: FontAwesomeToolbarItem) -> NSSize { var size: NSSize - if toolbar?.sizeMode == .small { + if NSAppKitVersion.isMacOSMojaveOrNewer && !isHighResolutionDisplay { + if toolbar?.sizeMode == .small { + guard item.itemIdentifier != .fixedPointSize + else { return NSSize(width: 15, height: 15) } + size = NSSize(width: 14, height: 14) + } else { + size = NSSize(width: 15, height: 15) + } + } else if toolbar?.sizeMode == .small { size = isHighResolutionDisplay ? NSSize(width: 15, height: 14.5) : NSSize(width: 14, height: 14) @@ -250,16 +272,17 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { } func expectedImageOrigin(for item: FontAwesomeToolbarItem) -> NSPoint { - var point: NSPoint = .zero - if toolbar?.sizeMode == .small { - point.y = isHighResolutionDisplay ? 3.5 : 4 - } else { - point.y = isHighResolutionDisplay ? 6 : 7 - } + guard let button = item.button, let buttonCell = button.buttonCell + else { return .zero } + + let bezelRect = button.bezelRect(forBounds: button.bounds) + var point: NSPoint = .zero point.x = ((expectedItemWidth(for: item) - expectedImageSize(for: item).width) / 2) + (item.bezelWidthAdjustment / 2) - + point.y = bezelRect.origin.y + + ((bezelRect.height - expectedImageSize(for: item).height) / 2).roundUpToPixel() + + buttonCell.verticalImageAdjustmentForBezel return point } @@ -273,12 +296,16 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { + " a valid `button` value", file: file, line: line) - XCTAssertTrue(item.button!.isInToolbar, + + guard let button = item.button + else { return } + + XCTAssertTrue(button.isInToolbar, "the item \"\(item.itemIdentifier.rawValue)\"'s button does not have " + "the correct `isInToolbar` value") let expectedWidth = expectedItemWidth(for: item) - XCTAssertEqual(item.button!.frame.width - item.bezelWidthAdjustment, + XCTAssertEqual(button.frame.width - item.bezelWidthAdjustment, expectedWidth, "the item \"\(item.itemIdentifier.rawValue)\" does not have" + " the expected button width \(expectedWidth)", @@ -292,7 +319,7 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { line: line) let expectedSize = expectedImageSize(for: item) - XCTAssertEqual(item.button!.image?.size, + XCTAssertEqual(button.image?.size, expectedSize, "the item \"\(item.itemIdentifier.rawValue)\" does not have" + " the expected image size \(expectedSize)", @@ -300,7 +327,7 @@ class FontAwesomeMacOSDemoUnitTests: XCTestCase { line: line) let expectedOrigin = expectedImageOrigin(for: item) - XCTAssertEqual(item.button!.buttonCell?.imageRect(forBounds: item.button!.bounds).origin, + XCTAssertEqual(button.buttonCell?.imageRect(forBounds: button.bounds).origin, expectedOrigin, "the item \"\(item.itemIdentifier.rawValue)\" does not have" + " the expected image origin \(expectedOrigin)", diff --git a/FontAwesome.xcodeproj/project.pbxproj b/FontAwesome.xcodeproj/project.pbxproj index 9d890b56..6b7e8fd5 100644 --- a/FontAwesome.xcodeproj/project.pbxproj +++ b/FontAwesome.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 145FF26E23ECC9F5000A7FD5 /* NSSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 145FF26D23ECC9F5000A7FD5 /* NSSize.swift */; }; 146609CE23CFAE2300DB1F4D /* Image+Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 146609CD23CFAE2300DB1F4D /* Image+Trim.swift */; }; 1466724823EBE03C0000389E /* NSButton.BezelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1466724723EBE03C0000389E /* NSButton.BezelStyle.swift */; }; + 147659AA23F3C1EB000CD514 /* NSAppKitVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147659A923F3C1EB000CD514 /* NSAppKitVersion.swift */; }; 149644E223D998B500045A58 /* FontAwesomeLayoutManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 149644E123D998B500045A58 /* FontAwesomeLayoutManager.swift */; }; 14971E9923D7FBEE00C1868E /* FontAwesome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8401A8D21B39601400269D14 /* FontAwesome.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 14971E9A23D7FBEE00C1868E /* FontAwesome.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 144772A0236DF81D00597F27 /* FontAwesome.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; @@ -38,6 +39,7 @@ 14C856B5236FC74600C6409A /* FontAwesomeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A381ECC816A0010B0BA /* FontAwesomeImageView.swift */; }; 14C856B6236FC75E00C6409A /* FontAwesomeImageRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A371ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift */; }; 14C856B9236FC93F00C6409A /* FontAwesomeToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14C856B8236FC93F00C6409A /* FontAwesomeToolbarItem.swift */; }; + 14ECBBCF23F5C9B5009DDA4F /* FontAwesomeTextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14ECBBCE23F5C9B5009DDA4F /* FontAwesomeTextFieldCell.swift */; }; 484F4A3D1ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A361ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift */; }; 484F4A3E1ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A371ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift */; }; 484F4A3F1ECC816A0010B0BA /* FontAwesomeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 484F4A381ECC816A0010B0BA /* FontAwesomeImageView.swift */; }; @@ -83,11 +85,13 @@ 145FF26D23ECC9F5000A7FD5 /* NSSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSSize.swift; sourceTree = ""; }; 146609CD23CFAE2300DB1F4D /* Image+Trim.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image+Trim.swift"; sourceTree = ""; }; 1466724723EBE03C0000389E /* NSButton.BezelStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSButton.BezelStyle.swift; sourceTree = ""; }; + 147659A923F3C1EB000CD514 /* NSAppKitVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAppKitVersion.swift; sourceTree = ""; }; 149644E123D998B500045A58 /* FontAwesomeLayoutManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeLayoutManager.swift; sourceTree = ""; }; 14A04525238DA90700E309DE /* CGFloat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGFloat.swift; sourceTree = ""; }; 14C856AD236F732A00C6409A /* FontAwesomeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeLabel.swift; sourceTree = ""; }; 14C856AF236F8A6300C6409A /* Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; 14C856B8236FC93F00C6409A /* FontAwesomeToolbarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeToolbarItem.swift; sourceTree = ""; }; + 14ECBBCE23F5C9B5009DDA4F /* FontAwesomeTextFieldCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontAwesomeTextFieldCell.swift; sourceTree = ""; }; 484F4A361ECC816A0010B0BA /* FontAwesomeBarButtonItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontAwesomeBarButtonItem.swift; sourceTree = ""; }; 484F4A371ECC816A0010B0BA /* FontAwesomeImageRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontAwesomeImageRepresentable.swift; sourceTree = ""; }; 484F4A381ECC816A0010B0BA /* FontAwesomeImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FontAwesomeImageView.swift; sourceTree = ""; }; @@ -166,6 +170,7 @@ isa = PBXGroup; children = ( 141B0EAA2374F57A00A019FD /* FontAwesomeButtonCell.swift */, + 14ECBBCE23F5C9B5009DDA4F /* FontAwesomeTextFieldCell.swift */, ); path = Cells; sourceTree = ""; @@ -202,6 +207,7 @@ 14A04525238DA90700E309DE /* CGFloat.swift */, 14C856AF236F8A6300C6409A /* Font.swift */, 146609CD23CFAE2300DB1F4D /* Image+Trim.swift */, + 147659A923F3C1EB000CD514 /* NSAppKitVersion.swift */, 1466724723EBE03C0000389E /* NSButton.BezelStyle.swift */, 145FF26D23ECC9F5000A7FD5 /* NSSize.swift */, ); @@ -548,12 +554,14 @@ 145FF26E23ECC9F5000A7FD5 /* NSSize.swift in Sources */, 144772A9236DF8CF00597F27 /* Enum.swift in Sources */, 144772AA236DF8CF00597F27 /* FontAwesome.swift in Sources */, + 14ECBBCF23F5C9B5009DDA4F /* FontAwesomeTextFieldCell.swift in Sources */, 149644E223D998B500045A58 /* FontAwesomeLayoutManager.swift in Sources */, 141B0EA62374983F00A019FD /* FontAwesomeRepresentable.swift in Sources */, 144772AC236DF8CF00597F27 /* FontAwesomeExtension.swift in Sources */, 1403403723E6513F00B22878 /* FontAwesomeToolbar.swift in Sources */, 144635DD23E3AB6800464BB8 /* FontAwesomeIconSize.swift in Sources */, 14C856B1236F8A6B00C6409A /* Font.swift in Sources */, + 147659AA23F3C1EB000CD514 /* NSAppKitVersion.swift in Sources */, 144772BF236E05E700597F27 /* FontAwesomeView.swift in Sources */, 144772BD236DF9B500597F27 /* Shims.swift in Sources */, 141B0EAB2374F57A00A019FD /* FontAwesomeButtonCell.swift in Sources */, diff --git a/FontAwesome/AppKit/Cells/FontAwesomeButtonCell.swift b/FontAwesome/AppKit/Cells/FontAwesomeButtonCell.swift index 899d9aad..bb08d41b 100644 --- a/FontAwesome/AppKit/Cells/FontAwesomeButtonCell.swift +++ b/FontAwesome/AppKit/Cells/FontAwesomeButtonCell.swift @@ -43,7 +43,7 @@ open class FontAwesomeButtonCell: NSButtonCell { /// Mirrors `NSButtonCell`'s private `_imageVerticalAdjustmentForBezel` property. open var verticalImageAdjustmentForBezel: CGFloat { - if FALayoutManager.isHighResolutionDisplay { + if NSAppKitVersion.isMacOSMojaveOrNewer { if bezelStyle == .texturedRounded { return FALayoutManager.pixelPointValue } diff --git a/FontAwesome/AppKit/Cells/FontAwesomeTextFieldCell.swift b/FontAwesome/AppKit/Cells/FontAwesomeTextFieldCell.swift new file mode 100644 index 00000000..322d4dfe --- /dev/null +++ b/FontAwesome/AppKit/Cells/FontAwesomeTextFieldCell.swift @@ -0,0 +1,74 @@ +// FontAwesomeTextFieldCell.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +open class FontAwesomeTextFieldCell: NSTextFieldCell { + + /// See `FontAwesomeLabel.iconCode`'s documentation. + public var iconCode: String = FontAwesome.fontAwesomeFlag.rawValue { + didSet { stringValue = fontAwesomeLabel?.fontAwesomeIcon ?? "" } + } + /// See `FontAwesomeLabel.iconPointSize`'s documentation. + public var iconPointSize: CGFloat = 0 + /// See `FontAwesomeLabel.styleName`'s documentation. + public var styleName: String = FontAwesomeStyle.brands.rawValue + + public var fontAwesomeLabel: FontAwesomeLabel? { + return controlView as? FontAwesomeLabel + } + + public override init(textCell string: String) { + super.init(textCell: string) + + commonInit() + } + + public required init(coder: NSCoder) { + super.init(coder: coder) + } + + open override func awakeFromNib() { + super.awakeFromNib() + + commonInit() + } + + open func commonInit() { + isSelectable = false + isBezeled = false + drawsBackground = false + } + + open override var cellSize: NSSize { + var bounds = FALayoutManager.typographicBounds(of: attributedStringValue).size + bounds.width = (bounds.width + (2 * FALayoutManager.pixelPointValue)).roundUpToPixel() + return bounds + } + + open override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { + let imageBounds = FALayoutManager.imageBounds(of: attributedStringValue) + let x = ((cellFrame.width - imageBounds.width) / 2).roundDownToPixel() + FALayoutManager.draw(attributedStringValue, at: CGPoint(x: x, y: 0)) + } + +} diff --git a/FontAwesome/AppKit/Extensions/Font.swift b/FontAwesome/AppKit/Extensions/Font.swift index b26f3368..8040d50b 100644 --- a/FontAwesome/AppKit/Extensions/Font.swift +++ b/FontAwesome/AppKit/Extensions/Font.swift @@ -41,15 +41,17 @@ extension Font { let arbitraryPointSize: CGFloat = 100 let font = Font.fontAwesome(ofSize: arbitraryPointSize, style: style) let attributedString = NSMutableAttributedString(string: icon, attributes: [.font: font]) + let adjustment = usingTypographicBounds ? 2 * FALayoutManager.pixelPointValue : 0 + let adjustedMaxSize = NSSize(width: maxSize.width - adjustment, height: maxSize.height) let iconSize = usingTypographicBounds ? FALayoutManager.typographicBounds(of: attributedString).size : FALayoutManager.imageBounds(of: attributedString).size - let iconAspectRatio = iconSize.width / iconSize.height let heightRatio = iconSize.height / arbitraryPointSize - let heightMaxPointSize = maxSize.height / heightRatio - let widthMaxPointSize = (maxSize.width / iconAspectRatio) / heightRatio + let widthRatio = iconSize.width / arbitraryPointSize + let heightMaxPointSize = adjustedMaxSize.height / heightRatio + let widthMaxPointSize = adjustedMaxSize.width / widthRatio let minimumPointSize = min(heightMaxPointSize, widthMaxPointSize).roundDownToPixel() return max(minimumPointSize, 0.1) } diff --git a/FontAwesome/AppKit/Extensions/NSAppKitVersion.swift b/FontAwesome/AppKit/Extensions/NSAppKitVersion.swift new file mode 100644 index 00000000..a7663640 --- /dev/null +++ b/FontAwesome/AppKit/Extensions/NSAppKitVersion.swift @@ -0,0 +1,33 @@ +// NSAppKitVersion.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import AppKit + +internal extension NSAppKitVersion { + + static var isMacOSMojaveOrNewer: Bool { + // Note: `1671` is the raw value of `.number10_14`. + // Hardcoded to allow compilation on macOS Mojave (10.14) and earlier. + return current.rawValue >= 1671 + } + +} diff --git a/FontAwesome/AppKit/FontAwesomeLabel.swift b/FontAwesome/AppKit/FontAwesomeLabel.swift index 29d0a9bd..d3a39321 100644 --- a/FontAwesome/AppKit/FontAwesomeLabel.swift +++ b/FontAwesome/AppKit/FontAwesomeLabel.swift @@ -25,55 +25,65 @@ import AppKit @IBDesignable open class FontAwesomeLabel: NSTextField { + public typealias TextFieldCell = FontAwesomeTextFieldCell + @IBInspectable - open var iconCode: String = FontAwesome.fontAwesomeFlag.rawValue { - didSet { - stringValue = fontAwesomeIcon ?? "" + open var iconCode: String { + get { return textFieldCell?.iconCode ?? FontAwesome.fontAwesomeFlag.rawValue } + set { + assert(textFieldCell != nil) + textFieldCell?.iconCode = newValue needsLayout = true } } @IBInspectable - open var styleName: String = FontAwesomeStyle.brands.rawValue { - didSet { needsLayout = true } + open var styleName: String { + get { return textFieldCell?.styleName ?? FontAwesomeStyle.brands.rawValue } + set { + assert(textFieldCell != nil) + textFieldCell?.styleName = newValue + needsLayout = true + } } @IBInspectable - open var iconPointSize: CGFloat = 0 { - didSet { needsLayout = true } + open var iconPointSize: CGFloat { + get { return textFieldCell?.iconPointSize ?? 0 } + set { + assert(textFieldCell != nil) + textFieldCell?.iconPointSize = newValue + needsLayout = true + } + } + + public var textFieldCell: TextFieldCell? { + return cell as? TextFieldCell } - public var textFieldCell: NSTextFieldCell? { - return cell as? NSTextFieldCell + open override var intrinsicContentSize: NSSize { + return cell?.cellSize ?? .zero } public override init(frame frameRect: NSRect) { super.init(frame: frameRect) - // The cell will be created in `commonInit()`. - cell = nil commonInit() } public required init?(coder: NSCoder) { super.init(coder: coder) + + commonInit() } public init(icon: FontAwesome, style: FontAwesomeStyle = .brands) { super.init(frame: .zero) - // The cell will be created in `commonInit()`. - cell = nil commonInit() self.icon = icon self.style = style } - open override func awakeFromNib() { - super.awakeFromNib() - - commonInit() - } - open override func prepareForInterfaceBuilder() { super.prepareForInterfaceBuilder() @@ -82,16 +92,17 @@ open class FontAwesomeLabel: NSTextField { } open func commonInit() { - if cell == nil { - cell = NSTextFieldCell(textCell: "") - isSelectable = false - isBezeled = false - drawsBackground = false + if !(cell is TextFieldCell) { + cell = createCell() } stringValue = fontAwesomeIcon ?? "" } + open func createCell() -> TextFieldCell { + return TextFieldCell(textCell: "") + } + open override func layout() { updateFontIfNeeded() diff --git a/FontAwesome/AppKit/FontAwesomeLayoutManager.swift b/FontAwesome/AppKit/FontAwesomeLayoutManager.swift index 8e3ca264..36b38093 100644 --- a/FontAwesome/AppKit/FontAwesomeLayoutManager.swift +++ b/FontAwesome/AppKit/FontAwesomeLayoutManager.swift @@ -41,19 +41,6 @@ public class FontAwesomeLayoutManager { private let textStorageLock: NSLock = NSLock() public lazy var sizingButton: NSButton = { NSButton() }() - public lazy var sizingTextField: NSTextField = { - if #available(OSX 10.12, *) { - return NSTextField(labelWithAttributedString: NSAttributedString()) - } else { - // Gotta do it ourselves. 🙄 - let textField = NSTextField() - textField.isBezeled = false - textField.drawsBackground = false - textField.isEditable = false - textField.isSelectable = false - return textField - } - }() /// The backing store pixel scale factor. open var backingScaleFactor: CGFloat { @@ -120,8 +107,13 @@ public class FontAwesomeLayoutManager { } open func typographicBounds(of attributedString: NSAttributedString) -> Rect { - sizingTextField.attributedStringValue = attributedString - return CGRect(origin: .zero, size: sizingTextField.intrinsicContentSize) + textStorageLock.lock() + defer { textStorageLock.unlock() } + + textStorage.setAttributedString(attributedString) + + let iconGlyphRange = layoutManager.glyphRange(for: textContainer) + return layoutManager.boundingRect(forGlyphRange: iconGlyphRange, in: textContainer) } } From 47e657b4518fe7a114cc853480179c6642430880 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Thu, 20 Feb 2020 18:46:27 -0800 Subject: [PATCH 08/17] Fix macOS font loading. --- FontAwesome/FontAwesome.swift | 8 ++++++++ FontAwesome/Shims.swift | 10 ---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/FontAwesome/FontAwesome.swift b/FontAwesome/FontAwesome.swift index ed6b1e82..ae4a781b 100644 --- a/FontAwesome/FontAwesome.swift +++ b/FontAwesome/FontAwesome.swift @@ -111,6 +111,12 @@ public extension Font { /// - parameter ofSize: The preferred font size. /// - returns: A font object of FontAwesome. class func fontAwesome(ofSize fontSize: CGFloat, style: FontAwesomeStyle) -> Font { + #if canImport(AppKit) + if let loadedFont = Font(name: style.fontName(), size: fontSize) { + return loadedFont + } + #endif + loadFontAwesome(ofStyle: style) return Font(name: style.fontName(), size: fontSize)! } @@ -118,9 +124,11 @@ public extension Font { /// Loads the FontAwesome font in to memory. /// This method should be called when setting icons without using code. class func loadFontAwesome(ofStyle style: FontAwesomeStyle) { + #if canImport(UIKit) if Font.fontNames(forFamilyName: style.fontFamilyName()).contains(style.fontName()) { return } + #endif FontLoader.loadFont(style.fontFilename()) } diff --git a/FontAwesome/Shims.swift b/FontAwesome/Shims.swift index e1f6168b..2b1a92a3 100644 --- a/FontAwesome/Shims.swift +++ b/FontAwesome/Shims.swift @@ -43,14 +43,4 @@ public typealias ImageView = NSImageView public typealias View = NSView public typealias Label = FontAwesomeLabel -extension Font { - - static func fontNames(forFamilyName familyName: String) -> [String] { - return NSFontManager.shared.availableMembers(ofFontFamily: familyName)? - .compactMap({ $0[0] as? String }) - ?? [] - } - -} - #endif From e3fe0139b6da304b0c19940d348fe7d015d66014 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Thu, 20 Feb 2020 19:08:19 -0800 Subject: [PATCH 09/17] Remove obsolete comment. --- FontAwesome/AppKit/FontAwesomeButton.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/FontAwesome/AppKit/FontAwesomeButton.swift b/FontAwesome/AppKit/FontAwesomeButton.swift index e7c71a6a..4bc1238d 100644 --- a/FontAwesome/AppKit/FontAwesomeButton.swift +++ b/FontAwesome/AppKit/FontAwesomeButton.swift @@ -248,11 +248,8 @@ extension FontAwesomeButton: FontAwesomeTextRepresentable { guard let icon = icon, let font = font else { return } - image = Image.fontAwesomeIcon(name: icon, - font: font, - textColor: .black, // Note: The image will be a template - size: maxIconSize) // image, so the color is largely - assert(image!.isTemplate) // irrelevant here. + image = Image.fontAwesomeIcon(name: icon, font: font, textColor: .black, size: maxIconSize) + assert(image!.isTemplate) // Condition necessary for proper behavior of buttons in toolbar items in macOS 10.13. if !isInToolbar { From 38bbf58bbb08112c008a7990fb12ed3ecc00598e Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Thu, 20 Feb 2020 23:10:11 -0800 Subject: [PATCH 10/17] Change accessibility of `FontAwesomeToolbarItem.init(itemIdentifier:)` to public. --- FontAwesome/AppKit/FontAwesomeToolbarItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FontAwesome/AppKit/FontAwesomeToolbarItem.swift b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift index e3bfbfc1..3529526b 100644 --- a/FontAwesome/AppKit/FontAwesomeToolbarItem.swift +++ b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift @@ -166,7 +166,7 @@ open class FontAwesomeToolbarItem: NSToolbarItem { self.style = style } - private override init(itemIdentifier: NSToolbarItem.Identifier) { + public override init(itemIdentifier: NSToolbarItem.Identifier) { super.init(itemIdentifier: itemIdentifier) view = FontAwesomeButton() From c3804954157ce66770e9c500817a9dd8c3d0b76e Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Thu, 20 Feb 2020 23:20:35 -0800 Subject: [PATCH 11/17] Add `FontAwesomeToolbarItem.createButton()` to improve extensibility. --- FontAwesome/AppKit/FontAwesomeToolbarItem.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/FontAwesome/AppKit/FontAwesomeToolbarItem.swift b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift index 3529526b..5032f80f 100644 --- a/FontAwesome/AppKit/FontAwesomeToolbarItem.swift +++ b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift @@ -169,7 +169,7 @@ open class FontAwesomeToolbarItem: NSToolbarItem { public override init(itemIdentifier: NSToolbarItem.Identifier) { super.init(itemIdentifier: itemIdentifier) - view = FontAwesomeButton() + view = createButton() } open override func awakeFromNib() { @@ -186,7 +186,7 @@ open class FontAwesomeToolbarItem: NSToolbarItem { open func commonInit() { if button == nil { - view = FontAwesomeButton() + view = createButton() } guard let button = button @@ -211,6 +211,10 @@ open class FontAwesomeToolbarItem: NSToolbarItem { } } + open func createButton() -> FontAwesomeButton { + return FontAwesomeButton() + } + open func fittingSize() -> NSSize { guard let button = button else { return .zero } From 62e66c574ea7c560893025941b3a3d49b3ffea73 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Fri, 21 Feb 2020 11:09:59 -0800 Subject: [PATCH 12/17] Refactor AppKit controls for better extensibility. --- FontAwesome/AppKit/FontAwesomeButton.swift | 6 ++---- FontAwesome/AppKit/FontAwesomeLabel.swift | 6 ++---- FontAwesome/AppKit/FontAwesomeToolbarItem.swift | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/FontAwesome/AppKit/FontAwesomeButton.swift b/FontAwesome/AppKit/FontAwesomeButton.swift index 4bc1238d..a8316028 100644 --- a/FontAwesome/AppKit/FontAwesomeButton.swift +++ b/FontAwesome/AppKit/FontAwesomeButton.swift @@ -23,7 +23,7 @@ import AppKit @IBDesignable -open class FontAwesomeButton: NSButton { +open class FontAwesomeButton: NSButton, FontAwesomeTextRepresentable { public typealias ButtonCell = FontAwesomeButtonCell @@ -234,9 +234,7 @@ open class FontAwesomeButton: NSButton { super.layout() } -} - -extension FontAwesomeButton: FontAwesomeTextRepresentable { + // MARK: - FontAwesomeTextRepresentable open var maxIconSize: NSSize { return iconPointSize > 0 diff --git a/FontAwesome/AppKit/FontAwesomeLabel.swift b/FontAwesome/AppKit/FontAwesomeLabel.swift index d3a39321..c346bae4 100644 --- a/FontAwesome/AppKit/FontAwesomeLabel.swift +++ b/FontAwesome/AppKit/FontAwesomeLabel.swift @@ -23,7 +23,7 @@ import AppKit @IBDesignable -open class FontAwesomeLabel: NSTextField { +open class FontAwesomeLabel: NSTextField, FontAwesomeTextRepresentable { public typealias TextFieldCell = FontAwesomeTextFieldCell @@ -109,9 +109,7 @@ open class FontAwesomeLabel: NSTextField { super.layout() } -} - -extension FontAwesomeLabel: FontAwesomeTextRepresentable { + // MARK: - FontAwesomeTextRepresentable open var maxIconSize: NSSize { return superview is FontAwesomeView diff --git a/FontAwesome/AppKit/FontAwesomeToolbarItem.swift b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift index 5032f80f..9858e447 100644 --- a/FontAwesome/AppKit/FontAwesomeToolbarItem.swift +++ b/FontAwesome/AppKit/FontAwesomeToolbarItem.swift @@ -22,7 +22,7 @@ import AppKit -open class FontAwesomeToolbarItem: NSToolbarItem { +open class FontAwesomeToolbarItem: NSToolbarItem, FontAwesomeRepresentable { @IBInspectable open var iconCode: String { @@ -265,5 +265,3 @@ open class FontAwesomeToolbarItem: NSToolbarItem { } } - -extension FontAwesomeToolbarItem: FontAwesomeRepresentable { } From 9b2b4a43eada5ca8172b09f9402375f4587a712b Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Sun, 29 Mar 2020 05:22:11 -0700 Subject: [PATCH 13/17] Fix font URL resolution. --- FontAwesome/FontAwesome.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/FontAwesome/FontAwesome.swift b/FontAwesome/FontAwesome.swift index ae4a781b..b8ca5514 100644 --- a/FontAwesome/FontAwesome.swift +++ b/FontAwesome/FontAwesome.swift @@ -328,14 +328,18 @@ private class FontLoader { extension URL { static func fontURL(for fontName: String) -> URL? { - let bundle = Bundle(for: FontLoader.self) - - if let fontURL = bundle.url(forResource: fontName, withExtension: "otf") { - return fontURL + let fontAwesomeBundle = Bundle(for: FontLoader.self) + + // We begin by iterating through the embedding application’s non-framework bundles in case + // other font variants were embedded. + for bundle in (Bundle.allBundles + [fontAwesomeBundle]) { + if let fontURL = bundle.url(forResource: fontName, withExtension: "otf") { + return fontURL + } } // If this framework is added using CocoaPods, resources is placed under a subdirectory - if let fontURL = bundle.url(forResource: fontName, withExtension: "otf", subdirectory: "FontAwesome.swift.bundle") { + if let fontURL = fontAwesomeBundle.url(forResource: fontName, withExtension: "otf", subdirectory: "FontAwesome.swift.bundle") { return fontURL } From 8db102ae152f122805d6b7a71749d03815655844 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Sun, 29 Mar 2020 05:25:36 -0700 Subject: [PATCH 14/17] Fix Catalyst compilation and add unit test (#249). --- FontAwesome/FontAwesome.swift | 2 +- FontAwesome/FontAwesomeImageView.swift | 14 +++++++------- FontAwesome/FontAwesomeView.swift | 6 +++--- FontAwesomeTests/FontAwesomeTests.swift | 13 +++++++++++-- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/FontAwesome/FontAwesome.swift b/FontAwesome/FontAwesome.swift index b8ca5514..a5e9d91f 100644 --- a/FontAwesome/FontAwesome.swift +++ b/FontAwesome/FontAwesome.swift @@ -45,7 +45,7 @@ public struct FontAwesomeConfig { /// make sure they are added to the target and are included in the Info.plist file. public static var usesProFonts: Bool = false - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) /// The default size of the Font Awesome icon in `FontAwesomeButton`s. /// /// This property has a default value of `.medium`. diff --git a/FontAwesome/FontAwesomeImageView.swift b/FontAwesome/FontAwesomeImageView.swift index 6926c68d..2d567ae4 100644 --- a/FontAwesome/FontAwesomeImageView.swift +++ b/FontAwesome/FontAwesomeImageView.swift @@ -39,7 +39,7 @@ import AppKit @IBInspectable public var imageBackgroundColor: NSColor = .clear #endif @IBInspectable public var styleName: String = "Brands" - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) /// The (exclusive) total horizontal and vertical change in points tolerated before the resolution of the backing /// image (of the Font Awesome icon) is updated. /// @@ -51,7 +51,7 @@ import AppKit @IBInspectable public var sizeChangeTolerance: CGFloat = 10 #endif - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) /// The size that was last used to generate the Font Awesome icon image. private var lastBoundsUsedForImage: NSSize = .zero @@ -62,7 +62,7 @@ import AppKit } #endif - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) override public init(frame frameRect: NSRect) { super.init(frame: frameRect) commonInit() @@ -88,7 +88,7 @@ import AppKit public override func awakeFromNib() { super.awakeFromNib() - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) commonInit() #endif useFontAwesomeImage() @@ -96,7 +96,7 @@ import AppKit public override func prepareForInterfaceBuilder() { super.prepareForInterfaceBuilder() - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) commonInit() #endif useFontAwesomeImage() @@ -107,12 +107,12 @@ import AppKit image = img } - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) lastBoundsUsedForImage = bounds.size #endif } - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) open override func viewDidEndLiveResize() { super.viewDidEndLiveResize() diff --git a/FontAwesome/FontAwesomeView.swift b/FontAwesome/FontAwesomeView.swift index 67d906e8..e7224f10 100644 --- a/FontAwesome/FontAwesomeView.swift +++ b/FontAwesome/FontAwesomeView.swift @@ -43,7 +43,7 @@ import AppKit @IBInspectable public var styleName: String = "Brands" { didSet { - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) self.iconView.styleName = styleName #endif } @@ -79,7 +79,7 @@ import AppKit #endif self.addSubview(iconView) - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) self.iconView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ @@ -113,6 +113,6 @@ import AppKit } -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) extension FontAwesomeView: FontAwesomeRepresentable { } #endif diff --git a/FontAwesomeTests/FontAwesomeTests.swift b/FontAwesomeTests/FontAwesomeTests.swift index a4c69d92..3ab5f856 100644 --- a/FontAwesomeTests/FontAwesomeTests.swift +++ b/FontAwesomeTests/FontAwesomeTests.swift @@ -36,7 +36,7 @@ import AppKit class FontAwesomeTests: XCTestCase { - #if canImport(AppKit) + #if canImport(AppKit) && !targetEnvironment(macCatalyst) func testFontAwesomeInit() { XCTAssertEqual(FontAwesome(name: "camera"), .camera) XCTAssertEqual(FontAwesome(name: "fa-camera"), .camera) @@ -153,12 +153,21 @@ class FontAwesomeTests: XCTestCase { label.text = String.fontAwesomeIcon(name: FontAwesome.github) XCTAssertEqual(label.text, "\u{f09b}") } + + func testFontsShouldBeLoadedIfFontUsedTwice() { + let solidFont = UIFont.fontAwesome(ofSize: 200, style: .solid) + let regularFont = UIFont.fontAwesome(ofSize: 200, style: .regular) + let regularFontSmaller = UIFont.fontAwesome(ofSize: 100, style: .regular) + XCTAssertNotNil(solidFont, "Solid font should be loaded.") + XCTAssertNotNil(regularFont, "Regular font should be loaded.") + XCTAssertNotNil(regularFontSmaller, "Smaller regular font should be loaded.") + } #endif } // MARK: - Test Shims -#if canImport(AppKit) +#if canImport(AppKit) && !targetEnvironment(macCatalyst) extension NSTextField { var text: String! { From 31f5c4e71953e01a518e52a1d49051f6f98fe704 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Sun, 29 Mar 2020 13:18:33 -0700 Subject: [PATCH 15/17] Patch edge case in `FontAwesomeButton`'s image generation + unit test it. --- .../FontAwesomeTextRepresentable.swift | 10 ++++++++ FontAwesomeTests/FontAwesomeTests.swift | 24 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/FontAwesome/FontAwesomeTextRepresentable.swift b/FontAwesome/FontAwesomeTextRepresentable.swift index 0ed8c760..b6704f02 100644 --- a/FontAwesome/FontAwesomeTextRepresentable.swift +++ b/FontAwesome/FontAwesomeTextRepresentable.swift @@ -82,6 +82,16 @@ public protocol FontAwesomeTextRepresentable: FontAwesomeRepresentable { public extension FontAwesomeTextRepresentable { + var icon: FontAwesome? { + get { return FontAwesome(name: iconCode) } + set { + iconCode = newValue?.rawValue ?? "" + // Forces `updateFontIfNeeded()` to call `didUpdateFont()`, which will + // render the new icon. + font = nil + } + } + var usesTypographicBounds: Bool { return false } diff --git a/FontAwesomeTests/FontAwesomeTests.swift b/FontAwesomeTests/FontAwesomeTests.swift index 3ab5f856..35ead06b 100644 --- a/FontAwesomeTests/FontAwesomeTests.swift +++ b/FontAwesomeTests/FontAwesomeTests.swift @@ -162,7 +162,29 @@ class FontAwesomeTests: XCTestCase { XCTAssertNotNil(regularFont, "Regular font should be loaded.") XCTAssertNotNil(regularFontSmaller, "Smaller regular font should be loaded.") } - #endif + #else + func testFontAwesomeButtonIconGenerationEdgeCase() { + continueAfterFailure = false + + let button = FontAwesomeButton(icon: .robot, style: .solid) + button.sizeToFit() + button.iconSize = .large + button.layout() + + XCTAssertNotNil(button.image) + XCTAssertNotEqual(button.image!.size, .zero) + XCTAssertNotEqual(button.image!.size, NSSize(width: 1, height: 1)) + + let flagImage = button.image! + button.icon = .handPaper + button.layout() + + XCTAssertNotNil(button.image) + XCTAssertNotEqual(button.image!.size, .zero) + XCTAssertNotEqual(button.image!.size, NSSize(width: 1, height: 1)) + XCTAssertNotEqual(button.image, flagImage) + } + #endif /* canImport(UIKit) */ } // MARK: - Test Shims From c4c795e677502e32c7817228960d94945c6ab028 Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Sun, 29 Mar 2020 13:28:04 -0700 Subject: [PATCH 16/17] =?UTF-8?q?=F0=9F=91=8B,=20Duotone.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FontAwesome/FontAwesome.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/FontAwesome/FontAwesome.swift b/FontAwesome/FontAwesome.swift index a5e9d91f..3de139d6 100644 --- a/FontAwesome/FontAwesome.swift +++ b/FontAwesome/FontAwesome.swift @@ -60,10 +60,12 @@ public struct FontAwesomeConfig { public enum FontAwesomeStyle: String { case solid - /// WARNING: Font Awesome Free doesn't include a Light variant. Using this with Free will fallback to Regular. + /// - Warning: Font Awesome Free doesn't include a Light variant. Using this with Free will fallback to Regular. case light case regular case brands + /// - Warning: Font Awesome Free doesn't include a Duotone variant. Using this with Free will fallback to Regular. + case duotone func fontName() -> String { switch self { @@ -75,6 +77,8 @@ public enum FontAwesomeStyle: String { return FontAwesomeConfig.usesProFonts ? "FontAwesome5Pro-Regular" : "FontAwesome5Free-Regular" case .brands: return "FontAwesome5Brands-Regular" + case .duotone: + return FontAwesomeConfig.usesProFonts ? "FontAwesome5Pro-Duotone" : "FontAwesome5Free-Regular" } } @@ -88,6 +92,8 @@ public enum FontAwesomeStyle: String { return FontAwesomeConfig.usesProFonts ? "Font Awesome 5 Pro-Regular-400" : "Font Awesome 5 Free-Regular-400" case .brands: return "Font Awesome 5 Brands-Regular-400" + case .duotone: + return FontAwesomeConfig.usesProFonts ? "Font Awesome 5 Duotone-Solid-900" : "Font Awesome 5 Free-Solid-900" } } @@ -97,7 +103,8 @@ public enum FontAwesomeStyle: String { return "Font Awesome 5 Brands" case .regular, .light, - .solid: + .solid, + .duotone: return FontAwesomeConfig.usesProFonts ? "Font Awesome 5 Pro" : "Font Awesome 5 Free" } } From 20b2cbcb8dd10966cd02fd1e39758e057485abac Mon Sep 17 00:00:00 2001 From: chriszielinski Date: Sun, 29 Mar 2020 16:18:23 -0700 Subject: [PATCH 17/17] Fix Duotone's `fontName()` return value (#250). --- FontAwesome/FontAwesome.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FontAwesome/FontAwesome.swift b/FontAwesome/FontAwesome.swift index 3de139d6..99467b0a 100644 --- a/FontAwesome/FontAwesome.swift +++ b/FontAwesome/FontAwesome.swift @@ -78,7 +78,7 @@ public enum FontAwesomeStyle: String { case .brands: return "FontAwesome5Brands-Regular" case .duotone: - return FontAwesomeConfig.usesProFonts ? "FontAwesome5Pro-Duotone" : "FontAwesome5Free-Regular" + return FontAwesomeConfig.usesProFonts ? "FontAwesome5Duotone-Solid" : "FontAwesome5Free-Regular" } }