Formspec: add hypertext element

This commit is contained in:
Pierre-Yves Rollo 2019-09-10 15:11:26 +02:00 committed by SmallJoker
parent 8697090b35
commit 72416a6a1f
22 changed files with 1792 additions and 79 deletions

@ -851,14 +851,9 @@ tooltip_append_itemname (Append item name) bool false
# If disabled, bitmap and XML vectors fonts are used instead.
freetype (FreeType fonts) bool true
# Path to the default font.
# If “freetype” setting is enabled: Must be a TrueType font.
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
# The fallback font will be used if the font cannot be loaded.
font_path (Font path) filepath fonts/liberationsans.ttf
font_bold (Font bold by default) bool false
# Font size of the default font in point (pt).
font_size (Font size) int 16 1
font_italic (Font italic by default) bool false
# Shadow offset (in pixels) of the default font. If 0, then shadow will not be drawn.
font_shadow (Font shadow) int 1
@ -866,20 +861,31 @@ font_shadow (Font shadow) int 1
# Opaqueness (alpha) of the shadow behind the default font, between 0 and 255.
font_shadow_alpha (Font shadow alpha) int 127 0 255
# Path to the monospace font.
# Font size of the default font in point (pt).
font_size (Font size) int 16 1
# Path to the default font.
# If “freetype” setting is enabled: Must be a TrueType font.
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
# This font is used for e.g. the console and profiler screen.
mono_font_path (Monospace font path) filepath fonts/liberationmono.ttf
# The fallback font will be used if the font cannot be loaded.
font_path (Regular font path) filepath fonts/Arimo-Regular.ttf
font_path_bold (Bold font path) filepath fonts/Arimo-Bold.ttf
font_path_italic (Italic font path) filepath fonts/Arimo-Italic.ttf
font_path_bolditalic (Bold and italic font path) filepath fonts/Arimo-BoldItalic.ttf
# Font size of the monospace font in point (pt).
mono_font_size (Monospace font size) int 15 1
# Path of the fallback font.
# Path to the monospace font.
# If “freetype” setting is enabled: Must be a TrueType font.
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
# This font will be used for certain languages or if the default font is unavailable.
fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf
# This font is used for e.g. the console and profiler screen.
mono_font_path (Monospace font path) filepath fonts/Cousine-Regular.ttf
mono_font_path_bold (Bold monospace font path) filepath fonts/Cousine-Bold.ttf
mono_font_path_italic (Italic monospace font path) filepath fonts/Cousine-Italic.ttf
mono_font_path_bolditalic (Bold and italic monospace font path) filepath fonts/Cousine-BoldItalic.ttf
# Font size of the fallback font in point (pt).
fallback_font_size (Fallback font size) int 15 1
@ -890,6 +896,12 @@ fallback_font_shadow (Fallback font shadow) int 1
# Opaqueness (alpha) of the shadow behind the fallback font, between 0 and 255.
fallback_font_shadow_alpha (Fallback font shadow alpha) int 128 0 255
# Path of the fallback font.
# If “freetype” setting is enabled: Must be a TrueType font.
# If “freetype” setting is disabled: Must be a bitmap or XML vectors font.
# This font will be used for certain languages or if the default font is unavailable.
fallback_font_path (Fallback font path) filepath fonts/DroidSansFallbackFull.ttf
# Path to save screenshots at.
screenshot_path (Screenshot folder) path

@ -2189,8 +2189,13 @@ Elements
half a coordinate. With the old system, newlines are spaced 2/5 of
an inventory slot.
### `vertlabel[<X>,<Y>;<label>]`
### `hypertext[<X>,<Y>;<W>,<H>;<name>;<text>]`
* Displays a static formated text with hyperlinks.
* `x`, `y`, `w` and `h` work as per field
* `name` is the name of the field as returned in fields to `on_receive_fields` in case of action in text.
* `text` is the formatted text using `markup language` described below.
### `vertlabel[<X>,<Y>;<label>]`
* Textual label drawn vertically
* `label` is the text on the label
* **Note**: If the new coordinate system is enabled, vertlabels are
@ -2534,6 +2539,110 @@ Some types may inherit styles from parent types.
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* textcolor - color. Default white.
Markup language
---------------
Markup language used in `hypertext[]` elements uses tag that look like HTML tags. Some
tags can enclose text, they open with `<tagname>` and close with `</tagname>`.
Tags can have attributes, in that case, attributes are in the opening tag in
form of a key/value separated with equal signs. Attribute values should not be quoted.
These are the technically basic tags but see below for usual tags. Base tags are:
`<style color=... font=... size=...>...</style>`
Changes the style of the text.
* `color`: Text color. Given color is a `colorspec`.
* `size`: Text size.
* `font`: Text font (`mono` or `normal`).
`<global background=... margin=... valign=... color=... hovercolor=... size=... font=... halign=... >`
Sets global style.
Global only styles:
* `background`: Text background, a `colorspec` or `none`.
* `margin`: Page margins in pixel.
* `valign`: Text vertical alignment (`top`, `middle`, `bottom`).
Inheriting styles (affects child elements):
* `color`: Default text color. Given color is a `colorspec`.
* `hovercolor`: Color of <action> tags when mouse is over.
* `size`: Default text size.
* `font`: Default text font (`mono` or `normal`).
* `halign`: Default text horizontal alignment (`left`, `right`, `center`, `justify`).
This tag needs to be placed only once as it changes the global settings of the
text. Anyway, if several tags are placed, each changed will be made in the order
tags appear.
`<tag name=... color=... hovercolor=... font=... size=...>`
Defines or redefines tag style. This can be used to define new tags.
* `name`: Name of the tag to define or change.
* `color`: Text color. Given color is a `colorspec`.
* `hovercolor`: Text color when element hovered (only for `action` tags). Given color is a `colorspec`.
* `size`: Text size.
* `font`: Text font (`mono` or `normal`).
Following tags are the usual tags for text layout. They are defined by default.
Other tags can be added using `<tag ...>` tag.
`<normal>...</normal>`: Normal size text
`<big>...</big>`: Big text
`<bigger>...</bigger>`: Bigger text
`<center>...</center>`: Centered text
`<left>...</left>`: Left-aligned text
`<right>...</right>`: Right-aligned text
`<justify>...</justify>`: Justified text
`<mono>...</mono>`: Monospaced font
`<b>...</b>`, `<i>...</i>`, `<u>...</u>`: Bold, italic, underline styles.
`<action name=...>...</action>`
Make that text a clickable text triggering an action.
* `name`: Name of the action (mandatory).
When clicked, the formspec is send to the server. The value of the text field
sent to `on_player_receive_fields` will be "action:" concatenated to the action
name.
`<img name=... float=... width=... height=...>`
Draws an image which is present in the client media cache.
* `name`: Name of the texture (mandatory).
* `float`: If present, makes the image floating (`left` or `right`).
* `width`: Force image width instead of taking texture width.
* `height`: Force image height instead of taking texture height.
If only width or height given, texture aspect is kept.
`<item name=... float=... width=... height=... rotate=...>`
Draws an item image.
* `name`: Item string of the item to draw (mandatory).
* `float`: If present, makes the image floating (`left` or `right`).
* `width`: Item image width.
* `height`: Item image height.
* `rotate`: Rotate item image if set to `yes` or `X,Y,Z`. X, Y and Z being
rotation speeds in percent of standard speed (-1000 to 1000). Works only if
`inventory_items_animations` is set to true.
* `angle`: Angle in which the item image is shown. Value has `X,Y,Z` form.
X, Y and Z being angles around each three axes. Works only if
`inventory_items_animations` is set to true.
Inventory
=========
@ -2557,7 +2666,6 @@ Player Inventory lists
* Is not created automatically, use `InvRef:set_size`
* Is only used to enhance the empty hand's tool capabilities
Colors
======

BIN
fonts/Arimo-Bold.ttf Normal file

Binary file not shown.

BIN
fonts/Arimo-BoldItalic.ttf Normal file

Binary file not shown.

BIN
fonts/Arimo-Italic.ttf Normal file

Binary file not shown.

BIN
fonts/Cousine-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

BIN
fonts/Cousine-Italic.ttf Normal file

Binary file not shown.

@ -41,6 +41,11 @@ static void font_setting_changed(const std::string &name, void *userdata)
g_fontengine->readSettings();
}
unsigned int get_font_cache_index(FontMode mode, bool bold = false, bool italic = false)
{
return (mode << 2) | (bold << 1) | italic;
}
/******************************************************************************/
FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
m_settings(main_settings),
@ -59,7 +64,12 @@ FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
if (m_currentMode == FM_Standard) {
m_settings->registerChangedCallback("font_size", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_bold", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_italic", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_path", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_path_bold", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_path_italic", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_path_bolditalic", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL);
m_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL);
}
@ -96,7 +106,8 @@ void FontEngine::cleanCache()
}
/******************************************************************************/
irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode)
irr::gui::IGUIFont *FontEngine::getFont(unsigned int font_size, FontMode mode,
bool bold, bool italic)
{
if (mode == FM_Unspecified) {
mode = m_currentMode;
@ -110,22 +121,30 @@ irr::gui::IGUIFont* FontEngine::getFont(unsigned int font_size, FontMode mode)
if (font_size == FONT_SIZE_UNSPECIFIED)
font_size = m_default_size[mode];
const auto &cache = m_font_cache[mode];
unsigned int cache_index = get_font_cache_index(mode, bold, italic);
const auto &cache = m_font_cache[cache_index];
if (cache.find(font_size) == cache.end()) {
if (mode == FM_Simple || mode == FM_SimpleMono)
initSimpleFont(font_size, mode);
else
initFont(font_size, mode);
initFont(font_size, mode, bold, italic);
}
if (m_font_cache[cache_index].find(font_size) ==
m_font_cache[cache_index].end())
initFont(font_size, mode, bold, italic);
const auto &font = cache.find(font_size);
return font != cache.end() ? font->second : nullptr;
}
/******************************************************************************/
unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode)
unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode,
bool bold, bool italic)
{
irr::gui::IGUIFont* font = getFont(font_size, mode);
irr::gui::IGUIFont *font = getFont(font_size, mode, bold, italic);
// use current skin font as fallback
if (font == NULL) {
@ -138,9 +157,9 @@ unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode)
/******************************************************************************/
unsigned int FontEngine::getTextWidth(const std::wstring& text,
unsigned int font_size, FontMode mode)
unsigned int font_size, FontMode mode, bool bold, bool italic)
{
irr::gui::IGUIFont* font = getFont(font_size, mode);
irr::gui::IGUIFont *font = getFont(font_size, mode, bold, italic);
// use current skin font as fallback
if (font == NULL) {
@ -153,9 +172,10 @@ unsigned int FontEngine::getTextWidth(const std::wstring& text,
/** get line height for a specific font (including empty room between lines) */
unsigned int FontEngine::getLineHeight(unsigned int font_size, FontMode mode)
unsigned int FontEngine::getLineHeight(unsigned int font_size, FontMode mode,
bool bold, bool italic)
{
irr::gui::IGUIFont* font = getFont(font_size, mode);
irr::gui::IGUIFont *font = getFont(font_size, mode, bold, italic);
// use current skin font as fallback
if (font == NULL) {
@ -183,6 +203,10 @@ void FontEngine::readSettings()
m_currentMode = is_yes(gettext("needs_fallback_font")) ?
FM_Fallback : FM_Standard;
m_default_bold = m_settings->getBool("font_bold");
m_default_italic = m_settings->getBool("font_italic");
} else {
m_currentMode = FM_Simple;
}
@ -226,14 +250,17 @@ void FontEngine::updateFontCache()
}
/******************************************************************************/
void FontEngine::initFont(unsigned int basesize, FontMode mode)
void FontEngine::initFont(unsigned int basesize, FontMode mode,
bool bold, bool italic)
{
assert(mode != FM_Unspecified);
assert(basesize != FONT_SIZE_UNSPECIFIED);
if (m_font_cache[mode].find(basesize) != m_font_cache[mode].end())
return;
int cache_index = get_font_cache_index(mode, bold, italic);
if (m_font_cache[cache_index].find(basesize) !=
m_font_cache[cache_index].end())
return;
std::string setting_prefix = "";
@ -249,8 +276,13 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
break;
}
std::string setting_suffix = (bold) ?
((italic) ? "_bold_italic" : "_bold") :
((italic) ? "_italic" : "");
u32 size = std::floor(RenderingEngine::getDisplayDensity() *
m_settings->getFloat("gui_scaling") * basesize);
if (size == 0) {
errorstream << "FontEngine: attempt to use font size 0" << std::endl;
errorstream << " display density: " << RenderingEngine::getDisplayDensity() << std::endl;
@ -260,10 +292,14 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
u16 font_shadow = 0;
u16 font_shadow_alpha = 0;
g_settings->getU16NoEx(setting_prefix + "font_shadow", font_shadow);
g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha", font_shadow_alpha);
g_settings->getU16NoEx(setting_prefix + "font_shadow_alpha",
font_shadow_alpha);
std::string wanted_font_path;
wanted_font_path = g_settings->get(setting_prefix + "font_path" + setting_suffix);
std::string fallback_settings[] = {
m_settings->get(setting_prefix + "font_path"),
wanted_font_path,
m_settings->get("fallback_font_path"),
m_settings->getDefault(setting_prefix + "font_path")
};
@ -275,7 +311,7 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
font_shadow_alpha);
if (font) {
m_font_cache[mode][basesize] = font;
m_font_cache[cache_index][basesize] = font;
return;
}
@ -340,7 +376,7 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
path.str(""); // Clear
path << basename << "_" << (size + offset * sign) << ext;
if (!fs::PathExists(path.str()))
if (!fs::PathExists(path.str()))
continue;
font = m_env->getFont(path.str().c_str());
@ -365,5 +401,5 @@ void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
}
if (font)
m_font_cache[mode][basesize] = font;
m_font_cache[get_font_cache_index(mode)][basesize] = font;
}

@ -48,29 +48,62 @@ public:
~FontEngine();
/** get Font */
irr::gui::IGUIFont* getFont(unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified);
irr::gui::IGUIFont *getFont(unsigned int font_size, FontMode mode,
bool bold, bool italic);
irr::gui::IGUIFont *getFont(unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified)
{
return getFont(font_size, mode, m_default_bold, m_default_italic);
}
/** get text height for a specific font */
unsigned int getTextHeight(unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified);
unsigned int getTextHeight(unsigned int font_size, FontMode mode,
bool bold, bool italic);
/** get text width if a text for a specific font */
unsigned int getTextWidth(const std::string& text,
unsigned int getTextHeight(
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified)
{
return getTextWidth(utf8_to_wide(text));
return getTextHeight(font_size, mode, m_default_bold, m_default_italic);
}
unsigned int getTextWidth(const std::wstring& text,
unsigned int font_size, FontMode mode, bool bold, bool italic);
/** get text width if a text for a specific font */
unsigned int getTextWidth(const std::wstring& text,
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified);
FontMode mode=FM_Unspecified)
{
return getTextWidth(text, font_size, mode, m_default_bold,
m_default_italic);
}
unsigned int getTextWidth(const std::string& text,
unsigned int font_size, FontMode mode, bool bold, bool italic)
{
return getTextWidth(utf8_to_wide(text), font_size, mode, bold, italic);
}
unsigned int getTextWidth(const std::string& text,
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified)
{
return getTextWidth(utf8_to_wide(text), font_size, mode, m_default_bold,
m_default_italic);
}
/** get line height for a specific font (including empty room between lines) */
unsigned int getLineHeight(unsigned int font_size, FontMode mode, bool bold,
bool italic);
unsigned int getLineHeight(unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified);
FontMode mode=FM_Unspecified)
{
return getLineHeight(font_size, mode, m_default_bold, m_default_italic);
}
/** get default font size */
unsigned int getDefaultFontSize();
@ -86,7 +119,11 @@ private:
void updateFontCache();
/** initialize a new font */
void initFont(unsigned int basesize, FontMode mode=FM_Unspecified);
void initFont(
unsigned int basesize,
FontMode mode,
bool bold,
bool italic);
/** initialize a font without freetype */
void initSimpleFont(unsigned int basesize, FontMode mode);
@ -104,11 +141,15 @@ private:
gui::IGUIEnvironment* m_env = nullptr;
/** internal storage for caching fonts of different size */
std::map<unsigned int, irr::gui::IGUIFont*> m_font_cache[FM_MaxMode];
std::map<unsigned int, irr::gui::IGUIFont*> m_font_cache[FM_MaxMode << 2];
/** default font size to use */
unsigned int m_default_size[FM_MaxMode];
/** default bold and italic */
bool m_default_bold;
bool m_default_italic;
/** current font engine mode */
FontMode m_currentMode = FM_Standard;

@ -608,23 +608,24 @@ void Hud::resizeHotbar() {
struct MeshTimeInfo {
u64 time;
scene::IMesh *mesh;
scene::IMesh *mesh = nullptr;
};
void drawItemStack(video::IVideoDriver *driver,
void drawItemStack(
video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind)
ItemRotationKind rotation_kind,
const v3s16 &angle,
const v3s16 &rotation_speed)
{
static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
static thread_local bool enable_animations =
g_settings->getBool("inventory_items_animations");
if (item.empty()) {
if (rotation_kind < IT_ROT_NONE) {
if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
rotation_time_infos[rotation_kind].mesh = NULL;
}
return;
@ -639,7 +640,7 @@ void drawItemStack(video::IVideoDriver *driver,
s32 delta = 0;
if (rotation_kind < IT_ROT_NONE) {
MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
if (mesh != ti.mesh) {
if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
ti.mesh = mesh;
ti.time = porting::getTimeMs();
} else {
@ -677,9 +678,16 @@ void drawItemStack(video::IVideoDriver *driver,
core::matrix4 matrix;
matrix.makeIdentity();
static thread_local bool enable_animations =
g_settings->getBool("inventory_items_animations");
if (enable_animations) {
float timer_f = (float) delta / 5000.0;
matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
float timer_f = (float) delta / 5000.f;
matrix.setRotationDegrees(v3f(
angle.X + rotation_speed.X * 3.60f * timer_f,
angle.Y + rotation_speed.Y * 3.60f * timer_f,
angle.Z + rotation_speed.Z * 3.60f * timer_f)
);
}
driver->setTransform(video::ETS_WORLD, matrix);
@ -695,15 +703,18 @@ void drawItemStack(video::IVideoDriver *driver,
// because these meshes are not buffered.
assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
video::SColor c = basecolor;
if (imesh->buffer_colors.size() > j) {
ItemPartColor *p = &imesh->buffer_colors[j];
if (p->override_base)
c = p->color;
}
if (imesh->needs_shading)
colorizeMeshBuffer(buf, &c);
else
setMeshBufferColor(buf, c);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.Lighting = false;
@ -726,12 +737,12 @@ void drawItemStack(video::IVideoDriver *driver,
}
}
if(def.type == ITEM_TOOL && item.wear != 0)
{
if (def.type == ITEM_TOOL && item.wear != 0) {
// Draw a progressbar
float barheight = rect.getHeight()/16;
float barpad_x = rect.getWidth()/16;
float barpad_y = rect.getHeight()/16;
float barheight = rect.getHeight() / 16;
float barpad_x = rect.getWidth() / 16;
float barpad_y = rect.getHeight() / 16;
core::rect<s32> progressrect(
rect.UpperLeftCorner.X + barpad_x,
rect.LowerRightCorner.Y - barpad_y - barheight,
@ -739,18 +750,19 @@ void drawItemStack(video::IVideoDriver *driver,
rect.LowerRightCorner.Y - barpad_y);
// Shrink progressrect by amount of tool damage
float wear = item.wear / 65535.0;
float wear = item.wear / 65535.0f;
int progressmid =
wear * progressrect.UpperLeftCorner.X +
(1-wear) * progressrect.LowerRightCorner.X;
(1 - wear) * progressrect.LowerRightCorner.X;
// Compute progressbar color
// wear = 0.0: green
// wear = 0.5: yellow
// wear = 1.0: red
video::SColor color(255,255,255,255);
video::SColor color(255, 255, 255, 255);
int wear_i = MYMIN(std::floor(wear * 600), 511);
wear_i = MYMIN(wear_i + 10, 511);
if (wear_i <= 255)
color.set(255, wear_i, 255, 0);
else
@ -760,18 +772,17 @@ void drawItemStack(video::IVideoDriver *driver,
progressrect2.LowerRightCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
color = video::SColor(255,0,0,0);
color = video::SColor(255, 0, 0, 0);
progressrect2 = progressrect;
progressrect2.UpperLeftCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
}
if(font != NULL && item.count >= 2)
{
if (font != NULL && item.count >= 2) {
// Get the item count as a string
std::string text = itos(item.count);
v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
v2s32 sdim(dim.X,dim.Y);
v2s32 sdim(dim.X, dim.Y);
core::rect<s32> rect2(
/*rect.UpperLeftCorner,
@ -780,10 +791,23 @@ void drawItemStack(video::IVideoDriver *driver,
sdim
);
video::SColor bgcolor(128,0,0,0);
video::SColor bgcolor(128, 0, 0, 0);
driver->draw2DRectangle(bgcolor, rect2, clip);
video::SColor color(255,255,255,255);
video::SColor color(255, 255, 255, 255);
font->draw(text.c_str(), rect2, color, false, false, clip);
}
}
void drawItemStack(
video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind)
{
drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
v3s16(0, 0, 0), v3s16(0, 100, 0));
}

@ -122,6 +122,7 @@ enum ItemRotationKind
IT_ROT_SELECTED,
IT_ROT_HOVERED,
IT_ROT_DRAGGED,
IT_ROT_OTHER,
IT_ROT_NONE, // Must be last, also serves as number
};
@ -133,4 +134,15 @@ void drawItemStack(video::IVideoDriver *driver,
Client *client,
ItemRotationKind rotation_kind);
void drawItemStack(
video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind,
const v3s16 &angle,
const v3s16 &rotation_speed);
#endif

@ -292,9 +292,17 @@ void set_default_settings(Settings *settings)
#if USE_FREETYPE
settings->setDefault("freetype", "true");
settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "Arimo-Regular.ttf"));
settings->setDefault("font_path_italic", porting::getDataPath("fonts" DIR_DELIM "Arimo-Italic.ttf"));
settings->setDefault("font_path_bold", porting::getDataPath("fonts" DIR_DELIM "Arimo-Bold.ttf"));
settings->setDefault("font_path_bold_italic", porting::getDataPath("fonts" DIR_DELIM "Arimo-BoldItalic.ttf"));
settings->setDefault("font_bold", "false");
settings->setDefault("font_italic", "false");
settings->setDefault("font_shadow", "1");
settings->setDefault("font_shadow_alpha", "127");
settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "Cousine-Regular.ttf"));
settings->setDefault("mono_font_path_italic", porting::getDataPath("fonts" DIR_DELIM "Cousine-Italic.ttf"));
settings->setDefault("mono_font_path_bold", porting::getDataPath("fonts" DIR_DELIM "Cousine-Bold.ttf"));
settings->setDefault("mono_font_path_bold_italic", porting::getDataPath("fonts" DIR_DELIM "Cousine-BoldItalic.ttf"));
settings->setDefault("fallback_font_path", porting::getDataPath("fonts" DIR_DELIM "DroidSansFallbackFull.ttf"));
settings->setDefault("fallback_font_shadow", "1");

@ -11,6 +11,7 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp
${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp

@ -57,6 +57,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/guiscalingfilter.h"
#include "guiEditBoxWithScrollbar.h"
#include "intlGUIEditBox.h"
#include "guiHyperText.h"
#define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \
@ -155,16 +156,15 @@ void GUIFormSpecMenu::removeChildren()
{
const core::list<gui::IGUIElement*> &children = getChildren();
while(!children.empty()) {
while (!children.empty()) {
(*children.getLast())->remove();
}
if(m_tooltip_element) {
if (m_tooltip_element) {
m_tooltip_element->remove();
m_tooltip_element->drop();
m_tooltip_element = NULL;
m_tooltip_element = nullptr;
}
}
void GUIFormSpecMenu::setInitialFocus()
@ -1318,7 +1318,6 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& parts,
const std::string &type)
{
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_geom = split(parts[1],',');
std::string name = parts[2];
@ -1402,6 +1401,59 @@ void GUIFormSpecMenu::parseField(parserData* data, const std::string &element,
errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
}
void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &element)
{
std::vector<std::string> parts = split(element, ';');
if (parts.size() != 4 && m_formspec_version < FORMSPEC_API_VERSION) {
errorstream << "Invalid text element(" << parts.size() << "): '" << element << "'" << std::endl;
return;
}
std::vector<std::string> v_pos = split(parts[0], ',');
std::vector<std::string> v_geom = split(parts[1], ',');
std::string name = parts[2];
std::string text = parts[3];
MY_CHECKPOS("hypertext", 0);
MY_CHECKGEOM("hypertext", 1);
v2s32 pos;
v2s32 geom;
if (data->real_coordinates) {
pos = getRealCoordinateBasePos(false, v_pos);
geom = getRealCoordinateGeometry(v_geom);
} else {
pos = getElementBasePos(false, &v_pos);
pos -= padding;
pos.X += stof(v_pos[0]) * spacing.X;
pos.Y += stof(v_pos[1]) * spacing.Y + (m_btn_height * 2);
geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
geom.Y = (stof(v_geom[1]) * imgsize.Y) - (spacing.Y - imgsize.Y);
}
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X + geom.X, pos.Y + geom.Y);
if(m_form_src)
text = m_form_src->resolveText(text);
FieldSpec spec(
name,
utf8_to_wide(unescape_string(text)),
L"",
258 + m_fields.size()
);
spec.ftype = f_Unknown;
new GUIHyperText(
spec.flabel.c_str(), Environment, this, spec.fid, rect, m_client, m_tsrc);
m_fields.push_back(spec);
}
void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
@ -2293,6 +2345,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
if (type == "hypertext") {
parseHyperText(data,description);
return;
}
if (type == "label") {
parseLabel(data,description);
return;
@ -2879,8 +2936,8 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
if (!item.empty()) {
// Draw item stack
drawItemStack(driver, m_font, item,
rect, &AbsoluteClippingRect, m_client,
rotation_kind);
rect, &AbsoluteClippingRect, m_client, rotation_kind);
// Draw tooltip
if (hovering && !m_selected_item) {
std::string tooltip = item.getDescription(m_client->idef());
@ -2900,8 +2957,8 @@ void GUIFormSpecMenu::drawSelectedItem()
if (!m_selected_item) {
drawItemStack(driver, m_font, ItemStack(),
core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
NULL, m_client, IT_ROT_DRAGGED);
core::rect<s32>(v2s32(0, 0), v2s32(0, 0)), NULL,
m_client, IT_ROT_DRAGGED);
return;
}
@ -3482,9 +3539,10 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
}
}
}
// Mouse wheel events: send to hovered element instead of focused
if(event.EventType==EET_MOUSE_INPUT_EVENT
&& event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
// Mouse wheel and move events: send to hovered element instead of focused
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
(event.MouseInput.Event == EMIE_MOUSE_WHEEL ||
event.MouseInput.Event == EMIE_MOUSE_MOVED)) {
s32 x = event.MouseInput.X;
s32 y = event.MouseInput.Y;
gui::IGUIElement *hovered =
@ -3492,7 +3550,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
core::position2d<s32>(x, y));
if (hovered && isMyChild(hovered)) {
hovered->OnEvent(event);
return true;
return event.MouseInput.Event == EMIE_MOUSE_WHEEL;
}
}
@ -4041,8 +4099,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
m_old_pointer = m_pointer;
}
if (event.EventType == EET_GUI_EVENT) {
if (event.EventType == EET_GUI_EVENT) {
if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED
&& isVisible()) {
// find the element that was clicked
@ -4128,6 +4186,11 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
s.fdefault = L"Changed";
acceptInput(quit_mode_no);
s.fdefault = L"";
} else if ((s.ftype == f_Unknown) &&
(s.fid == event.GUIEvent.Caller->getID())) {
s.send = true;
acceptInput();
s.send = false;
}
}
}

@ -469,6 +469,7 @@ protected:
video::SColor m_default_tooltip_bgcolor;
video::SColor m_default_tooltip_color;
private:
IFormSource *m_form_src;
TextDest *m_text_dst;
@ -529,6 +530,7 @@ private:
void parseSimpleField(parserData* data,std::vector<std::string> &parts);
void parseTextArea(parserData* data,std::vector<std::string>& parts,
const std::string &type);
void parseHyperText(parserData *data, const std::string &element);
void parseLabel(parserData* data, const std::string &element);
void parseVertLabel(parserData* data, const std::string &element);
void parseImageButton(parserData* data, const std::string &element,

1137
src/gui/guiHyperText.cpp Normal file

File diff suppressed because it is too large Load Diff

229
src/gui/guiHyperText.h Normal file

@ -0,0 +1,229 @@
/*
Minetest
Copyright (C) 2019 EvicenceBKidscode / Pierre-Yves Rollo <dev@pyrollo.com>
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 "config.h" // for USE_FREETYPE
using namespace irr;
class ISimpleTextureSource;
class Client;
#if USE_FREETYPE
#include "irrlicht_changes/CGUITTFont.h"
#endif
class ParsedText
{
public:
ParsedText(const wchar_t *text);
~ParsedText();
enum ElementType
{
ELEMENT_TEXT,
ELEMENT_SEPARATOR,
ELEMENT_IMAGE,
ELEMENT_ITEM
};
enum BackgroundType
{
BACKGROUND_NONE,
BACKGROUND_COLOR
};
enum FloatType
{
FLOAT_NONE,
FLOAT_RIGHT,
FLOAT_LEFT
};
enum HalignType
{
HALIGN_CENTER,
HALIGN_LEFT,
HALIGN_RIGHT,
HALIGN_JUSTIFY
};
enum ValignType
{
VALIGN_MIDDLE,
VALIGN_TOP,
VALIGN_BOTTOM
};
typedef std::unordered_map<std::string, std::string> StyleList;
typedef std::unordered_map<std::string, std::string> AttrsList;
struct Tag
{
std::string name;
AttrsList attrs;
StyleList style;
};
struct Element
{
std::list<Tag *> tags;
ElementType type;
core::stringw text = "";
core::dimension2d<u32> dim;
core::position2d<s32> pos;
s32 drawwidth;
FloatType floating = FLOAT_NONE;
ValignType valign;
#if USE_FREETYPE
gui::CGUITTFont *font;
#else
gui::IGUIFont *font;
#endif
irr::video::SColor color;
irr::video::SColor hovercolor;
bool underline;
s32 baseline = 0;
// img & item specific attributes
std::string name;
v3s16 angle{0, 0, 0};
v3s16 rotation{0, 0, 0};
s32 margin = 10;
void setStyle(StyleList &style);
};
struct Paragraph
{
std::vector<Element> elements;
HalignType halign;
s32 margin = 10;
void setStyle(StyleList &style);
};
std::vector<Paragraph> m_paragraphs;
// Element style
s32 margin = 3;
ValignType valign = VALIGN_TOP;
BackgroundType background_type = BACKGROUND_NONE;
irr::video::SColor background_color;
Tag m_root_tag;
protected:
// Parser functions
void enterElement(ElementType type);
void endElement();
void enterParagraph();
void endParagraph();
void pushChar(wchar_t c);
ParsedText::Tag *newTag(const std::string &name, const AttrsList &attrs);
ParsedText::Tag *openTag(const std::string &name, const AttrsList &attrs);
bool closeTag(const std::string &name);
void parseGenericStyleAttr(const std::string &name, const std::string &value,
StyleList &style);
void parseStyles(const AttrsList &attrs, StyleList &style);
void globalTag(const ParsedText::AttrsList &attrs);
u32 parseTag(const wchar_t *text, u32 cursor);
void parse(const wchar_t *text);
std::unordered_map<std::string, StyleList> m_elementtags;
std::unordered_map<std::string, StyleList> m_paragraphtags;
std::vector<Tag *> m_tags;
std::list<Tag *> m_active_tags;
// Current values
StyleList m_style;
Element *m_element;
Paragraph *m_paragraph;
};
class TextDrawer
{
public:
TextDrawer(const wchar_t *text, Client *client, gui::IGUIEnvironment *environment,
ISimpleTextureSource *tsrc);
void place(const core::rect<s32> &dest_rect);
inline s32 getHeight() { return m_height; };
void draw(const core::rect<s32> &dest_rect,
const core::position2d<s32> &dest_offset);
ParsedText::Element *getElementAt(core::position2d<s32> pos);
ParsedText::Tag *m_hovertag;
protected:
struct RectWithMargin
{
core::rect<s32> rect;
s32 margin;
};
ParsedText m_text;
Client *m_client;
gui::IGUIEnvironment *m_environment;
s32 m_height;
s32 m_voffset;
std::vector<RectWithMargin> m_floating;
};
class GUIHyperText : public gui::IGUIElement
{
public:
//! constructor
GUIHyperText(const wchar_t *text, gui::IGUIEnvironment *environment,
gui::IGUIElement *parent, s32 id,
const core::rect<s32> &rectangle, Client *client,
ISimpleTextureSource *tsrc);
//! destructor
virtual ~GUIHyperText();
//! draws the element and its children
virtual void draw();
core::dimension2du getTextDimension();
bool OnEvent(const SEvent &event);
protected:
// GUI members
Client *m_client;
GUIScrollBar *m_vscrollbar;
TextDrawer m_drawer;
// Positioning
u32 m_scrollbar_width;
core::rect<s32> m_display_text_rect;
core::position2d<s32> m_text_scrollpos;
ParsedText::Element *getElementAt(s32 X, s32 Y);
void checkHover(s32 X, s32 Y);
};

@ -327,6 +327,8 @@ namespace gui
(const wchar_t* text, scene::ISceneManager* smgr, scene::ISceneNode* parent = 0,
const video::SColor& color = video::SColor(255, 0, 0, 0), bool center = false );
inline s32 getAscender() const { return font_metrics.ascender; }
protected:
bool use_monochrome;
bool use_transparency;

@ -947,3 +947,28 @@ std::wstring translate_string(const std::wstring &s) {
translate_all(s, i, res);
return res;
}
/**
* Create a std::string from a irr::core:stringw.
*/
std::string strwtostr(const irr::core::stringw &str)
{
std::string text = core::stringc(str.c_str()).c_str();
return text;
}
/**
* Create a irr::core:stringw from a std::string.
*/
irr::core::stringw strtostrw(const std::string &str)
{
size_t size = str.size();
// s.size() doesn't include NULL terminator
wchar_t *text = new wchar_t[size + sizeof(wchar_t)];
const char *data = &str[0];
mbsrtowcs(text, &data, size, NULL);
text[size] = L'\0';
return text;
}

@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_bloated.h"
#include "irrString.h"
#include <cstdlib>
#include <string>
#include <cstring>
@ -723,3 +724,13 @@ inline std::string str_join(const std::vector<std::string> &list,
}
return oss.str();
}
/**
* Create a std::string from a irr::core::stringw.
*/
std::string strwtostr(const irr::core::stringw &str);
/**
* Create a irr::core:stringw from a std::string.
*/
irr::core::stringw strtostrw(const std::string &str);

@ -167,6 +167,8 @@ src/gui/guiEngine.h
src/gui/guiFormSpecMenu.cpp
src/gui/guiFormSpecMenu.h
src/gui/guiKeyChangeMenu.cpp
src/gui/guiHyperText.cpp
src/gui/guiHyperText.h
src/gui/guiMainMenu.h
src/gui/guiPasswordChange.cpp
src/gui/guiPathSelectMenu.cpp