Highlight URL with skin marked text color
This commit is contained in:
parent
8f41b721ed
commit
280d8fc114
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user