Expand description
Parley2 is an experimental high level text library based on Parley.
The goal is to allow any winit/wgpu program to have full-featured text and text editing with minimal integration effort.
Most GUI programs or toolkits, games, etc. don’t have any advanced requirements for text: they just want basic text boxes that “work the same way as everywhere else” (browsers, native operating system GUIs, etc.).
If all that is available is a relatively low level library such as Parley, all these projects will have to do a large amount of repeated work, needlessly raising the barrier of entry to GUI programming.
§Limitations
This library is not ready for use. It currently does not reach its own goal of “full featured text”.
-
Accessibility is supported in Parley itself but not in Parley2, because of my personal lack of familiarity with the subject.
-
Parley2 currently uses the built-in Swash CPU rasterizer and a basic homemade atlas renderer to actually show the text on screen. The performance is acceptable but it is not as good as it could be. There is also a questionable bit of unsafe code to circumvent some performance problems in Swash. This might eventually be fixed by switching to the new “Vello Hybrid” renderer.
-
Parley itself has some limitations, but they will be probably fixed soon.
§Usage
See the basic.rs
example in the repository to see how the library can be used.
The two main structs are:
Text
- manages collections of text boxes and stylesTextRenderer
- renders the text on the GPU
§Imperative Mode
The main way to use the library is imperative with a handle-based system:
let mut text = Text::new();
// Add text boxes and get handles
let handle = text.add_text_box("Hello".to_string(), (10.0, 10.0), (200.0, 50.0), 0.0);
let edit_handle = text.add_text_edit("Type here".to_string(), (10.0, 70.0), (200.0, 30.0), 0.0);
// Use handles to access and modify the boxes
text.get_text_box_mut(&handle).set_style(&my_style);
text.get_text_box_mut(&handle).raw_text_mut().push_str("... World");
text.get_text_box_mut(&handle).set_hidden(true);
// Manually remove text boxes
text.remove_text_box(handle);
text.remove_text_edit(edit_handle);
This interface is ideal for retained-mode GUI libraries, but declarative GUI libraries that diff their node trees can still use the imperative interface, calling the Text::remove_*
functions when the nodes holding the handles are removed.
Handles can’t be Clone
d or constructed manually, so they are unique references that can never be “dangling”.
Text
uses slabs internally, so get_text_box_mut()
and all similar functions are basically as fast as an array lookup. There is no hashing involved.
§Declarative Mode
There is an optional declarative interface for hiding and removing text boxes. For hiding text boxes declaratively:
// Each frame, advance an internal frame counter,
// and implicitly mark all text boxes as "outdated"
text.advance_frame_and_hide_boxes();
// "Refresh" only the nodes that should remain visible
for node in current_nodes {
text.refresh_text_box(&node.text_box_handle);
}
// Text boxes that were not refreshed will be remain hidden,
// and they will be skipped when rendering or handling events.
There’s also an experimental function for removing text boxes declaratively: Text::remove_old_nodes()
.
This library was written for use in Keru. Keru is a declarative library that diffs node trees, so it uses imperative-mode calls to remove widgets. However, it uses the declarative interface for hiding text boxes that need to be kept hidden in the background.
§Interaction
Text boxes and text edit boxes are fully interactive. In simple situations, this requires a single function call: Text::handle_event()
. This function takes a winit::WindowEvent
and updates all the text boxes accordingly.
As great as this sounds, sometimes text boxes are occluded by other objects, such as an opaque panel. In this case, handling a mouse click event requires information that the Text
struct doesn’t have, so the integration needs to be a bit more complex. The process is this:
- Run
let topmost_text_box =
Text::find_topmost_text_box()
to find out which text box would have received the event, if it wasn’t for other objects. - Run some custom code to find out which other object would have received the event, if it wasn’t for text boxes.
- Compare the depth of the two candidates. For the text box, use
Text::get_text_box_depth()
. - If the text box is on top, run
Text::handle_event_with_topmost()
(Some(topmost_text_box))
, which will handle the event normally, but avoid looking for the topmost box again. - If the text box, is occluded, run
Text::handle_event_with_topmost()
(None)
.
For any winit::WindowEvent
other than a winit::WindowEvent::MouseInput
, this process can be skipped, and you can just call Text::handle_event()
.
The occlusion.rs
example shows how this works.
Structs§
- Color
Brush - RGBA color value for text rendering.
- Parley
Text Style - Unresolved styles.
- Rect
- A rectangle.
- Split
String - A string that may be split into two parts (used for IME composition).
- Static
Text BoxHandle - Handle for a static text box.
- Style
Handle - Handle for a text style. Use with Text methods to apply styles to text.
- Text
- Centralized struct that holds collections of
TextBox
es,TextEdit
s,TextStyle2
s. - TextBox
- A text box.
- Text
BoxHandle - Handle for a text box.
- Text
Edit - A text edit box.
- Text
Edit Handle - Handle for a text edit box.
- Text
Event Result - Result of handling a window event.
- Text
Renderer - A struct for rendering text and text edit boxes on the GPU.
- Text
Renderer Params - Configuration parameters for the text renderer.
Enums§
- AnyBox
- Enum that can represent any type of text box (text box, static text box, or text edit).
- Atlas
Page Size - Determines the size of texture atlas pages for glyph storage.
- Newline
Mode - Defines how newlines are entered in a text edit box.
Constants§
- DEFAULT_
STYLE_ HANDLE - Pre-defined handle for the default text style.
Type Aliases§
- Text
Style2 - Text style.