-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Text Refactor
The next major release (probably Abelour) will feature a refactor of the text handling parts to address the issues and limitations encountered.
Text handling and typesetting is one of the hardest and most complex piece of a UI toolkit. The current implementation was a good first step, but as the project has matured and developers have shared their feedback, it is clear that we're ready to take the next step.
- No rich text support; eg bulleted/numbered lists, headings, paragraphs, images, links
- No bi-directional support (LTR, RTL, mixed)
- Hard to convey semantic meaning of text; eg cannot configure widgets to be H1-H6, Subtitle, Body, Caption, et al. (https://gophers.slack.com/archives/CK5U4BU87/p1607524897399600, https://gophers.slack.com/archives/CK5U4BU87/p1610012275206000)
- No support for mixing styles; eg neither widget.Label nor widget.Entry cannot show a clickable hyperlink
- Difficult for devs to customize existing widgets; eg cannot create center-aligned, widget.Entry (https://gophers.slack.com/archives/CB4QUBXGQ/p1554989104009500, https://gophers.slack.com/archives/CK5U4BU87/p1603126884485900) <- Andy is not sure this is a problem to solve, as most of our widgets are not customisable in this way.
- Difficult for devs to create custom text widgets and access toolkit behaviour such as wrapping, selecting, and editing. For example, a dev cannot render multiple editable lines in a custom font without re-implementing textProvider and Entry.
- Performance is low as the entire text must be rendered - could be improved with "collectionification" (applying lessons learned from caches in collection widgets) - applies to TextGrid as well
The goal of this project is to develop a text handling solution that will have;
- Extensibility - developers can create custom widgets and leverage the text handling utilities
- Functionality - developers can provide a rich user experience
- Testability & Performance - developers can test and benchmark their apps across all supported languages, locales, and regions.
Possibly out of scope for a refactor project, but we need to keep in mind:
- Accessibility - users can read and write text via accessibility tools (eg screen readers)
- Internationalization - users can configure an app to their mother-tongue
Libraries of note that we should checkout to understand the complexities ahead:
- Harfbuzz (MIT) - https://harfbuzz.github.io/
- Pango (LGPL) - https://pango.gnome.org/
- Go Text Layout (LGPL) - https://github.com/benoitkugler/textlayout
- Partially completed Go library https://github.com/benoitkugler/textlayout (a manual port or harfbuzz and other features)
There are 3 main areas of scope when planning this project. Those areas that can be addressed by a shared library, areas specific to Fyne that must be addressed for this to work, and finally items that could be worked on later.
The following areas could/should be part of a shared library so that multiple Go toolkits can benefit
- Shaping of script/complex string of characters
- Splitting a string into constituent LTR/RTL sections so we can pass in BiDi content
- A common return type that can be rendered (rasterised - see golang.org's freetype Context.DrawString perhaps? or defined as a path)
These are core to refactoring text within Fyne that need to be worked on as a priority
- Text measurement cache, with baseline info etc not just size
- Removal of old textPresenter/textProvider etc - capture this in a cleaner API that can be encapsulated
- Layout of newlines, wrapping, elipses etc
- Layout of portions of text cut on style boundaries
- Supporting rich text description, for example illustration at https://github.com/fyne-io/fyne/issues/21
Areas of important work that could be addressed after the main refactor
- Loading UI translations
- Managing multiple language files
- Accessibility
- Content Hinting - enable devs to hint at the content of a UI Text element to leverage platforms APIs for providing suggestions (from dictionary or contacts), check spelling, keyboard tayloring (numeric vs alphanumeric, action button type, input language)
- Mathematical expressions
- Wrapping Binding - takes a mode, width, and string binding as inputs, then splits the text from the binding into lines less than or equal to the given length depending on the wrapping mode, providing as output a list binding which can be bound to a text line list collection. <- Andy notes here that whatever we do should not require binding, but could have binding helpers.
- Using a
strings.Builder
orbytes.Buffer
for providing an efficient way to append a lot of information to the string (like when typing). It would also open up for havingcanvas.Text
be anio.Writer
(andio.Reader
In the case of the buffer). - Text Measurement Cache - measuring text is a time consuming operation and is called frequently, we could see a performance increase by caching the result for a given input (text, font, size, and style).
- Adding
MeasureTextDistanceToBaseline
function (or something like this) alongsideMeasureText
or driver equivalent to enable future alignment according to widgets text baseline. - String Bundling - to assist in internationalization
fyne bundle
or a newfyne translate
could be used to bundle string resources into the binary in such a way that they can be keyed with language tag (eg. "en-US") and ID (eg. "welcome_message") - We will need to support at a minimum RTL (right to left) as well as the default LTR, but bi-directional will be required for a complete solution
- Shaping is exceptionally hard - I recommend everyone become familiar with Harfbuzz and pango
- Gio project is keen to collaborate on these problems so a shared solution could be created for Go text rendering (https://gophers.slack.com/archives/CM87SNCGM/p1617180821092100)