Skip to content

Commit

Permalink
Add Example App (#64)
Browse files Browse the repository at this point in the history
### 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)
  • Loading branch information
thecoolwinter authored Jan 10, 2025
1 parent 41ea36f commit 09a3f21
Show file tree
Hide file tree
Showing 13 changed files with 746 additions and 0 deletions.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
39 changes: 39 additions & 0 deletions Example/CodeEditTextViewExample/CodeEditTextViewExample/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>com.example.plain-text</string>
</array>
<key>NSUbiquitousDocumentUserActivityType</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).exampledocument</string>
</dict>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.plain-text</string>
</array>
<key>UTTypeDescription</key>
<string>Example Text</string>
<key>UTTypeIdentifier</key>
<string>com.example.plain-text</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>exampletext</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -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()))
}
Original file line number Diff line number Diff line change
@@ -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<String>

init(text: Binding<String>) {
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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit 09a3f21

Please sign in to comment.