rajdeep / proton Goto Github PK
View Code? Open in Web Editor NEWPurely native and extensible rich text editor for iOS and macOS Catalyst apps
License: Other
Purely native and extensible rich text editor for iOS and macOS Catalyst apps
License: Other
Trying to build "proton-main.zip" or "proton-0.6.1.zip" ExampleApp fails with:
_Undefined symbols for architecture x86_64:
"OBJC_CLASS$PREditorContentName", referenced from:
objc-class-ref in JSONDecoder.o
objc-class-ref in JSONEncoder.o
objc-class-ref in ParagraphEncoder.o
objc-class-ref in WidthRangeAttachmentExampleViewController.o
objc-class-ref in AutogrowingTextField.o
objc-class-ref in PanelView.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Environment: macOS 10.15.7, Xcode 12.4.
I note that there does not seem to be any build target for ProtonExtensions in the Proton.xcworkspace.
I could find no documentation on how one is supposed to integrate Proton into a project. Shouldn't there be a documented Swift Package Manager URL?
Thank you for providing such a powerful and customizable library.
In the demo, there is a simple text processor demo: MakeupProcessing.
are there any plans to develop a complete set of Markdown text processing?
For example, support markdown bold, italic, quotation marks, lists, etc.
I am trying to store and set the data in EditorView to/from core data persistence.
I came across EditorView.contents()
to extract the array of EditorContent
that I could store to CoreData. However I'm not quite sure how to set this data to the EditorView from the persistent store? Is this the best way to handle data storage from the EditorView?
I don't just want to store the attributed string, but everything needed to recreate the layout of the editor view.
Many thanks for the great work being done with proton, this is a great library and I'm looking forward to where it can go.
I'm trying to get lists working. The attributed text looks the same as that produced by the example app, but the bullets don't appear, so I guess I'm missing a step somewhere. I've tried setting the listItem value to a bullet character and also the string "listItemValue".
I've traced through the code but can't see what's going wrong, although I did come across a TODO that questions the code at line 141 of LayoutManager.
Here's the attributed text for three lines:
Line 1{
NSColor = "<UIDynamicSystemColor: 0x60000318dc00; name = labelColor>";
NSFont = "<UICTFont: 0x7ffb9e42c4f0> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 12/24, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_listItem" = listItemValue;
}
{
NSColor = "<UIDynamicSystemColor: 0x60000318dc00; name = labelColor>";
NSFont = "<UICTFont: 0x7ffb9e42c4f0> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 12/24, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_blockContentType" = "Proton.EditorContent.Name(rawValue: "_newline")";
"_listItem" = listItemValue;
}Line 2{
NSColor = "<UIDynamicSystemColor: 0x60000318dc00; name = labelColor>";
NSFont = "<UICTFont: 0x7ffb9e42c4f0> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 12/24, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_listItem" = listItemValue;
}
{
NSColor = "<UIDynamicSystemColor: 0x60000318dc00; name = labelColor>";
NSFont = "<UICTFont: 0x7ffb9e42c4f0> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 12/24, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_blockContentType" = "Proton.EditorContent.Name(rawValue: "_newline")";
"_listItem" = listItemValue;
}Line 3{
NSColor = "<UIDynamicSystemColor: 0x60000318dc00; name = labelColor>";
NSFont = "<UICTFont: 0x7ffb9e42c4f0> font-family: "Helvetica"; font-weight: normal; font-style: normal; font-size: 12.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 12/24, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_listItem" = listItemValue;
}
Hello! Congratulations and thanks for this great UITextView extension.
It seems that lists are not encoded/decoded yet, do you think that will be supported soon? ;)
Best regards,
Franky
When trying to include Proton in my project using SPM I get the following error message:
target at '[...]/SourcePackages/checkouts/proton/Proton/Sources' contains mixed language source files; feature not supported
I only get this issue on the master branch. Installing from the 0.5.0 tag works fine.
Trying to add a list in between two existing paragraphs results in an unexpected behaviour. Executing the ListCommand
renders an indented bullet point between the two paragraphs but as soon as a character is entered, both the bullet point and the indentation disappear.
Repro steps:
ListCommand
to start a new listExpected behaviour:
The entered test is drawn next to the indented bullet point
Actual behaviour:
The bullet point and the indentation disappears.
I'm looking into how to support multiple list formats in the same editor. At minimum it feels natural to support ordered and unordered lists in one editor. Before going too far, is this something already in progress? Would it be a welcome addition?
Setting attributedText containing an attribute containing strikethrough results in strikethrough applied to all attributes. Setting the text with an attribute containing underline results in strikethrough=0 appearing in the other attributes. Maybe the fact that both underline and strikethrough attributes use the same value type - namely NSUnderlineStyle.single.rawvalue?
Here are dumps taken from the setter at line 381 of EditorView.attributed text.
First the underline:
(lldb) po newValue
This {
EasiStyle = "Easiwriter.StyleIdentifier(name: Optional("title1"), id: Optional("401D4EBD-B20F-4DE8-8A7F-A32A77F73CB4"))";
NSColor = "<UIDynamicSystemColor: 0x600001eb57c0; name = labelColor>";
NSFont = "<UICTFont: 0x7fbb4fd6f4e0> font-family: "Helvetica Neue"; font-weight: bold; font-style: normal; font-size: 28.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 10, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)'";
}test{
EasiStyle = "Easiwriter.StyleIdentifier(name: Optional("underline"), id: Optional("16C65DC4-94FB-4851-96BE-8FE4FF04EC72"))";
NSColor = "<UIDynamicSystemColor: 0x600001eb57c0; name = labelColor>";
NSFont = "<UICTFont: 0x7fbb4fd6f4e0> font-family: "Helvetica Neue"; font-weight: bold; font-style: normal; font-size: 28.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 10, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)'";
NSUnderline = 1;
}
(lldb) po richTextView.attributedText
▿ Optional
Now the strikethrough case:
(lldb) po newValue
This {
EasiStyle = "Easiwriter.StyleIdentifier(name: Optional("title1"), id: Optional("401D4EBD-B20F-4DE8-8A7F-A32A77F73CB4"))";
NSColor = "<UIDynamicSystemColor: 0x600001eb57c0; name = labelColor>";
NSFont = "<UICTFont: 0x7fbb4fd6f4e0> font-family: "Helvetica Neue"; font-weight: bold; font-style: normal; font-size: 28.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 10, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)'";
}test{
EasiStyle = "Easiwriter.StyleIdentifier(name: Optional("strikethrough"), id: Optional("3B68996A-E9E7-497D-B20C-B4A1DA843DE3"))";
NSColor = "<UIDynamicSystemColor: 0x600001eb57c0; name = labelColor>";
NSFont = "<UICTFont: 0x7fbb4fd6f4e0> font-family: "Helvetica Neue"; font-weight: bold; font-style: normal; font-size: 28.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 10, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)'";
NSStrikethrough = 1;
}
(lldb) po richTextView.attributedText
▿ Optional
Hi,
I really enjoy proton :). I was wondering if you have any plans to create a new tag soon?
SE Proposal: https://forums.swift.org/t/modify-accessors/31872
Talk: https://www.youtube.com/watch?v=BXJIIQ-B4-E
When this lands in Swift, we should leverage modify
for better performance. Especially accessing text:
var attributedText: NSAttributedString {
get { editor.attributedText }
set { editor.attributedText = newValue }
modify { yield &editor.attributedText }
}
Note: We can use _modify
today... but it would break when it changes.
Add a line of text and press return
Add another line of text
Create a multi-level list in the middle of these two lines (preferable 5-6 levels deep)
Hit enter at the last element of the list
The list item is outdented
Keep doing it all the way until all lists are exited.
Observed:
Selected range moves outside list at some point without exiting all the levels.
Expected:
List should outdent all the way to zeroth level without loosing focus.
Could you please support SWiftUI framework?It will be pretty good~ Thanks for your great job.
Steps to reproduce:
.foregroundColor
attribute.systemBlue
colorLooks like setting . foregroundColor
attribute also changes typingAttributes
and textColor
properties of UITextView
. Then UITextView
uses these properties for further input. Because there is no text before this range where other attributes can be inherited.
Setting typingAttributes
right after cleaning . foregroundColor
doesn't help.
Tested in demo project. This is default UITextView
behaviour.
I believe this will be important in two scenarios.
Imagine two paragraphs with a Panel in between them which contains a paragraph worth of content. If my cursor is half way along the top paragraph, tapping down should move to “the next line” which is either inside the same paragraph or inside the Panel. I’d expect my cursor to jump into the middle of the first line of the Panel.
Imagine we build a Table with a 2-dimensional array of Editors. If I tap down in a cell, I’d expect the cursor to move into the editor below. Similarly, if I move my cursor to the end of the content within a cell, tapping right should jump to the next cell to the right.
I believe we can leverage the protocol used for “us focusable”.
Given that there is no tab key on the screen kbd have you any idea how to simulate one given that List Processing depends on it. I can set up an item using UIMenuController, but can't work out how to invoke handleKeyCommand
.
This repo currently contains the core Editor as a framework and the example project. I believe there is space for a framework which contains text processors and commands for common editor behaviour.
Implementing the Text Processor for markdown (* _ #) would be great. Additionally and separately we can co-develop a Command for allowing the Integrator to implement a key command or UIButton action.
These ProtonExtensions are optional and can be ignored by the Integrator - but it is assumed that these will be valuable to most Editor usages. The Example app in this repo will use these extensions, as well as demoing some exotic examples not suitable for production.
Before TextStorage replacement in #66 deleteBackward
caused didCompleteLayout
layout manager delegate call which has updatePlaceholderVisibility
inside.
However, after this TextStorage update didCompleteLayout
isn't being called anymore and after removing the last character placeholder doesn't appear.
Trying to figure out why it happens 🤔
My app needs to respond to text changes in order to update its model data. I have implemented func editor(_ editor: EditorView, didChangeTextAt range: NSRange)
to do this. If I enter some text, place the caret anywhere but the end, then enter a character, the character goes in the correct place but the caret is moved to the end. If I remove the delegate method it works OK (but obviously doesn't update the model). My delegate method does not invoke any Proton methods.
I have tried tracing through the code but can't pinpoint why this is happening, but it looks like it has to do with the way delegate methods are handled.
Is this meant to be !text.isEmpty
?
That is the same as:
if text.isEmpty, (range.length > 0 || range == .zero) {
That is the same as:
if text.isEmpty {
Hi, sorry for bombarding you with issues recently.
I am trying to save the attributedString created by Proton to Core Data. To save the NSAttributedString
to core data I'm converting it to NSData
using an NSSecureUnarchiveFromDataTransformer
. This requires that everything conform to NSSecureCoding
before being transformed into NSData
. Here lies the problem
I believe the custom NSAttributedString.Key
- in my case "_blockContentType" came up a lot - don't conform to NSSecureCoding
hence not allowing the NSAttributedString
to be stored to core data. Would it be possible to make it conform in some way?
How could I use proton in a swiftui based macos app? It would be great if there's such an example.
I want to let the user create both ordered and unordered lists but I'm not sure how to support this.
I tried adding a @Binding
to the EditorListFormattingProvider but for obvious reasons this affects all lists in the EditorView:
class ListFormattingProvider: EditorListFormattingProvider {
@Binding var listStyle: ListStyle
init(listStyle: Binding<ListStyle>) {
_listStyle = listStyle
}
let listLineFormatting: LineFormatting = LineFormatting(indentation: 25, spacingBefore: 0)
func listLineMarkerFor(editor: EditorView, index: Int, level: Int, previousLevel: Int, attributeValue: Any?) -> ListLineMarker {
switch listStyle {
case .bullet:
return BulletSequenceProvider().value(at: index)
case .numbered:
if level == 1 {
return NumberSequenceProvider().value(at: index)
} else {
return DashSequenceProvider().value(at: index)
}
}
}
}
Is there a way to do this?
Thank you
return UIFont(descriptor: updatedFontDescriptor, size: pointSize)
This happens in both adding(...)
and removing(...)
IMHO I think editor.enableSelectionHandles should default to false.
Steps:
I suspect that before calling deleteBackward
in parent RichTextView selectedRange
hasn't been update and instead of empty range from 0
position contains empty range from 1
position. Not sure what's the right way to update this property 🤔
Hi,
Is there a way to ask the framework, for example the ListTextProcessor if the selected range is a list?
This would be good if you have a control to toggle lists on/off.
Thanks for a great framework!
Jonathan
I am using Proton with swiftUI through a UIRepresentableView. I am having trouble enabling the scroll functionality when the editor reaches its container size.
I set up my view this way:
struct RepresentableRichTextEditor: UIViewRepresentable {
@Binding var attributedText: NSAttributedString
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
let commandExecutor = EditorCommandExecutor()
func makeUIView(context: Context) -> UIView {
let editor = EditorView()
editor.delegate = context.coordinator
//This forces the editor view to scroll however disables the auto layout provided by the representable view
//editor.translatesAutoresizingMaskIntoConstraints = false
//editor.maxHeight = 300
EditorViewContext.shared.delegate = context.coordinator
editor.setFocus()
return editor
}
func updateUIView(_ uiView: UIView, context: Context) {
//Update logic here
}
}
I have tried using .frame(height) in the parent SwiftUI View but this didn't seem to affect the underlying editorView.
Does anyone know how to get scrolling working with SwiftUI?
Many thanks
My app uses a style mechanism similar to the one in Pages. To this end it maintains model data describing the style structure of each document. This data is used to generate the attributed text whenever necessary. The issue I have faced is that Proton keeps the nesting structure of lists close to its chest, so it is impossible to regenerate it from model data. I have resolved this for now by saving a copy of the attributed text for each list and replacing it when the document is reloaded/updated. This is not ideal, but does work.
The other issue that I have yet to resolve is how to detect (a) double returns terminating a list and (b) backspacing into a list. It would be great if events could be signalled/state maintained so that these changes can be identified. Maybe this is already possible and I just haven't found it.
When using SPM to install proton 0.5.0, I cannot access BoldCommand(), ItalicCommand(), etc or anything within the ProtonExtensions folder.
To recreate issue:
import Foundation
import Proton
let command = BoldCommand()
I have implemented my own EditorListFormattingProvider
. Looking at your code it looks like I need to have a LayoutManagerDelegate
that overrides listLineMarkerFor
somehow. Is that correct?
My app embeds an EditorView in a SwiftUI UIViewRepresentable struct which is the documented way to use UIKit views. The problem is that the text does not scroll when it reaches the bottom of the view. However, if I close the view and reopen it things wok as expected.
I have found that AutogrowingTextView.LayoutSubviews is only called once when the view opens, so isScrollEnabled is left set to false. Closing and reopening the view sorts this because there is sufficient text available.
The problem seems to be the layoutConstraints because the console contains the following immediately after EditorView.setup is called.
I can overcome the issue by setting isScrollEnabled after every keystroke.
2021-08-08 10:15:17.067778+0100 Easiwriter[3611:91422] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSAutoresizingMaskLayoutConstraint:0x60000243ae40 h=--& v=--& Proton.EditorView:0x7fb90d839800.height == 759.333 (active)>",
"<NSLayoutConstraint:0x60000240ecb0 V:|-(0)-[Proton.RichTextView:0x7fb90d0b4800] (active, names: '|':Proton.EditorView:0x7fb90d839800 )>",
"<NSLayoutConstraint:0x60000240eb70 Proton.RichTextView:0x7fb90d0b4800.bottom == Proton.EditorView:0x7fb90d839800.bottom (active)>",
"<NSLayoutConstraint:0x600002409ae0 Proton.RichTextView:0x7fb90d0b4800.height >= 926 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600002409ae0 Proton.RichTextView:0x7fb90d0b4800.height >= 926 (active)>
Let's imagine we have VC that holds parent EditorView. It subscribes to its changes via EditorViewDelegate
. However, it doesn't receive notifications from nested editors in attachments.
Some posible use cases:
Some possible solutions:
EditorView
in EditorContentView
and pass main EditorViewDelegate
to nested EditorView
EditorContentView
so this view can get notifications and provide specific callbacks like in PanelViewDelegate
examples.Maybe, I missed some other solution. And another question is should it be part of the Core library like some utility code?
I am trying to add Proton to a SwiftUI app I've been developing for about a year. It contains a UITextView editor embedded in a UIViewRepresentable class. I want to have bullet/numbered lists and embedded images, which is not easy as I'm sure you know. In fact, trying to solve the problem is how I found Proton. Congratulations on a great piece of work!
I have experimented with the ExampleApp to get a feel for things and have managed to embed a Proton EditorView in my app as a replacement for the UITextView. I can type into it, but that's about all. What I am missing is the Proton equivalents for UITextViewDelegate methods. I am also struggling to detect the EditorView's firstResponder state.
Finally, as you know, the Master package doesn't compile because of the mixed language issue, so I am using version 0.5 at the moment.
How to run the ExampleApp?
Proton fails to compile with Xcode 13.0 beta. Error: CompileSwiftSources failed with nonzero exit code
I'd like to filter some types of commands for some editors. registeredCommands
seems the right place to do it.
However, EditorView::isCommandRegistered
checks for instance equality but not for type equality. So, for example, if I don't have exact instance of command (mention with some userId is a good example) I can't register it and then it won't be executed.
Theoretically, I can skip this check by calling directly command.execute(on editor: EditorView)
, but I don't know active editor. So if I need to execute command from keyboard shortcut or toolbar button I need to find active EditorView
first, which is quite hard (see #27)
Hi,
This bug can be reproduced using the example app using the "Commands" example.
The letter will now only be italic.
The code in question is the following in FontTraitToggleCommand.
if selectedText.length == 0 {
guard let font = editor.attributedText.attribute(.font, at: editor.selectedRange.location - 1, effectiveRange: nil) as? UIFont else { return }
editor.typingAttributes[.font] = font.toggled(trait: trait)
return
}
It only looks at the attributes for the letter before the selected range. If you previously set something to editor.typingAttributes
without typing it will be reset.
The code in the previous if-statement does the correct thing for this case.
if editor.isEmpty || editor.selectedRange == .zero {
guard let font = editor.typingAttributes[.font] as? UIFont else { return }
editor.typingAttributes[.font] = font.toggled(trait: trait)
return
}
Hi, this is a crash that is 100% reproducible in the sample app in the "Commands" example.
2021-05-14 08:34:40.949847+0200 ExampleApp[4044:79319] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSBigMutableString _getBlockStart:end:contentsEnd:forRange:stopAtLineSeparators:]: Range {4, 0} out of bounds; string length 3'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff20422fba __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff20193ff5 objc_exception_throw + 48
2 Foundation 0x00007fff2084c066 -[NSString _getBlockStart:end:contentsEnd:forRange:stopAtLineSeparators:] + 900
3 Foundation 0x00007fff2084c66a -[NSString paragraphRangeForRange:] + 75
4 UIKitCore 0x00007fff24a224c0 __58-[UITextInputController setBaseWritingDirection:forRange:]_block_invoke + 86
5 UIFoundation 0x00007fff23a5584c -[NSTextStorage coordinateEditing:] + 35
6 UIKitCore 0x00007fff24a22361 -[UITextInputController setBaseWritingDirection:forRange:] + 152
7 UIKitCore 0x00007fff24a42023 -[UITextView setBaseWritingDirection:forRange:] + 68
8 UIKitCore 0x00007fff244bab9b -[UIKeyboardImpl(UIKitInternal) setInitialDirection] + 294
9 UIKitCore 0x00007fff244a5ffd -[UIKeyboardImpl completeAddInputString:generateCandidates:] + 124
10 UIKitCore 0x00007fff244a5f44 __100-[UIKeyboardImpl addWordTerminator:afterSpace:afterAcceptingCandidate:elapsedTime:executionContext:]_block_invoke + 177
11 UIKitCore 0x00007fff244cba5d -[UIKeyboardTaskExecutionContext returnExecutionToParentWithInfo:] + 109
12 UIKitCore 0x00007fff2449a41c __55-[UIKeyboardImpl handleKeyboardInput:executionContext:]_block_invoke_2 + 799
13 UIKitCore 0x00007fff244cd2a1 -[UIKeyboardTaskEntry execute:] + 147
14 UIKitCore 0x00007fff244cbf35 -[UIKeyboardTaskQueue continueExecutionOnMainThread] + 310
15 Foundation 0x00007fff2085cd08 __NSThreadPerformPerform + 204
16 CoreFoundation 0x00007fff20390ede __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
17 CoreFoundation 0x00007fff20390dd6 __CFRunLoopDoSource0 + 180
18 CoreFoundation 0x00007fff2039029e __CFRunLoopDoSources0 + 242
19 CoreFoundation 0x00007fff2038a9f7 __CFRunLoopRun + 875
20 CoreFoundation 0x00007fff2038a1a7 CFRunLoopRunSpecific + 567
21 GraphicsServices 0x00007fff2b874d85 GSEventRunModal + 139
22 UIKitCore 0x00007fff246c14df -[UIApplication _run] + 912
23 UIKitCore 0x00007fff246c639c UIApplicationMain + 101
24 ExampleApp 0x000000010e19c8cb main + 75
25 libdyld.dylib 0x00007fff2025abbd start + 1
26 ??? 0x0000000000000001 0x0 + 1
)
libc++abi: terminating with uncaught exception of type NSException
Currently, we have contents()
method that returns [EditorContent]
. But its case contains only view, but not an attachment.
public struct EditorContent {
public let type: EditorContentType
...
}
public enum EditorContentType {
case attachment(name: EditorContent.Name, contentView: UIView, type: AttachmentType)
....
}
So if user creates Attachment
, then put it into editor there is no designated way to get them back (well except iterating over attributedString of course).
I can create small PR if @rajdeep approves the idea 🙂
Just upgraded Xcode and Proton's ObjC code has stopped compiling - NS_ASSUME_NONNULL_BEGIN unknown.
I've tried going back to v0.5.0 but that won't compile because ListParser is not implemented in that version. So I'm stuck, although I could copy the ListParser code as a workaround.
One of the use cases: show typeahead picker while typing following the cursor in the current editor. It should work in nested editors as well.
EditorCommandExecutor
uses internal RichTextEditorContext
to get activeTextView
. However, integrators don't have access to EditorViewContext
internal properties.
The only way for now to get active editor I found is somehow detect active firstResponder and try to cast its superview to EditorView
. Which is obviously a) not very handy b) exposes internal details of EditorView
To show selected state (formatting option or selected type ahead processor) in a toolbar (by highlighting corresponding button) or to show/hide options picker on changing cursor position.
As a suggestion EditorView
can ask TextProcessor
for active text processors for current range. Maybe, it requires new method in TextProcessing
that return whether this processor should be active in selected range.
I have an issue with displaying a list. The list is displayed OK until I hit enter, after which the output is incorrect.
Here's the list before typing Enter:
Here's the list after:
And here's the NSAttributedString. There is a custom attribute called EasiwriterStyle, but that shouldn't upset it should it?
One
{
EasiwriterStyle = "Easiwriter.StyleIdentifier(name: \"Body\", id: \"325A872C-7289-4319-8DEB-CE06B111E956\")";
NSColor = "<UIDynamicSystemColor: 0x600002ae5600; name = labelColor>";
NSFont = "<UICTFont: 0x7feff6909f30> font-family: \"UICTFontTextStyleBody\"; font-weight: normal; font-style: normal; font-size: 17.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_blockContentType" = "Proton.EditorContent.Name(rawValue: \"_newline\")";
"_listItem" = ListValue;
}Two{
EasiwriterStyle = "Easiwriter.StyleIdentifier(name: \"Body\", id: \"325A872C-7289-4319-8DEB-CE06B111E956\")";
NSColor = "<UIDynamicSystemColor: 0x600002ae5600; name = labelColor>";
NSFont = "<UICTFont: 0x7feff6909f30> font-family: \"UICTFontTextStyleBody\"; font-weight: normal; font-style: normal; font-size: 17.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_listItem" = ListValue;
}
{
EasiwriterStyle = "Easiwriter.StyleIdentifier(name: \"Body\", id: \"325A872C-7289-4319-8DEB-CE06B111E956\")";
NSColor = "<UIDynamicSystemColor: 0x600002ae5600; name = labelColor>";
NSFont = "<UICTFont: 0x7feff6909f30> font-family: \"UICTFontTextStyleBody\"; font-weight: normal; font-style: normal; font-size: 17.00pt";
NSParagraphStyle = "Alignment 0, LineSpacing 1, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 1, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0";
"_blockContentType" = "Proton.EditorContent.Name(rawValue: \"_newline\")";
"_listItem" = ListValue;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.