Highlight URL with skin marked text color

This commit is contained in:
Benau 2021-09-06 11:05:24 +08:00
parent 8f41b721ed
commit 280d8fc114
4 changed files with 103 additions and 10 deletions

View File

@ -23,6 +23,7 @@ namespace gui
enum ShapeFlag
{
SF_DISABLE_CACHE = 1, /* Disable caching glyph layouts. */
SF_DISABLE_URL_HIGHLIGHT = 2, /* Disable URL highlight. */
};
enum GlyphLayoutFlag
@ -32,7 +33,8 @@ GLF_RTL_CHAR = 2, /* This character(s) from this glyph is RTL. */
GLF_BREAKABLE = 4, /* This glyph is breakable when line breaking. */
GLF_QUICK_DRAW = 8, /* This glyph is not created by libraqm, which get x_advance_x directly from font. */
GLF_NEWLINE = 16, /* This glyph will start a newline. */
GLF_COLORED = 32 /* This glyph is a colored one (for example emoji). */
GLF_COLORED = 32, /* This glyph is a colored one (for example emoji). */
GLF_URL = 64 /* This glyph contains clickable url (https or http atm). */
};
enum GlyphLayoutDraw

View File

@ -263,7 +263,8 @@ namespace LineBreakingRules
// ----------------------------------------------------------------------------
/* Turn text into glyph layout for rendering by libraqm. */
void FontManager::shape(const std::u32string& text,
std::vector<irr::gui::GlyphLayout>& gls)
std::vector<irr::gui::GlyphLayout>& gls,
u32 shape_flag)
{
// Helper struct
struct ShapeGlyph
@ -300,6 +301,82 @@ void FontManager::shape(const std::u32string& text,
if (text.back() == U'\n')
lines.push_back(U"");
// URL marker
std::vector<std::pair<int, int> > http_pos;
auto fix_end_pos = [](const std::u32string& url, size_t start_pos,
size_t pos)->size_t
{
// https:// has 8 characters, shortest URL has 3 characters (like t.me)
// so 8 is valid for http:// too
size_t next_forward_slash = url.find(U'/', start_pos + 8);
if (next_forward_slash > pos)
next_forward_slash = std::string::npos;
// Tested in gnome terminal, URL ends with 0-9, aA-zZ, /- or ~:_=#$%&'+@*]) only
// ~:_=#$%&'+@*]) will not be highlighted unless it's after / (forward slash)
// We assume the URL is valid so we only test ]) instead of ([ blah ])
std::u32string valid_end_characters = U"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789/-";
std::u32string valid_end_characters_extra = U"~:_=#$%&'+@*])";
if (next_forward_slash != std::string::npos)
valid_end_characters += valid_end_characters_extra;
while (pos > 1)
{
char32_t url_char = url[pos - 1];
for (char32_t valid_char : valid_end_characters)
{
if (valid_char == url_char)
return pos;
}
pos--;
}
return 0;
};
if ((shape_flag & gui::SF_DISABLE_URL_HIGHLIGHT) == 0)
{
size_t pos = text.find(U"http://", 0);
while (pos != std::u32string::npos)
{
// Find nearest newline or whitespace
size_t newline_pos = text.find(U'\n', pos + 1);
size_t space_pos = text.find(U' ', pos + 1);
size_t end_pos = std::u32string::npos;
if (newline_pos != std::u32string::npos ||
space_pos != std::u32string::npos)
{
if (space_pos > newline_pos)
end_pos = newline_pos;
else
end_pos = space_pos;
}
else
end_pos = text.size();
end_pos = fix_end_pos(text, pos, end_pos);
http_pos.emplace_back((int)pos, (int)end_pos);
pos = text.find(U"http://", pos + 1);
}
pos = text.find(U"https://", 0);
while (pos != std::u32string::npos)
{
size_t newline_pos = text.find(U'\n', pos + 1);
size_t space_pos = text.find(U' ', pos + 1);
size_t end_pos = std::u32string::npos;
if (newline_pos != std::u32string::npos ||
space_pos != std::u32string::npos)
{
if (space_pos > newline_pos)
end_pos = newline_pos;
else
end_pos = space_pos;
}
else
end_pos = text.size();
end_pos = fix_end_pos(text, pos, end_pos);
http_pos.emplace_back((int)pos, (int)end_pos);
pos = text.find(U"https://", pos + 1);
}
}
int start = 0;
std::shared_ptr<std::u32string> orig_string =
std::make_shared<std::u32string>(text);
@ -566,7 +643,15 @@ void FontManager::shape(const std::u32string& text,
gl.flags |= gui::GLF_BREAKABLE;
// Add start offset to clusters
for (int& each_cluster : gl.cluster)
{
each_cluster += start;
for (auto& p : http_pos)
{
if (each_cluster >= p.first &&
each_cluster < p.second)
gl.flags |= gui::GLF_URL;
}
}
}
gls.insert(gls.end(), cur_line.begin(), cur_line.end());
}
@ -605,13 +690,13 @@ void FontManager::initGlyphLayouts(const core::stringw& text,
if ((shape_flag & gui::SF_DISABLE_CACHE) != 0)
{
shape(StringUtils::wideToUtf32(text), gls);
shape(StringUtils::wideToUtf32(text), gls, shape_flag);
return;
}
auto& cached_gls = getCachedLayouts(text);
if (cached_gls.empty())
shape(StringUtils::wideToUtf32(text), cached_gls);
shape(StringUtils::wideToUtf32(text), cached_gls, shape_flag);
gls = cached_gls;
} // initGlyphLayouts

View File

@ -132,7 +132,8 @@ public:
unsigned getShapingDPI() const { return m_shaping_dpi; }
// ------------------------------------------------------------------------
void shape(const std::u32string& text,
std::vector<irr::gui::GlyphLayout>& gls);
std::vector<irr::gui::GlyphLayout>& gls,
irr::u32 shape_flag = 0);
// ------------------------------------------------------------------------
std::vector<irr::gui::GlyphLayout>& getCachedLayouts
(const irr::core::stringw& str);

View File

@ -601,7 +601,7 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
// Collect character locations
const unsigned int text_size = gl.size();
std::vector<std::pair<s32, bool> > indices;
std::vector<std::pair<int, int> > indices;
core::array<core::position2d<float>> offsets(text_size);
std::vector<bool> fallback(text_size);
core::array<core::position2d<float>> gld_offsets;
@ -704,8 +704,7 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
offset.Y -= glyph_offset_y;
}
indices.emplace_back(area->spriteno,
(glyph_layout.flags & gui::GLF_COLORED) != 0);
indices.emplace_back(area->spriteno, glyph_layout.flags);
if ((glyph_layout.flags & gui::GLF_QUICK_DRAW) != 0)
{
offset.X += glyph_layout.x_advance * scale;
@ -860,7 +859,8 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
m_fallback_font->m_spritebank->getTexture(tex_id) :
m_spritebank->getTexture(tex_id));
const bool is_colored = indices[n].second;
const bool is_colored = (indices[n].second & gui::GLF_COLORED) != 0;
const bool is_url = (indices[n].second & gui::GLF_URL) != 0;
if (isBold())
{
if (char_collector != NULL)
@ -885,8 +885,13 @@ void FontWithFace::render(const std::vector<gui::GlyphLayout>& gl,
}
else
{
video::SColor single_color = color;
if (is_url)
single_color = text_marked;
else if (is_colored)
single_color = video::SColor(-1);
FontDrawer::addGlyph(texture, dest, source, clip,
is_colored ? video::SColor(-1) : color);
single_color);
}
}
}