#Lorem Ipsum: A New Text ~~Backend~~ Frontend for Bevy

1 messages · Page 3 of 1

warped jolt
#

We could make the entities field private and only expose a method that returns &[TextEntity] so it's not mutable.

warped jolt
#

Added a comment to Implementation details about using custom-managed cosmic buffers.

finite magnet
#

@warped jolt I'm assuming that the TextStyle component will evolve over time; as I'm sure you know we've talked about making it easier to choose fonts (font family, system fonts, etc.) but that should be addressed in a different PR.

warped jolt
#

Considering I built my own framework. It's very opinionated and I don't see a way around that.

finite magnet
# warped jolt Sure, follow-ups are good. I'm actually doubtful bevy can/should have a font man...

Well, there are several ergonomic problems with the current system that should be addressed:

  • For complex text structures (for example, parsing markup into text nodes), having to assign a font handle to each section is onerous.
  • Because there's no consistency in font asset names ("_Bold", "+Bold", "-Bold", etc.), specifying font styles is an error prone process.
  • A lot of people have asked for the ability to use system fonts, but given that system fonts are different on different platforms, some sort of fallback mechanism is required.
warped jolt
#

The fact there are few ecosystem tools doing this speaks to how challenging it is. And also indicates there's not much to reference for an in-house implementation.

finite magnet
warped jolt
#

And there's a bunch of additional background code for auto-localizing fonts.

finite magnet
# warped jolt My perspective is the opposite, I dislike inheritance and prefer everything expl...

Well, in my case, building an editor - the entire editor uses the same font everywhere. So I prefer to specify the font one time. Now, the opinionated widget collection of course hard-codes the font selection in every widget, but not every ui element in the editor is an opinionated library widget - some are just app-specific widgets. Forcing the app to specify the font for every app-specific widget significantly increases the burden of building an editor ui.

finite magnet
warped jolt
#

The problem with inheritance in general is non-locality. Last year you might recall I implemented a naive style-inheritance in bevy_kot. Ended up having bugs and generally not enjoying trying to reason about what the style state was.

#

My objection is strongest against implicit inheritance. If you do something like add an InheritFontFamily component to an entity then behind the scenes do either a direct or deferred font lookup, that's a design I can get behind because it's explicit and a local action (local to the entity spawn).

finite magnet
#

In Quill, only text nodes inherit styles (with the marker component, but the templating engine inserts the marker automatically when it does String.into()), and the only styles that are inheritable are the five text style properties - family, color, and the various style flags. If you set a style on the text node explicitly, there's no inheritance. So you can inherit family but not italic, or vice versa.

warped jolt
warped jolt
#

@mental tree @finite magnet @shadow marsh @crude tinsel @near moon @vague cave Can you guys review https://github.com/bevyengine/bevy/discussions/15014 ? I will work on an implementation once everyone signs off on the proposed design. Other reviewers are welcome, I made the list from those heavily involved in UI and/or text.

GitHub

Continued from the Lorum Ipsum discord working group. This is a proposal for a new text API using required components and replacing Vec<TextSection> with a hierarchy of text entities. Objecti...

shadow marsh
# warped jolt <@153249376947535872> <@301060831314182146> <@257920265697230848> <@4713196811...

Haven't been following as closely as I would like, sorry. The fonts.get doesn't imply something new being created here right, just signaling that "if you wanna do fancy font lookup stuff, it's possible but you're on your own for now"?

Anyhow, this satisfies an issue I had with the previous sections-as-ents impl which was how awkward it was to get back to a section entity from a section index.

And I am in favor of this limited scope as a starting point.

I am quite confused by

In this proposal CosmicBuffer is embedded in TextBlock and not accessible by users.

Er, why? I need the Buffer. Not sure how you would build a text input without it.

finite magnet
warped jolt
shadow marsh
warped jolt
#

I guess we can expose a mutable reference with the caveat that it should only be used for cursor-related activity.

I'd also point out that simple_text_input PR is cloning the buffer.

shadow marsh
warped jolt
shadow marsh
#

Cool, sorry my feedback is sort of limited to skimming and remarking on anything that immediately jumps out. But 👍 from me if that's resolved for what it's worth

warped jolt
#

Updated the proposal

warped jolt
near moon
tulip hinge
# shadow marsh yeah, we have to due to limitations in cosmic right now but it's not ideal / I w...

For hit detection, drawing cursors and drawing selections, I think we have to use cosmic-text's primitives as it has all the details on how each glyph is laid out visually.

For editing though, cosmic-text's editing feature is "lossy" and I hit a dead-end* with it; it's not possible to "round trip" and preserve all the entities. For example, in Bevy we might have two spans like ("Your name: ".bold(), ""). Cosmic-text will eliminate the second when it is used to display.

So I would unfortunately recommend recreating the editing experience from scratch, and using cosmic-text purely for text rendering. At least in its current state.

GitHub

When there's an empty line (i.e. "\n"), the BufferLine loses Attrs information from the source. This results in a couple issues: When you clear the text in a buffer line, it falls bac...

GitHub

Possibly related to #283 I'd like to use Editor actions to drive changes to "editor state" that lives "outside" so to speak. Without having to impl Edit for MyType and reimp...

finite magnet
# tulip hinge For hit detection, drawing cursors and drawing selections, I think we have to us...

I think that preserving the entities only matters if you are trying to build a rich text editor. What I'm interested in (and I think this aligns with Bevy users in general) are text editors for the following use cases:

  • Naming objects in the editor (resources, tree nodes, labels, etc).
  • Editing character dialogues, quest descriptions
  • Naming objects in the game (saved games, characters, etc.)
    None of these use cases require anything other than editing a simple string - even character dialog, which might have italics or hyperlinks, are more likely to use explicit markup rather than some kind of WYSIWYG editor.
#

Going back to the web as an example, few websites offer WYSIWYG editing because it's a hard technical problem. Most get along perfectly well with <input> and <textarea> which is the goal I think we should aim for.

warped jolt
tulip hinge
tulip hinge
#

But I guess your view is that the author of a rich text editor would need to maintain their own data structures for interacting with a rich text document. It still feels limiting to me though. I think you can achieve it with the ECS as it is, and it's actually a pretty nice API.

finite magnet
#

Before we even begin to address this question, there's a more fundamental one: will the future Bevy text input widget use the same cosmic-text integration (TextBlock, etc.), or will it use some completely separate cosmic text integration (some new kind of TextEdit component)? This is important to decide because it changes the requirements - that is, when designing TextBlock does it need to handle all the requirements of editing, or just display?

#

I mean, if it's easy to combine them, implementation-wise, then sure.

warped jolt
#

The priority for TextBlock is code and asset driven text setup and control. If adjustments can be made to support editing, that's great but not needed for MVP as long as TextBlock doesn't get in the way of/opinionate the underlying CosmicBuffer -> TextLayoutInfo -> render text pipeline.

tulip hinge
#

Something doesn't quite feel right to me about the new proposal. It feels more complex to me rather than less.

I see more things gradually becoming entities, and with that future I can see more things being "constructed as" hierarchies of entities; I think we should be addressing the ergonomics of constructing entire entity hierarchies. This is not a Text-specific problem, it's just that Text is the first feature to encounter it in any meaningful way.

finite magnet
#

I originally assumed that I would have to write my own text editing widget, built out of text primitives and key event handlers, because I figured any potential native Bevy text input component wouldn't be reactive in the way I needed it to be. But the other side of this argument is that HTML input fields aren't reactive either - React wraps them and listens to the appropriate events to make them reactive.

tulip hinge
#

For example, consider a date picker widget. You want the user to spawn one thing with a bunch of config options, but under the hood it's a very complex arrangement of buttons and text and other layout.

warped jolt
#

The reason I'm putting any effort on this in the first place is to integrate markup with bevy_cobweb_ui. The current proposal should be enough (in combination with a text picking backend) to get good markup that includes hyperlink sections.

finite magnet
tulip hinge
#

I think that Bevy should provide a primitive for the "doing something more complicated". Just as Required Components spawn additional components for the user, something could spawn additional entities, whether those additional entities are part of the hierarchy or not, e.g. entity relations.

tulip hinge
# warped jolt The reason I'm putting any effort on this in the first place is to integrate mar...

I believe that preon's PR would also be enough for you to do this though. Add a Hyperlink(Url) component to the TextSection entity.

The challenge is the ergonomics loss of spawning single isolated text blocks in native Bevy (which is in many users' views not sufficiently addressed by with_child). Since your library is in charge of constructing the UI hierarchy, the ergonomics of this is less important I'm guessing (it's an implementation detail).

The proposal addresses that but I feel it complicates things: there are now so many ways to spawn Text, and you now need to teach each of these and how they might interact with each other. And that's not considering how it may complicate the implementation.

There was a previous version of preon's PR that allowed the Text entity to hold the TextSection (with any children simply coming afterwards), but that complicated things, and it was arguably less complicated than what's being proposed here.

warped jolt
# tulip hinge I believe that preon's PR would also be enough for you to do this though. Add a ...

preon's PR is a precursor to this proposal, and presented some problems that I wanted to solve:

  • Not integrated with required components.
  • Doesn't allow adding text to the root entity.
  • Does not support hierarchies of text entities, which is necessary if you want to do hyperlinks that contain multiple spans (i.e. via event bubbling: picking selects glyph in TextLayoutInfo and gets span index -> look up entity in TextBlock -> submit event to entity -> event bubbles to the root of the span where the listener sits).
warped jolt
tulip hinge
#

We discussed deep text hierarchies here prior to preon's PR and I think the conclusions were:

  1. Any hierarchies can be flattened into a flat list of sections (and would have to be for display purposes).
  2. Text/flow-display-elements hierarchies could be built as a separate layer on top of the underlying primitives.
  3. Templating layers / UI frameworks would handle this for the user anyway.
warped jolt
tulip hinge
#

I think that is easily doable but not nice. I agree that first-class support for hierarchies is better here.

tulip hinge
#

It depends on the approach you take.

  1. The text block's "semantic" hierarchy is defined within Bevy, and is separate to the visual (bevy_text) hierarchy.

You forward interactions from the visual hierarchy to the semantic hierarchy.

  1. The semantic hierarchy is purely a templating concern, so you flatten the hierarchy, duplicating some data (or references to data).

Each section separately forwards interactions to the same handlers.

warped jolt
true zephyr
#

Hey, haven't been active here lately, sorry about that, I got a bit sick, but feeling better now.
I read through @warped jolt's proposal for TextBlock. I assume this means my original text PR is not getting merged?

Also, if everyone does end up agreeing on the TextBlock API, I will gladly re-write my PR or start a new one for this.

shadow marsh
#

We need a cart-approved design.

true zephyr
#

Yeah we do

shadow marsh
#

I had hoped that sections/spans as entities could be made uncontroversial by doing it with very minimal changes, but that resulted in an interface that's sort of unpalatable. Your feedback on koe's proposal "from the trenches" would surely be appreciated.

true zephyr
shadow marsh
#

If you have any. Your point of view as someone who has been working on implementing this would be valuable.

true zephyr
#

alright, I'll take a more detailed look in a sec

#

Honestly, I really like the proposal. the one potential issue I see is confusion with the different components, there is a bit many, and the names are similar. not sure what to do about it other than properly documenting though 🤔

warped jolt
true zephyr
mental tree
mental tree
warped jolt
#

Thanks @mental tree, good iteration on the concept. Should we proceed from here with an implementation or wait for more reviews/people to review your adjustments?

finite magnet
#

bevy_ui can re-export it.

#

I'm also 👍 on Nico's comment about optimizing for macros/DSLs -- my editor has dozens of individual text strings, and none of them are spawned using the low-level APIs.

true zephyr
warped jolt
crude tinsel
#

I don't think so. I'm still keen on UI and 2D entirely merging at some point if there are no blockers, and barring that I think it might be good to put "UI" in the type (UiText, UiNode, etc), because we get quite a lot of people getting confused and using the wrong one.

warped jolt
tame aspen
#

Fantastic!

#

@sonic island Can I ask you to help us put together a little showcase for the examples to test this? It's much better to have someone who actually reads an RTL language prepare it

true zephyr
sonic island
#

I had an idea in mind anyways so it's a good time to do both 😄

sonic island
#

@tame aspen apparently Text2dBundle doesn't handle Text::justify if you don't set text_2d_bounds and defaults to justifying left (regardless of text_anchor).

#

The top text is TextBundle and the center one is Text2dBundle

#

I'll take a look at it tonight and see if I can fix it.

tame aspen
warped jolt
# sonic island The top text is `TextBundle` and the center one is `Text2dBundle`

When using TextBundle, the ContentSize of the node is automatically calculated based on the text, so there is enough room for justified text lines. With Text2dBundle there's no space by default so I believe what you're seeing is essentially 'line overflow' from the transform point. There could also be some bugs though ^.^

near moon
#

Yeah none of the problems with text2d are bugs exactly, it works as intended but it's not intuitive and the API needs a redesign

tame aspen
#

Please review!

true zephyr
true zephyr
#

I assume cart's proposal is what everyone agreed upon?

twilit iron
# crude tinsel I don't think so. I'm still keen on UI and 2D entirely merging at some point if ...

Yeah I agree, we should really change Node into UiNode and Text into UiText. Much more clear. And helps with autocomplete 🙂

its unfortunate for 2D and 3D, since you cant (with good reason) start a struct name with a number (2DText, 3DText) But these contexts are quite hard to mix, and it would help a lot in naming components and concepts in a way that makes it easy to filter.

Personally I use ThreeD, and TwoD when I have to deal with it, just because I have not found a better option.

Putting the Text3D works, but I really like my autocomplete during coding, and that assumes I know I wanted Text, and knew that Text existed.

When using widgets or wanting to explore, and hope that something exists, i like to type:

  1. the context (Ui, ThreeD, etc)
  2. get presented with the possibilities in that context

the other case:

  1. I type Text
  2. I get presented with the other contexts in which I can represent texts

which is kinda useless mostly, and only helpful if I already know about text

robust solstice
#

We are getting kind of close to the next release. It would be good to know what y’all consider “incomplete” so we can prioritize.

#

If we are going to do refactors or renames, is it important that they ship in 0.15?

twilit iron
#

Refactors and renames should be quite low priority. Carts API suggestion looks really good to me, and I think that should get highest priority. Because it improves functionality and ergonomics.

Renaming and refactor: should IMO be prioritized a bit after, because they help mostly with discoverability and ergonomics.

and at this stage, I would argue that functionality > discoverability 🙂

#

A native text-input widget, even if focus and everything is not there, would be something I would want, quite heavily.

But there are not even suggestions for textinput widgets, unless we chose one from the community. So that might get crammed to get into 0.15, but I think should be a goal for 0.16.

Many games needs them, at least for inventory/recipe searching

robust solstice
#

Seems like focus is a pre-req for text input. Need to know when you click away.

#

Just a basic text input example (like a full screen notepad) might be easier now?

#

bevy-ui-navigation looks interesting.

#

Sorry that’s kind of out-of-scope for this discussion though.

twilit iron
#

Yeah, focus is needed, but also for first text-input, could be very rudimentary

#

basically, have a bool for the text-input, isfocused (true/false)

#

Would not be complete, but at least a start

robust solstice
#

True. I’ll try to spin up progress on focus, that seems like a pretty obvious next step after picking.

twilit iron
#

I wish there was a good way to pin a list to a discussion...

#

Like the notepad thingy from slack...

#

Some lists from me:

Text High Priority Ordered:

  1. Carts API for textblocks/spans ( into 0.15 )

  2. MVP for Text Input (doesnt even have to support multispan, or multiline or anything fancy like that, just a way to add/remove characters) (0.15 or 0.16 for first MVP)

Text Unsorted list:

  • Abstraction for system fonts

  • Renaming UI specific stuff, to not be unclear as to what is 2D stuff and what is UI stuff

Unsorted Ui:

  • Observer/event bubbling ( @finite magnet )
  • Focus (Not text specific), since I can imagine someone wants to like navigate UI, and even 3d/2d elements in a nice way.
  • Clipboard support for text
  • Shadows for UI
  • Widget systems ( recently used Sickle UI, the builder patterns is <3, and the traits implementation onto commands is also ❤️ for creating a widget by storing entity references on a top-level component.
  • ONE opinionated UI system natively
  • Split up style

Done related to Text:

  • better text-backend to achieve above items
  • Ui picking ( would probably help with simple text input)
#

I probably forgot a lot, since I am not that active in here, but those things are stuff I notice when working in bevy

finite magnet
#

Some comments on your list:

  • For focus, the main thing we need is to define a version of keyboard events which supports bubbling. Everything else can (for now) be third-party library code - different third-party libraries can interoperate as long as they agree on what the event type is, they just need to stick observers in the right places.
  • Not text related, but if I could get support for "fragment nodes", this would unblock a lot of stuff for me - I'd rewrite both quill and bevy_reactor, and both would get smaller. Similarly, I really think that if BSN is going to support any kind of reactivity you are going to need this in place first.
#

Also, with respect to focus, there's a whole conversation around "keyboard focus" vs "mouse focus", and how these should interact. Obviously, if you have a text input widget with a cursor, keystrokes need to go their first, regardless of where the mouse is on-screen - if you hit control-X/C/V/Z you want it to affect the text and not be interpreted as a global shortcut. But the text input widget might not be just a single entity (lots of text widgets have decorative frames, adornments like clear/search buttons, etc) and the entity listening for events might be one or two levels higher than the node that has focus, so bubbling is going to happen. But if people that want Blender-like shortcuts where the set of shortcuts changes based on where the mouse is, that kind of conflicts with the keyboard focus model. I'm not sure what the answer is on that one.

twilit iron
#

If we get a way to indicate focus onto a specific entity inside bevy, users could implement their own focus logic

#

and bubbling ofcourse.

then I guess maybe some focus experimentation could start to answer many questions

#

and raise new ones

finite magnet
#

It's a resource which contains an Option<Entity>.

twilit iron
#

that used with bubbling, wouldnt that unblock a lot of experimentation?

twilit iron
#

But do you think that text-input could be done without that, and just with the a11y focus?

#

(wondering if I need to change my prioritized list)

finite magnet
#

Here's what I do:

  • When I get a keyboard event from the event queue, if focus is not none, I send a bubbled keyboard event to the focus entity.
  • If focus is none, I query for an entity with DefaultKeyboardHandler and send it there.
  • Text input widgets receive bubbled events and call stop_propagation if they recognize the key.
  • Buttons and checkboxes also do this for ENTER/SPACE.
  • I have a reactive context method cx.is_focused(entity) which returns a boolean which is true if the widget is focused. This is used to draw the focus outline. It reacts when focus changes.
  • Global shortcuts are just listeners which are placed on the app's root element, which also has the DefaultKeyboardHandler component.
  • Tab navigation is handled by a special system.
robust solstice
#

do we even need bubbling? can't widgets read their focus state and determine if they should grab input or not?

finite magnet
robust solstice
#

If we are cool with using observers for that then I can write that in a weekend. (maybe)

finite magnet
#

For example if a button has focus, you can still press ctrl-Z to undo.

#

A slightly different approach is one where, instead of using the focus resource, we send keyboard events to whichever entity is being hovered by the mouse and let the events bubble upwards from there. This gives us the Blender-style contextual shortcuts - that is, where the shortcuts change based on which panel the mouse is in. You basically just stick an observer on each panel that knows about the shortcuts it knows.

However, if you want both - that is, keyboard events dispatches to both the focus entity and the hover entity - then you are probably going to need to dispatch the key event twice, which now that I think about it is probably not the worst idea in the world. The global handler would collect any events that were left over when the widgets were finished with them, and dispatch them to the current active panel.

robust solstice
#

oh! also, now that we have input event ordering, we can actually interleave these keyboard events with point events in the correct order.

#

that seems useful.

#

it might matter if you press a button before or after a drag drop event, for instance.

finite magnet
robust solstice
#

ah right, cool. we don't have to do it that way.

#

but on some inputs, button presses and joystick moves will have the same clock. so idk.

#

controllers off topic anyway.

finite magnet
#

I think the main requirement for ordering is multiple input events which are produced by the same hardware event, like drag end and drop, which are both generated by mouse up.

robust solstice
#

what happens if two input devices both take actions that would change focus?

finite magnet
#

last one wins?

robust solstice
#

fair

#

deep edge case

finite magnet
#

I mean, honestly, I don't think I care which one it is, so long as the result is "reasonable".

crude tinsel
#

I definitely think we'll want bubbling. But we could probably get basic text input without if need be?

crude tinsel
# finite magnet Some comments on your list: * For focus, the main thing we need is to define a v...

Not text related, but if I could get support for "fragment nodes", this would unblock a lot of stuff for me - I'd rewrite both quill and bevy_reactor, and both would get smaller. Similarly, I really think that if BSN is going

You seem particularly keen on this feature: have you considered implementing it yourself? No obligation of course, but I don't think this feature should be particularly hard to implement (and the bevy_ui codebase is quite small and approachable :))

vast cape
finite magnet
# crude tinsel > Not text related, but if I could get support for "fragment nodes", this would ...

I did actually spend some time looking into what it would take. Part of the problem is that the idea, as proposed, would have broad impact - I originally assumed that it would only affect layout and rendering extraction, but other design proposals would have an impact on observers and queries as well. I don't have a good sense of "how many places in Bevy do we traverse entity hierarchies?", which is something that requires broad knowledge of the Bevy code base. Up to this point, I've only worked on a few small pieces of Bevy (AssetPath and bevy_color)

#

What I am asking for, right now, is not so much an implementation as a general sense of whether other people think this is a good idea or not.

vast cape
#

Transform and visibility propagation might get a bit tricky though

finite magnet
vast cape
finite magnet
#

Some questions I would like to ask are:

  • Is this idea even feasible?
  • Is it hard?
  • What parts of Bevy would be affected?
  • Is anyone else interested in this idea?
  • Are there other use cases for this besides the ones I have thought of?
    BTW I do like the name "shadow node".
vast cape
#

I for one am interested in the idea at least (web dev background). And I feel like there should be other use cases than for reactive scenes/UI, but can't think of anything right now...

#

As for the difficulty, I will continue exploring it and hopefully find out 😅

finite magnet
#

Actually, we really should be discussing this in ui-dev

vast cape
#

Right, forgot which channel we are in. Gotta head to bed now though. But I'll continue exploring this in the coming days

white schooner
#
thread 'Compute Task Pool (21)' panicked at c:\dev\tools\cargo\registry\src\index.crates.io-6f17d22bba15001f\cosmic-text-0.12.1\src\buffer.rs:651:13:
assertion `left != right` failed: font size cannot be 0
  left: 0.0
 right: 0.0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_ui::widget::text::measure_text_system`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!

This can be hard to track down. Theres no indication of what node is causing the problem.

warped jolt
#

We should be able to filter out segments higher up to avoid that.

finite magnet
#

In the latest Bevy main, vertically centered text looks like it's a pixel too low (to my eye anyway). This was true under the old text code as well, and I kind of hoped that moving to cosmic text would fix it. I couldn't find an open ticket for this, but I remember there was discussion about this at some point.

#

Note that for some sizes it seems OK, while for others (the "Xs" case) it's fairly obvious.

warped jolt
#

Looks like bad rounding?

#

Sounds like UI rounding is all-around a mess and needs a deep correctness pass.

tame aspen
white schooner
white schooner
crude tinsel
white schooner
warped jolt
#

@true zephyr looks like release candidate for v0.15 is tentatively Oct 4th. Do you think the text PR you're working on will be ready for review by then?

sleek raft
tame aspen
#

No objections from me. It's a pretty standard "expose more of our internals for advanced users" decision

warped jolt
velvet estuary
#

Ok, nice. I was just trying to label all the milestone issues with an updated status, but let me know if you have any small fish you'd like me to help with :)

twilit iron
#

@tame aspen @shadow marsh checked out the bevy_simple_text_input. I would say this is something we could upstream into bevy no?

Or would there be something blocking that?

tame aspen
#

I'm not sure on the implementation details, but I think that we're ready for a PR now

twilit iron
#

Honestly, its 900 lines, contained in a single file. has no external dependencies other than bevy and is well documented I would say.

#

quite feature-complete for a simple single-line textinput as well

shadow marsh
# twilit iron <@159873981174906880> <@257920265697230848> checked out the bevy_simple_text_inp...

Upstreaming is not a goal for me personally. I just wanted to see if I could do something good enough for a lot of people within the current limitations of Bevy, and outside of Bevy's bureaucracy. The whole thing is kinda janky really. Less janky all the time thanks to character input improvements / upcoming cosmic text stuff though.

But the roadblock preventing widgets like this being included in Bevy hasn't been solved. That's (IMO) a lack of consensus on how to implement widgets that require hierarchies, and what should be done about "reactivity." The current disposition seems to be to wait for bsn or whatever. I don't personally understand why an implementation detail like that needs to block progress on widgets. 🤷‍♂️

Attempts to add simpler widgets than b_s_t_i have been deemed controvertial and/or bikeshed into oblivion. So I don't really see it ever happening.

crude tinsel
#

IMO some of the simpler widgets are things that don't necessarily need to built-in to Bevy, and could be implemented as high-level components if such a system existed.

#

Text input is different though and probably does need to be built-in

twilit iron
#

@shadow marsh While I agree with your points regarding widgeting and such. I believe that with @tame aspen blessing on this now, upstreaming the text-input to have something to start with would be very beneficial.

I believe that by adding ONE way to create hierarchical widgets to bevy, will open up a lot of possibility. And your implementation is the simplest one out there, and will get bevy beginners started quickly.

I also believe that to actually get any kind of good bsn, it should be driven by real need, such as a simple widget, for example a text-input.

With that said, I could do the "work" of copy-pasting your code with your blessing and co-author you in it and try to push the PR through.

tame aspen
#

Yeah, I'm interested in getting something simple and not-terrible in place for this at the start of the 0.16 cycle @twilit iron, and then refining it as BSN itself drops

twilit iron
tame aspen
#

I'm hopeful that we get the Text rework in, but even that's a big stretch

warped jolt
tame aspen
#

Yeah. I'd do it myself but the Interaction stuff from #1236111180624297984 is more pressing

#

Welp, next cycle it is

robust solstice
#

I may be able to shoulder the interaction depreciation

robust solstice
fresh ginkgo
#

Isn't it also needed for the required components migration? At least with the blessed/selected proposal

#

(text I mean)

tame aspen
robust solstice
#

I thought you’d say that, lol. Fair enough, it can wait.

#

I don’t want to do it either

tame aspen
#

Doing a huge grindy migration and then the release notes was brutal last time lol

robust solstice
#

I am hoping to be in a better place to help with release notes this cycle. Didn’t have spoons last time.

true zephyr
#

I am very sorry about not working on this yet. I was busy with life and honestly my mental health isn't that great lately. Not sure when 0.15 is planned, but if noone else took over, I want to try to work on this again.

robust solstice
#

You’ve been super communicative about the status and that’s really all that matters ❤️

#

We are going to start the RC process for 0.15 on the fourth, just so you’re aware, but please don’t crunch to get anything in by then.

true zephyr
#

Yeah I will work on it but I don't think it will be done by then. honestly might be better for both my stress levels and the time we need to get proper reviews

true zephyr
tame aspen
warped jolt
true zephyr
warped jolt
shadow marsh
#

@runic belfry I haven't been able to pay attention to Bevy in the last couple weeks. Is there anything you've found in your cosmic text / bevy_simple_text_input experimentation that we might want to address in Bevy and/or cosmic before 0.15?

warped jolt
#

But the timeline probably isn't feasible.

runic belfry
fresh ginkgo
warped jolt
warped jolt
#

Posted a draft PR. It isn't close to compiling yet, but has all the main changes now. Tomorrow will be getting it to compile then fixing all the tests and examples, etc. Also haven't rebased onto ghost nodes yet. https://github.com/bevyengine/bevy/pull/15591

GitHub

Objective

Implement Bevy Text rework: TextBlock #15014

Solution
This implements #15014 faithfully except for one change. I separated TextSpan from TextSpan2d because TextSpan needs to require the...

true zephyr
#

Like, start of a review or not yet?

warped jolt
warped jolt
warped jolt
#

The text rework PR is ready for review https://github.com/bevyengine/bevy/pull/15591. I have migrated 1/4 of the examples (or 1/4 of the compile errors anyway). Looks like there is only 1 day remaining before RC, so it urgently needs review to make it in @true zephyr @finite magnet @near moon. @mental tree you may want to review it, the changes are pretty massive (affecting ~50% of examples) and the migration is a little tougher than expected (had to introduce TextReader/TextWriter system params for accessing spans).

true zephyr
#

Awesome! I will take a look somewhere today (9am for me right now)

finite magnet
warped jolt
finite magnet
#

Also, does anyone remember the old-time TV commercials where you had some musical jingle with lyrics printed at the bottom of the screen and a bouncing-ball hitting the words in time with the music? Something along those lines 🙂

sonic island
#

Sorry I missed the discussion here. I will write a BiDi demo. What do you want to demonstrate?

#

Currently I am just using a flex and grid layout to display mixed direction TextBundles.

#

rough draft:

#

Someone mentioned multiple text sections so I will add different text styles in the same text.

#

ping me with your requirements 🙂

sonic island
#

i.e. should I base my bidi example on it or is it too early?

near moon
#

I think alice is keen to merge it asap

#

I'm working my way through it and most of the things I have problems with so far are fairly superficial if changed

near moon
near moon
#

performance seems ~5% improved for text relayout but seeing some dips in fps, slightly worse when text is unchanged but nothing significant

true zephyr
#

Left a 👍 on some comments I agree with, and left some comments myself. other than that I think the PR looks great!

warped jolt
warped jolt
near moon
#

maybe I was imagining it though, I'll run it again

#

Yeah ran it again, and still seeing a significant improvement

warped jolt
# sonic island ping me with your requirements 🙂

Looks like the main combinations are FlexStart/FlexEnd, JustifyText::Left/JustifyText::Right, and then the implicit LTR/RTL information embedded in a text. Then there is vertical and reverse-vertical(?) text but IDK if we support that (maybe FlexDirection?).

sonic island
#

Column and ReverseColumn will not work without CSS direction property

#

E.g. where you horizontally align items depends on whether it’s rtl

#

I plan on contributing a patch to taffy but it will take a while (certainly not before RC)

warped jolt
#

I will try to make a better constructor for spans at least.

near moon
warped jolt
#

Ok addressed all of @near moon's comments aside from the design feedback. Will get back to updating examples later today.

tame aspen
#

FYI, looking to push back the release candidate a week: #engine-dev message

#

Which means we should definitely be able to land the Text rework

#

And maybe a system font solution?

warped jolt
finite magnet
#

I want to bring up the issue of not using ghost nodes for text spans, but instead adding smarts to the layout code not to dive into text blocks. While I am still very bullish on ghost nodes, I also understand the desire to put it behind a feature flag. I also think ghosts may be overkill for text nodes.

warped jolt
#

I'd rather have a generally-usable feature than special-casing for text spans.

near moon
near moon
warped jolt
near moon
#

It should just be enough to stop any walk at nodes without either a Node or a GhostNode component?

#

We could look at it in a follow up, but probably the most controversial part of the PR is the reliance on ghost nodes

warped jolt
near moon
finite magnet
warped jolt
# near moon You don't have to skip them necessarily, could make them full UI nodes but set t...

Then you need to lug around Node and Style components, bloating memory use. And you don't get away from Transform/Visibility because Node will surely require those when migrated to required components. GhostNodes are the least-bad solution in my view, and we can easily revisit if a truly better solution appears. They are basically an implementation detail with minimal exposure to users.

mental tree
warped jolt
#

All examples are now migrated. Will work on docs and migration guide tomorrow.

shadow marsh
#

Apologies, no time to follow closely lately and do a proper review. But I took a quick peek and tried to catch up on some of the discussion, and

I don't totally understand this TextWriter business. It seems potentially useful, but with most examples having been migrated sort of mechanically to use it, we seem to be really underselling the benefits of spans-as-entities. IMO, most of the examples mutating spans should be querying for "marked text span entities" and modifying them that way. Similarly not really understanding spawn_text_block, which seems really inflexible.

#

And just another small note: at least a few of the examples using a large amount of text spans are doing so for no good reason, just because it looks nice to have the code formatted that way. The spans are totally static and could just be one big span with line breaks.

near moon
#

maybe the grouping of text nodes should just be implicit

#

with any adjacent text nodes in the tree getting grouped together automatically

#

and text nodes would always be leaf nodes, never have children

sonic thicket
shadow marsh
#

At least when I talk about not doing text hierarchies, it's just "for now" because not having it isn't a regression, and it's not going to get done in time. Not saying it isn't useful.

near moon
sonic thicket
#

but I want to draw the background with my own shader so that it's a trippy star

#

background color is maybe a bad example 🙂

near moon
#

It's less efficient maybe but we could split up text layout info

#

So each text span would have it's own layout info just with the glyphs for that span

#

So that would make it easier to pick out the geometry for a single word and draw it with a shader or whatever

crude tinsel
warped jolt
# shadow marsh Apologies, no time to follow closely lately and do a proper review. But I took a...

The TextWriter API evolved as part of me trying to make the migration less painful. It's very practical. IMO if people have problems with the available helpers (TextWriter and spawn_text_block) then they should make follow-up PRs to iterate on what this PR does instead of trying to make this one gigantic PR perfect. Sorry if I sound grumpy, just woke up after spending 3 days and 23hrs of work on this.

tulip hinge
#

It looks very ECS-native

#

fits my preference but I understand if you don't want to tackle that (it was quite a lot of work for Preon)

true zephyr
#

Yeah I did do that. And honestly, if no one else wants to do it, I can probably port quite a few examples this weekend as I quite literally have nothing but programming planned.

I can probably re-use a bunch of code from my PR too

#

If you guys want me to do that, let me know. I would probably do it in a separate PR I assume? As in a follow-up, like @warped jolt said above.

shadow marsh
#

My comment was prompted by a comment that seemed to be questioning whether spans-as-entities was worth the hassle. And that situation was resolved by cart if I followed the discussion properly.

I'm fine with leaving the example cleanup for later.

#

But I'm not sure if there are any examples where we actually should be using TextWriter so it probably should get done eventually. (And add an example showing off TextWriter if that last sentence turns out to be true)

near moon
warped jolt
tame aspen
near moon
mental tree
finite magnet
# mental tree I've made the various adjustments discussed for the Text Rework and merged with ...

One thing I plan to do soonish is port bevy_reactor to the new text components; I was holding off on this until things had stabilized. The UiBuilder class has two helper methods that create simple textual entities:

    fn text(&mut self, s: impl Into<String>) -> &mut Self;
    fn text_computed<F: FnMut(&Rcx) -> String + Send + Sync + 'static>(
        &mut self,
        text_fn: F,
    ) -> &mut Self;

90% of use cases only involve a single text span, so the builder API provides methods that hide all of the messy details of text blocks and spans.

#

These methods are also implemented on WorldChildBuilder via an extension trait.

#

Note that simple APIs like this are only useful in a world where text styles such as font and color are inherited. Unstyled text generally looks out of place (because it uses the default built-in font), and extending the "simple" API to allow for styling makes the API considerably more complex. Instead, I chose to allow the styles to be specified on the parent, and have them propagate downward unless overridden.

#

For cases involving multiple spans, with span-specific styling, there are a couple of approaches:

  • Let users do it themselves by spawning the entities manually.
  • Leave it up to some markup parser (markdown, HTML, etc) to properly set up the spans.
  • Design a more advanced API with more options.
warped jolt
#

I think we'll need to ask devs in all the working groups/channels to run the examples they are familiar with once this is merged. With diffs this big (and including all the other migrations) there are bound to be a number of bugs.

warped jolt
tame aspen
warped jolt
warped jolt
#

Text rework is merged! Thanks everyone :)

tame aspen
#

Very pleased to get that in 🙂

#

Test it out! Small improvements will be much less painful now that it's on main

warped jolt
#

@mental tree I imagine you'll want a say in this one ^

sonic island
#

Really appreciate it

finite magnet
#
fn change_text_color(mut q_text: Query<(&mut TextStyle, &mut AnimateTextColor)>, time: Res<Time>) {
    for (mut text_style, mut animate) in q_text.iter_mut() {
        animate.hue = (animate.hue + time.delta_seconds() * 200.).rem_euclid(360.0);
        text_style.color = Hsla::new(animate.hue, 1., 0.5, 1.).into();
    }
}
tame aspen
#

God that's so nice

#

Text as entities is such an improvement

#

Proper font abstractions is next on my wishlist (0.16 tho)

finite magnet
#

I've finished porting bevy_reactor to the new API and current Bevy main

tame aspen
finite magnet
#

The code for the demo above:

builder
    .spawn((TextBlock::default(), Text("".to_string())))
    .styles((typography::text_default, |sb: &mut StyleBuilder| {
        sb.font_size(32).color(palettes::css::GRAY);
    }))
    .create_children(|builder| {
        builder.spawn((
            Text("The quick brown fox jumps over the ".to_string()),
            TextStyle::default(),
            UseInheritedTextStyles,
        ));
        builder.spawn((
            Text("lazy".to_string()),
            TextStyle::default(),
            UseInheritedTextStyles,
            AnimateTextColor { hue: 0. },
        ));
        builder.spawn((
            Text(" dog".to_string()),
            TextStyle::default(),
            UseInheritedTextStyles,
        ));
    });

Note how the top-level text block has an empty string

#

For this demo, I wanted to use "child spans only", so I had to insert a placeholder string at the top level.

#

Wouldn't render without it.

#

One thing I haven't tried, although I am curious about, is whether I can mutate Transform to get individual words to "shake".

#

Probably not I would expect.

#

I was thinking about the old-timey TV commercials where you have some musical jingle with the lyrics shown at the bottom of the screen, with a bouncing ball bouncing off each word in time with the music.

crude tinsel
sonic thicket
crude tinsel
warped jolt
finite magnet
warped jolt
#

You might be able to hack into TextLayoutInfo to change glyph positions.

#

Can use span_index to identify the target span (with ComputedTextBlock::entities to get the entity list).

finite magnet
#

I was thinking I could wrap the word in a separate element, treat it as an inline block.

#

Actually, what I am more interested in, in terms of next steps, is accessing the cosmic-edit API

warped jolt
finite magnet
#

What about picking? If the word is a hyperlink, can I put a picking observer on one word span?

finite magnet
#

No worries

#

But that's probably more important use case

warped jolt
#

Should not be hard to adapt existing text picking to it.

finite magnet
#

Help screens in games often have links, so this is a real use case and not a hypothetical like the shaking thing

warped jolt
#

Sure. I want it too. Hopefully someone implements it :)

finite magnet
#

So, next steps on the lorem ipsum agenda:

  • font abstraction
  • cosmic-edit
  • picking
tame aspen
#

And loading system fonts

near moon
#

Animated text the simple thing is to write a custom extraction system, then you can just adjust the positions of the glyphs any way you want

#

or it used to be, maybe required components gets in the way

#

I guess you have to do something ugly like set the text color to transparent and then it'll still work

mental tree
warped jolt
#

Same with TextStyle ^

shadow marsh
#

migrated b_s_t_i.

My only issue/feedback so far is that it's very easy to goof and add a Text as a child instead of a TextSpan, and when you do that,

  • things just sort of look broken, no warning/error
  • unless you try to modify a span with UiTextWriter, which panics on an unwrap
finite magnet
#

Maybe we need to add some "this makes no sense" warnings.

#

In fact...

#

Here's something to think about: with required components, ghost nodes, the changes to node traversal, and text entities, we are starting to produce more cases where certain hierarchy patterns are invalid. I wonder if it's possible to generalize these in some ways. On the one hand, this is problematic because of crate dependencies, the kinds of validation rules you'd want use components from various sub-crates. On the other hand, they all have to deal with the problem of not running the validator too often or making it too expensive to run.

tame aspen
finite magnet
river loom
#

Mentioned this before, but I have a generalized runtime hierarchy validator in big space, with a declarative API. I really think it's worth upstreaming for runtime validation. It produces text output to describe why the lint is triggered, the archetype it was triggered on, and can support custom messages when you define the lint.

#

Output looks like:

-------------------------------------------
big_space hierarchy validation error report
-------------------------------------------

Entity 8v1 is a child of a "Non-root entity in a high-precision BigSpace hierarchy", but the components on this entity do not match any of the allowed archetypes for children of this parent.
                    
Because it is a child of a "Non-root entity in a high-precision BigSpace hierarchy", the entity must be one of the following:
  - Root of a low-precision Transform hierarchy, within a BigSpace
  - Any non-spatial entity

However, the entity has the following components, which does not match any of the allowed archetypes listed above:
  - bevy_transform::components::transform::Transform
  - bevy_transform::components::global_transform::GlobalTransform
  - bevy_render::view::visibility::Visibility
  - bevy_render::view::visibility::InheritedVisibility
  - bevy_render::view::visibility::ViewVisibility
  - bevy_hierarchy::components::parent::Parent
  - bevy_hierarchy::components::children::Children

Common errors include:
  - Using mismatched GridPrecisions, like GridCell<i32> and GridCell<i64>
  - Spawning an entity with a GridCell as a child of an entity without a ReferenceFrame.

If possible, use commands.spawn_big_space(), which prevents these errors, instead of manually assembling a hierarchy. See src/validation.rs for details.
#

It came about for similar reasons, I think. The shape of the hierarchy was semantically important, and silent failure wasn't a nice DX.

sonic island
#

Let me know any bi-directional use cases you want to test.

#

I am planning on replacing the message with a long and text-wrapped message with multiple styles.

tame aspen
sonic island
#

On it.

tame aspen
#

And bidirectional text selection eventually

sonic island
#

@tame aspen one thing to note is that the start of the text determines the direction automatically.

#

notice where the punctuation is placed

#

especially the last period

#

I added Arabic "ع" and English "E" right before the period to confirm that the punctuation placement direction is decided by the beginning of the sentence.

#

So far the arrangement looks sensible to me. I need to try it with text spans now.

tame aspen
sonic island
#

(I switched the text justification in the middle)

tame aspen
#

Looks like we're getting some overflow, but I think that's not your fault

sonic island
#

the text renders correctly despite changing font size in the middle

#

i love cosmic text ❤️

tame aspen
#

Yeah I'm super impressed 😄

#

Thanks @calm notch!

sonic island
#

Looks like the overflow check doesn't check if the beginning of text is overflowing.

#

So if LTR text is overflowing to the left because the text is right justified then it goes out of the box..

tame aspen
sonic island
#

Maybe 🤔 I want to work on taffy a bit to add the direction property.

#

overflow issues are a bit more complex so I'll check it out after I get direction working hopefully soon™️

#

I don't have a pure cosmic_text test project to isolate the issue (if any).

finite magnet
#

Ugh, I was going to create a thread and forgot that I can't create threads in WG channels 😦

#

I wanted to start a thread about text editing support (for games) in Bevy

sonic island
#

The core problem for editing in my opinion is translating pointer position to text cursor position. Everything else falls into place after that is solved.

near moon
#

Yeah I made a simple text editor for hedra using ab_glyph wasn't that difficult

near moon
#

to get multiline editing and selections blocks to work had to write my own glyph layouting to work around the bugs in ab_glyph, should be much simpler with cosmictext

crude tinsel
near moon
#

calculating the cursor wasn't a problem anyway, you have the glyph geometry already in the layout

sonic island
#

Awesome. Then all that's left is deciding how UI focus and navigation should be handled.

near moon
#

yeah navigation and focus yeah that was what made me sweat

sonic island
#

"all that's left" == 90% of the work 😄

near moon
#

cross platform support was annoying as well to sort out

warped jolt
#

Note that the cosmic-text editor can’t be used directly with bevy’s ComputedTextBlock because the editor invasively modifies the cosmic-text buffer.

#

At least, that was the conclusion of my investigation.

warped jolt
#

Right, I have the docs note

/// This is private because buffer contents are always refreshed from ECS state when writing glyphs to
/// `TextLayoutInfo`. If you want to control the buffer contents manually or use the `cosmic-text`
/// editor, then you need to not use `TextLayout` and instead manually implement the conversion to
/// `TextLayoutInfo`.
finite magnet
#

All right, here are my notes on text editing:

#

What I want is a text editing widget for games. Use cases are character name, email address, password, save file name, multi-user chat, and so on.

#

I realize that there's an ongoing effort to build a text input widget for the editor, but what I am envisioning is somewhat different.

#

Goals:

  • Text color, font, cursor, and selection color should be customizable.
  • For password entry, there should be an option to display only placeholder glyphs instead of actual text.
  • It should interoperate with other keyboard focus widgets (see the discussion here: https://github.com/bevyengine/bevy/discussions/15374)
  • Basic multi-line editing would be a "nice to have" for things like multi-user chat boxes.
  • However, we can assume that the text being edited is short - no bigger than a tweet.
  • It should allow third-party frameworks to extend the text input widget in various ways using observers, but these extensions should not be built-in. For example, a numeric spinbox can support editing numbers by dragging, or incrementing using arrow keys, but this should not be a feature of the core widget.
  • Scrolling support
    Non-goals:
  • Syntax highlighting
  • Large documents
  • Source code formatting
  • Rich text styles
    In other words, if you are editing long docs or source code, use an editor specialized for that purpose.
finite magnet
#

In React, there are two kinds of text input widgets: "controlled" and "uncontrolled". I'd like to build a controlled widget - this is a widget which doesn't update its own state, but instead triggers an event with the proposed new state, such that the receiver (usually the parent widget) can decide whether or not to make it the new state or take some other action. So for example, when I hit the 'delete' key, the widget doesn't mutate the current text state directly, instead it triggers a "change" event with a copy of what the text buffer would look like with the character or selection range deleted. It's up to the parent to apply that change.

#

Controlled widgets are nice for things like field validation - for example, you can have a phone number input, and if the user types an invalid character you can flash the screen or display an error. They are also good for cases where the text input can change programmatically - such as the case where you have a hex color input that you can either type into, or can be changed by dragging the "hue" slider.

#

Uncontrolled widgets, on the other hand, are slightly more performant in editing, because keystrokes can be applied to the buffer directly. But they require additional synchronization in some cases because now there are two sources of truth for the text, and which source is authoritative changes depending on whether the input widget has focus. Also there's no way for the parent widget to cancel the effect of a key, so validation can only be done after the fact.

#

As you might expect, controlled widgets work best with small text buffers, since every change event contains a clone of the buffer with changes applied.

#

So what I am envisioning is something like a TextInput component that emits TextChange events which the observer can then handle.

crude tinsel
#

I believe classic React controlled components modify the value and then apply validation after

warped jolt
finite magnet
finite magnet
warped jolt
finite magnet
finite magnet
#

Fixed it now

crude tinsel
finite magnet
#

The other benefit of uncontrolled is that it conforms to most programmers expectations of how text editors work

crude tinsel
#

I figure it shouldn't be that hard to build an editor abstraction that supports either style

finite magnet
#

However, I tend to only use controlled widgets in my designs - not just text inputs, but checkboxes, any kind of widget really

#

Certainly in React you have the option of controlled or uncontrolled text input, with slightly different APIs.

crude tinsel
#

Oh, I definitely use controlled too

finite magnet
#

With respect to cosmic-edit: I can see that cosmic's Buffer and CursorMotion have most of the methods we would need. What advantage would you see in using Editor?

#

I'm thinking of a TextInput component which would:

  • Accept FocusKeyboardEvents and consume the ones it cares about (propagate(false)), allowing other keystrokes to propagate.
    • Note that in single-line mode, arrow up/down would be allowed to propagate upward
  • Emits TextChange events which bubble up
  • Has methods for setting / replacing the current editor state.
  • Has methods for getting the selection rect coords (with discontinuous rects for bidi) and cursor coords.
  • Uses cosmic Buffer and CursorMotion for most editing functions.
#

Note that on some platforms, like consoles, text input is just a button which pops up a console-specific text input overlay. This swap should probably be done at a higher level, possibly with a feature flag.

#

Note that nothing I have said references reactivity, but it's perfectly compatible with reactivity. Just like in React, the "reactive text input" is a wrapper around the native text input DOM element, so too here: the reactive text widget would be a wrapper around this.

twilit iron
#

I'm thinking inventory searching, naming pets or cities, and that sorta thing. Typing in a password, username and server

#

Making it more advanced could be a follow up, I'm scared of adding too many requirements that would delay inclusion.

I mean, manual focus control and such could be implemented with picking or other mechanics for now as well, so isn't really required for an MVP either

near moon
#

I agree though it's better to start simple

twilit iron
#

It probably is, but it also needs to handle scrolling and wordwrapping, as well as letting users type in the enter character etc.

And a single line has another set of features is cares about. Such as typing in passwords as asterix, or only allowing for maybe numbers or whatever 🙂

And I believe more games and such would be unblocked by the single line branch of text-input 🙂

near moon
#

yeah I mean scrolling and wordwrapping as well, if you've got a single input it's actually pretty trivial to turn it into to a multiline one

#

getting a numeric only input was actually harder when I did it for some reasons

#

we had endless bugs with the numeric inputs

warped jolt
#

Single-line does need horizontal scrolling though

twilit iron
#

I wouldn't say need, not at first at least.

#

Many of my intended use cases would probably treat that as an edgecase without any meaningfulness in practise

warped jolt
#

Sure, the first MVP doesn't require it. But the design should take into account it will be implemented.

twilit iron
#

True

#

It should not block such development no, but neither should the lack of that feature block including it into the engine

finite magnet
#

What I am hearing is that there are not too many disagreements to the rough spec that I've outlined.

#

For the next step, I could do one of several:

  • Collect the various design points in a hackmd
  • Comment on the original ticket
  • Close the old ticket and start a new one and discuss there
twilit iron
finite magnet
#

As far as implementation, I would want to get the keyboard focus stuff accepted before any work begins on text editing. Well, I suppose we could use placeholder events.

#

The keyboard focus stuff is super simple, it just adds bubbling keyboard events. Then once it's in:

  • Integrate with bevy_a11y
  • Integrate with LWIM
  • Pluggable extensions for tab navigation
  • Text edit widget
finite magnet
tame aspen
thorny owl
#

i'm looking at the 0.15 rc migration now. there's a place where i'm storing a Text so that i can spawn a (rich) text entity later. given that Text is now split into potentially many separate components / entities and there's no bsn yet to store it all as "data", is there an alternative that i'm missing or would i have to define my own rich text type?
||(or redesign so this isn't necessary--which i was planning on doing anyways for other reasons--but i'd prefer to migrate directly first)||

mental tree
mental tree
thorny owl
#

TextSpan only contains String, so no font / color specified

mental tree
#

Ah right

#

So i guess tuples of the relevant data then

thorny owl
#

a simpler example i remembered now is a bbcode-like markup parser that returns the rich text data as a Text

mental tree
#

Theres also nothing stopping us from creating a RichText(Vec<RichTextElement>) component, that is internally driven by the same systems. But I don't want to go down the "zoo of variants" route unless we have very good reason to

thorny owl
#

yeah ultimately this is the "i want to pass around a hierarchy of entities as data" problem, which will be solved in the general case by bsn

mental tree
#

We can also re-evaluate the component splits / combine everything back into TextSpan if this is a regular sticking point. The split was done for perf / change detection reasons, but we can probably optimize in other ways

#

I'm very bullish on "make simple combined component APIs whenever possible" to improve UX

crude tinsel
mental tree
#

I also think the current TextUiWriter / TextUiReader API usage should largely be replaced by marker components to take advantage of the fact that these are entities / avoid the need to peck around with indices.

But right now that would often be a UX downgrade because it would require a separate query for each marker. Doesn't scale well to many text entries.

Feels like we're missing an API like:

commands.spawn(Text::default())
  .with_child((TextSpan::new("Hi"), Hi))

fn system(text_spans: Query<&mut TextSpan>) {
  text_spans.with::<Hi>().single_mut().0 = "hello".to_string();
}
#

This would be safe (as it isn't accessing component data just checking existence). Doing this inline would have performance concerns though, as it would require scanning all archetypes in the query for the existence of the component.

#

But right now that would often be a UX downgrade because it would require a separate query for each marker

#

Forgot to mention that these queries would conflict with each other by default if we're setting text values, making the UX even worse

mental tree
# mental tree This would be safe (as it isn't accessing component data just checking existence...

We could optimize this with pre-filters:

commands.spawn(Text::default())
  .with_child((TextSpan::new("Foo"), A))
  .with_child((TextSpan::new("Bar"), B))

fn system(text_spans: Query<&mut TextSpan, Or<With<A>, With<B>)>) {
  text_spans.with::<A>().single_mut().0 = "hello".to_string();
  text_spans.with::<B>().single_mut().0 = "world".to_string();
}

But the redundancy / boilerplate there still makes me a bit sad.

I'm thinking something like this might be closer to the "ideal" API in terms of an ergonomics / performance balance:

commands.spawn(Text::default())
  .with_child((TextSpan::new("Foo"), A))
  .with_child((TextSpan::new("Bar"), B))

fn system(text_spans: Query<&mut TextSpan>, a: Marker<A>, b: Marker<B>) {
  text_spans.with(a).single_mut().0 = "hello".to_string();
  text_spans.with(b).single_mut().0 = "world".to_string();
}

Seems like this may have intersections with Query join infrastructure / maybe we could build on top of that

#

Notably Marker<A> would behave like (or maybe even alias to) Query<Entity, With<A>>

#

Which means it would cache the matching archetypes and avoid the need to compute them each time at the call site

serene parcel
# mental tree We could optimize this with pre-filters: ```rust commands.spawn(Text::default()...

You can achieve this:

fn setup(mut commands: Commands) {
    commands.spawn(Text::default())
        .with_child((TextSpan::new("Foo"), A))
        .with_child((TextSpan::new("Bar"), B));
}

fn system(mut text_spans: Query<&mut TextSpan>, mut a: Marker<A>, mut b: Marker<B>) {
    text_spans.with(&mut a).query().single_mut().0 = "hello".to_string();
    text_spans.with(&mut b).query().single_mut().0 = "world".to_string();
}

Using:

type Marker<'world, 'state, T> = Query<'world, 'state, Entity, With<T>>;

trait QueryExt<'world, 'state, D: QueryData, F: QueryFilter> {
    fn with<F2: QueryFilter>(&mut self, query: &mut Query<'_, '_, Entity, F2>) -> QueryLens<'_, D, (F, F2)>;
}

impl<'world, 'state, D: QueryData, F: QueryFilter> QueryExt<'world, 'state, D, F> for Query<'world, 'state, D, F> {
    fn with<F2: QueryFilter>(&mut self, query: &mut Query<'_, '_, Entity, F2>) -> QueryLens<'_, D, (F, F2)> {
        self.join_filtered(query)
    }
}

But I'm not sure if it's that much of an ergonomics win...

finite magnet
#

There are four sets of use cases I think we need to be concerned about:

  • Simple text: button with a "Load" or "Save" label, possibly localized. I think this use case is covered pretty well.
  • Text-from-markup: Slightly less ergonomic today, but possible. Most users won't care, the only ones that do are the ones who are actually writing the conversion, which should ideally be something in a crate which is only implemented once.
  • Basic unstyled text editing, equivalent to HTML input or textarea. Used by games for character names, save games, password login; used by editors for naming assets, labeling scenes and so on. This is not well supported today, but likely will be its own dedicated component, separate from TextBlock, that is provided by Bevy.
  • Advanced editing (rich editing, syntax highlighting, etc.) - I think this is pretty much out of scope for Bevy, if someone wants to work on it fine but it's not something I would prioritize.
crude tinsel
# finite magnet There are four sets of use cases I think we need to be concerned about: * Simple...

I basically agree that the advanced use cases are out of scope for bevy core. But it would be good if there was an integration point for input, focus, rendering, etc so that 3rd-party libs can provide it.

I also think it would nice if the "text-from-markup" cases (of which there are many) could skip the text-as-entities API for performance reasons. It's not great to be repeatedly allocating bevy entities (each time the content changes) just because the underlying text layout functionality isn't exposed.

finite magnet
#

Input and focus are separate issues, and I think we will (soon) have good solutions for these.

warped jolt
mental tree
#

Feels like there has to be a "nice" solve for this, somehow

#

Maybe even a method that converts this:

text_spans.single_with(&mut b)

To this:

text_spans.with(&mut b).query().single_mut()
#

(this would have ownership / scoping issues I think)

serene parcel
mental tree
# serene parcel Yeah I tried returning a `Query` directly but not possible due to returning a re...

Ok I'm thinking a solid "generic solution" is to:

  1. Implement "uncached" queries that are "just" the generated WorldQuery::State. Iterating such a Query involves checking each archetype for matches
  2. Implement CachedQuery::join(UncachedQuery) to allow us to filter down the uncached iteration over all archetypes to the set of cached matching archetypes.
  3. Implement JoinedUncachedQuery::into_single_mut(), which consumes the JoinedUncachedQuery and returns a reference to a single matching element (by consuming, it allows us to return the single result).
#

Then we should be able to implement:

text_spans.single_mut_with::<A>()

which internally is

// uncached_query_filtered returns an Option because it uses WorldQuery::get_state(&text_spans.world.components) under the hood
let uncached_query = text_spans.uncached_query_filtered::<(), With<A>>().unwrap();
text_spans.join_uncached(uncached_query).into_single_mut()
#

People have been wanting (1) to enable things like for foo in world.iter_query::<&Foo>() { }

dense tangle
#

We also have a basic component index so finding all archetypes for &Foo is pretty fast. Having a cleaner separation in the code between the iteration logic/data and the archetype/table matching logic/data is generally a good move for future ECS improvements IMO.

flint compass
mental tree
#

However I can't really think of a reason to not cache a query specified in a system parameter

#

So I don't think we need to build the infrastructure to support fn system(q: UncachedQuery<&mut A>) {}.

#

They could still be used in normal systems, in the context of a filter on an existing query (like the example above)
We would of course need to be careful that we don't inadvertently expand access. Ex: by only using it internally for methods like query.single_with::<A>()

finite magnet
#

@warped jolt Is this correct?

#[require(Node, TextLayout, TextFont, TextColor, TextNodeFlags)]
pub struct Text(pub String);

I thought that Text was supposed to represent a single span? I'm having trouble constructing paragraphs because it's trying to lay out each span as a separate node.

#

Here's what I am working on BTW @mental tree @warped jolt

mental tree
finite magnet
mental tree
#

It would be helpful to mention TextSpan there as well, given that it is a part of replacing TextBundle and would help provide more context on the system's usage

finite magnet
#

OK so I switched to using TextSpan and weird stuff is happening - when I shrink the window, the words at the end of the line simply disappear instead of wrapping - I think that the paragraph height is not getting recalculated.

mental tree
#

Are you spawning everything at once or is this getting dynamically inserted at various points via reactivity?
If this is a bug, getting a non-reactive repro would be a good place to start

#

I hand-checked most of the text examples during my review. But I think they're pretty static / spawned up front in general

near moon
#

There are measure func bugs that can cause text nodes to be missized

#

if you remove any constraints from the text node itself and set them on a wrapping parent node it might help (or not)

near moon
warped jolt
finite magnet
#

There are no reactions in the demo.

finite magnet
#

OK, I changed Text to TextSpan in one more place and now it's working, although I am somewhat confused as to why. That is, I understand the mistake I made, but not why it caused that specific result. The mistake was in using Text rather than TextSpan for the soft line break character, for which I simply insert a space character.

#

In any case, the goal of this experiment was "how hard is it to hook up a markdown parser to produce bevy_ui/text nodes?" and the answer is, "About two hours of coding". (For basic functionality, no code blocks, inline images, or tables, etc.)

warped jolt
finite magnet
warped jolt
#

Maybe having child nodes of a text node borks text measurement.

#

Might be caused by the child text node's linebreak setting affecting measurement of the parent during layout.

finite magnet
#

In any case, it would be fairly easy to split this out as a separate bevy_ui_markdown crate which does not depend on bevy_reactor at all, but which does have a dependency on pulldown_cmark. The only issue is custom styling: a game which wants to have quest descriptions or character bios is going to want to be able to specify the font, text color, and so on. We don't yet have a consensus solution on how to do portable styles, that is, abstract styles that can be passed around as parameters. My approach in bevy_reactor is that "styles are functions", but they require a complex infrastructure that creates the illusion of a fluent API on top of component insertions and removals.

warped jolt
#

I'd rather design a custom markup language from scratch than try to contort markdown into a game-friendly shape.

#

I want to design one similar to bbcode when I have the time, with support for localization (with the expectation that all localization strings will be parsed with the markup engine) using that number-tag approach you mentioned a while back. So a localization entry might be fancy-count = [1]Count:[\1] [2]{count}[\2] and then 1 and 2 need to be assigned in-code like localized.set_tag(1, "b") (and {count} would be a variable you can assign using the fluent framework).

tulip hinge
#

Yes, markdown is a document authoring system designed to be human readable, not a text formatting system or a general system designed to be machine readable. What you actually need is a way to arbitrarily annotate/mark/tag certain spans of text.

There are many projects that have trod this ground to draw inspiration from.

For example, asciidoc custom roles https://docs.asciidoctor.org/asciidoc/latest/text/text-span-built-in-roles/:

We need [.line-through]#ten# twenty VMs.
A [.myrole]#custom role# must be fulfilled by the theme.

Another example is the project https://github.com/mbakeranalecta/sam

In {Rio Bravo}(movie), {the Duke}(actor "John Wayne" (SAG)) plays a union colonel.

Maybe that looks like overkill for common cases like bold, italic and hyperlinks and it feels like you're missing the ease of markdown. Well, what all of these projects effectively do is provide syntax sugar. That is:

**this is bold**

effectively desugars to

{this is bold}(bold)

If you're married to Markdown, one more recent proposal that exists is Semantic Markdown https://hackmd.io/@sparna/semantic-markdown-draft#What-is-Semantic-Markdown

My name is
[Manu Sporny]{:name}
and you can give me a ring via
[1-800-555-0199]{:telephone}.

To be honest I think it's a major oversight for fluent, being built for rich user-facing applications, not to provide some means of arbitrarily tagging spans of text built in to it.

tulip hinge
#

As for the number-tagged system [1]Count:[\1]:

  1. I prefer syntaxes that don't duplicate the "marker" (i.e. I prefer something like {Count:}[marker] or marker!{Count:}.

  2. Numbered tags feels very "brittle". You write localized.set_tag(1, "b") and later someone comes across and decides they want to use localized.set_tag(2, "b") or localized.set_tag(1, "a"). This is the same problem as you have with magic numbers, essentially. Named tags feels better as they can be self documenting.

  3. There should ideally be a way to inject additional data. Think hyperlinks: you need to specify a URL. Another example is coloured text: you need to specify a colour.

  4. They should ideally be able to compose, e.g. The {Bevy game engine}[bold, color(#ececec), url(https://bevyengine.org/)] is a refreshingly simple data-driven game engine

warped jolt
# tulip hinge As for the number-tagged system `[1]Count:[\1]`: 1. I prefer syntaxes that don'...

The goal of the numbered tags is to ensure the localization files don't contain any formatting logic, so translators don't have any power to add crazy formatting (or e.g. exploit a DoS vector if for example a super large font size causes system crash; also hyperlinks are very vulnerable). I also don't love the tag duplication, and named tags are a good suggestion (although need to be careful to disambiguate tags from actual settings like bold/b).

finite magnet
warped jolt
#

Maybe The {Bevy game engine}[@arbitrary_tag1 @arbitrary_tag2] is a refreshingly simple data-driven game engine would work (simplify: no commas, the () functional syntax is a good idea). Then the first pass to parse a localization string replaces tags with values registered in rust, and discards any non-tag stuff in the original string.

cunning hollow
#

Can this markdown system support tags with custom behaviours like [on hover: show item tooltip] or [on click: open inventory and highlight a specific item]?

#

It would also be neat to be able to insert icons into text using tags eg. a picture of a sword before a damage value

warped jolt
# cunning hollow Can this markdown system support tags with custom behaviours like [on hover: sho...

It might be possible to do so directly with function reflection. I expect you'd need to manually register a 'function id'-to-system callback mapping in the markdown component. Then the markdown component will auto-pipe that system to the correct entity.

So like:

let mut mt = MarkupText::new("My {cat}[on_hover(sparkly cat_info)] is so cuddly!");
mt.add_animation("sparkly", sparkly_text_effect); // Not sure exactly.
mt.add_callback_with_cleanup("caf_info", spawn_cat_info, cleanup_cat_info);
#

Actually would probably use a different approach for animations since for that you often have multiple different things for hover/press/etc. and it's easier to package them separately.

wooden drum
#

What I did for bevy_mod_bbcode is to allow the user to register custom marker components which can then be added to the text entities.
For example, with [m=foo]test[/m], it could insert a custom Foo marker component and the user can use it to implement hover logic or any other custom logic on the text

cunning hollow
#

Without duplicated tags how would you support overlapping styles as in

#

[b]This [i]example[/b] sentence[/i]

shadow marsh
# cunning hollow `[b]This [i]example[/b] sentence[/i]`

I personally chose not to, forcing users to do

[b]This [b,i]example[i] sentence[] back to default

I'm also storing "styles" associated with b and i as entities and cloning their components onto text spans. Users just spawn them.

commands.spawn((
   RegisteredStyle("b"),
   TextFont { ... },
   TextColor(...),
   SomeMarkerComponent
));

This can all be done without macros. Everything must be Reflect though. Do whatever fancy stuff you want in normal systems.

I'm still not totally sure if I like the "end-tag-less" approach though.

cunning hollow
#

Having the style data separated from the tag might be beneficial in cases where you want to apply the same style to multiple different areas of the text. That way you don’t need to duplicate the style data within the text each time you use that style and it can easily be updated in one place

wooden drum
wooden drum
#

Just updated bevy_mod_bbcode to Bevy 0.15.0-rc.2 and it was surprisingly straightforward, even with the draft migration guide!
Once it compiled it just worked again.
The hierarchical text layout is quite nice for this plugin, I actually already used just one span per Text already to emulate text-as-entities, so having it less hacky now is nice.
Also required components allowed me to remove a bunch of boilerplate.

Here's the update PR if anyone's interested: https://github.com/TimJentzsch/bevy_mod_bbcode/pull/17

Great work y'all!

mental tree
finite magnet
#

@tame aspen and others: if anyone is interested, the latest version of my "inheritable text styles" abstraction can be found here: https://github.com/viridia/thorium_ui/blob/main/crates/thorium_ui_controls/src/text_styles.rs - this includes both a trigger hook to compute the text style when the text entity is first inserted, as well as a system which keeps text styles up to date when inherited style definitions change. This behavior requires opting-in via a marker component.

This also includes the "either a handle or an asset path" functionality we discussed.

tame aspen
#

Excellent, that's pretty much exactly the sort of design I would like to see for heritable style

#

You could use immutable components or OnMutate observers for keeping synced instead in the future, rather than change detection

#

But that's just a performance question really

finite magnet
#

I think the main performance issue is that changing an inherited style high up in the tree can potentially affect an unknown number of descendant entities. However, in practice that never happens, generally text styles high in the tree are set once and never changed.

tame aspen
#

The biggest issue is going to be the O(n) performance of the current change detection impl

finite magnet
#

It's really nice to be able to set the font handle once at the root of the tree and then never have to worry about it.

tame aspen
#

But all sorts of users want that improved anyways

shadow marsh
#

Don't have time to fully debug right this second, but I'm seeing a bizarre text issue where

- text
   - textspan, textcolor, textfont
   - textspan, textcolor, textfont

does not work (no text displayed at all), but

- text, textcolor, textfont
   - textspan, textcolor, textfont
   - textspan, textcolor, textfont

does. possibly only in situations where a mix of the default / non-default font handle is used

warped jolt
shadow marsh
warped jolt
shadow marsh
#

I diagnosed the issue, details in issue linked above.

mental tree
shadow marsh
#

Nice

thorny owl
#

i'm curious about the text rework having TextColor and TextFont components separate from the primary Text component after reading this design guideline:

Prefer simple APIs / don't over-componentize. By default, if you need to attach new properties to a concept, just add them as fields to that concept's component. Only break out new components / concepts when you have a good reason, and that reason is motivated by user experience or performance (and weight user experience highly).

#

i assume the exception is for a good reason but it wasn't explained in the release post

#

and then contrasted with Sprite and UiImage's optional components (texture atlas and scale mode) which became Option fields

finite magnet
warped jolt
mental tree
#

Seems like we could find ways to avoid text recomputes without relying directly on change detection

#

(or solely on it)

finite magnet
mental tree
#

Ex: cache the "color since last check" on a computed component

robust solstice
#

What's the status of this group now that we have cosmic text and the new font api?

#

Is the work complete?

warped jolt
tame aspen
#

Yeah I'm tempted to close this out and move to #ui-dev ?

#

All of those things need to be done, but I think the big scary bit is complete

warped jolt
tame aspen
#

Yep!

finite magnet
tame aspen
#

I think that font families warrants a design doc, but if that gets too unruly we can always make a new working group 🙂

#

Everything else is pretty small thankfully

finite magnet
warped jolt
tulip hinge
#

cosmic-text now exposes outline curves (not sure which version that happened in), so some low hanging fruit would be to expose that, then potentially build a mesh-text backend (e.g. using lyon): great for reasonably high fidelity billboards in 3d. additionally, we could now apply transformations to the curves so we can do skew, scale, rotation, etc. and more word-art style things, maybe even text outlining and shadows, however that's rendered: if that were supported in a 2d rasterized context, we would probably need to revisit the idea of opting out of glyph caching so we don't run into the original issue around font resizing.

#

alternatively maybe the future is in a vello-based or inspired compute shader-based renderer, which kinda does it all, except I guess 3d (though maybe that just needs some transformations to make that work)

shadow marsh
#

Finally migrating one of my more text-heavy apps. So far this sort of stuff seems like the least fun part.

fn update_target_text<R: TextRoot>

It's much more annoying to deal with text that may be a Text2d or a Text.

robust solstice
#

yeah, i have found that annoying as well.

#

can you put both a Text and a TextSpan on the same entity?

shadow marsh
#

I dunno, sounds scary.

robust solstice
#

my point is more, maybe Text and Text2d should be markers and TextSpan reserved for content.

#

Currently we are mixing behaviors in a weird way

shadow marsh
#

Oh, yeah, that's also where my had was at

robust solstice
#

(my ideal world would be TextUi and Text2d + Text content but i know that's not going to happen)

shadow marsh
#

But that idea seems like something would be called a "ux regression"

robust solstice
#

it's mildly annoying that the more common type has the longer (less discoverable) name as well.

#

feel like people are going to try and make a tree of Text, rather than Text root + TextSpan nodes.

shadow marsh
#

I wonder if TextWriter could be rewritten with stuff like Query<(Option<&mut Text>, Option<&mut Text2d>), With<Or<(Text, Text2d)>>)> or whatever. Gross, but the grossness would be contained in bevy.

crude tinsel
warped jolt
robust solstice
#

the end result is that we now have 3 (..n from the ecosystem) components that all behave like text

#

seems kind of contradictory to the direction of required components imo

crimson flume
mental tree
#

Cross context edits are a less common scenario, and when it’s being done it’s probably by a generalized “text editing system” rather than something specific. I’m fine with foisting the comparatively small complexity on those cases

#

When compared to making every single text spawn in bevy significantly worse

shadow marsh
#

Well, the current arrangement does seem to make "garbage collection" trivial. I feel like we'd still want separate atlases per font handle at least.

shadow marsh
#

Not personally seeing any downsides to this

robust solstice
crimson flume
#

I reckon it shouldn't be too difficult and unlikely to be a performance bottleneck if we abstracted the storage away from the text shaping and layouting, no?

shadow marsh
#

Maybe? It feels like what you’re doing could be a trade off not everyone wants, so it makes sense to me in that regard. I’m just trying to understand that trade off because improving things by default for all users would be good.

I think others probably want to plug in their own text renderers as well.

shadow marsh
#

Ah, glyphs with TextFont { font_smoothing: false } need to be stored separately so that the underlying texture can have ImageSampler::nearest().

shadow marsh
shadow marsh
#

Okay, done playing with this for now. Left some notes in the issue.

shadow marsh
tame aspen
#

This smells like a cosmic-text bug to me

#

And aha, this is what I saw in my example from yesterday

tulip hinge
#

currently travelling so can only really look at it on my phone right now but the logic seems right but I'd just look at whether visual_line.w is being correctly extended here, maybe some floating point arithmetic bug

https://github.com/pop-os/cosmic-text/blob/main/src/shape.rs

        let line_width = match width_opt {
            Some(width) => width,
            None => {
                let mut width: f32 = 0.0;
                for visual_line in visual_lines.iter() {
                    width = width.max(visual_line.w);
                }
                width
            }
        };



            let alignment_correction = match (align, self.rtl) {
                (Align::Left, true) => line_width - visual_line.w,
                (Align::Left, false) => 0.,
                (Align::Right, true) => 0.,
                (Align::Right, false) => line_width - visual_line.w,
                (Align::Center, _) => (line_width - visual_line.w) / 2.0,
                (Align::End, _) => line_width - visual_line.w,
                (Align::Justified, _) => 0.,
            };
GitHub

Pure Rust multi-line text handling. Contribute to pop-os/cosmic-text development by creating an account on GitHub.

shadow marsh
#

Appreciate the response. As far as I can tell, layout is happening "line by line" so the "max width" in the pointed-at code is only relevant if a line gets wrapped onto multiple "visual lines."

There doesn't seem to be any mechanism for adjusting the entire text after layout is done.

So an unbounded buffer with line breaks just doesn't seem to be something cosmic is doing anything with. More of a missing feature than a bug, maybe?

shadow marsh
#

I'm not even sure if it's possible to implement on the cosmic side.

crude tinsel
#

In Parley I made alignment a seperate phase that the caller can pass a seperate width constraint to

#

I think it would be possible to do inside the library. But designing the interface would be tricky and making it a separate call was easy.

shadow marsh
#

Okay yeah, that's where my thinking was headed too -- I have no idea how to many something like that work with the incremental layout stuff that's going on in cosmic.

Might just do it on the Bevy side though, unless cosmic authors see this as a bug and/or wanted feature and have a design in mind.

#

I'm actually fairly certain @near moon wrote the code we need at some point, lol.

near moon
#

Yeah I had to make a lot of post layout adjustments to work around different ab_glyph bugs before

near moon
#

Both options make sense though

#

The text2d problem isn't a bug, the Text2d API is just really confusing to use. The way to fix the user's problem is to set a large width text bound withAnchor::Center

tulip hinge
#

cosmic-text attempts to do the latter here too

near moon
#

Oh does it

tulip hinge
#

so it does seem like a bug

tulip hinge
#

the reason we'd want to make cosmic-text's work is so that the built-in hit detection works as expected, for mouse interactions

near moon
#

yeah, otherwise it would be simple to calculate the visual width again in bevy and center the text ourselves

tulip hinge
#

but didn't play well with the hit detection

near moon
#

we could center the text ourselves and modify the mouse coordinates we send to cosmic-text, adjusted as though the text was still left-aligned

#

it's a bit of a hack but not difficult to setup

tulip hinge
#

true

tulip hinge
#

On the one hand, when you set the text in the Buffer, it asks for an alignment, implying you want the alignment to apply to the whole buffer.

#

On the other hand, each BufferLine has the ability to set an alignment, implying buffers can have mixed alignments.

#

like:

left align
               right align
      center align
shadow marsh
#

I guess I was headscratching over how "post-layout alignment" would jive with the the e.g. shape_to_scroll "incremental" API, in the case of a buffer that has a vertical bound but not a horizontal one.

#

It seems like if you're not shaping the whole buffer, you can't calculate the max width to align stuff properly?

tulip hinge
#

Yeah, but I think the unbounded layout was an afterthought to be honest

shadow marsh
#

But

In Parley I made alignment a seperate phase that the caller can pass a seperate width constraint to
-- nico
Makes sense to me

tulip hinge
#

right but does Parley have mixed alignment?

shadow marsh
#

I think it might work fine with mixed alignment

tulip hinge
#

which to me is more reasonable

#

it feels like there's a bit of a confusion between what I'd call "blocks" and "lines" in cosmic-text

shadow marsh
#

I think I've been calling those (groups of "visual lines") and "lines" but possibly

tulip hinge
#

I think we agree on definition of lines there

#

i mean like a paragraph, or a block layout, as a block

#

which is more like a Buffer

#

but in cosmic-text's case it's actually a BufferLine

#

it's sort of a mix

shadow marsh
#

Ah okay

tulip hinge
#

I've left a comment on the issue and pinged Jeremy so I guess we'll see what he says.

We could just go back to aligning everything to the left, then aligning the glyphs manually, as suggested by ickshonpe. But we won't have Justified text then (since that is word-aware), though I can't recall if we even support that.

shadow marsh
#

Thanks! And, that's a good point, we do I think.

near moon
#

there is a simple fix

#

I think

#

just run text layout twice

#

you run it once unbounded, then run it back again with the text bounds generated by the first run

#

it's not very efficient but you only need to do it for non-left-justified unbounded text

shadow marsh
#

ooh, clever

shadow marsh
#

yeah, that seems to work. I'll open a PR soon.

I am also pretty confident that we are doing one additional shape_until_scroll that isn't necessary most of the time. And possibly like 2-3 more the first time the text is processed, or if the size, font size, or wrapping change. Most of these buffer.set_ functions can cause reshaping and/or relayout.

I'll test that theory after. I think that could be partially fixed on our end.

crude tinsel
#

Yeah, cosmic-text really ought to changed to shape lazily so that multiple write can be coalesced into one relayout.

near moon
#

oh wait bestRanar you're rparret? I had no idea hehehe

crude tinsel
shadow marsh
#

Yeah, sorry. Discord is still sort of a strange place to me, haven't really wanted to make any effort to tie the two identities together.

shadow marsh
shadow marsh
#

Is there no longer a way to get the cosmic Buffer for some text? I thought we had that in an earlier iteration.

Seems necessary for picking: https://github.com/bevyengine/bevy/issues/17706 adds a way to get it non-mutably.

But also if you want to do a text input with a cosmic Editor you need a mutable ref.

warped jolt
#

It should not be needed for picking. TextLayoutInfo should be sufficient

#

From ComputedTextBlock::buffer:

/// This is private because buffer contents are always refreshed from ECS state when writing glyphs to
/// `TextLayoutInfo`. If you want to control the buffer contents manually or use the `cosmic-text`
/// editor, then you need to not use `TextLayout` and instead manually implement the conversion to
/// `TextLayoutInfo`.
shadow marsh
# warped jolt From `ComputedTextBlock::buffer`: ```rust /// This is private because buffer con...

Hm, I see. robtfm had something working in bevy_simple_text_input that created an Editor but still used bevy's copy of the Buffer. I'll have to look closer and try to understand what's going on there. I think it was syncing the state of the editor back to the Text afterwards or something. But this was based on the first cosmic PR.

re: picking, using cosmic's built-in hit detection would be nice... bytemunch had issues with the current state of TextLayoutInfo trying to do picking, but I didn't look close enough to see if they were surmountable in some other way.

#ui-dev message

#

Ick made a decent argument for not using it (hit) though

warped jolt
#

It's kind of convoluted and inefficient to use the editor to set text, which then resets the buffer. Better to just use the editor to set a buffer, then directly use that buffer to make layout glyphs. And save the text value in a custom component.

#

No need to cram all functionality into the existing text API. Text input is a huge project, so it will benefit from starting off on fresh ground and not tied to the non-text-input architecture.

shadow marsh
shadow marsh
#

(And side-goal: enable other people to build fancier third party text inputs)

crude tinsel
#

IMO the raw CosmicBuffer ought to be the primary text API in Bevy

#

Everything else (including the text-as-entities API) ought to be higher-level APIs on top

#

If that means breaking changes if/when Bevy switches to a different text backend then... so be it?

#

Maybe "primary" is overstating things. I think it ought to be a public supported API that other people are encouraged to build abstractions on top of. But with end-users encouraged to use one of those abstractions, be that the built-in one or some other 3rd-party one.

warped jolt
shadow marsh
calm notch
#

I took a bit of a break from social media, but I'm back.

shadow marsh
#

Thank you!

shadow marsh
shadow marsh
#

Awesome, Thank you!

calm notch