├── Art └── artwork.pcvd ├── Demos └── DSFDropFilesView Demo │ ├── DSFDropFilesView Demo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── folder-add.imageset │ │ │ ├── folder-add.png │ │ │ ├── folder-add@2x.png │ │ │ ├── folder-add@3x.png │ │ │ └── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── DSFDropFilesView_Demo.entitlements │ ├── AppDelegate.swift │ ├── Info.plist │ └── ViewController.swift │ ├── DSFDropFilesView ObjC Demo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── AppDelegate.h │ ├── ViewController.h │ ├── main.m │ ├── DSFDropFilesView_ObjC_Demo.entitlements │ ├── AppDelegate.m │ ├── Info.plist │ ├── ViewController.m │ └── Base.lproj │ │ └── Main.storyboard │ ├── DSFDropFilesView SwiftUI Demo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── DSFDropFilesView_SwiftUI_Demo.entitlements │ ├── Info.plist │ ├── AppDelegate.swift │ ├── ContentView.swift │ └── Base.lproj │ │ └── Main.storyboard │ └── DSFDropFilesView Demo.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── project.pbxproj ├── Tests ├── LinuxMain.swift └── DSFDropFilesViewTests │ ├── XCTestManifests.swift │ └── DSFDropFilesViewTests.swift ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Package.resolved ├── .github └── workflows │ └── swift.yml ├── Package.swift ├── LICENSE ├── Sources └── DSFDropFilesView │ ├── private │ ├── DSFDropFilesView+utils.swift │ └── DSFDropFilesView+image.swift │ ├── DSFDropFilesView+protocol.swift │ ├── DSFDropFilesView+SwiftUI.swift │ └── DSFDropFilesView.swift └── README.md /Art/artwork.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Art/artwork.pcvd -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import DSFDropFilesViewTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += DSFDropFilesViewTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add.png -------------------------------------------------------------------------------- /Tests/DSFDropFilesViewTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(DSFDropFilesViewTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@2x.png -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagronf/DSFDropFilesView/HEAD/Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/folder-add@3x.png -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DSFDropFilesView ObjC Demo 4 | // 5 | // Created by Darren Ford on 24/11/20. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : NSObject 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | Package~.swift 7 | Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.xcworkspace/xcshareddata 8 | ._Package.swift 9 | Demos/DSFDropFilesView Demo/._DSFDropFilesView Demo.xcodeproj 10 | Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/._project.xcworkspace 11 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // DSFDropFilesView ObjC Demo 4 | // 5 | // Created by Darren Ford on 24/11/20. 6 | // 7 | 8 | #import 9 | 10 | @import DSFDropFilesView; 11 | 12 | @interface ViewController : NSViewController 13 | 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DSFDropFilesView ObjC Demo 4 | // 5 | // Created by Darren Ford on 24/11/20. 6 | // 7 | 8 | #import 9 | 10 | int main(int argc, const char * argv[]) { 11 | @autoreleasepool { 12 | // Setup code that might create autoreleased objects goes here. 13 | } 14 | return NSApplicationMain(argc, argv); 15 | } 16 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "DSFAppearanceManager", 6 | "repositoryURL": "https://github.com/dagronf/DSFAppearanceManager", 7 | "state": { 8 | "branch": null, 9 | "revision": "fd459c8060d46d17b780a80ac43db83b444e2e13", 10 | "version": "3.6.2" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/DSFDropFilesView_Demo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/DSFDropFilesView_ObjC_Demo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/DSFDropFilesView_SwiftUI_Demo.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Swift project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift 3 | 4 | name: Swift 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: macos-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Build 20 | run: swift build -v 21 | - name: Run tests 22 | run: swift test -v 23 | -------------------------------------------------------------------------------- /Tests/DSFDropFilesViewTests/DSFDropFilesViewTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import DSFDropFilesView 3 | 4 | final class DSFDropFilesViewTests: XCTestCase { 5 | // func testExample() { 6 | // // This is an example of a functional test case. 7 | // // Use XCTAssert and related functions to verify your tests produce the correct 8 | // // results. 9 | // XCTAssertEqual(DSFDropFilesView().text, "Hello, World!") 10 | // } 11 | // 12 | // static var allTests = [ 13 | // ("testExample", testExample), 14 | // ] 15 | } 16 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DSFDropFilesView Demo 4 | // 5 | // Created by Darren Ford on 27/10/20. 6 | // 7 | 8 | import Cocoa 9 | 10 | @NSApplicationMain 11 | class AppDelegate: NSObject, NSApplicationDelegate { 12 | 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DSFDropFilesView ObjC Demo 4 | // 5 | // Created by Darren Ford on 24/11/20. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | 22 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 23 | // Insert code here to tear down your application 24 | } 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/folder-add.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "folder-add.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "folder-add@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "folder-add@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.4 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "DSFDropFilesView", 7 | platforms: [ 8 | .macOS(.v10_13) 9 | ], 10 | products: [ 11 | .library(name: "DSFDropFilesView", targets: ["DSFDropFilesView"]), 12 | .library(name: "DSFDropFilesView-static", type: .static, targets: ["DSFDropFilesView"]), 13 | .library(name: "DSFDropFilesView-shared", type: .dynamic, targets: ["DSFDropFilesView"]), 14 | ], 15 | dependencies: [ 16 | .package(url: "https://github.com/dagronf/DSFAppearanceManager", from: "3.5.0") 17 | ], 18 | targets: [ 19 | .target( 20 | name: "DSFDropFilesView", 21 | dependencies: [ 22 | .product(name: "DSFAppearanceManager", package: "DSFAppearanceManager") 23 | ] 24 | ), 25 | .testTarget( 26 | name: "DSFDropFilesViewTests", 27 | dependencies: ["DSFDropFilesView"]), 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Darren Ford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSMainStoryboardFile 26 | Main 27 | NSPrincipalClass 28 | NSApplication 29 | 30 | 31 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSMainStoryboardFile 26 | Main 27 | NSPrincipalClass 28 | NSApplication 29 | 30 | 31 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSMainStoryboardFile 26 | Main 27 | NSPrincipalClass 28 | NSApplication 29 | 30 | 31 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DSFDropFilesView SwiftUI Demo 4 | // 5 | // Created by Darren Ford on 23/11/20. 6 | // 7 | 8 | import Cocoa 9 | import SwiftUI 10 | 11 | @main 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | var window: NSWindow! 15 | 16 | 17 | func applicationDidFinishLaunching(_ aNotification: Notification) { 18 | // Create the SwiftUI view that provides the window contents. 19 | let contentView = ContentView() 20 | 21 | // Create the window and set the content view. 22 | window = NSWindow( 23 | contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), 24 | styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], 25 | backing: .buffered, defer: false) 26 | window.isReleasedWhenClosed = false 27 | window.center() 28 | window.setFrameAutosaveName("Main Window") 29 | window.contentView = NSHostingView(rootView: contentView) 30 | window.makeKeyAndOrderFront(nil) 31 | } 32 | 33 | func applicationWillTerminate(_ aNotification: Notification) { 34 | // Insert code here to tear down your application 35 | } 36 | 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // DSFDropFilesView SwiftUI Demo 4 | // 5 | // Created by Darren Ford on 23/11/20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | import DSFDropFilesView 11 | 12 | struct ContentView: View { 13 | 14 | @State private var label = "Drop On Me!" 15 | @State private var dropEnabled = true 16 | 17 | private var displayProperties = 18 | DSFDropFilesView.SwiftUI.DisplayProperties(cornerRadius: 12) 19 | 20 | var body: some View { 21 | VStack { 22 | DSFDropFilesView.SwiftUI( 23 | displayProperties: self.displayProperties, 24 | isEnabled: self.dropEnabled, 25 | allowsMultipleDrop: true, 26 | iconLabel: self.label, 27 | selectFilesLabel: "Select Files…", 28 | selectFilesButtonIsLink: true, 29 | validateFiles: { urls in 30 | return .copy 31 | }, 32 | dropFiles: { urls in 33 | Swift.print("\(urls)") 34 | return true 35 | }, 36 | selectFiles: { 37 | Swift.print("User clicked select files") 38 | } 39 | ) 40 | .padding(4) 41 | 42 | Button("Toggle settings") { 43 | self.dropEnabled.toggle() 44 | self.label = self.dropEnabled ? "Drop On Me!" : "You Can't Drop On Me!" 45 | } 46 | .padding(8) 47 | } 48 | } 49 | } 50 | 51 | 52 | struct ContentView_Previews: PreviewProvider { 53 | static var previews: some View { 54 | ContentView() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // DSFDropFilesView ObjC Demo 4 | // 5 | // Created by Darren Ford on 24/11/20. 6 | // 7 | 8 | #import "ViewController.h" 9 | 10 | 11 | @implementation ViewController 12 | 13 | - (void)viewDidLoad { 14 | [super viewDidLoad]; 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | - (void)setRepresentedObject:(id)representedObject { 20 | [super setRepresentedObject:representedObject]; 21 | 22 | // Update the view, if already loaded. 23 | } 24 | 25 | - (NSDragOperation)dropFilesView:(DSFDropFilesView *)sender validateFiles:(NSArray * _Nonnull)validateFiles { 26 | return NSDragOperationCopy; 27 | } 28 | 29 | - (BOOL)dropFilesView:(DSFDropFilesView *)sender didDropFiles:(NSArray *)files { 30 | NSLog(@"User dropped files %@", files); 31 | return true; 32 | } 33 | 34 | - (void)dropFilesViewWantsSelectFiles:(DSFDropFilesView *)sender { 35 | NSLog(@"User clicked select files"); 36 | 37 | __weak typeof(self) bself = self; 38 | dispatch_async(dispatch_get_main_queue(), ^(void){ 39 | [bself selectFiles]; 40 | }); 41 | } 42 | 43 | - (void)selectFiles { 44 | NSOpenPanel* openPanel = [NSOpenPanel openPanel]; 45 | 46 | [openPanel setAllowsMultipleSelection: YES]; 47 | [openPanel setCanChooseDirectories:YES]; 48 | [openPanel setCanCreateDirectories:YES]; 49 | [openPanel setCanChooseFiles:YES]; 50 | 51 | [openPanel beginWithCompletionHandler:^(NSInteger result) { 52 | if (result == NSModalResponseOK) { 53 | NSLog(@"User dropped files %@", [openPanel URLs]); 54 | } 55 | }]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Sources/DSFDropFilesView/private/DSFDropFilesView+utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFDropFilesView+utils.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT license 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all copies or substantial 14 | // portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | // 21 | 22 | #if os(macOS) 23 | 24 | import AppKit 25 | 26 | internal class DSFHandButton: NSButton { 27 | var wantsHandCursor: Bool = true { 28 | didSet { 29 | self.needsLayout = true 30 | } 31 | } 32 | public override func resetCursorRects() { 33 | // Add the hand cursor when we're over the button 34 | if self.wantsHandCursor && self.isEnabled { 35 | self.addCursorRect(bounds, cursor: .pointingHand) 36 | } 37 | } 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DSFDropFilesView Demo 4 | // 5 | // Created by Darren Ford on 27/10/20. 6 | // 7 | 8 | import Cocoa 9 | import DSFDropFilesView 10 | 11 | class ViewController: NSViewController { 12 | @IBOutlet var left: DSFDropFilesView! 13 | @IBOutlet weak var middle: DSFDropFilesView! 14 | @IBOutlet var right: DSFDropFilesView! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | // Do any additional setup after loading the view. 20 | } 21 | 22 | override var representedObject: Any? { 23 | didSet { 24 | // Update the view, if already loaded. 25 | } 26 | } 27 | } 28 | 29 | extension ViewController: DSFDropFilesViewProtocol { 30 | private func isFolder(_ url: URL) -> Bool { 31 | var r = ObjCBool(false) 32 | FileManager.default.fileExists(atPath: url.path, isDirectory: &r) 33 | return r.boolValue 34 | } 35 | 36 | func dropFilesViewWantsSelectFiles(_ sender: DSFDropFilesView) { 37 | guard sender === self.left else { 38 | return 39 | } 40 | 41 | let myOpen = NSOpenPanel() 42 | myOpen.allowsMultipleSelection = true 43 | myOpen.canChooseFiles = true 44 | myOpen.canChooseDirectories = true 45 | 46 | if let w = self.view.window { 47 | myOpen.beginSheetModal(for: w) { response in 48 | if response == NSApplication.ModalResponse.OK { 49 | Swift.print(myOpen.urls) 50 | } 51 | } 52 | } 53 | } 54 | 55 | func dropFilesView(_ sender: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation { 56 | if sender === self.left { 57 | let foundInvalidImage = validateFiles.first { (url) -> Bool in 58 | guard let im = NSImage(byReferencingFile: url.path) else { 59 | // Found an invalid image 60 | return true 61 | } 62 | return !im.isValid 63 | } 64 | 65 | if let _ = foundInvalidImage { 66 | return [] 67 | } 68 | 69 | return .copy 70 | } 71 | else if sender === self.right { 72 | let c = validateFiles.filter { !self.isFolder($0) }.count 73 | return c > 0 ? [] : .copy 74 | } 75 | else if sender === self.middle { 76 | return .copy 77 | } 78 | return [] 79 | } 80 | 81 | func dropFilesView(_: DSFDropFilesView, didDropFiles files: [URL]) -> Bool { 82 | Swift.print(files) 83 | return true 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/DSFDropFilesView/DSFDropFilesView+protocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFDropFilesView+protocol.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT license 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all copies or substantial 14 | // portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | // 21 | 22 | #if os(macOS) 23 | 24 | import AppKit 25 | 26 | /// Delegate protocol for a Drop Files View. 27 | @objc public protocol DSFDropFilesViewProtocol: NSObjectProtocol { 28 | /// Callback to the owner that the user has clicked the 'Select Files...' button 29 | @objc optional func dropFilesViewWantsSelectFiles(_ sender: DSFDropFilesView) 30 | 31 | /// Ask the delegate to validate the urls that the user has dragged onto the control 32 | /// - Parameters: 33 | /// - sender: The drop files view that initiated the action 34 | /// - validateFiles: The urls to be validated 35 | /// - Returns the drag operation for the dragged files. Return [] to fail the drag 36 | func dropFilesView(_ sender: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation 37 | 38 | /// The files have been dropped on the view 39 | /// - Parameters: 40 | /// - sender: The drop files view that initiated the action 41 | /// - files: The urls that were dropped 42 | /// - Returns true if the drop action was successful 43 | func dropFilesView(_ sender: DSFDropFilesView, didDropFiles files: [URL]) -> Bool 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DSFDropFilesView 2 | 3 | ![](https://github.com/dagronf/dagronf.github.io/blob/master/art/projects/DSFDropFilesView/screenshot.png?raw=true) 4 | 5 | A simple view class for dropping files onto. Supporting Swift, SwiftUI and Objective-C. 6 | 7 | ![](https://img.shields.io/github/v/tag/dagronf/DSFDropFilesView) ![](https://img.shields.io/badge/macOS-10.13+-red) ![](https://img.shields.io/badge/Swift-5.0-orange.svg) 8 | ![](https://img.shields.io/badge/License-MIT-lightgrey) [![](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) 9 | 10 | 11 | ## Why 12 | 13 | I've need something like this many times. So here's a module to make my future life easier maybe. 14 | 15 | ## Installation 16 | 17 | ### Swift Package Manager 18 | 19 | Add `https://github.com/dagronf/DSFDropFilesView` to your project. 20 | 21 | ## Usage 22 | 23 | ### Via Interface Builder 24 | 25 | Add a new `NSView` instance using Interface Builder, then change the class type to `DSFDropFilesView` 26 | 27 | ### Programatically 28 | 29 | ```swift 30 | let dropView = DSFDropFilesView(frame: .zero) 31 | ``` 32 | 33 | ### SwiftUI 34 | 35 | See below. 36 | 37 | ### Objective-C 38 | 39 | Add `https://github.com/dagronf/DSFDropFilesView` to your project. 40 | 41 | Note that if you need to use `DSFDropFilesView` in Objective-C projects supporting macOS earlier than 10.14, you will also need to make sure to set `Always Embed Swift Standard Libraries` to `Yes` in the project settings. Before macOS 10.14 the Swift standard libraries are not distributed as part of the macOS installation and you'll need to provide your own in your application. 42 | 43 | ## Delegation 44 | 45 | To receive callbacks from the control, set the `dropDelegate` to your view controller. 46 | 47 | ```swift 48 | @objc optional func dropFilesViewWantsSelectFiles(_ sender: DSFDropFilesView) 49 | ``` 50 | An optional method that gets called if the user clicks the 'Select files...' button on the control. 51 | 52 | ```swift 53 | func dropFilesView(_ sender: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation 54 | ``` 55 | Called when the drag enters the view. Return the drag operation (or an empty array) to indicate how the drag is to proceed. Use this function to filter out if the 'wrong' types of files are dropped. (for example, if you only want to receive PDFs). 56 | 57 | ```swift 58 | func dropFilesView(_ sender: DSFDropFilesView, didDropFiles files: [URL]) -> Bool 59 | ``` 60 | Called with the files when the user actually drops the files. 61 | 62 | ## Customizations 63 | 64 | ### Properties 65 | 66 | These properties can all be configured via Interface Builder or programatically. 67 | 68 | #### Allowed drop counts 69 | 70 | * `allowsMultipleDrop` : Allow multiple files/folders to dropped 71 | 72 | #### Select files button (optional) 73 | 74 | * `selectFilesButtonLabel` : Embed a clickable button for accessibility with the specified label. If empty, doesn't display the button (default) 75 | * `selectFilesButtonIsLink` : If the select files button is display, show the button as a hyperlink (blue underlined text) instead of a button. 76 | 77 | #### Icon 78 | 79 | * `showIcon` : Should we display an icon 80 | * `icon` : The icon to display. A default icon is supplied 81 | 82 | #### Label 83 | 84 | * `label` : The text of the label. If empty, the label is hidden. 85 | * `lineWidth` : The line width for the dotted border 86 | * `cornerWidth` : The radius for the corners 87 | 88 | ## SwiftUI 89 | 90 | ### SwiftUI 91 | 92 | There is a basic SwiftUI View that embeds the `DSFDropFilesView` control. 93 | 94 | ```swift 95 | DSFDropFilesView.SwiftUI( 96 | isEnabled: true, 97 | allowsMultipleDrop: true, 98 | iconLabel: "Drop files here", 99 | selectFilesLabel: "Select Files…", 100 | selectFilesButtonIsLink: true, 101 | validateFiles: { urls in 102 | // Check the urls, and return the appropriate drop mode 103 | return .copy 104 | }, 105 | dropFiles: { urls in 106 | Swift.print("\(urls)") 107 | return true 108 | }, 109 | selectFiles: { 110 | Swift.print("User clicked select files") 111 | } 112 | ) 113 | ``` 114 | 115 | ## License 116 | 117 | ``` 118 | MIT License 119 | 120 | Copyright (c) 2024 Darren Ford 121 | 122 | Permission is hereby granted, free of charge, to any person obtaining a copy 123 | of this software and associated documentation files (the "Software"), to deal 124 | in the Software without restriction, including without limitation the rights 125 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 126 | copies of the Software, and to permit persons to whom the Software is 127 | furnished to do so, subject to the following conditions: 128 | 129 | The above copyright notice and this permission notice shall be included in all 130 | copies or substantial portions of the Software. 131 | 132 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 133 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 134 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 135 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 136 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 137 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 138 | SOFTWARE. 139 | ``` 140 | -------------------------------------------------------------------------------- /Sources/DSFDropFilesView/DSFDropFilesView+SwiftUI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFDropFilesView+SwiftUI.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT License 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | 27 | #if canImport(SwiftUI) && os(macOS) 28 | 29 | import SwiftUI 30 | 31 | @available(macOS 10.15, *) 32 | public extension DSFDropFilesView { 33 | /// A SwiftUI wrapper for DSFDropFilesView 34 | struct SwiftUI { 35 | public typealias NSViewType = DSFDropFilesView 36 | 37 | /// Display properties for the view. 38 | public struct DisplayProperties { 39 | /// Is the border animated when a valid drop is available? 40 | let isAnimated: Bool 41 | /// Draw a dotted border around the outside of the view 42 | let isBordered: Bool 43 | /// The corner radius for the border 44 | let cornerRadius: CGFloat 45 | /// The border line width 46 | let lineWidth: CGFloat 47 | 48 | public init( 49 | isBordered: Bool = true, 50 | isAnimated: Bool = true, 51 | lineWidth: CGFloat = 2, 52 | cornerRadius: CGFloat = 4) { 53 | self.isBordered = isBordered 54 | self.isAnimated = isAnimated 55 | self.cornerRadius = cornerRadius 56 | self.lineWidth = lineWidth 57 | } 58 | } 59 | 60 | // MARK: Types 61 | 62 | /// User has dragged files into the view and require drop validation. Return the drag operation supported for the dragged files, 63 | /// or [] to disallow the drag. 64 | public typealias ValidateFuncType = (([URL]) -> NSDragOperation) 65 | 66 | /// Files dropped callback block. Return true if the block has successfully handled the drop, false otherwise. 67 | public typealias DropFuncType = (([URL]) -> Bool) 68 | 69 | /// Select files callback type 70 | public typealias SelectFuncType = (() -> Void) 71 | 72 | // MARK: - Properties 73 | 74 | /// The display properties for the view 75 | public let displayProperties: DisplayProperties 76 | 77 | /// Enable or disable the control 78 | public let isEnabled: Bool 79 | 80 | /// Allow dropping multiple files 81 | public let allowsMultipleDrop: Bool 82 | 83 | /// The icon for the control. If nil, no icon is displayed 84 | public let icon: NSImage? 85 | 86 | /// The label to use for the icon. If empty, label is not displayed 87 | public let iconLabel: String 88 | 89 | /// The font to use for the icon label. 90 | public let iconLabelFont: NSFont 91 | 92 | /// The label for the 'select files' button. If empty, no select files button is displayed 93 | public let selectFilesLabel: String 94 | 95 | /// If true, the select files button is displayed as a hyperlink style. If false, displays as a regular button. 96 | public let selectFilesButtonIsLink: Bool 97 | 98 | // MARK: - Callbacks 99 | 100 | /// Called when the user drags some files into the view. 101 | public let validateFiles: ValidateFuncType? 102 | 103 | /// Called when the user drops files on the view 104 | public let dropFiles: DropFuncType? 105 | 106 | /// Called when the user clicks the 'select files' button 107 | public let selectFiles: SelectFuncType? 108 | 109 | /// Initializer 110 | /// 111 | /// Needs to be provided as (by default) a default initializer is marked as internal 112 | public init( 113 | displayProperties: DisplayProperties = DisplayProperties(), 114 | isEnabled: Bool = true, 115 | allowsMultipleDrop: Bool = true, 116 | icon: NSImage? = nil, 117 | iconLabel: String = "", 118 | iconLabelFont: NSFont = .systemFont(ofSize: 16, weight: .bold), 119 | selectFilesLabel: String = "", 120 | selectFilesButtonIsLink: Bool = true, 121 | validateFiles: ValidateFuncType? = nil, 122 | dropFiles: DropFuncType? = nil, 123 | selectFiles: SelectFuncType? = nil 124 | ) { 125 | self.displayProperties = displayProperties 126 | self.isEnabled = isEnabled 127 | self.allowsMultipleDrop = allowsMultipleDrop 128 | if let i = icon { 129 | self.icon = i 130 | } 131 | else { 132 | self.icon = DSFDropFilesView.StaticImage 133 | } 134 | self.iconLabel = iconLabel 135 | self.iconLabelFont = iconLabelFont 136 | self.selectFilesLabel = selectFilesLabel 137 | self.selectFilesButtonIsLink = selectFilesButtonIsLink 138 | self.validateFiles = validateFiles 139 | self.dropFiles = dropFiles 140 | self.selectFiles = selectFiles 141 | } 142 | } 143 | } 144 | 145 | // MARK: - Coordinator 146 | 147 | /// Convenience function for optionally updating a value 148 | /// - Parameters: 149 | /// - result: The property to optionally update if 'val' is not equal to its value 150 | /// - val: The value to check against 151 | @inlinable internal func UpdateIfNotEqual(_ result: inout T, _ val: T) where T: Equatable { 152 | if result != val { 153 | result = val 154 | } 155 | } 156 | 157 | @available(macOS 10.15, *) 158 | extension DSFDropFilesView.SwiftUI: NSViewRepresentable { 159 | public func makeCoordinator() -> Coordinator { 160 | Coordinator(self) 161 | } 162 | 163 | public func makeNSView(context: Context) -> DSFDropFilesView { 164 | let view = DSFDropFilesView(frame: .zero) 165 | view.translatesAutoresizingMaskIntoConstraints = false 166 | view.dropDelegate = context.coordinator 167 | return view 168 | } 169 | 170 | public func updateNSView(_ nsView: DSFDropFilesView, context _: Context) { 171 | if self.icon !== nsView.icon { 172 | nsView.icon = self.icon 173 | nsView.showIcon = (nsView.icon != nil) 174 | } 175 | 176 | UpdateIfNotEqual(&nsView.isEnabled, self.isEnabled) 177 | UpdateIfNotEqual(&nsView.label, self.iconLabel) 178 | UpdateIfNotEqual(&nsView.labelFont, self.iconLabelFont) 179 | UpdateIfNotEqual(&nsView.selectFilesButtonLabel, self.selectFilesLabel) 180 | UpdateIfNotEqual(&nsView.allowsMultipleDrop, self.allowsMultipleDrop) 181 | UpdateIfNotEqual(&nsView.selectFilesButtonIsLink, self.selectFilesButtonIsLink) 182 | 183 | UpdateIfNotEqual(&nsView.isBordered, self.displayProperties.isBordered) 184 | UpdateIfNotEqual(&nsView.isAnimated, self.displayProperties.isAnimated) 185 | UpdateIfNotEqual(&nsView.lineWidth, self.displayProperties.lineWidth) 186 | UpdateIfNotEqual(&nsView.cornerRadius, self.displayProperties.cornerRadius) 187 | } 188 | } 189 | 190 | // MARK: - Delegate handling 191 | 192 | @available(macOS 10.15, *) 193 | public extension DSFDropFilesView.SwiftUI { 194 | class Coordinator: NSObject, DSFDropFilesViewProtocol { 195 | let parent: DSFDropFilesView.SwiftUI 196 | init(_ dropView: DSFDropFilesView.SwiftUI) { 197 | self.parent = dropView 198 | } 199 | 200 | public func dropFilesView(_: DSFDropFilesView, validateFiles: [URL]) -> NSDragOperation { 201 | return self.parent.validateFiles?(validateFiles) ?? [] 202 | } 203 | 204 | public func dropFilesView(_: DSFDropFilesView, didDropFiles files: [URL]) -> Bool { 205 | return self.parent.dropFiles?(files) ?? false 206 | } 207 | 208 | public func dropFilesViewWantsSelectFiles(_: DSFDropFilesView) { 209 | self.parent.selectFiles?() 210 | } 211 | } 212 | } 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /Sources/DSFDropFilesView/private/DSFDropFilesView+image.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFDropFilesView+image.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT license 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all copies or substantial 14 | // portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | // 21 | 22 | #if os(macOS) 23 | 24 | import AppKit 25 | 26 | internal extension DSFDropFilesView { 27 | 28 | static let StaticImage: NSImage = { 29 | let im = NSImage.init(size: NSSize(width: 64, height: 64), flipped: false) { rect in 30 | let bezier = DSFDropFilesView.iconBezier() 31 | NSColor.black.set() 32 | bezier.fill() 33 | return true 34 | } 35 | im.isTemplate = true 36 | return im 37 | }() 38 | 39 | static func iconBezier() -> NSBezierPath { 40 | //// Text Drawing 41 | let textPath = NSBezierPath() 42 | textPath.move(to: NSPoint(x: 33.2, y: 32.68)) 43 | textPath.curve(to: NSPoint(x: 32.89, y: 33.4), controlPoint1: NSPoint(x: 33.2, y: 32.95), controlPoint2: NSPoint(x: 33.09, y: 33.19)) 44 | textPath.curve(to: NSPoint(x: 32.14, y: 33.71), controlPoint1: NSPoint(x: 32.68, y: 33.6), controlPoint2: NSPoint(x: 32.44, y: 33.71)) 45 | textPath.curve(to: NSPoint(x: 31.4, y: 33.4), controlPoint1: NSPoint(x: 31.85, y: 33.71), controlPoint2: NSPoint(x: 31.6, y: 33.6)) 46 | textPath.curve(to: NSPoint(x: 31.09, y: 32.68), controlPoint1: NSPoint(x: 31.19, y: 33.19), controlPoint2: NSPoint(x: 31.09, y: 32.95)) 47 | textPath.line(to: NSPoint(x: 31.09, y: 17.86)) 48 | textPath.line(to: NSPoint(x: 31.18, y: 11.76)) 49 | textPath.line(to: NSPoint(x: 25.61, y: 17.42)) 50 | textPath.line(to: NSPoint(x: 23.18, y: 19.91)) 51 | textPath.curve(to: NSPoint(x: 22.86, y: 20.11), controlPoint1: NSPoint(x: 23.1, y: 19.98), controlPoint2: NSPoint(x: 22.99, y: 20.05)) 52 | textPath.curve(to: NSPoint(x: 22.45, y: 20.2), controlPoint1: NSPoint(x: 22.72, y: 20.17), controlPoint2: NSPoint(x: 22.58, y: 20.2)) 53 | textPath.curve(to: NSPoint(x: 21.71, y: 19.91), controlPoint1: NSPoint(x: 22.15, y: 20.2), controlPoint2: NSPoint(x: 21.91, y: 20.1)) 54 | textPath.curve(to: NSPoint(x: 21.42, y: 19.17), controlPoint1: NSPoint(x: 21.52, y: 19.71), controlPoint2: NSPoint(x: 21.42, y: 19.47)) 55 | textPath.curve(to: NSPoint(x: 21.61, y: 18.56), controlPoint1: NSPoint(x: 21.42, y: 18.9), controlPoint2: NSPoint(x: 21.48, y: 18.7)) 56 | textPath.curve(to: NSPoint(x: 22.01, y: 18.12), controlPoint1: NSPoint(x: 21.74, y: 18.42), controlPoint2: NSPoint(x: 21.87, y: 18.28)) 57 | textPath.line(to: NSPoint(x: 31.35, y: 8.98)) 58 | textPath.curve(to: NSPoint(x: 31.73, y: 8.69), controlPoint1: NSPoint(x: 31.47, y: 8.84), controlPoint2: NSPoint(x: 31.6, y: 8.74)) 59 | textPath.curve(to: NSPoint(x: 32.14, y: 8.6), controlPoint1: NSPoint(x: 31.87, y: 8.63), controlPoint2: NSPoint(x: 32.01, y: 8.6)) 60 | textPath.curve(to: NSPoint(x: 32.54, y: 8.69), controlPoint1: NSPoint(x: 32.28, y: 8.6), controlPoint2: NSPoint(x: 32.41, y: 8.63)) 61 | textPath.curve(to: NSPoint(x: 32.93, y: 8.98), controlPoint1: NSPoint(x: 32.67, y: 8.74), controlPoint2: NSPoint(x: 32.8, y: 8.84)) 62 | textPath.line(to: NSPoint(x: 42.25, y: 18.12)) 63 | textPath.curve(to: NSPoint(x: 42.67, y: 18.56), controlPoint1: NSPoint(x: 42.41, y: 18.28), controlPoint2: NSPoint(x: 42.55, y: 18.42)) 64 | textPath.curve(to: NSPoint(x: 42.87, y: 19.17), controlPoint1: NSPoint(x: 42.8, y: 18.7), controlPoint2: NSPoint(x: 42.87, y: 18.9)) 65 | textPath.curve(to: NSPoint(x: 42.56, y: 19.91), controlPoint1: NSPoint(x: 42.87, y: 19.47), controlPoint2: NSPoint(x: 42.76, y: 19.71)) 66 | textPath.curve(to: NSPoint(x: 41.84, y: 20.2), controlPoint1: NSPoint(x: 42.35, y: 20.1), controlPoint2: NSPoint(x: 42.11, y: 20.2)) 67 | textPath.curve(to: NSPoint(x: 41.42, y: 20.11), controlPoint1: NSPoint(x: 41.7, y: 20.2), controlPoint2: NSPoint(x: 41.56, y: 20.17)) 68 | textPath.curve(to: NSPoint(x: 41.08, y: 19.91), controlPoint1: NSPoint(x: 41.27, y: 20.05), controlPoint2: NSPoint(x: 41.16, y: 19.98)) 69 | textPath.line(to: NSPoint(x: 38.65, y: 17.42)) 70 | textPath.line(to: NSPoint(x: 33.11, y: 11.76)) 71 | textPath.line(to: NSPoint(x: 33.2, y: 17.86)) 72 | textPath.line(to: NSPoint(x: 33.2, y: 32.68)) 73 | textPath.close() 74 | textPath.move(to: NSPoint(x: 16.35, y: 1.13)) 75 | textPath.line(to: NSPoint(x: 47.93, y: 1.13)) 76 | textPath.curve(to: NSPoint(x: 53.79, y: 3.13), controlPoint1: NSPoint(x: 50.51, y: 1.13), controlPoint2: NSPoint(x: 52.46, y: 1.8)) 77 | textPath.curve(to: NSPoint(x: 55.79, y: 9.04), controlPoint1: NSPoint(x: 55.12, y: 4.47), controlPoint2: NSPoint(x: 55.79, y: 6.44)) 78 | textPath.line(to: NSPoint(x: 55.79, y: 37.69)) 79 | textPath.curve(to: NSPoint(x: 55.64, y: 39.68), controlPoint1: NSPoint(x: 55.79, y: 38.47), controlPoint2: NSPoint(x: 55.74, y: 39.13)) 80 | textPath.curve(to: NSPoint(x: 55.1, y: 41.18), controlPoint1: NSPoint(x: 55.54, y: 40.23), controlPoint2: NSPoint(x: 55.36, y: 40.73)) 81 | textPath.curve(to: NSPoint(x: 53.97, y: 42.55), controlPoint1: NSPoint(x: 54.83, y: 41.63), controlPoint2: NSPoint(x: 54.46, y: 42.08)) 82 | textPath.line(to: NSPoint(x: 35.37, y: 61.24)) 83 | textPath.curve(to: NSPoint(x: 33.99, y: 62.34), controlPoint1: NSPoint(x: 34.9, y: 61.71), controlPoint2: NSPoint(x: 34.44, y: 62.08)) 84 | textPath.curve(to: NSPoint(x: 32.51, y: 62.9), controlPoint1: NSPoint(x: 33.54, y: 62.61), controlPoint2: NSPoint(x: 33.05, y: 62.79)) 85 | textPath.curve(to: NSPoint(x: 30.59, y: 63.06), controlPoint1: NSPoint(x: 31.97, y: 63.01), controlPoint2: NSPoint(x: 31.33, y: 63.06)) 86 | textPath.line(to: NSPoint(x: 16.35, y: 63.06)) 87 | textPath.curve(to: NSPoint(x: 10.49, y: 61.05), controlPoint1: NSPoint(x: 13.79, y: 63.06), controlPoint2: NSPoint(x: 11.84, y: 62.39)) 88 | textPath.curve(to: NSPoint(x: 8.47, y: 55.18), controlPoint1: NSPoint(x: 9.14, y: 59.72), controlPoint2: NSPoint(x: 8.47, y: 57.76)) 89 | textPath.line(to: NSPoint(x: 8.47, y: 9.04)) 90 | textPath.curve(to: NSPoint(x: 10.46, y: 3.12), controlPoint1: NSPoint(x: 8.47, y: 6.42), controlPoint2: NSPoint(x: 9.13, y: 4.45)) 91 | textPath.curve(to: NSPoint(x: 16.35, y: 1.13), controlPoint1: NSPoint(x: 11.79, y: 1.79), controlPoint2: NSPoint(x: 13.75, y: 1.13)) 92 | textPath.close() 93 | textPath.move(to: NSPoint(x: 16.44, y: 3.29)) 94 | textPath.curve(to: NSPoint(x: 12.1, y: 4.76), controlPoint1: NSPoint(x: 14.53, y: 3.29), controlPoint2: NSPoint(x: 13.08, y: 3.78)) 95 | textPath.curve(to: NSPoint(x: 10.64, y: 9.1), controlPoint1: NSPoint(x: 11.13, y: 5.74), controlPoint2: NSPoint(x: 10.64, y: 7.18)) 96 | textPath.line(to: NSPoint(x: 10.64, y: 55.09)) 97 | textPath.curve(to: NSPoint(x: 12.1, y: 59.38), controlPoint1: NSPoint(x: 10.64, y: 56.95), controlPoint2: NSPoint(x: 11.13, y: 58.38)) 98 | textPath.curve(to: NSPoint(x: 16.44, y: 60.89), controlPoint1: NSPoint(x: 13.08, y: 60.39), controlPoint2: NSPoint(x: 14.53, y: 60.89)) 99 | textPath.line(to: NSPoint(x: 31.09, y: 60.89)) 100 | textPath.line(to: NSPoint(x: 31.09, y: 42.44)) 101 | textPath.curve(to: NSPoint(x: 32.14, y: 39.27), controlPoint1: NSPoint(x: 31.09, y: 40.99), controlPoint2: NSPoint(x: 31.44, y: 39.94)) 102 | textPath.curve(to: NSPoint(x: 35.22, y: 38.28), controlPoint1: NSPoint(x: 32.85, y: 38.61), controlPoint2: NSPoint(x: 33.87, y: 38.28)) 103 | textPath.line(to: NSPoint(x: 53.62, y: 38.28)) 104 | textPath.line(to: NSPoint(x: 53.62, y: 9.1)) 105 | textPath.curve(to: NSPoint(x: 52.15, y: 4.76), controlPoint1: NSPoint(x: 53.62, y: 7.18), controlPoint2: NSPoint(x: 53.13, y: 5.74)) 106 | textPath.curve(to: NSPoint(x: 47.82, y: 3.29), controlPoint1: NSPoint(x: 51.18, y: 3.78), controlPoint2: NSPoint(x: 49.73, y: 3.29)) 107 | textPath.line(to: NSPoint(x: 16.44, y: 3.29)) 108 | textPath.close() 109 | textPath.move(to: NSPoint(x: 35.39, y: 40.38)) 110 | textPath.curve(to: NSPoint(x: 33.68, y: 40.88), controlPoint1: NSPoint(x: 34.59, y: 40.38), controlPoint2: NSPoint(x: 34.02, y: 40.55)) 111 | textPath.curve(to: NSPoint(x: 33.17, y: 42.61), controlPoint1: NSPoint(x: 33.34, y: 41.21), controlPoint2: NSPoint(x: 33.17, y: 41.79)) 112 | textPath.line(to: NSPoint(x: 33.17, y: 60.39)) 113 | textPath.line(to: NSPoint(x: 53.12, y: 40.38)) 114 | textPath.line(to: NSPoint(x: 35.39, y: 40.38)) 115 | textPath.close() 116 | return textPath 117 | } 118 | } 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /Sources/DSFDropFilesView/DSFDropFilesView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DSFDropFilesView.swift 3 | // 4 | // Copyright © 2024 Darren Ford. All rights reserved. 5 | // 6 | // MIT license 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 9 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 11 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all copies or substantial 14 | // portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 17 | // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 18 | // OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | // 21 | 22 | #if os(macOS) 23 | 24 | import AppKit 25 | import DSFAppearanceManager 26 | 27 | /// A view that support dropping files 28 | @IBDesignable 29 | @objc public class DSFDropFilesView: NSView { 30 | // MARK: - Public accessors 31 | 32 | /// Enable or disable the control 33 | @IBInspectable public dynamic var isEnabled: Bool = true { 34 | didSet { 35 | self.selectButton.isEnabled = self.isEnabled 36 | 37 | // Force the cursor to update to reflect the new state 38 | self.window?.invalidateCursorRects(for: self.selectButton) 39 | } 40 | } 41 | 42 | /// The drop delegate. 43 | @IBOutlet public var dropDelegate: DSFDropFilesViewProtocol? { 44 | didSet { 45 | self.syncTitle() 46 | } 47 | } 48 | 49 | /// Do we support dropping multiple files at once? 50 | @IBInspectable public var allowsMultipleDrop: Bool = true { 51 | didSet { 52 | self.syncTitle() 53 | } 54 | } 55 | 56 | /// The label for the 'select files...' button. If empty, the button is not displayed. 57 | @IBInspectable public var selectFilesButtonLabel: String = "" { 58 | didSet { 59 | self.syncTitle() 60 | } 61 | } 62 | 63 | /// If the select files button is shown, do we display it as a hyperlink or a regular button? 64 | @IBInspectable public var selectFilesButtonIsLink: Bool = true { 65 | didSet { 66 | self.syncTitle() 67 | } 68 | } 69 | 70 | /// Should the drop target display an icon. 71 | @IBInspectable public var showIcon: Bool = true { 72 | didSet { 73 | self.syncTitle() 74 | } 75 | } 76 | 77 | /// The image to be displayed. 78 | @IBInspectable public var icon: NSImage? = DSFDropFilesView.StaticImage { 79 | didSet { 80 | self.imageView.image = self.icon 81 | self.syncImage() 82 | } 83 | } 84 | 85 | /// Display a informational label. If empty, the label is hidden. 86 | @IBInspectable public var label: String = "" { 87 | didSet { 88 | self.syncTitle() 89 | } 90 | } 91 | 92 | /// The font to use for the label 93 | @IBInspectable public var labelFont: NSFont? { 94 | get { self.imageLabel.font } 95 | set { self.imageLabel.font = newValue } 96 | } 97 | 98 | /// Display a border around the drop target 99 | @IBInspectable public var isBordered: Bool = true { 100 | didSet { 101 | self.outerBoundary.isHidden = !self.isBordered 102 | self.syncTitle() 103 | } 104 | } 105 | 106 | /// Width of the border line around the control 107 | @IBInspectable public var lineWidth: CGFloat = 2 { 108 | didSet { 109 | self.outerBoundary.lineWidth = self.lineWidth 110 | } 111 | } 112 | 113 | /// The corner radius for the border 114 | @IBInspectable public var cornerRadius: CGFloat = 4 115 | var borderInset: CGFloat { 116 | return self.cornerRadius / 2.0 117 | } 118 | 119 | /// Should we animate the border when we can drop? 120 | @IBInspectable public var isAnimated: Bool = true 121 | 122 | // MARK: - Initialization 123 | 124 | /// Create a drop view 125 | override public init(frame frameRect: NSRect) { 126 | super.init(frame: frameRect) 127 | self.configureControl() 128 | } 129 | 130 | /// Create a drop view 131 | required public init?(coder: NSCoder) { 132 | super.init(coder: coder) 133 | self.configureControl() 134 | } 135 | 136 | deinit { 137 | DSFAppearanceCache.shared.deregister(self) 138 | } 139 | 140 | // MARK: - Private definitions 141 | 142 | private let outerBoundary = CAShapeLayer() 143 | 144 | private lazy var containerStack: NSStackView = { 145 | let s = NSStackView() 146 | s.translatesAutoresizingMaskIntoConstraints = false 147 | s.wantsLayer = true 148 | s.edgeInsets = NSEdgeInsets(top: 4, left: 4, bottom: 4, right: 4) 149 | 150 | s.orientation = .vertical 151 | s.spacing = 4 152 | s.alignment = .centerX 153 | s.setContentHuggingPriority(NSLayoutConstraint.Priority(50), for: .horizontal) 154 | s.setContentHuggingPriority(NSLayoutConstraint.Priority(50), for: .vertical) 155 | 156 | s.setHuggingPriority(NSLayoutConstraint.Priority(50), for: .horizontal) 157 | s.setHuggingPriority(NSLayoutConstraint.Priority(50), for: .vertical) 158 | 159 | s.setContentCompressionResistancePriority(.required, for: .horizontal) 160 | s.setContentCompressionResistancePriority(.required, for: .vertical) 161 | 162 | s.addArrangedSubview(self.imageView) 163 | s.addArrangedSubview(self.imageLabel) 164 | s.addArrangedSubview(self.buttonSpacerView) 165 | s.addArrangedSubview(self.selectButton) 166 | 167 | return s 168 | }() 169 | 170 | private lazy var buttonSpacerView: NSView = { 171 | let sep = NSView(frame: .zero) 172 | sep.translatesAutoresizingMaskIntoConstraints = false 173 | sep.addConstraint(NSLayoutConstraint(item: sep, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 4)) 174 | sep.addConstraint(NSLayoutConstraint(item: sep, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 8)) 175 | return sep 176 | }() 177 | 178 | private lazy var imageView: NSImageView = { 179 | let i = NSImageView() 180 | i.translatesAutoresizingMaskIntoConstraints = false 181 | i.wantsLayer = true 182 | i.imageScaling = .scaleProportionallyDown 183 | i.image = self.icon 184 | 185 | i.isEditable = false 186 | i.unregisterDraggedTypes() 187 | 188 | i.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) 189 | i.setContentCompressionResistancePriority(.defaultLow, for: .vertical) 190 | 191 | return i 192 | }() 193 | 194 | private lazy var imageLabel: NSTextField = { 195 | let t = NSTextField() 196 | t.translatesAutoresizingMaskIntoConstraints = false 197 | t.wantsLayer = true 198 | t.drawsBackground = false 199 | t.isBezeled = false 200 | t.font = NSFont.systemFont(ofSize: 16, weight: .bold) 201 | t.textColor = NSColor.tertiaryLabelColor 202 | t.stringValue = self.label 203 | t.alignment = .center 204 | t.isEditable = false 205 | t.isSelectable = false 206 | t.lineBreakMode = .byWordWrapping 207 | 208 | t.setContentHuggingPriority(.defaultLow, for: .horizontal) 209 | t.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) 210 | 211 | return t 212 | }() 213 | 214 | private lazy var selectButton: DSFHandButton = { 215 | let button = DSFHandButton() 216 | button.wantsLayer = true 217 | button.translatesAutoresizingMaskIntoConstraints = false 218 | button.target = self 219 | button.action = #selector(self.selectFiles(_:)) 220 | button.bezelStyle = .rounded 221 | button.isBordered = false 222 | button.controlSize = .regular 223 | button.isEnabled = true 224 | return button 225 | }() 226 | } 227 | 228 | // MARK: - Action callbacks 229 | 230 | extension DSFDropFilesView { 231 | @objc func selectFiles(_ sender: Any) { 232 | if let userSelectedFiles = self.dropDelegate?.dropFilesViewWantsSelectFiles { 233 | userSelectedFiles(self) 234 | } 235 | } 236 | } 237 | 238 | // MARK: - UI Layout 239 | 240 | extension DSFDropFilesView { 241 | override public func prepareForInterfaceBuilder() { 242 | self.configureControl() 243 | } 244 | 245 | override public func layout() { 246 | super.layout() 247 | self.outerBoundary.path = CGPath( 248 | roundedRect: self.bounds.insetBy(dx: self.borderInset, dy: self.borderInset), 249 | cornerWidth: self.cornerRadius, 250 | cornerHeight: self.cornerRadius, transform: nil 251 | ) 252 | } 253 | 254 | private func syncImage() { 255 | let template = self.icon?.isTemplate ?? false 256 | self.imageView.alphaValue = template ? 0.4 : 1.0 257 | } 258 | 259 | private func syncTitle() { 260 | let buttonTitle = self.selectFilesButtonLabel 261 | if self.selectFilesButtonIsLink { 262 | let att = NSAttributedString( 263 | string: buttonTitle, 264 | attributes: [ 265 | NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue, 266 | NSAttributedString.Key.foregroundColor: NSColor.linkColor, 267 | ] 268 | ) 269 | self.selectButton.isBordered = false 270 | self.selectButton.attributedTitle = att 271 | self.selectButton.wantsHandCursor = true 272 | } 273 | else { 274 | self.selectButton.isBordered = true 275 | self.selectButton.title = buttonTitle 276 | self.selectButton.wantsHandCursor = false 277 | } 278 | 279 | self.imageView.image = self.icon 280 | self.syncImage() 281 | self.imageView.isHidden = !self.showIcon 282 | 283 | self.imageLabel.stringValue = self.label 284 | 285 | // Hide the label if the label text is empty 286 | self.imageLabel.isHidden = self.label.isEmpty 287 | 288 | // Hide the select files button if no text has been set 289 | let hideSelectFilesButton = self.selectFilesButtonLabel.isEmpty 290 | self.buttonSpacerView.isHidden = hideSelectFilesButton 291 | self.selectButton.isHidden = hideSelectFilesButton 292 | } 293 | 294 | private func configureControl() { 295 | self.wantsLayer = true 296 | 297 | if #available(OSX 10.13, *) { 298 | self.registerForDraggedTypes([.fileURL]) 299 | } 300 | else { 301 | let furl = NSPasteboard.PasteboardType(kUTTypeFileURL as String) 302 | self.registerForDraggedTypes([furl]) 303 | } 304 | 305 | self.outerBoundary.fillColor = self.backgroundColor() 306 | self.outerBoundary.strokeColor = self.backgroundStrokeColor() 307 | self.outerBoundary.lineWidth = self.lineWidth 308 | 309 | self.outerBoundary.lineDashPattern = [4, 4] 310 | self.outerBoundary.lineDashPhase = 0 311 | 312 | self.layer!.addSublayer(self.outerBoundary) 313 | 314 | let stack = self.containerStack 315 | self.addSubview(stack) 316 | 317 | self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-(8)-[but]-(8)-|", options: .alignAllCenterY, metrics: nil, views: ["but": stack])) 318 | self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-(>=8)-[but]-(>=8)-|", options: .alignAllCenterX, metrics: nil, views: ["but": stack])) 319 | 320 | self.addConstraint(NSLayoutConstraint(item: stack, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0)) 321 | self.addConstraint(NSLayoutConstraint(item: stack, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0)) 322 | 323 | // Register for appearance updates 324 | DSFAppearanceCache.shared.register(self) 325 | 326 | self.syncTitle() 327 | } 328 | } 329 | 330 | extension DSFDropFilesView: DSFAppearanceCacheNotifiable { 331 | public func appearanceDidChange() { 332 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { 333 | self.outerBoundary.strokeColor = self.backgroundStrokeColor() 334 | } 335 | } 336 | } 337 | 338 | // MARK: - Drag and drop 339 | 340 | public extension DSFDropFilesView { 341 | private func filesOnPasteboard(for sender: NSDraggingInfo) -> [URL]? { 342 | let pb = sender.draggingPasteboard 343 | guard let objs = pb.readObjects(forClasses: [NSURL.self], options: nil) as? [NSURL] else { 344 | return nil 345 | } 346 | 347 | let urls = objs.compactMap { $0 as URL } 348 | return urls.count == 0 ? nil : urls 349 | } 350 | 351 | // Called when the user has dropped a file 352 | 353 | override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { 354 | if self.isEnabled, 355 | let delegate = self.dropDelegate, 356 | let files = self.filesOnPasteboard(for: sender) 357 | { 358 | return delegate.dropFilesView(self, validateFiles: files) != [] 359 | } 360 | return false 361 | } 362 | 363 | // When the user has dragged INTO the view 364 | 365 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 366 | var dragOperation: NSDragOperation = [] 367 | 368 | // If there are no files on the pasteboard then do nothing 369 | guard let files = self.filesOnPasteboard(for: sender) else { 370 | return [] 371 | } 372 | 373 | // If we are in single select and there are multiple files then don't allow drop 374 | if !self.allowsMultipleDrop, files.count != 1 { 375 | return [] 376 | } 377 | 378 | if let delegate = self.dropDelegate { 379 | dragOperation = delegate.dropFilesView(self, validateFiles: files) 380 | } 381 | 382 | if dragOperation == [] { 383 | self.outerBoundary.strokeColor = self.backgroundStrokeColor() 384 | self.outerBoundary.fillColor = self.backgroundColor() 385 | } 386 | else { 387 | self.outerBoundary.strokeColor = self.backgroundActiveStrokeColor() 388 | self.outerBoundary.fillColor = self.backgroundActiveColor() 389 | 390 | self.startAnimation() 391 | } 392 | 393 | return dragOperation 394 | } 395 | 396 | // When the user has dragged OUT of the view or drag is cancelled 397 | 398 | override func draggingExited(_: NSDraggingInfo?) { 399 | self.outerBoundary.strokeColor = self.backgroundStrokeColor() 400 | self.outerBoundary.fillColor = self.backgroundColor() 401 | self.stopAnimation() 402 | } 403 | 404 | // Perform the drag operation 405 | 406 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 407 | self.stopAnimation() 408 | 409 | self.outerBoundary.strokeColor = self.backgroundStrokeColor() 410 | self.outerBoundary.fillColor = self.backgroundColor() 411 | 412 | if let delegate = self.dropDelegate, 413 | let files = self.filesOnPasteboard(for: sender) 414 | { 415 | return delegate.dropFilesView(self, didDropFiles: files) 416 | } 417 | 418 | return false 419 | } 420 | } 421 | 422 | // MARK: - Colors 423 | 424 | extension DSFDropFilesView { 425 | func backgroundColor() -> CGColor { 426 | return NSColor(calibratedWhite: 0, alpha: 0.05).cgColor 427 | } 428 | 429 | func backgroundStrokeColor() -> CGColor { 430 | var color: CGColor! 431 | self.usingEffectiveAppearance { 432 | color = NSColor.tertiaryLabelColor.cgColor 433 | } 434 | return color 435 | } 436 | 437 | func backgroundActiveColor() -> CGColor { 438 | var color: CGColor! 439 | self.usingEffectiveAppearance { 440 | color = NSColor.quaternaryLabelColor.cgColor 441 | } 442 | return color 443 | } 444 | 445 | func backgroundActiveStrokeColor() -> CGColor { 446 | var color: CGColor! 447 | self.usingEffectiveAppearance { 448 | color = NSColor.secondaryLabelColor.cgColor 449 | } 450 | return color 451 | } 452 | } 453 | 454 | // MARK: - Animation 455 | 456 | extension DSFDropFilesView { 457 | func startAnimation() { 458 | if !self.isAnimated || DSFAppearanceManager.ReduceMotion { 459 | return 460 | } 461 | 462 | let lineDashPhaseAnimation = CABasicAnimation(keyPath: "lineDashPhase") 463 | lineDashPhaseAnimation.byValue = 8.0 464 | lineDashPhaseAnimation.duration = 0.75 465 | lineDashPhaseAnimation.timingFunction = CAMediaTimingFunction(name: .linear) 466 | lineDashPhaseAnimation.repeatCount = .greatestFiniteMagnitude 467 | 468 | self.outerBoundary.add(lineDashPhaseAnimation, forKey: "lineDashPhaseAnimation") 469 | } 470 | 471 | func stopAnimation() { 472 | self.outerBoundary.removeAllAnimations() 473 | } 474 | } 475 | 476 | #endif 477 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 23D29C41256C890300E5CBC0 /* All */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 23D29C44256C890300E5CBC0 /* Build configuration list for PBXAggregateTarget "All" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | 230A4B8E256CA760009B4689 /* PBXTargetDependency */, 17 | 23D29C49256C890800E5CBC0 /* PBXTargetDependency */, 18 | 23D29C4B256C890800E5CBC0 /* PBXTargetDependency */, 19 | ); 20 | name = All; 21 | productName = All; 22 | }; 23 | /* End PBXAggregateTarget section */ 24 | 25 | /* Begin PBXBuildFile section */ 26 | 230A4B79256CA750009B4689 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 230A4B78256CA750009B4689 /* AppDelegate.m */; }; 27 | 230A4B7C256CA750009B4689 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 230A4B7B256CA750009B4689 /* ViewController.m */; }; 28 | 230A4B7E256CA751009B4689 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 230A4B7D256CA751009B4689 /* Assets.xcassets */; }; 29 | 230A4B81256CA751009B4689 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 230A4B7F256CA751009B4689 /* Main.storyboard */; }; 30 | 230A4B84256CA751009B4689 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 230A4B83256CA751009B4689 /* main.m */; }; 31 | 235981A8256B333F000D2479 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 235981A7256B333F000D2479 /* AppDelegate.swift */; }; 32 | 235981AA256B333F000D2479 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 235981A9256B333F000D2479 /* ContentView.swift */; }; 33 | 235981AC256B3340000D2479 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 235981AB256B3340000D2479 /* Assets.xcassets */; }; 34 | 235981AF256B3340000D2479 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 235981AE256B3340000D2479 /* Preview Assets.xcassets */; }; 35 | 235981B2256B3340000D2479 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 235981B0256B3340000D2479 /* Main.storyboard */; }; 36 | 23D18D0A286FA4B500D5A0D2 /* DSFDropFilesView in Frameworks */ = {isa = PBXBuildFile; productRef = 23D18D09286FA4B500D5A0D2 /* DSFDropFilesView */; }; 37 | 23D18D0C286FA4C000D5A0D2 /* DSFDropFilesView in Frameworks */ = {isa = PBXBuildFile; productRef = 23D18D0B286FA4C000D5A0D2 /* DSFDropFilesView */; }; 38 | 23D18D0E286FA4DF00D5A0D2 /* DSFDropFilesView in Frameworks */ = {isa = PBXBuildFile; productRef = 23D18D0D286FA4DF00D5A0D2 /* DSFDropFilesView */; }; 39 | 23E5973C2547D4460080BE0E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E5973B2547D4460080BE0E /* AppDelegate.swift */; }; 40 | 23E5973E2547D4460080BE0E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23E5973D2547D4460080BE0E /* ViewController.swift */; }; 41 | 23E597402547D4470080BE0E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23E5973F2547D4470080BE0E /* Assets.xcassets */; }; 42 | 23E597432547D4470080BE0E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 23E597412547D4470080BE0E /* Main.storyboard */; }; 43 | /* End PBXBuildFile section */ 44 | 45 | /* Begin PBXContainerItemProxy section */ 46 | 230A4B8D256CA760009B4689 /* PBXContainerItemProxy */ = { 47 | isa = PBXContainerItemProxy; 48 | containerPortal = 23E597302547D4460080BE0E /* Project object */; 49 | proxyType = 1; 50 | remoteGlobalIDString = 230A4B74256CA750009B4689; 51 | remoteInfo = "DSFDropFilesView ObjC Demo"; 52 | }; 53 | 23D29C48256C890800E5CBC0 /* PBXContainerItemProxy */ = { 54 | isa = PBXContainerItemProxy; 55 | containerPortal = 23E597302547D4460080BE0E /* Project object */; 56 | proxyType = 1; 57 | remoteGlobalIDString = 23E597372547D4460080BE0E; 58 | remoteInfo = "DSFDropFilesView Demo"; 59 | }; 60 | 23D29C4A256C890800E5CBC0 /* PBXContainerItemProxy */ = { 61 | isa = PBXContainerItemProxy; 62 | containerPortal = 23E597302547D4460080BE0E /* Project object */; 63 | proxyType = 1; 64 | remoteGlobalIDString = 235981A4256B333F000D2479; 65 | remoteInfo = "DSFDropFilesView SwiftUI Demo"; 66 | }; 67 | /* End PBXContainerItemProxy section */ 68 | 69 | /* Begin PBXFileReference section */ 70 | 230A4B75256CA750009B4689 /* DSFDropFilesView ObjC Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFDropFilesView ObjC Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 230A4B77256CA750009B4689 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 72 | 230A4B78256CA750009B4689 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 73 | 230A4B7A256CA750009B4689 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 74 | 230A4B7B256CA750009B4689 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 75 | 230A4B7D256CA751009B4689 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 76 | 230A4B80256CA751009B4689 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 77 | 230A4B82256CA751009B4689 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78 | 230A4B83256CA751009B4689 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 79 | 230A4B85256CA751009B4689 /* DSFDropFilesView_ObjC_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFDropFilesView_ObjC_Demo.entitlements; sourceTree = ""; }; 80 | 235981A5256B333F000D2479 /* DSFDropFilesView SwiftUI Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFDropFilesView SwiftUI Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 235981A7256B333F000D2479 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 82 | 235981A9256B333F000D2479 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 83 | 235981AB256B3340000D2479 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 84 | 235981AE256B3340000D2479 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 85 | 235981B1256B3340000D2479 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 86 | 235981B3256B3340000D2479 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 87 | 235981B4256B3340000D2479 /* DSFDropFilesView_SwiftUI_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFDropFilesView_SwiftUI_Demo.entitlements; sourceTree = ""; }; 88 | 23D29C4F256C8DDC00E5CBC0 /* DSFDropFilesView */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DSFDropFilesView; path = ../..; sourceTree = ""; }; 89 | 23E597382547D4460080BE0E /* DSFDropFilesView Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DSFDropFilesView Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | 23E5973B2547D4460080BE0E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 91 | 23E5973D2547D4460080BE0E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 92 | 23E5973F2547D4470080BE0E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 93 | 23E597422547D4470080BE0E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 94 | 23E597442547D4470080BE0E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 95 | 23E597452547D4470080BE0E /* DSFDropFilesView_Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DSFDropFilesView_Demo.entitlements; sourceTree = ""; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | 230A4B72256CA750009B4689 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | 23D18D0E286FA4DF00D5A0D2 /* DSFDropFilesView in Frameworks */, 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | 235981A2256B333F000D2479 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | 23D18D0C286FA4C000D5A0D2 /* DSFDropFilesView in Frameworks */, 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | 23E597352547D4460080BE0E /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | 23D18D0A286FA4B500D5A0D2 /* DSFDropFilesView in Frameworks */, 120 | ); 121 | runOnlyForDeploymentPostprocessing = 0; 122 | }; 123 | /* End PBXFrameworksBuildPhase section */ 124 | 125 | /* Begin PBXGroup section */ 126 | 230A4B76256CA750009B4689 /* DSFDropFilesView ObjC Demo */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 230A4B77256CA750009B4689 /* AppDelegate.h */, 130 | 230A4B78256CA750009B4689 /* AppDelegate.m */, 131 | 230A4B7A256CA750009B4689 /* ViewController.h */, 132 | 230A4B7B256CA750009B4689 /* ViewController.m */, 133 | 230A4B7D256CA751009B4689 /* Assets.xcassets */, 134 | 230A4B7F256CA751009B4689 /* Main.storyboard */, 135 | 230A4B82256CA751009B4689 /* Info.plist */, 136 | 230A4B83256CA751009B4689 /* main.m */, 137 | 230A4B85256CA751009B4689 /* DSFDropFilesView_ObjC_Demo.entitlements */, 138 | ); 139 | path = "DSFDropFilesView ObjC Demo"; 140 | sourceTree = ""; 141 | }; 142 | 235981A6256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 235981A7256B333F000D2479 /* AppDelegate.swift */, 146 | 235981A9256B333F000D2479 /* ContentView.swift */, 147 | 235981AB256B3340000D2479 /* Assets.xcassets */, 148 | 235981B0256B3340000D2479 /* Main.storyboard */, 149 | 235981B3256B3340000D2479 /* Info.plist */, 150 | 235981B4256B3340000D2479 /* DSFDropFilesView_SwiftUI_Demo.entitlements */, 151 | 235981AD256B3340000D2479 /* Preview Content */, 152 | ); 153 | path = "DSFDropFilesView SwiftUI Demo"; 154 | sourceTree = ""; 155 | }; 156 | 235981AD256B3340000D2479 /* Preview Content */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 235981AE256B3340000D2479 /* Preview Assets.xcassets */, 160 | ); 161 | path = "Preview Content"; 162 | sourceTree = ""; 163 | }; 164 | 23E5972F2547D4460080BE0E = { 165 | isa = PBXGroup; 166 | children = ( 167 | 23D29C4F256C8DDC00E5CBC0 /* DSFDropFilesView */, 168 | 23E5973A2547D4460080BE0E /* DSFDropFilesView Demo */, 169 | 235981A6256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */, 170 | 230A4B76256CA750009B4689 /* DSFDropFilesView ObjC Demo */, 171 | 23E597392547D4460080BE0E /* Products */, 172 | 23E597522547D56D0080BE0E /* Frameworks */, 173 | ); 174 | sourceTree = ""; 175 | }; 176 | 23E597392547D4460080BE0E /* Products */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 23E597382547D4460080BE0E /* DSFDropFilesView Demo.app */, 180 | 235981A5256B333F000D2479 /* DSFDropFilesView SwiftUI Demo.app */, 181 | 230A4B75256CA750009B4689 /* DSFDropFilesView ObjC Demo.app */, 182 | ); 183 | name = Products; 184 | sourceTree = ""; 185 | }; 186 | 23E5973A2547D4460080BE0E /* DSFDropFilesView Demo */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 23E5973B2547D4460080BE0E /* AppDelegate.swift */, 190 | 23E5973D2547D4460080BE0E /* ViewController.swift */, 191 | 23E5973F2547D4470080BE0E /* Assets.xcassets */, 192 | 23E597412547D4470080BE0E /* Main.storyboard */, 193 | 23E597442547D4470080BE0E /* Info.plist */, 194 | 23E597452547D4470080BE0E /* DSFDropFilesView_Demo.entitlements */, 195 | ); 196 | path = "DSFDropFilesView Demo"; 197 | sourceTree = ""; 198 | }; 199 | 23E597522547D56D0080BE0E /* Frameworks */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | ); 203 | name = Frameworks; 204 | sourceTree = ""; 205 | }; 206 | /* End PBXGroup section */ 207 | 208 | /* Begin PBXNativeTarget section */ 209 | 230A4B74256CA750009B4689 /* DSFDropFilesView ObjC Demo */ = { 210 | isa = PBXNativeTarget; 211 | buildConfigurationList = 230A4B88256CA751009B4689 /* Build configuration list for PBXNativeTarget "DSFDropFilesView ObjC Demo" */; 212 | buildPhases = ( 213 | 230A4B71256CA750009B4689 /* Sources */, 214 | 230A4B72256CA750009B4689 /* Frameworks */, 215 | 230A4B73256CA750009B4689 /* Resources */, 216 | ); 217 | buildRules = ( 218 | ); 219 | dependencies = ( 220 | 230A4B94256CA76B009B4689 /* PBXTargetDependency */, 221 | ); 222 | name = "DSFDropFilesView ObjC Demo"; 223 | packageProductDependencies = ( 224 | 23D18D0D286FA4DF00D5A0D2 /* DSFDropFilesView */, 225 | ); 226 | productName = "DSFDropFilesView ObjC Demo"; 227 | productReference = 230A4B75256CA750009B4689 /* DSFDropFilesView ObjC Demo.app */; 228 | productType = "com.apple.product-type.application"; 229 | }; 230 | 235981A4256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */ = { 231 | isa = PBXNativeTarget; 232 | buildConfigurationList = 235981B5256B3340000D2479 /* Build configuration list for PBXNativeTarget "DSFDropFilesView SwiftUI Demo" */; 233 | buildPhases = ( 234 | 235981A1256B333F000D2479 /* Sources */, 235 | 235981A2256B333F000D2479 /* Frameworks */, 236 | 235981A3256B333F000D2479 /* Resources */, 237 | ); 238 | buildRules = ( 239 | ); 240 | dependencies = ( 241 | 23D29C57256C8DF300E5CBC0 /* PBXTargetDependency */, 242 | ); 243 | name = "DSFDropFilesView SwiftUI Demo"; 244 | packageProductDependencies = ( 245 | 23D18D0B286FA4C000D5A0D2 /* DSFDropFilesView */, 246 | ); 247 | productName = "DSFDropFilesView SwiftUI Demo"; 248 | productReference = 235981A5256B333F000D2479 /* DSFDropFilesView SwiftUI Demo.app */; 249 | productType = "com.apple.product-type.application"; 250 | }; 251 | 23E597372547D4460080BE0E /* DSFDropFilesView Demo */ = { 252 | isa = PBXNativeTarget; 253 | buildConfigurationList = 23E597482547D4470080BE0E /* Build configuration list for PBXNativeTarget "DSFDropFilesView Demo" */; 254 | buildPhases = ( 255 | 23E597342547D4460080BE0E /* Sources */, 256 | 23E597352547D4460080BE0E /* Frameworks */, 257 | 23E597362547D4460080BE0E /* Resources */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | ); 263 | name = "DSFDropFilesView Demo"; 264 | packageProductDependencies = ( 265 | 23D18D09286FA4B500D5A0D2 /* DSFDropFilesView */, 266 | ); 267 | productName = "DSFDropFilesView Demo"; 268 | productReference = 23E597382547D4460080BE0E /* DSFDropFilesView Demo.app */; 269 | productType = "com.apple.product-type.application"; 270 | }; 271 | /* End PBXNativeTarget section */ 272 | 273 | /* Begin PBXProject section */ 274 | 23E597302547D4460080BE0E /* Project object */ = { 275 | isa = PBXProject; 276 | attributes = { 277 | LastSwiftUpdateCheck = 1220; 278 | LastUpgradeCheck = 1220; 279 | TargetAttributes = { 280 | 230A4B74256CA750009B4689 = { 281 | CreatedOnToolsVersion = 12.2; 282 | }; 283 | 235981A4256B333F000D2479 = { 284 | CreatedOnToolsVersion = 12.2; 285 | }; 286 | 23D29C41256C890300E5CBC0 = { 287 | CreatedOnToolsVersion = 12.2; 288 | }; 289 | 23E597372547D4460080BE0E = { 290 | CreatedOnToolsVersion = 12.2; 291 | }; 292 | }; 293 | }; 294 | buildConfigurationList = 23E597332547D4460080BE0E /* Build configuration list for PBXProject "DSFDropFilesView Demo" */; 295 | compatibilityVersion = "Xcode 9.3"; 296 | developmentRegion = en; 297 | hasScannedForEncodings = 0; 298 | knownRegions = ( 299 | en, 300 | Base, 301 | ); 302 | mainGroup = 23E5972F2547D4460080BE0E; 303 | productRefGroup = 23E597392547D4460080BE0E /* Products */; 304 | projectDirPath = ""; 305 | projectRoot = ""; 306 | targets = ( 307 | 23E597372547D4460080BE0E /* DSFDropFilesView Demo */, 308 | 235981A4256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */, 309 | 23D29C41256C890300E5CBC0 /* All */, 310 | 230A4B74256CA750009B4689 /* DSFDropFilesView ObjC Demo */, 311 | ); 312 | }; 313 | /* End PBXProject section */ 314 | 315 | /* Begin PBXResourcesBuildPhase section */ 316 | 230A4B73256CA750009B4689 /* Resources */ = { 317 | isa = PBXResourcesBuildPhase; 318 | buildActionMask = 2147483647; 319 | files = ( 320 | 230A4B7E256CA751009B4689 /* Assets.xcassets in Resources */, 321 | 230A4B81256CA751009B4689 /* Main.storyboard in Resources */, 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | 235981A3256B333F000D2479 /* Resources */ = { 326 | isa = PBXResourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | 235981B2256B3340000D2479 /* Main.storyboard in Resources */, 330 | 235981AF256B3340000D2479 /* Preview Assets.xcassets in Resources */, 331 | 235981AC256B3340000D2479 /* Assets.xcassets in Resources */, 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | }; 335 | 23E597362547D4460080BE0E /* Resources */ = { 336 | isa = PBXResourcesBuildPhase; 337 | buildActionMask = 2147483647; 338 | files = ( 339 | 23E597402547D4470080BE0E /* Assets.xcassets in Resources */, 340 | 23E597432547D4470080BE0E /* Main.storyboard in Resources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | /* End PBXResourcesBuildPhase section */ 345 | 346 | /* Begin PBXSourcesBuildPhase section */ 347 | 230A4B71256CA750009B4689 /* Sources */ = { 348 | isa = PBXSourcesBuildPhase; 349 | buildActionMask = 2147483647; 350 | files = ( 351 | 230A4B7C256CA750009B4689 /* ViewController.m in Sources */, 352 | 230A4B84256CA751009B4689 /* main.m in Sources */, 353 | 230A4B79256CA750009B4689 /* AppDelegate.m in Sources */, 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | 235981A1256B333F000D2479 /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | 235981AA256B333F000D2479 /* ContentView.swift in Sources */, 362 | 235981A8256B333F000D2479 /* AppDelegate.swift in Sources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | 23E597342547D4460080BE0E /* Sources */ = { 367 | isa = PBXSourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | 23E5973E2547D4460080BE0E /* ViewController.swift in Sources */, 371 | 23E5973C2547D4460080BE0E /* AppDelegate.swift in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | /* End PBXSourcesBuildPhase section */ 376 | 377 | /* Begin PBXTargetDependency section */ 378 | 230A4B8E256CA760009B4689 /* PBXTargetDependency */ = { 379 | isa = PBXTargetDependency; 380 | target = 230A4B74256CA750009B4689 /* DSFDropFilesView ObjC Demo */; 381 | targetProxy = 230A4B8D256CA760009B4689 /* PBXContainerItemProxy */; 382 | }; 383 | 230A4B94256CA76B009B4689 /* PBXTargetDependency */ = { 384 | isa = PBXTargetDependency; 385 | productRef = 230A4B93256CA76B009B4689 /* DSFDropFilesView */; 386 | }; 387 | 23D29C49256C890800E5CBC0 /* PBXTargetDependency */ = { 388 | isa = PBXTargetDependency; 389 | target = 23E597372547D4460080BE0E /* DSFDropFilesView Demo */; 390 | targetProxy = 23D29C48256C890800E5CBC0 /* PBXContainerItemProxy */; 391 | }; 392 | 23D29C4B256C890800E5CBC0 /* PBXTargetDependency */ = { 393 | isa = PBXTargetDependency; 394 | target = 235981A4256B333F000D2479 /* DSFDropFilesView SwiftUI Demo */; 395 | targetProxy = 23D29C4A256C890800E5CBC0 /* PBXContainerItemProxy */; 396 | }; 397 | 23D29C57256C8DF300E5CBC0 /* PBXTargetDependency */ = { 398 | isa = PBXTargetDependency; 399 | productRef = 23D29C56256C8DF300E5CBC0 /* DSFDropFilesView */; 400 | }; 401 | /* End PBXTargetDependency section */ 402 | 403 | /* Begin PBXVariantGroup section */ 404 | 230A4B7F256CA751009B4689 /* Main.storyboard */ = { 405 | isa = PBXVariantGroup; 406 | children = ( 407 | 230A4B80256CA751009B4689 /* Base */, 408 | ); 409 | name = Main.storyboard; 410 | sourceTree = ""; 411 | }; 412 | 235981B0256B3340000D2479 /* Main.storyboard */ = { 413 | isa = PBXVariantGroup; 414 | children = ( 415 | 235981B1256B3340000D2479 /* Base */, 416 | ); 417 | name = Main.storyboard; 418 | sourceTree = ""; 419 | }; 420 | 23E597412547D4470080BE0E /* Main.storyboard */ = { 421 | isa = PBXVariantGroup; 422 | children = ( 423 | 23E597422547D4470080BE0E /* Base */, 424 | ); 425 | name = Main.storyboard; 426 | sourceTree = ""; 427 | }; 428 | /* End PBXVariantGroup section */ 429 | 430 | /* Begin XCBuildConfiguration section */ 431 | 230A4B86256CA751009B4689 /* Debug */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 435 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 436 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 437 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES; 438 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView ObjC Demo/DSFDropFilesView_ObjC_Demo.entitlements"; 439 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 440 | CODE_SIGN_STYLE = Automatic; 441 | COMBINE_HIDPI_IMAGES = YES; 442 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 443 | ENABLE_HARDENED_RUNTIME = YES; 444 | INFOPLIST_FILE = "DSFDropFilesView ObjC Demo/Info.plist"; 445 | LD_RUNPATH_SEARCH_PATHS = ( 446 | "$(inherited)", 447 | "@executable_path/../Frameworks", 448 | ); 449 | MACOSX_DEPLOYMENT_TARGET = 10.13; 450 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-ObjC-Demo"; 451 | PRODUCT_NAME = "$(TARGET_NAME)"; 452 | }; 453 | name = Debug; 454 | }; 455 | 230A4B87256CA751009B4689 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 459 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 460 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 461 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES; 462 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView ObjC Demo/DSFDropFilesView_ObjC_Demo.entitlements"; 463 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 464 | CODE_SIGN_STYLE = Automatic; 465 | COMBINE_HIDPI_IMAGES = YES; 466 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 467 | ENABLE_HARDENED_RUNTIME = YES; 468 | INFOPLIST_FILE = "DSFDropFilesView ObjC Demo/Info.plist"; 469 | LD_RUNPATH_SEARCH_PATHS = ( 470 | "$(inherited)", 471 | "@executable_path/../Frameworks", 472 | ); 473 | MACOSX_DEPLOYMENT_TARGET = 10.13; 474 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-ObjC-Demo"; 475 | PRODUCT_NAME = "$(TARGET_NAME)"; 476 | }; 477 | name = Release; 478 | }; 479 | 235981B6256B3340000D2479 /* Debug */ = { 480 | isa = XCBuildConfiguration; 481 | buildSettings = { 482 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 483 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 484 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView SwiftUI Demo/DSFDropFilesView_SwiftUI_Demo.entitlements"; 485 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 486 | CODE_SIGN_STYLE = Automatic; 487 | COMBINE_HIDPI_IMAGES = YES; 488 | DEVELOPMENT_ASSET_PATHS = "\"DSFDropFilesView SwiftUI Demo/Preview Content\""; 489 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 490 | ENABLE_HARDENED_RUNTIME = YES; 491 | ENABLE_PREVIEWS = YES; 492 | INFOPLIST_FILE = "DSFDropFilesView SwiftUI Demo/Info.plist"; 493 | LD_RUNPATH_SEARCH_PATHS = ( 494 | "$(inherited)", 495 | "@executable_path/../Frameworks", 496 | ); 497 | MACOSX_DEPLOYMENT_TARGET = 11.0; 498 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-SwiftUI-Demo"; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | SWIFT_VERSION = 5.0; 501 | }; 502 | name = Debug; 503 | }; 504 | 235981B7256B3340000D2479 /* Release */ = { 505 | isa = XCBuildConfiguration; 506 | buildSettings = { 507 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 508 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 509 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView SwiftUI Demo/DSFDropFilesView_SwiftUI_Demo.entitlements"; 510 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 511 | CODE_SIGN_STYLE = Automatic; 512 | COMBINE_HIDPI_IMAGES = YES; 513 | DEVELOPMENT_ASSET_PATHS = "\"DSFDropFilesView SwiftUI Demo/Preview Content\""; 514 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 515 | ENABLE_HARDENED_RUNTIME = YES; 516 | ENABLE_PREVIEWS = YES; 517 | INFOPLIST_FILE = "DSFDropFilesView SwiftUI Demo/Info.plist"; 518 | LD_RUNPATH_SEARCH_PATHS = ( 519 | "$(inherited)", 520 | "@executable_path/../Frameworks", 521 | ); 522 | MACOSX_DEPLOYMENT_TARGET = 11.0; 523 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-SwiftUI-Demo"; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | SWIFT_VERSION = 5.0; 526 | }; 527 | name = Release; 528 | }; 529 | 23D29C42256C890300E5CBC0 /* Debug */ = { 530 | isa = XCBuildConfiguration; 531 | buildSettings = { 532 | CODE_SIGN_STYLE = Automatic; 533 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 534 | PRODUCT_NAME = "$(TARGET_NAME)"; 535 | }; 536 | name = Debug; 537 | }; 538 | 23D29C43256C890300E5CBC0 /* Release */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | CODE_SIGN_STYLE = Automatic; 542 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | }; 545 | name = Release; 546 | }; 547 | 23E597462547D4470080BE0E /* Debug */ = { 548 | isa = XCBuildConfiguration; 549 | buildSettings = { 550 | ALWAYS_SEARCH_USER_PATHS = NO; 551 | CLANG_ANALYZER_NONNULL = YES; 552 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 553 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 554 | CLANG_CXX_LIBRARY = "libc++"; 555 | CLANG_ENABLE_MODULES = YES; 556 | CLANG_ENABLE_OBJC_ARC = YES; 557 | CLANG_ENABLE_OBJC_WEAK = YES; 558 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 559 | CLANG_WARN_BOOL_CONVERSION = YES; 560 | CLANG_WARN_COMMA = YES; 561 | CLANG_WARN_CONSTANT_CONVERSION = YES; 562 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 563 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 564 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 565 | CLANG_WARN_EMPTY_BODY = YES; 566 | CLANG_WARN_ENUM_CONVERSION = YES; 567 | CLANG_WARN_INFINITE_RECURSION = YES; 568 | CLANG_WARN_INT_CONVERSION = YES; 569 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 570 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 571 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 572 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 573 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 574 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 575 | CLANG_WARN_STRICT_PROTOTYPES = YES; 576 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 577 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 578 | CLANG_WARN_UNREACHABLE_CODE = YES; 579 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 580 | COPY_PHASE_STRIP = NO; 581 | DEBUG_INFORMATION_FORMAT = dwarf; 582 | ENABLE_STRICT_OBJC_MSGSEND = YES; 583 | ENABLE_TESTABILITY = YES; 584 | GCC_C_LANGUAGE_STANDARD = gnu11; 585 | GCC_DYNAMIC_NO_PIC = NO; 586 | GCC_NO_COMMON_BLOCKS = YES; 587 | GCC_OPTIMIZATION_LEVEL = 0; 588 | GCC_PREPROCESSOR_DEFINITIONS = ( 589 | "DEBUG=1", 590 | "$(inherited)", 591 | ); 592 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 593 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 594 | GCC_WARN_UNDECLARED_SELECTOR = YES; 595 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 596 | GCC_WARN_UNUSED_FUNCTION = YES; 597 | GCC_WARN_UNUSED_VARIABLE = YES; 598 | MACOSX_DEPLOYMENT_TARGET = 10.15; 599 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 600 | MTL_FAST_MATH = YES; 601 | ONLY_ACTIVE_ARCH = YES; 602 | SDKROOT = macosx; 603 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 604 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 605 | }; 606 | name = Debug; 607 | }; 608 | 23E597472547D4470080BE0E /* Release */ = { 609 | isa = XCBuildConfiguration; 610 | buildSettings = { 611 | ALWAYS_SEARCH_USER_PATHS = NO; 612 | CLANG_ANALYZER_NONNULL = YES; 613 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 614 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 615 | CLANG_CXX_LIBRARY = "libc++"; 616 | CLANG_ENABLE_MODULES = YES; 617 | CLANG_ENABLE_OBJC_ARC = YES; 618 | CLANG_ENABLE_OBJC_WEAK = YES; 619 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 620 | CLANG_WARN_BOOL_CONVERSION = YES; 621 | CLANG_WARN_COMMA = YES; 622 | CLANG_WARN_CONSTANT_CONVERSION = YES; 623 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 624 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 625 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 626 | CLANG_WARN_EMPTY_BODY = YES; 627 | CLANG_WARN_ENUM_CONVERSION = YES; 628 | CLANG_WARN_INFINITE_RECURSION = YES; 629 | CLANG_WARN_INT_CONVERSION = YES; 630 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 631 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 632 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 633 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 634 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 635 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 636 | CLANG_WARN_STRICT_PROTOTYPES = YES; 637 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 638 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 639 | CLANG_WARN_UNREACHABLE_CODE = YES; 640 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 641 | COPY_PHASE_STRIP = NO; 642 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 643 | ENABLE_NS_ASSERTIONS = NO; 644 | ENABLE_STRICT_OBJC_MSGSEND = YES; 645 | GCC_C_LANGUAGE_STANDARD = gnu11; 646 | GCC_NO_COMMON_BLOCKS = YES; 647 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 648 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 649 | GCC_WARN_UNDECLARED_SELECTOR = YES; 650 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 651 | GCC_WARN_UNUSED_FUNCTION = YES; 652 | GCC_WARN_UNUSED_VARIABLE = YES; 653 | MACOSX_DEPLOYMENT_TARGET = 10.15; 654 | MTL_ENABLE_DEBUG_INFO = NO; 655 | MTL_FAST_MATH = YES; 656 | SDKROOT = macosx; 657 | SWIFT_COMPILATION_MODE = wholemodule; 658 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 659 | }; 660 | name = Release; 661 | }; 662 | 23E597492547D4470080BE0E /* Debug */ = { 663 | isa = XCBuildConfiguration; 664 | buildSettings = { 665 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 666 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 667 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView Demo/DSFDropFilesView_Demo.entitlements"; 668 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 669 | CODE_SIGN_STYLE = Automatic; 670 | COMBINE_HIDPI_IMAGES = YES; 671 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 672 | ENABLE_HARDENED_RUNTIME = YES; 673 | INFOPLIST_FILE = "DSFDropFilesView Demo/Info.plist"; 674 | LD_RUNPATH_SEARCH_PATHS = ( 675 | "$(inherited)", 676 | "@executable_path/../Frameworks", 677 | ); 678 | MACOSX_DEPLOYMENT_TARGET = 10.13; 679 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-Demo"; 680 | PRODUCT_NAME = "$(TARGET_NAME)"; 681 | SWIFT_VERSION = 5.0; 682 | }; 683 | name = Debug; 684 | }; 685 | 23E5974A2547D4470080BE0E /* Release */ = { 686 | isa = XCBuildConfiguration; 687 | buildSettings = { 688 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 689 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 690 | CODE_SIGN_ENTITLEMENTS = "DSFDropFilesView Demo/DSFDropFilesView_Demo.entitlements"; 691 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 692 | CODE_SIGN_STYLE = Automatic; 693 | COMBINE_HIDPI_IMAGES = YES; 694 | DEVELOPMENT_TEAM = 3L6RK3LGGW; 695 | ENABLE_HARDENED_RUNTIME = YES; 696 | INFOPLIST_FILE = "DSFDropFilesView Demo/Info.plist"; 697 | LD_RUNPATH_SEARCH_PATHS = ( 698 | "$(inherited)", 699 | "@executable_path/../Frameworks", 700 | ); 701 | MACOSX_DEPLOYMENT_TARGET = 10.13; 702 | PRODUCT_BUNDLE_IDENTIFIER = "com.darrenford.DSFDropFilesView-Demo"; 703 | PRODUCT_NAME = "$(TARGET_NAME)"; 704 | SWIFT_VERSION = 5.0; 705 | }; 706 | name = Release; 707 | }; 708 | /* End XCBuildConfiguration section */ 709 | 710 | /* Begin XCConfigurationList section */ 711 | 230A4B88256CA751009B4689 /* Build configuration list for PBXNativeTarget "DSFDropFilesView ObjC Demo" */ = { 712 | isa = XCConfigurationList; 713 | buildConfigurations = ( 714 | 230A4B86256CA751009B4689 /* Debug */, 715 | 230A4B87256CA751009B4689 /* Release */, 716 | ); 717 | defaultConfigurationIsVisible = 0; 718 | defaultConfigurationName = Release; 719 | }; 720 | 235981B5256B3340000D2479 /* Build configuration list for PBXNativeTarget "DSFDropFilesView SwiftUI Demo" */ = { 721 | isa = XCConfigurationList; 722 | buildConfigurations = ( 723 | 235981B6256B3340000D2479 /* Debug */, 724 | 235981B7256B3340000D2479 /* Release */, 725 | ); 726 | defaultConfigurationIsVisible = 0; 727 | defaultConfigurationName = Release; 728 | }; 729 | 23D29C44256C890300E5CBC0 /* Build configuration list for PBXAggregateTarget "All" */ = { 730 | isa = XCConfigurationList; 731 | buildConfigurations = ( 732 | 23D29C42256C890300E5CBC0 /* Debug */, 733 | 23D29C43256C890300E5CBC0 /* Release */, 734 | ); 735 | defaultConfigurationIsVisible = 0; 736 | defaultConfigurationName = Release; 737 | }; 738 | 23E597332547D4460080BE0E /* Build configuration list for PBXProject "DSFDropFilesView Demo" */ = { 739 | isa = XCConfigurationList; 740 | buildConfigurations = ( 741 | 23E597462547D4470080BE0E /* Debug */, 742 | 23E597472547D4470080BE0E /* Release */, 743 | ); 744 | defaultConfigurationIsVisible = 0; 745 | defaultConfigurationName = Release; 746 | }; 747 | 23E597482547D4470080BE0E /* Build configuration list for PBXNativeTarget "DSFDropFilesView Demo" */ = { 748 | isa = XCConfigurationList; 749 | buildConfigurations = ( 750 | 23E597492547D4470080BE0E /* Debug */, 751 | 23E5974A2547D4470080BE0E /* Release */, 752 | ); 753 | defaultConfigurationIsVisible = 0; 754 | defaultConfigurationName = Release; 755 | }; 756 | /* End XCConfigurationList section */ 757 | 758 | /* Begin XCSwiftPackageProductDependency section */ 759 | 230A4B93256CA76B009B4689 /* DSFDropFilesView */ = { 760 | isa = XCSwiftPackageProductDependency; 761 | productName = DSFDropFilesView; 762 | }; 763 | 23D18D09286FA4B500D5A0D2 /* DSFDropFilesView */ = { 764 | isa = XCSwiftPackageProductDependency; 765 | productName = DSFDropFilesView; 766 | }; 767 | 23D18D0B286FA4C000D5A0D2 /* DSFDropFilesView */ = { 768 | isa = XCSwiftPackageProductDependency; 769 | productName = DSFDropFilesView; 770 | }; 771 | 23D18D0D286FA4DF00D5A0D2 /* DSFDropFilesView */ = { 772 | isa = XCSwiftPackageProductDependency; 773 | productName = DSFDropFilesView; 774 | }; 775 | 23D29C56256C8DF300E5CBC0 /* DSFDropFilesView */ = { 776 | isa = XCSwiftPackageProductDependency; 777 | productName = DSFDropFilesView; 778 | }; 779 | /* End XCSwiftPackageProductDependency section */ 780 | }; 781 | rootObject = 23E597302547D4460080BE0E /* Project object */; 782 | } 783 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView SwiftUI Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | Default 529 | 530 | 531 | 532 | 533 | 534 | 535 | Left to Right 536 | 537 | 538 | 539 | 540 | 541 | 542 | Right to Left 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | Default 554 | 555 | 556 | 557 | 558 | 559 | 560 | Left to Right 561 | 562 | 563 | 564 | 565 | 566 | 567 | Right to Left 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | -------------------------------------------------------------------------------- /Demos/DSFDropFilesView Demo/DSFDropFilesView ObjC Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | Default 530 | 531 | 532 | 533 | 534 | 535 | 536 | Left to Right 537 | 538 | 539 | 540 | 541 | 542 | 543 | Right to Left 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | Default 555 | 556 | 557 | 558 | 559 | 560 | 561 | Left to Right 562 | 563 | 564 | 565 | 566 | 567 | 568 | Right to Left 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | --------------------------------------------------------------------------------