mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-27 17:28:41 +00:00
Formspec: add hypertext element
This commit is contained in:
parent
8697090b35
commit
72416a6a1f
22 changed files with 1792 additions and 79 deletions
|
@ -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
1137
src/gui/guiHyperText.cpp
Normal file
File diff suppressed because it is too large
Load diff
229
src/gui/guiHyperText.h
Normal file
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);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue