From 344eab85123592032de9b787c8b61a885f6c4299 Mon Sep 17 00:00:00 2001 From: Darren Ford Date: Wed, 15 Mar 2023 10:56:41 +1100 Subject: [PATCH] First cut refactor secure --- .gitignore | 1 + DSFFloatLabelledTextControl.podspec | 8 +- .../xcschemes/xcschememanagement.plist | 57 --- .../project.pbxproj | 237 ++------- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 ...oatLabelledTextControls Demo objc.xcscheme | 0 .../DSFFloatLabelledTextControls RTL.xcscheme | 0 .../DSFFloatLabelledTextControls.xcscheme | 0 .../DSFFloatLabelledTextField.xcscheme | 0 .../AppDelegate.h | 0 .../AppDelegate.m | 2 +- .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../Base.lproj/MainMenu.xib | 11 +- ...abelledTextControls_Demo_objc.entitlements | 0 .../Info.plist | 0 .../main.m | 0 .../AppDelegate.swift | 33 +- .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../Base.lproj/MainMenu.xib | 40 +- .../DSFFloatLabelledTextControls.entitlements | 0 .../DSFFloatLabelledTextControls/Info.plist | 0 .../ar.lproj/MainMenu.strings | 0 Package.swift | 42 +- README.md | 6 +- .../DSFFloatLabelledSecureTextField.swift | 451 ++++++++++++++++++ .../DSFFloatLabelledTextControl.h | 38 -- .../DSFFloatLabelledTextField.swift | 136 +----- Sources/DSFFloatLabelledTextField/Info.plist | 24 - .../private/utils.swift | 46 ++ 32 files changed, 626 insertions(+), 506 deletions(-) delete mode 100644 DSFFloatLabelledTextControls.xcodeproj/xcuserdata/dford.xcuserdatad/xcschemes/xcschememanagement.plist rename {DSFFloatLabelledTextControls.xcodeproj => Demos/DSFFloatLabelledTextControls.xcodeproj}/project.pbxproj (69%) rename {DSFFloatLabelledTextControls.xcodeproj => Demos/DSFFloatLabelledTextControls.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) rename {DSFFloatLabelledTextControls.xcodeproj => Demos/DSFFloatLabelledTextControls.xcodeproj}/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {DSFFloatLabelledTextControls.xcodeproj => Demos/DSFFloatLabelledTextControls.xcodeproj}/xcshareddata/xcschemes/DSFFloatLabelledTextControls Demo objc.xcscheme (100%) rename {DSFFloatLabelledTextControls.xcodeproj => Demos/DSFFloatLabelledTextControls.xcodeproj}/xcshareddata/xcschemes/DSFFloatLabelledTextControls RTL.xcscheme (100%) rename {DSFFloatLabelledTextControls.xcodeproj => Demos/DSFFloatLabelledTextControls.xcodeproj}/xcshareddata/xcschemes/DSFFloatLabelledTextControls.xcscheme (100%) rename {DSFFloatLabelledTextControls.xcodeproj => Demos/DSFFloatLabelledTextControls.xcodeproj}/xcshareddata/xcschemes/DSFFloatLabelledTextField.xcscheme (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/AppDelegate.h (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/AppDelegate.m (87%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/Contents.json (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/Base.lproj/MainMenu.xib (98%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/Info.plist (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls Demo objc/main.m (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls/AppDelegate.swift (84%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls/Assets.xcassets/Contents.json (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls/Base.lproj/MainMenu.xib (97%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls/Info.plist (100%) rename Demos/{ => Demos}/DSFFloatLabelledTextControls/ar.lproj/MainMenu.strings (100%) create mode 100644 Sources/DSFFloatLabelledTextField/DSFFloatLabelledSecureTextField.swift delete mode 100644 Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextControl.h delete mode 100644 Sources/DSFFloatLabelledTextField/Info.plist create mode 100644 Sources/DSFFloatLabelledTextField/private/utils.swift diff --git a/.gitignore b/.gitignore index a8ee677..527afa6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ DSFFloatLabelledTextControls.xcodeproj/xcuserdata xcschememanagement.plist .swiftpm +Demos/DSFFloatLabelledTextControls.xcodeproj/xcuserdata diff --git a/DSFFloatLabelledTextControl.podspec b/DSFFloatLabelledTextControl.podspec index 8db189e..15a428c 100644 --- a/DSFFloatLabelledTextControl.podspec +++ b/DSFFloatLabelledTextControl.podspec @@ -1,9 +1,9 @@ Pod::Spec.new do |s| s.name = "DSFFloatLabelledTextControl" s.version = "1.8.0" - s.summary = "A macOS Cocoa single-line NSTextField that implements the Float Label Pattern" + s.summary = "A macOS Cocoa single-line NSTextField/NSSecureTextField that implements the Float Label Pattern" s.description = <<-DESC - A macOS Cocoa single-line NSTextField that implements the Float Label Pattern. Supports secure edit fields. + A macOS Cocoa single-line NSTextField/NSSecureTextField that implements the Float Label Pattern. DESC s.homepage = "https://github.com/dagronf/DSFFloatLabelledTextControl" s.license = { :type => "MIT", :file => "LICENSE" } @@ -11,7 +11,7 @@ Pod::Spec.new do |s| s.social_media_url = "" s.osx.deployment_target = "10.11" s.source = { :git => ".git", :tag => s.version.to_s } - s.source_files = "DSFFloatLabelledTextControl/DSFFloatLabelledTextField.swift" + s.source_files = "Sources/DSFFloatLabelledTextControl/*.swift" s.frameworks = "Cocoa" - s.swift_version = "5.0" + s.swift_version = "5.4" end diff --git a/DSFFloatLabelledTextControls.xcodeproj/xcuserdata/dford.xcuserdatad/xcschemes/xcschememanagement.plist b/DSFFloatLabelledTextControls.xcodeproj/xcuserdata/dford.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index d0a84d9..0000000 --- a/DSFFloatLabelledTextControls.xcodeproj/xcuserdata/dford.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,57 +0,0 @@ - - - - - SchemeUserState - - All.xcscheme_^#shared#^_ - - orderHint - 4 - - DSFFloatLabelledTextControl.xcscheme_^#shared#^_ - - orderHint - 1 - - DSFFloatLabelledTextControls Demo objc.xcscheme_^#shared#^_ - - orderHint - 3 - - DSFFloatLabelledTextControls RTL.xcscheme_^#shared#^_ - - orderHint - 1 - - DSFFloatLabelledTextControls.xcscheme_^#shared#^_ - - orderHint - 0 - - DSFFloatLabelledTextField.xcscheme_^#shared#^_ - - orderHint - 2 - - - SuppressBuildableAutocreation - - 2351F84A2207D15C00EFA4F7 - - primary - - - 23A2B7E8220792FE00B2FC77 - - primary - - - 23C3AB9923FBCFA30079D9C3 - - primary - - - - - diff --git a/DSFFloatLabelledTextControls.xcodeproj/project.pbxproj b/Demos/DSFFloatLabelledTextControls.xcodeproj/project.pbxproj similarity index 69% rename from DSFFloatLabelledTextControls.xcodeproj/project.pbxproj rename to Demos/DSFFloatLabelledTextControls.xcodeproj/project.pbxproj index 6001a19..adff5ef 100644 --- a/DSFFloatLabelledTextControls.xcodeproj/project.pbxproj +++ b/Demos/DSFFloatLabelledTextControls.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXAggregateTarget section */ @@ -22,38 +22,18 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 2351F84F2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 2351F84D2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 2351F8522207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; }; - 2351F8532207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 2351F8582207D16100EFA4F7 /* DSFFloatLabelledTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A2B7FA2207931500B2FC77 /* DSFFloatLabelledTextField.swift */; }; - 2351F85B22082AD800EFA4F7 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 2351F85922082AD800EFA4F7 /* LICENSE */; }; - 2351F85C22082AD800EFA4F7 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 2351F85A22082AD800EFA4F7 /* README.md */; }; 23A2B7ED220792FE00B2FC77 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A2B7EC220792FE00B2FC77 /* AppDelegate.swift */; }; 23A2B7EF220792FF00B2FC77 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23A2B7EE220792FF00B2FC77 /* Assets.xcassets */; }; 23A2B7F2220792FF00B2FC77 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 23A2B7F0220792FF00B2FC77 /* MainMenu.xib */; }; + 23A3061C29C12CB200BEC98A /* DSFFloatLabelledTextField in Frameworks */ = {isa = PBXBuildFile; productRef = 23A3061B29C12CB200BEC98A /* DSFFloatLabelledTextField */; }; + 23A3061E29C12CB800BEC98A /* DSFFloatLabelledTextField in Frameworks */ = {isa = PBXBuildFile; productRef = 23A3061D29C12CB800BEC98A /* DSFFloatLabelledTextField */; }; 23C3AB9E23FBCFA30079D9C3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 23C3AB9D23FBCFA30079D9C3 /* AppDelegate.m */; }; 23C3ABA023FBCFA40079D9C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23C3AB9F23FBCFA40079D9C3 /* Assets.xcassets */; }; 23C3ABA323FBCFA40079D9C3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 23C3ABA123FBCFA40079D9C3 /* MainMenu.xib */; }; 23C3ABA623FBCFA40079D9C3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 23C3ABA523FBCFA40079D9C3 /* main.m */; }; - 23C3ABAC23FBCFDE0079D9C3 /* DSFFloatLabelledTextField.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; }; - 23C3ABB023FBD0F80079D9C3 /* DSFFloatLabelledTextField.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 2351F8502207D15C00EFA4F7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 23A2B7E1220792FE00B2FC77 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2351F84A2207D15C00EFA4F7; - remoteInfo = DSFFloatLabelledTextControl; - }; - 23C3ABAD23FBCFE40079D9C3 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 23A2B7E1220792FE00B2FC77 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2351F84A2207D15C00EFA4F7; - remoteInfo = DSFFloatLabelledTextField; - }; 23E825672575D17E0023A3C7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 23A2B7E1220792FE00B2FC77 /* Project object */; @@ -77,7 +57,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 2351F8532207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -88,26 +67,19 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 23C3ABB023FBD0F80079D9C3 /* DSFFloatLabelledTextField.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DSFFloatLabelledTextField.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2351F84D2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSFFloatLabelledTextControl.h; sourceTree = ""; }; - 2351F84E2207D15C00EFA4F7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 2351F85922082AD800EFA4F7 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - 2351F85A22082AD800EFA4F7 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 23A2B7E9220792FE00B2FC77 /* DSFFloatLabelledTextControls.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DSFFloatLabelledTextControls.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23A2B7EC220792FE00B2FC77 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 23A2B7EE220792FF00B2FC77 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 23A2B7F1220792FF00B2FC77 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 23A2B7F3220792FF00B2FC77 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23A2B7F4220792FF00B2FC77 /* DSFFloatLabelledTextControls.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFFloatLabelledTextControls.entitlements; sourceTree = ""; }; - 23A2B7FA2207931500B2FC77 /* DSFFloatLabelledTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSFFloatLabelledTextField.swift; sourceTree = ""; }; - 23ACF96D228A3F6600FF6594 /* DSFFloatLabelledTextControl.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = DSFFloatLabelledTextControl.podspec; sourceTree = ""; }; + 23A3061A29C12C9C00BEC98A /* DSFFloatLabelledTextControls */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DSFFloatLabelledTextControls; path = ..; sourceTree = ""; }; 23C3AB9A23FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFFloatLabelledTextControls Demo objc.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 23C3AB9C23FBCFA30079D9C3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 23C3AB9D23FBCFA30079D9C3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -117,22 +89,14 @@ 23C3ABA523FBCFA40079D9C3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 23C3ABA723FBCFA40079D9C3 /* DSFFloatLabelledTextControls_Demo_objc.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFFloatLabelledTextControls_Demo_objc.entitlements; sourceTree = ""; }; 23E8256D2575D28A0023A3C7 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/MainMenu.strings; sourceTree = ""; }; - 23FFF1CD23BD490F008FA55A /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 2351F8482207D15C00EFA4F7 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 23A2B7E6220792FE00B2FC77 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2351F8522207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework in Frameworks */, + 23A3061C29C12CB200BEC98A /* DSFFloatLabelledTextField in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -140,32 +104,18 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 23C3ABAC23FBCFDE0079D9C3 /* DSFFloatLabelledTextField.framework in Frameworks */, + 23A3061E29C12CB800BEC98A /* DSFFloatLabelledTextField in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 2351F84C2207D15C00EFA4F7 /* DSFFloatLabelledTextField */ = { - isa = PBXGroup; - children = ( - 23A2B7FA2207931500B2FC77 /* DSFFloatLabelledTextField.swift */, - 2351F84D2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h */, - 2351F84E2207D15C00EFA4F7 /* Info.plist */, - ); - path = DSFFloatLabelledTextField; - sourceTree = ""; - }; 23A2B7E0220792FE00B2FC77 = { isa = PBXGroup; children = ( + 23A3061A29C12C9C00BEC98A /* DSFFloatLabelledTextControls */, 23FFF1CF23BD4982008FA55A /* Demos */, - 23FFF1CE23BD496A008FA55A /* Sources */, - 23ACF96D228A3F6600FF6594 /* DSFFloatLabelledTextControl.podspec */, - 2351F85922082AD800EFA4F7 /* LICENSE */, - 2351F85A22082AD800EFA4F7 /* README.md */, - 23FFF1CD23BD490F008FA55A /* Package.swift */, 23A2B7EA220792FE00B2FC77 /* Products */, 23C3ABAB23FBCFDE0079D9C3 /* Frameworks */, ); @@ -175,7 +125,6 @@ isa = PBXGroup; children = ( 23A2B7E9220792FE00B2FC77 /* DSFFloatLabelledTextControls.app */, - 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */, 23C3AB9A23FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc.app */, ); name = Products; @@ -214,14 +163,6 @@ name = Frameworks; sourceTree = ""; }; - 23FFF1CE23BD496A008FA55A /* Sources */ = { - isa = PBXGroup; - children = ( - 2351F84C2207D15C00EFA4F7 /* DSFFloatLabelledTextField */, - ); - path = Sources; - sourceTree = ""; - }; 23FFF1CF23BD4982008FA55A /* Demos */ = { isa = PBXGroup; children = ( @@ -233,36 +174,7 @@ }; /* End PBXGroup section */ -/* Begin PBXHeadersBuildPhase section */ - 2351F8462207D15C00EFA4F7 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 2351F84F2207D15C00EFA4F7 /* DSFFloatLabelledTextControl.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - /* Begin PBXNativeTarget section */ - 2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */ = { - isa = PBXNativeTarget; - buildConfigurationList = 2351F8542207D15C00EFA4F7 /* Build configuration list for PBXNativeTarget "DSFFloatLabelledTextField" */; - buildPhases = ( - 2351F8462207D15C00EFA4F7 /* Headers */, - 2351F8472207D15C00EFA4F7 /* Sources */, - 2351F8482207D15C00EFA4F7 /* Frameworks */, - 2351F8492207D15C00EFA4F7 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DSFFloatLabelledTextField; - productName = DSFFloatLabelledTextControl; - productReference = 2351F84B2207D15C00EFA4F7 /* DSFFloatLabelledTextField.framework */; - productType = "com.apple.product-type.framework"; - }; 23A2B7E8220792FE00B2FC77 /* DSFFloatLabelledTextControls */ = { isa = PBXNativeTarget; buildConfigurationList = 23A2B7F7220792FF00B2FC77 /* Build configuration list for PBXNativeTarget "DSFFloatLabelledTextControls" */; @@ -275,9 +187,11 @@ buildRules = ( ); dependencies = ( - 2351F8512207D15C00EFA4F7 /* PBXTargetDependency */, ); name = DSFFloatLabelledTextControls; + packageProductDependencies = ( + 23A3061B29C12CB200BEC98A /* DSFFloatLabelledTextField */, + ); productName = DSFFloatLabelledTextControls; productReference = 23A2B7E9220792FE00B2FC77 /* DSFFloatLabelledTextControls.app */; productType = "com.apple.product-type.application"; @@ -294,9 +208,11 @@ buildRules = ( ); dependencies = ( - 23C3ABAE23FBCFE40079D9C3 /* PBXTargetDependency */, ); name = "DSFFloatLabelledTextControls Demo objc"; + packageProductDependencies = ( + 23A3061D29C12CB800BEC98A /* DSFFloatLabelledTextField */, + ); productName = "DSFFloatLabelledTextControls Demo objc"; productReference = 23C3AB9A23FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc.app */; productType = "com.apple.product-type.application"; @@ -311,9 +227,6 @@ LastUpgradeCheck = 1010; ORGANIZATIONNAME = "Darren Ford"; TargetAttributes = { - 2351F84A2207D15C00EFA4F7 = { - CreatedOnToolsVersion = 10.1; - }; 23A2B7E8220792FE00B2FC77 = { CreatedOnToolsVersion = 10.1; }; @@ -339,7 +252,6 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */, 23A2B7E8220792FE00B2FC77 /* DSFFloatLabelledTextControls */, 23C3AB9923FBCFA30079D9C3 /* DSFFloatLabelledTextControls Demo objc */, 23E825612575D1760023A3C7 /* All */, @@ -348,20 +260,11 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 2351F8492207D15C00EFA4F7 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 23A2B7E7220792FE00B2FC77 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2351F85B22082AD800EFA4F7 /* LICENSE in Resources */, 23A2B7EF220792FF00B2FC77 /* Assets.xcassets in Resources */, - 2351F85C22082AD800EFA4F7 /* README.md in Resources */, 23A2B7F2220792FF00B2FC77 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -378,14 +281,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 2351F8472207D15C00EFA4F7 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2351F8582207D16100EFA4F7 /* DSFFloatLabelledTextField.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 23A2B7E5220792FE00B2FC77 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -406,16 +301,6 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 2351F8512207D15C00EFA4F7 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */; - targetProxy = 2351F8502207D15C00EFA4F7 /* PBXContainerItemProxy */; - }; - 23C3ABAE23FBCFE40079D9C3 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 2351F84A2207D15C00EFA4F7 /* DSFFloatLabelledTextField */; - targetProxy = 23C3ABAD23FBCFE40079D9C3 /* PBXContainerItemProxy */; - }; 23E825682575D17E0023A3C7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 23A2B7E8220792FE00B2FC77 /* DSFFloatLabelledTextControls */; @@ -449,66 +334,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 2351F8552207D15C00EFA4F7 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Sources/DSFFloatLabelledTextField/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.5; - PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControl; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 2351F8562207D15C00EFA4F7 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = Sources/DSFFloatLabelledTextField/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.5; - PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControl; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; 23A2B7F5220792FF00B2FC77 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -629,7 +454,6 @@ 23A2B7F8220792FF00B2FC77 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; CODE_SIGN_ENTITLEMENTS = Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements; @@ -642,6 +466,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.5; PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControls; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -653,7 +478,6 @@ 23A2B7F9220792FF00B2FC77 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = ""; CODE_SIGN_ENTITLEMENTS = Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements; @@ -666,6 +490,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.13; MARKETING_VERSION = 1.5; PRODUCT_BUNDLE_IDENTIFIER = com.darrenford.DSFFloatLabelledTextControls; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -680,17 +505,17 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 3L6RK3LGGW; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Demos/DSFFloatLabelledTextControls Demo objc/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFFloatLabelledTextControls-Demo-objc"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -702,17 +527,17 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = "Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 3L6RK3LGGW; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = "Demos/DSFFloatLabelledTextControls Demo objc/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.13; PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFFloatLabelledTextControls-Demo-objc"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -739,15 +564,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 2351F8542207D15C00EFA4F7 /* Build configuration list for PBXNativeTarget "DSFFloatLabelledTextField" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2351F8552207D15C00EFA4F7 /* Debug */, - 2351F8562207D15C00EFA4F7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 23A2B7E4220792FE00B2FC77 /* Build configuration list for PBXProject "DSFFloatLabelledTextControls" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -785,6 +601,17 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 23A3061B29C12CB200BEC98A /* DSFFloatLabelledTextField */ = { + isa = XCSwiftPackageProductDependency; + productName = DSFFloatLabelledTextField; + }; + 23A3061D29C12CB800BEC98A /* DSFFloatLabelledTextField */ = { + isa = XCSwiftPackageProductDependency; + productName = DSFFloatLabelledTextField; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 23A2B7E1220792FE00B2FC77 /* Project object */; } diff --git a/DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Demos/DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Demos/DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Demos/DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Demos/DSFFloatLabelledTextControls.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls Demo objc.xcscheme b/Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls Demo objc.xcscheme similarity index 100% rename from DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls Demo objc.xcscheme rename to Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls Demo objc.xcscheme diff --git a/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls RTL.xcscheme b/Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls RTL.xcscheme similarity index 100% rename from DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls RTL.xcscheme rename to Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls RTL.xcscheme diff --git a/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls.xcscheme b/Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls.xcscheme similarity index 100% rename from DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls.xcscheme rename to Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextControls.xcscheme diff --git a/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextField.xcscheme b/Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextField.xcscheme similarity index 100% rename from DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextField.xcscheme rename to Demos/DSFFloatLabelledTextControls.xcodeproj/xcshareddata/xcschemes/DSFFloatLabelledTextField.xcscheme diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.h b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.h similarity index 100% rename from Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.h rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.h diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.m b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.m similarity index 87% rename from Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.m rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.m index 720bb9f..443f28f 100644 --- a/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.m +++ b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/AppDelegate.m @@ -24,7 +24,7 @@ // Insert code here to tear down your application } -- (void)floatLabelledTextFieldContentChanged:(DSFFloatLabelledTextField *)field { +- (void)floatLabelledTextFieldContentChanged:(DSFFloatLabelledSecureTextField *)field { NSLog(@"Password is now `%@`", field.stringValue); } diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/Contents.json b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/Contents.json similarity index 100% rename from Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/Contents.json rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/Assets.xcassets/Contents.json diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/Base.lproj/MainMenu.xib b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/Base.lproj/MainMenu.xib similarity index 98% rename from Demos/DSFFloatLabelledTextControls Demo objc/Base.lproj/MainMenu.xib rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/Base.lproj/MainMenu.xib index d5d659b..9b06dba 100644 --- a/Demos/DSFFloatLabelledTextControls Demo objc/Base.lproj/MainMenu.xib +++ b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/Base.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -692,7 +692,7 @@ - + @@ -705,7 +705,7 @@ - + @@ -718,7 +718,7 @@ - + @@ -729,7 +729,6 @@ - diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements similarity index 100% rename from Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/DSFFloatLabelledTextControls_Demo_objc.entitlements diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/Info.plist b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/Info.plist similarity index 100% rename from Demos/DSFFloatLabelledTextControls Demo objc/Info.plist rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/Info.plist diff --git a/Demos/DSFFloatLabelledTextControls Demo objc/main.m b/Demos/Demos/DSFFloatLabelledTextControls Demo objc/main.m similarity index 100% rename from Demos/DSFFloatLabelledTextControls Demo objc/main.m rename to Demos/Demos/DSFFloatLabelledTextControls Demo objc/main.m diff --git a/Demos/DSFFloatLabelledTextControls/AppDelegate.swift b/Demos/Demos/DSFFloatLabelledTextControls/AppDelegate.swift similarity index 84% rename from Demos/DSFFloatLabelledTextControls/AppDelegate.swift rename to Demos/Demos/DSFFloatLabelledTextControls/AppDelegate.swift index 802aefa..e89b7fe 100644 --- a/Demos/DSFFloatLabelledTextControls/AppDelegate.swift +++ b/Demos/Demos/DSFFloatLabelledTextControls/AppDelegate.swift @@ -35,6 +35,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var window: NSWindow! @IBOutlet weak var topFloatingLabel: DSFFloatLabelledTextField! + @IBOutlet weak var secureTextField: DSFFloatLabelledSecureTextField! @IBOutlet weak var passwordFloatingLabel: DSFFloatLabelledTextField! func applicationDidFinishLaunching(_ aNotification: Notification) { @@ -47,26 +48,34 @@ class AppDelegate: NSObject, NSApplicationDelegate { let cv = self.window.contentView! - let x = NSLayoutConstraint(item: ft, attribute: .width, - relatedBy: .equal, - toItem: nil, attribute: .notAnAttribute, - multiplier: 1, constant: 300) + let x = NSLayoutConstraint( + item: ft, attribute: .width, + relatedBy: .equal, + toItem: nil, attribute: .notAnAttribute, + multiplier: 1, constant: 300 + ) ft.addConstraint(x) cv.addSubview(ft) - let x1 = NSLayoutConstraint(item: ft, attribute: .left, - relatedBy: .equal, - toItem: cv, attribute: .left, - multiplier: 1, constant: 20) + let x1 = NSLayoutConstraint( + item: ft, attribute: .leading, + relatedBy: .equal, + toItem: cv, attribute: .leading, + multiplier: 1, constant: 20 + ) cv.addConstraint(x1) - let x2 = NSLayoutConstraint(item: ft, attribute: .bottom, - relatedBy: .equal, - toItem: cv, attribute: .bottom, - multiplier: 1, constant: -20) + let x2 = NSLayoutConstraint( + item: ft, attribute: .bottom, + relatedBy: .equal, + toItem: cv, attribute: .bottom, + multiplier: 1, constant: -20 + ) cv.addConstraint(x2) + + self.secureTextField.stringValue = "caterpillar" } @IBAction func resetPressed(_ sender: Any) { diff --git a/Demos/DSFFloatLabelledTextControls/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demos/Demos/DSFFloatLabelledTextControls/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Demos/DSFFloatLabelledTextControls/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Demos/Demos/DSFFloatLabelledTextControls/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Demos/DSFFloatLabelledTextControls/Assets.xcassets/Contents.json b/Demos/Demos/DSFFloatLabelledTextControls/Assets.xcassets/Contents.json similarity index 100% rename from Demos/DSFFloatLabelledTextControls/Assets.xcassets/Contents.json rename to Demos/Demos/DSFFloatLabelledTextControls/Assets.xcassets/Contents.json diff --git a/Demos/DSFFloatLabelledTextControls/Base.lproj/MainMenu.xib b/Demos/Demos/DSFFloatLabelledTextControls/Base.lproj/MainMenu.xib similarity index 97% rename from Demos/DSFFloatLabelledTextControls/Base.lproj/MainMenu.xib rename to Demos/Demos/DSFFloatLabelledTextControls/Base.lproj/MainMenu.xib index 0021f6f..c802433 100644 --- a/Demos/DSFFloatLabelledTextControls/Base.lproj/MainMenu.xib +++ b/Demos/Demos/DSFFloatLabelledTextControls/Base.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -16,6 +16,7 @@ + @@ -686,19 +687,19 @@ - + - + - - - + + + - + @@ -714,7 +715,7 @@ - - + @@ -746,7 +747,7 @@ - + @@ -759,10 +760,10 @@ - + - + @@ -804,10 +805,10 @@ - + - + @@ -852,10 +853,10 @@ - + - + @@ -873,7 +874,7 @@ - + @@ -887,7 +888,6 @@ - @@ -971,7 +971,7 @@ - + diff --git a/Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements b/Demos/Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements similarity index 100% rename from Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements rename to Demos/Demos/DSFFloatLabelledTextControls/DSFFloatLabelledTextControls.entitlements diff --git a/Demos/DSFFloatLabelledTextControls/Info.plist b/Demos/Demos/DSFFloatLabelledTextControls/Info.plist similarity index 100% rename from Demos/DSFFloatLabelledTextControls/Info.plist rename to Demos/Demos/DSFFloatLabelledTextControls/Info.plist diff --git a/Demos/DSFFloatLabelledTextControls/ar.lproj/MainMenu.strings b/Demos/Demos/DSFFloatLabelledTextControls/ar.lproj/MainMenu.strings similarity index 100% rename from Demos/DSFFloatLabelledTextControls/ar.lproj/MainMenu.strings rename to Demos/Demos/DSFFloatLabelledTextControls/ar.lproj/MainMenu.strings diff --git a/Package.swift b/Package.swift index a16c6ee..d95c09c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,29 +1,23 @@ -// swift-tools-version:5.1 -// The swift-tools-version declares the minimum version of Swift required to build this package. +// swift-tools-version: 5.4 import PackageDescription let package = Package( - name: "DSFFloatLabelledTextField", - platforms: [ - .macOS(.v10_11) - ], - products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. - .library( - name: "DSFFloatLabelledTextField", - targets: ["DSFFloatLabelledTextField"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages which this package depends on. - .target( - name: "DSFFloatLabelledTextField", - dependencies: []) - ], - swiftLanguageVersions: [.v5] + name: "DSFFloatLabelledTextField", + platforms: [ + .macOS(.v10_11) + ], + products: [ + .library( + name: "DSFFloatLabelledTextField", + targets: ["DSFFloatLabelledTextField"] + ), + ], + targets: [ + .target( + name: "DSFFloatLabelledTextField", + dependencies: [] + ) + ], + swiftLanguageVersions: [.v5] ) diff --git a/README.md b/README.md index 626851f..2a6a5ea 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,10 @@ Copy the `DSFFloatLabelledTextField.swift` into your project. This class inheri ### Interface builder -* Drop in a new Text Field into your canvas and set its class to `DSFFloatLabelledTextField` +* Drop in a new Text Field into your canvas and set its class to `DSFFloatLabelledTextField` or + `DSFFloatLabelledSecureTextControl` depending on whether you need a secure field * Set the size and style of your primary font as you would a regular text field * Set the size of the secondary font via the attributes inspector for the control -* If you want a secure field, set the `isSecure` property on the control ### Dynamically @@ -89,7 +89,7 @@ You can specify a delegate (`floatLabelDelegate`), either programatically or via ``` MIT License -Copyright (c) 2020 Darren Ford +Copyright (c) 2023 Darren Ford Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Sources/DSFFloatLabelledTextField/DSFFloatLabelledSecureTextField.swift b/Sources/DSFFloatLabelledTextField/DSFFloatLabelledSecureTextField.swift new file mode 100644 index 0000000..b2aa182 --- /dev/null +++ b/Sources/DSFFloatLabelledTextField/DSFFloatLabelledSecureTextField.swift @@ -0,0 +1,451 @@ +// +// DSFFloatLabelledSecureTextField.swift +// +// Copyright © 2023 Darren Ford. All rights reserved. +// +// MIT license +// +// 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 + +/// DSFFloatLabelledTextField delegate protocol +@objc public protocol DSFFloatLabelledSecureTextFieldDelegate: NSObjectProtocol { + /// Called when the label is shown or hidden + @objc optional func floatLabelledTextField(_ field: DSFFloatLabelledSecureTextField, didShowFloatingLabel didShow: Bool) + /// Called when the field becomes or loses first responder status + @objc optional func floatLabelledTextField(_ field: DSFFloatLabelledSecureTextField, didFocus: Bool) + /// Called when the content of the field changes + @objc optional func floatLabelledTextFieldContentChanged(_ field: DSFFloatLabelledSecureTextField) +} + +/// An NSSecureTextField that implements the Float Label Pattern +@IBDesignable open class DSFFloatLabelledSecureTextField: NSSecureTextField { + + /// Optional delegate to provide callbacks for the floating label state. + /// + /// This delegate can be set via Interface Builder or programatically + @IBOutlet @objc public weak var floatLabelDelegate: DSFFloatLabelledSecureTextFieldDelegate? + + /// The size (in pt) of the floating label text + @IBInspectable public var placeholderTextSize: CGFloat = NSFont.smallSystemFontSize { + didSet { + self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize) + self.reconfigureControl() + } + } + + /// Spacing between the floating label and the text field text + @IBInspectable public var placeholderSpacing: CGFloat = 0.0 { + didSet { + self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize) + self.reconfigureControl() + } + } + + // Override so that we can notify when the developer changes the text programatically too + open override var stringValue: String { + get { + return super.stringValue + } + set { + super.stringValue = newValue + NotificationCenter.default.post(name: NSControl.textDidChangeNotification, object: self) + } + } + + // Override so that we can update when the developer changes the placeholder string + open override var placeholderString: String? { + get { + return super.placeholderString + } + set { + super.placeholderString = newValue + self.floatingLabel.stringValue = newValue ?? "" + } + } + + /// Floating label + private let floatingLabel = NSTextField() + + /// Is the label currently showing + private var isShowing: Bool = false { + didSet { + self.floatLabelDelegate?.floatLabelledTextField?(self, didShowFloatingLabel: self.isShowing) + } + } + + /// Constraint to tie the label to the top of the control + private var floatingTop: NSLayoutConstraint? + + /// Height of the control + private var heightConstraint: NSLayoutConstraint? + + /// Observers for the font and placeholder text + private var fontObserver: NSKeyValueObservation? + private var placeholderObserver: NSKeyValueObservation? + + /// Returns the height of the placeholder text + var placeholderHeight: CGFloat { + let layoutManager = NSLayoutManager() + return layoutManager.defaultLineHeight(for: self.floatingLabel.font!) + 1 + } + + /// Returns the height of the primary (editable) text + private var textHeight: CGFloat { + let layoutManager = NSLayoutManager() + return layoutManager.defaultLineHeight(for: self.font!) + 1 + } + + /// Returns the total height of the control given the font settings + private var controlHeight: CGFloat { + return self.textHeight + self.placeholderSpacing + self.placeholderHeight + } + + open override var intrinsicContentSize: NSSize { + var sz = super.intrinsicContentSize + sz.height = self.controlHeight + return sz + } + + func configureCell() { + let customCell = DSFFloatLabelledSecureTextFieldCell() + customCell.isEditable = true + customCell.wraps = false + customCell.usesSingleLineMode = true + customCell.placeholderString = self.placeholderString + customCell.title = self.stringValue + customCell.font = self.font + customCell.isBordered = self.isBordered + customCell.isBezeled = self.isBezeled + customCell.bezelStyle = self.bezelStyle + customCell.isScrollable = true + customCell.isContinuous = self.isContinuous + customCell.alignment = self.alignment + customCell.formatter = self.formatter + customCell.topOffset = self.placeholderHeight + self.cell = customCell + } + + func setTopOffset(_ value: CGFloat) { + guard var f = self.cell as? DSFFloatLabelledTextFieldCellProtocol else { + return + } + f.topOffset = value + } + + /// Set the fonts to be used in the control + open func setFonts(primary: NSFont, secondary: NSFont) { + self.floatingLabel.font = secondary + self.font = primary + } + + public override init(frame frameRect: NSRect) { + super.init(frame: frameRect) + self.setup() + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + self.setup() + } + + open override func awakeFromNib() { + super.awakeFromNib() + + } + + open override func viewDidMoveToWindow() { + super.viewDidMoveToWindow() + + // When we're added to the view, make sure that the floating label alignment matches us + // (as the user might have changed the alignment BEFORE adding to the superview + self.floatingLabel.alignment = self.alignment + } + + private func setup() { + // Setup the common elements of the control + self.commonSetup() + + // Configure a default text cell + self.configureCell() + + // Listen to changes in the primary font so we can reconfigure to match + self.fontObserver = self.observe(\.font, options: [.new]) { [weak self] _, _ in + self?.reconfigureControl() + } + + // Listen to changes in the placeholder text so we can reflect it in the floater + self.placeholderObserver = self.observe(\.placeholderString, options: [.new]) { [weak self] _, _ in + guard let `self` = self else { return } + self.floatingLabel.stringValue = self.placeholderString! + self.reconfigureControl() + } + } + + /// Build the floating label + private func createFloatingLabel() { + if self.floatingLabel.superview == nil { + self.addSubview(self.floatingLabel) + } + + self.floatingLabel.wantsLayer = true + self.floatingLabel.isEditable = false + self.floatingLabel.isSelectable = false + self.floatingLabel.isEnabled = true + self.floatingLabel.isBezeled = false + self.floatingLabel.isBordered = false + self.floatingLabel.translatesAutoresizingMaskIntoConstraints = false + self.floatingLabel.font = NSFont.systemFont(ofSize: self.placeholderTextSize) + self.floatingLabel.textColor = NSColor.placeholderTextColor + self.floatingLabel.stringValue = self.placeholderString ?? "" + self.floatingLabel.alphaValue = 0.0 + self.floatingLabel.alignment = self.alignment + self.floatingLabel.drawsBackground = false + + self.floatingTop = NSLayoutConstraint( + item: self.floatingLabel, attribute: .top, + relatedBy: .equal, + toItem: self, attribute: .top, + multiplier: 1.0, constant: 10 + ) + self.addConstraint(self.floatingTop!) + + var x = NSLayoutConstraint( + item: self.floatingLabel, attribute: .leading, + relatedBy: .equal, + toItem: self, attribute: .leading, + multiplier: 1.0, constant: self.isBezeled ? 4 : 0 + ) + self.addConstraint(x) + + x = NSLayoutConstraint( + item: self.floatingLabel, attribute: .trailing, + relatedBy: .equal, + toItem: self, attribute: .trailing, + multiplier: 1.0, constant: self.isBezeled ? -4 : 0 + ) + self.addConstraint(x) + + self.floatingLabel.setContentHuggingPriority(NSLayoutConstraint.Priority(10), for: .horizontal) + self.floatingLabel.setContentCompressionResistancePriority(NSLayoutConstraint.Priority(10), for: .horizontal) + } + + private func commonSetup() { + self.wantsLayer = true + self.translatesAutoresizingMaskIntoConstraints = false + self.usesSingleLineMode = true + self.delegate = self + + // Default to natural layout + self.alignment = .natural + + self.createFloatingLabel() + + self.heightConstraint = NSLayoutConstraint( + item: self, attribute: .height, + relatedBy: .equal, + toItem: nil, attribute: .notAnAttribute, + multiplier: 1.0, constant: self.controlHeight + ) + self.addConstraint(self.heightConstraint!) + + // If the field already has text, make sure the placeholder is shown + if self.stringValue.count > 0 { + self.showPlaceholder(animated: false) + } + } + + /// Change the layout if any changes occur + private func reconfigureControl() { + if self.isCurrentFocus() { + /// If we are currently editing, then finish before changing. + self.window?.endEditing(for: nil) + } + self.expandFrame() + } + + /// Rebuild the frame of the text field to match the new settings + private func expandFrame() { + self.heightConstraint?.constant = self.controlHeight + self.setTopOffset(self.placeholderHeight + self.placeholderSpacing) + self.needsLayout = true + } + + open override func layout() { + super.layout() + + self.setTopOffset(self.placeholderHeight + self.placeholderSpacing) + self.needsLayout = true + } + +} + +// MARK: - Focus and editing + +extension DSFFloatLabelledSecureTextField: NSTextFieldDelegate { + + // Change the floating label color to represent active state + private func setFloatingLabelActive(_ active: Bool) { + if active { + self.floatingLabel.textColor = NSColor.simulatedAccentColor + } + else { + self.floatingLabel.textColor = NSColor.placeholderTextColor + } + + self.floatLabelDelegate?.floatLabelledTextField?(self, didFocus: active) + } + + public func controlTextDidChange(_ obj: Notification) { + guard let field = obj.object as? NSTextField else { + return + } + + if field.stringValue.count > 0, !self.isShowing { + self.showPlaceholder(animated: true) + } + else if field.stringValue.count == 0, self.isShowing { + self.hidePlaceholder() + } + + self.floatLabelDelegate?.floatLabelledTextFieldContentChanged?(self) + } + + open override func becomeFirstResponder() -> Bool { + let becomeResult = super.becomeFirstResponder() + if becomeResult, self.isCurrentFocus() { + // Set the color of the 'label' to match the current focus color. + // We need to perform this on the main thread after the current set of notifications are processed + // Why? We (occasionally) receive a 'controlTextDidEndEditing' message AFTER we receive a + // 'becomeFirstResponder'. I've read that this is related to the text field automatically selecting + // text when taking focus, but I haven't been able to verify this in any useful manner. + DispatchQueue.main.async { [weak self] in + if let `self` = self { + self.setFloatingLabelActive(true) + } + } + } + return becomeResult + } + + open func controlTextDidEndEditing(_: Notification) { + // When we lose focus, set the label color back to the placeholder color + self.setFloatingLabelActive(false) + } + + /// Does our text field currently have input focus? + private func isCurrentFocus() -> Bool { + // 1. Get the window's first responder + // 2. Check is has an active field editor + // 3. Is the delegate of the field editor us? + if let fr = self.window?.firstResponder as? NSTextView, + self.window?.fieldEditor(false, for: nil) != nil, + fr.delegate === self { + return true + } + return false + } +} + +// MARK: - Animations + +extension DSFFloatLabelledSecureTextField { + + /// Duration of the fade in/out of the secondary label + private var animationDuration: TimeInterval { + return shouldAnimate() ? 0.4 : 0.0 + } + + /// Returns true if the system is NOT set to reduce motion (accessibility settings) + private func shouldAnimate() -> Bool { + if #available(OSX 10.12, *) { + return !NSWorkspace.shared.accessibilityDisplayShouldReduceMotion + } else { + // Fallback on earlier versions. Just animate + return true + } + } + + private func showPlaceholder(animated: Bool) { + self.isShowing = true + if animated { + NSAnimationContext.runAnimationGroup({ [weak self] context in + guard let `self` = self else { return } + context.allowsImplicitAnimation = true + context.duration = self.animationDuration + self.floatingTop?.constant = 0 + self.floatingLabel.alphaValue = 1.0 + self.layoutSubtreeIfNeeded() + }, completionHandler: { + // + }) + } + else { + self.setFloatingLabelActive(false) + self.floatingTop?.constant = 0 + self.floatingLabel.alphaValue = 1.0 + } + } + + private func hidePlaceholder() { + self.isShowing = false + let duration = self.animationDuration + NSAnimationContext.runAnimationGroup({ [weak self] context in + guard let `self` = self else { return } + context.allowsImplicitAnimation = true + context.duration = duration + self.floatingTop?.constant = self.textHeight / 1.5 + self.floatingLabel.alphaValue = 0.0 + self.layoutSubtreeIfNeeded() + }, completionHandler: { + // + }) + } +} + +// MARK: - Cell definition + +private class DSFFloatLabelledSecureTextFieldCell: NSSecureTextFieldCell, DSFFloatLabelledTextFieldCellProtocol { + var topOffset: CGFloat = 0 + + private func offset() -> CGFloat { + return self.topOffset - (self.isBezeled ? 5 : 1) + } + + override func titleRect(forBounds rect: NSRect) -> NSRect { + return NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height) + } + + override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) { + let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height) + super.edit(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, event: event) + } + + override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) { + let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height) + super.select(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength) + } + + override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { + let insetRect = NSRect(x: cellFrame.origin.x, y: cellFrame.origin.y + self.offset(), width: cellFrame.width, height: cellFrame.height) + super.drawInterior(withFrame: insetRect, in: controlView) + } +} diff --git a/Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextControl.h b/Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextControl.h deleted file mode 100644 index 953be89..0000000 --- a/Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextControl.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// DSFFloatLabelledTextControl.h -// DSFFloatLabelledTextControl -// -// Created by Darren Ford on 4/2/19. -// Copyright © 2020 Darren Ford. All rights reserved. -// -// MIT License -// -// 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 - -//! Project version number for DSFFloatLabelledTextControl. -FOUNDATION_EXPORT double DSFFloatLabelledTextControlVersionNumber; - -//! Project version string for DSFFloatLabelledTextControl. -FOUNDATION_EXPORT const unsigned char DSFFloatLabelledTextControlVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift b/Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift index 19cd85d..f7369ed 100644 --- a/Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift +++ b/Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift @@ -1,31 +1,30 @@ // // DSFFloatLabelledTextField.swift -// DSFFloatLabelledTextControls // -// Created by Darren Ford on 4/2/19. -// Copyright © 2020 Darren Ford. All rights reserved. +// Copyright © 2023 Darren Ford. All rights reserved. // -// MIT License +// MIT license // -// 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: +// 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 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. // -// 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 AppKit /// DSFFloatLabelledTextField delegate protocol @objc public protocol DSFFloatLabelledTextFieldDelegate: NSObjectProtocol { @@ -61,13 +60,6 @@ import Cocoa } } - /// Spacing between the floating label and the text field text - @IBInspectable public var isSecure: Bool = false { - didSet { - self.configureCell(isSecure: self.isSecure) - } - } - // Override so that we can notify when the developer changes the text programatically too open override var stringValue: String { get { @@ -133,8 +125,8 @@ import Cocoa return sz } - func configureCell(isSecure: Bool = false) { - let customCell: NSTextFieldCell = isSecure ? DSFFloatLabelledSecureTextFieldCell() : DSFFloatLabelledTextFieldCell() + func configureCell() { + let customCell = DSFFloatLabelledTextFieldCell() customCell.isEditable = true customCell.wraps = false customCell.usesSingleLineMode = true @@ -148,10 +140,7 @@ import Cocoa customCell.isContinuous = self.isContinuous customCell.alignment = self.alignment customCell.formatter = self.formatter - - if var s = customCell as? DSFFloatLabelledTextFieldCellProtocol { - s.topOffset = self.placeholderHeight - } + customCell.topOffset = self.placeholderHeight self.cell = customCell } @@ -192,7 +181,7 @@ import Cocoa self.commonSetup() // Configure a default text cell - self.configureCell(isSecure: false) + self.configureCell() // Listen to changes in the primary font so we can reconfigure to match self.fontObserver = self.observe(\.font, options: [.new]) { [weak self] _, _ in @@ -312,7 +301,7 @@ extension DSFFloatLabelledTextField: NSTextFieldDelegate { // Change the floating label color to represent active state private func setFloatingLabelActive(_ active: Bool) { if active { - self.floatingLabel.textColor = NSColor.systemAccentColor + self.floatingLabel.textColor = NSColor.simulatedAccentColor } else { self.floatingLabel.textColor = NSColor.placeholderTextColor @@ -430,10 +419,6 @@ extension DSFFloatLabelledTextField { // MARK: - Cell definition -protocol DSFFloatLabelledTextFieldCellProtocol { - var topOffset: CGFloat { get set } -} - private class DSFFloatLabelledTextFieldCell: NSTextFieldCell, DSFFloatLabelledTextFieldCellProtocol { var topOffset: CGFloat = 0 @@ -460,76 +445,3 @@ private class DSFFloatLabelledTextFieldCell: NSTextFieldCell, DSFFloatLabelledTe super.drawInterior(withFrame: insetRect, in: controlView) } } - -private class DSFFloatLabelledSecureTextFieldCell: NSSecureTextFieldCell, DSFFloatLabelledTextFieldCellProtocol { - var topOffset: CGFloat = 0 - - private func offset() -> CGFloat { - return self.topOffset - (self.isBezeled ? 5 : 1) - } - - override func titleRect(forBounds rect: NSRect) -> NSRect { - return NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height) - } - - override func edit(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, event: NSEvent?) { - let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height) - super.edit(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, event: event) - } - - override func select(withFrame rect: NSRect, in controlView: NSView, editor textObj: NSText, delegate: Any?, start selStart: Int, length selLength: Int) { - let insetRect = NSRect(x: rect.origin.x, y: rect.origin.y + self.offset(), width: rect.width, height: rect.height) - super.select(withFrame: insetRect, in: controlView, editor: textObj, delegate: delegate, start: selStart, length: selLength) - } - - override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { - let insetRect = NSRect(x: cellFrame.origin.x, y: cellFrame.origin.y + self.offset(), width: cellFrame.width, height: cellFrame.height) - super.drawInterior(withFrame: insetRect, in: controlView) - } -} - -/// MARK: - Utilities - -fileprivate let kAccentColor: String = "AppleAccentColor" -fileprivate extension NSColor { - - /// The system accent color, with fallbacks for older macOS versions - static var systemAccentColor: NSColor { - if #available(OSX 10.14, *) { - // macOS 10.14 and above have a dedicated static NSColor - return NSColor.controlAccentColor - } - - // Use standard user defaults for anything lower than 10.14 - let userDefaults = UserDefaults.standard - guard userDefaults.object(forKey: kAccentColor) != nil else { - return DefaultColor() - } - - return ColorForSystemColorOffset(userDefaults.integer(forKey: kAccentColor)) - } - - /// Map an integer value to a system color - static func ColorForSystemColorOffset(_ value: Int) -> NSColor { - switch value { - case -1: return NSColor.systemGray - case 0: return NSColor.systemRed - case 1: return NSColor.systemOrange - case 2: return NSColor.systemYellow - case 3: return NSColor.systemGreen - case 4: return NSColor.systemBlue - case 5: return NSColor.systemPurple - case 6: return NSColor.systemPink - default: return DefaultColor() - } - } - - static func DefaultColor() -> NSColor { - if #available(OSX 11.0, *) { - return NSColor.systemGray - } - else { - return NSColor.systemBlue - } - } -} diff --git a/Sources/DSFFloatLabelledTextField/Info.plist b/Sources/DSFFloatLabelledTextField/Info.plist deleted file mode 100644 index f49eb3e..0000000 --- a/Sources/DSFFloatLabelledTextField/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSHumanReadableCopyright - Copyright © 2020 Darren Ford. All rights reserved. - - diff --git a/Sources/DSFFloatLabelledTextField/private/utils.swift b/Sources/DSFFloatLabelledTextField/private/utils.swift new file mode 100644 index 0000000..5f15d40 --- /dev/null +++ b/Sources/DSFFloatLabelledTextField/private/utils.swift @@ -0,0 +1,46 @@ +// +// utils.swift +// +// Copyright © 2023 Darren Ford. All rights reserved. +// +// MIT license +// +// 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 +import AppKit + +protocol DSFFloatLabelledTextFieldCellProtocol { + var topOffset: CGFloat { get set } +} + +extension NSColor { + /// A backwards compatible `controlAccentColor` + @inlinable static var simulatedAccentColor: NSColor { + if #available(macOS 10.14, *) { + // macOS 10.14 and above have a dedicated static NSColor + return NSColor.controlAccentColor + } + else { + // Just use the menu highlight color - there's no concept of 'accent' color pre 10.14 + return NSColor.selectedMenuItemColor + } + } +}