(Appkit) Tab Insertion Inside of Nstextblock

(AppKit) Tab insertion inside of NSTextBlock

Apparently a tab in a text block moves the insertion point to the next text block, which makes sense in a table. Workaround: subclass NSTextView, override insertTab and insert a tab.

override func insertTab(_ sender: Any?) {
insertText("\t", replacementRange: selectedRange())
}

How to relayout content of NSTextView so that my tab characters are drawn with width of 4 characters

Douglas Davidson of Apple provided me with the clues to get to the answer via the cocoa-dev@apple.lists.com email list.

I've resolved the problem by updating the updateMyTextViewTextAttributes function like so:

- (void)updateMyTextViewTextAttributes
{
   NSMutableParagraphStyle* paragraphStyle = [[myTextView defaultParagraphStyle] mutableCopy];

   if (paragraphStyle == nil) {
       paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
   }

   float charWidth = [[myFont screenFontWithRenderingMode:NSFontDefaultRenderingMode] advancementForGlyph:(NSGlyph) ' '].width;
   [paragraphStyle setDefaultTabInterval:(charWidth * 4)];
   [paragraphStyle setTabStops:[NSArray array]];

   [myTextView setDefaultParagraphStyle:paragraphStyle];

   NSMutableDictionary* typingAttributes = [[myTextView typingAttributes] mutableCopy];
   [typingAttributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
   [typingAttributes setObject:scriptFont forKey:NSFontAttributeName];
   [myTextView setTypingAttributes:typingAttributes];

/** ADDED CODE BELOW **/
   NSRange rangeOfChange = NSMakeRange(0, [[myTextView string] length]);
   [myTextView shouldChangeTextInRange:rangeOfChange replacementString:nil];
   [[myTextView textStorage] setAttributes:typingAttributes range:rangeOfChange];
   [myTextView didChangeText];

[paragraphStyle release];
[typingAttributes release];
}

recognizing key event in entire window

Step 1 is to set your View Controller to be a delegate of NSTextField. If you use nibs or storyboard it will look like this:

Sample Image

Basically ctrl drag to view controller and set delegate.

Then you can react on text changed in your view controller:

import Cocoa

class ViewController: NSViewController {

override func controlTextDidChange(obj: NSNotification) {

guard let textField = obj.object as? NSTextField else { return }

if textField.stringValue.characters.last == " " {
print(textField.stringValue.componentsSeparatedByString(" "))
}

}
}

It will print out array of words which were separated by space:

["a", ""]

["a", "boy", ""]

["a", "boy", "came", ""]

["a", "boy", "came", "to", ""]

["a", "boy", "came", "to", "school", ""]

You may need to remove last item from the array:

     override func controlTextDidChange(obj: NSNotification) {

guard let textField = obj.object as? NSTextField else { return }

if textField.stringValue.characters.last == " " {
print(textField.stringValue.componentsSeparatedByString(" ").filter { $0 != "" })
}

}

For NSTextView the logic is similar. Ctrl drag from text view to set view controller as delegate:
Sample Image

Then use the following code:

  func textDidChange(obj: NSNotification) {

guard let textView = obj.object as? NSTextView else { return }
guard let stringValue = textView.textContainer?.textView?.string else { return }

if stringValue.characters.last == " " {
print(stringValue.componentsSeparatedByString(" ").filter { $0 != "" })
}

}

It will work fine:

Sample Image

Adding margins to TextEditor

so I found a simple solution and hard one.

1. Simple one

    import SwiftUI

extension NSTextView {
open override var frame: CGRect {
didSet {
// Top inset
textContainerInset = NSSize(width: 0, height: 72)
// Left fragment padding <<< This is what I was looking for
textContainer?.lineFragmentPadding = 72
}
}
}

struct TextEditingView: View {
@State private var fullText: String = "One \nTwo \nThree"

var body: some View {
TextEditor(text: $fullText)
.frame(width: 720, height: 480)
.font(.system(size: 24, design: .monospaced))


}

}

As result you get this:
TextEditor with margins on left

A repository of the demo:
https://github.com/yaosamo/Swift-TextView-Demo

2. Second solution

Using NSParagraphStyle, headIndent, firstLineHeadIndent
I believe this is how indents on Pages on Mac implemented. I do not know tho how they persist default indent. If you open ruler you will see that it set to 1 and you can't go below it.

Using code of
(AppKit) Tab insertion inside of NSTextBlock

class ParagraphStyle {

let bgColor: NSColor
let paragraphStyle: NSParagraphStyle

init(bgColor: NSColor) {
self.bgColor = bgColor
//Set paragraph style
self.paragraphStyle = {
let mutableParagraphStyle = NSMutableParagraphStyle()
let specialBlock = CustomTextBlock(bgColor: bgColor)
mutableParagraphStyle.textBlocks.append(specialBlock)
mutableParagraphStyle.headIndent = 50 // Add indent here
let style = mutableParagraphStyle as NSParagraphStyle
return style
}()
}}

you can add headIndent to text style. And it will work for copy that you insert there. Problem like I said if you start typing Indents break and I don't know how to preserve it.

First one works for me exactly how I want it. Next will figure out how to use headIndent/FirstlineheadIndent

Thanks to this community I was able to find a solution! Don't give up you also can make it! :D



Related Topics



Leave a reply



Submit