LibGfx+LibGUI: Support multi code point emojis in text painting :^)
This necessitates switching from passing a single code point to the callback to passing a non-const Utf8CodePointIterator instead. Note that the text selection mechanisms in LibGUI and LibWeb don't handle this properly yet; they still assume that each code point renders as one glyph. Similarly, width calculations for text widths don't either, so a single such an emoji will require space for more than one glyph. It also doesn't work in LibVT's TerminalWidget, where each code point is handled and rendered separately, so LibGfx never gets a chance to check if subsequent code points could result in a combined emoji.
This commit is contained in:
parent
e1eb882b1c
commit
cab032f1ee
Notes:
sideshowbarker
2024-07-17 18:21:00 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/cab032f1ee Pull-request: https://github.com/SerenityOS/serenity/pull/12736 Reviewed-by: https://github.com/davidot ✅
3 changed files with 50 additions and 32 deletions
|
@ -569,9 +569,9 @@ void AbstractView::keydown_event(KeyEvent& event)
|
|||
if (is_searchable()) {
|
||||
if (event.key() == KeyCode::Key_Backspace) {
|
||||
if (!m_highlighted_search.is_null()) {
|
||||
//if (event.modifiers() == Mod_Ctrl) {
|
||||
// TODO: delete last word
|
||||
//}
|
||||
// if (event.modifiers() == Mod_Ctrl) {
|
||||
// TODO: delete last word
|
||||
// }
|
||||
Utf8View view(m_highlighted_search);
|
||||
size_t n_code_points = view.length();
|
||||
if (n_code_points > 1) {
|
||||
|
@ -704,7 +704,7 @@ void AbstractView::draw_item_text(Gfx::Painter& painter, ModelIndex const& index
|
|||
|
||||
// Highlight the text background first
|
||||
auto background_searching_length = searching_length;
|
||||
painter.draw_text([&](Gfx::IntRect const& rect, u32) {
|
||||
painter.draw_text([&](Gfx::IntRect const& rect, Utf8CodePointIterator&) {
|
||||
if (background_searching_length > 0) {
|
||||
background_searching_length--;
|
||||
painter.fill_rect(rect.inflated(0, 2), palette().highlight_searching());
|
||||
|
@ -716,12 +716,12 @@ void AbstractView::draw_item_text(Gfx::Painter& painter, ModelIndex const& index
|
|||
auto text_searching_length = searching_length;
|
||||
auto highlight_text_color = palette().highlight_searching_text();
|
||||
searching_length = searching_text.length();
|
||||
painter.draw_text([&](Gfx::IntRect const& rect, u32 code_point) {
|
||||
painter.draw_text([&](Gfx::IntRect const& rect, Utf8CodePointIterator& it) {
|
||||
if (text_searching_length > 0) {
|
||||
text_searching_length--;
|
||||
painter.draw_glyph_or_emoji(rect.location(), code_point, font, highlight_text_color);
|
||||
painter.draw_glyph_or_emoji(rect.location(), it, font, highlight_text_color);
|
||||
} else {
|
||||
painter.draw_glyph_or_emoji(rect.location(), code_point, font, text_color);
|
||||
painter.draw_glyph_or_emoji(rect.location(), it, font, text_color);
|
||||
}
|
||||
},
|
||||
text_rect, item_text, font, alignment, elision);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
|
||||
* Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -1240,20 +1241,31 @@ void Painter::draw_emoji(IntPoint const& point, Gfx::Bitmap const& emoji, Font c
|
|||
|
||||
void Painter::draw_glyph_or_emoji(IntPoint const& point, u32 code_point, Font const& font, Color color)
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append_code_point(code_point);
|
||||
auto it = Utf8View { builder.string_view() }.begin();
|
||||
return draw_glyph_or_emoji(point, it, font, color);
|
||||
}
|
||||
|
||||
void Painter::draw_glyph_or_emoji(IntPoint const& point, Utf8CodePointIterator& it, Font const& font, Color color)
|
||||
{
|
||||
// FIXME: Handle variation selectors. Additionally, some emojis start with a 'regular' character,
|
||||
// so the 'font contains' check isn't sufficient for all of them - we'll have to peek.
|
||||
|
||||
u32 code_point = *it;
|
||||
|
||||
if (font.contains_glyph(code_point)) {
|
||||
draw_glyph(point, code_point, font, color);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perhaps it's an emoji?
|
||||
auto* emoji = Emoji::emoji_for_code_point(code_point);
|
||||
if (emoji == nullptr) {
|
||||
dbgln_if(EMOJI_DEBUG, "Failed to find an emoji for code_point {}", code_point);
|
||||
draw_glyph(point, 0xFFFD, font, color);
|
||||
if (auto const* emoji = Emoji::emoji_for_code_point_iterator(it)) {
|
||||
draw_emoji(point, *emoji, font);
|
||||
return;
|
||||
}
|
||||
|
||||
draw_emoji(point, *emoji, font);
|
||||
dbgln_if(EMOJI_DEBUG, "Failed to find a glyph or emoji for code_point {}", code_point);
|
||||
draw_glyph(point, 0xFFFD, font, color);
|
||||
}
|
||||
|
||||
template<typename DrawGlyphFunction>
|
||||
|
@ -1295,7 +1307,8 @@ void draw_text_line(IntRect const& a_rect, Utf8View const& text, Font const& fon
|
|||
space_width = -space_width; // Draw spaces backwards
|
||||
}
|
||||
|
||||
for (u32 code_point : text) {
|
||||
for (auto it = text.begin(); it != text.end(); ++it) {
|
||||
auto code_point = *it;
|
||||
if (code_point == ' ') {
|
||||
point.translate_by(space_width, 0);
|
||||
continue;
|
||||
|
@ -1303,9 +1316,12 @@ void draw_text_line(IntRect const& a_rect, Utf8View const& text, Font const& fon
|
|||
IntSize glyph_size(font.glyph_or_emoji_width(code_point) + font.glyph_spacing(), font.glyph_height());
|
||||
if (direction == TextDirection::RTL)
|
||||
point.translate_by(-glyph_size.width(), 0); // If we are drawing right to left, we have to move backwards before drawing the glyph
|
||||
draw_glyph({ point, glyph_size }, code_point);
|
||||
draw_glyph({ point, glyph_size }, it);
|
||||
if (direction == TextDirection::LTR)
|
||||
point.translate_by(glyph_size.width(), 0);
|
||||
// The callback function might have exhausted the iterator.
|
||||
if (it == text.end())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1558,8 +1574,8 @@ void Painter::draw_text(IntRect const& rect, Utf32View const& text, TextAlignmen
|
|||
void Painter::draw_text(IntRect const& rect, StringView raw_text, Font const& font, TextAlignment alignment, Color color, TextElision elision, TextWrapping wrapping)
|
||||
{
|
||||
Utf8View text { raw_text };
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, u32 code_point) {
|
||||
draw_glyph_or_emoji(r.location(), code_point, font, color);
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, Utf8CodePointIterator& it) {
|
||||
draw_glyph_or_emoji(r.location(), it, font, color);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1570,31 +1586,31 @@ void Painter::draw_text(IntRect const& rect, Utf32View const& raw_text, Font con
|
|||
StringBuilder builder;
|
||||
builder.append(raw_text);
|
||||
auto text = Utf8View { builder.string_view() };
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, u32 code_point) {
|
||||
draw_glyph_or_emoji(r.location(), code_point, font, color);
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, Utf8CodePointIterator& it) {
|
||||
draw_glyph_or_emoji(r.location(), it, font, color);
|
||||
});
|
||||
}
|
||||
|
||||
void Painter::draw_text(Function<void(IntRect const&, u32)> draw_one_glyph, IntRect const& rect, Utf8View const& text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
void Painter::draw_text(Function<void(IntRect const&, Utf8CodePointIterator&)> draw_one_glyph, IntRect const& rect, Utf8View const& text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
{
|
||||
VERIFY(scale() == 1); // FIXME: Add scaling support.
|
||||
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, u32 code_point) {
|
||||
draw_one_glyph(r, code_point);
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, Utf8CodePointIterator& it) {
|
||||
draw_one_glyph(r, it);
|
||||
});
|
||||
}
|
||||
|
||||
void Painter::draw_text(Function<void(IntRect const&, u32)> draw_one_glyph, IntRect const& rect, StringView raw_text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
void Painter::draw_text(Function<void(IntRect const&, Utf8CodePointIterator&)> draw_one_glyph, IntRect const& rect, StringView raw_text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
{
|
||||
VERIFY(scale() == 1); // FIXME: Add scaling support.
|
||||
|
||||
Utf8View text { raw_text };
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, u32 code_point) {
|
||||
draw_one_glyph(r, code_point);
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, Utf8CodePointIterator& it) {
|
||||
draw_one_glyph(r, it);
|
||||
});
|
||||
}
|
||||
|
||||
void Painter::draw_text(Function<void(IntRect const&, u32)> draw_one_glyph, IntRect const& rect, Utf32View const& raw_text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
void Painter::draw_text(Function<void(IntRect const&, Utf8CodePointIterator&)> draw_one_glyph, IntRect const& rect, Utf32View const& raw_text, Font const& font, TextAlignment alignment, TextElision elision, TextWrapping wrapping)
|
||||
{
|
||||
VERIFY(scale() == 1); // FIXME: Add scaling support.
|
||||
|
||||
|
@ -1603,8 +1619,8 @@ void Painter::draw_text(Function<void(IntRect const&, u32)> draw_one_glyph, IntR
|
|||
StringBuilder builder;
|
||||
builder.append(raw_text);
|
||||
auto text = Utf8View { builder.string_view() };
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, u32 code_point) {
|
||||
draw_one_glyph(r, code_point);
|
||||
do_draw_text(rect, text, font, alignment, elision, wrapping, [&](IntRect const& r, Utf8CodePointIterator& it) {
|
||||
draw_one_glyph(r, it);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/FontDatabase.h>
|
||||
|
@ -75,14 +76,15 @@ public:
|
|||
void draw_text(IntRect const&, StringView, TextAlignment = TextAlignment::TopLeft, Color = Color::Black, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(IntRect const&, Utf32View const&, Font const&, TextAlignment = TextAlignment::TopLeft, Color = Color::Black, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(IntRect const&, Utf32View const&, TextAlignment = TextAlignment::TopLeft, Color = Color::Black, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(Function<void(IntRect const&, u32)>, IntRect const&, StringView, Font const&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(Function<void(IntRect const&, u32)>, IntRect const&, Utf8View const&, Font const&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(Function<void(IntRect const&, u32)>, IntRect const&, Utf32View const&, Font const&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(Function<void(IntRect const&, Utf8CodePointIterator&)>, IntRect const&, StringView, Font const&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(Function<void(IntRect const&, Utf8CodePointIterator&)>, IntRect const&, Utf8View const&, Font const&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_text(Function<void(IntRect const&, Utf8CodePointIterator&)>, IntRect const&, Utf32View const&, Font const&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None, TextWrapping = TextWrapping::DontWrap);
|
||||
void draw_ui_text(Gfx::IntRect const&, StringView, Gfx::Font const&, TextAlignment, Gfx::Color);
|
||||
void draw_glyph(IntPoint const&, u32, Color);
|
||||
void draw_glyph(IntPoint const&, u32, Font const&, Color);
|
||||
void draw_emoji(IntPoint const&, Gfx::Bitmap const&, Font const&);
|
||||
void draw_glyph_or_emoji(IntPoint const&, u32 code_point, Font const&, Color);
|
||||
void draw_glyph_or_emoji(IntPoint const&, u32, Font const&, Color);
|
||||
void draw_glyph_or_emoji(IntPoint const&, Utf8CodePointIterator&, Font const&, Color);
|
||||
void draw_circle_arc_intersecting(IntRect const&, IntPoint const&, int radius, Color, int thickness);
|
||||
|
||||
enum class CornerOrientation {
|
||||
|
|
Loading…
Add table
Reference in a new issue