2010-12-22 02:34:21 +01:00
|
|
|
/*
|
2013-02-24 18:40:43 +01:00
|
|
|
Minetest
|
2013-02-24 19:38:45 +01:00
|
|
|
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
2010-12-22 02:34:21 +01:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
2012-06-05 16:56:56 +02:00
|
|
|
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
|
2010-12-22 02:34:21 +01:00
|
|
|
(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
|
2012-06-05 16:56:56 +02:00
|
|
|
GNU Lesser General Public License for more details.
|
2010-12-22 02:34:21 +01:00
|
|
|
|
2012-06-05 16:56:56 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
2010-12-22 02:34:21 +01:00
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
|
|
|
#include <limits>
|
2019-11-20 19:39:10 +01:00
|
|
|
#include <sstream>
|
2012-07-15 18:19:38 +02:00
|
|
|
#include "guiFormSpecMenu.h"
|
2010-12-22 02:34:21 +01:00
|
|
|
#include "constants.h"
|
2012-01-12 06:10:39 +01:00
|
|
|
#include "gamedef.h"
|
2018-11-28 20:01:49 +01:00
|
|
|
#include "client/keycode.h"
|
2016-03-19 17:08:24 +01:00
|
|
|
#include "util/strfnd.h"
|
2019-11-07 20:11:01 +01:00
|
|
|
#include <IGUIButton.h>
|
2011-10-12 12:53:38 +02:00
|
|
|
#include <IGUICheckBox.h>
|
2019-11-07 20:11:01 +01:00
|
|
|
#include <IGUIComboBox.h>
|
2011-10-12 12:53:38 +02:00
|
|
|
#include <IGUIEditBox.h>
|
|
|
|
#include <IGUIStaticText.h>
|
|
|
|
#include <IGUIFont.h>
|
2013-06-23 18:30:21 +02:00
|
|
|
#include <IGUITabControl.h>
|
2017-06-26 20:11:17 +02:00
|
|
|
#include "client/renderingengine.h"
|
2011-10-17 00:03:45 +02:00
|
|
|
#include "log.h"
|
2015-03-05 11:52:57 +01:00
|
|
|
#include "client/tile.h" // ITextureSource
|
2018-01-20 14:09:58 +01:00
|
|
|
#include "client/hud.h" // drawItemStack
|
2013-06-23 18:30:21 +02:00
|
|
|
#include "filesys.h"
|
2013-08-15 21:46:55 +02:00
|
|
|
#include "gettime.h"
|
2012-07-15 18:19:38 +02:00
|
|
|
#include "gettext.h"
|
2017-04-25 19:38:08 +02:00
|
|
|
#include "scripting_server.h"
|
2018-01-13 10:54:18 +01:00
|
|
|
#include "mainmenumanager.h"
|
2014-05-30 03:07:48 +02:00
|
|
|
#include "porting.h"
|
2014-06-14 11:22:09 +02:00
|
|
|
#include "settings.h"
|
2018-11-28 20:01:49 +01:00
|
|
|
#include "client/client.h"
|
|
|
|
#include "client/fontengine.h"
|
2015-02-21 14:17:36 +01:00
|
|
|
#include "util/hex.h"
|
|
|
|
#include "util/numeric.h"
|
|
|
|
#include "util/string.h" // for parseColorString()
|
2016-05-31 17:30:11 +02:00
|
|
|
#include "irrlicht_changes/static_text.h"
|
2018-11-28 20:01:49 +01:00
|
|
|
#include "client/guiscalingfilter.h"
|
2019-11-07 20:11:01 +01:00
|
|
|
#include "guiBackgroundImage.h"
|
|
|
|
#include "guiBox.h"
|
|
|
|
#include "guiButton.h"
|
2017-02-18 20:40:37 +01:00
|
|
|
#include "guiEditBoxWithScrollbar.h"
|
2019-11-07 20:11:01 +01:00
|
|
|
#include "guiItemImage.h"
|
|
|
|
#include "guiScrollBar.h"
|
|
|
|
#include "guiTable.h"
|
2015-06-10 01:54:33 +02:00
|
|
|
#include "intlGUIEditBox.h"
|
2019-09-10 15:11:26 +02:00
|
|
|
#include "guiHyperText.h"
|
2015-06-10 01:54:33 +02:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
#define MY_CHECKPOS(a,b) \
|
|
|
|
if (v_pos.size() != 2) { \
|
|
|
|
errorstream<< "Invalid pos for element " << a << "specified: \"" \
|
|
|
|
<< parts[b] << "\"" << std::endl; \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MY_CHECKGEOM(a,b) \
|
|
|
|
if (v_geom.size() != 2) { \
|
2019-06-27 14:40:49 +02:00
|
|
|
errorstream<< "Invalid geometry for element " << a << \
|
|
|
|
"specified: \"" << parts[b] << "\"" << std::endl; \
|
2013-06-23 18:30:21 +02:00
|
|
|
return; \
|
|
|
|
}
|
2010-12-22 02:34:21 +01:00
|
|
|
/*
|
2012-07-15 18:19:38 +02:00
|
|
|
GUIFormSpecMenu
|
2010-12-22 02:34:21 +01:00
|
|
|
*/
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
static unsigned int font_line_height(gui::IGUIFont *font)
|
|
|
|
{
|
|
|
|
return font->getDimension(L"Ay").Height + font->getKerningHeight();
|
|
|
|
}
|
|
|
|
|
2017-08-29 19:25:16 +02:00
|
|
|
inline u32 clamp_u8(s32 value)
|
|
|
|
{
|
|
|
|
return (u32) MYMIN(MYMAX(value, 0), 255);
|
|
|
|
}
|
|
|
|
|
2017-06-26 20:11:17 +02:00
|
|
|
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
|
|
|
|
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
|
|
|
|
Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
|
Optimize string (mis)handling (#8128)
* Optimize statbar drawing
The texture name of the statbar is a string passed by value.
That slows down the client and creates litter in the heap
as the content of the string is allocated there. Convert the
offending parameter to a const reference to avoid the
performance hit.
* Optimize texture cache
There is an unnecessary temporary created when the texture
path is being generated. This slows down the cache each time
a new texture is encountered and it needs to be loaded into
the cache. Additionally, the heap litter created by this
unnecessary temporary is particularly troublesome here as
the following code then piles another string (the resulting
full path of the texture) on top of it, followed by the
texture itself, which both are quite long term objects as
they are subsequently inserted into the cache where they can
remain for quite a while (especially if the texture turns
out to be a common one like dirt, grass or stone).
Use std::string.append to get rid of the temporary which
solves both issues (speed and heap fragmentation).
* Optimize animations in client
Each time an animated node is updated, an unnecessary copy of
the texture name is created, littering the heap with lots of
fragments. This can be specifically troublesome when looking
at oceans or large lava lakes as both of these nodes are
usually animated (the lava animation is pretty visible).
Convert the parameter of GenericCAO::updateTextures to a
const reference to get rid of the unnecessary copy.
There is a comment stating "std::string copy is mandatory as
mod can be a class member and there is a swap on those class
members ... do NOT pass by reference", reinforcing the
belief that the unnecessary copy is in fact necessary.
However one of the first things the code of the method does
is to assign the parameter to its class member, creating
another copy. By rearranging the code a little bit this
"another copy" can then be used by the subsequent code,
getting rid of the need to pass the parameter by value and
thus saving that copying effort.
* Optimize chat console history handling
The GUIChatConsole::replaceAndAddToHistory was getting the
line to work on by value which turns out to be unnecessary.
Get rid of that unnecessary copy by converting the parameter
to a const reference.
* Optimize gui texture setting
The code used to set the texture for GUI components was
getting the name of the texture by value, creating
unnecessary performance bottleneck for mods/games with
heavily textured GUIs. Get rid of the bottleneck by passing
the texture name as a const reference.
* Optimize sound playing code in GUIEngine
The GUIEngine's code receives the specification of the sound
to be played by value, which turns out to be most likely a
mistake as the underlying sound manager interface receives
the same thing by reference. Convert the offending parameter
to a const reference to get rid of the rather bulky copying
effort and the associated performance hit.
* Silence CLANG TIDY warnings for unit tests
Change "std::string" to "const std::string &" to avoid an
unnecessary local value copy, silencing the CLANG TIDY
process.
* Optimize formspec handling
The "formspec prepend" parameter was passed to the formspec
handling code by value, creating unnecessary copy of
std::string and slowing down the game if mods add things like
textured backgrounds for the player inventory and/or other
forms. Get rid of that performance bottleneck by converting
the parameter to a const reference.
* Optimize hotbar image handling
The code that sets the background images for the hotbar is
getting the name of the image by value, creating an
unnecessary std::string copying effort. Fix that by
converting the relevant parameters to const references.
* Optimize inventory deserialization
The inventory manager deserialization code gets the
serialized version of the inventory by value, slowing the
server and the client down when there are inventory updates.
This can get particularly troublesome with pipeworks which
adds nodes that can mess around with inventories
automatically or with mods that have mobs with inventories
that actively use them.
* Optimize texture scaling cache
There is an io::path parameter passed by value in the
procedure used to add images converted from textures,
leading to slowdown when the image is not yet created and
the conversion is thus needed. The performance hit is
quite significant as io::path is similar to std::string
so convert the parameter to a const reference to get rid of
it.
* Optimize translation file loader
Use "std::string::append" when calculating the final index
for the translation table to avoid unnecessary temporary
strings. This speeds the translation file loader up
significantly as std::string uses heap allocation which
tends to be rather slow. Additionally, the heap is no
longer being littered by these unnecessary string
temporaries, increasing performance of code that gets
executed after the translation file loader finishes.
* Optimize server map saving
When the directory structure for the world data is created
during server map saving, an unnecessary value passing of
the directory name slows things down. Remove that overhead
by converting the offending parameter to a const reference.
2019-05-18 17:19:13 +02:00
|
|
|
const std::string &formspecPrepend,
|
2018-03-28 17:04:41 +02:00
|
|
|
bool remap_dbl_click):
|
2017-06-26 20:11:17 +02:00
|
|
|
GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
|
2017-01-09 20:39:22 +01:00
|
|
|
m_invmgr(client),
|
2013-08-20 22:38:14 +02:00
|
|
|
m_tsrc(tsrc),
|
2014-09-21 21:18:43 +02:00
|
|
|
m_client(client),
|
2018-03-28 17:04:41 +02:00
|
|
|
m_formspec_prepend(formspecPrepend),
|
2014-04-20 02:40:25 +02:00
|
|
|
m_form_src(fsrc),
|
|
|
|
m_text_dst(tdst),
|
2016-05-27 08:35:07 +02:00
|
|
|
m_joystick(joystick),
|
2015-03-14 10:55:35 +01:00
|
|
|
m_remap_dbl_click(remap_dbl_click)
|
2010-12-22 02:34:21 +01:00
|
|
|
{
|
2013-07-07 21:53:40 +02:00
|
|
|
current_keys_pending.key_down = false;
|
|
|
|
current_keys_pending.key_up = false;
|
|
|
|
current_keys_pending.key_enter = false;
|
2013-08-07 19:48:31 +02:00
|
|
|
current_keys_pending.key_escape = false;
|
2013-07-07 21:53:40 +02:00
|
|
|
|
2014-05-30 03:07:48 +02:00
|
|
|
m_doubleclickdetect[0].time = 0;
|
|
|
|
m_doubleclickdetect[1].time = 0;
|
|
|
|
|
|
|
|
m_doubleclickdetect[0].pos = v2s32(0, 0);
|
|
|
|
m_doubleclickdetect[1].pos = v2s32(0, 0);
|
|
|
|
|
2014-06-25 11:52:09 +02:00
|
|
|
m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay");
|
2017-09-16 18:42:17 +02:00
|
|
|
m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname");
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
GUIFormSpecMenu::~GUIFormSpecMenu()
|
2010-12-22 02:34:21 +01:00
|
|
|
{
|
2010-12-25 15:04:51 +01:00
|
|
|
removeChildren();
|
2014-06-14 11:22:09 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (auto &table_it : m_tables) {
|
|
|
|
table_it.second->drop();
|
2014-06-20 19:50:47 +02:00
|
|
|
}
|
2019-11-07 20:11:01 +01:00
|
|
|
for (auto &inventorylist_it : m_inventorylists) {
|
|
|
|
inventorylist_it.e->drop();
|
|
|
|
}
|
|
|
|
for (auto &checkbox_it : m_checkboxes) {
|
|
|
|
checkbox_it.second->drop();
|
|
|
|
}
|
|
|
|
for (auto &scrollbar_it : m_scrollbars) {
|
|
|
|
scrollbar_it.second->drop();
|
|
|
|
}
|
|
|
|
for (auto &background_it : m_backgrounds) {
|
|
|
|
background_it->drop();
|
|
|
|
}
|
|
|
|
for (auto &tooltip_rect_it : m_tooltip_rects) {
|
|
|
|
tooltip_rect_it.first->drop();
|
|
|
|
}
|
2010-12-25 15:04:51 +01:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
delete m_selected_item;
|
2017-06-05 01:52:55 +02:00
|
|
|
delete m_form_src;
|
|
|
|
delete m_text_dst;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2018-01-13 10:54:18 +01:00
|
|
|
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
|
2018-03-28 17:04:41 +02:00
|
|
|
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
|
|
|
|
const std::string &formspecPrepend)
|
2018-01-13 10:34:56 +01:00
|
|
|
{
|
2018-01-13 10:54:18 +01:00
|
|
|
if (cur_formspec == nullptr) {
|
|
|
|
cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr,
|
2018-03-28 17:04:41 +02:00
|
|
|
client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend);
|
2018-01-13 10:54:18 +01:00
|
|
|
cur_formspec->doPause = false;
|
2018-01-13 10:34:56 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Caution: do not call (*cur_formspec)->drop() here --
|
|
|
|
the reference might outlive the menu, so we will
|
|
|
|
periodically check if *cur_formspec is the only
|
|
|
|
remaining reference (i.e. the menu was removed)
|
|
|
|
and delete it in that case.
|
|
|
|
*/
|
|
|
|
|
|
|
|
} else {
|
2018-03-28 17:04:41 +02:00
|
|
|
cur_formspec->setFormspecPrepend(formspecPrepend);
|
2018-01-13 10:54:18 +01:00
|
|
|
cur_formspec->setFormSource(fs_src);
|
|
|
|
cur_formspec->setTextDest(txt_dest);
|
2018-01-13 10:34:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
void GUIFormSpecMenu::removeChildren()
|
|
|
|
{
|
|
|
|
const core::list<gui::IGUIElement*> &children = getChildren();
|
2014-05-30 03:04:10 +02:00
|
|
|
|
2019-09-10 15:11:26 +02:00
|
|
|
while (!children.empty()) {
|
2014-05-30 03:04:10 +02:00
|
|
|
(*children.getLast())->remove();
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
2013-08-25 10:48:29 +02:00
|
|
|
|
2019-09-10 15:11:26 +02:00
|
|
|
if (m_tooltip_element) {
|
2013-06-23 18:30:21 +02:00
|
|
|
m_tooltip_element->remove();
|
2013-08-25 10:48:29 +02:00
|
|
|
m_tooltip_element->drop();
|
2019-09-10 15:11:26 +02:00
|
|
|
m_tooltip_element = nullptr;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
void GUIFormSpecMenu::setInitialFocus()
|
|
|
|
{
|
|
|
|
// Set initial focus according to following order of precedence:
|
|
|
|
// 1. first empty editbox
|
|
|
|
// 2. first editbox
|
2013-08-23 12:24:11 +02:00
|
|
|
// 3. first table
|
2013-08-19 11:26:51 +02:00
|
|
|
// 4. last button
|
|
|
|
// 5. first focusable (not statictext, not tabheader)
|
|
|
|
// 6. first child element
|
|
|
|
|
|
|
|
core::list<gui::IGUIElement*> children = getChildren();
|
|
|
|
|
|
|
|
// in case "children" contains any NULL elements, remove them
|
|
|
|
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
|
|
|
it != children.end();) {
|
|
|
|
if (*it)
|
|
|
|
++it;
|
|
|
|
else
|
|
|
|
it = children.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1. first empty editbox
|
2017-08-20 19:37:29 +02:00
|
|
|
for (gui::IGUIElement *it : children) {
|
|
|
|
if (it->getType() == gui::EGUIET_EDIT_BOX
|
|
|
|
&& it->getText()[0] == 0) {
|
|
|
|
Environment->setFocus(it);
|
2013-08-19 11:26:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. first editbox
|
2017-08-20 19:37:29 +02:00
|
|
|
for (gui::IGUIElement *it : children) {
|
|
|
|
if (it->getType() == gui::EGUIET_EDIT_BOX) {
|
|
|
|
Environment->setFocus(it);
|
2013-08-19 11:26:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
// 3. first table
|
2017-08-20 19:37:29 +02:00
|
|
|
for (gui::IGUIElement *it : children) {
|
|
|
|
if (it->getTypeName() == std::string("GUITable")) {
|
|
|
|
Environment->setFocus(it);
|
2013-08-19 11:26:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 4. last button
|
|
|
|
for (core::list<gui::IGUIElement*>::Iterator it = children.getLast();
|
|
|
|
it != children.end(); --it) {
|
|
|
|
if ((*it)->getType() == gui::EGUIET_BUTTON) {
|
|
|
|
Environment->setFocus(*it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 5. first focusable (not statictext, not tabheader)
|
2017-08-20 19:37:29 +02:00
|
|
|
for (gui::IGUIElement *it : children) {
|
|
|
|
if (it->getType() != gui::EGUIET_STATIC_TEXT &&
|
|
|
|
it->getType() != gui::EGUIET_TAB_CONTROL) {
|
|
|
|
Environment->setFocus(it);
|
2013-08-19 11:26:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 6. first child element
|
|
|
|
if (children.empty())
|
|
|
|
Environment->setFocus(this);
|
|
|
|
else
|
|
|
|
Environment->setFocus(*(children.begin()));
|
|
|
|
}
|
|
|
|
|
2015-06-10 01:54:33 +02:00
|
|
|
GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
|
2013-08-15 21:46:55 +02:00
|
|
|
{
|
2017-08-20 19:37:29 +02:00
|
|
|
for (auto &table : m_tables) {
|
|
|
|
if (tablename == table.first.fname)
|
|
|
|
return table.second;
|
2013-08-16 00:54:38 +02:00
|
|
|
}
|
2013-08-23 12:24:11 +02:00
|
|
|
return 0;
|
2013-08-16 00:54:38 +02:00
|
|
|
}
|
|
|
|
|
2016-05-05 18:50:02 +02:00
|
|
|
std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &name)
|
|
|
|
{
|
2017-08-20 19:37:29 +02:00
|
|
|
for (auto &dropdown : m_dropdowns) {
|
|
|
|
if (name == dropdown.first.fname)
|
|
|
|
return &dropdown.second;
|
2016-05-05 18:50:02 +02:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32 GUIFormSpecMenu::getElementBasePos(const std::vector<std::string> *v_pos)
|
2018-08-04 18:55:54 +02:00
|
|
|
{
|
2019-11-07 20:11:01 +01:00
|
|
|
v2f32 pos_f = v2f32(padding.X, padding.Y) + pos_offset * spacing;
|
2018-08-04 18:55:54 +02:00
|
|
|
if (v_pos) {
|
|
|
|
pos_f.X += stof((*v_pos)[0]) * spacing.X;
|
|
|
|
pos_f.Y += stof((*v_pos)[1]) * spacing.Y;
|
|
|
|
}
|
|
|
|
return v2s32(pos_f.X, pos_f.Y);
|
|
|
|
}
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32 GUIFormSpecMenu::getRealCoordinateBasePos(const std::vector<std::string> &v_pos)
|
2019-06-27 14:40:49 +02:00
|
|
|
{
|
2019-11-07 20:11:01 +01:00
|
|
|
return v2s32((stof(v_pos[0]) + pos_offset.X) * imgsize.X,
|
|
|
|
(stof(v_pos[1]) + pos_offset.Y) * imgsize.Y);
|
2019-06-27 14:40:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
v2s32 GUIFormSpecMenu::getRealCoordinateGeometry(const std::vector<std::string> &v_geom)
|
|
|
|
{
|
|
|
|
return v2s32(stof(v_geom[0]) * imgsize.X, stof(v_geom[1]) * imgsize.Y);
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,',');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if (((parts.size() == 2) || parts.size() == 3) ||
|
|
|
|
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
if (parts[1].find(';') != std::string::npos)
|
|
|
|
parts[1] = parts[1].substr(0,parts[1].find(';'));
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
data->invsize.X = MYMAX(0, stof(parts[0]));
|
|
|
|
data->invsize.Y = MYMAX(0, stof(parts[1]));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
lockSize(false);
|
2018-11-18 11:31:19 +01:00
|
|
|
#ifndef __ANDROID__
|
2014-03-04 19:57:39 +01:00
|
|
|
if (parts.size() == 3) {
|
|
|
|
if (parts[2] == "true") {
|
|
|
|
lockSize(true,v2u32(800,600));
|
|
|
|
}
|
|
|
|
}
|
2018-11-18 11:31:19 +01:00
|
|
|
#endif
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
data->explicit_size = true;
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &element)
|
2016-07-07 22:10:15 +02:00
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element, ',');
|
|
|
|
|
|
|
|
if (parts.size() >= 2) {
|
|
|
|
if (parts[1].find(';') != std::string::npos)
|
|
|
|
parts[1] = parts[1].substr(0, parts[1].find(';'));
|
|
|
|
|
|
|
|
container_stack.push(pos_offset);
|
2019-08-03 17:20:15 +02:00
|
|
|
pos_offset.X += stof(parts[0]);
|
|
|
|
pos_offset.Y += stof(parts[1]);
|
2016-07-07 22:10:15 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIFormSpecMenu::parseContainerEnd(parserData* data)
|
|
|
|
{
|
|
|
|
if (container_stack.empty()) {
|
|
|
|
errorstream<< "Invalid container end element, no matching container start element" << std::endl;
|
|
|
|
} else {
|
|
|
|
pos_offset = container_stack.top();
|
|
|
|
container_stack.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2017-01-09 20:39:22 +01:00
|
|
|
if (m_client == 0) {
|
|
|
|
warningstream<<"invalid use of 'list' with m_client==0"<<std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if (((parts.size() == 4) || (parts.size() == 5)) ||
|
|
|
|
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::string location = parts[0];
|
|
|
|
std::string listname = parts[1];
|
|
|
|
std::vector<std::string> v_pos = split(parts[2],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[3],',');
|
2017-08-20 19:37:29 +02:00
|
|
|
std::string startindex;
|
2013-06-23 18:30:21 +02:00
|
|
|
if (parts.size() == 5)
|
|
|
|
startindex = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS("list",2);
|
|
|
|
MY_CHECKGEOM("list",3);
|
|
|
|
|
|
|
|
InventoryLocation loc;
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (location == "context" || location == "current_name")
|
2013-06-23 18:30:21 +02:00
|
|
|
loc = m_current_inventory_location;
|
|
|
|
else
|
|
|
|
loc.deSerialize(location);
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = stoi(v_geom[0]);
|
|
|
|
geom.Y = stoi(v_geom[1]);
|
|
|
|
|
|
|
|
s32 start_i = 0;
|
2017-08-20 19:37:29 +02:00
|
|
|
if (!startindex.empty())
|
2013-06-23 18:30:21 +02:00
|
|
|
start_i = stoi(startindex);
|
2013-12-09 22:39:24 +01:00
|
|
|
|
|
|
|
if (geom.X < 0 || geom.Y < 0 || start_i < 0) {
|
|
|
|
errorstream<< "Invalid list element: '" << element << "'" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
// check for the existence of inventory and list
|
|
|
|
Inventory *inv = m_invmgr->getInventory(loc);
|
|
|
|
if (!inv) {
|
|
|
|
warningstream << "GUIFormSpecMenu::parseList(): "
|
|
|
|
<< "The inventory location "
|
|
|
|
<< "\"" << loc.dump() << "\" doesn't exist"
|
|
|
|
<< std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
InventoryList *ilist = inv->getList(listname);
|
|
|
|
if (!ilist) {
|
|
|
|
warningstream << "GUIFormSpecMenu::parseList(): "
|
|
|
|
<< "The inventory list \"" << listname << "\" "
|
|
|
|
<< "@ \"" << loc.dump() << "\" doesn't exist"
|
|
|
|
<< std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// trim geom if it is larger than the actual inventory size
|
|
|
|
s32 list_size = (s32)ilist->getSize();
|
|
|
|
if (list_size < geom.X * geom.Y + start_i) {
|
|
|
|
list_size -= MYMAX(start_i, 0);
|
|
|
|
geom.Y = list_size / geom.X;
|
|
|
|
geom.Y += list_size % geom.X > 0 ? 1 : 0;
|
|
|
|
if (geom.Y <= 1)
|
|
|
|
geom.X = list_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data->explicit_size)
|
|
|
|
warningstream << "invalid use of list without a size[] element" << std::endl;
|
|
|
|
|
|
|
|
FieldSpec spec(
|
|
|
|
"",
|
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-20 19:39:10 +01:00
|
|
|
258 + m_fields.size(),
|
|
|
|
3
|
2019-11-07 20:11:01 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
v2s32 pos;
|
|
|
|
core::rect<s32> rect;
|
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
|
|
|
rect = core::rect<s32>(pos.X, pos.Y,
|
|
|
|
pos.X + (geom.X - 1) * (imgsize.X * 1.25) + imgsize.X,
|
|
|
|
pos.Y + (geom.Y - 1) * (imgsize.Y * 1.25) + imgsize.Y);
|
|
|
|
} else {
|
|
|
|
pos = getElementBasePos(&v_pos);
|
|
|
|
rect = core::rect<s32>(pos.X, pos.Y,
|
|
|
|
pos.X + (geom.X - 1) * spacing.X + imgsize.X,
|
|
|
|
pos.Y + (geom.Y - 1) * spacing.Y + imgsize.Y);
|
|
|
|
}
|
|
|
|
|
|
|
|
gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment,
|
|
|
|
this, spec.fid, rect);
|
|
|
|
|
|
|
|
m_inventorylists.emplace_back(loc, listname, e, geom, start_i,
|
|
|
|
data->real_coordinates);
|
|
|
|
m_fields.push_back(spec);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseListRing(parserData* data, const std::string &element)
|
2015-06-16 10:48:54 +02:00
|
|
|
{
|
2017-01-09 20:39:22 +01:00
|
|
|
if (m_client == 0) {
|
|
|
|
errorstream << "WARNING: invalid use of 'listring' with m_client==0" << std::endl;
|
2015-06-16 10:48:54 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(element, ';');
|
|
|
|
|
|
|
|
if (parts.size() == 2) {
|
|
|
|
std::string location = parts[0];
|
|
|
|
std::string listname = parts[1];
|
|
|
|
|
|
|
|
InventoryLocation loc;
|
|
|
|
|
|
|
|
if (location == "context" || location == "current_name")
|
|
|
|
loc = m_current_inventory_location;
|
|
|
|
else
|
|
|
|
loc.deSerialize(location);
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
m_inventory_rings.emplace_back(loc, listname);
|
2015-06-16 10:48:54 +02:00
|
|
|
return;
|
2017-08-20 19:37:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (element.empty() && m_inventorylists.size() > 1) {
|
2015-06-16 10:48:54 +02:00
|
|
|
size_t siz = m_inventorylists.size();
|
|
|
|
// insert the last two inv list elements into the list ring
|
|
|
|
const ListDrawSpec &spa = m_inventorylists[siz - 2];
|
|
|
|
const ListDrawSpec &spb = m_inventorylists[siz - 1];
|
2017-08-20 19:37:29 +02:00
|
|
|
m_inventory_rings.emplace_back(spa.inventoryloc, spa.listname);
|
|
|
|
m_inventory_rings.emplace_back(spb.inventoryloc, spb.listname);
|
2015-06-18 04:25:11 +02:00
|
|
|
return;
|
2015-06-16 10:48:54 +02:00
|
|
|
}
|
2017-08-20 19:37:29 +02:00
|
|
|
|
2015-06-16 10:48:54 +02:00
|
|
|
errorstream<< "Invalid list ring element(" << parts.size() << ", "
|
|
|
|
<< m_inventorylists.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2015-01-09 16:57:54 +01:00
|
|
|
if (((parts.size() >= 3) && (parts.size() <= 4)) ||
|
2014-06-25 19:04:47 +02:00
|
|
|
((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::string name = parts[1];
|
|
|
|
std::string label = parts[2];
|
2017-08-20 19:37:29 +02:00
|
|
|
std::string selected;
|
2014-06-14 11:22:09 +02:00
|
|
|
|
2014-06-19 00:22:03 +02:00
|
|
|
if (parts.size() >= 4)
|
2014-06-14 11:22:09 +02:00
|
|
|
selected = parts[3];
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
MY_CHECKPOS("checkbox",0);
|
|
|
|
|
|
|
|
bool fselected = false;
|
|
|
|
|
|
|
|
if (selected == "true")
|
|
|
|
fselected = true;
|
|
|
|
|
2017-01-31 18:05:03 +01:00
|
|
|
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
|
2019-04-27 01:56:31 +02:00
|
|
|
const core::dimension2d<u32> label_size = m_font->getDimension(wlabel.c_str());
|
|
|
|
s32 cb_size = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH);
|
|
|
|
s32 y_center = (std::max(label_size.Height, (u32)cb_size) + 1) / 2;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
|
|
|
core::rect<s32> rect;
|
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
rect = core::rect<s32>(
|
|
|
|
pos.X,
|
|
|
|
pos.Y - y_center,
|
|
|
|
pos.X + label_size.Width + cb_size + 7,
|
|
|
|
pos.Y + y_center
|
|
|
|
);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
rect = core::rect<s32>(
|
|
|
|
pos.X,
|
|
|
|
pos.Y + imgsize.Y / 2 - y_center,
|
|
|
|
pos.X + label_size.Width + cb_size + 7,
|
|
|
|
pos.Y + imgsize.Y / 2 + y_center
|
|
|
|
);
|
|
|
|
}
|
2014-06-18 22:38:29 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2014-05-30 03:04:10 +02:00
|
|
|
wlabel, //Needed for displaying text on MSVC
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
spec.ftype = f_CheckBox;
|
2014-06-19 18:17:35 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, this,
|
2013-08-03 17:57:51 +02:00
|
|
|
spec.fid, spec.flabel.c_str());
|
2013-08-19 11:26:51 +02:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("checkbox", name);
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
|
|
|
}
|
2014-06-24 12:28:24 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
e->grab();
|
|
|
|
m_checkboxes.emplace_back(spec, e);
|
2013-06-23 18:30:21 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &element)
|
2014-06-19 18:17:35 +02:00
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
|
|
|
if (parts.size() >= 5) {
|
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
2019-06-27 14:40:49 +02:00
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
2015-04-19 22:06:34 +02:00
|
|
|
std::string name = parts[3];
|
2014-06-19 18:17:35 +02:00
|
|
|
std::string value = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS("scrollbar",0);
|
2019-06-27 14:40:49 +02:00
|
|
|
MY_CHECKGEOM("scrollbar",1);
|
2014-06-19 18:17:35 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
|
|
|
v2s32 dim;
|
2014-06-19 18:17:35 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
dim = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
dim.X = stof(v_geom[0]) * spacing.X;
|
|
|
|
dim.Y = stof(v_geom[1]) * spacing.Y;
|
2014-06-19 18:17:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
core::rect<s32> rect =
|
|
|
|
core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
|
|
|
|
|
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2014-06-19 18:17:35 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
bool is_horizontal = true;
|
|
|
|
|
|
|
|
if (parts[2] == "vertical")
|
|
|
|
is_horizontal = false;
|
|
|
|
|
|
|
|
spec.ftype = f_ScrollBar;
|
|
|
|
spec.send = true;
|
2019-11-07 20:11:01 +01:00
|
|
|
GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect,
|
|
|
|
is_horizontal, false);
|
2014-06-19 18:17:35 +02:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("scrollbar", name);
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
|
2014-06-19 18:17:35 +02:00
|
|
|
e->setMax(1000);
|
|
|
|
e->setMin(0);
|
|
|
|
e->setPos(stoi(parts[4]));
|
|
|
|
e->setSmallStep(10);
|
|
|
|
e->setLargeStep(100);
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
m_scrollbars.emplace_back(spec,e);
|
2014-06-19 18:17:35 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 3) ||
|
|
|
|
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
2013-08-20 22:38:14 +02:00
|
|
|
std::string name = unescape_string(parts[2]);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2016-07-07 22:10:15 +02:00
|
|
|
MY_CHECKPOS("image", 0);
|
|
|
|
MY_CHECKGEOM("image", 1);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = stof(v_geom[0]) * (float)imgsize.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2016-07-07 22:10:15 +02:00
|
|
|
if (!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of image without a size[] element"<<std::endl;
|
2019-11-07 20:11:01 +01:00
|
|
|
|
|
|
|
video::ITexture *texture = m_tsrc->getTexture(name);
|
|
|
|
if (!texture) {
|
|
|
|
errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:"
|
|
|
|
<< std::endl << "\t" << name << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FieldSpec spec(
|
|
|
|
name,
|
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-20 19:39:10 +01:00
|
|
|
258 + m_fields.size(),
|
|
|
|
1
|
2019-11-07 20:11:01 +01:00
|
|
|
);
|
|
|
|
core::rect<s32> rect(pos, pos + geom);
|
|
|
|
gui::IGUIImage *e = Environment->addImage(rect, this, spec.fid, 0, true);
|
|
|
|
e->setImage(texture);
|
|
|
|
e->setScaleImage(true);
|
|
|
|
e->setNotClipped(true);
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
2017-08-20 19:37:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parts.size() == 2) {
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
2013-08-20 22:38:14 +02:00
|
|
|
std::string name = unescape_string(parts[1]);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2016-07-07 22:10:15 +02:00
|
|
|
MY_CHECKPOS("image", 0);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32 pos = getElementBasePos(&v_pos);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2016-07-07 22:10:15 +02:00
|
|
|
if (!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of image without a size[] element"<<std::endl;
|
2019-11-07 20:11:01 +01:00
|
|
|
|
|
|
|
video::ITexture *texture = m_tsrc->getTexture(name);
|
|
|
|
if (!texture) {
|
|
|
|
errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:"
|
|
|
|
<< std::endl << "\t" << name << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
FieldSpec spec(
|
|
|
|
name,
|
|
|
|
L"",
|
|
|
|
L"",
|
|
|
|
258 + m_fields.size()
|
|
|
|
);
|
|
|
|
gui::IGUIImage *e = Environment->addImage(texture, pos, true, this,
|
|
|
|
spec.fid, 0);
|
|
|
|
e->setNotClipped(true);
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 3) ||
|
|
|
|
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
|
|
|
std::string name = parts[2];
|
|
|
|
|
|
|
|
MY_CHECKPOS("itemimage",0);
|
|
|
|
MY_CHECKGEOM("itemimage",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = stof(v_geom[0]) * (float)imgsize.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
if(!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of item_image without a size[] element"<<std::endl;
|
2019-11-07 20:11:01 +01:00
|
|
|
|
|
|
|
FieldSpec spec(
|
|
|
|
"",
|
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-20 19:39:10 +01:00
|
|
|
258 + m_fields.size(),
|
|
|
|
2
|
2019-11-07 20:11:01 +01:00
|
|
|
);
|
|
|
|
spec.ftype = f_ItemImage;
|
|
|
|
|
|
|
|
GUIItemImage *e = new GUIItemImage(Environment, this, spec.fid,
|
|
|
|
core::rect<s32>(pos, pos + geom), name, m_font, m_client);
|
|
|
|
e->drop();
|
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
|
|
|
|
const std::string &type)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 4) ||
|
|
|
|
((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
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 label = parts[3];
|
|
|
|
|
|
|
|
MY_CHECKPOS("button",0);
|
|
|
|
MY_CHECKGEOM("button",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
core::rect<s32> rect;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
|
|
|
|
pos.Y+geom.Y);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
|
|
|
|
pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
|
|
|
|
|
|
|
|
rect = core::rect<s32>(pos.X, pos.Y - m_btn_height,
|
2014-06-14 11:22:09 +02:00
|
|
|
pos.X + geom.X, pos.Y + m_btn_height);
|
2019-06-27 14:40:49 +02:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
if(!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of button without a size[] element"<<std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-01-31 18:05:03 +01:00
|
|
|
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
spec.ftype = f_Button;
|
|
|
|
if(type == "button_exit")
|
|
|
|
spec.is_exit = true;
|
2013-08-19 11:26:51 +02:00
|
|
|
|
2019-03-15 19:39:23 +01:00
|
|
|
GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str());
|
2019-03-15 20:03:12 +01:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
|
|
|
|
if (style.isNotDefault(StyleSpec::BGCOLOR)) {
|
2019-03-15 20:03:12 +01:00
|
|
|
e->setColor(style.getColor(StyleSpec::BGCOLOR));
|
|
|
|
}
|
2019-10-12 18:44:23 +02:00
|
|
|
if (style.isNotDefault(StyleSpec::BGCOLOR_HOVERED)) {
|
|
|
|
e->setHoveredColor(style.getColor(StyleSpec::BGCOLOR_HOVERED));
|
|
|
|
}
|
|
|
|
if (style.isNotDefault(StyleSpec::BGCOLOR_PRESSED)) {
|
|
|
|
e->setPressedColor(style.getColor(StyleSpec::BGCOLOR_PRESSED));
|
|
|
|
}
|
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {
|
2019-03-15 20:03:12 +01:00
|
|
|
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));
|
|
|
|
}
|
2019-03-16 22:38:36 +01:00
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
|
|
|
|
|
|
|
|
if (style.isNotDefault(StyleSpec::BGIMG)) {
|
|
|
|
std::string image_name = style.get(StyleSpec::BGIMG, "");
|
2019-10-12 18:44:23 +02:00
|
|
|
std::string hovered_image_name = style.get(StyleSpec::BGIMG_HOVERED, "");
|
2019-03-16 22:38:36 +01:00
|
|
|
std::string pressed_image_name = style.get(StyleSpec::BGIMG_PRESSED, "");
|
|
|
|
|
|
|
|
video::ITexture *texture = 0;
|
2019-10-12 18:44:23 +02:00
|
|
|
video::ITexture *hovered_texture = 0;
|
2019-03-16 22:38:36 +01:00
|
|
|
video::ITexture *pressed_texture = 0;
|
|
|
|
texture = m_tsrc->getTexture(image_name);
|
2019-10-12 18:44:23 +02:00
|
|
|
if (!hovered_image_name.empty())
|
|
|
|
hovered_texture = m_tsrc->getTexture(hovered_image_name);
|
|
|
|
else
|
|
|
|
hovered_texture = texture;
|
2019-03-16 22:38:36 +01:00
|
|
|
if (!pressed_image_name.empty())
|
|
|
|
pressed_texture = m_tsrc->getTexture(pressed_image_name);
|
|
|
|
else
|
|
|
|
pressed_texture = texture;
|
|
|
|
|
|
|
|
e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
|
|
|
|
e->setImage(guiScalingImageButton(
|
|
|
|
Environment->getVideoDriver(), texture, geom.X, geom.Y));
|
2019-10-12 18:44:23 +02:00
|
|
|
e->setHoveredImage(guiScalingImageButton(
|
|
|
|
Environment->getVideoDriver(), hovered_texture, geom.X, geom.Y));
|
2019-03-16 22:38:36 +01:00
|
|
|
e->setPressedImage(guiScalingImageButton(
|
|
|
|
Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
|
|
|
|
e->setScaleImage(true);
|
|
|
|
}
|
2019-03-15 20:03:12 +01:00
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
|
|
|
}
|
2014-06-14 11:22:09 +02:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2019-06-22 16:03:54 +02:00
|
|
|
if ((parts.size() >= 3 && parts.size() <= 5) ||
|
|
|
|
(parts.size() > 5 && m_formspec_version > FORMSPEC_API_VERSION)) {
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
2013-08-20 22:38:14 +02:00
|
|
|
std::string name = unescape_string(parts[2]);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
MY_CHECKPOS("background",0);
|
|
|
|
MY_CHECKGEOM("background",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
pos.X -= (spacing.X - (float)imgsize.X) / 2;
|
|
|
|
pos.Y -= (spacing.Y - (float)imgsize.Y) / 2;
|
|
|
|
|
|
|
|
geom.X = stof(v_geom[0]) * spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * spacing.Y;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2016-08-07 15:32:31 +02:00
|
|
|
bool clip = false;
|
2019-06-22 16:03:54 +02:00
|
|
|
if (parts.size() >= 4 && is_yes(parts[3])) {
|
2019-06-27 14:40:49 +02:00
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos) * -1;
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = v2s32(0, 0);
|
|
|
|
} else {
|
|
|
|
pos.X = stoi(v_pos[0]); //acts as offset
|
|
|
|
pos.Y = stoi(v_pos[1]);
|
|
|
|
}
|
2016-08-07 15:32:31 +02:00
|
|
|
clip = true;
|
2013-11-02 04:26:44 +01:00
|
|
|
}
|
2018-11-28 20:01:49 +01:00
|
|
|
|
2019-06-22 16:03:54 +02:00
|
|
|
core::rect<s32> middle;
|
|
|
|
if (parts.size() >= 5) {
|
|
|
|
std::vector<std::string> v_middle = split(parts[4], ',');
|
|
|
|
if (v_middle.size() == 1) {
|
|
|
|
s32 x = stoi(v_middle[0]);
|
|
|
|
middle.UpperLeftCorner = core::vector2di(x, x);
|
|
|
|
middle.LowerRightCorner = core::vector2di(-x, -x);
|
|
|
|
} else if (v_middle.size() == 2) {
|
|
|
|
s32 x = stoi(v_middle[0]);
|
|
|
|
s32 y = stoi(v_middle[1]);
|
|
|
|
middle.UpperLeftCorner = core::vector2di(x, y);
|
|
|
|
middle.LowerRightCorner = core::vector2di(-x, -y);
|
|
|
|
// `-x` is interpreted as `w - x`
|
|
|
|
} else if (v_middle.size() == 4) {
|
|
|
|
middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1]));
|
|
|
|
middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3]));
|
|
|
|
} else {
|
|
|
|
warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-20 14:56:16 +02:00
|
|
|
if (!data->explicit_size && !clip)
|
|
|
|
warningstream << "invalid use of unclipped background without a size[] element" << std::endl;
|
2018-11-28 20:01:49 +01:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
FieldSpec spec(
|
|
|
|
name,
|
|
|
|
L"",
|
|
|
|
L"",
|
|
|
|
258 + m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
core::rect<s32> rect;
|
|
|
|
if (!clip) {
|
|
|
|
// no auto_clip => position like normal image
|
|
|
|
rect = core::rect<s32>(pos, pos + geom);
|
|
|
|
} else {
|
|
|
|
// it will be auto-clipped when drawing
|
|
|
|
rect = core::rect<s32>(-pos, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
GUIBackgroundImage *e = new GUIBackgroundImage(Environment, this, spec.fid,
|
|
|
|
rect, name, middle, m_tsrc, clip);
|
|
|
|
|
|
|
|
FATAL_ERROR_IF(!e, "Failed to create background formspec element");
|
|
|
|
|
|
|
|
e->setNotClipped(true);
|
|
|
|
|
|
|
|
e->setVisible(false); // the element is drawn manually before all others
|
2013-11-02 04:26:44 +01:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
m_backgrounds.push_back(e);
|
|
|
|
m_fields.push_back(spec);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-08-23 12:24:11 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
|
|
|
data->table_options.clear();
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const std::string &part : parts) {
|
2013-08-23 12:24:11 +02:00
|
|
|
// Parse table option
|
2017-08-20 19:37:29 +02:00
|
|
|
std::string opt = unescape_string(part);
|
2013-08-23 12:24:11 +02:00
|
|
|
data->table_options.push_back(GUITable::splitOption(opt));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
data->table_columns.clear();
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const std::string &part : parts) {
|
|
|
|
std::vector<std::string> col_parts = split(part,',');
|
2013-08-23 12:24:11 +02:00
|
|
|
GUITable::TableColumn column;
|
|
|
|
// Parse column type
|
|
|
|
if (!col_parts.empty())
|
|
|
|
column.type = col_parts[0];
|
|
|
|
// Parse column options
|
|
|
|
for (size_t j = 1; j < col_parts.size(); ++j) {
|
|
|
|
std::string opt = unescape_string(col_parts[j]);
|
|
|
|
column.options.push_back(GUITable::splitOption(opt));
|
|
|
|
}
|
|
|
|
data->table_columns.push_back(column);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-08-23 12:24:11 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if (((parts.size() == 4) || (parts.size() == 5)) ||
|
|
|
|
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
|
|
|
std::string name = parts[2];
|
2013-08-14 20:21:39 +02:00
|
|
|
std::vector<std::string> items = split(parts[3],',');
|
2017-08-20 19:37:29 +02:00
|
|
|
std::string str_initial_selection;
|
2013-06-23 18:30:21 +02:00
|
|
|
std::string str_transparent = "false";
|
|
|
|
|
|
|
|
if (parts.size() >= 5)
|
|
|
|
str_initial_selection = parts[4];
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
MY_CHECKPOS("table",0);
|
|
|
|
MY_CHECKGEOM("table",1);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = stof(v_geom[0]) * spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * spacing.Y;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
spec.ftype = f_Table;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (std::string &item : items) {
|
2017-01-31 18:05:03 +01:00
|
|
|
item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item))));
|
2013-08-23 12:24:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//now really show table
|
2019-11-07 20:11:01 +01:00
|
|
|
GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
e->setTable(data->table_options, data->table_columns, items);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2015-06-10 01:54:33 +02:00
|
|
|
if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
|
|
|
|
e->setDynamicData(data->table_dyndata[name]);
|
2013-08-23 12:24:11 +02:00
|
|
|
}
|
2013-07-07 21:53:40 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
if (!str_initial_selection.empty() && str_initial_selection != "0")
|
|
|
|
e->setSelected(stoi(str_initial_selection));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("table", name);
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
m_tables.emplace_back(spec, e);
|
2013-08-23 12:24:11 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-08-23 12:24:11 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if (((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) ||
|
|
|
|
((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-08-23 12:24:11 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
|
|
|
std::string name = parts[2];
|
|
|
|
std::vector<std::string> items = split(parts[3],',');
|
2017-08-20 19:37:29 +02:00
|
|
|
std::string str_initial_selection;
|
2013-08-23 12:24:11 +02:00
|
|
|
std::string str_transparent = "false";
|
|
|
|
|
|
|
|
if (parts.size() >= 5)
|
|
|
|
str_initial_selection = parts[4];
|
|
|
|
|
|
|
|
if (parts.size() >= 6)
|
|
|
|
str_transparent = parts[5];
|
|
|
|
|
|
|
|
MY_CHECKPOS("textlist",0);
|
|
|
|
MY_CHECKGEOM("textlist",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-08-23 12:24:11 +02:00
|
|
|
v2s32 geom;
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = stof(v_geom[0]) * spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * spacing.Y;
|
|
|
|
}
|
2013-08-23 12:24:11 +02:00
|
|
|
|
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-08-23 12:24:11 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-08-23 12:24:11 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
spec.ftype = f_Table;
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (std::string &item : items) {
|
2017-01-31 18:05:03 +01:00
|
|
|
item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item))));
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
//now really show list
|
2019-11-07 20:11:01 +01:00
|
|
|
GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc);
|
2013-08-23 12:24:11 +02:00
|
|
|
|
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
2013-08-16 00:54:38 +02:00
|
|
|
}
|
2013-08-23 12:24:11 +02:00
|
|
|
|
|
|
|
e->setTextList(items, is_yes(str_transparent));
|
|
|
|
|
2015-06-10 01:54:33 +02:00
|
|
|
if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
|
|
|
|
e->setDynamicData(data->table_dyndata[name]);
|
2013-11-10 19:37:45 +01:00
|
|
|
}
|
2013-08-16 00:54:38 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
if (!str_initial_selection.empty() && str_initial_selection != "0")
|
|
|
|
e->setSelected(stoi(str_initial_selection));
|
2013-07-07 21:53:40 +02:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("textlist", name);
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
m_tables.emplace_back(spec, e);
|
2013-06-23 18:30:21 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 5) ||
|
|
|
|
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::string name = parts[2];
|
|
|
|
std::vector<std::string> items = split(parts[3],',');
|
2017-08-20 19:37:29 +02:00
|
|
|
std::string str_initial_selection;
|
2013-06-23 18:30:21 +02:00
|
|
|
str_initial_selection = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS("dropdown",0);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
|
|
|
v2s32 geom;
|
|
|
|
core::rect<s32> rect;
|
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (v_geom.size() == 1)
|
|
|
|
v_geom.emplace_back("1");
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
MY_CHECKGEOM("dropdown",1);
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
s32 width = stof(parts[1]) * spacing.Y;
|
|
|
|
|
|
|
|
rect = core::rect<s32>(pos.X, pos.Y,
|
|
|
|
pos.X + width, pos.Y + (m_btn_height * 2));
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
spec.ftype = f_DropDown;
|
|
|
|
spec.send = true;
|
|
|
|
|
|
|
|
//now really show list
|
2019-11-07 20:11:01 +01:00
|
|
|
gui::IGUIComboBox *e = Environment->addComboBox(rect, this, spec.fid);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const std::string &item : items) {
|
2017-01-31 18:05:03 +01:00
|
|
|
e->addItem(unescape_translate(unescape_string(
|
2017-08-20 19:37:29 +02:00
|
|
|
utf8_to_wide(item))).c_str());
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
if (!str_initial_selection.empty())
|
|
|
|
e->setSelected(stoi(str_initial_selection)-1);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("dropdown", name);
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
m_fields.push_back(spec);
|
2016-05-05 18:50:02 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
m_dropdowns.emplace_back(spec, std::vector<std::string>());
|
2016-05-05 18:50:02 +02:00
|
|
|
std::vector<std::string> &values = m_dropdowns.back().second;
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const std::string &item : items) {
|
|
|
|
values.push_back(unescape_string(item));
|
2016-05-05 18:50:02 +02:00
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream << "Invalid dropdown element(" << parts.size() << "): '"
|
|
|
|
<< element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element)
|
2016-10-03 01:30:33 +02:00
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
if (parts.size() == 2 ||
|
|
|
|
(parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION)) {
|
|
|
|
field_close_on_enter[parts[0]] = is_yes(parts[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2016-08-07 17:22:50 +02:00
|
|
|
if ((parts.size() == 4) || (parts.size() == 5) ||
|
|
|
|
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
2014-06-25 19:04:47 +02:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
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 label = parts[3];
|
|
|
|
|
|
|
|
MY_CHECKPOS("pwdfield",0);
|
|
|
|
MY_CHECKGEOM("pwdfield",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
pos -= padding;
|
|
|
|
|
|
|
|
geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
|
|
|
|
|
|
|
|
pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
|
|
|
|
pos.Y -= m_btn_height;
|
|
|
|
geom.Y = m_btn_height*2;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
|
2017-01-31 18:05:03 +01:00
|
|
|
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
spec.send = true;
|
|
|
|
gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
|
2013-08-19 11:26:51 +02:00
|
|
|
|
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2018-01-03 17:28:57 +01:00
|
|
|
if (label.length() >= 1) {
|
2015-01-09 18:01:59 +01:00
|
|
|
int font_height = g_fontengine->getTextHeight();
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
rect.UpperLeftCorner.Y -= font_height;
|
|
|
|
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
|
2018-01-03 17:28:57 +01:00
|
|
|
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
|
|
|
|
this, 0);
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
e->setPasswordBox(true,L'*');
|
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("pwdfield", name, "field");
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
|
|
|
|
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
irr::SEvent evt;
|
2013-08-07 01:36:19 +02:00
|
|
|
evt.EventType = EET_KEY_INPUT_EVENT;
|
|
|
|
evt.KeyInput.Key = KEY_END;
|
|
|
|
evt.KeyInput.Char = 0;
|
2017-08-20 19:37:29 +02:00
|
|
|
evt.KeyInput.Control = false;
|
|
|
|
evt.KeyInput.Shift = false;
|
2013-06-23 18:30:21 +02:00
|
|
|
evt.KeyInput.PressedDown = true;
|
|
|
|
e->OnEvent(evt);
|
2016-08-07 17:22:50 +02:00
|
|
|
|
2016-10-03 01:30:33 +02:00
|
|
|
if (parts.size() >= 5) {
|
|
|
|
// TODO: remove after 2016-11-03
|
|
|
|
warningstream << "pwdfield: use field_close_on_enter[name, enabled]" <<
|
|
|
|
" instead of the 5th param" << std::endl;
|
|
|
|
field_close_on_enter[name] = is_yes(parts[4]);
|
2016-08-07 17:22:50 +02:00
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2018-03-10 00:24:57 +01:00
|
|
|
void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
|
|
|
|
core::rect<s32> &rect, bool is_multiline)
|
|
|
|
{
|
|
|
|
bool is_editable = !spec.fname.empty();
|
|
|
|
if (!is_editable && !is_multiline) {
|
|
|
|
// spec field id to 0, this stops submit searching for a value that isn't there
|
|
|
|
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
|
2019-11-07 20:11:01 +01:00
|
|
|
this, spec.fid);
|
2018-03-10 00:24:57 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_editable) {
|
|
|
|
spec.send = true;
|
|
|
|
} else if (is_multiline &&
|
|
|
|
spec.fdefault.empty() && !spec.flabel.empty()) {
|
|
|
|
// Multiline textareas: swap default and label for backwards compat
|
|
|
|
spec.flabel.swap(spec.fdefault);
|
|
|
|
}
|
|
|
|
|
|
|
|
gui::IGUIEditBox *e = nullptr;
|
|
|
|
static constexpr bool use_intl_edit_box = USE_FREETYPE &&
|
|
|
|
IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9;
|
|
|
|
|
|
|
|
if (use_intl_edit_box && g_settings->getBool("freetype")) {
|
2019-11-07 20:11:01 +01:00
|
|
|
e = new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment,
|
|
|
|
this, spec.fid, rect, is_editable, is_multiline);
|
2018-03-10 00:24:57 +01:00
|
|
|
} else {
|
2019-09-24 19:05:28 +02:00
|
|
|
if (is_multiline) {
|
2018-03-10 00:24:57 +01:00
|
|
|
e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true,
|
2019-11-07 20:11:01 +01:00
|
|
|
Environment, this, spec.fid, rect, is_editable, true);
|
2019-09-24 19:05:28 +02:00
|
|
|
} else if (is_editable) {
|
2019-11-07 20:11:01 +01:00
|
|
|
e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this,
|
|
|
|
spec.fid);
|
|
|
|
e->grab();
|
2019-09-24 19:05:28 +02:00
|
|
|
}
|
2018-03-10 00:24:57 +01:00
|
|
|
}
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname);
|
|
|
|
|
2018-03-10 00:24:57 +01:00
|
|
|
if (e) {
|
2018-03-11 17:45:58 +01:00
|
|
|
if (is_editable && spec.fname == data->focused_fieldname)
|
2018-03-10 00:24:57 +01:00
|
|
|
Environment->setFocus(e);
|
|
|
|
|
|
|
|
if (is_multiline) {
|
|
|
|
e->setMultiLine(true);
|
|
|
|
e->setWordWrap(true);
|
|
|
|
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
|
|
|
|
} else {
|
|
|
|
irr::SEvent evt;
|
|
|
|
evt.EventType = EET_KEY_INPUT_EVENT;
|
|
|
|
evt.KeyInput.Key = KEY_END;
|
|
|
|
evt.KeyInput.Char = 0;
|
|
|
|
evt.KeyInput.Control = 0;
|
|
|
|
evt.KeyInput.Shift = 0;
|
|
|
|
evt.KeyInput.PressedDown = true;
|
|
|
|
e->OnEvent(evt);
|
|
|
|
}
|
2019-03-16 22:38:36 +01:00
|
|
|
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
|
|
|
|
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
|
|
|
|
if (style.get(StyleSpec::BGCOLOR, "") == "transparent") {
|
|
|
|
e->setDrawBackground(false);
|
|
|
|
}
|
2019-11-07 20:11:01 +01:00
|
|
|
|
|
|
|
e->drop();
|
2018-03-10 00:24:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!spec.flabel.empty()) {
|
|
|
|
int font_height = g_fontengine->getTextHeight();
|
|
|
|
rect.UpperLeftCorner.Y -= font_height;
|
|
|
|
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
|
2019-11-07 20:11:01 +01:00
|
|
|
IGUIElement *t = gui::StaticText::add(Environment, spec.flabel.c_str(),
|
|
|
|
rect, false, true, this, 0);
|
|
|
|
|
|
|
|
if (t)
|
|
|
|
t->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
2018-03-10 00:24:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
void GUIFormSpecMenu::parseSimpleField(parserData *data,
|
|
|
|
std::vector<std::string> &parts)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::string name = parts[0];
|
|
|
|
std::string label = parts[1];
|
|
|
|
std::string default_val = parts[2];
|
|
|
|
|
|
|
|
core::rect<s32> rect;
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
if (data->explicit_size)
|
|
|
|
warningstream << "invalid use of unpositioned \"field\" in inventory" << std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32 pos = getElementBasePos(nullptr);
|
|
|
|
pos.Y = (data->simple_field_count + 2) * 60;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 size = DesiredRect.getSize();
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
rect = core::rect<s32>(
|
|
|
|
size.X / 2 - 150, pos.Y,
|
|
|
|
size.X / 2 - 150 + 300, pos.Y + m_btn_height * 2
|
|
|
|
);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
if (m_form_src)
|
2013-06-23 18:30:21 +02:00
|
|
|
default_val = m_form_src->resolveText(default_val);
|
|
|
|
|
|
|
|
|
2017-01-31 18:05:03 +01:00
|
|
|
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2016-05-05 19:08:45 +02:00
|
|
|
utf8_to_wide(unescape_string(default_val)),
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
|
2018-03-10 00:24:57 +01:00
|
|
|
createTextField(data, spec, rect, false);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
2019-11-07 20:11:01 +01:00
|
|
|
|
|
|
|
data->simple_field_count++;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& parts,
|
|
|
|
const std::string &type)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
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 label = parts[3];
|
|
|
|
std::string default_val = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS(type,0);
|
|
|
|
MY_CHECKGEOM(type,1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
pos -= padding;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
|
|
|
|
|
|
|
|
if (type == "textarea")
|
|
|
|
{
|
|
|
|
geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
|
|
|
|
pos.Y += m_btn_height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
|
|
|
|
pos.Y -= m_btn_height;
|
|
|
|
geom.Y = m_btn_height*2;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
if(!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
if(m_form_src)
|
|
|
|
default_val = m_form_src->resolveText(default_val);
|
|
|
|
|
|
|
|
|
2017-01-31 18:05:03 +01:00
|
|
|
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2016-05-05 19:08:45 +02:00
|
|
|
utf8_to_wide(unescape_string(default_val)),
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
|
2018-03-10 00:24:57 +01:00
|
|
|
createTextField(data, spec, rect, type == "textarea");
|
2016-08-07 17:22:50 +02:00
|
|
|
|
2016-10-03 01:30:33 +02:00
|
|
|
if (parts.size() >= 6) {
|
|
|
|
// TODO: remove after 2016-11-03
|
|
|
|
warningstream << "field/textarea: use field_close_on_enter[name, enabled]" <<
|
|
|
|
" instead of the 6th param" << std::endl;
|
|
|
|
field_close_on_enter[name] = is_yes(parts[5]);
|
2016-08-07 17:22:50 +02:00
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseField(parserData* data, const std::string &element,
|
|
|
|
const std::string &type)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-08-14 20:21:39 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-06-19 00:22:03 +02:00
|
|
|
if (parts.size() == 3 || parts.size() == 4) {
|
2013-06-23 18:30:21 +02:00
|
|
|
parseSimpleField(data,parts);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-07 17:22:50 +02:00
|
|
|
if ((parts.size() == 5) || (parts.size() == 6) ||
|
|
|
|
((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
2014-06-25 19:04:47 +02:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
parseTextArea(data,parts,type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2019-09-10 15:11:26 +02:00
|
|
|
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) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-09-10 15:11:26 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-09-10 15:11:26 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 2) ||
|
|
|
|
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::string text = parts[1];
|
|
|
|
|
|
|
|
MY_CHECKPOS("label",0);
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
if(!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of label without a size[] element"<<std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
std::vector<std::string> lines = split(text, '\n');
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i != lines.size(); i++) {
|
2019-09-29 14:52:21 +02:00
|
|
|
std::wstring wlabel_colors = translate_string(
|
|
|
|
utf8_to_wide(unescape_string(lines[i])));
|
|
|
|
// Without color escapes to get the font dimensions
|
|
|
|
std::wstring wlabel_plain = unescape_enriched(wlabel_colors);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
core::rect<s32> rect;
|
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
|
|
|
// Lines are spaced at the distance of 1/2 imgsize.
|
|
|
|
// This alows lines that line up with the new elements
|
|
|
|
// easily without sacrificing good line distance. If
|
|
|
|
// it was one whole imgsize, it would have too much
|
|
|
|
// spacing.
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32 pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
// Labels are positioned by their center, not their top.
|
|
|
|
pos.Y += (((float) imgsize.Y) / -2) + (((float) imgsize.Y) * i / 2);
|
|
|
|
|
|
|
|
rect = core::rect<s32>(
|
|
|
|
pos.X, pos.Y,
|
2019-09-29 14:52:21 +02:00
|
|
|
pos.X + m_font->getDimension(wlabel_plain.c_str()).Width,
|
2019-06-27 14:40:49 +02:00
|
|
|
pos.Y + imgsize.Y);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Lines are spaced at the nominal distance of
|
|
|
|
// 2/5 inventory slot, even if the font doesn't
|
|
|
|
// quite match that. This provides consistent
|
|
|
|
// form layout, at the expense of sometimes
|
|
|
|
// having sub-optimal spacing for the font.
|
|
|
|
// We multiply by 2 and then divide by 5, rather
|
|
|
|
// than multiply by 0.4, to get exact results
|
|
|
|
// in the integer cases: 0.4 is not exactly
|
|
|
|
// representable in binary floating point.
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32 pos = getElementBasePos(nullptr);
|
2019-06-27 14:40:49 +02:00
|
|
|
pos.X += stof(v_pos[0]) * spacing.X;
|
|
|
|
pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y;
|
|
|
|
|
|
|
|
pos.Y += ((float) i) * spacing.Y * 2.0 / 5.0;
|
|
|
|
|
|
|
|
rect = core::rect<s32>(
|
|
|
|
pos.X, pos.Y - m_btn_height,
|
2019-09-29 14:52:21 +02:00
|
|
|
pos.X + m_font->getDimension(wlabel_plain.c_str()).Width,
|
2019-06-27 14:40:49 +02:00
|
|
|
pos.Y + m_btn_height);
|
|
|
|
}
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
"",
|
2019-09-29 14:52:21 +02:00
|
|
|
wlabel_colors,
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
L"",
|
2019-11-20 19:39:10 +01:00
|
|
|
258 + m_fields.size(),
|
|
|
|
4
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
);
|
2018-01-03 17:28:57 +01:00
|
|
|
gui::IGUIStaticText *e = gui::StaticText::add(Environment,
|
2019-11-07 20:11:01 +01:00
|
|
|
spec.flabel.c_str(), rect, false, false, this, spec.fid);
|
2018-01-03 17:28:57 +01:00
|
|
|
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
|
2019-03-16 22:38:36 +01:00
|
|
|
|
|
|
|
auto style = getStyleForElement("label", spec.fname);
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
}
|
2014-06-18 22:38:29 +02:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-06-27 14:40:49 +02:00
|
|
|
errorstream << "Invalid label element(" << parts.size() << "): '" << element
|
|
|
|
<< "'" << std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 2) ||
|
|
|
|
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
2017-01-31 18:05:03 +01:00
|
|
|
std::wstring text = unescape_translate(
|
2016-05-05 19:08:45 +02:00
|
|
|
unescape_string(utf8_to_wide(parts[1])));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
MY_CHECKPOS("vertlabel",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
|
|
|
core::rect<s32> rect;
|
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
// Vertlabels are positioned by center, not left.
|
|
|
|
pos.X -= imgsize.X / 2;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
// We use text.length + 1 because without it, the rect
|
|
|
|
// isn't quite tall enough and cuts off the text.
|
|
|
|
rect = core::rect<s32>(pos.X, pos.Y,
|
|
|
|
pos.X + imgsize.X,
|
|
|
|
pos.Y + font_line_height(m_font) *
|
|
|
|
(text.length() + 1));
|
|
|
|
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
// As above, the length must be one longer. The width of
|
|
|
|
// the rect (15 pixels) seems rather arbitrary, but
|
|
|
|
// changing it might break something.
|
|
|
|
rect = core::rect<s32>(
|
|
|
|
pos.X, pos.Y+((imgsize.Y/2) - m_btn_height),
|
2014-06-18 22:38:29 +02:00
|
|
|
pos.X+15, pos.Y +
|
2019-06-27 14:40:49 +02:00
|
|
|
font_line_height(m_font) *
|
|
|
|
(text.length() + 1) +
|
|
|
|
((imgsize.Y/2) - m_btn_height));
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
if(!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of label without a size[] element"<<std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
std::wstring label;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (wchar_t i : text) {
|
|
|
|
label += i;
|
2013-08-31 21:07:53 +02:00
|
|
|
label += L"\n";
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
"",
|
2013-08-31 21:07:53 +02:00
|
|
|
label,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
2019-03-16 22:38:36 +01:00
|
|
|
gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(),
|
2019-11-07 20:11:01 +01:00
|
|
|
rect, false, false, this, spec.fid);
|
2019-03-16 22:38:36 +01:00
|
|
|
e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
|
|
|
|
|
|
|
|
auto style = getStyleForElement("vertlabel", spec.fname, "label");
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &element,
|
|
|
|
const std::string &type)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) ||
|
|
|
|
((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
|
|
|
std::string image_name = parts[2];
|
|
|
|
std::string name = parts[3];
|
|
|
|
std::string label = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS("imagebutton",0);
|
|
|
|
MY_CHECKGEOM("imagebutton",1);
|
|
|
|
|
2014-06-19 00:22:03 +02:00
|
|
|
bool noclip = false;
|
2013-06-23 18:30:21 +02:00
|
|
|
bool drawborder = true;
|
2017-08-20 19:37:29 +02:00
|
|
|
std::string pressed_image_name;
|
2014-06-14 11:22:09 +02:00
|
|
|
|
2014-06-19 00:22:03 +02:00
|
|
|
if (parts.size() >= 7) {
|
2013-06-23 18:30:21 +02:00
|
|
|
if (parts[5] == "true")
|
|
|
|
noclip = true;
|
|
|
|
if (parts[6] == "false")
|
|
|
|
drawborder = false;
|
|
|
|
}
|
2014-06-14 11:22:09 +02:00
|
|
|
|
2014-06-19 00:22:03 +02:00
|
|
|
if (parts.size() >= 8) {
|
2013-07-18 21:20:37 +02:00
|
|
|
pressed_image_name = parts[7];
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
|
|
|
v2s32 geom;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
|
|
|
|
geom.Y = (stof(v_geom[1]) * spacing.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 (!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of image_button without a size[] element"<<std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-08-20 22:38:14 +02:00
|
|
|
image_name = unescape_string(image_name);
|
|
|
|
pressed_image_name = unescape_string(pressed_image_name);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2016-05-05 19:08:45 +02:00
|
|
|
std::wstring wlabel = utf8_to_wide(unescape_string(label));
|
2013-07-17 21:18:24 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-07-17 21:18:24 +02:00
|
|
|
wlabel,
|
2015-06-10 01:54:33 +02:00
|
|
|
utf8_to_wide(image_name),
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
spec.ftype = f_Button;
|
2019-06-27 14:40:49 +02:00
|
|
|
if (type == "image_button_exit")
|
2013-06-23 18:30:21 +02:00
|
|
|
spec.is_exit = true;
|
|
|
|
|
|
|
|
video::ITexture *texture = 0;
|
2013-07-18 21:20:37 +02:00
|
|
|
video::ITexture *pressed_texture = 0;
|
2013-08-20 22:38:14 +02:00
|
|
|
texture = m_tsrc->getTexture(image_name);
|
2017-08-20 19:37:29 +02:00
|
|
|
if (!pressed_image_name.empty())
|
2013-08-20 22:38:14 +02:00
|
|
|
pressed_texture = m_tsrc->getTexture(pressed_image_name);
|
|
|
|
else
|
2013-07-18 21:20:37 +02:00
|
|
|
pressed_texture = texture;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-10-12 18:44:23 +02:00
|
|
|
GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str());
|
2013-08-19 11:26:51 +02:00
|
|
|
|
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
|
|
|
}
|
2014-07-06 16:17:46 +02:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("image_button", spec.fname);
|
|
|
|
|
|
|
|
e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
|
2015-04-01 06:19:10 +02:00
|
|
|
e->setImage(guiScalingImageButton(
|
|
|
|
Environment->getVideoDriver(), texture, geom.X, geom.Y));
|
|
|
|
e->setPressedImage(guiScalingImageButton(
|
|
|
|
Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
|
2013-06-23 18:30:21 +02:00
|
|
|
e->setScaleImage(true);
|
2019-03-16 22:38:36 +01:00
|
|
|
if (parts.size() >= 7) {
|
|
|
|
e->setNotClipped(noclip);
|
|
|
|
e->setDrawBorder(drawborder);
|
|
|
|
} else {
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2019-06-27 14:40:49 +02:00
|
|
|
std::vector<std::string> parts = split(element, ';');
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (((parts.size() == 4) || (parts.size() == 6)) || (parts.size() == 7 &&
|
|
|
|
data->real_coordinates) || ((parts.size() > 6) &&
|
|
|
|
(m_formspec_version > FORMSPEC_API_VERSION)))
|
2014-06-25 19:04:47 +02:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
// If we're using real coordinates, add an extra field for height.
|
|
|
|
// Width is not here because tabs are the width of the text, and
|
|
|
|
// there's no reason to change that.
|
|
|
|
unsigned int i = 0;
|
|
|
|
std::vector<std::string> v_geom = {"1", "0.75"}; // Dummy width and default height
|
|
|
|
bool auto_width = true;
|
|
|
|
if (parts.size() == 7) {
|
|
|
|
i++;
|
|
|
|
|
|
|
|
v_geom = split(parts[1], ',');
|
|
|
|
if (v_geom.size() == 1)
|
|
|
|
v_geom.insert(v_geom.begin(), "1"); // Dummy value
|
|
|
|
else
|
|
|
|
auto_width = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string name = parts[i+1];
|
|
|
|
std::vector<std::string> buttons = split(parts[i+2], ',');
|
|
|
|
std::string str_index = parts[i+3];
|
2013-06-23 18:30:21 +02:00
|
|
|
bool show_background = true;
|
|
|
|
bool show_border = true;
|
2019-06-27 14:40:49 +02:00
|
|
|
int tab_index = stoi(str_index) - 1;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
MY_CHECKPOS("tabheader", 0);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (parts.size() == 6 + i) {
|
|
|
|
if (parts[4+i] == "true")
|
2013-06-23 18:30:21 +02:00
|
|
|
show_background = false;
|
2019-06-27 14:40:49 +02:00
|
|
|
if (parts[5+i] == "false")
|
2013-06-23 18:30:21 +02:00
|
|
|
show_border = false;
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-07 20:11:01 +01:00
|
|
|
258 + m_fields.size()
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
spec.ftype = f_TabHeader;
|
|
|
|
|
2018-08-04 18:55:54 +02:00
|
|
|
v2s32 pos;
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 geom;
|
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
pos.Y -= geom.Y; // TabHeader base pos is the bottom, not the top.
|
|
|
|
if (auto_width)
|
|
|
|
geom.X = DesiredRect.getWidth(); // Set automatic width
|
|
|
|
|
|
|
|
MY_CHECKGEOM("tabheader", 1);
|
|
|
|
} else {
|
2018-08-04 18:55:54 +02:00
|
|
|
v2f32 pos_f = pos_offset * spacing;
|
|
|
|
pos_f.X += stof(v_pos[0]) * spacing.X;
|
|
|
|
pos_f.Y += stof(v_pos[1]) * spacing.Y - m_btn_height * 2;
|
|
|
|
pos = v2s32(pos_f.X, pos_f.Y);
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
geom.Y = m_btn_height * 2;
|
|
|
|
geom.X = DesiredRect.getWidth();
|
2018-08-04 18:55:54 +02:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:07:48 +02:00
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
|
|
|
|
pos.Y+geom.Y);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:07:48 +02:00
|
|
|
gui::IGUITabControl *e = Environment->addTabControl(rect, this,
|
2019-03-16 22:38:36 +01:00
|
|
|
show_background, show_border, spec.fid);
|
2014-06-14 11:22:09 +02:00
|
|
|
e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
|
|
|
|
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
|
2019-06-27 14:40:49 +02:00
|
|
|
e->setTabHeight(geom.Y);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
|
|
|
}
|
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
auto style = getStyleForElement("tabheader", name);
|
|
|
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
|
2019-03-15 20:03:12 +01:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const std::string &button : buttons) {
|
2019-03-15 20:03:12 +01:00
|
|
|
auto tab = e->addTab(unescape_translate(unescape_string(
|
2017-08-20 19:37:29 +02:00
|
|
|
utf8_to_wide(button))).c_str(), -1);
|
2019-03-16 22:38:36 +01:00
|
|
|
if (style.isNotDefault(StyleSpec::BGCOLOR))
|
2019-03-15 20:03:12 +01:00
|
|
|
tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR));
|
|
|
|
|
|
|
|
tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((tab_index >= 0) &&
|
|
|
|
(buttons.size() < INT_MAX) &&
|
|
|
|
(tab_index < (int) buttons.size()))
|
|
|
|
e->setActiveTab(tab_index);
|
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
errorstream << "Invalid TabHeader element(" << parts.size() << "): '"
|
|
|
|
<< element << "'" << std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-01-09 20:39:22 +01:00
|
|
|
if (m_client == 0) {
|
|
|
|
warningstream << "invalid use of item_image_button with m_client==0"
|
2015-10-14 07:26:03 +02:00
|
|
|
<< std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 5) ||
|
|
|
|
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
|
|
|
std::string item_name = parts[2];
|
|
|
|
std::string name = parts[3];
|
|
|
|
std::string label = parts[4];
|
|
|
|
|
2016-05-05 19:08:45 +02:00
|
|
|
label = unescape_string(label);
|
|
|
|
item_name = unescape_string(item_name);
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
MY_CHECKPOS("itemimagebutton",0);
|
|
|
|
MY_CHECKGEOM("itemimagebutton",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
|
|
|
|
geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
if(!data->explicit_size)
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"invalid use of item_image_button without a size[] element"<<std::endl;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-01-09 20:39:22 +01:00
|
|
|
IItemDefManager *idef = m_client->idef();
|
2013-06-23 18:30:21 +02:00
|
|
|
ItemStack item;
|
|
|
|
item.deSerialize(item_name, idef);
|
2014-06-24 12:28:24 +02:00
|
|
|
|
2015-06-10 01:54:33 +02:00
|
|
|
m_tooltips[name] =
|
2017-01-31 18:05:03 +01:00
|
|
|
TooltipSpec(utf8_to_wide(item.getDefinition(idef).description),
|
2014-06-24 12:28:24 +02:00
|
|
|
m_default_tooltip_bgcolor,
|
|
|
|
m_default_tooltip_color);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
// the spec for the button
|
|
|
|
FieldSpec spec_btn(
|
2015-06-10 01:54:33 +02:00
|
|
|
name,
|
|
|
|
utf8_to_wide(label),
|
|
|
|
utf8_to_wide(item_name),
|
2019-11-20 19:39:10 +01:00
|
|
|
258 + m_fields.size(),
|
|
|
|
2
|
2013-06-23 18:30:21 +02:00
|
|
|
);
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
gui::IGUIButton *e_btn = GUIButton::addButton(Environment, rect, this, spec_btn.fid, L"");
|
2013-08-19 11:26:51 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button");
|
|
|
|
e_btn->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
e_btn->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
|
2019-03-16 22:38:36 +01:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
if (spec_btn.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e_btn);
|
2013-08-19 11:26:51 +02:00
|
|
|
}
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
spec_btn.ftype = f_Button;
|
|
|
|
rect += data->basepos-padding;
|
|
|
|
spec_btn.rect = rect;
|
|
|
|
m_fields.push_back(spec_btn);
|
2018-08-04 18:55:54 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
// the spec for the item-image
|
|
|
|
FieldSpec spec_img(
|
2019-11-20 19:39:10 +01:00
|
|
|
name,
|
2019-11-07 20:11:01 +01:00
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-20 19:39:10 +01:00
|
|
|
258 + m_fields.size(),
|
|
|
|
2
|
2019-11-07 20:11:01 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
GUIItemImage *e_img = new GUIItemImage(Environment, e_btn, spec_img.fid,
|
|
|
|
core::rect<s32>(0, 0, geom.X, geom.Y), item_name, m_font, m_client);
|
|
|
|
|
|
|
|
e_img->setText(utf8_to_wide(label).c_str());
|
2019-06-27 14:40:49 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
e_img->drop();
|
|
|
|
|
|
|
|
m_fields.push_back(spec_img);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if ((parts.size() == 3) ||
|
|
|
|
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1],',');
|
|
|
|
|
|
|
|
MY_CHECKPOS("box",0);
|
|
|
|
MY_CHECKGEOM("box",1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = stof(v_geom[0]) * spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * spacing.Y;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
video::SColor tmp_color;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2018-04-23 19:50:50 +02:00
|
|
|
if (parseColorString(parts[2], tmp_color, false, 0x8C)) {
|
2019-11-07 20:11:01 +01:00
|
|
|
FieldSpec spec(
|
|
|
|
"",
|
|
|
|
L"",
|
|
|
|
L"",
|
2019-11-20 19:39:10 +01:00
|
|
|
258 + m_fields.size(),
|
|
|
|
-2
|
2019-11-07 20:11:01 +01:00
|
|
|
);
|
|
|
|
spec.ftype = f_Box;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
core::rect<s32> rect(pos, pos + geom);
|
|
|
|
|
|
|
|
GUIBox *e = new GUIBox(Environment, this, spec.fid, rect, tmp_color);
|
|
|
|
|
|
|
|
e->setNotClipped(true);
|
|
|
|
|
|
|
|
e->drop();
|
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
|
|
|
|
} else {
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2019-10-06 14:50:45 +02:00
|
|
|
void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-11-02 04:26:44 +01:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if (((parts.size() == 1) || (parts.size() == 2)) ||
|
2017-08-29 19:25:16 +02:00
|
|
|
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) {
|
2019-10-06 14:50:45 +02:00
|
|
|
parseColorString(parts[0], m_bgcolor, false);
|
|
|
|
|
|
|
|
if (parts.size() == 2) {
|
|
|
|
std::string fullscreen = parts[1];
|
|
|
|
m_bgfullscreen = is_yes(fullscreen);
|
2013-11-02 04:26:44 +01:00
|
|
|
}
|
2017-08-29 19:25:16 +02:00
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-08-29 19:25:16 +02:00
|
|
|
|
|
|
|
errorstream << "Invalid bgcolor element(" << parts.size() << "): '" << element << "'"
|
|
|
|
<< std::endl;
|
2013-11-02 04:26:44 +01:00
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-11-02 04:26:44 +01:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
|
|
|
|
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2014-10-03 06:11:21 +02:00
|
|
|
parseColorString(parts[0], m_slotbg_n, false);
|
|
|
|
parseColorString(parts[1], m_slotbg_h, false);
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
if (parts.size() >= 3) {
|
2014-10-03 06:11:21 +02:00
|
|
|
if (parseColorString(parts[2], m_slotbordercolor, false)) {
|
2013-11-02 04:26:44 +01:00
|
|
|
m_slotborder = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (parts.size() == 5) {
|
|
|
|
video::SColor tmp_color;
|
|
|
|
|
2014-10-03 06:11:21 +02:00
|
|
|
if (parseColorString(parts[3], tmp_color, false))
|
2014-06-24 12:28:24 +02:00
|
|
|
m_default_tooltip_bgcolor = tmp_color;
|
2014-10-03 06:11:21 +02:00
|
|
|
if (parseColorString(parts[4], tmp_color, false))
|
2014-06-24 12:28:24 +02:00
|
|
|
m_default_tooltip_color = tmp_color;
|
2013-11-02 04:26:44 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
|
2014-06-24 12:28:24 +02:00
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element,';');
|
2018-06-20 22:55:39 +02:00
|
|
|
if (parts.size() < 2) {
|
|
|
|
errorstream << "Invalid tooltip element(" << parts.size() << "): '"
|
|
|
|
<< element << "'" << std::endl;
|
2014-06-24 12:28:24 +02:00
|
|
|
return;
|
2017-08-20 19:37:29 +02:00
|
|
|
}
|
|
|
|
|
2018-06-20 22:55:39 +02:00
|
|
|
// Get mode and check size
|
|
|
|
bool rect_mode = parts[0].find(',') != std::string::npos;
|
|
|
|
size_t base_size = rect_mode ? 3 : 2;
|
|
|
|
if (parts.size() != base_size && parts.size() != base_size + 2) {
|
|
|
|
errorstream << "Invalid tooltip element(" << parts.size() << "): '"
|
|
|
|
<< element << "'" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read colors
|
|
|
|
video::SColor bgcolor = m_default_tooltip_bgcolor;
|
|
|
|
video::SColor color = m_default_tooltip_color;
|
|
|
|
if (parts.size() == base_size + 2 &&
|
|
|
|
(!parseColorString(parts[base_size], bgcolor, false) ||
|
|
|
|
!parseColorString(parts[base_size + 1], color, false))) {
|
|
|
|
errorstream << "Invalid color in tooltip element(" << parts.size()
|
|
|
|
<< "): '" << element << "'" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make tooltip spec
|
|
|
|
std::string text = unescape_string(parts[rect_mode ? 2 : 1]);
|
|
|
|
TooltipSpec spec(utf8_to_wide(text), bgcolor, color);
|
|
|
|
|
|
|
|
// Add tooltip
|
|
|
|
if (rect_mode) {
|
|
|
|
std::vector<std::string> v_pos = split(parts[0], ',');
|
|
|
|
std::vector<std::string> v_geom = split(parts[1], ',');
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
MY_CHECKPOS("tooltip", 0);
|
2018-06-20 22:55:39 +02:00
|
|
|
MY_CHECKGEOM("tooltip", 1);
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
v2s32 pos;
|
2018-06-20 22:55:39 +02:00
|
|
|
v2s32 geom;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
if (data->real_coordinates) {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getRealCoordinateBasePos(v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom = getRealCoordinateGeometry(v_geom);
|
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
pos = getElementBasePos(&v_pos);
|
2019-06-27 14:40:49 +02:00
|
|
|
geom.X = stof(v_geom[0]) * spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * spacing.Y;
|
|
|
|
}
|
2018-06-20 22:55:39 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
FieldSpec fieldspec(
|
|
|
|
"",
|
|
|
|
L"",
|
|
|
|
L"",
|
|
|
|
258 + m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
core::rect<s32> rect(pos, pos + geom);
|
|
|
|
|
|
|
|
gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment,
|
|
|
|
this, fieldspec.fid, rect);
|
|
|
|
|
|
|
|
m_fields.push_back(fieldspec);
|
|
|
|
m_tooltip_rects.emplace_back(e, spec);
|
|
|
|
|
2018-06-20 22:55:39 +02:00
|
|
|
} else {
|
|
|
|
m_tooltips[parts[0]] = spec;
|
2014-06-24 12:28:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
bool GUIFormSpecMenu::parseVersionDirect(const std::string &data)
|
2014-06-25 19:04:47 +02:00
|
|
|
{
|
|
|
|
//some prechecks
|
2017-08-20 19:37:29 +02:00
|
|
|
if (data.empty())
|
2014-06-25 19:04:47 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(data,'[');
|
|
|
|
|
|
|
|
if (parts.size() < 2) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parts[0] != "formspec_version") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_number(parts[1])) {
|
|
|
|
m_formspec_version = mystoi(parts[1]);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-20 00:12:52 +02:00
|
|
|
bool GUIFormSpecMenu::parseSizeDirect(parserData* data, const std::string &element)
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
{
|
2017-08-20 19:37:29 +02:00
|
|
|
if (element.empty())
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(element,'[');
|
|
|
|
|
|
|
|
if (parts.size() < 2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::string type = trim(parts[0]);
|
|
|
|
std::string description = trim(parts[1]);
|
|
|
|
|
|
|
|
if (type != "size" && type != "invsize")
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (type == "invsize")
|
|
|
|
log_deprecated("Deprecated formspec element \"invsize\" is used");
|
|
|
|
|
|
|
|
parseSize(data, description);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-04 10:46:55 +01:00
|
|
|
bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &element)
|
|
|
|
{
|
|
|
|
if (element.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(element, '[');
|
|
|
|
|
|
|
|
if (parts.size() != 2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::string type = trim(parts[0]);
|
|
|
|
std::string description = trim(parts[1]);
|
|
|
|
|
|
|
|
if (type != "position")
|
|
|
|
return false;
|
|
|
|
|
|
|
|
parsePosition(data, description);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element)
|
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element, ',');
|
|
|
|
|
|
|
|
if (parts.size() == 2) {
|
|
|
|
data->offset.X = stof(parts[0]);
|
|
|
|
data->offset.Y = stof(parts[1]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
errorstream << "Invalid position element (" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &element)
|
|
|
|
{
|
|
|
|
if (element.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(element, '[');
|
|
|
|
|
|
|
|
if (parts.size() != 2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::string type = trim(parts[0]);
|
|
|
|
std::string description = trim(parts[1]);
|
|
|
|
|
|
|
|
if (type != "anchor")
|
|
|
|
return false;
|
|
|
|
|
|
|
|
parseAnchor(data, description);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
|
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element, ',');
|
|
|
|
|
|
|
|
if (parts.size() == 2) {
|
|
|
|
data->anchor.X = stof(parts[0]);
|
|
|
|
data->anchor.Y = stof(parts[1]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-21 23:40:48 +02:00
|
|
|
errorstream << "Invalid anchor element (" << parts.size() << "): '" << element
|
|
|
|
<< "'" << std::endl;
|
2017-03-04 10:46:55 +01:00
|
|
|
}
|
|
|
|
|
2019-03-15 20:03:12 +01:00
|
|
|
bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, bool style_type)
|
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element, ';');
|
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
if (parts.size() < 2) {
|
2019-03-15 20:03:12 +01:00
|
|
|
errorstream << "Invalid style element (" << parts.size() << "): '" << element
|
|
|
|
<< "'" << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string selector = trim(parts[0]);
|
2019-03-16 22:38:36 +01:00
|
|
|
if (selector.empty()) {
|
|
|
|
errorstream << "Invalid style element (Selector required): '" << element
|
2019-03-15 20:03:12 +01:00
|
|
|
<< "'" << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
StyleSpec spec;
|
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
for (size_t i = 1; i < parts.size(); i++) {
|
|
|
|
size_t equal_pos = parts[i].find('=');
|
|
|
|
if (equal_pos == std::string::npos) {
|
|
|
|
errorstream << "Invalid style element (Property missing value): '" << element
|
|
|
|
<< "'" << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string propname = trim(parts[i].substr(0, equal_pos));
|
|
|
|
std::string value = trim(unescape_string(parts[i].substr(equal_pos + 1)));
|
|
|
|
|
|
|
|
std::transform(propname.begin(), propname.end(), propname.begin(), ::tolower);
|
|
|
|
|
|
|
|
StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname);
|
|
|
|
if (prop == StyleSpec::NONE) {
|
|
|
|
if (property_warned.find(propname) != property_warned.end()) {
|
|
|
|
warningstream << "Invalid style element (Unknown property " << propname << "): '"
|
|
|
|
<< element
|
|
|
|
<< "'" << std::endl;
|
|
|
|
property_warned.insert(propname);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
spec.set(prop, value);
|
2019-03-15 20:03:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (style_type) {
|
|
|
|
theme_by_type[selector] |= spec;
|
|
|
|
} else {
|
|
|
|
theme_by_name[selector] |= spec;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-21 23:40:48 +02:00
|
|
|
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
//some prechecks
|
2017-08-20 19:37:29 +02:00
|
|
|
if (element.empty())
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
|
2019-09-10 21:18:42 +02:00
|
|
|
if (parseVersionDirect(element))
|
|
|
|
return;
|
|
|
|
|
2013-08-14 20:21:39 +02:00
|
|
|
std::vector<std::string> parts = split(element,'[');
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-07-07 21:53:40 +02:00
|
|
|
// ugly workaround to keep compatibility
|
|
|
|
if (parts.size() > 2) {
|
|
|
|
if (trim(parts[0]) == "image") {
|
|
|
|
for (unsigned int i=2;i< parts.size(); i++) {
|
|
|
|
parts[1] += "[" + parts[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { return; }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parts.size() < 2) {
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
2013-07-07 21:53:40 +02:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
std::string type = trim(parts[0]);
|
|
|
|
std::string description = trim(parts[1]);
|
|
|
|
|
2016-07-07 22:10:15 +02:00
|
|
|
if (type == "container") {
|
|
|
|
parseContainer(data, description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "container_end") {
|
|
|
|
parseContainerEnd(data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
if (type == "list") {
|
2016-07-07 22:10:15 +02:00
|
|
|
parseList(data, description);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-16 10:48:54 +02:00
|
|
|
if (type == "listring") {
|
|
|
|
parseListRing(data, description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
if (type == "checkbox") {
|
2016-07-07 22:10:15 +02:00
|
|
|
parseCheckbox(data, description);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "image") {
|
2016-07-07 22:10:15 +02:00
|
|
|
parseImage(data, description);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "item_image") {
|
2016-07-07 22:10:15 +02:00
|
|
|
parseItemImage(data, description);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-07 22:10:15 +02:00
|
|
|
if (type == "button" || type == "button_exit") {
|
|
|
|
parseButton(data, description, type);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-29 13:40:59 +02:00
|
|
|
if (type == "background" || type == "background9") {
|
|
|
|
parseBackground(data, description);
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
if (type == "tableoptions"){
|
|
|
|
parseTableOptions(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "tablecolumns"){
|
|
|
|
parseTableColumns(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "table"){
|
|
|
|
parseTable(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
if (type == "textlist"){
|
|
|
|
parseTextList(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "dropdown"){
|
|
|
|
parseDropDown(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-03 01:30:33 +02:00
|
|
|
if (type == "field_close_on_enter") {
|
|
|
|
parseFieldCloseOnEnter(data, description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
if (type == "pwdfield") {
|
|
|
|
parsePwdField(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((type == "field") || (type == "textarea")){
|
|
|
|
parseField(data,description,type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-10 15:11:26 +02:00
|
|
|
if (type == "hypertext") {
|
|
|
|
parseHyperText(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
if (type == "label") {
|
|
|
|
parseLabel(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "vertlabel") {
|
|
|
|
parseVertLabel(data,description);
|
|
|
|
return;
|
|
|
|
}
|
2010-12-22 02:34:21 +01:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
if (type == "item_image_button") {
|
|
|
|
parseItemImageButton(data,description);
|
|
|
|
return;
|
2011-04-04 14:13:19 +02:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
if ((type == "image_button") || (type == "image_button_exit")) {
|
|
|
|
parseImageButton(data,description,type);
|
|
|
|
return;
|
2011-04-04 23:24:47 +02:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
if (type == "tabheader") {
|
|
|
|
parseTabHeader(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "box") {
|
|
|
|
parseBox(data,description);
|
|
|
|
return;
|
2012-01-13 12:35:55 +01:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
if (type == "bgcolor") {
|
|
|
|
parseBackgroundColor(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "listcolors") {
|
|
|
|
parseListColors(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-24 12:28:24 +02:00
|
|
|
if (type == "tooltip") {
|
|
|
|
parseTooltip(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-19 18:17:35 +02:00
|
|
|
if (type == "scrollbar") {
|
|
|
|
parseScrollBar(data, description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (type == "real_coordinates") {
|
|
|
|
data->real_coordinates = is_yes(description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-15 20:03:12 +01:00
|
|
|
if (type == "style") {
|
|
|
|
parseStyle(data, description, false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "style_type") {
|
|
|
|
parseStyle(data, description, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
// Ignore others
|
2017-08-29 19:25:16 +02:00
|
|
|
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
|
|
|
|
<< std::endl;
|
2010-12-25 15:04:51 +01:00
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
2010-12-22 02:34:21 +01:00
|
|
|
{
|
2014-06-14 11:22:09 +02:00
|
|
|
/* useless to regenerate without a screensize */
|
|
|
|
if ((screensize.X <= 0) || (screensize.Y <= 0)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
parserData mydata;
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
//preserve tables
|
2017-08-20 19:37:29 +02:00
|
|
|
for (auto &m_table : m_tables) {
|
|
|
|
std::string tablename = m_table.first.fname;
|
|
|
|
GUITable *table = m_table.second;
|
2013-08-23 12:24:11 +02:00
|
|
|
mydata.table_dyndata[tablename] = table->getDynamicData();
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2015-01-27 01:17:04 +01:00
|
|
|
//set focus
|
|
|
|
if (!m_focused_element.empty())
|
|
|
|
mydata.focused_fieldname = m_focused_element;
|
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
//preserve focus
|
|
|
|
gui::IGUIElement *focused_element = Environment->getFocus();
|
|
|
|
if (focused_element && focused_element->getParent() == this) {
|
|
|
|
s32 focused_id = focused_element->getID();
|
|
|
|
if (focused_id > 257) {
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
|
|
|
|
if (field.fid == focused_id) {
|
|
|
|
mydata.focused_fieldname = field.fname;
|
2013-08-19 11:26:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-25 15:04:51 +01:00
|
|
|
// Remove children
|
|
|
|
removeChildren();
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (auto &table_it : m_tables) {
|
|
|
|
table_it.second->drop();
|
2014-05-30 03:04:10 +02:00
|
|
|
}
|
2019-11-07 20:11:01 +01:00
|
|
|
for (auto &inventorylist_it : m_inventorylists) {
|
|
|
|
inventorylist_it.e->drop();
|
|
|
|
}
|
|
|
|
for (auto &checkbox_it : m_checkboxes) {
|
|
|
|
checkbox_it.second->drop();
|
|
|
|
}
|
|
|
|
for (auto &scrollbar_it : m_scrollbars) {
|
|
|
|
scrollbar_it.second->drop();
|
|
|
|
}
|
|
|
|
for (auto &background_it : m_backgrounds) {
|
|
|
|
background_it->drop();
|
|
|
|
}
|
|
|
|
for (auto &tooltip_rect_it : m_tooltip_rects) {
|
|
|
|
tooltip_rect_it.first->drop();
|
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
mydata.size= v2s32(100,100);
|
|
|
|
mydata.screensize = screensize;
|
2017-03-04 10:46:55 +01:00
|
|
|
mydata.offset = v2f32(0.5f, 0.5f);
|
|
|
|
mydata.anchor = v2f32(0.5f, 0.5f);
|
2019-11-07 20:11:01 +01:00
|
|
|
mydata.simple_field_count = 0;
|
2012-07-15 18:19:38 +02:00
|
|
|
|
|
|
|
// Base position of contents of form
|
2013-06-23 18:30:21 +02:00
|
|
|
mydata.basepos = getBasePos();
|
|
|
|
|
2012-06-03 16:18:08 +02:00
|
|
|
/* Convert m_init_draw_spec to m_inventorylists */
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2012-06-03 16:18:08 +02:00
|
|
|
m_inventorylists.clear();
|
2012-11-08 19:22:37 +01:00
|
|
|
m_backgrounds.clear();
|
2013-08-23 12:24:11 +02:00
|
|
|
m_tables.clear();
|
2013-06-23 18:30:21 +02:00
|
|
|
m_checkboxes.clear();
|
2014-06-19 18:17:35 +02:00
|
|
|
m_scrollbars.clear();
|
2012-07-15 18:19:38 +02:00
|
|
|
m_fields.clear();
|
2014-06-24 12:28:24 +02:00
|
|
|
m_tooltips.clear();
|
2018-06-20 22:55:39 +02:00
|
|
|
m_tooltip_rects.clear();
|
2015-10-10 17:07:53 +02:00
|
|
|
m_inventory_rings.clear();
|
2017-06-01 08:00:26 +02:00
|
|
|
m_dropdowns.clear();
|
2019-03-15 20:03:12 +01:00
|
|
|
theme_by_name.clear();
|
|
|
|
theme_by_type.clear();
|
2014-07-06 16:17:46 +02:00
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
m_bgfullscreen = false;
|
|
|
|
|
2019-10-20 12:44:52 +02:00
|
|
|
m_formspec_version = 1;
|
|
|
|
|
2017-08-29 19:25:16 +02:00
|
|
|
{
|
|
|
|
v3f formspec_bgcolor = g_settings->getV3F("formspec_default_bg_color");
|
|
|
|
m_bgcolor = video::SColor(
|
|
|
|
(u8) clamp_u8(g_settings->getS32("formspec_default_bg_opacity")),
|
|
|
|
clamp_u8(myround(formspec_bgcolor.X)),
|
|
|
|
clamp_u8(myround(formspec_bgcolor.Y)),
|
|
|
|
clamp_u8(myround(formspec_bgcolor.Z))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v3f formspec_bgcolor = g_settings->getV3F("formspec_fullscreen_bg_color");
|
|
|
|
m_fullscreen_bgcolor = video::SColor(
|
|
|
|
(u8) clamp_u8(g_settings->getS32("formspec_fullscreen_bg_opacity")),
|
|
|
|
clamp_u8(myround(formspec_bgcolor.X)),
|
|
|
|
clamp_u8(myround(formspec_bgcolor.Y)),
|
|
|
|
clamp_u8(myround(formspec_bgcolor.Z))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
m_slotbg_n = video::SColor(255,128,128,128);
|
|
|
|
m_slotbg_h = video::SColor(255,192,192,192);
|
|
|
|
|
2014-06-24 12:28:24 +02:00
|
|
|
m_default_tooltip_bgcolor = video::SColor(255,110,130,60);
|
|
|
|
m_default_tooltip_color = video::SColor(255,255,255,255);
|
2014-07-06 16:17:46 +02:00
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
m_slotbordercolor = video::SColor(200,0,0,0);
|
|
|
|
m_slotborder = false;
|
|
|
|
|
|
|
|
// Add tooltip
|
|
|
|
{
|
2017-06-17 19:11:28 +02:00
|
|
|
assert(!m_tooltip_element);
|
2013-11-02 04:26:44 +01:00
|
|
|
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle
|
2018-01-03 17:28:57 +01:00
|
|
|
m_tooltip_element = gui::StaticText::add(Environment, L"",
|
2018-01-05 19:39:06 +01:00
|
|
|
core::rect<s32>(0, 0, 110, 18));
|
2013-11-02 04:26:44 +01:00
|
|
|
m_tooltip_element->enableOverrideColor(true);
|
2014-06-24 12:28:24 +02:00
|
|
|
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
|
2013-11-02 04:26:44 +01:00
|
|
|
m_tooltip_element->setDrawBackground(true);
|
|
|
|
m_tooltip_element->setDrawBorder(true);
|
2014-06-24 12:28:24 +02:00
|
|
|
m_tooltip_element->setOverrideColor(m_default_tooltip_color);
|
2013-11-02 04:26:44 +01:00
|
|
|
m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
|
|
|
|
m_tooltip_element->setWordWrap(false);
|
|
|
|
//we're not parent so no autograb for this one!
|
|
|
|
m_tooltip_element->grab();
|
|
|
|
}
|
|
|
|
|
2013-08-14 20:21:39 +02:00
|
|
|
std::vector<std::string> elements = split(m_formspec_string,']');
|
2014-06-25 19:04:47 +02:00
|
|
|
unsigned int i = 0;
|
|
|
|
|
|
|
|
/* try to read version from first element only */
|
2017-08-20 19:37:29 +02:00
|
|
|
if (!elements.empty()) {
|
2018-03-28 17:04:41 +02:00
|
|
|
if (parseVersionDirect(elements[0])) {
|
2014-06-25 19:04:47 +02:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
/* we need size first in order to calculate image scale */
|
|
|
|
mydata.explicit_size = false;
|
|
|
|
for (; i< elements.size(); i++) {
|
|
|
|
if (!parseSizeDirect(&mydata, elements[i])) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-04 10:46:55 +01:00
|
|
|
/* "position" element is always after "size" element if it used */
|
|
|
|
for (; i< elements.size(); i++) {
|
|
|
|
if (!parsePositionDirect(&mydata, elements[i])) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "anchor" element is always after "position" (or "size" element) if it used */
|
|
|
|
for (; i< elements.size(); i++) {
|
|
|
|
if (!parseAnchorDirect(&mydata, elements[i])) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-28 17:04:41 +02:00
|
|
|
/* "no_prepend" element is always after "position" (or "size" element) if it used */
|
|
|
|
bool enable_prepends = true;
|
|
|
|
for (; i < elements.size(); i++) {
|
|
|
|
if (elements[i].empty())
|
|
|
|
break;
|
|
|
|
|
|
|
|
std::vector<std::string> parts = split(elements[i], '[');
|
2018-08-15 21:06:09 +02:00
|
|
|
if (trim(parts[0]) == "no_prepend")
|
2018-03-28 17:04:41 +02:00
|
|
|
enable_prepends = false;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
2017-03-04 10:46:55 +01:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
/* Copy of the "real_coordinates" element for after the form size. */
|
2019-09-10 21:18:42 +02:00
|
|
|
mydata.real_coordinates = m_formspec_version >= 2;
|
2019-06-27 14:40:49 +02:00
|
|
|
for (; i < elements.size(); i++) {
|
|
|
|
std::vector<std::string> parts = split(elements[i], '[');
|
|
|
|
std::string name = trim(parts[0]);
|
|
|
|
if (name != "real_coordinates" || parts.size() != 2)
|
|
|
|
break; // Invalid format
|
|
|
|
|
|
|
|
mydata.real_coordinates = is_yes(trim(parts[1]));
|
|
|
|
}
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
if (mydata.explicit_size) {
|
|
|
|
// compute scaling for specified form size
|
|
|
|
if (m_lock) {
|
2017-06-26 20:11:17 +02:00
|
|
|
v2u32 current_screensize = RenderingEngine::get_video_driver()->getScreenSize();
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
v2u32 delta = current_screensize - m_lockscreensize;
|
|
|
|
|
|
|
|
if (current_screensize.Y > m_lockscreensize.Y)
|
|
|
|
delta.Y /= 2;
|
|
|
|
else
|
|
|
|
delta.Y = 0;
|
|
|
|
|
|
|
|
if (current_screensize.X > m_lockscreensize.X)
|
|
|
|
delta.X /= 2;
|
|
|
|
else
|
|
|
|
delta.X = 0;
|
|
|
|
|
|
|
|
offset = v2s32(delta.X,delta.Y);
|
|
|
|
|
|
|
|
mydata.screensize = m_lockscreensize;
|
|
|
|
} else {
|
|
|
|
offset = v2s32(0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
double gui_scaling = g_settings->getFloat("gui_scaling");
|
2017-06-26 20:11:17 +02:00
|
|
|
double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
|
|
|
|
double use_imgsize;
|
|
|
|
if (m_lock) {
|
|
|
|
// In fixed-size mode, inventory image size
|
|
|
|
// is 0.53 inch multiplied by the gui_scaling
|
|
|
|
// config parameter. This magic size is chosen
|
|
|
|
// to make the main menu (15.5 inventory images
|
|
|
|
// wide, including border) just fit into the
|
|
|
|
// default window (800 pixels wide) at 96 DPI
|
|
|
|
// and default scaling (1.00).
|
2015-01-02 16:52:02 +01:00
|
|
|
use_imgsize = 0.5555 * screen_dpi * gui_scaling;
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
} else {
|
|
|
|
// In variable-size mode, we prefer to make the
|
|
|
|
// inventory image size 1/15 of screen height,
|
|
|
|
// multiplied by the gui_scaling config parameter.
|
|
|
|
// If the preferred size won't fit the whole
|
|
|
|
// form on the screen, either horizontally or
|
|
|
|
// vertically, then we scale it down to fit.
|
|
|
|
// (The magic numbers in the computation of what
|
|
|
|
// fits arise from the scaling factors in the
|
|
|
|
// following stanza, including the form border,
|
|
|
|
// help text space, and 0.1 inventory slot spare.)
|
|
|
|
// However, a minimum size is also set, that
|
|
|
|
// the image size can't be less than 0.3 inch
|
|
|
|
// multiplied by gui_scaling, even if this means
|
|
|
|
// the form doesn't fit the screen.
|
2018-11-18 11:31:19 +01:00
|
|
|
#ifdef __ANDROID__
|
|
|
|
// For mobile devices these magic numbers are
|
|
|
|
// different and forms should always use the
|
|
|
|
// maximum screen space available.
|
|
|
|
double prefer_imgsize = mydata.screensize.Y / 10 * gui_scaling;
|
|
|
|
double fitx_imgsize = mydata.screensize.X /
|
|
|
|
((12.0 / 8.0) * (0.5 + mydata.invsize.X));
|
|
|
|
double fity_imgsize = mydata.screensize.Y /
|
|
|
|
((15.0 / 11.0) * (0.85 + mydata.invsize.Y));
|
|
|
|
use_imgsize = MYMIN(prefer_imgsize,
|
|
|
|
MYMIN(fitx_imgsize, fity_imgsize));
|
|
|
|
#else
|
|
|
|
double prefer_imgsize = mydata.screensize.Y / 15 * gui_scaling;
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
double fitx_imgsize = mydata.screensize.X /
|
2018-11-18 11:31:19 +01:00
|
|
|
((5.0 / 4.0) * (0.5 + mydata.invsize.X));
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
double fity_imgsize = mydata.screensize.Y /
|
2018-11-18 11:31:19 +01:00
|
|
|
((15.0 / 13.0) * (0.85 * mydata.invsize.Y));
|
2017-06-26 20:11:17 +02:00
|
|
|
double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
double min_imgsize = 0.3 * screen_dpi * gui_scaling;
|
|
|
|
use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
|
|
|
|
MYMIN(fitx_imgsize, fity_imgsize)));
|
2018-11-18 11:31:19 +01:00
|
|
|
#endif
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Everything else is scaled in proportion to the
|
|
|
|
// inventory image size. The inventory slot spacing
|
|
|
|
// is 5/4 image size horizontally and 15/13 image size
|
|
|
|
// vertically. The padding around the form (incorporating
|
|
|
|
// the border of the outer inventory slots) is 3/8
|
|
|
|
// image size. Font height (baseline to baseline)
|
|
|
|
// is 2/5 vertical inventory slot spacing, and button
|
|
|
|
// half-height is 7/8 of font height.
|
|
|
|
imgsize = v2s32(use_imgsize, use_imgsize);
|
2018-08-04 18:55:54 +02:00
|
|
|
spacing = v2f32(use_imgsize*5.0/4, use_imgsize*15.0/13);
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
padding = v2s32(use_imgsize*3.0/8, use_imgsize*3.0/8);
|
|
|
|
m_btn_height = use_imgsize*15.0/13 * 0.35;
|
|
|
|
|
2015-01-09 18:01:59 +01:00
|
|
|
m_font = g_fontengine->getFont();
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
if (mydata.real_coordinates) {
|
|
|
|
mydata.size = v2s32(
|
|
|
|
mydata.invsize.X*imgsize.X,
|
|
|
|
mydata.invsize.Y*imgsize.Y
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
mydata.size = v2s32(
|
|
|
|
padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X,
|
|
|
|
padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
DesiredRect = mydata.rect = core::rect<s32>(
|
2017-03-04 10:46:55 +01:00
|
|
|
(s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * (f32)mydata.size.X) + offset.X,
|
|
|
|
(s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * (f32)mydata.size.Y) + offset.Y,
|
|
|
|
(s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * (f32)mydata.size.X) + offset.X,
|
|
|
|
(s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * (f32)mydata.size.Y) + offset.Y
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// Non-size[] form must consist only of text fields and
|
|
|
|
// implicit "Proceed" button. Use default font, and
|
|
|
|
// temporary form size which will be recalculated below.
|
2014-11-28 20:06:34 +01:00
|
|
|
m_font = g_fontengine->getFont();
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
m_btn_height = font_line_height(m_font) * 0.875;
|
|
|
|
DesiredRect = core::rect<s32>(
|
2017-03-04 10:46:55 +01:00
|
|
|
(s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * 580.0),
|
|
|
|
(s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * 300.0),
|
|
|
|
(s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * 580.0),
|
|
|
|
(s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * 300.0)
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
recalculateAbsolutePosition(false);
|
|
|
|
mydata.basepos = getBasePos();
|
|
|
|
m_tooltip_element->setOverrideFont(m_font);
|
|
|
|
|
2018-03-28 17:04:41 +02:00
|
|
|
gui::IGUISkin *skin = Environment->getSkin();
|
2017-06-17 19:11:28 +02:00
|
|
|
sanity_check(skin);
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
gui::IGUIFont *old_font = skin->getFont();
|
|
|
|
skin->setFont(m_font);
|
|
|
|
|
2018-08-04 18:55:54 +02:00
|
|
|
pos_offset = v2f32();
|
2018-03-28 17:04:41 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
// used for formspec versions < 3
|
|
|
|
core::list<IGUIElement *>::Iterator legacy_sort_start = Children.getLast();
|
|
|
|
|
2018-03-28 17:04:41 +02:00
|
|
|
if (enable_prepends) {
|
2019-06-27 14:40:49 +02:00
|
|
|
// Backup the coordinates so that prepends can use the coordinates of choice.
|
|
|
|
bool rc_backup = mydata.real_coordinates;
|
2019-09-15 17:56:11 +02:00
|
|
|
u16 version_backup = m_formspec_version;
|
2019-06-27 14:40:49 +02:00
|
|
|
mydata.real_coordinates = false; // Old coordinates by default.
|
2019-09-10 21:18:42 +02:00
|
|
|
|
2018-03-28 17:04:41 +02:00
|
|
|
std::vector<std::string> prepend_elements = split(m_formspec_prepend, ']');
|
|
|
|
for (const auto &element : prepend_elements)
|
|
|
|
parseElement(&mydata, element);
|
2019-09-10 21:18:42 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
// legacy sorting for formspec versions < 3
|
|
|
|
if (m_formspec_version >= 3)
|
|
|
|
// prepends do not need to be reordered
|
|
|
|
legacy_sort_start = Children.getLast();
|
|
|
|
else if (version_backup >= 3)
|
|
|
|
// only prepends elements have to be reordered
|
|
|
|
legacySortElements(legacy_sort_start);
|
|
|
|
|
2019-09-10 21:18:42 +02:00
|
|
|
m_formspec_version = version_backup;
|
2019-06-27 14:40:49 +02:00
|
|
|
mydata.real_coordinates = rc_backup; // Restore coordinates
|
2018-03-28 17:04:41 +02:00
|
|
|
}
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
for (; i< elements.size(); i++) {
|
|
|
|
parseElement(&mydata, elements[i]);
|
2011-04-04 14:13:19 +02:00
|
|
|
}
|
|
|
|
|
2016-07-07 22:10:15 +02:00
|
|
|
if (!container_stack.empty()) {
|
|
|
|
errorstream << "Invalid formspec string: container was never closed!"
|
|
|
|
<< std::endl;
|
|
|
|
}
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
// If there are fields without explicit size[], add a "Proceed"
|
|
|
|
// button and adjust size to fit all the fields.
|
2019-11-07 20:11:01 +01:00
|
|
|
if (mydata.simple_field_count > 0 && !mydata.explicit_size) {
|
2013-06-23 18:30:21 +02:00
|
|
|
mydata.rect = core::rect<s32>(
|
2019-11-07 20:11:01 +01:00
|
|
|
mydata.screensize.X / 2 - 580 / 2,
|
|
|
|
mydata.screensize.Y / 2 - 300 / 2,
|
|
|
|
mydata.screensize.X / 2 + 580 / 2,
|
|
|
|
mydata.screensize.Y / 2 + 240 / 2 + mydata.simple_field_count * 60
|
2012-07-15 18:19:38 +02:00
|
|
|
);
|
2019-11-07 20:11:01 +01:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
DesiredRect = mydata.rect;
|
2012-07-15 18:19:38 +02:00
|
|
|
recalculateAbsolutePosition(false);
|
2013-06-23 18:30:21 +02:00
|
|
|
mydata.basepos = getBasePos();
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 pos = mydata.basepos;
|
2019-11-07 20:11:01 +01:00
|
|
|
pos.Y = (mydata.simple_field_count + 2) * 60;
|
2012-07-15 18:19:38 +02:00
|
|
|
|
|
|
|
v2s32 size = DesiredRect.getSize();
|
2019-11-07 20:11:01 +01:00
|
|
|
mydata.rect = core::rect<s32>(
|
|
|
|
size.X / 2 - 70, pos.Y,
|
|
|
|
size.X / 2 - 70 + 140, pos.Y + m_btn_height * 2
|
|
|
|
);
|
2015-02-01 23:59:23 +01:00
|
|
|
const wchar_t *text = wgettext("Proceed");
|
2019-10-12 18:44:23 +02:00
|
|
|
GUIButton::addButton(Environment, mydata.rect, this, 257, text);
|
2013-04-07 19:15:17 +02:00
|
|
|
delete[] text;
|
2012-07-15 18:19:38 +02:00
|
|
|
}
|
|
|
|
}
|
2013-08-19 11:26:51 +02:00
|
|
|
|
|
|
|
//set initial focus if parser didn't set it
|
|
|
|
focused_element = Environment->getFocus();
|
|
|
|
if (!focused_element
|
|
|
|
|| !isMyChild(focused_element)
|
|
|
|
|| focused_element->getType() == gui::EGUIET_TAB_CONTROL)
|
|
|
|
setInitialFocus();
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
|
|
|
|
skin->setFont(old_font);
|
2019-11-07 20:11:01 +01:00
|
|
|
|
|
|
|
// legacy sorting
|
|
|
|
if (m_formspec_version < 3)
|
|
|
|
legacySortElements(legacy_sort_start);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator from)
|
|
|
|
{
|
|
|
|
/*
|
2019-11-20 19:39:10 +01:00
|
|
|
Draw order for formspec_version <= 2:
|
|
|
|
-3 bgcolor
|
|
|
|
-2 background
|
|
|
|
-1 box
|
|
|
|
0 All other elements
|
|
|
|
1 image
|
|
|
|
2 item_image, item_image_button
|
|
|
|
3 list
|
|
|
|
4 label
|
|
|
|
*/
|
2019-11-07 20:11:01 +01:00
|
|
|
|
|
|
|
if (from == Children.end())
|
|
|
|
from = Children.begin();
|
|
|
|
else
|
|
|
|
from++;
|
|
|
|
|
|
|
|
core::list<IGUIElement *>::Iterator to = Children.end();
|
2019-11-20 19:39:10 +01:00
|
|
|
// 1: Copy into a sortable container
|
|
|
|
std::vector<IGUIElement *> elements;
|
|
|
|
for (auto it = from; it != to; ++it)
|
|
|
|
elements.emplace_back(*it);
|
|
|
|
|
|
|
|
// 2: Sort the container
|
|
|
|
std::sort(elements.begin(), elements.end(),
|
|
|
|
[this] (const IGUIElement *a, const IGUIElement *b) -> bool {
|
|
|
|
const FieldSpec *spec_a = getSpecByID(a->getID());
|
|
|
|
const FieldSpec *spec_b = getSpecByID(b->getID());
|
|
|
|
return spec_a && spec_b &&
|
|
|
|
spec_a->priority < spec_b->priority;
|
|
|
|
});
|
|
|
|
|
|
|
|
// 3: Re-assign the pointers
|
|
|
|
for (auto e : elements) {
|
|
|
|
*from = e;
|
|
|
|
from++;
|
2019-11-07 20:11:01 +01:00
|
|
|
}
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
|
|
|
|
2014-04-21 14:10:59 +02:00
|
|
|
#ifdef __ANDROID__
|
|
|
|
bool GUIFormSpecMenu::getAndroidUIInput()
|
|
|
|
{
|
2018-06-24 21:50:57 +02:00
|
|
|
if (!hasAndroidUIInput())
|
2014-04-21 14:10:59 +02:00
|
|
|
return false;
|
|
|
|
|
2018-06-24 21:50:57 +02:00
|
|
|
std::string fieldname = m_jni_field_name;
|
|
|
|
m_jni_field_name.clear();
|
2014-04-21 14:10:59 +02:00
|
|
|
|
|
|
|
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
|
2015-08-25 22:23:05 +02:00
|
|
|
iter != m_fields.end(); ++iter) {
|
2014-04-21 14:10:59 +02:00
|
|
|
|
|
|
|
if (iter->fname != fieldname) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-11-07 20:11:01 +01:00
|
|
|
IGUIElement *tochange = getElementFromId(iter->fid, true);
|
2014-04-21 14:10:59 +02:00
|
|
|
|
|
|
|
if (tochange == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string text = porting::getInputDialogValue();
|
|
|
|
|
2018-06-24 21:50:57 +02:00
|
|
|
((gui::IGUIEditBox *)tochange)->setText(utf8_to_wide(text).c_str());
|
2014-04-21 14:10:59 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
|
2010-12-22 15:30:23 +01:00
|
|
|
{
|
2019-11-07 20:11:01 +01:00
|
|
|
core::rect<s32> imgrect(0, 0, imgsize.X, imgsize.Y);
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
|
2019-11-07 20:11:01 +01:00
|
|
|
core::rect<s32> clipping_rect = s.e->getAbsoluteClippingRect();
|
|
|
|
v2s32 base_pos = s.e->getAbsolutePosition().UpperLeftCorner;
|
2014-04-21 14:10:59 +02:00
|
|
|
for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
|
2012-07-24 19:57:17 +02:00
|
|
|
s32 item_i = i + s.start_item_i;
|
2019-06-27 14:40:49 +02:00
|
|
|
|
|
|
|
s32 x;
|
|
|
|
s32 y;
|
|
|
|
if (s.real_coordinates) {
|
|
|
|
x = (i%s.geom.X) * (imgsize.X * 1.25);
|
|
|
|
y = (i/s.geom.X) * (imgsize.Y * 1.25);
|
|
|
|
} else {
|
|
|
|
x = (i%s.geom.X) * spacing.X;
|
|
|
|
y = (i/s.geom.X) * spacing.Y;
|
|
|
|
}
|
2010-12-22 15:30:23 +01:00
|
|
|
v2s32 p0(x,y);
|
2019-11-07 20:11:01 +01:00
|
|
|
core::rect<s32> rect = imgrect + base_pos + p0;
|
|
|
|
rect.clipAgainst(clipping_rect);
|
|
|
|
if (rect.getArea() > 0 && rect.isPointInside(p))
|
2012-07-24 19:57:17 +02:00
|
|
|
return ItemSpec(s.inventoryloc, s.listname, item_i);
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
return ItemSpec(InventoryLocation(), "", -1);
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
|
2016-01-24 14:19:17 +01:00
|
|
|
bool &item_hovered)
|
2010-12-22 02:34:21 +01:00
|
|
|
{
|
2010-12-22 15:30:23 +01:00
|
|
|
video::IVideoDriver* driver = Environment->getVideoDriver();
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
|
2019-11-07 20:11:01 +01:00
|
|
|
if (!inv) {
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"GUIFormSpecMenu::drawList(): "
|
2019-11-07 20:11:01 +01:00
|
|
|
<< "The inventory location "
|
|
|
|
<< "\"" << s.inventoryloc.dump() << "\" doesn't exist anymore"
|
|
|
|
<< std::endl;
|
2012-06-02 14:28:12 +02:00
|
|
|
return;
|
|
|
|
}
|
2011-04-04 14:13:19 +02:00
|
|
|
InventoryList *ilist = inv->getList(s.listname);
|
2019-11-07 20:11:01 +01:00
|
|
|
if (!ilist) {
|
|
|
|
warningstream << "GUIFormSpecMenu::drawList(): "
|
|
|
|
<< "The inventory list \"" << s.listname << "\" @ \""
|
|
|
|
<< s.inventoryloc.dump() << "\" doesn't exist anymore"
|
|
|
|
<< std::endl;
|
2012-06-02 14:28:12 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
core::rect<s32> imgrect(0, 0, imgsize.X, imgsize.Y);
|
|
|
|
core::rect<s32> clipping_rect = s.e->getAbsoluteClippingRect();
|
|
|
|
v2s32 base_pos = s.e->getAbsolutePosition().UpperLeftCorner;
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
for (s32 i = 0; i < s.geom.X * s.geom.Y; i++) {
|
2013-06-23 18:30:21 +02:00
|
|
|
s32 item_i = i + s.start_item_i;
|
2018-04-03 11:15:58 +02:00
|
|
|
if (item_i >= (s32)ilist->getSize())
|
2012-07-24 19:57:17 +02:00
|
|
|
break;
|
2018-04-03 11:15:58 +02:00
|
|
|
|
2019-06-27 14:40:49 +02:00
|
|
|
s32 x;
|
|
|
|
s32 y;
|
|
|
|
if (s.real_coordinates) {
|
|
|
|
x = (i%s.geom.X) * (imgsize.X * 1.25);
|
|
|
|
y = (i/s.geom.X) * (imgsize.Y * 1.25);
|
|
|
|
} else {
|
|
|
|
x = (i%s.geom.X) * spacing.X;
|
|
|
|
y = (i/s.geom.X) * spacing.Y;
|
|
|
|
}
|
2010-12-22 15:30:23 +01:00
|
|
|
v2s32 p(x,y);
|
2019-11-07 20:11:01 +01:00
|
|
|
core::rect<s32> rect = imgrect + base_pos + p;
|
2018-04-03 11:15:58 +02:00
|
|
|
ItemStack item = ilist->getItem(item_i);
|
2010-12-22 15:30:23 +01:00
|
|
|
|
2012-01-13 12:35:55 +01:00
|
|
|
bool selected = m_selected_item
|
|
|
|
&& m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
|
|
|
|
&& m_selected_item->listname == s.listname
|
2013-06-18 01:18:54 +02:00
|
|
|
&& m_selected_item->i == item_i;
|
2019-11-07 20:11:01 +01:00
|
|
|
core::rect<s32> clipped_rect(rect);
|
|
|
|
clipped_rect.clipAgainst(clipping_rect);
|
|
|
|
bool hovering = clipped_rect.getArea() > 0 &&
|
|
|
|
clipped_rect.isPointInside(m_pointer);
|
2016-02-07 20:27:50 +01:00
|
|
|
ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
|
|
|
|
(hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
if (layer == 0) {
|
2016-01-24 14:19:17 +01:00
|
|
|
if (hovering) {
|
|
|
|
item_hovered = true;
|
2019-11-07 20:11:01 +01:00
|
|
|
driver->draw2DRectangle(m_slotbg_h, rect, &clipping_rect);
|
2016-01-24 14:19:17 +01:00
|
|
|
} else {
|
2019-11-07 20:11:01 +01:00
|
|
|
driver->draw2DRectangle(m_slotbg_n, rect, &clipping_rect);
|
2016-01-24 14:19:17 +01:00
|
|
|
}
|
2013-11-02 04:26:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//Draw inv slot borders
|
|
|
|
if (m_slotborder) {
|
|
|
|
s32 x1 = rect.UpperLeftCorner.X;
|
|
|
|
s32 y1 = rect.UpperLeftCorner.Y;
|
|
|
|
s32 x2 = rect.LowerRightCorner.X;
|
|
|
|
s32 y2 = rect.LowerRightCorner.Y;
|
|
|
|
s32 border = 1;
|
|
|
|
driver->draw2DRectangle(m_slotbordercolor,
|
|
|
|
core::rect<s32>(v2s32(x1 - border, y1 - border),
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32(x2 + border, y1)), &clipping_rect);
|
2013-11-02 04:26:44 +01:00
|
|
|
driver->draw2DRectangle(m_slotbordercolor,
|
|
|
|
core::rect<s32>(v2s32(x1 - border, y2),
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32(x2 + border, y2 + border)), &clipping_rect);
|
2013-11-02 04:26:44 +01:00
|
|
|
driver->draw2DRectangle(m_slotbordercolor,
|
|
|
|
core::rect<s32>(v2s32(x1 - border, y1),
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32(x1, y2)), &clipping_rect);
|
2013-11-02 04:26:44 +01:00
|
|
|
driver->draw2DRectangle(m_slotbordercolor,
|
|
|
|
core::rect<s32>(v2s32(x2, y1),
|
2019-11-07 20:11:01 +01:00
|
|
|
v2s32(x2 + border, y2)), &clipping_rect);
|
2011-12-01 10:25:55 +01:00
|
|
|
}
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
if (layer == 1) {
|
|
|
|
if (selected)
|
2012-01-21 21:21:41 +01:00
|
|
|
item.takeItem(m_selected_amount);
|
2018-04-03 11:15:58 +02:00
|
|
|
|
|
|
|
if (!item.empty()) {
|
2019-08-24 18:38:02 +02:00
|
|
|
// Draw item stack
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
drawItemStack(driver, m_font, item,
|
2019-11-07 20:11:01 +01:00
|
|
|
rect, &clipping_rect, m_client, rotation_kind);
|
2019-08-24 18:38:02 +02:00
|
|
|
// Draw tooltip
|
|
|
|
if (hovering && !m_selected_item) {
|
|
|
|
std::string tooltip = item.getDescription(m_client->idef());
|
|
|
|
if (m_tooltip_append_itemname)
|
|
|
|
tooltip += "\n[" + item.name + "]";
|
|
|
|
showTooltip(utf8_to_wide(tooltip), m_default_tooltip_color,
|
|
|
|
m_default_tooltip_bgcolor);
|
2017-09-15 14:31:32 +02:00
|
|
|
}
|
2016-04-04 18:31:00 +02:00
|
|
|
}
|
2011-02-14 16:41:49 +01:00
|
|
|
}
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
void GUIFormSpecMenu::drawSelectedItem()
|
2012-01-21 21:21:41 +01:00
|
|
|
{
|
|
|
|
video::IVideoDriver* driver = Environment->getVideoDriver();
|
|
|
|
|
2016-01-24 14:19:17 +01:00
|
|
|
if (!m_selected_item) {
|
2019-11-07 20:11:01 +01:00
|
|
|
// reset rotation time
|
2016-01-24 14:19:17 +01:00
|
|
|
drawItemStack(driver, m_font, ItemStack(),
|
2019-09-10 15:11:26 +02:00
|
|
|
core::rect<s32>(v2s32(0, 0), v2s32(0, 0)), NULL,
|
|
|
|
m_client, IT_ROT_DRAGGED);
|
2016-01-24 14:19:17 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
|
2015-03-06 11:21:51 +01:00
|
|
|
sanity_check(inv);
|
2012-01-21 21:21:41 +01:00
|
|
|
InventoryList *list = inv->getList(m_selected_item->listname);
|
2015-03-06 11:21:51 +01:00
|
|
|
sanity_check(list);
|
2012-01-21 21:21:41 +01:00
|
|
|
ItemStack stack = list->getItem(m_selected_item->i);
|
|
|
|
stack.count = m_selected_amount;
|
|
|
|
|
|
|
|
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
|
|
|
|
core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
|
2016-12-20 14:27:14 +01:00
|
|
|
rect.constrainTo(driver->getViewPort());
|
2017-01-09 20:39:22 +01:00
|
|
|
drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED);
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
void GUIFormSpecMenu::drawMenu()
|
2010-12-22 02:34:21 +01:00
|
|
|
{
|
2017-08-29 19:25:16 +02:00
|
|
|
if (m_form_src) {
|
|
|
|
const std::string &newform = m_form_src->getForm();
|
|
|
|
if (newform != m_formspec_string) {
|
2012-06-03 17:30:34 +02:00
|
|
|
m_formspec_string = newform;
|
|
|
|
regenerateGui(m_screensize_old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
gui::IGUISkin* skin = Environment->getSkin();
|
2015-03-06 11:21:51 +01:00
|
|
|
sanity_check(skin != NULL);
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
gui::IGUIFont *old_font = skin->getFont();
|
|
|
|
skin->setFont(m_font);
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
updateSelectedItem();
|
|
|
|
|
2010-12-22 02:34:21 +01:00
|
|
|
video::IVideoDriver* driver = Environment->getVideoDriver();
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
/*
|
|
|
|
Draw background color
|
|
|
|
*/
|
2013-11-02 04:26:44 +01:00
|
|
|
v2u32 screenSize = driver->getScreenSize();
|
2017-08-29 19:25:16 +02:00
|
|
|
core::rect<s32> allbg(0, 0, screenSize.X, screenSize.Y);
|
|
|
|
|
2013-12-11 23:07:38 +01:00
|
|
|
if (m_bgfullscreen)
|
2017-08-29 19:25:16 +02:00
|
|
|
driver->draw2DRectangle(m_fullscreen_bgcolor, allbg, &allbg);
|
2019-10-06 14:50:45 +02:00
|
|
|
else
|
|
|
|
driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
|
2010-12-22 02:34:21 +01:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
/*
|
|
|
|
Draw rect_mode tooltip
|
|
|
|
*/
|
2012-01-13 12:35:55 +01:00
|
|
|
m_tooltip_element->setVisible(false);
|
|
|
|
|
2018-06-20 22:55:39 +02:00
|
|
|
for (const auto &pair : m_tooltip_rects) {
|
2019-11-07 20:11:01 +01:00
|
|
|
const core::rect<s32> &rect = pair.first->getAbsoluteClippingRect();
|
|
|
|
if (rect.getArea() > 0 && rect.isPointInside(m_pointer)) {
|
2018-06-20 22:55:39 +02:00
|
|
|
const std::wstring &text = pair.second.tooltip;
|
|
|
|
if (!text.empty()) {
|
|
|
|
showTooltip(text, pair.second.color, pair.second.bgcolor);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-08 19:22:37 +01:00
|
|
|
/*
|
|
|
|
Draw backgrounds
|
|
|
|
*/
|
2019-11-07 20:11:01 +01:00
|
|
|
for (gui::IGUIElement *e : m_backgrounds) {
|
|
|
|
e->setVisible(true);
|
|
|
|
e->draw();
|
|
|
|
e->setVisible(false);
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
2016-01-24 14:19:17 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Call base class
|
|
|
|
*/
|
|
|
|
gui::IGUIElement::draw();
|
|
|
|
|
2012-11-04 21:18:28 +01:00
|
|
|
/*
|
|
|
|
Draw items
|
2018-04-03 11:15:58 +02:00
|
|
|
Layer 0: Item slot rectangles
|
|
|
|
Layer 1: Item images; prepare tooltip
|
2012-11-04 21:18:28 +01:00
|
|
|
*/
|
2016-01-24 14:19:17 +01:00
|
|
|
bool item_hovered = false;
|
2018-04-03 11:15:58 +02:00
|
|
|
for (int layer = 0; layer < 2; layer++) {
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const GUIFormSpecMenu::ListDrawSpec &spec : m_inventorylists) {
|
2018-04-03 11:15:58 +02:00
|
|
|
drawList(spec, layer, item_hovered);
|
2016-01-24 14:19:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!item_hovered) {
|
2019-11-07 20:11:01 +01:00
|
|
|
// reset rotation time
|
2016-01-24 14:19:17 +01:00
|
|
|
drawItemStack(driver, m_font, ItemStack(),
|
|
|
|
core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
|
2017-01-09 20:39:22 +01:00
|
|
|
NULL, m_client, IT_ROT_HOVERED);
|
2012-11-04 21:18:28 +01:00
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2014-04-21 14:10:59 +02:00
|
|
|
/* TODO find way to show tooltips on touchscreen */
|
|
|
|
#ifndef HAVE_TOUCHSCREENGUI
|
2017-06-26 20:11:17 +02:00
|
|
|
m_pointer = RenderingEngine::get_raw_device()->getCursorControl()->getPosition();
|
2014-04-21 14:10:59 +02:00
|
|
|
#endif
|
|
|
|
|
2012-12-01 18:36:42 +01:00
|
|
|
/*
|
|
|
|
Draw fields/buttons tooltips
|
|
|
|
*/
|
2014-06-19 00:22:03 +02:00
|
|
|
gui::IGUIElement *hovered =
|
|
|
|
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
2014-06-14 11:22:09 +02:00
|
|
|
|
2014-06-19 00:22:03 +02:00
|
|
|
if (hovered != NULL) {
|
|
|
|
s32 id = hovered->getID();
|
2014-10-30 07:53:20 +01:00
|
|
|
|
2017-05-26 14:03:36 +02:00
|
|
|
u64 delta = 0;
|
2014-06-24 12:28:24 +02:00
|
|
|
if (id == -1) {
|
|
|
|
m_old_tooltip_id = id;
|
2014-10-30 07:53:20 +01:00
|
|
|
} else {
|
|
|
|
if (id == m_old_tooltip_id) {
|
2016-03-06 20:31:16 +01:00
|
|
|
delta = porting::getDeltaMs(m_hovered_time, porting::getTimeMs());
|
2014-10-30 07:53:20 +01:00
|
|
|
} else {
|
2016-03-06 20:31:16 +01:00
|
|
|
m_hovered_time = porting::getTimeMs();
|
2014-10-30 07:53:20 +01:00
|
|
|
m_old_tooltip_id = id;
|
|
|
|
}
|
2014-07-02 15:32:54 +02:00
|
|
|
}
|
2014-10-30 07:53:20 +01:00
|
|
|
|
2017-06-03 08:55:26 +02:00
|
|
|
// Find and update the current tooltip
|
2014-07-02 15:32:54 +02:00
|
|
|
if (id != -1 && delta >= m_tooltip_show_delay) {
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const FieldSpec &field : m_fields) {
|
2017-06-03 08:55:26 +02:00
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
if (field.fid != id)
|
2017-06-03 08:55:26 +02:00
|
|
|
continue;
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
const std::wstring &text = m_tooltips[field.fname].tooltip;
|
2017-06-03 08:55:26 +02:00
|
|
|
if (!text.empty())
|
2017-08-20 19:37:29 +02:00
|
|
|
showTooltip(text, m_tooltips[field.fname].color,
|
|
|
|
m_tooltips[field.fname].bgcolor);
|
2017-06-03 08:55:26 +02:00
|
|
|
|
|
|
|
break;
|
2012-12-01 18:36:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2016-05-31 17:30:11 +02:00
|
|
|
m_tooltip_element->draw();
|
|
|
|
|
2012-12-01 18:36:42 +01:00
|
|
|
/*
|
|
|
|
Draw dragged item stack
|
|
|
|
*/
|
|
|
|
drawSelectedItem();
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
|
|
|
|
skin->setFont(old_font);
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
|
|
|
|
2017-06-03 08:55:26 +02:00
|
|
|
|
|
|
|
void GUIFormSpecMenu::showTooltip(const std::wstring &text,
|
|
|
|
const irr::video::SColor &color, const irr::video::SColor &bgcolor)
|
|
|
|
{
|
2017-01-31 18:05:03 +01:00
|
|
|
const std::wstring ntext = translate_string(text);
|
2017-06-03 08:55:26 +02:00
|
|
|
m_tooltip_element->setOverrideColor(color);
|
|
|
|
m_tooltip_element->setBackgroundColor(bgcolor);
|
2017-01-31 18:05:03 +01:00
|
|
|
setStaticText(m_tooltip_element, ntext.c_str());
|
2017-06-03 08:55:26 +02:00
|
|
|
|
|
|
|
// Tooltip size and offset
|
|
|
|
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
|
|
|
|
#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1
|
2017-01-31 18:05:03 +01:00
|
|
|
std::vector<std::wstring> text_rows = str_split(ntext, L'\n');
|
2017-06-03 08:55:26 +02:00
|
|
|
s32 tooltip_height = m_tooltip_element->getTextHeight() * text_rows.size() + 5;
|
|
|
|
#else
|
|
|
|
s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
|
|
|
|
#endif
|
|
|
|
v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
|
|
|
|
int tooltip_offset_x = m_btn_height;
|
|
|
|
int tooltip_offset_y = m_btn_height;
|
|
|
|
#ifdef __ANDROID__
|
|
|
|
tooltip_offset_x *= 3;
|
|
|
|
tooltip_offset_y = 0;
|
|
|
|
if (m_pointer.X > (s32)screenSize.X / 2)
|
|
|
|
tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Calculate and set the tooltip position
|
|
|
|
s32 tooltip_x = m_pointer.X + tooltip_offset_x;
|
|
|
|
s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
|
|
|
|
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;
|
|
|
|
|
|
|
|
m_tooltip_element->setRelativePosition(
|
|
|
|
core::rect<s32>(
|
|
|
|
core::position2d<s32>(tooltip_x, tooltip_y),
|
|
|
|
core::dimension2d<s32>(tooltip_width, tooltip_height)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Display the tooltip
|
|
|
|
m_tooltip_element->setVisible(true);
|
|
|
|
bringToFront(m_tooltip_element);
|
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
void GUIFormSpecMenu::updateSelectedItem()
|
2012-01-21 21:21:41 +01:00
|
|
|
{
|
2018-04-03 11:15:58 +02:00
|
|
|
verifySelectedItem();
|
|
|
|
|
|
|
|
// If craftresult is nonempty and nothing else is selected, select it now.
|
|
|
|
if (!m_selected_item) {
|
|
|
|
for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
|
|
|
|
if (s.listname != "craftpreview")
|
|
|
|
continue;
|
|
|
|
|
2012-09-02 21:51:38 +02:00
|
|
|
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
|
2018-04-03 11:15:58 +02:00
|
|
|
if (!inv)
|
2012-09-02 21:51:38 +02:00
|
|
|
continue;
|
2018-04-03 11:15:58 +02:00
|
|
|
|
|
|
|
InventoryList *list = inv->getList("craftresult");
|
|
|
|
|
|
|
|
if (!list || list->getSize() == 0)
|
2012-09-02 21:51:38 +02:00
|
|
|
continue;
|
2012-01-21 21:21:41 +01:00
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
const ItemStack &item = list->getItem(0);
|
|
|
|
if (item.empty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Grab selected item from the crafting result list
|
|
|
|
m_selected_item = new ItemSpec;
|
|
|
|
m_selected_item->inventoryloc = s.inventoryloc;
|
|
|
|
m_selected_item->listname = "craftresult";
|
|
|
|
m_selected_item->i = 0;
|
|
|
|
m_selected_amount = item.count;
|
|
|
|
m_selected_dragging = false;
|
|
|
|
break;
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If craftresult is selected, keep the whole stack selected
|
2018-04-03 11:15:58 +02:00
|
|
|
if (m_selected_item && m_selected_item->listname == "craftresult")
|
2013-06-18 01:32:11 +02:00
|
|
|
m_selected_amount = verifySelectedItem().count;
|
|
|
|
}
|
|
|
|
|
|
|
|
ItemStack GUIFormSpecMenu::verifySelectedItem()
|
|
|
|
{
|
|
|
|
// If the selected stack has become empty for some reason, deselect it.
|
|
|
|
// If the selected stack has become inaccessible, deselect it.
|
|
|
|
// If the selected stack has become smaller, adjust m_selected_amount.
|
|
|
|
// Return the selected stack.
|
|
|
|
|
|
|
|
if(m_selected_item)
|
|
|
|
{
|
|
|
|
if(m_selected_item->isValid())
|
|
|
|
{
|
|
|
|
Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
|
|
|
|
if(inv)
|
|
|
|
{
|
|
|
|
InventoryList *list = inv->getList(m_selected_item->listname);
|
|
|
|
if(list && (u32) m_selected_item->i < list->getSize())
|
|
|
|
{
|
|
|
|
ItemStack stack = list->getItem(m_selected_item->i);
|
2018-04-03 11:15:58 +02:00
|
|
|
if (!m_selected_swap.empty()) {
|
|
|
|
if (m_selected_swap.name == stack.name &&
|
|
|
|
m_selected_swap.count == stack.count)
|
|
|
|
m_selected_swap.clear();
|
|
|
|
} else {
|
|
|
|
m_selected_amount = std::min(m_selected_amount, stack.count);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!stack.empty())
|
2013-06-18 01:32:11 +02:00
|
|
|
return stack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// selection was not valid
|
|
|
|
delete m_selected_item;
|
|
|
|
m_selected_item = NULL;
|
|
|
|
m_selected_amount = 0;
|
|
|
|
m_selected_dragging = false;
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
2013-06-18 01:32:11 +02:00
|
|
|
return ItemStack();
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
|
2012-07-15 18:19:38 +02:00
|
|
|
{
|
|
|
|
if(m_text_dst)
|
|
|
|
{
|
2015-05-19 08:24:14 +02:00
|
|
|
StringMap fields;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
if (quitmode == quit_mode_accept) {
|
2013-10-26 09:56:38 +02:00
|
|
|
fields["quit"] = "true";
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
if (quitmode == quit_mode_cancel) {
|
|
|
|
fields["quit"] = "true";
|
|
|
|
m_text_dst->gotText(fields);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-07 21:53:40 +02:00
|
|
|
if (current_keys_pending.key_down) {
|
|
|
|
fields["key_down"] = "true";
|
|
|
|
current_keys_pending.key_down = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current_keys_pending.key_up) {
|
|
|
|
fields["key_up"] = "true";
|
|
|
|
current_keys_pending.key_up = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current_keys_pending.key_enter) {
|
|
|
|
fields["key_enter"] = "true";
|
|
|
|
current_keys_pending.key_enter = false;
|
|
|
|
}
|
|
|
|
|
2016-08-07 17:01:00 +02:00
|
|
|
if (!current_field_enter_pending.empty()) {
|
|
|
|
fields["key_enter_field"] = current_field_enter_pending;
|
2016-08-07 17:22:50 +02:00
|
|
|
current_field_enter_pending = "";
|
2016-08-07 17:01:00 +02:00
|
|
|
}
|
|
|
|
|
2013-07-07 21:53:40 +02:00
|
|
|
if (current_keys_pending.key_escape) {
|
|
|
|
fields["key_escape"] = "true";
|
|
|
|
current_keys_pending.key_escape = false;
|
|
|
|
}
|
|
|
|
|
2017-08-20 19:37:29 +02:00
|
|
|
for (const GUIFormSpecMenu::FieldSpec &s : m_fields) {
|
2014-05-30 03:04:10 +02:00
|
|
|
if(s.send) {
|
2015-06-10 01:54:33 +02:00
|
|
|
std::string name = s.fname;
|
|
|
|
if (s.ftype == f_Button) {
|
|
|
|
fields[name] = wide_to_utf8(s.flabel);
|
|
|
|
} else if (s.ftype == f_Table) {
|
2013-08-23 12:24:11 +02:00
|
|
|
GUITable *table = getTable(s.fname);
|
|
|
|
if (table) {
|
2014-05-30 03:04:10 +02:00
|
|
|
fields[name] = table->checkEvent();
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(s.ftype == f_DropDown) {
|
|
|
|
// no dynamic cast possible due to some distributions shipped
|
|
|
|
// without rtti support in irrlicht
|
2019-11-07 20:11:01 +01:00
|
|
|
IGUIElement *element = getElementFromId(s.fid, true);
|
2013-06-23 18:30:21 +02:00
|
|
|
gui::IGUIComboBox *e = NULL;
|
|
|
|
if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
|
2017-08-20 20:24:26 +02:00
|
|
|
e = static_cast<gui::IGUIComboBox*>(element);
|
2019-11-07 20:11:01 +01:00
|
|
|
} else {
|
|
|
|
warningstream << "GUIFormSpecMenu::acceptInput: dropdown "
|
|
|
|
<< "field without dropdown element" << std::endl;
|
|
|
|
continue;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
2013-12-11 23:07:38 +01:00
|
|
|
s32 selected = e->getSelected();
|
|
|
|
if (selected >= 0) {
|
2016-05-05 18:50:02 +02:00
|
|
|
std::vector<std::string> *dropdown_values =
|
|
|
|
getDropDownValues(s.fname);
|
|
|
|
if (dropdown_values && selected < (s32)dropdown_values->size()) {
|
|
|
|
fields[name] = (*dropdown_values)[selected];
|
|
|
|
}
|
2013-12-11 23:07:38 +01:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
else if (s.ftype == f_TabHeader) {
|
|
|
|
// no dynamic cast possible due to some distributions shipped
|
2017-08-20 20:24:26 +02:00
|
|
|
// without rttzi support in irrlicht
|
2019-11-07 20:11:01 +01:00
|
|
|
IGUIElement *element = getElementFromId(s.fid, true);
|
|
|
|
gui::IGUITabControl *e = nullptr;
|
2013-06-23 18:30:21 +02:00
|
|
|
if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
|
2017-08-20 20:24:26 +02:00
|
|
|
e = static_cast<gui::IGUITabControl *>(element);
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (e != 0) {
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << (e->getActiveTab() +1);
|
2014-05-30 03:04:10 +02:00
|
|
|
fields[name] = ss.str();
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (s.ftype == f_CheckBox) {
|
|
|
|
// no dynamic cast possible due to some distributions shipped
|
|
|
|
// without rtti support in irrlicht
|
2019-11-07 20:11:01 +01:00
|
|
|
IGUIElement *element = getElementFromId(s.fid, true);
|
|
|
|
gui::IGUICheckBox *e = nullptr;
|
2013-06-23 18:30:21 +02:00
|
|
|
if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
|
2017-08-20 20:24:26 +02:00
|
|
|
e = static_cast<gui::IGUICheckBox*>(element);
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (e != 0) {
|
|
|
|
if (e->isChecked())
|
2014-05-30 03:04:10 +02:00
|
|
|
fields[name] = "true";
|
2013-06-23 18:30:21 +02:00
|
|
|
else
|
2014-05-30 03:04:10 +02:00
|
|
|
fields[name] = "false";
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-19 18:17:35 +02:00
|
|
|
else if (s.ftype == f_ScrollBar) {
|
|
|
|
// no dynamic cast possible due to some distributions shipped
|
|
|
|
// without rtti support in irrlicht
|
2019-11-07 20:11:01 +01:00
|
|
|
IGUIElement *element = getElementFromId(s.fid, true);
|
|
|
|
GUIScrollBar *e = nullptr;
|
|
|
|
if (element && element->getType() == gui::EGUIET_ELEMENT)
|
|
|
|
e = static_cast<GUIScrollBar *>(element);
|
2014-06-19 18:17:35 +02:00
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
if (e) {
|
2014-06-19 18:17:35 +02:00
|
|
|
std::stringstream os;
|
|
|
|
os << e->getPos();
|
|
|
|
if (s.fdefault == L"Changed")
|
|
|
|
fields[name] = "CHG:" + os.str();
|
|
|
|
else
|
|
|
|
fields[name] = "VAL:" + os.str();
|
|
|
|
}
|
|
|
|
}
|
2019-11-07 20:11:01 +01:00
|
|
|
else {
|
|
|
|
IGUIElement *e = getElementFromId(s.fid, true);
|
|
|
|
if (e)
|
2015-06-10 01:54:33 +02:00
|
|
|
fields[name] = wide_to_utf8(e->getText());
|
2012-07-15 18:19:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
m_text_dst->gotText(fields);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:07:48 +02:00
|
|
|
static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent)
|
|
|
|
{
|
|
|
|
while(tocheck != NULL) {
|
|
|
|
if (tocheck == parent) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
tocheck = tocheck->getParent();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
|
|
|
{
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
// The IGUITabControl renders visually using the skin's selected
|
|
|
|
// font, which we override for the duration of form drawing,
|
|
|
|
// but computes tab hotspots based on how it would have rendered
|
|
|
|
// using the font that is selected at the time of button release.
|
|
|
|
// To make these two consistent, temporarily override the skin's
|
|
|
|
// font while the IGUITabControl is processing the event.
|
|
|
|
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
|
|
|
|
event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
|
|
|
|
s32 x = event.MouseInput.X;
|
|
|
|
s32 y = event.MouseInput.Y;
|
|
|
|
gui::IGUIElement *hovered =
|
|
|
|
Environment->getRootGUIElement()->getElementFromPoint(
|
|
|
|
core::position2d<s32>(x, y));
|
2014-12-13 00:39:07 +01:00
|
|
|
if (hovered && isMyChild(hovered) &&
|
|
|
|
hovered->getType() == gui::EGUIET_TAB_CONTROL) {
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
gui::IGUISkin* skin = Environment->getSkin();
|
2015-03-06 11:21:51 +01:00
|
|
|
sanity_check(skin != NULL);
|
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now
fixed, aside from variations caused by rounding. This makes form layout
almost fully predictable, and particularly independent of player's
screen size. The proportions of non-text elements are the traditional
proportions.
For compatibility, the way in which element positions and sizes are
specified remains unchanged, in all its baroqueness, with one exception.
The exception is that the position of a label[] element is now defined
in terms of the vertically center of the first line of the label,
rather than the bottom of the first line of the label. This change
allows a label to be precisely aligned with button text or an edit box,
which are positioned in a centering manner. Label positioning remains
consistent with the previous system, just more precisely defined.
Make multi-line label[] elements work properly. Previously the code set
a bounding rectangle assuming that there would be only a single line,
and as a result a multi-line label would be cut somewhere in the middle
of the second line. Now multi-line labels not only work, but have
guaranteed line spacing relative to inventory slots, to aid alignment.
Incidentally fix tabheader[] elements which were being constrained to
the wrong width.
Given an unusually large form, in variable-size mode, the form rendering
system now chooses a scale that will fit the entire form on the screen,
if that doesn't make elements too small. Fixed-size forms, including the
main menu, are have their sizes fixed in inch terms. The fixed size for
fixed-size forms and the preferred and minimum sizes for variable-size
forms all scale according to the gui_scaling parameter.
2014-08-21 00:42:27 +02:00
|
|
|
gui::IGUIFont *old_font = skin->getFont();
|
|
|
|
skin->setFont(m_font);
|
|
|
|
bool retval = hovered->OnEvent(event);
|
|
|
|
skin->setFont(old_font);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
// Fix Esc/Return key being eaten by checkboxen and tables
|
2014-05-30 03:04:10 +02:00
|
|
|
if(event.EventType==EET_KEY_INPUT_EVENT) {
|
2013-08-19 11:26:51 +02:00
|
|
|
KeyPress kp(event.KeyInput);
|
2014-04-21 14:10:59 +02:00
|
|
|
if (kp == EscapeKey || kp == CancelKey
|
|
|
|
|| kp == getKeySetting("keymap_inventory")
|
2014-05-30 03:04:10 +02:00
|
|
|
|| event.KeyInput.Key==KEY_RETURN) {
|
2013-08-19 11:26:51 +02:00
|
|
|
gui::IGUIElement *focused = Environment->getFocus();
|
|
|
|
if (focused && isMyChild(focused) &&
|
|
|
|
(focused->getType() == gui::EGUIET_LIST_BOX ||
|
2018-07-28 12:58:16 +02:00
|
|
|
focused->getType() == gui::EGUIET_CHECK_BOX) &&
|
|
|
|
(focused->getParent()->getType() != gui::EGUIET_COMBO_BOX ||
|
|
|
|
event.KeyInput.Key != KEY_RETURN)) {
|
2013-08-19 11:26:51 +02:00
|
|
|
OnEvent(event);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-10 15:11:26 +02:00
|
|
|
// 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)) {
|
2013-08-19 11:26:51 +02:00
|
|
|
s32 x = event.MouseInput.X;
|
|
|
|
s32 y = event.MouseInput.Y;
|
|
|
|
gui::IGUIElement *hovered =
|
|
|
|
Environment->getRootGUIElement()->getElementFromPoint(
|
|
|
|
core::position2d<s32>(x, y));
|
|
|
|
if (hovered && isMyChild(hovered)) {
|
|
|
|
hovered->OnEvent(event);
|
2019-09-10 15:11:26 +02:00
|
|
|
return event.MouseInput.Event == EMIE_MOUSE_WHEEL;
|
2013-08-19 11:26:51 +02:00
|
|
|
}
|
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
|
2014-05-30 03:07:48 +02:00
|
|
|
if (event.EventType == EET_MOUSE_INPUT_EVENT) {
|
|
|
|
s32 x = event.MouseInput.X;
|
|
|
|
s32 y = event.MouseInput.Y;
|
|
|
|
gui::IGUIElement *hovered =
|
|
|
|
Environment->getRootGUIElement()->getElementFromPoint(
|
|
|
|
core::position2d<s32>(x, y));
|
2014-06-24 12:28:24 +02:00
|
|
|
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
|
|
|
m_old_tooltip_id = -1;
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
if (!isChild(hovered,this)) {
|
|
|
|
if (DoubleClickDetection(event)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-27 08:35:07 +02:00
|
|
|
if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
|
|
|
|
/* TODO add a check like:
|
|
|
|
if (event.JoystickEvent != joystick_we_listen_for)
|
|
|
|
return false;
|
|
|
|
*/
|
|
|
|
bool handled = m_joystick->handleEvent(event.JoystickEvent);
|
|
|
|
if (handled) {
|
|
|
|
if (m_joystick->wasKeyDown(KeyType::ESC)) {
|
|
|
|
tryClose();
|
|
|
|
} else if (m_joystick->wasKeyDown(KeyType::JUMP)) {
|
|
|
|
if (m_allowclose) {
|
|
|
|
acceptInput(quit_mode_accept);
|
|
|
|
quitMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return handled;
|
|
|
|
}
|
|
|
|
|
2018-06-24 21:50:57 +02:00
|
|
|
return GUIModalMenu::preprocessEvent(event);
|
2014-05-30 03:07:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
|
|
|
|
{
|
2015-03-09 03:42:25 +01:00
|
|
|
/* The following code is for capturing double-clicks of the mouse button
|
2015-03-14 10:55:35 +01:00
|
|
|
* and translating the double-click into an EET_KEY_INPUT_EVENT event
|
|
|
|
* -- which closes the form -- under some circumstances.
|
2015-03-09 03:42:25 +01:00
|
|
|
*
|
|
|
|
* There have been many github issues reporting this as a bug even though it
|
2015-03-14 10:55:35 +01:00
|
|
|
* was an intended feature. For this reason, remapping the double-click as
|
|
|
|
* an ESC must be explicitly set when creating this class via the
|
|
|
|
* /p remap_dbl_click parameter of the constructor.
|
2015-03-09 03:42:25 +01:00
|
|
|
*/
|
2015-03-14 10:55:35 +01:00
|
|
|
|
|
|
|
if (!m_remap_dbl_click)
|
|
|
|
return false;
|
|
|
|
|
2014-05-30 03:07:48 +02:00
|
|
|
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
|
|
|
m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
|
|
|
|
m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
|
|
|
|
|
|
|
|
m_doubleclickdetect[1].pos = m_pointer;
|
2016-03-06 20:31:16 +01:00
|
|
|
m_doubleclickdetect[1].time = porting::getTimeMs();
|
2014-05-30 03:07:48 +02:00
|
|
|
}
|
|
|
|
else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
|
2017-05-26 14:03:36 +02:00
|
|
|
u64 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, porting::getTimeMs());
|
2014-05-30 03:07:48 +02:00
|
|
|
if (delta > 400) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
double squaredistance =
|
|
|
|
m_doubleclickdetect[0].pos
|
|
|
|
.getDistanceFromSQ(m_doubleclickdetect[1].pos);
|
|
|
|
|
|
|
|
if (squaredistance > (30*30)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SEvent* translated = new SEvent();
|
|
|
|
assert(translated != 0);
|
|
|
|
//translate doubleclick to escape
|
|
|
|
memset(translated, 0, sizeof(SEvent));
|
|
|
|
translated->EventType = irr::EET_KEY_INPUT_EVENT;
|
|
|
|
translated->KeyInput.Key = KEY_ESCAPE;
|
|
|
|
translated->KeyInput.Control = false;
|
|
|
|
translated->KeyInput.Shift = false;
|
|
|
|
translated->KeyInput.PressedDown = true;
|
|
|
|
translated->KeyInput.Char = 0;
|
|
|
|
OnEvent(*translated);
|
|
|
|
|
|
|
|
// no need to send the key up event as we're already deleted
|
|
|
|
// and no one else did notice this event
|
|
|
|
delete translated;
|
|
|
|
return true;
|
|
|
|
}
|
2015-03-14 10:55:35 +01:00
|
|
|
|
2013-08-19 11:26:51 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-05-27 08:35:07 +02:00
|
|
|
void GUIFormSpecMenu::tryClose()
|
|
|
|
{
|
|
|
|
if (m_allowclose) {
|
|
|
|
doPause = false;
|
|
|
|
acceptInput(quit_mode_cancel);
|
|
|
|
quitMenu();
|
|
|
|
} else {
|
|
|
|
m_text_dst->gotText(L"MenuQuit");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
enum ButtonEventType : u8
|
|
|
|
{
|
|
|
|
BET_LEFT,
|
|
|
|
BET_RIGHT,
|
|
|
|
BET_MIDDLE,
|
|
|
|
BET_WHEEL_UP,
|
|
|
|
BET_WHEEL_DOWN,
|
|
|
|
BET_UP,
|
|
|
|
BET_DOWN,
|
|
|
|
BET_MOVE,
|
|
|
|
BET_OTHER
|
|
|
|
};
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
2010-12-22 02:34:21 +01:00
|
|
|
{
|
2015-06-16 20:33:07 +02:00
|
|
|
if (event.EventType==EET_KEY_INPUT_EVENT) {
|
2011-08-13 22:44:31 +02:00
|
|
|
KeyPress kp(event.KeyInput);
|
2017-04-15 07:16:57 +02:00
|
|
|
if (event.KeyInput.PressedDown && (
|
|
|
|
(kp == EscapeKey) || (kp == CancelKey) ||
|
|
|
|
((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) {
|
2016-05-27 08:35:07 +02:00
|
|
|
tryClose();
|
2010-12-22 02:34:21 +01:00
|
|
|
return true;
|
2017-08-20 19:37:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m_client != NULL && event.KeyInput.PressedDown &&
|
2015-06-16 20:33:07 +02:00
|
|
|
(kp == getKeySetting("keymap_screenshot"))) {
|
2017-06-19 00:00:55 +02:00
|
|
|
m_client->makeScreenshot();
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
2013-07-07 21:53:40 +02:00
|
|
|
if (event.KeyInput.PressedDown &&
|
|
|
|
(event.KeyInput.Key==KEY_RETURN ||
|
|
|
|
event.KeyInput.Key==KEY_UP ||
|
|
|
|
event.KeyInput.Key==KEY_DOWN)
|
|
|
|
) {
|
|
|
|
switch (event.KeyInput.Key) {
|
|
|
|
case KEY_RETURN:
|
2013-11-23 00:30:16 +01:00
|
|
|
current_keys_pending.key_enter = true;
|
2013-07-07 21:53:40 +02:00
|
|
|
break;
|
|
|
|
case KEY_UP:
|
|
|
|
current_keys_pending.key_up = true;
|
|
|
|
break;
|
|
|
|
case KEY_DOWN:
|
|
|
|
current_keys_pending.key_down = true;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//can't happen at all!
|
2015-03-06 11:21:51 +01:00
|
|
|
FATAL_ERROR("Reached a source line that can't ever been reached");
|
2013-07-07 21:53:40 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-11-23 00:30:16 +01:00
|
|
|
if (current_keys_pending.key_enter && m_allowclose) {
|
2014-03-04 19:57:39 +01:00
|
|
|
acceptInput(quit_mode_accept);
|
2013-11-23 00:30:16 +01:00
|
|
|
quitMenu();
|
2014-05-30 03:04:10 +02:00
|
|
|
} else {
|
2013-11-23 00:30:16 +01:00
|
|
|
acceptInput();
|
|
|
|
}
|
2012-07-15 18:19:38 +02:00
|
|
|
return true;
|
|
|
|
}
|
2013-07-07 21:53:40 +02:00
|
|
|
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
2014-07-06 16:17:46 +02:00
|
|
|
|
2014-10-08 04:40:14 +02:00
|
|
|
/* Mouse event other than movement, or crossing the border of inventory
|
|
|
|
field while holding right mouse button
|
|
|
|
*/
|
|
|
|
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
|
|
|
|
(event.MouseInput.Event != EMIE_MOUSE_MOVED ||
|
|
|
|
(event.MouseInput.Event == EMIE_MOUSE_MOVED &&
|
|
|
|
event.MouseInput.isRightPressed() &&
|
|
|
|
getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i))) {
|
2011-12-01 10:25:55 +01:00
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
// Get selected item and hovered/clicked item (s)
|
|
|
|
|
2014-06-24 12:28:24 +02:00
|
|
|
m_old_tooltip_id = -1;
|
2012-01-21 21:21:41 +01:00
|
|
|
updateSelectedItem();
|
2012-11-28 18:39:58 +01:00
|
|
|
ItemSpec s = getItemAtPos(m_pointer);
|
2011-12-01 10:32:51 +01:00
|
|
|
|
2012-01-13 12:35:55 +01:00
|
|
|
Inventory *inv_selected = NULL;
|
|
|
|
Inventory *inv_s = NULL;
|
2015-06-16 20:33:07 +02:00
|
|
|
InventoryList *list_s = NULL;
|
2012-01-21 21:21:41 +01:00
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (m_selected_item) {
|
2012-01-13 12:35:55 +01:00
|
|
|
inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
|
2015-03-06 11:21:51 +01:00
|
|
|
sanity_check(inv_selected);
|
|
|
|
sanity_check(inv_selected->getList(m_selected_item->listname) != NULL);
|
2012-01-13 12:35:55 +01:00
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
u32 s_count = 0;
|
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (s.isValid())
|
2014-05-30 03:04:10 +02:00
|
|
|
do { // breakable
|
2012-01-13 12:35:55 +01:00
|
|
|
inv_s = m_invmgr->getInventory(s.inventoryloc);
|
2012-06-01 23:33:51 +02:00
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (!inv_s) {
|
|
|
|
errorstream << "InventoryMenu: The selected inventory location "
|
|
|
|
<< "\"" << s.inventoryloc.dump() << "\" doesn't exist"
|
|
|
|
<< std::endl;
|
2012-06-01 23:33:51 +02:00
|
|
|
s.i = -1; // make it invalid again
|
|
|
|
break;
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
list_s = inv_s->getList(s.listname);
|
|
|
|
if (list_s == NULL) {
|
|
|
|
verbosestream << "InventoryMenu: The selected inventory list \""
|
|
|
|
<< s.listname << "\" does not exist" << std::endl;
|
2012-06-01 18:33:20 +02:00
|
|
|
s.i = -1; // make it invalid again
|
2012-06-01 23:33:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if ((u32)s.i >= list_s->getSize()) {
|
|
|
|
infostream << "InventoryMenu: The selected inventory list \""
|
|
|
|
<< s.listname << "\" is too small (i=" << s.i << ", size="
|
|
|
|
<< list_s->getSize() << ")" << std::endl;
|
2012-01-21 21:21:41 +01:00
|
|
|
s.i = -1; // make it invalid again
|
2012-06-01 23:33:51 +02:00
|
|
|
break;
|
2012-06-01 18:33:20 +02:00
|
|
|
}
|
2012-06-01 23:33:51 +02:00
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
s_count = list_s->getItem(s.i).count;
|
2014-05-30 03:04:10 +02:00
|
|
|
} while(0);
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
bool identical = m_selected_item && s.isValid() &&
|
2012-01-22 00:49:02 +01:00
|
|
|
(inv_selected == inv_s) &&
|
|
|
|
(m_selected_item->listname == s.listname) &&
|
|
|
|
(m_selected_item->i == s.i);
|
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
ButtonEventType button = BET_LEFT;
|
|
|
|
ButtonEventType updown = BET_OTHER;
|
2018-04-02 16:52:07 +02:00
|
|
|
switch (event.MouseInput.Event) {
|
|
|
|
case EMIE_LMOUSE_PRESSED_DOWN:
|
2018-04-03 11:15:58 +02:00
|
|
|
button = BET_LEFT; updown = BET_DOWN;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
case EMIE_RMOUSE_PRESSED_DOWN:
|
2018-04-03 11:15:58 +02:00
|
|
|
button = BET_RIGHT; updown = BET_DOWN;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
case EMIE_MMOUSE_PRESSED_DOWN:
|
2018-04-03 11:15:58 +02:00
|
|
|
button = BET_MIDDLE; updown = BET_DOWN;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
case EMIE_MOUSE_WHEEL:
|
2018-04-03 11:15:58 +02:00
|
|
|
button = (event.MouseInput.Wheel > 0) ?
|
|
|
|
BET_WHEEL_UP : BET_WHEEL_DOWN;
|
|
|
|
updown = BET_DOWN;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
case EMIE_LMOUSE_LEFT_UP:
|
2018-04-03 11:15:58 +02:00
|
|
|
button = BET_LEFT; updown = BET_UP;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
case EMIE_RMOUSE_LEFT_UP:
|
2018-04-03 11:15:58 +02:00
|
|
|
button = BET_RIGHT; updown = BET_UP;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
case EMIE_MMOUSE_LEFT_UP:
|
2018-04-03 11:15:58 +02:00
|
|
|
button = BET_MIDDLE; updown = BET_UP;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
case EMIE_MOUSE_MOVED:
|
2018-04-03 11:15:58 +02:00
|
|
|
updown = BET_MOVE;
|
2018-04-02 16:52:07 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
// Set this number to a positive value to generate a move action
|
|
|
|
// from m_selected_item to s.
|
|
|
|
u32 move_amount = 0;
|
|
|
|
|
2015-06-16 10:48:54 +02:00
|
|
|
// Set this number to a positive value to generate a move action
|
|
|
|
// from s to the next inventory ring.
|
|
|
|
u32 shift_move_amount = 0;
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
// Set this number to a positive value to generate a drop action
|
|
|
|
// from m_selected_item.
|
|
|
|
u32 drop_amount = 0;
|
|
|
|
|
|
|
|
// Set this number to a positive value to generate a craft action at s.
|
|
|
|
u32 craft_amount = 0;
|
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
switch (updown) {
|
|
|
|
case BET_DOWN:
|
2012-01-21 21:21:41 +01:00
|
|
|
// Some mouse button has been pressed
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
//infostream<<"Mouse button "<<button<<" pressed at p=("
|
|
|
|
// <<p.X<<","<<p.Y<<")"<<std::endl;
|
|
|
|
|
|
|
|
m_selected_dragging = false;
|
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (s.isValid() && s.listname == "craftpreview") {
|
2012-01-21 21:21:41 +01:00
|
|
|
// Craft preview has been clicked: craft
|
2018-04-03 11:15:58 +02:00
|
|
|
craft_amount = (button == BET_MIDDLE ? 10 : 1);
|
|
|
|
} else if (!m_selected_item) {
|
|
|
|
if (s_count && button != BET_WHEEL_UP) {
|
2015-06-16 10:48:54 +02:00
|
|
|
// Non-empty stack has been clicked: select or shift-move it
|
2012-01-21 21:21:41 +01:00
|
|
|
m_selected_item = new ItemSpec(s);
|
|
|
|
|
2015-06-16 10:48:54 +02:00
|
|
|
u32 count;
|
2018-04-03 11:15:58 +02:00
|
|
|
if (button == BET_RIGHT)
|
2015-06-16 10:48:54 +02:00
|
|
|
count = (s_count + 1) / 2;
|
2018-04-03 11:15:58 +02:00
|
|
|
else if (button == BET_MIDDLE)
|
2015-06-16 10:48:54 +02:00
|
|
|
count = MYMIN(s_count, 10);
|
2018-06-20 22:55:39 +02:00
|
|
|
else if (button == BET_WHEEL_DOWN)
|
2018-04-02 16:52:07 +02:00
|
|
|
count = 1;
|
2012-01-21 21:21:41 +01:00
|
|
|
else // left
|
2015-06-16 10:48:54 +02:00
|
|
|
count = s_count;
|
|
|
|
|
|
|
|
if (!event.MouseInput.Shift) {
|
|
|
|
// no shift: select item
|
|
|
|
m_selected_amount = count;
|
2018-04-03 11:15:58 +02:00
|
|
|
m_selected_dragging = button != BET_WHEEL_DOWN;
|
2017-10-17 21:50:58 +02:00
|
|
|
m_auto_place = false;
|
2015-06-16 10:48:54 +02:00
|
|
|
} else {
|
2018-04-02 16:52:07 +02:00
|
|
|
// shift pressed: move item, right click moves 1
|
2018-04-03 11:15:58 +02:00
|
|
|
shift_move_amount = button == BET_RIGHT ? 1 : count;
|
2015-06-16 10:48:54 +02:00
|
|
|
}
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
2015-06-16 20:33:07 +02:00
|
|
|
} else { // m_selected_item != NULL
|
2012-01-21 21:21:41 +01:00
|
|
|
assert(m_selected_amount >= 1);
|
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (s.isValid()) {
|
2012-01-22 00:49:02 +01:00
|
|
|
// Clicked a slot: move
|
2018-04-03 11:15:58 +02:00
|
|
|
if (button == BET_RIGHT || button == BET_WHEEL_UP)
|
2012-01-21 21:21:41 +01:00
|
|
|
move_amount = 1;
|
2018-04-03 11:15:58 +02:00
|
|
|
else if (button == BET_MIDDLE)
|
2012-01-21 21:21:41 +01:00
|
|
|
move_amount = MYMIN(m_selected_amount, 10);
|
2018-04-03 11:15:58 +02:00
|
|
|
else if (button == BET_LEFT)
|
2012-01-21 21:21:41 +01:00
|
|
|
move_amount = m_selected_amount;
|
2018-04-02 16:52:07 +02:00
|
|
|
// else wheeldown
|
2012-01-22 00:49:02 +01:00
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (identical) {
|
2018-04-03 11:15:58 +02:00
|
|
|
if (button == BET_WHEEL_DOWN) {
|
2018-04-02 16:52:07 +02:00
|
|
|
if (m_selected_amount < s_count)
|
|
|
|
++m_selected_amount;
|
|
|
|
} else {
|
|
|
|
if (move_amount >= m_selected_amount)
|
|
|
|
m_selected_amount = 0;
|
|
|
|
else
|
|
|
|
m_selected_amount -= move_amount;
|
|
|
|
move_amount = 0;
|
|
|
|
}
|
2012-01-22 00:49:02 +01:00
|
|
|
}
|
2018-04-03 11:15:58 +02:00
|
|
|
} else if (!getAbsoluteClippingRect().isPointInside(m_pointer)
|
|
|
|
&& button != BET_WHEEL_DOWN) {
|
2012-01-21 21:21:41 +01:00
|
|
|
// Clicked outside of the window: drop
|
2018-04-03 11:15:58 +02:00
|
|
|
if (button == BET_RIGHT || button == BET_WHEEL_UP)
|
2012-01-21 21:21:41 +01:00
|
|
|
drop_amount = 1;
|
2018-04-03 11:15:58 +02:00
|
|
|
else if (button == BET_MIDDLE)
|
2012-01-21 21:21:41 +01:00
|
|
|
drop_amount = MYMIN(m_selected_amount, 10);
|
|
|
|
else // left
|
|
|
|
drop_amount = m_selected_amount;
|
|
|
|
}
|
|
|
|
}
|
2018-04-03 11:15:58 +02:00
|
|
|
break;
|
|
|
|
case BET_UP:
|
2012-01-21 21:21:41 +01:00
|
|
|
// Some mouse button has been released
|
|
|
|
|
|
|
|
//infostream<<"Mouse button "<<button<<" released at p=("
|
|
|
|
// <<p.X<<","<<p.Y<<")"<<std::endl;
|
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
if (m_selected_dragging && m_selected_item) {
|
2018-04-02 16:52:07 +02:00
|
|
|
if (s.isValid()) {
|
|
|
|
if (!identical) {
|
|
|
|
// Dragged to different slot: move all selected
|
|
|
|
move_amount = m_selected_amount;
|
|
|
|
}
|
|
|
|
} else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
|
|
|
|
// Dragged outside of window: drop all selected
|
|
|
|
drop_amount = m_selected_amount;
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
m_selected_dragging = false;
|
2017-10-17 21:50:58 +02:00
|
|
|
// Keep track of whether the mouse button be released
|
|
|
|
// One click is drag without dropping. Click + release
|
|
|
|
// + click changes to drop item when moved mode
|
|
|
|
if (m_selected_item)
|
|
|
|
m_auto_place = true;
|
2018-04-03 11:15:58 +02:00
|
|
|
break;
|
|
|
|
case BET_MOVE:
|
2014-09-19 15:27:48 +02:00
|
|
|
// Mouse has been moved and rmb is down and mouse pointer just
|
|
|
|
// entered a new inventory field (checked in the entry-if, this
|
|
|
|
// is the only action here that is generated by mouse movement)
|
2019-05-25 17:37:54 +02:00
|
|
|
if (m_selected_item && s.isValid() && s.listname != "craftpreview") {
|
2014-09-19 15:27:48 +02:00
|
|
|
// Move 1 item
|
|
|
|
// TODO: middle mouse to move 10 items might be handy
|
2017-10-17 21:50:58 +02:00
|
|
|
if (m_auto_place) {
|
2014-10-09 09:53:20 +02:00
|
|
|
// Only move an item if the destination slot is empty
|
|
|
|
// or contains the same item type as what is going to be
|
|
|
|
// moved
|
|
|
|
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
|
2015-06-16 20:33:07 +02:00
|
|
|
InventoryList *list_to = list_s;
|
2014-10-09 09:53:20 +02:00
|
|
|
assert(list_from && list_to);
|
|
|
|
ItemStack stack_from = list_from->getItem(m_selected_item->i);
|
|
|
|
ItemStack stack_to = list_to->getItem(s.i);
|
|
|
|
if (stack_to.empty() || stack_to.name == stack_from.name)
|
|
|
|
move_amount = 1;
|
|
|
|
}
|
2014-09-19 15:27:48 +02:00
|
|
|
}
|
2018-04-03 11:15:58 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2014-09-19 15:27:48 +02:00
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
// Possibly send inventory action to server
|
2015-06-16 10:48:54 +02:00
|
|
|
if (move_amount > 0) {
|
2017-07-01 14:07:40 +02:00
|
|
|
// Send IAction::Move
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
assert(m_selected_item && m_selected_item->isValid());
|
|
|
|
assert(s.isValid());
|
|
|
|
|
|
|
|
assert(inv_selected && inv_s);
|
|
|
|
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
|
2015-06-16 20:33:07 +02:00
|
|
|
InventoryList *list_to = list_s;
|
2012-01-21 21:21:41 +01:00
|
|
|
assert(list_from && list_to);
|
|
|
|
ItemStack stack_from = list_from->getItem(m_selected_item->i);
|
|
|
|
ItemStack stack_to = list_to->getItem(s.i);
|
|
|
|
|
|
|
|
// Check how many items can be moved
|
|
|
|
move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
|
2017-01-09 20:39:22 +01:00
|
|
|
ItemStack leftover = stack_to.addItem(stack_from, m_client->idef());
|
2018-04-03 11:15:58 +02:00
|
|
|
bool move = true;
|
2012-09-02 21:51:38 +02:00
|
|
|
// If source stack cannot be added to destination stack at all,
|
|
|
|
// they are swapped
|
2018-04-03 11:15:58 +02:00
|
|
|
if (leftover.count == stack_from.count &&
|
|
|
|
leftover.name == stack_from.name) {
|
|
|
|
|
|
|
|
if (m_selected_swap.empty()) {
|
|
|
|
m_selected_amount = stack_to.count;
|
|
|
|
m_selected_dragging = false;
|
|
|
|
|
|
|
|
// WARNING: BLACK MAGIC, BUT IN A REDUCED SET
|
|
|
|
// Skip next validation checks due async inventory calls
|
|
|
|
m_selected_swap = stack_to;
|
|
|
|
} else {
|
|
|
|
move = false;
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
2012-09-02 21:51:38 +02:00
|
|
|
// Source stack goes fully into destination stack
|
2015-06-16 20:33:07 +02:00
|
|
|
else if (leftover.empty()) {
|
2012-01-22 00:49:02 +01:00
|
|
|
m_selected_amount -= move_amount;
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
2012-09-02 21:51:38 +02:00
|
|
|
// Source stack goes partly into destination stack
|
2014-05-30 03:04:10 +02:00
|
|
|
else {
|
2012-01-21 21:21:41 +01:00
|
|
|
move_amount -= leftover.count;
|
2012-01-22 00:49:02 +01:00
|
|
|
m_selected_amount -= move_amount;
|
2012-01-13 12:35:55 +01:00
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
2018-04-03 11:15:58 +02:00
|
|
|
if (move) {
|
|
|
|
infostream << "Handing IAction::Move to manager" << std::endl;
|
|
|
|
IMoveAction *a = new IMoveAction();
|
|
|
|
a->count = move_amount;
|
|
|
|
a->from_inv = m_selected_item->inventoryloc;
|
|
|
|
a->from_list = m_selected_item->listname;
|
|
|
|
a->from_i = m_selected_item->i;
|
|
|
|
a->to_inv = s.inventoryloc;
|
|
|
|
a->to_list = s.listname;
|
|
|
|
a->to_i = s.i;
|
|
|
|
m_invmgr->inventoryAction(a);
|
|
|
|
}
|
2015-06-16 10:48:54 +02:00
|
|
|
} else if (shift_move_amount > 0) {
|
|
|
|
u32 mis = m_inventory_rings.size();
|
|
|
|
u32 i = 0;
|
|
|
|
for (; i < mis; i++) {
|
|
|
|
const ListRingSpec &sp = m_inventory_rings[i];
|
|
|
|
if (sp.inventoryloc == s.inventoryloc
|
|
|
|
&& sp.listname == s.listname)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
if (i >= mis) // if not found
|
|
|
|
break;
|
|
|
|
u32 to_inv_ind = (i + 1) % mis;
|
|
|
|
const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
|
2015-06-16 20:33:07 +02:00
|
|
|
InventoryList *list_from = list_s;
|
|
|
|
if (!s.isValid())
|
2015-06-16 10:48:54 +02:00
|
|
|
break;
|
|
|
|
Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
|
|
|
|
if (!inv_to)
|
|
|
|
break;
|
|
|
|
InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
|
|
|
|
if (!list_to)
|
|
|
|
break;
|
|
|
|
ItemStack stack_from = list_from->getItem(s.i);
|
|
|
|
assert(shift_move_amount <= stack_from.count);
|
2018-04-03 11:15:58 +02:00
|
|
|
|
|
|
|
infostream << "Handing IAction::Move to manager" << std::endl;
|
|
|
|
IMoveAction *a = new IMoveAction();
|
|
|
|
a->count = shift_move_amount;
|
|
|
|
a->from_inv = s.inventoryloc;
|
|
|
|
a->from_list = s.listname;
|
|
|
|
a->from_i = s.i;
|
|
|
|
a->to_inv = to_inv_sp.inventoryloc;
|
|
|
|
a->to_list = to_inv_sp.listname;
|
|
|
|
a->move_somewhere = true;
|
|
|
|
m_invmgr->inventoryAction(a);
|
2015-06-16 10:48:54 +02:00
|
|
|
} while (0);
|
|
|
|
} else if (drop_amount > 0) {
|
2017-07-01 14:07:40 +02:00
|
|
|
// Send IAction::Drop
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
assert(m_selected_item && m_selected_item->isValid());
|
|
|
|
assert(inv_selected);
|
|
|
|
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
|
|
|
|
assert(list_from);
|
|
|
|
ItemStack stack_from = list_from->getItem(m_selected_item->i);
|
|
|
|
|
|
|
|
// Check how many items can be dropped
|
|
|
|
drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
|
|
|
|
assert(drop_amount > 0 && drop_amount <= m_selected_amount);
|
|
|
|
m_selected_amount -= drop_amount;
|
|
|
|
|
2017-07-01 14:07:40 +02:00
|
|
|
infostream << "Handing IAction::Drop to manager" << std::endl;
|
2012-01-21 21:21:41 +01:00
|
|
|
IDropAction *a = new IDropAction();
|
|
|
|
a->count = drop_amount;
|
|
|
|
a->from_inv = m_selected_item->inventoryloc;
|
|
|
|
a->from_list = m_selected_item->listname;
|
|
|
|
a->from_i = m_selected_item->i;
|
|
|
|
m_invmgr->inventoryAction(a);
|
2015-06-16 20:33:07 +02:00
|
|
|
} else if (craft_amount > 0) {
|
2012-01-21 21:21:41 +01:00
|
|
|
assert(s.isValid());
|
2018-01-03 17:28:57 +01:00
|
|
|
|
2017-12-06 17:32:05 +01:00
|
|
|
// if there are no items selected or the selected item
|
|
|
|
// belongs to craftresult list, proceed with crafting
|
|
|
|
if (m_selected_item == NULL ||
|
|
|
|
!m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
|
2018-01-03 17:28:57 +01:00
|
|
|
|
2017-12-06 17:32:05 +01:00
|
|
|
assert(inv_s);
|
|
|
|
|
|
|
|
// Send IACTION_CRAFT
|
|
|
|
infostream << "Handing IACTION_CRAFT to manager" << std::endl;
|
|
|
|
ICraftAction *a = new ICraftAction();
|
|
|
|
a->count = craft_amount;
|
|
|
|
a->craft_inv = s.inventoryloc;
|
|
|
|
m_invmgr->inventoryAction(a);
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If m_selected_amount has been decreased to zero, deselect
|
2015-06-16 20:33:07 +02:00
|
|
|
if (m_selected_amount == 0) {
|
2018-04-03 11:15:58 +02:00
|
|
|
m_selected_swap.clear();
|
2012-01-21 21:21:41 +01:00
|
|
|
delete m_selected_item;
|
|
|
|
m_selected_item = NULL;
|
2012-01-22 00:49:02 +01:00
|
|
|
m_selected_amount = 0;
|
2012-01-21 21:21:41 +01:00
|
|
|
m_selected_dragging = false;
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
2014-09-19 15:27:48 +02:00
|
|
|
m_old_pointer = m_pointer;
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2019-09-10 15:11:26 +02:00
|
|
|
if (event.EventType == EET_GUI_EVENT) {
|
2015-06-16 20:33:07 +02:00
|
|
|
if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED
|
2014-05-30 03:04:10 +02:00
|
|
|
&& isVisible()) {
|
2013-06-23 18:30:21 +02:00
|
|
|
// find the element that was clicked
|
2017-08-20 19:37:29 +02:00
|
|
|
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
|
2014-05-30 03:04:10 +02:00
|
|
|
if ((s.ftype == f_TabHeader) &&
|
|
|
|
(s.fid == event.GUIEvent.Caller->getID())) {
|
2013-06-23 18:30:21 +02:00
|
|
|
s.send = true;
|
|
|
|
acceptInput();
|
|
|
|
s.send = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-16 20:33:07 +02:00
|
|
|
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
|
2014-05-30 03:04:10 +02:00
|
|
|
&& isVisible()) {
|
2015-06-16 20:33:07 +02:00
|
|
|
if (!canTakeFocus(event.GUIEvent.Element)) {
|
2012-07-15 18:19:38 +02:00
|
|
|
infostream<<"GUIFormSpecMenu: Not allowing focus change."
|
2010-12-22 02:34:21 +01:00
|
|
|
<<std::endl;
|
|
|
|
// Returning true disables focus change
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2015-06-16 20:33:07 +02:00
|
|
|
if ((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
|
2014-06-14 12:27:56 +02:00
|
|
|
(event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
|
2014-06-19 18:17:35 +02:00
|
|
|
(event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
|
|
|
|
(event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
|
2013-06-23 18:30:21 +02:00
|
|
|
unsigned int btn_id = event.GUIEvent.Caller->getID();
|
|
|
|
|
|
|
|
if (btn_id == 257) {
|
2013-10-26 09:56:38 +02:00
|
|
|
if (m_allowclose) {
|
2014-03-04 19:57:39 +01:00
|
|
|
acceptInput(quit_mode_accept);
|
2013-06-23 18:30:21 +02:00
|
|
|
quitMenu();
|
2013-10-26 09:56:38 +02:00
|
|
|
} else {
|
|
|
|
acceptInput();
|
2015-06-10 01:54:33 +02:00
|
|
|
m_text_dst->gotText(L"ExitButton");
|
2013-10-26 09:56:38 +02:00
|
|
|
}
|
2012-07-15 18:19:38 +02:00
|
|
|
// quitMenu deallocates menu
|
|
|
|
return true;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
// find the element that was clicked
|
2017-08-20 19:37:29 +02:00
|
|
|
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
|
2013-12-11 23:07:38 +01:00
|
|
|
// if its a button, set the send field so
|
2012-07-15 18:19:38 +02:00
|
|
|
// lua knows which button was pressed
|
2017-08-20 19:37:29 +02:00
|
|
|
if ((s.ftype == f_Button || s.ftype == f_CheckBox) &&
|
|
|
|
s.fid == event.GUIEvent.Caller->getID()) {
|
2012-07-15 18:19:38 +02:00
|
|
|
s.send = true;
|
2015-06-16 20:33:07 +02:00
|
|
|
if (s.is_exit) {
|
2013-10-26 09:56:38 +02:00
|
|
|
if (m_allowclose) {
|
2014-03-04 19:57:39 +01:00
|
|
|
acceptInput(quit_mode_accept);
|
2013-06-23 18:30:21 +02:00
|
|
|
quitMenu();
|
2013-10-26 09:56:38 +02:00
|
|
|
} else {
|
2015-06-10 01:54:33 +02:00
|
|
|
m_text_dst->gotText(L"ExitButton");
|
2013-10-26 09:56:38 +02:00
|
|
|
}
|
2012-07-22 16:28:09 +02:00
|
|
|
return true;
|
|
|
|
}
|
2017-08-20 19:37:29 +02:00
|
|
|
|
|
|
|
acceptInput(quit_mode_no);
|
|
|
|
s.send = false;
|
|
|
|
return true;
|
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
} else if ((s.ftype == f_DropDown) &&
|
2014-06-14 12:27:56 +02:00
|
|
|
(s.fid == event.GUIEvent.Caller->getID())) {
|
|
|
|
// only send the changed dropdown
|
2017-08-20 19:37:29 +02:00
|
|
|
for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) {
|
2014-06-14 12:27:56 +02:00
|
|
|
if (s2.ftype == f_DropDown) {
|
|
|
|
s2.send = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.send = true;
|
|
|
|
acceptInput(quit_mode_no);
|
|
|
|
|
|
|
|
// revert configuration to make sure dropdowns are sent on
|
|
|
|
// regular button click
|
2017-08-20 19:37:29 +02:00
|
|
|
for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) {
|
2014-06-14 12:27:56 +02:00
|
|
|
if (s2.ftype == f_DropDown) {
|
|
|
|
s2.send = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2015-06-16 20:33:07 +02:00
|
|
|
} else if ((s.ftype == f_ScrollBar) &&
|
|
|
|
(s.fid == event.GUIEvent.Caller->getID())) {
|
2014-06-19 18:17:35 +02:00
|
|
|
s.fdefault = L"Changed";
|
|
|
|
acceptInput(quit_mode_no);
|
|
|
|
s.fdefault = L"";
|
2019-09-10 15:11:26 +02:00
|
|
|
} else if ((s.ftype == f_Unknown) &&
|
|
|
|
(s.fid == event.GUIEvent.Caller->getID())) {
|
|
|
|
s.send = true;
|
|
|
|
acceptInput();
|
|
|
|
s.send = false;
|
2014-06-19 18:17:35 +02:00
|
|
|
}
|
2012-07-15 18:19:38 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-19 18:17:35 +02:00
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
|
|
|
|
if (event.GUIEvent.Caller->getID() > 257) {
|
2016-08-07 17:22:50 +02:00
|
|
|
bool close_on_enter = true;
|
2017-08-20 19:37:29 +02:00
|
|
|
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
|
2016-08-07 17:01:00 +02:00
|
|
|
if (s.ftype == f_Unknown &&
|
|
|
|
s.fid == event.GUIEvent.Caller->getID()) {
|
|
|
|
current_field_enter_pending = s.fname;
|
2017-06-04 21:00:04 +02:00
|
|
|
std::unordered_map<std::string, bool>::const_iterator it =
|
2016-10-03 01:30:33 +02:00
|
|
|
field_close_on_enter.find(s.fname);
|
|
|
|
if (it != field_close_on_enter.end())
|
|
|
|
close_on_enter = (*it).second;
|
|
|
|
|
2016-08-07 17:22:50 +02:00
|
|
|
break;
|
2016-08-07 17:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
2013-07-07 21:53:40 +02:00
|
|
|
|
2016-08-07 17:22:50 +02:00
|
|
|
if (m_allowclose && close_on_enter) {
|
2016-08-07 17:01:00 +02:00
|
|
|
current_keys_pending.key_enter = true;
|
2014-03-04 19:57:39 +01:00
|
|
|
acceptInput(quit_mode_accept);
|
2013-06-23 18:30:21 +02:00
|
|
|
quitMenu();
|
2014-05-30 03:04:10 +02:00
|
|
|
} else {
|
2013-07-07 21:53:40 +02:00
|
|
|
current_keys_pending.key_enter = true;
|
|
|
|
acceptInput();
|
|
|
|
}
|
2012-07-15 18:19:38 +02:00
|
|
|
// quitMenu deallocates menu
|
|
|
|
return true;
|
|
|
|
}
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2015-06-16 20:33:07 +02:00
|
|
|
if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
|
2013-06-23 18:30:21 +02:00
|
|
|
int current_id = event.GUIEvent.Caller->getID();
|
2015-06-16 20:33:07 +02:00
|
|
|
if (current_id > 257) {
|
2013-06-23 18:30:21 +02:00
|
|
|
// find the element that was clicked
|
2017-08-20 19:37:29 +02:00
|
|
|
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
|
2013-08-23 12:24:11 +02:00
|
|
|
// if it's a table, set the send field
|
|
|
|
// so lua knows which table was changed
|
2014-05-30 03:04:10 +02:00
|
|
|
if ((s.ftype == f_Table) && (s.fid == current_id)) {
|
2013-06-23 18:30:21 +02:00
|
|
|
s.send = true;
|
2013-08-15 21:46:55 +02:00
|
|
|
acceptInput();
|
2013-06-23 18:30:21 +02:00
|
|
|
s.send=false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2010-12-22 02:34:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return Parent ? Parent->OnEvent(event) : false;
|
|
|
|
}
|
|
|
|
|
2014-04-21 14:10:59 +02:00
|
|
|
/**
|
|
|
|
* get name of element by element id
|
|
|
|
* @param id of element
|
|
|
|
* @return name string or empty string
|
|
|
|
*/
|
2015-06-10 01:54:33 +02:00
|
|
|
std::string GUIFormSpecMenu::getNameByID(s32 id)
|
2014-04-21 14:10:59 +02:00
|
|
|
{
|
2017-08-20 19:37:29 +02:00
|
|
|
for (FieldSpec &spec : m_fields) {
|
2019-11-07 20:11:01 +01:00
|
|
|
if (spec.fid == id)
|
2017-08-20 19:37:29 +02:00
|
|
|
return spec.fname;
|
2014-04-21 14:10:59 +02:00
|
|
|
}
|
2015-06-10 01:54:33 +02:00
|
|
|
return "";
|
2014-04-21 14:10:59 +02:00
|
|
|
}
|
|
|
|
|
2019-11-07 20:11:01 +01:00
|
|
|
|
2019-11-20 19:39:10 +01:00
|
|
|
const GUIFormSpecMenu::FieldSpec *GUIFormSpecMenu::getSpecByID(s32 id)
|
2019-11-07 20:11:01 +01:00
|
|
|
{
|
|
|
|
for (FieldSpec &spec : m_fields) {
|
|
|
|
if (spec.fid == id)
|
2019-11-20 19:39:10 +01:00
|
|
|
return &spec;
|
2019-11-07 20:11:01 +01:00
|
|
|
}
|
2019-11-20 19:39:10 +01:00
|
|
|
return nullptr;
|
2019-11-07 20:11:01 +01:00
|
|
|
}
|
|
|
|
|
2014-04-21 14:10:59 +02:00
|
|
|
/**
|
|
|
|
* get label of element by id
|
|
|
|
* @param id of element
|
|
|
|
* @return label string or empty string
|
|
|
|
*/
|
|
|
|
std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
|
|
|
|
{
|
2017-08-20 19:37:29 +02:00
|
|
|
for (FieldSpec &spec : m_fields) {
|
2019-11-07 20:11:01 +01:00
|
|
|
if (spec.fid == id)
|
2017-08-20 19:37:29 +02:00
|
|
|
return spec.flabel;
|
2014-04-21 14:10:59 +02:00
|
|
|
}
|
|
|
|
return L"";
|
|
|
|
}
|
2019-03-15 20:03:12 +01:00
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type,
|
|
|
|
const std::string &name, const std::string &parent_type) {
|
2019-03-15 20:03:12 +01:00
|
|
|
StyleSpec ret;
|
|
|
|
|
2019-03-16 22:38:36 +01:00
|
|
|
if (!parent_type.empty()) {
|
|
|
|
auto it = theme_by_type.find(parent_type);
|
|
|
|
if (it != theme_by_type.end()) {
|
|
|
|
ret |= it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 20:03:12 +01:00
|
|
|
auto it = theme_by_type.find(type);
|
|
|
|
if (it != theme_by_type.end()) {
|
|
|
|
ret |= it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
it = theme_by_name.find(name);
|
|
|
|
if (it != theme_by_name.end()) {
|
|
|
|
ret |= it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|