Compare commits

...

4 Commits

Author SHA1 Message Date
Darren Ford 637703bd5e Update documentation 2023-03-15 12:25:08 +11:00
Darren Ford ad85f71bd2 Moved 'isSecure' out into two different classes
In later versions of Xcode changing the NSTextFieldCell to an NSSecureTextFieldCell generates an error (no longer secure).

Rather than maintain two almost identical field types, GYB is being used to templatize the primary source file 'DSFFloatLabelledTextField.swift.gyb' into two different classes.

As a result, changes _must_ be made to the template file rather than the sources.
2023-03-15 12:16:35 +11:00
Darren Ford 0e89b0c162 Fixed podspec 2023-03-15 11:20:41 +11:00
Darren Ford 344eab8512 First cut refactor secure 2023-03-15 11:06:11 +11:00
36 changed files with 2370 additions and 530 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
DSFFloatLabelledTextControls.xcodeproj/xcuserdata
xcschememanagement.plist
.swiftpm
Demos/DSFFloatLabelledTextControls.xcodeproj/xcuserdata
gyb.cpython-310.pyc

View File

@ -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.version = "2.0.0"
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/DSFFloatLabelledTextField/**/*.{swift}"
s.frameworks = "Cocoa"
s.swift_version = "5.0"
s.swift_version = "5.4"
end

View File

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>All.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>DSFFloatLabelledTextControl.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>DSFFloatLabelledTextControls Demo objc.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>DSFFloatLabelledTextControls RTL.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>DSFFloatLabelledTextControls.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>DSFFloatLabelledTextField.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>2351F84A2207D15C00EFA4F7</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>23A2B7E8220792FE00B2FC77</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>23C3AB9923FBCFA30079D9C3</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -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 = "<group>"; };
2351F84E2207D15C00EFA4F7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
2351F85922082AD800EFA4F7 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
2351F85A22082AD800EFA4F7 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
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 = "<group>"; };
23A2B7EE220792FF00B2FC77 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
23A2B7F1220792FF00B2FC77 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
23A2B7F3220792FF00B2FC77 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
23A2B7F4220792FF00B2FC77 /* DSFFloatLabelledTextControls.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFFloatLabelledTextControls.entitlements; sourceTree = "<group>"; };
23A2B7FA2207931500B2FC77 /* DSFFloatLabelledTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DSFFloatLabelledTextField.swift; sourceTree = "<group>"; };
23ACF96D228A3F6600FF6594 /* DSFFloatLabelledTextControl.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = DSFFloatLabelledTextControl.podspec; sourceTree = "<group>"; };
23A3061A29C12C9C00BEC98A /* DSFFloatLabelledTextControls */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = DSFFloatLabelledTextControls; path = ..; sourceTree = "<group>"; };
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 = "<group>"; };
23C3AB9D23FBCFA30079D9C3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@ -117,22 +89,14 @@
23C3ABA523FBCFA40079D9C3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
23C3ABA723FBCFA40079D9C3 /* DSFFloatLabelledTextControls_Demo_objc.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFFloatLabelledTextControls_Demo_objc.entitlements; sourceTree = "<group>"; };
23E8256D2575D28A0023A3C7 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/MainMenu.strings; sourceTree = "<group>"; };
23FFF1CD23BD490F008FA55A /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
/* 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 = "<group>";
};
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 = "<group>";
};
23FFF1CE23BD496A008FA55A /* Sources */ = {
isa = PBXGroup;
children = (
2351F84C2207D15C00EFA4F7 /* DSFFloatLabelledTextField */,
);
path = Sources;
sourceTree = "<group>";
};
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 */;
}

View File

@ -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);
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -692,7 +692,7 @@
<stackView distribution="fill" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="250" verticalStackHuggingPriority="249.99998474121094" ambiguous="YES" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5Xn-w2-ecV">
<rect key="frame" x="20" y="27" width="440" height="98"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XPK-cR-iSb" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="XPK-cR-iSb" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="66" width="440" height="32"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="Title" id="PFO-GA-4nl">
<font key="font" metaFont="system" size="24"/>
@ -705,7 +705,7 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5uu-Hi-xn8" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="5uu-Hi-xn8" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="33" width="440" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="Subtitle text goes here" id="Uic-HN-dxQ">
<font key="font" metaFont="system" size="18"/>
@ -718,7 +718,7 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="257-gt-5Ui" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="257-gt-5Ui" userLabel="Title Field" customClass="DSFFloatLabelledSecureTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="0.0" width="440" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" alignment="left" placeholderString="My Secret Password" id="Msd-kf-k0Q">
<font key="font" metaFont="system" size="18"/>
@ -729,7 +729,6 @@
<userDefinedRuntimeAttribute type="number" keyPath="placeholderTextSize">
<real key="value" value="13"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSecure" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="floatLabelDelegate" destination="Voe-Tx-rLC" id="fDf-Y2-VaF"/>

View File

@ -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) {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -16,6 +16,7 @@
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="DSFFloatLabelledTextControls" customModuleProvider="target">
<connections>
<outlet property="passwordFloatingLabel" destination="VLP-0u-Kf3" id="9Lw-el-n78"/>
<outlet property="secureTextField" destination="VLP-0u-Kf3" id="uDf-HC-5AQ"/>
<outlet property="topFloatingLabel" destination="tnz-85-NdV" id="Pyt-zO-up9"/>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
@ -686,19 +687,19 @@
<window title="Floating Labels Test" allowsToolTipsWhenApplicationIsInactive="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="497" height="441"/>
<rect key="contentRect" x="335" y="390" width="497" height="592"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="497" height="491"/>
<rect key="frame" x="0.0" y="0.0" width="497" height="592"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<box boxType="secondary" title="Purchase Form" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="pnz-8U-z2F">
<rect key="frame" x="17" y="333" width="463" height="140"/>
<view key="contentView" id="fUJ-lp-O9s">
<rect key="frame" x="3" y="3" width="457" height="134"/>
<rect key="frame" x="17" y="432" width="463" height="142"/>
<view key="contentView" ambiguous="YES" id="fUJ-lp-O9s">
<rect key="frame" x="4" y="5" width="455" height="134"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="tnz-85-NdV" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tnz-85-NdV" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="12" y="97" width="374" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" title="Groovy Software v1" placeholderString="Title" id="ETD-Tb-a7N">
<font key="font" metaFont="system" size="18"/>
@ -714,7 +715,7 @@
<outlet property="floatLabelDelegate" destination="Voe-Tx-rLC" id="62z-Xl-apm"/>
</connections>
</textField>
<button toolTip="Check for programattic changing of stringValue" horizontalHuggingPriority="999" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nTi-Ym-3BC">
<button toolTip="Check for programattic changing of stringValue" horizontalHuggingPriority="999" verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nTi-Ym-3BC">
<rect key="frame" x="387" y="93" width="69" height="32"/>
<buttonCell key="cell" type="push" title="Reset" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="pTp-yr-DKx">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@ -724,7 +725,7 @@
<action selector="resetPressed:" target="Voe-Tx-rLC" id="keN-c6-1wD"/>
</connections>
</button>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="pyP-dk-pAy">
<box verticalHuggingPriority="750" ambiguous="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="pyP-dk-pAy">
<rect key="frame" x="12" y="86" width="437" height="5"/>
</box>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="zbB-HI-AVu" userLabel="Price Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
@ -746,7 +747,7 @@
<box horizontalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="Mrp-MT-gGa">
<rect key="frame" x="117" y="46" width="5" height="43"/>
</box>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="SAo-aL-Ofe" userLabel="Location Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SAo-aL-Ofe" userLabel="Location Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="128" y="55" width="321" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" placeholderString="Specific Location (Optional)" id="BHQ-MA-tIJ">
<font key="font" metaFont="system" size="18"/>
@ -759,10 +760,10 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="YiQ-45-hxx">
<box verticalHuggingPriority="750" ambiguous="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="YiQ-45-hxx">
<rect key="frame" x="12" y="44" width="437" height="5"/>
</box>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" translatesAutoresizingMaskIntoConstraints="NO" id="h8z-Ku-xLD" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="h8z-Ku-xLD" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="12" y="13" width="437" height="25"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="border" placeholderString="Description" id="XtC-Dr-5RL">
<font key="font" metaFont="system" size="18"/>
@ -804,10 +805,10 @@
</view>
</box>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="CVr-p8-VOx">
<rect key="frame" x="20" y="303" width="457" height="5"/>
<rect key="frame" x="20" y="402" width="457" height="5"/>
</box>
<stackView distribution="fillEqually" orientation="horizontal" alignment="centerY" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="waN-7O-qLn">
<rect key="frame" x="20" y="242" width="457" height="33"/>
<rect key="frame" x="20" y="341" width="457" height="33"/>
<subviews>
<textField horizontalHuggingPriority="50" verticalHuggingPriority="750" horizontalCompressionResistancePriority="500" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="oU9-jl-8Ow" userLabel="Title Field" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="0.0" width="50" height="33"/>
@ -852,10 +853,10 @@
</customSpacing>
</stackView>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="QuB-jf-wmu">
<rect key="frame" x="20" y="206" width="457" height="5"/>
<rect key="frame" x="20" y="305" width="457" height="5"/>
</box>
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CBs-1o-JjM">
<rect key="frame" x="20" y="80" width="250" height="112"/>
<rect key="frame" x="20" y="179" width="250" height="112"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3hp-BV-Ls3" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="90" width="250" height="22"/>
@ -873,7 +874,7 @@
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VLP-0u-Kf3" customClass="DSFFloatLabelledTextField" customModule="DSFFloatLabelledTextField">
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VLP-0u-Kf3" customClass="DSFFloatLabelledSecureTextField" customModule="DSFFloatLabelledTextField">
<rect key="frame" x="0.0" y="60" width="250" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="caterpillar" placeholderString="Password" drawsBackground="YES" id="2oD-a4-pQh">
<font key="font" metaFont="system" size="14"/>
@ -887,7 +888,6 @@
<userDefinedRuntimeAttribute type="number" keyPath="placeholderSpacing">
<real key="value" value="1"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSecure" value="YES"/>
</userDefinedRuntimeAttributes>
<connections>
<outlet property="floatLabelDelegate" destination="Voe-Tx-rLC" id="IaQ-aC-gRR"/>
@ -971,7 +971,7 @@
<constraint firstItem="pnz-8U-z2F" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" id="zgF-Fx-OjE"/>
</constraints>
</view>
<point key="canvasLocation" x="-480" y="220"/>
<point key="canvasLocation" x="-480.5" y="144"/>
</window>
</objects>
</document>

View File

@ -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]
)

View File

@ -1,8 +1,9 @@
# macOS Float Label Pattern Text Field
A macOS Cocoa single-line NSTextField that implements the Float Label Pattern. You can read about the float pattern [here](http://mds.is/float-label-pattern/). Usable in Swift and Objective-C projects. Supports secure edit fields.
A macOS Cocoa single-line NSTextField/NSSecureTextField that implements the Float Label Pattern.
You can read about the float pattern [here](http://mds.is/float-label-pattern/).
![](https://img.shields.io/github/v/tag/dagronf/DSFFloatLabelledTextControl) ![](https://img.shields.io/badge/macOS-10.11+-red) ![](https://img.shields.io/badge/Swift-5.0-orange.svg)
![](https://img.shields.io/github/v/tag/dagronf/DSFFloatLabelledTextControl) ![](https://img.shields.io/badge/macOS-10.11+-red) ![](https://img.shields.io/badge/Swift-5.4-orange.svg) ![](https://img.shields.io/badge/ObjectiveC-2.0-purple.svg)
![](https://img.shields.io/badge/License-MIT-lightgrey) [![](https://img.shields.io/badge/pod-compatible-informational)](https://cocoapods.org) [![](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager)
## Screenshot
@ -23,17 +24,14 @@ Add `https://github.com/dagronf/DSFFloatLabelledTextControl` to your project.
Add `pod 'DSFFloatLabelledTextControl', :git => 'https://github.com/dagronf/DSFFloatLabelledTextControl'` to your podfile.
### Directly
Copy the `DSFFloatLabelledTextField.swift` into your project. This class inherits directly from `NSTextField` so all settings that you make on a text field (eg. alignment) should also apply here.
## Usage
### 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
@ -48,32 +46,19 @@ parentView.addSubview(field)
## Custom Properties
This control inherits from `NSTextField`, so all `NSTextField` functionalities (like cocoa binding and Interface Builder settings) are available. If you can use an NSTextField, you can use this control.
These controls inherit from `NSTextField`, so all `NSTextField` functionalities (like cocoa binding and Interface Builder settings) are available. If you can use an NSTextField, you can use this control.
`placeholderTextSize` - the size of the text used in the floating label (in pt)
`placeholderSpacing` - the distance between the text field text and the floating label (in px)
* `placeholderTextSize` - the size of the text used in the floating label (in pt)
* `placeholderSpacing` - the distance between the text field text and the floating label (in px)
## Delegate Handling
You can specify a delegate (`floatLabelDelegate`), either programatically or via Interface Builder, to receive additional information regarding the actions of the control.
```swift
@objc public protocol DSFFloatLabelledTextFieldDelegate: NSObjectProtocol {
/// Called when the label is shown or hidden
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelledTextField, didShowFloatingLabel didShow: Bool)
/// Called when the field becomes or loses first responder status
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelledTextField, didFocus: Bool)
/// Called when the content of the field changes
@objc optional func floatLabelledTextFieldContentChanged(_ field: DSFFloatLabelledTextField)
}
```
## Screenshot
<img src="https://github.com/dagronf/dagronf.github.io/blob/master/art/projects/DSFFloatingLabel/light-mode-secure-field.png?raw=true" alt="drawing" width="265"/>
## Credits
* Pattern devised by [Matt D. Smith](http://mds.is/matt/)
@ -82,14 +67,15 @@ You can specify a delegate (`floatLabelDelegate`), either programatically or via
## Versions
* `1.8.0` Added secure text field support, delegate support
* `2.0.0`: Moved secure text field into separate class (fixing security warnings in modern Xcode)
* `1.8.0`: Added secure text field support, delegate support
## License
```
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

View File

@ -0,0 +1,448 @@
// ** Automatically generated from DSFFloatLabelledTextField.swift.gyb **
//
// DSFFloatLabelledSecureTextField.swift
// (generated using tools/regenerate-sources.sh)
//
// 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 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)
}
}

View File

@ -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 <Cocoa/Cocoa.h>
//! 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 <DSFFloatLabelledTextControl/PublicHeader.h>

View File

@ -1,31 +1,32 @@
// ** Automatically generated from DSFFloatLabelledTextField.swift.gyb **
//
// DSFFloatLabelledTextField.swift
// DSFFloatLabelledTextControls
// (generated using tools/regenerate-sources.sh)
//
// 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 +62,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 +127,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,11 +142,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 +182,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 +302,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 +420,6 @@ extension DSFFloatLabelledTextField {
// MARK: - Cell definition
protocol DSFFloatLabelledTextFieldCellProtocol {
var topOffset: CGFloat { get set }
}
private class DSFFloatLabelledTextFieldCell: NSTextFieldCell, DSFFloatLabelledTextFieldCellProtocol {
var topOffset: CGFloat = 0
@ -460,76 +446,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
}
}
}

View File

@ -0,0 +1,448 @@
// ** Automatically generated from DSFFloatLabelledTextField.swift.gyb **
//
// DSFFloatLabelled${field_type}Field.swift
// (generated using tools/regenerate-sources.sh)
//
// 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 DSFFloatLabelled${field_type}FieldDelegate: NSObjectProtocol {
/// Called when the label is shown or hidden
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelled${field_type}Field, didShowFloatingLabel didShow: Bool)
/// Called when the field becomes or loses first responder status
@objc optional func floatLabelledTextField(_ field: DSFFloatLabelled${field_type}Field, didFocus: Bool)
/// Called when the content of the field changes
@objc optional func floatLabelledTextFieldContentChanged(_ field: DSFFloatLabelled${field_type}Field)
}
/// An NS${field_type}Field that implements the Float Label Pattern
@IBDesignable open class DSFFloatLabelled${field_type}Field: NS${field_type}Field {
/// 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: DSFFloatLabelled${field_type}FieldDelegate?
/// 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 = DSFFloatLabelled${field_type}FieldCell()
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 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 DSFFloatLabelled${field_type}Field: 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 DSFFloatLabelled${field_type}Field {
/// 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 DSFFloatLabelled${field_type}FieldCell: NS${field_type}FieldCell, 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)
}
}

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 Darren Ford. All rights reserved.</string>
</dict>
</plist>

View File

@ -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
}
}
}

3
tools/gyb Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env python3
import gyb
gyb.main()

1258
tools/gyb.py Normal file

File diff suppressed because it is too large Load Diff

26
tools/regenerate-sources.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
# A script to re-build the text field sources from the source template file
# Stop on error
set -e
# The path where this script resides
script_path="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
# The path for the package (one level up)
package_path="$( cd -- "${script_path}/../" >/dev/null 2>&1 ; pwd -P )"
pushd .
# We have to run docbuild in the root of the source package
cd "${package_path}"
# Generate the NSTextField implementation
python3 ${script_path}/gyb --line-directive '' -D field_type="Text" -o "DSFFloatLabelledTextField.swift" "Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift.gyb"
# Generate the NSSecureTextField implementation
python3 ${script_path}/gyb --line-directive '' -D field_type="SecureText" -o "DSFFloatLabelledSecureTextField.swift" "Sources/DSFFloatLabelledTextField/DSFFloatLabelledTextField.swift.gyb"
popd
echo "Done!"