Compare commits

...

14 Commits

Author SHA1 Message Date
Wuzzy
d153c78406
Merge e211a5fa64771bd7219da95be370fe9c6be30e44 into 9a1501ae89ffe79c38dbd6756c9e7ed647dd7dc1 2024-06-27 11:13:16 -07:00
grorp
9a1501ae89
CIrrDeviceSDL: Fix numpad key events not having correct KeyInput.Char (#14780)
Allows you to change viewing range using numpad +/- again. This fix also works with the current unreleased version of SDL 3.

The keycodes for numpad keys (both SDL keycodes and Irrlicht keycodes) are not the same as the keycodes for the equivalent non-numpad keys and don't correspond to chars, so I mapped them to chars manually.

Since I think the resolution of https://github.com/minetest/minetest/issues/13770 was "just disable numlock", I made sure to only do this for the numpad number keys if numlock is enabled.
2024-06-27 14:44:44 +02:00
Wuzzy
e211a5fa64 Coding style fix in guiHyperText.cpp 2024-06-21 18:13:31 +02:00
Jean-Patrick Guerrero
0f12a322d9 Fix delay on bound supertips 2024-06-21 18:00:14 +02:00
Wuzzy
bab7187a23 DevTest: Add tests for element-bound tooltips 2024-06-08 17:39:53 +02:00
Wuzzy
f2422a2596 Clean up supertip documentation 2024-06-08 11:09:00 +02:00
Jean-Patrick Guerrero
3638d93b77 Allow to bind supertip to a formspec element 2024-06-08 11:06:31 +02:00
Wuzzy
7c25907675 Document supertip border default as true 2024-06-07 08:40:04 +02:00
Jean-Patrick Guerrero
4fcf9797aa Fix supertip flicker; enable supertip border 2024-06-07 08:35:00 +02:00
Wuzzy
b207351c41 Fix segfault if invalid arg in supertip element 2024-06-06 14:27:33 +02:00
Wuzzy
14fa03172d Supertip: Make static position argument mandatory 2024-06-06 13:45:14 +02:00
Wuzzy
b3c06bb9e5 Fix positioning of dynamic supertips 2024-06-06 12:14:50 +02:00
Wuzzy
4fa1b479a6 DevTest: Add test for supertip formspec element 2024-06-04 22:47:41 +02:00
Jean-Patrick Guerrero
46bc0d5ca0 Add "supertip" formspec element 2024-06-04 19:50:00 +02:00
8 changed files with 463 additions and 34 deletions

@ -2840,6 +2840,7 @@ Elements
### `tooltip[<gui_element_name>;<tooltip_text>;<bgcolor>;<fontcolor>]`
* Adds tooltip for an element
* It has to be declared *after* the element that is bound to
* `bgcolor` tooltip background color as `ColorString` (optional)
* `fontcolor` tooltip font color as `ColorString` (optional)
@ -2849,6 +2850,25 @@ Elements
* `bgcolor` tooltip background color as `ColorString` (optional)
* `fontcolor` tooltip font color as `ColorString` (optional)
### `supertip[<gui_element_name>;<staticPos>;<width>;<name>;<text>]`
* Adds an advanced tooltip for an element. Displays a formatted text using
`Markup Language` in a tooltip.
* This supertip has to be declared *after* the element that is bound to.
* `staticPos` is an optional position of the form `posX,posY` in formspec coordinates.
If specified, the tooltip will always appear at these given formspec coordinates.
If this field is empty, the tooltip will follow the cursor.
* `width` sets the tooltip width (in formspec units).
* `name` is the name of the field.
* `text` is the formatted text using `Markup Language` described below.
### `supertip[<X>,<Y>;<W>,<H>;<staticPos>;<width>;<name>;<text>]`
* Adds an advanced tooltip for an area. Displays a formatted text using
`Markup Language` in a tooltip.
* `X`, `Y`, `W` and `H` set the cursor hover area that allows the tooltip to pop-up.
* `staticPos`, `width`, `name`, `text`: See above.
### `image[<X>,<Y>;<W>,<H>;<texture name>;<middle>]`
* Show an image.
@ -3412,6 +3432,7 @@ Some types may inherit styles from parent types.
* model
* pwdfield, inherits from field
* scrollbar
* supertip
* tabheader
* table
* textarea
@ -3512,6 +3533,13 @@ Some types may inherit styles from parent types.
* sound - a sound to be played when triggered.
* scrollbar
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* supertip
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* bgcolor - color, sets background color.
* border - boolean, draw border. Set to false to hide the bevelled tooltip pane. Default true.
* bgimg - standard background image. Defaults to none.
* bgimg_middle - Makes the bgimg textures render in 9-sliced mode and defines the middle rect.
See background9[] documentation for more details.
* tabheader
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* sound - a sound to be played when a different tab is selected.

@ -468,6 +468,51 @@ mouse control = true]
background9[0,0;0,0;testformspec_bg_9slice.png;true;4,6]
background[1,1;0,0;testformspec_bg.png;true]
]],
-- Tooltip
[[
formspec_version[7]
size[12,13]
label[1,0.5;Hover a red box or a button for a tooltip.]
box[1,1;1,1;#ff000080]
tooltip[1,1;1,1;Normal tooltip in an area]
button[2.5,1;3,1;tt_btn;HOVERME]
tooltip[tt_btn;Normal tooltip on a button]
box[1,3;1,1;#ff000080]
box[2.4,3.4;0.2,0.2;#ffffff80]
supertip[1,3;1,1;2.5,3.5;5;supertip_static;<big>Simple supertip (<i>static</i>)</big>
This one should always appear at the tiny white square.]
box[1,5;1,1;#ff000080]
supertip[1,5;1,1;;5;supertip_dynamic;<big>Simple supertip (<i>dynamic</i>)</big>
This should appear at the cursor.]
button[2.5,5;3,1;st_btn;HOVERME]
supertip[st_btn;;5;supertip_dynamic_btn;<big>Simple supertip (<i>dynamic</i>)</big>
This should appear at the cursor when hovering the button.]
box[1,7;1,1;#ff000080]
supertip[1,7;1,1;;5;supertip_dynamic_complex;]]..minetest.formspec_escape([[<big>Complex supertip (<i>dynamic</i>)</big>
<img name=testformspec_node.png float=right width=64 height=64>
<left>Left align</left>
<center>Right align</center>
<right>Right align</right>
<b>Bold</b> <i>Italic</i> <u>Underline</u> <mono>Mono</mono>
Item:
<item name=testformspec:node>]])..[[]
box[1,9;1,1;#ff000080]
supertip[1,9;1,1;;5;supertip_stone;]]..minetest.formspec_escape([[<global color=#333 background=#aaa margin=20>
<item name=testformspec:node float=left width=64 height=64>
<big><b><center>Formspec Test Node</center></b></big>
The <b>Formspec Test Node</b> is a dummy node to display an item in the <mono>testformspec</mono> mod.
<b>Max. stack size:</b> 99
<b>Drops:</b> <i>itself</i> <item name=testformspec:node width=32 height=32>]])..
"]"
}
local page_id = 2
@ -477,7 +522,7 @@ local function show_test_formspec(pname)
page = page()
end
local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized;" .. page_id .. ";false;false]"
local fs = page .. "tabheader[0,0;11,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound,Background,Unsized,Tooltip;" .. page_id .. ";false;false]"
minetest.show_formspec(pname, "testformspec:formspec", fs)
end

@ -129,9 +129,9 @@ EM_BOOL CIrrDeviceSDL::MouseLeaveCallback(int eventType, const EmscriptenMouseEv
}
#endif
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE irrlichtKey)
{
switch (key) {
switch (irrlichtKey) {
// keys which are known to have safe special character interpretation
// could need changes over time (removals and additions!)
case KEY_RETURN:
@ -189,24 +189,68 @@ bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
}
}
int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock)
{
switch (irrlichtKey) {
// special cases that always return a char regardless of how the SDL keycode
// looks
switch (key) {
case KEY_RETURN:
case KEY_ESCAPE:
return (int)key;
return (int)irrlichtKey;
// This is necessary for keys on the numpad because they don't use the same
// keycodes as their non-numpad versions (whose keycodes correspond to chars),
// but have their own SDL keycodes and their own Irrlicht keycodes (which
// don't correspond to chars).
case KEY_MULTIPLY:
return '*';
case KEY_ADD:
return '+';
case KEY_SUBTRACT:
return '-';
case KEY_DIVIDE:
return '/';
default:
break;
}
if (numlock) {
// Number keys on the numpad are also affected, but we only want them
// to produce number chars when numlock is enabled.
switch (irrlichtKey) {
case KEY_NUMPAD0:
return '0';
case KEY_NUMPAD1:
return '1';
case KEY_NUMPAD2:
return '2';
case KEY_NUMPAD3:
return '3';
case KEY_NUMPAD4:
return '4';
case KEY_NUMPAD5:
return '5';
case KEY_NUMPAD6:
return '6';
case KEY_NUMPAD7:
return '7';
case KEY_NUMPAD8:
return '8';
case KEY_NUMPAD9:
return '9';
default:
break;
}
}
// SDL in-place ORs values with no character representation with 1<<30
// https://wiki.libsdl.org/SDL2/SDLKeycodeLookup
if (assumedChar & (1 << 30))
// This also affects the numpad keys btw.
if (sdlKey & (1 << 30))
return 0;
switch (key) {
switch (irrlichtKey) {
case KEY_PRIOR:
case KEY_NEXT:
case KEY_HOME:
@ -218,7 +262,7 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
case KEY_NUMLOCK:
return 0;
default:
return assumedChar;
return sdlKey;
}
}
@ -825,7 +869,8 @@ bool CIrrDeviceSDL::run()
irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0;
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key);
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key,
(SDL_event.key.keysym.mod & KMOD_NUM) != 0);
postEventFromUser(irrevent);
} break;

@ -273,10 +273,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub
#endif
// Check if a key is a known special character with no side effects on text boxes.
static bool keyIsKnownSpecial(EKEY_CODE key);
static bool keyIsKnownSpecial(EKEY_CODE irrlichtKey);
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
static int findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key);
static int findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock);
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
void resetReceiveTextInputEvents();

@ -65,7 +65,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiInventoryList.h"
#include "guiItemImage.h"
#include "guiScrollContainer.h"
#include "guiHyperText.h"
#include "guiScene.h"
#define MY_CHECKPOS(a,b) \
@ -1755,6 +1754,118 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen
m_fields.push_back(spec);
}
void GUIFormSpecMenu::parseSuperTip(parserData *data, const std::string &element)
{
std::vector<std::string> parts;
if (!precheckElement("supertip", element, 5, 6, parts))
return;
// Get mode and check size
bool rect_mode = parts[0].find(',') != std::string::npos;
size_t base_size = rect_mode ? 6 : 5;
if (parts.size() != base_size && parts.size() != base_size + 2) {
errorstream << "Invalid supertip element(" << parts.size() << "): '"
<< element << "'" << std::endl;
return;
}
std::vector<std::string> v_stpos;
bool floating = true;
size_t i = rect_mode ? 2 : 1;
if (parts[i] != "") {
v_stpos = split(parts[i], ',');
if (v_stpos.size() != i) {
errorstream << "Invalid staticPos in supertip element(" << parts.size() <<
"): \"" << parts[2] << "\"" << std::endl;
return;
}
floating = false;
}
std::string name = parts[rect_mode ? 4 : 3];
std::string text = parts[rect_mode ? 5 : 4];
if (m_form_src)
text = m_form_src->resolveText(text);
FieldSpec spec(
name,
translate_string(utf8_to_wide(unescape_string(text))),
L"",
258 + m_fields.size()
);
m_fields.push_back(spec);
if (rect_mode) {
std::vector<std::string> v_pos = split(parts[0], ',');
std::vector<std::string> v_geom = split(parts[1], ',');
MY_CHECKPOS("supertip", 0);
MY_CHECKGEOM("supertip", 1);
s32 width = stof(parts[3]) * spacing.Y;
v2s32 pos;
v2s32 geom;
v2s32 stpos;
if (data->real_coordinates) {
pos = getRealCoordinateBasePos(v_pos);
geom = getRealCoordinateGeometry(v_geom);
if (!floating)
stpos = getRealCoordinateBasePos(v_stpos);
} else {
pos = getElementBasePos(&v_pos);
geom.X = stof(v_geom[0]) * spacing.X;
geom.Y = stof(v_geom[1]) * spacing.Y;
if (!floating)
stpos = getElementBasePos(&v_stpos);
}
core::rect<s32> rect(pos, pos + geom);
GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment,
data->current_parent, spec.fid, rect, m_client, m_tsrc);
auto style = getStyleForElement("supertip", spec.fname);
e->setStyles(style);
SuperTipSpec geospec(name, "", text, e->getAbsoluteClippingRect(), stpos, width, floating);
m_supertips.emplace_back(e, geospec);
e->setVisible(false);
e->drop();
} else {
std::string fieldname = parts[0];
core::rect<s32> rect;
for (const auto &f : m_fields) {
if (f.fname == fieldname) {
auto *e = getElementFromId(f.fid, true);
rect = e->getAbsoluteClippingRect();
break;
}
}
s32 width = stof(parts[2]) * spacing.Y;
v2s32 stpos;
if (!floating) {
if (data->real_coordinates)
stpos = getRealCoordinateBasePos(v_stpos);
else
stpos = getElementBasePos(&v_stpos);
}
m_supertip_map[fieldname] = SuperTipSpec(name, fieldname, text, rect, stpos, width, floating);
}
}
void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
{
std::vector<std::string> parts;
@ -2958,6 +3069,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
if (type == "supertip") {
parseSuperTip(data,description);
return;
}
if (type == "label") {
parseLabel(data,description);
return;
@ -3110,6 +3226,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_scrollbars.clear();
m_fields.clear();
m_tooltips.clear();
m_supertips.clear();
m_supertip_map.clear();
m_tooltip_rects.clear();
m_inventory_rings.clear();
m_dropdowns.clear();
@ -3639,6 +3757,19 @@ void GUIFormSpecMenu::drawMenu()
}
}
/*
Draw rect_mode supertip
*/
for (const auto &pair : m_supertips) {
if (m_supertip_map.count(pair.second.parent_name) == 0) {
const auto &hover_rect = pair.second.hover_rect;
if (hover_rect.getArea() > 0 && hover_rect.isPointInside(m_pointer)) {
showSuperTip(pair.first, pair.second);
break;
}
}
}
// Some elements are only visible while being drawn
for (gui::IGUIElement *e : m_clickthrough_elements)
e->setVisible(true);
@ -3668,6 +3799,10 @@ void GUIFormSpecMenu::drawMenu()
NULL, m_client, IT_ROT_HOVERED);
}
for (const auto &pair : m_supertips)
if (pair.first->isVisible())
pair.first->setVisible(false);
/*
Draw fields/buttons tooltips and update the mouse cursor
*/
@ -3718,9 +3853,39 @@ void GUIFormSpecMenu::drawMenu()
if (delta >= m_tooltip_show_delay) {
const std::wstring &text = m_tooltips[field.fname].tooltip;
if (!text.empty())
if (!text.empty()) {
/* Tooltips get the priority over supertips */
showTooltip(text, m_tooltips[field.fname].color,
m_tooltips[field.fname].bgcolor);
} else if (m_supertip_map.count(field.fname) != 0) {
auto &spec = m_supertip_map[field.fname];
if (!spec.bound) {
spec.bound = true;
auto *parent_element = getElementFromId(field.fid, true);
auto txt = translate_string(utf8_to_wide(unescape_string(spec.text)));
GUIHyperText *e = new GUIHyperText(
txt.c_str(), Environment,
parent_element->getParent(), field.fid,
spec.hover_rect, m_client, m_tsrc);
auto style = getStyleForElement("supertip", spec.name);
e->setStyles(style);
m_supertips.emplace_back(e, spec);
e->setVisible(false);
e->drop();
} else {
for (const auto &pair : m_supertips) {
if (field.fname == pair.second.parent_name) {
showSuperTip(pair.first, pair.second);
break;
}
}
}
}
}
if (cursor_control &&
@ -3797,6 +3962,56 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text,
bringToFront(m_tooltip_element);
}
void GUIFormSpecMenu::showSuperTip(GUIHyperText *e, const SuperTipSpec &spec)
{
// Supertip size and offset
s32 tooltip_width = spec.width;
s32 tooltip_height = e->getTextHeight() + 5;
s32 tooltip_x, tooltip_y;
v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
// Calculate and set the tooltip position
if (spec.floating) {
/* Dynamic tooltip position, relative to cursor */
int tooltip_offset_x = m_btn_height;
int tooltip_offset_y = m_btn_height;
if (m_pointer_type == PointerType::Touch) {
tooltip_offset_x *= 3;
tooltip_offset_y = 0;
if (m_pointer.X > (s32)screenSize.X / 2)
tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
}
v2s32 basePos = getBasePos();
tooltip_x = (m_pointer.X - basePos.X) + tooltip_offset_x*2;
tooltip_y = (m_pointer.Y - basePos.Y) + tooltip_offset_y*2;
} else {
/* Static tooltip position, using formspec coordinates */
tooltip_x = spec.stpos[0];
tooltip_y = spec.stpos[1];
}
if (tooltip_x + tooltip_width > (s32)screenSize.X)
tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height;
if (tooltip_y + tooltip_height > (s32)screenSize.Y)
tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
e->setRelativePosition(
core::rect<s32>(
core::position2d<s32>(tooltip_x, tooltip_y),
core::dimension2d<s32>(tooltip_width, tooltip_height)
)
);
// Display the supertip
e->setVisible(true);
bringToFront(e);
}
void GUIFormSpecMenu::updateSelectedItem()
{
// Don't update when dragging an item

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiInventoryList.h"
#include "guiScrollBar.h"
#include "guiTable.h"
#include "guiHyperText.h"
#include "network/networkprotocol.h"
#include "client/joystick_controller.h"
#include "util/string.h"
@ -165,6 +166,36 @@ class GUIFormSpecMenu : public GUIModalMenu
irr::video::SColor color;
};
struct SuperTipSpec
{
SuperTipSpec() = default;
SuperTipSpec(const std::string &a_name,
const std::string &a_parent_name,
const std::string &a_text,
const core::rect<s32> &a_rect,
v2s32 a_stpos,
s32 a_width,
bool a_floating) :
name(a_name),
parent_name(a_parent_name),
text(a_text),
hover_rect(a_rect),
stpos(a_stpos),
width(a_width),
floating(a_floating)
{
}
std::string name;
std::string parent_name;
std::string text;
core::rect<s32> hover_rect;
v2s32 stpos;
s32 width;
bool floating;
bool bound = false;
};
public:
GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement* parent, s32 id,
@ -348,6 +379,8 @@ class GUIFormSpecMenu : public GUIModalMenu
std::vector<std::pair<FieldSpec, GUITable *>> m_tables;
std::vector<std::pair<FieldSpec, gui::IGUICheckBox *>> m_checkboxes;
std::map<std::string, TooltipSpec> m_tooltips;
std::vector<std::pair<GUIHyperText *, SuperTipSpec>> m_supertips;
std::map<std::string, SuperTipSpec> m_supertip_map;
std::vector<std::pair<gui::IGUIElement *, TooltipSpec>> m_tooltip_rects;
std::vector<std::pair<FieldSpec, GUIScrollBar *>> m_scrollbars;
std::vector<std::pair<FieldSpec, std::vector<std::string>>> m_dropdowns;
@ -469,6 +502,7 @@ class GUIFormSpecMenu : public GUIModalMenu
void parseTextArea(parserData* data,std::vector<std::string>& parts,
const std::string &type);
void parseHyperText(parserData *data, const std::string &element);
void parseSuperTip(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,
@ -499,6 +533,7 @@ class GUIFormSpecMenu : public GUIModalMenu
void showTooltip(const std::wstring &text, const irr::video::SColor &color,
const irr::video::SColor &bgcolor);
void showSuperTip(GUIHyperText *e, const SuperTipSpec &spec);
/**
* In formspec version < 2 the elements were not ordered properly. Some element

@ -29,12 +29,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlicht_changes/CGUITTFont.h"
#include "mainmenumanager.h"
#include "porting.h"
using namespace irr::gui;
#include "client/guiscalingfilter.h"
static bool check_color(const std::string &str)
{
irr::video::SColor color;
video::SColor color;
return parseColorString(str, color, false);
}
@ -372,7 +371,7 @@ void ParsedText::globalTag(const AttrsList &attrs)
else if (attr.second == "middle")
valign = ParsedText::VALIGN_MIDDLE;
} else if (attr.first == "background") {
irr::video::SColor color;
video::SColor color;
if (attr.second == "none") {
background_type = BACKGROUND_NONE;
} else if (parseColorString(attr.second, color, false)) {
@ -643,7 +642,7 @@ TextDrawer::TextDrawer(const wchar_t *text, Client *client,
if (e.font) {
e.dim.Width = e.font->getDimension(e.text.c_str()).Width;
e.dim.Height = e.font->getDimension(L"Yy").Height;
if (e.font->getType() == irr::gui::EGFT_CUSTOM) {
if (e.font->getType() == gui::EGFT_CUSTOM) {
CGUITTFont *tmp = static_cast<CGUITTFont*>(e.font);
e.baseline = e.dim.Height - 1 - tmp->getAscender() / 64;
}
@ -940,18 +939,49 @@ void TextDrawer::place(const core::rect<s32> &dest_rect)
m_voffset = 0;
}
void TextDrawer::drawBackgroundImage(
video::IVideoDriver *driver, const ParsedText &m_text, const core::rect<s32> &clip_rect)
{
auto size = m_text.background_image->getOriginalSize();
if (m_text.background_middle.getArea() > 0) {
draw2DImage9Slice(driver, m_text.background_image, clip_rect,
core::rect<s32>(0, 0, size.Width, size.Height), m_text.background_middle);
} else {
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
draw2DImageFilterScaled(driver, m_text.background_image, clip_rect,
core::rect<s32>(0, 0, size.Width, size.Height), nullptr, colors, true);
}
}
// Draw text in a rectangle with a given offset. Items are actually placed in
// relative (to upper left corner) coordinates.
void TextDrawer::draw(const core::rect<s32> &clip_rect,
const core::position2d<s32> &dest_offset)
{
irr::video::IVideoDriver *driver = m_guienv->getVideoDriver();
video::IVideoDriver *driver = m_guienv->getVideoDriver();
core::position2d<s32> offset = dest_offset;
offset.Y += m_voffset;
if (m_text.background_type == ParsedText::BACKGROUND_COLOR)
driver->draw2DRectangle(m_text.background_color, clip_rect);
if (m_text.border) {
const video::SColor color(255,0,0,0);
const auto &UpperLeft = clip_rect.UpperLeftCorner;
const auto &LowerRight = clip_rect.LowerRightCorner;
driver->draw2DLine(UpperLeft, core::position2di(LowerRight.X, UpperLeft.Y), color);
driver->draw2DLine(core::position2di(LowerRight.X, UpperLeft.Y), LowerRight, color);
driver->draw2DLine(LowerRight, core::position2di(UpperLeft.X, LowerRight.Y), color);
driver->draw2DLine(core::position2di(UpperLeft.X, LowerRight.Y), UpperLeft, color);
}
if (m_text.background_image)
drawBackgroundImage(driver, m_text, clip_rect);
for (auto &p : m_text.m_paragraphs) {
for (auto &el : p.elements) {
core::rect<s32> rect(el.pos + offset, el.dim);
@ -961,7 +991,7 @@ void TextDrawer::draw(const core::rect<s32> &clip_rect,
switch (el.type) {
case ParsedText::ELEMENT_SEPARATOR:
case ParsedText::ELEMENT_TEXT: {
irr::video::SColor color = el.color;
video::SColor color = el.color;
for (auto tag : el.tags)
if (&(*tag) == m_hovertag)
@ -992,9 +1022,9 @@ void TextDrawer::draw(const core::rect<s32> &clip_rect,
m_tsrc->getTexture(
stringw_to_utf8(el.text));
if (texture != 0)
m_guienv->getVideoDriver()->draw2DImage(
driver->draw2DImage(
texture, rect,
irr::core::rect<s32>(
core::rect<s32>(
core::position2d<s32>(0, 0),
texture->getOriginalSize()),
&clip_rect, 0, true);
@ -1006,7 +1036,7 @@ void TextDrawer::draw(const core::rect<s32> &clip_rect,
ItemStack item;
item.deSerialize(stringw_to_utf8(el.text), idef);
drawItemStack(m_guienv->getVideoDriver(),
drawItemStack(driver,
g_fontengine->getFont(), item, rect, &clip_rect, m_client,
IT_ROT_OTHER, el.angle, el.rotation);
}
@ -1032,13 +1062,13 @@ GUIHyperText::GUIHyperText(const wchar_t *text, IGUIEnvironment *environment,
setDebugName("GUIHyperText");
#endif
IGUISkin *skin = 0;
IGUISkin *skin = nullptr;
if (Environment)
skin = Environment->getSkin();
m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16;
core::rect<s32> rect = irr::core::rect<s32>(
core::rect<s32> rect = core::rect<s32>(
RelativeRect.getWidth() - m_scrollbar_width, 0,
RelativeRect.getWidth(), RelativeRect.getHeight());
@ -1084,6 +1114,25 @@ void GUIHyperText::checkHover(s32 X, s32 Y)
cursor_control->setActiveIcon(m_drawer.m_hovertag ? gui::ECI_HAND : gui::ECI_NORMAL);
}
void GUIHyperText::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles)
{
StyleSpec::State state = StyleSpec::STATE_DEFAULT;
StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state);
ParsedText &text = m_drawer.getText();
text.background_middle = style.getRect(StyleSpec::BGIMG_MIDDLE, core::rect<s32>());
text.border = style.getBool(StyleSpec::BORDER, true);
setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
if (text.background_type != text.BackgroundType::BACKGROUND_COLOR) {
text.background_type = text.BackgroundType::BACKGROUND_COLOR;
text.background_color = style.getColor(StyleSpec::BGCOLOR, video::SColor(255,110,130,60));
}
if (style.isNotDefault(StyleSpec::BGIMG))
text.background_image = style.getTexture(StyleSpec::BGIMG, m_tsrc);
}
bool GUIHyperText::OnEvent(const SEvent &event)
{
// Scroll bar
@ -1187,8 +1236,11 @@ void GUIHyperText::draw()
m_vscrollbar->setPos(0);
m_vscrollbar->setVisible(false);
}
m_drawer.draw(AbsoluteClippingRect,
m_display_text_rect.UpperLeftCorner + m_text_scrollpos);
if (m_drawer_ready) {
m_drawer.draw(AbsoluteClippingRect,
m_display_text_rect.UpperLeftCorner + m_text_scrollpos);
} else
m_drawer_ready = true;
// draw children
IGUIElement::draw();

@ -24,8 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <unordered_map>
#include <string>
#include "irrlichttypes_extrabloated.h"
using namespace irr;
#include "StyleSpec.h"
class ISimpleTextureSource;
class Client;
@ -99,8 +98,8 @@ class ParsedText
gui::IGUIFont *font;
irr::video::SColor color;
irr::video::SColor hovercolor;
video::SColor color;
video::SColor hovercolor;
bool underline;
s32 baseline = 0;
@ -130,7 +129,10 @@ class ParsedText
s32 margin = 3;
ValignType valign = VALIGN_TOP;
BackgroundType background_type = BACKGROUND_NONE;
irr::video::SColor background_color;
video::SColor background_color;
video::ITexture *background_image = nullptr;
core::rect<s32> background_middle;
bool border = false;
Tag m_root_tag;
@ -177,6 +179,8 @@ class TextDrawer
inline s32 getHeight() { return m_height; };
void draw(const core::rect<s32> &clip_rect,
const core::position2d<s32> &dest_offset);
void drawBackgroundImage(video::IVideoDriver *driver, const ParsedText &m_text, const core::rect<s32> &clip_rect);
ParsedText &getText() { return m_text; }
ParsedText::Element *getElementAt(core::position2d<s32> pos);
ParsedText::Tag *m_hovertag;
@ -211,10 +215,13 @@ class GUIHyperText : public gui::IGUIElement
//! draws the element and its children
virtual void draw();
core::dimension2du getTextDimension();
//! Returns the height of the text in pixels when it is drawn.
s32 getTextHeight() { return m_drawer.getHeight(); }
bool OnEvent(const SEvent &event);
void setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles);
protected:
// GUI members
ISimpleTextureSource *m_tsrc;
@ -228,4 +235,6 @@ class GUIHyperText : public gui::IGUIElement
ParsedText::Element *getElementAt(s32 X, s32 Y);
void checkHover(s32 X, s32 Y);
bool m_drawer_ready = false;
};