Skip to content

Move Layout Updates to NSTextStorage Delegate #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

thecoolwinter
Copy link
Contributor

@thecoolwinter thecoolwinter commented Apr 8, 2025

Description

Detailed Changes

  • Makes TextLayoutManager a text storage delegate.
  • Updates TextLayoutManager to use the text storage methods to update internal state.
  • Remove TextLayoutManager's layout transaction capabilities.
  • Updates replaceCharacters to reflect the new layout manager requirements.
  • Moves layout back into a CATransaction, now that method ordering is slightly different, macOS might call layout on the text view while we're laying out text. Doing that in a transaction ensures that doesn't happen and layout is atomic.

Tests

  • Adds tests to the layout manager to ensure modifications are kept valid.
  • Adds tests for rects(in:) to ensure it doesn't invalidate layout information.
  • Adds a test ensuring editing text contents in a text view delegate callback doesn't break layout.

Discussion

This change untangles the text layout updating from text view code. Right now the hierarchy looks like this:

(Update occurs)
TextView -> Text Layout Update
         -> Text Storage Update

This isn't great, because it means anything that modifies the text outside of the text view must remember to update layout, selection, send notifications, and do it all in the right order. I'd like to move it to this hierarchy:

(Update occurs)
TextView -> Text Storage Update -> Text Layout Update

This way any component can modify the text storage, and that will be reflected in the layout manager. There's a myriad of small bugs in our editor right now that are effected by this.

One key result of this change is the ability to correctly modify text in textView delegate callbacks. Right now, if the text is updated in a callback, it can lead to a recursive layout call to the text layout manager. This leads to bad internal state, and bugs where entire sections of text disappear entirely.

This also comes with a performance boost. Since NSTextStorage automatically coagulates text changes, we'll perform less layout passes for each edit, and components can make use of the text storage's transaction capabilities to only cause layout after all edits have been applied in a transaction.

Note

This change cannot be applied to the selection manager, the selection manager needs much more fine-grained information about edits than the layout manager to correctly update itself.
@hi2gage and I have also discussed some modifications to the selection API, since keeping it in sync with the text storage makes less sense than the layout manager.

Related Issues

Checklist

  • Add Tests
  • I read and understood the contributing guide as well as the code of conduct
  • The issues this PR addresses are related to each other
  • My changes generate no new warnings
  • My code builds and runs on my machine
  • My changes are all related to the related issue above
  • I documented my code

Screenshots

@thecoolwinter thecoolwinter changed the title Move Text Layout, Selection Edits to Storage Delegates Move Text Layout Edits to Storage Delegates Apr 9, 2025
@thecoolwinter thecoolwinter changed the title Move Text Layout Edits to Storage Delegates Move Layout Updates to NSTextStorage Delegate Apr 9, 2025
@thecoolwinter thecoolwinter merged commit 5114e0d into CodeEditApp:main Apr 9, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant