clay icon indicating copy to clipboard operation
clay copied to clipboard

feat: Text Outline/Stroke

Open Nul-led opened this issue 6 months ago • 2 comments

Im not sure if this is supported by renderers other than html and canvas2d but would still be neat to have.

Its possible that the line width would need to be accounted for when measuring text.

Nul-led avatar Jul 11 '25 01:07 Nul-led

I have a kind of stupid solution to achieve custom line breaking logic and text styling with Clay. It is kind of hacky and has its own limitations. But maybe someone will be inspired.

	struct ParsedText
	{
		static constexpr uint32_t MaxTextSize = 1024;
		// Hacky Struct to get custom line break logic and styling to work with Clay Text.
		// 
		// This LineBreakText gets fed into the Clay Text Parser for Layouting
		// It is not used for rendering, but for parsing the text into elements
		// It contains the line break information for the actual text by having NEWLINES and SPACES in the corresponding positions
		// where the text should or can be broken.
		// 
		// To get the actual text, the pointer needs to be advanced by MaxTextSize or
		// the base Pointer of the String Slice can be casted to ParsedText to get the full struct back >:)
		//
		// Whenever Clay calls GetTextSize we can reconstruct this Struct and measure the actual String Slice with styling information.
		// Whenever Clay emits a Text RenderCommand we can also reconstruct this Struct and render with correct style.
		//
		// This way Style changes, complex line break Logic can be injected into clay
		// THE ONLY change that needs to be done in Clay is the internal hashing of the string for caching.
		// We solve this by a callback that returns a boolean and a hash value.
		// If the boolean is true, the hash value is used as the ID for the text.
		// If it is false, Clay can use its own hashing mechanism.
		//
		// On top of that our own GetSize and TextRenderCommand implementations need to differeantiate between
		// Standard CLAY_TEXT calls and our own Styled Text.
		// We solved this by checking if the pointer of the Clay String is within the ParsedText Array Memory range.
		// This way we can use the same Clay Text API for both our own styled text and the standard Clay Text.

		// Bad example. Chinese characters, emojis, and other complex character would make more sense.
		// Example: "Hello World\nThis is a test text.\n\nThis is a new line."
		char LineBreakText[MaxTextSize];//Example Contents "aaaaa aaaaa\naaaa aa a aaa aaaaa\n\naaaa aa a aaa aaaaa"
		char String[MaxTextSize];
		int8_t Styles[MaxTextSize];

		TextStyleColorData ParsedStyles[32];
		uint32_t TitleFont = 0;
		uint32_t DataLength = 0;
	};

The basic idea is to somehow reconstruct style information from clay calls. For custom linebreak logic [e.g. other languages] I give clay a string with placeholder letters, spaces and linebreaks so it can do its usual thing. Very hacky but enables a lot of text features that otherwise would not be possible.

Jannes1000Orks avatar Aug 12 '25 10:08 Jannes1000Orks

Got it working!

Image

Jannes1000Orks avatar Aug 12 '25 18:08 Jannes1000Orks