Font escape sequence for nametags
This commit is contained in:
parent
c49ff76955
commit
bb712686ef
@ -548,6 +548,10 @@ function core.get_background_escape_sequence(color)
|
||||
return ESCAPE_CHAR .. "(b@" .. color .. ")"
|
||||
end
|
||||
|
||||
function core.get_font_escape_sequence(font)
|
||||
return ESCAPE_CHAR .. "(f@" .. font .. ")"
|
||||
end
|
||||
|
||||
function core.colorize(color, message)
|
||||
local lines = tostring(message):split("\n", true)
|
||||
local color_code = core.get_color_escape_sequence(color)
|
||||
@ -572,6 +576,10 @@ function core.strip_colors(str)
|
||||
return (str:gsub(ESCAPE_CHAR .. "%([bc]@[^)]+%)", ""))
|
||||
end
|
||||
|
||||
function core.strip_font(str)
|
||||
return (str:gsub(ESCAPE_CHAR .. "%(f@[^)]+%)", ""))
|
||||
end
|
||||
|
||||
local function translate(textdomain, str, num, ...)
|
||||
local start_seq
|
||||
if textdomain == "" and num == "" then
|
||||
|
@ -3823,6 +3823,13 @@ The following functions provide escape sequences:
|
||||
* Removes background colors added by `get_background_escape_sequence`.
|
||||
* `core.strip_colors(str)`
|
||||
* Removes all color escape sequences.
|
||||
* `minetest.get_font_escape_sequence(font_modifier)`
|
||||
* Can currently only be used for nametags.
|
||||
* `font_modifier` can be "mono", "unmono", "bold", "unbold", "italic" or "unitalic"
|
||||
* The escape sequence modifies the font of the subsequent string.
|
||||
* You can concatenate them, to for example get bold italic text.
|
||||
* `minetest.strip_font(str)`
|
||||
* Removes all font escape sequences.
|
||||
|
||||
|
||||
|
||||
@ -5666,6 +5673,8 @@ Utilities
|
||||
biome_weights = true,
|
||||
-- Particles can specify a "clip" blend mode (5.11.0)
|
||||
particle_blend_clip = true,
|
||||
-- Nametags support color escaped sequences and new font escape sequences. (5.10.0)
|
||||
nametag_font_escape_sequences = true,
|
||||
}
|
||||
```
|
||||
|
||||
@ -9274,6 +9283,8 @@ Player properties need to be saved manually.
|
||||
|
||||
nametag_color = <ColorSpec>,
|
||||
-- Sets text color of nametag
|
||||
-- If the text contains color escape sequences, only the part before the first
|
||||
-- color escape sequences will have the `nametag_color`.
|
||||
|
||||
nametag_bgcolor = <ColorSpec>,
|
||||
-- Sets background color of nametag
|
||||
|
@ -162,3 +162,89 @@ core.register_entity("testentities:nametag", {
|
||||
return core.serialize({ color = self.color, bgcolor = self.bgcolor })
|
||||
end,
|
||||
})
|
||||
|
||||
-- Enriched nametag strings
|
||||
do
|
||||
|
||||
local bold = minetest.get_font_escape_sequence("bold")
|
||||
local unbold = minetest.get_font_escape_sequence("unbold")
|
||||
local italic = minetest.get_font_escape_sequence("italic")
|
||||
local unitalic = minetest.get_font_escape_sequence("unitalic")
|
||||
local mono = minetest.get_font_escape_sequence("mono")
|
||||
local unmono = minetest.get_font_escape_sequence("unmono")
|
||||
local all_off = unbold..unitalic..unmono
|
||||
|
||||
local nametags = {
|
||||
"normal "..bold.."bold "..unbold..italic.."italic "..unitalic..mono.."mono",
|
||||
bold..italic.."bold+italic"..unitalic..mono.." bold+mono"..unbold..italic.." mono+italic",
|
||||
bold..italic..mono.."bold+mono+italic",
|
||||
bold.."bold colored"..minetest.colorize("red", " red").." text",
|
||||
"colored"..minetest.colorize("red", " red"..bold.." bold"..unbold).." text",
|
||||
minetest.strip_font(bold.."striped"..italic.." font fomats"),
|
||||
minetest.get_background_escape_sequence("green").."background is not supported",
|
||||
"colored"..minetest.colorize("gray", " gray"..bold.." bold multi\nline").." text",
|
||||
bold..unbold.."\nempty lines"..italic.." before and after\n"..unitalic,
|
||||
"m"..mono.."m"..unmono.."m"..mono.."m"..unmono.."m"..mono.."m"..unmono.."m"..mono.."m",
|
||||
"i"..italic.."i"..unitalic.."i"..italic.."i"..unitalic.."i"..italic.."i"..unitalic.."i"..
|
||||
italic.."i",
|
||||
"b"..bold.."b"..unbold.."b"..bold.."b"..unbold.."b"..bold.."b"..unbold.."b"..bold.."b",
|
||||
}
|
||||
|
||||
-- Remove this if you want to look them separately
|
||||
local all_lines = "Enriched nametag text:"
|
||||
for _, nametag in ipairs(nametags) do
|
||||
all_lines = all_lines.."\n"..all_off..nametag
|
||||
end
|
||||
nametags = {all_lines}
|
||||
|
||||
local nametag_iterator = 0
|
||||
minetest.register_entity("testentities:nametag_enriched_text", {
|
||||
initial_properties = {
|
||||
visual = "sprite",
|
||||
textures = { "testentities_sprite.png" },
|
||||
},
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
local color
|
||||
local bgcolor
|
||||
if (nametag_iterator/#nametags) % 2 < 1 then
|
||||
color = {r = 0, g = 0, b = 255}
|
||||
bgcolor = {r = 200, g = 200, b = 10,a = 150}
|
||||
end
|
||||
|
||||
local nametag = nametags[(nametag_iterator % #nametags)+1]
|
||||
nametag_iterator = nametag_iterator + 1
|
||||
|
||||
self.object:set_properties({
|
||||
nametag = nametag,
|
||||
nametag_color = color,
|
||||
nametag_bgcolor = bgcolor,
|
||||
})
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
-- Who doesn't like ascii art
|
||||
minetest.register_entity("testentities:nametag_ascii_art", {
|
||||
initial_properties = {
|
||||
visual = "sprite",
|
||||
textures = { "testentities_sprite.png" },
|
||||
},
|
||||
on_activate = function(self, staticdata)
|
||||
local nametag = mono..
|
||||
" __. __. __. \n" ..
|
||||
" _____ |__| ____ _____ / |_ _____ _____ / |_ \n" ..
|
||||
" / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\\n" ..
|
||||
"| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | \n" ..
|
||||
"|__|_| / |___| /\\______> | \\______>_____/| | \n" ..
|
||||
" \\/ \\/ \\/ \\/ \\/ "
|
||||
self.object:set_properties({
|
||||
nametag = nametag,
|
||||
nametag_color = "green",
|
||||
nametag_bgcolor = "black",
|
||||
})
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
end
|
||||
|
@ -49,6 +49,7 @@ set(client_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/content_mapblock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/filecache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fontengine.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/font_enriched_string_composite.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/game.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gameui.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/game_formspec.cpp
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <SViewFrustum.h>
|
||||
#include <IGUIFont.h>
|
||||
#include <IVideoDriver.h>
|
||||
#include "font_enriched_string_composite.h"
|
||||
|
||||
#define CAMERA_OFFSET_STEP 200
|
||||
#define WIELDMESH_OFFSET_X 55.0f
|
||||
@ -632,7 +633,6 @@ void Camera::drawNametags()
|
||||
core::matrix4 trans = m_cameranode->getProjectionMatrix();
|
||||
trans *= m_cameranode->getViewMatrix();
|
||||
|
||||
gui::IGUIFont *font = g_fontengine->getFont();
|
||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
|
||||
@ -643,10 +643,10 @@ void Camera::drawNametags()
|
||||
f32 transformed_pos[4] = { pos.X, pos.Y, pos.Z, 1.0f };
|
||||
trans.multiplyWith1x4Matrix(transformed_pos);
|
||||
if (transformed_pos[3] > 0) {
|
||||
std::wstring nametag_colorless =
|
||||
unescape_translate(utf8_to_wide(nametag->text));
|
||||
core::dimension2d<u32> textsize = font->getDimension(
|
||||
nametag_colorless.c_str());
|
||||
FontEnrichedStringComposite fesc(translate_string(utf8_to_wide(nametag->text)).c_str(),
|
||||
nametag->textcolor);
|
||||
|
||||
core::dimension2d<u32> textsize = fesc.getDimension();
|
||||
f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
|
||||
core::reciprocal(transformed_pos[3]);
|
||||
v2s32 screen_pos;
|
||||
@ -661,10 +661,7 @@ void Camera::drawNametags()
|
||||
core::rect<s32> bg_size(-2, 0, textsize.Width + 2, textsize.Height);
|
||||
driver->draw2DRectangle(bgcolor, bg_size + screen_pos);
|
||||
}
|
||||
|
||||
font->draw(
|
||||
translate_string(utf8_to_wide(nametag->text)).c_str(),
|
||||
size + screen_pos, nametag->textcolor);
|
||||
fesc.draw(size + screen_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
166
src/client/font_enriched_string_composite.cpp
Normal file
166
src/client/font_enriched_string_composite.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2024 cx384
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "font_enriched_string_composite.h"
|
||||
#include "irrlicht_changes/CGUITTFont.h"
|
||||
#include "util/string.h"
|
||||
#include <utility>
|
||||
|
||||
static bool parseFontModifier(std::string_view s, FontModifier &modifier) {
|
||||
if (s == "mono")
|
||||
modifier = FontModifier::Mono;
|
||||
else if (s == "unmono")
|
||||
modifier = FontModifier::Unmono;
|
||||
else if (s == "bold")
|
||||
modifier = FontModifier::Bold;
|
||||
else if (s == "unbold")
|
||||
modifier = FontModifier::Unbold;
|
||||
else if (s == "italic")
|
||||
modifier = FontModifier::Italic;
|
||||
else if (s == "unitalic")
|
||||
modifier = FontModifier::Unitalic;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
FontEnrichedStringComposite::FontEnrichedStringComposite(const std::wstring &s,
|
||||
const video::SColor &initial_color, const FontSpec &initial_font) {
|
||||
|
||||
FontSpec font = initial_font;
|
||||
video::SColor color = initial_color;
|
||||
|
||||
// Handle font escape sequence like in EnrichedString and translate_string
|
||||
Line line;
|
||||
size_t fragmen_start = 0;
|
||||
size_t i = 0;
|
||||
while (i < s.length()) {
|
||||
if (s[i] == L'\n') {
|
||||
// Split lines
|
||||
|
||||
EnrichedString fragment(std::wstring(s, fragmen_start, i-fragmen_start), color);
|
||||
line.push_back(std::make_pair(fragment, font));
|
||||
|
||||
auto colors = fragment.getColors();
|
||||
if (!colors.empty())
|
||||
color = colors.back();
|
||||
|
||||
if (!line.empty()) {
|
||||
m_lines.emplace_back(std::move(line));
|
||||
line = Line();
|
||||
}
|
||||
i++;
|
||||
fragmen_start = i;
|
||||
continue;
|
||||
} else if (s[i] != L'\x1b') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
size_t start_index = i;
|
||||
size_t length;
|
||||
if (i == s.length())
|
||||
break;
|
||||
if (s[i] == L'(') {
|
||||
++i;
|
||||
++start_index;
|
||||
while (i < s.length() && s[i] != L')') {
|
||||
if (s[i] == L'\\') {
|
||||
++i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
length = i - start_index;
|
||||
++i;
|
||||
} else {
|
||||
++i;
|
||||
length = 1;
|
||||
}
|
||||
std::wstring escape_sequence(s, start_index, length);
|
||||
std::vector<std::wstring> parts = split(escape_sequence, L'@');
|
||||
if (parts[0] == L"f") {
|
||||
if (parts.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
FontModifier modifier;
|
||||
if (parseFontModifier(wide_to_utf8(parts[1]), modifier)) {
|
||||
EnrichedString fragment(std::wstring(s, fragmen_start, start_index-fragmen_start), color);
|
||||
line.push_back(std::make_pair(fragment, font));
|
||||
|
||||
fragmen_start = start_index + length + 1;
|
||||
font.applyFontModifier(modifier);
|
||||
|
||||
auto colors = fragment.getColors();
|
||||
if (!colors.empty())
|
||||
color = colors.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fragmen_start < s.length()) {
|
||||
EnrichedString fragment(std::wstring(s, fragmen_start), color);
|
||||
line.push_back(std::make_pair(fragment, font));
|
||||
}
|
||||
|
||||
if (!line.empty())
|
||||
m_lines.push_back(line);
|
||||
|
||||
};
|
||||
|
||||
void FontEnrichedStringComposite::draw(core::rect<s32> position) const {
|
||||
u32 start_pos_x = position.UpperLeftCorner.X;
|
||||
for (auto line : m_lines) {
|
||||
position.UpperLeftCorner.X = start_pos_x;
|
||||
u32 max_h = 0;
|
||||
for (auto [es, spec] : line) {
|
||||
gui::IGUIFont *font = g_fontengine->getFont(spec);
|
||||
gui::CGUITTFont *ttfont = dynamic_cast<gui::CGUITTFont*>(font);
|
||||
if (ttfont) { // Don't draw other fonts
|
||||
ttfont->draw(es, position);
|
||||
|
||||
auto frag_dim = ttfont->getDimension(es.c_str());
|
||||
position.UpperLeftCorner.X += frag_dim.Width;
|
||||
if (frag_dim.Height > max_h)
|
||||
max_h = frag_dim.Height;
|
||||
}
|
||||
}
|
||||
position.UpperLeftCorner.Y += max_h;
|
||||
}
|
||||
};
|
||||
|
||||
core::dimension2d<u32> FontEnrichedStringComposite::getDimension() const {
|
||||
core::dimension2d<u32> dim(0, 0);
|
||||
for (auto line : m_lines) {
|
||||
u32 max_h = 0;
|
||||
u32 sum_w = 0;
|
||||
for (auto [es, spec] : line) {
|
||||
gui::IGUIFont *font = g_fontengine->getFont(spec);
|
||||
gui::CGUITTFont *ttfont = dynamic_cast<gui::CGUITTFont*>(font);
|
||||
if (ttfont) {
|
||||
auto frag_dim = ttfont->getDimension(es.c_str());
|
||||
sum_w += frag_dim.Width;
|
||||
if (frag_dim.Height > max_h)
|
||||
max_h = frag_dim.Height;
|
||||
}
|
||||
}
|
||||
dim.Height += max_h;
|
||||
if (dim.Width < sum_w)
|
||||
dim.Width = sum_w;
|
||||
}
|
||||
return dim;
|
||||
};
|
41
src/client/font_enriched_string_composite.h
Normal file
41
src/client/font_enriched_string_composite.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2024 cx384
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/enriched_string.h"
|
||||
#include "fontengine.h"
|
||||
#include "rect.h"
|
||||
|
||||
// Note this is client code, because of the draw function.
|
||||
|
||||
// A text consisting of multiple enriched strings which are drawn with different fonts
|
||||
struct FontEnrichedStringComposite {
|
||||
FontEnrichedStringComposite(const std::wstring &s,
|
||||
const video::SColor &initial_color = video::SColor(255, 255, 255, 255),
|
||||
const FontSpec &initial_font = FontSpec(FONT_SIZE_UNSPECIFIED, FM_Standard, false, false));
|
||||
|
||||
void draw(core::rect<s32> position) const;
|
||||
|
||||
core::dimension2d<u32> getDimension() const;
|
||||
|
||||
private:
|
||||
using Line = std::vector<std::pair<EnrichedString, FontSpec>>;
|
||||
std::vector<Line> m_lines;
|
||||
};
|
@ -19,6 +19,17 @@ namespace irr {
|
||||
|
||||
#define FONT_SIZE_UNSPECIFIED 0xFFFFFFFF
|
||||
|
||||
using namespace irr;
|
||||
|
||||
enum class FontModifier {
|
||||
Mono,
|
||||
Unmono,
|
||||
Bold,
|
||||
Unbold,
|
||||
Italic,
|
||||
Unitalic,
|
||||
};
|
||||
|
||||
enum FontMode : u8 {
|
||||
FM_Standard = 0,
|
||||
FM_Mono,
|
||||
@ -39,6 +50,31 @@ struct FontSpec {
|
||||
return (mode << 2) | (static_cast<u8>(bold) << 1) | static_cast<u8>(italic);
|
||||
}
|
||||
|
||||
void applyFontModifier(FontModifier modifier) {
|
||||
switch(modifier) {
|
||||
case FontModifier::Mono :
|
||||
mode = FM_Mono;
|
||||
break;
|
||||
case FontModifier::Unmono :
|
||||
mode = FM_Standard;
|
||||
break;
|
||||
case FontModifier::Bold :
|
||||
bold = true;
|
||||
break;
|
||||
case FontModifier::Unbold :
|
||||
bold = false;
|
||||
break;
|
||||
case FontModifier::Italic :
|
||||
italic = true;
|
||||
break;
|
||||
case FontModifier::Unitalic :
|
||||
italic = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int size;
|
||||
FontMode mode;
|
||||
bool bold;
|
||||
|
Loading…
Reference in New Issue
Block a user