From 09a3f21d8b37bf457d214a6fc49ccee6f1083cb1 Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Thu, 9 Jan 2025 21:53:09 -0600 Subject: [PATCH] Add Example App (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description - Adds an example app. Just a barebones example of how to use the `TextView` class to create a plain-text editor. - The project is set up so the package is a local, editable, dependency. This has been extremely useful in CESE, and should help when developing this package. It's also good documentation, and we can point people towards this example app if they'd like to see how to use the text view. ### Related Issues - N/A ### Checklist - [x] I read and understood the [contributing guide](https://github.com/CodeEditApp/CodeEdit/blob/main/CONTRIBUTING.md) as well as the [code of conduct](https://github.com/CodeEditApp/CodeEdit/blob/main/CODE_OF_CONDUCT.md) - [x] The issues this PR addresses are related to each other - [x] My changes generate no new warnings - [x] My code builds and runs on my machine - [x] My changes are all related to the related issue above - [x] I documented my code ### Screenshots ![Screenshot 2025-01-09 at 3 06 14 PM](https://github.com/user-attachments/assets/bc94e85d-eada-4dd0-9337-133cd79ede9e) --- .../project.pbxproj | 408 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/swiftpm/Package.resolved | 41 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Assets.xcassets/Contents.json | 6 + .../CodeEditTextViewExample.entitlements | 10 + .../CodeEditTextViewExampleApp.swift | 17 + .../CodeEditTextViewExampleDocument.swift | 35 ++ .../CodeEditTextViewExample/Info.plist | 39 ++ .../Views/ContentView.swift | 20 + .../Views/SwiftUITextView.swift | 59 +++ .../Views/TextViewController.swift | 58 +++ 13 files changed, 746 insertions(+) create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.pbxproj create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/Contents.json create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExample.entitlements create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExampleApp.swift create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Documents/CodeEditTextViewExampleDocument.swift create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Info.plist create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/ContentView.swift create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/SwiftUITextView.swift create mode 100644 Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/TextViewController.swift diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.pbxproj b/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..97a0dffc --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.pbxproj @@ -0,0 +1,408 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 6C2265DF2D306AB7008710D7 /* CodeEditTextView in Frameworks */ = {isa = PBXBuildFile; productRef = 6C2265DE2D306AB7008710D7 /* CodeEditTextView */; }; + 6C2265E42D306B90008710D7 /* SwiftUITextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C2265E32D306B90008710D7 /* SwiftUITextView.swift */; }; + 6C2265E62D306D37008710D7 /* TextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C2265E52D306D37008710D7 /* TextViewController.swift */; }; + 6CCDA29B2D306A25007CD84A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CCDA2942D306A25007CD84A /* Assets.xcassets */; }; + 6CCDA29D2D306A25007CD84A /* CodeEditTextViewExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CCDA2952D306A25007CD84A /* CodeEditTextViewExampleApp.swift */; }; + 6CCDA29E2D306A25007CD84A /* CodeEditTextViewExampleDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CCDA2962D306A25007CD84A /* CodeEditTextViewExampleDocument.swift */; }; + 6CCDA29F2D306A25007CD84A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CCDA2972D306A25007CD84A /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6C2265E12D306B58008710D7 /* CodeEditTextViewExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CodeEditTextViewExample.entitlements; sourceTree = ""; }; + 6C2265E32D306B90008710D7 /* SwiftUITextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUITextView.swift; sourceTree = ""; }; + 6C2265E52D306D37008710D7 /* TextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewController.swift; sourceTree = ""; }; + 6CCDA27D2D306A1B007CD84A /* CodeEditTextViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CodeEditTextViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6CCDA2942D306A25007CD84A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6CCDA2952D306A25007CD84A /* CodeEditTextViewExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditTextViewExampleApp.swift; sourceTree = ""; }; + 6CCDA2962D306A25007CD84A /* CodeEditTextViewExampleDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeEditTextViewExampleDocument.swift; sourceTree = ""; }; + 6CCDA2972D306A25007CD84A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 6CCDA2982D306A25007CD84A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6CCDA2A12D306A5B007CD84A /* CodeEditTextView */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = CodeEditTextView; path = ../..; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6CCDA27A2D306A1B007CD84A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6C2265DF2D306AB7008710D7 /* CodeEditTextView in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6C2265E02D306AEB008710D7 /* Documents */ = { + isa = PBXGroup; + children = ( + 6CCDA2962D306A25007CD84A /* CodeEditTextViewExampleDocument.swift */, + ); + path = Documents; + sourceTree = ""; + }; + 6C2265E22D306B69008710D7 /* Views */ = { + isa = PBXGroup; + children = ( + 6CCDA2972D306A25007CD84A /* ContentView.swift */, + 6C2265E32D306B90008710D7 /* SwiftUITextView.swift */, + 6C2265E52D306D37008710D7 /* TextViewController.swift */, + ); + path = Views; + sourceTree = ""; + }; + 6CCDA2742D306A1B007CD84A = { + isa = PBXGroup; + children = ( + 6CCDA2A12D306A5B007CD84A /* CodeEditTextView */, + 6CCDA2992D306A25007CD84A /* CodeEditTextViewExample */, + 6CCDA2A02D306A5B007CD84A /* Frameworks */, + 6CCDA27E2D306A1B007CD84A /* Products */, + ); + sourceTree = ""; + }; + 6CCDA27E2D306A1B007CD84A /* Products */ = { + isa = PBXGroup; + children = ( + 6CCDA27D2D306A1B007CD84A /* CodeEditTextViewExample.app */, + ); + name = Products; + sourceTree = ""; + }; + 6CCDA2992D306A25007CD84A /* CodeEditTextViewExample */ = { + isa = PBXGroup; + children = ( + 6CCDA2952D306A25007CD84A /* CodeEditTextViewExampleApp.swift */, + 6C2265E02D306AEB008710D7 /* Documents */, + 6C2265E22D306B69008710D7 /* Views */, + 6CCDA2942D306A25007CD84A /* Assets.xcassets */, + 6CCDA2982D306A25007CD84A /* Info.plist */, + 6C2265E12D306B58008710D7 /* CodeEditTextViewExample.entitlements */, + ); + path = CodeEditTextViewExample; + sourceTree = ""; + }; + 6CCDA2A02D306A5B007CD84A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6CCDA27C2D306A1B007CD84A /* CodeEditTextViewExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6CCDA28D2D306A1C007CD84A /* Build configuration list for PBXNativeTarget "CodeEditTextViewExample" */; + buildPhases = ( + 6CCDA2792D306A1B007CD84A /* Sources */, + 6CCDA27A2D306A1B007CD84A /* Frameworks */, + 6CCDA27B2D306A1B007CD84A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CodeEditTextViewExample; + packageProductDependencies = ( + 6C2265DE2D306AB7008710D7 /* CodeEditTextView */, + ); + productName = CodeEditTextViewExample; + productReference = 6CCDA27D2D306A1B007CD84A /* CodeEditTextViewExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6CCDA2752D306A1B007CD84A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1620; + LastUpgradeCheck = 1620; + TargetAttributes = { + 6CCDA27C2D306A1B007CD84A = { + CreatedOnToolsVersion = 16.2; + }; + }; + }; + buildConfigurationList = 6CCDA2782D306A1B007CD84A /* Build configuration list for PBXProject "CodeEditTextViewExample" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6CCDA2742D306A1B007CD84A; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 6CCDA27E2D306A1B007CD84A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6CCDA27C2D306A1B007CD84A /* CodeEditTextViewExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6CCDA27B2D306A1B007CD84A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6CCDA29B2D306A25007CD84A /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6CCDA2792D306A1B007CD84A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6C2265E62D306D37008710D7 /* TextViewController.swift in Sources */, + 6CCDA29D2D306A25007CD84A /* CodeEditTextViewExampleApp.swift in Sources */, + 6CCDA29E2D306A25007CD84A /* CodeEditTextViewExampleDocument.swift in Sources */, + 6CCDA29F2D306A25007CD84A /* ContentView.swift in Sources */, + 6C2265E42D306B90008710D7 /* SwiftUITextView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6CCDA28E2D306A1C007CD84A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = CodeEditTextViewExample/CodeEditTextViewExample.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"CodeEditTextViewExample/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = CodeEditTextViewExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportsDocumentBrowser = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 13; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = app.codeedit.CodeEditTextViewExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = NO; + SUPPORTED_PLATFORMS = macosx; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 6CCDA28F2D306A1C007CD84A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = CodeEditTextViewExample/CodeEditTextViewExample.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"CodeEditTextViewExample/Preview Content\""; + DEVELOPMENT_TEAM = ""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = CodeEditTextViewExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportsDocumentBrowser = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 13; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = app.codeedit.CodeEditTextViewExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = NO; + SUPPORTED_PLATFORMS = macosx; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 6CCDA2902D306A1C007CD84A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 6CCDA2912D306A1C007CD84A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6CCDA2782D306A1B007CD84A /* Build configuration list for PBXProject "CodeEditTextViewExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CCDA2902D306A1C007CD84A /* Debug */, + 6CCDA2912D306A1C007CD84A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6CCDA28D2D306A1C007CD84A /* Build configuration list for PBXNativeTarget "CodeEditTextViewExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6CCDA28E2D306A1C007CD84A /* Debug */, + 6CCDA28F2D306A1C007CD84A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 6C2265DE2D306AB7008710D7 /* CodeEditTextView */ = { + isa = XCSwiftPackageProductDependency; + productName = CodeEditTextView; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 6CCDA2752D306A1B007CD84A /* Project object */; +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..c39b388a --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,41 @@ +{ + "pins" : [ + { + "identity" : "rearrange", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ChimeHQ/Rearrange", + "state" : { + "revision" : "5ff7f3363f7a08f77e0d761e38e6add31c2136e1", + "version" : "1.8.1" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7", + "version" : "1.1.4" + } + }, + { + "identity" : "swiftlintplugin", + "kind" : "remoteSourceControl", + "location" : "https://github.com/lukepistrol/SwiftLintPlugin", + "state" : { + "revision" : "87454f5c9ff4d644086aec2a0df1ffba678e7f3c", + "version" : "0.57.1" + } + }, + { + "identity" : "textstory", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ChimeHQ/TextStory", + "state" : { + "revision" : "8dc9148b46fcf93b08ea9d4ef9bdb5e4f700e008", + "version" : "0.9.0" + } + } + ], + "version" : 2 +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/Contents.json b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExample.entitlements b/Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExample.entitlements new file mode 100644 index 00000000..19afff14 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExample.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExampleApp.swift b/Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExampleApp.swift new file mode 100644 index 00000000..682e67bb --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/CodeEditTextViewExampleApp.swift @@ -0,0 +1,17 @@ +// +// CodeEditTextViewExampleApp.swift +// CodeEditTextViewExample +// +// Created by Khan Winter on 1/9/25. +// + +import SwiftUI + +@main +struct CodeEditTextViewExampleApp: App { + var body: some Scene { + DocumentGroup(newDocument: CodeEditTextViewExampleDocument()) { file in + ContentView(document: file.$document) + } + } +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Documents/CodeEditTextViewExampleDocument.swift b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Documents/CodeEditTextViewExampleDocument.swift new file mode 100644 index 00000000..43a1bdcb --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Documents/CodeEditTextViewExampleDocument.swift @@ -0,0 +1,35 @@ +// +// CodeEditTextViewExampleDocument.swift +// CodeEditTextViewExample +// +// Created by Khan Winter on 1/9/25. +// + +import SwiftUI +import UniformTypeIdentifiers + +struct CodeEditTextViewExampleDocument: FileDocument { + var text: String + + init(text: String = "") { + self.text = text + } + + static var readableContentTypes: [UTType] { + [ + .item + ] + } + + init(configuration: ReadConfiguration) throws { + guard let data = configuration.file.regularFileContents else { + throw CocoaError(.fileReadCorruptFile) + } + text = String(decoding: data, as: UTF8.self) + } + + func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { + let data = Data(text.utf8) + return .init(regularFileWithContents: data) + } +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Info.plist b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Info.plist new file mode 100644 index 00000000..6951ec92 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleDocumentTypes + + + CFBundleTypeRole + Editor + LSItemContentTypes + + com.example.plain-text + + NSUbiquitousDocumentUserActivityType + $(PRODUCT_BUNDLE_IDENTIFIER).exampledocument + + + UTImportedTypeDeclarations + + + UTTypeConformsTo + + public.plain-text + + UTTypeDescription + Example Text + UTTypeIdentifier + com.example.plain-text + UTTypeTagSpecification + + public.filename-extension + + exampletext + + + + + + diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/ContentView.swift b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/ContentView.swift new file mode 100644 index 00000000..f914a802 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/ContentView.swift @@ -0,0 +1,20 @@ +// +// ContentView.swift +// CodeEditTextViewExample +// +// Created by Khan Winter on 1/9/25. +// + +import SwiftUI + +struct ContentView: View { + @Binding var document: CodeEditTextViewExampleDocument + + var body: some View { + SwiftUITextView(text: $document.text) + } +} + +#Preview { + ContentView(document: .constant(CodeEditTextViewExampleDocument())) +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/SwiftUITextView.swift b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/SwiftUITextView.swift new file mode 100644 index 00000000..8dd341c2 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/SwiftUITextView.swift @@ -0,0 +1,59 @@ +// +// SwiftUITextView.swift +// CodeEditTextViewExample +// +// Created by Khan Winter on 1/9/25. +// + +import SwiftUI +import AppKit +import CodeEditTextView + +struct SwiftUITextView: NSViewControllerRepresentable { + @Binding var text: String + + func makeNSViewController(context: Context) -> TextViewController { + let controller = TextViewController(string: text) + context.coordinator.controller = controller + return controller + } + + func updateNSViewController(_ nsViewController: TextViewController, context: Context) { + // Do nothing, our binding has to be a one-way binding + } + + func makeCoordinator() -> Coordinator { + Coordinator(text: $text) + } + + @MainActor + public class Coordinator: NSObject { + weak var controller: TextViewController? + var text: Binding + + init(text: Binding) { + self.text = text + super.init() + + NotificationCenter.default.addObserver( + self, + selector: #selector(textViewDidChangeText(_:)), + name: TextView.textDidChangeNotification, + object: nil + ) + } + + @objc func textViewDidChangeText(_ notification: Notification) { + guard let textView = notification.object as? TextView, + let controller, + controller.textView === textView else { + return + } + text.wrappedValue = textView.string + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + } +} diff --git a/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/TextViewController.swift b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/TextViewController.swift new file mode 100644 index 00000000..0e568b55 --- /dev/null +++ b/Example/CodeEditTextViewExample/CodeEditTextViewExample/Views/TextViewController.swift @@ -0,0 +1,58 @@ +// +// TextViewController.swift +// CodeEditTextViewExample +// +// Created by Khan Winter on 1/9/25. +// + +import AppKit +import CodeEditTextView + +class TextViewController: NSViewController { + var scrollView: NSScrollView! + var textView: TextView! + + init(string: String) { + textView = TextView(string: string) + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + scrollView = NSScrollView() + textView.translatesAutoresizingMaskIntoConstraints = false + + scrollView.translatesAutoresizingMaskIntoConstraints = false + scrollView.documentView = textView + scrollView.contentView.postsFrameChangedNotifications = true + scrollView.contentView.postsBoundsChangedNotifications = true + scrollView.hasVerticalScroller = true + + self.view = scrollView + + NSLayoutConstraint.activate([ + scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + scrollView.topAnchor.constraint(equalTo: view.topAnchor), + scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + + // Layout on scroll change + NotificationCenter.default.addObserver( + forName: NSView.frameDidChangeNotification, + object: scrollView.contentView, + queue: .main + ) { [weak self] _ in + self?.textView.updatedViewport(self?.scrollView.documentVisibleRect ?? .zero) + } + + textView.updateFrameIfNeeded() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } +}