bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
35.84k stars 3.54k forks source link

TextSpan can not recieve Pointer<Click> events #15984

Open rewin123 opened 3 days ago

rewin123 commented 3 days ago

What problem does this solve or what need does it fill?

Text can accept Pointer events, but TextSpan cannot. I wanted to make a link inside the large text and I can't implement this with multiple Texts because the layout of the text will be bad.

What solution would you like?

Add support to recieve Pointer events for TextSpan

What alternative(s) have you considered?

Provide some other check that the mouse is over a particular TextSpan

Additional context

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_observer(span_observer)
        .add_systems(Startup, setup)
        .run();

}

fn setup(
    mut commands: Commands,
) {
    commands.spawn(Camera2d::default());
    commands.spawn(
        Text::new("")
    ).with_child(TextSpan::from("text_span"));
}

fn span_observer(
    trigger: Trigger<Pointer<Click>>,
    q_spans: Query<(Entity, &TextSpan)>,
    q_text: Query<(Entity, &Text)>,
) {
    if let Ok((e, _)) = q_spans.get(trigger.entity()) {
        info!("span_observer {}", e); //never called event if click directly on text span
    }

    if let Ok((e, _)) = q_text.get(trigger.entity()) {
        info!("text_observer {}", e);
    }
}
2024-10-18T11:32:03.020663Z  INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19045", cpu: "AMD Ryzen 7 2700X Eight-Core Processor", core_count: 
"8", memory: "63.9 GiB" }
2024-10-18T11:32:03.578891Z  INFO bevy_render::renderer: AdapterInfo { name: "NVIDIA TITAN Xp", vendor: 4318, device: 6914, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "560.94", backend: Vulkan }
2024-10-18T11:32:04.556414Z  INFO bevy_winit::system: Creating new window "App" (0v1#4294967296)
2024-10-18T11:32:08.781002Z  INFO bug: text_observer 8v1
ickshonpe commented 3 days ago

You can make this work currently by either:

  1. Splitting up your text into one Text node entity per word and then having Taffy layout the words using FlexWrap and JustifyContent automatically. Then you can check for clicks on the individual Text node entity that contains your link. It's easier if the text is a single line, then you only need to split the text into three Text entities.
  2. Looking up the glyph geometry in TextLayoutInfo for the glyphs you want to be pickable and using the Text node's GlobalTransform to transform the glyph coords so that they can be compared to the pointer's position.

Both methods are really clumsy and annoying though. It's not very hard to implement a builtin solution for Bevy but there isn't time for it before the 0.15 release I think.

rewin123 commented 3 days ago

I agree that such functionality can wait. Although its absence leads to ugly code. And I want to try option 1 as a temporary solution. Thanks for the idea.