Skip to content

Commit

Permalink
Refactor. Rename module to STAnnotationsPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
krzyzanowskim committed Jan 26, 2024
1 parent b80a135 commit f347442
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 132 deletions.
28 changes: 23 additions & 5 deletions DemoApp/DemoApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
752C90732AB1AF12006C835E /* LabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752C90722AB1AF12006C835E /* LabelView.swift */; };
752C90732AB1AF12006C835E /* AnnotationLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752C90722AB1AF12006C835E /* AnnotationLabelView.swift */; };
752C90752AB1FA5F006C835E /* MessageLineAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752C90742AB1FA5F006C835E /* MessageLineAnnotation.swift */; };
7565EBBE2A9BE09D0005B170 /* EditorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7565EBBD2A9BE09D0005B170 /* EditorViewController.swift */; };
7565EBC12A9BE0B10005B170 /* STTextView in Frameworks */ = {isa = PBXBuildFile; productRef = 7565EBC02A9BE0B10005B170 /* STTextView */; };
Expand All @@ -18,7 +18,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
752C90722AB1AF12006C835E /* LabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelView.swift; sourceTree = "<group>"; };
752C90722AB1AF12006C835E /* AnnotationLabelView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnnotationLabelView.swift; sourceTree = "<group>"; };
752C90742AB1FA5F006C835E /* MessageLineAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageLineAnnotation.swift; sourceTree = "<group>"; };
7565EBBD2A9BE09D0005B170 /* EditorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditorViewController.swift; sourceTree = "<group>"; };
7576EDCA2AB0627D00540BF5 /* STTextView-Plugin-Annotations */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "STTextView-Plugin-Annotations"; path = ..; sourceTree = "<group>"; };
Expand All @@ -27,6 +27,7 @@
758BAB542A9BDFC300D840BF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
758BAB572A9BDFC300D840BF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
758BAB592A9BDFC300D840BF /* DemoApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DemoApp.entitlements; sourceTree = "<group>"; };
75E3BF172B6305B800680EE4 /* STTextView */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = STTextView; path = ../../STTextView; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -52,10 +53,11 @@
758BAB462A9BDFC200D840BF = {
isa = PBXGroup;
children = (
75E3BF172B6305B800680EE4 /* STTextView */,
7576EDCA2AB0627D00540BF5 /* STTextView-Plugin-Annotations */,
7590AFEA2B64617E00947F82 /* View */,
7590AFE92B64617300947F82 /* Model */,
7565EBBD2A9BE09D0005B170 /* EditorViewController.swift */,
752C90722AB1AF12006C835E /* LabelView.swift */,
752C90742AB1FA5F006C835E /* MessageLineAnnotation.swift */,
758BAB522A9BDFC200D840BF /* AppDelegate.swift */,
758BAB542A9BDFC300D840BF /* Assets.xcassets */,
758BAB562A9BDFC300D840BF /* MainMenu.xib */,
Expand All @@ -73,6 +75,22 @@
name = Products;
sourceTree = "<group>";
};
7590AFE92B64617300947F82 /* Model */ = {
isa = PBXGroup;
children = (
752C90742AB1FA5F006C835E /* MessageLineAnnotation.swift */,
);
path = Model;
sourceTree = "<group>";
};
7590AFEA2B64617E00947F82 /* View */ = {
isa = PBXGroup;
children = (
752C90722AB1AF12006C835E /* AnnotationLabelView.swift */,
);
path = View;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -151,7 +169,7 @@
buildActionMask = 2147483647;
files = (
758BAB532A9BDFC200D840BF /* AppDelegate.swift in Sources */,
752C90732AB1AF12006C835E /* LabelView.swift in Sources */,
752C90732AB1AF12006C835E /* AnnotationLabelView.swift in Sources */,
752C90752AB1FA5F006C835E /* MessageLineAnnotation.swift in Sources */,
7565EBBE2A9BE09D0005B170 /* EditorViewController.swift in Sources */,
);
Expand Down
85 changes: 35 additions & 50 deletions DemoApp/EditorViewController.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import AppKit
import STTextView

import AnnotationsPlugin
import STAnnotationsPlugin

final class EditorViewController: NSViewController {

private var annotationsPlugin: AnnotationsPlugin!
private var annotationsPlugin: STAnnotationsPlugin!
private var textView: STTextView!
private var annotations: [MessageLineAnnotation] = [] {
didSet {
Expand All @@ -23,7 +23,7 @@ final class EditorViewController: NSViewController {
super.viewDidLoad()
view.frame.size = CGSize(width: 500, height: 500)

annotationsPlugin = AnnotationsPlugin(dataSource: self)
annotationsPlugin = STAnnotationsPlugin(dataSource: self)

textView.addPlugin(
annotationsPlugin
Expand All @@ -32,72 +32,57 @@ final class EditorViewController: NSViewController {
textView.backgroundColor = .controlBackgroundColor
textView.font = .monospacedSystemFont(ofSize: 0, weight: .regular)

let defaultParagraphStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
// defaultParagraphStyle.lineHeightMultiple = 1.5

textView.defaultParagraphStyle = defaultParagraphStyle

textView.string = """
import Foundation
func hello() {
print("Hello World!")
func main() {
print("Hello World!)
}
"""

// add annotation
do {
let stringRange = textView.string.startIndex..<textView.string.endIndex
if let ocurrenceRange = textView.string.range(of: "Foundation", range: stringRange) {
let characterLocationOffset = textView.string.distance(from: textView.string.startIndex, to: ocurrenceRange.upperBound)
let annotation = try! MessageLineAnnotation(
message: AttributedString(markdown: "**TODO**: to cry _or_ not to cry"),
location: textView.textLayoutManager.location(textView.textLayoutManager.documentRange.location, offsetBy: characterLocationOffset)!
)
annotations.append(annotation)
}
}
let annotation1 = try! MessageLineAnnotation(
message: AttributedString(markdown: "Swift Foundation framework"),
kind: .info,
location: textView.textLayoutManager.location(textView.textLayoutManager.documentRange.location, offsetBy: 17)!
)

let annotation2 = try! MessageLineAnnotation(
message: AttributedString(markdown: "**ERROR**: Missing \" at the end of the string literal"),
kind: .error,
location: textView.textLayoutManager.location(textView.textLayoutManager.documentRange.location, offsetBy: 56)!
)

annotations += [annotation1, annotation2]
}
}

import SwiftUI

extension EditorViewController: AnnotationsDataSource {

private func removeAnnotation(_ annotation: MessageLineAnnotation) {
annotations.removeAll(where: { $0.id == annotation.id })
}
extension EditorViewController: STAnnotationsDataSource {

func textView(_ textView: STTextView, viewForLineAnnotation lineAnnotation: any LineAnnotation, textLineFragment: NSTextLineFragment) -> NSView? {
guard let myLineAnnotation = lineAnnotation as? MessageLineAnnotation else {
func textView(_ textView: STTextView, viewForLineAnnotation lineAnnotation: any STLineAnnotation, textLineFragment: NSTextLineFragment, proposedViewFrame: CGRect) -> NSView? {
guard let lineAnnotation = lineAnnotation as? MessageLineAnnotation else {
assertionFailure()
return nil
}

let messageFont = NSFont.preferredFont(forTextStyle: .body)

let decorationView = AnnotationView(
LabelView(
message: myLineAnnotation.message,
action: { [weak self] annotation in
self?.removeAnnotation(annotation)
},
lineAnnotation: myLineAnnotation
)
.font(Font(messageFont))
)

// Position

let segmentFrame = textView.textLayoutManager.textSegmentFrame(at: myLineAnnotation.location, type: .standard)!
let annotationHeight = min(textLineFragment.typographicBounds.height, textView.font?.boundingRectForFont.height ?? 24)

decorationView.frame = CGRect(
x: segmentFrame.maxX,
y: segmentFrame.minY + (segmentFrame.height - annotationHeight),
width: textView.visibleRect.maxX - segmentFrame.maxX,
height: annotationHeight
)
return decorationView
return STAnnotationView(frame: proposedViewFrame) {
AnnotationLabelView(Text(lineAnnotation.message), annotation: lineAnnotation) { [weak self] annotation in
// Remove annotation
self?.annotations.removeAll(where: { $0.id == annotation.id })
}
.font(.body)
}
}

func textViewAnnotations() -> [any LineAnnotation] {

func textViewAnnotations() -> [any STLineAnnotation] {
annotations
}

}
12 changes: 0 additions & 12 deletions DemoApp/MessageLineAnnotation.swift

This file was deleted.

20 changes: 20 additions & 0 deletions DemoApp/Model/MessageLineAnnotation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Cocoa
import STAnnotationsPlugin

enum AnnotationKind {
case info
case warning
case error
}

final class MessageLineAnnotation: NSObject, STLineAnnotation {
var location: NSTextLocation
let message: AttributedString
let kind: AnnotationKind

init(message: AttributedString, kind: AnnotationKind, location: NSTextLocation) {
self.message = message
self.kind = kind
self.location = location
}
}
43 changes: 34 additions & 9 deletions DemoApp/LabelView.swift → DemoApp/View/AnnotationLabelView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,29 @@
import SwiftUI
import Foundation
import STTextView
import AnnotationsPlugin
import STAnnotationsPlugin

struct LabelView<T: LineAnnotation>: View {
let message: AttributedString
let action: (T) -> Void
let lineAnnotation: T
struct AnnotationLabelView : View {
@Environment(\.colorScheme) private var colorScheme

private let text: Text
private let annotation: MessageLineAnnotation
private let action: (MessageLineAnnotation) -> Void

init(_ text: Text, annotation: MessageLineAnnotation, action: @escaping (MessageLineAnnotation) -> Void) {
self.text = text
self.action = action
self.annotation = annotation
}

var body: some View {
Label {
Text(message)
text
.foregroundColor(.primary)
.frame(maxWidth: .infinity, alignment: .leading)
} icon: {
Button {
action(lineAnnotation)
action(annotation)
} label: {
ZStack {
// the way it draws bothers me
Expand All @@ -34,9 +42,12 @@ struct LabelView<T: LineAnnotation>: View {
}
.buttonStyle(.plain)
}
.background(Color.yellow)
.clipShape(UnevenRoundedRectangle(cornerRadii: RectangleCornerRadii(topLeading: 4, bottomLeading: 4, bottomTrailing: 0, topTrailing: 0), style: .circular))
.background(
annotation.kind.color.background(Color.white)
)
.clipShape(UnevenRoundedRectangle(cornerRadii: RectangleCornerRadii(topLeading: 2, bottomLeading: 2), style: .circular))
.labelStyle(AnnotationLabelStyle())
.shadow(radius: 1, x: 0.3, y: 0.5)
}
}

Expand Down Expand Up @@ -64,3 +75,17 @@ private struct AnnotationLabelStyle: LabelStyle {
}
}
}

private extension AnnotationKind {
var color: Color {
switch self {
case .info:
Color.accentColor.opacity(0.2)
case .warning:
Color.yellow.opacity(0.2)
case .error:
Color.red.opacity(0.2)
}
}

}
13 changes: 5 additions & 8 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,21 @@

import PackageDescription

let pluginName = "Annotations"
let pluginTargetName = "\(pluginName)Plugin"

let package = Package(
name: "STTextView-Plugin-\(pluginName)",
name: "STTextView-Plugin-Annotations",
platforms: [.macOS(.v12)],
products: [
.library(
name: "STTextView\(pluginName)Plugin",
targets: [pluginTargetName]
name: "STTextViewAnnotationsPlugin",
targets: ["STAnnotationsPlugin"]
)
],
dependencies: [
.package(url: "https://github.com/krzyzanowskim/STTextView", from: "0.8.22")
.package(url: "https://github.com/krzyzanowskim/STTextView", from: "0.8.23")
],
targets: [
.target(
name: pluginTargetName,
name: "STAnnotationsPlugin",
dependencies: [
.product(name: "STTextView", package: "STTextView")
]
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
Add the plugin package as a dependency of your application, then register/add it to the STTextView instance:

```swift
import AnnotationsPlugin
import STAnnotationsPlugin

// Implement AnnotationsDataSource protocol to provide annotations
annotationsPlugin = AnnotationsPlugin(dataSource: self)
let plugin = STAnnotationsPlugin(dataSource: self)

textView.addPlugin(
annotationsPlugin
)
// Add/register plugin in the STTextView instance
textView.addPlugin(plugin)
```

Check DemoApp for reference implementation

<img width="499" alt="Screenshot 2023-09-13 at 19 00 09" src="https://github.com/krzyzanowskim/STTextView-Plugin-Annotations/assets/758033/49f6392b-fbce-47e9-a6ee-f4828c095cbd">
<img width="499" alt="Demo9" src="https://github.com/krzyzanowskim/STTextView-Plugin-Annotations/assets/758033/ce1e2050-977e-4be9-b78c-81023d66b4f2">
14 changes: 0 additions & 14 deletions Sources/AnnotationsPlugin/AnnotationsDataSource.swift

This file was deleted.

Loading

0 comments on commit f347442

Please sign in to comment.