emilk / egui

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
https://www.egui.rs/
Apache License 2.0
20.85k stars 1.5k forks source link

`epaint::text::Glyph` should own a grapheme cluster, not a char #2532

Open parasyte opened 1 year ago

parasyte commented 1 year ago

epaint should probably use unicode-segmentation to split strings with Unicode awareness. And ultimately the Glyph type needs to own a grapheme cluster instead of a char. I.e., the chr field might be changed to a String, Range, or even &'a str.

This should help improve written language integration, and will also fix bugs with text rendering. For example, if you insert an emoji with skin tone (which is composed of two Unicode code points) egui will render two Glyphs:

image
use eframe::egui;

fn main() {
    let options = eframe::NativeOptions {
        initial_window_size: Some(egui::vec2(320.0, 240.0)),
        ..Default::default()
    };
    eframe::run_native(
        "Thumbs Up",
        options,
        Box::new(|_cc| Box::<MyApp>::default()),
    );
}

#[derive(Default)]
struct MyApp {}

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.label("šŸ‘šŸ¾");
        });
    }
}

You can see the specific code points with the escape_unicode() method:

fn main() {
    println!("šŸ‘šŸ¾: {}", "šŸ‘šŸ¾".escape_unicode());
}

Prints:

šŸ‘šŸ¾: \u{1f44d}\u{1f3fe}

Additional context:

I discovered this issue as I was making a terminal-emulator-like project. I am rendering strings directly with Galleys and I have a need to count the number of fixed-width columns in each Galley row. char does not accurately map to a single cell in my "terminal" (as demonstrated above). But the Glyph type and its Row counterpart are the closest thing that egui exposes for getting the length of a row after it is broken into paragraphs by text layout.

emilk commented 1 year ago