freya icon indicating copy to clipboard operation
freya copied to clipboard

proposal: Unified text primitives/nestable text elements

Open Tropix126 opened this issue 5 months ago • 4 comments

Consider this a mini-RFC. I think I may have proposed it last year on discord, but it probably wasn't conveyed as well as it could have been.

Freya's Text Primitives

Currently freya has three elements (primitives) related to rendering text:

  • label
  • paragraph
  • text

These all render similar (or identical things), but serve slightly different purposes at the same time:

  • label is generally used for "one-off" cases where you just need a single line of contigiously-styled text. It's simple and used for most things text-related where you just need one style. label cannot take children other than a raw string.
  • paragraph/text are used when you need differently styled text nodes in the same text layout at once. For example, if you wanted emphasized text, you would use a paragraph/text combo.
paragraph {
    line_height: "2",
    text {
        font_size: "20",
        font_style: "normal",
        "Normal "
    }
    text {
        font_size: "20",
        font_style: "italic",
        "Italic "
    }
    text {
        font_size: "20",
        font_style: "oblique",
        "Oblique"
    }
}

The case for a unified element

All three of these elements serve the same purpose -- drawing text, yet they have different arbitrary restrictions or API complexity. Currently, paragraph could almost certainly replace label in all cases, but that would be annoying to deal with due to having paragraph split into a pair of elements. Instead, what if there was a single simplified text primitive that served all the usecases. This would have some advantages:

  • Freya wouldn't have to individually handle three elements in its native APIs, and all rendering and node-related code for text would be unified under a single primitive, reducing internal complexity.
  • It's easier to teach to users. The difference between label and paragraph wouldn't need to be explained.
  • It would remove any accidental differences in behavior or attributes between the elements as a result of feature additions to one element but not the other.
  • It better fits the paradigm of having a set of "base elements". Generally most UI is visually comprised of text and rounded rectangles if you break it into its base parts, and this better fits that paradigm.

A new text element

This proposal presents a new text element that takes the place of paragraph. In its simplest form, it looks a lot like the current label.

text {
    "Hi",
}

text elements can contain bare strings or spans as children.

text {
    "Hello",
    span {
        font_weight: "bold",
        "World"
    }
}

The span element

span is basically a replacement for the previous text element, and defines a styled span of text in SkParagraph. Unlike the parent text, they can only take inline text styling properties such as font_size, font_weight, etc...

  • A span that is not a child of text is meaningless and does not render anything at all.
  • spans inherit the text styling properties of their parent text.
  • A bare string child of text is equivalent to a span with no styles.

This reduces the number of text elements from 3 to 2 and provides a clear distinction between a SkParagraph and a text span in the paragraph. It also reduces the API complexity of using styled spanned text while also removing label.

Past Work

This is very similar to the approaches of React Native and HTML:

<span>
    I am
    <span style="font-weight: bold;">
        bold
    </span>
</span>

Unlike HTML, there is a distinction between the text's block element (in Freya's case a SkParagraph) and the inline children.

Remaining Questions

  • Should spans be able to have span children to inherit styles? (probably not initially I guess...)

Tropix126 avatar Aug 31 '24 15:08 Tropix126