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 <sstream>
|
|
|
|
#include <limits>
|
2012-07-15 18:19:38 +02:00
|
|
|
#include "guiFormSpecMenu.h"
|
2013-08-23 12:24:11 +02:00
|
|
|
#include "guiTable.h"
|
2010-12-22 02:34:21 +01:00
|
|
|
#include "constants.h"
|
2012-01-12 06:10:39 +01:00
|
|
|
#include "gamedef.h"
|
2011-05-14 14:43:26 +02:00
|
|
|
#include "keycode.h"
|
2011-06-18 01:00:01 +02:00
|
|
|
#include "strfnd.h"
|
2011-10-12 12:53:38 +02:00
|
|
|
#include <IGUICheckBox.h>
|
|
|
|
#include <IGUIEditBox.h>
|
|
|
|
#include <IGUIButton.h>
|
|
|
|
#include <IGUIStaticText.h>
|
|
|
|
#include <IGUIFont.h>
|
2013-06-23 18:30:21 +02:00
|
|
|
#include <IGUITabControl.h>
|
|
|
|
#include <IGUIComboBox.h>
|
2011-10-17 00:03:45 +02:00
|
|
|
#include "log.h"
|
2012-06-03 16:18:08 +02:00
|
|
|
#include "tile.h" // ITextureSource
|
2013-08-11 04:09:45 +02:00
|
|
|
#include "hud.h" // drawItemStack
|
2013-11-09 22:49:27 +01:00
|
|
|
#include "hex.h"
|
2012-06-17 01:40:36 +02:00
|
|
|
#include "util/string.h"
|
|
|
|
#include "util/numeric.h"
|
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"
|
2014-04-29 17:47:34 +02:00
|
|
|
#include "scripting_game.h"
|
2014-05-30 03:07:48 +02:00
|
|
|
#include "porting.h"
|
2014-06-14 11:22:09 +02:00
|
|
|
#include "main.h"
|
|
|
|
#include "settings.h"
|
2014-09-21 02:23:55 +02:00
|
|
|
#include "client.h"
|
2014-10-03 06:11:21 +02:00
|
|
|
#include "util/string.h" // for parseColorString()
|
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
|
|
|
#include "fontengine.h"
|
2012-07-15 18:19:38 +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) { \
|
|
|
|
errorstream<< "Invalid pos for element " << a << "specified: \"" \
|
|
|
|
<< parts[b] << "\"" << std::endl; \
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2012-11-28 18:39:58 +01:00
|
|
|
GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
|
2014-03-04 19:57:39 +01:00
|
|
|
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
|
|
|
|
InventoryManager *invmgr, IGameDef *gamedef,
|
2014-04-20 02:40:25 +02:00
|
|
|
ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst,
|
2014-10-24 16:22:05 +02:00
|
|
|
Client* client) :
|
2012-11-28 18:39:58 +01:00
|
|
|
GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
|
|
|
|
m_device(dev),
|
2011-11-13 23:19:48 +01:00
|
|
|
m_invmgr(invmgr),
|
2012-06-03 17:30:34 +02:00
|
|
|
m_gamedef(gamedef),
|
2013-08-20 22:38:14 +02:00
|
|
|
m_tsrc(tsrc),
|
2014-09-21 21:18:43 +02:00
|
|
|
m_client(client),
|
2012-06-03 17:30:34 +02:00
|
|
|
m_selected_item(NULL),
|
|
|
|
m_selected_amount(0),
|
|
|
|
m_selected_dragging(false),
|
2013-06-23 18:30:21 +02:00
|
|
|
m_tooltip_element(NULL),
|
2014-10-30 07:53:20 +01:00
|
|
|
m_hovered_time(0),
|
2014-07-06 16:17:46 +02:00
|
|
|
m_old_tooltip_id(-1),
|
2014-10-09 09:53:20 +02:00
|
|
|
m_rmouse_auto_place(false),
|
2013-06-23 18:30:21 +02:00
|
|
|
m_allowclose(true),
|
2014-04-20 02:40:25 +02:00
|
|
|
m_lock(false),
|
|
|
|
m_form_src(fsrc),
|
|
|
|
m_text_dst(tdst),
|
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_formspec_version(0),
|
|
|
|
m_font(NULL)
|
2014-04-21 14:10:59 +02:00
|
|
|
#ifdef __ANDROID__
|
|
|
|
,m_JavaDialogFieldName(L"")
|
|
|
|
#endif
|
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");
|
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
|
|
|
|
2014-06-20 19:50:47 +02:00
|
|
|
for (u32 i = 0; i < m_tables.size(); ++i) {
|
|
|
|
GUITable *table = m_tables[i].second;
|
|
|
|
table->drop();
|
|
|
|
}
|
2010-12-25 15:04:51 +01:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
delete m_selected_item;
|
2014-04-20 02:40:25 +02:00
|
|
|
|
|
|
|
if (m_form_src != NULL) {
|
|
|
|
delete m_form_src;
|
|
|
|
}
|
|
|
|
if (m_text_dst != NULL) {
|
|
|
|
delete m_text_dst;
|
|
|
|
}
|
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
|
|
|
|
|
|
|
while(!children.empty()) {
|
|
|
|
(*children.getLast())->remove();
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
2013-08-25 10:48:29 +02:00
|
|
|
|
2014-05-30 03:04:10 +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();
|
2013-06-23 18:30:21 +02:00
|
|
|
m_tooltip_element = NULL;
|
|
|
|
}
|
2013-08-25 10:48:29 +02:00
|
|
|
|
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
|
|
|
|
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
|
|
|
it != children.end(); ++it) {
|
|
|
|
if ((*it)->getType() == gui::EGUIET_EDIT_BOX
|
|
|
|
&& (*it)->getText()[0] == 0) {
|
|
|
|
Environment->setFocus(*it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. first editbox
|
|
|
|
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
|
|
|
it != children.end(); ++it) {
|
|
|
|
if ((*it)->getType() == gui::EGUIET_EDIT_BOX) {
|
|
|
|
Environment->setFocus(*it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
// 3. first table
|
2013-08-19 11:26:51 +02:00
|
|
|
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
|
|
|
it != children.end(); ++it) {
|
2013-08-23 12:24:11 +02:00
|
|
|
if ((*it)->getTypeName() == std::string("GUITable")) {
|
2013-08-19 11:26:51 +02:00
|
|
|
Environment->setFocus(*it);
|
|
|
|
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)
|
|
|
|
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
|
|
|
it != children.end(); ++it) {
|
|
|
|
if ((*it)->getType() != gui::EGUIET_STATIC_TEXT &&
|
|
|
|
(*it)->getType() != gui::EGUIET_TAB_CONTROL) {
|
|
|
|
Environment->setFocus(*it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 6. first child element
|
|
|
|
if (children.empty())
|
|
|
|
Environment->setFocus(this);
|
|
|
|
else
|
|
|
|
Environment->setFocus(*(children.begin()));
|
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
GUITable* GUIFormSpecMenu::getTable(std::wstring tablename)
|
2013-08-15 21:46:55 +02:00
|
|
|
{
|
2013-08-23 12:24:11 +02:00
|
|
|
for (u32 i = 0; i < m_tables.size(); ++i) {
|
|
|
|
if (tablename == m_tables[i].first.fname)
|
|
|
|
return m_tables[i].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
|
|
|
}
|
|
|
|
|
2013-08-14 20:21:39 +02:00
|
|
|
std::vector<std::string> split(const std::string &s, char delim) {
|
2013-06-23 18:30:21 +02:00
|
|
|
std::vector<std::string> tokens;
|
|
|
|
|
2013-08-14 20:21:39 +02:00
|
|
|
std::string current = "";
|
|
|
|
bool last_was_escape = false;
|
|
|
|
for(unsigned int i=0; i < s.size(); i++) {
|
|
|
|
if (last_was_escape) {
|
|
|
|
current += '\\';
|
|
|
|
current += s.c_str()[i];
|
|
|
|
last_was_escape = false;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
2013-08-14 20:21:39 +02:00
|
|
|
else {
|
|
|
|
if (s.c_str()[i] == delim) {
|
|
|
|
tokens.push_back(current);
|
|
|
|
current = "";
|
2013-07-07 21:53:40 +02:00
|
|
|
last_was_escape = false;
|
|
|
|
}
|
2013-08-14 20:21:39 +02:00
|
|
|
else if (s.c_str()[i] == '\\'){
|
|
|
|
last_was_escape = true;
|
|
|
|
}
|
2013-07-07 21:53:40 +02:00
|
|
|
else {
|
2013-08-14 20:21:39 +02:00
|
|
|
current += s.c_str()[i];
|
|
|
|
last_was_escape = false;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-14 20:21:39 +02:00
|
|
|
//push last element
|
|
|
|
tokens.push_back(current);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
return tokens;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
|
|
|
|
{
|
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);
|
|
|
|
if (parts.size() == 3) {
|
|
|
|
if (parts[2] == "true") {
|
|
|
|
lockSize(true,v2u32(800,600));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseList(parserData* data,std::string element)
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
if (m_gamedef == 0) {
|
|
|
|
errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
|
|
|
|
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],',');
|
|
|
|
std::string startindex = "";
|
|
|
|
if (parts.size() == 5)
|
|
|
|
startindex = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS("list",2);
|
|
|
|
MY_CHECKGEOM("list",3);
|
|
|
|
|
|
|
|
InventoryLocation loc;
|
|
|
|
|
|
|
|
if(location == "context" || location == "current_name")
|
|
|
|
loc = m_current_inventory_location;
|
|
|
|
else
|
|
|
|
loc.deSerialize(location);
|
|
|
|
|
|
|
|
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = stoi(v_geom[0]);
|
|
|
|
geom.Y = stoi(v_geom[1]);
|
|
|
|
|
|
|
|
s32 start_i = 0;
|
|
|
|
if(startindex != "")
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
|
|
|
|
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
|
|
|
|
{
|
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];
|
|
|
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float) spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
|
|
|
|
|
|
|
|
bool fselected = false;
|
|
|
|
|
|
|
|
if (selected == "true")
|
|
|
|
fselected = true;
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring wlabel = narrow_to_wide(label);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-06-18 22:38:29 +02:00
|
|
|
core::rect<s32> rect = core::rect<s32>(
|
2014-06-14 11:22:09 +02:00
|
|
|
pos.X, pos.Y + ((imgsize.Y/2) - 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
|
|
|
pos.X + m_font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
|
2014-06-14 11:22:09 +02:00
|
|
|
pos.Y + ((imgsize.Y/2) + m_btn_height));
|
2014-06-18 22:38:29 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(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
|
|
|
|
2013-06-23 18:30:21 +02: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
|
|
|
|
|
|
|
if (spec.fname == data->focused_fieldname) {
|
|
|
|
Environment->setFocus(e);
|
|
|
|
}
|
2014-06-24 12:28:24 +02:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
m_checkboxes.push_back(std::pair<FieldSpec,gui::IGUICheckBox*>(spec,e));
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-06-19 18:17:35 +02:00
|
|
|
void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
|
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
|
|
|
if (parts.size() >= 5) {
|
|
|
|
std::vector<std::string> v_pos = split(parts[0],',');
|
|
|
|
std::vector<std::string> v_dim = split(parts[1],',');
|
|
|
|
std::string name = parts[2];
|
|
|
|
std::string value = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS("scrollbar",0);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float) spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
|
|
|
|
|
|
|
|
if (v_dim.size() != 2) {
|
|
|
|
errorstream<< "Invalid size for element " << "scrollbar"
|
|
|
|
<< "specified: \"" << parts[1] << "\"" << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
v2s32 dim;
|
|
|
|
dim.X = stof(v_dim[0]) * (float) spacing.X;
|
|
|
|
dim.Y = stof(v_dim[1]) * (float) spacing.Y;
|
|
|
|
|
|
|
|
core::rect<s32> rect =
|
|
|
|
core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
|
|
|
|
|
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(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;
|
|
|
|
gui::IGUIScrollBar* e =
|
|
|
|
Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
|
|
|
|
|
|
|
|
e->setMax(1000);
|
|
|
|
e->setMin(0);
|
|
|
|
e->setPos(stoi(parts[4]));
|
|
|
|
e->setSmallStep(10);
|
|
|
|
e->setLargeStep(100);
|
|
|
|
|
|
|
|
m_scrollbars.push_back(std::pair<FieldSpec,gui::IGUIScrollBar*>(spec,e));
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
|
|
|
|
{
|
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
|
|
|
|
|
|
|
MY_CHECKPOS("image",0);
|
|
|
|
MY_CHECKGEOM("image",1);
|
|
|
|
|
|
|
|
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
|
|
|
|
pos.X += stof(v_pos[0]) * (float) spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
2013-08-09 20:53:11 +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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
|
|
|
|
m_images.push_back(ImageDrawSpec(name, pos, geom));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parts.size() == 2) {
|
|
|
|
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
|
|
|
|
|
|
|
MY_CHECKPOS("image",0);
|
|
|
|
|
|
|
|
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
|
|
|
|
pos.X += stof(v_pos[0]) * (float) spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float) spacing.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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
|
|
|
|
m_images.push_back(ImageDrawSpec(name, pos));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
|
|
|
|
{
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
|
|
|
|
pos.X += stof(v_pos[0]) * (float) spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
2014-07-02 16:03:27 +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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
|
|
|
|
m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
|
|
|
|
std::string type)
|
|
|
|
{
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
|
|
|
|
pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
|
|
|
|
|
2014-06-14 11:22:09 +02:00
|
|
|
core::rect<s32> rect =
|
|
|
|
core::rect<s32>(pos.X, pos.Y - m_btn_height,
|
|
|
|
pos.X + geom.X, pos.Y + 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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
|
|
|
|
|
|
|
|
label = unescape_string(label);
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring wlabel = narrow_to_wide(label);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(name),
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
spec.ftype = f_Button;
|
|
|
|
if(type == "button_exit")
|
|
|
|
spec.is_exit = true;
|
2013-08-19 11:26:51 +02:00
|
|
|
gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid,
|
|
|
|
spec.flabel.c_str());
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
|
|
|
|
{
|
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() == 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],',');
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = stof(v_geom[0]) * (float)spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
|
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
if (parts.size() == 4) {
|
|
|
|
m_clipbackground = is_yes(parts[3]);
|
|
|
|
if (m_clipbackground) {
|
|
|
|
pos.X = stoi(v_pos[0]); //acts as offset
|
|
|
|
pos.Y = stoi(v_pos[1]); //acts as offset
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
|
|
|
|
m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseTableOptions(parserData* data,std::string element)
|
|
|
|
{
|
2013-08-23 12:24:11 +02:00
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
|
|
|
|
data->table_options.clear();
|
|
|
|
for (size_t i = 0; i < parts.size(); ++i) {
|
|
|
|
// Parse table option
|
|
|
|
std::string opt = unescape_string(parts[i]);
|
|
|
|
data->table_options.push_back(GUITable::splitOption(opt));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element)
|
|
|
|
{
|
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();
|
|
|
|
for (size_t i = 0; i < parts.size(); ++i) {
|
|
|
|
std::vector<std::string> col_parts = split(parts[i],',');
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
|
|
|
|
{
|
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],',');
|
2013-06-23 18:30:21 +02:00
|
|
|
std::string str_initial_selection = "";
|
|
|
|
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
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = stof(v_geom[0]) * (float)spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
|
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring fname_w = narrow_to_wide(name);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2013-06-23 18:30:21 +02:00
|
|
|
fname_w,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
spec.ftype = f_Table;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
for (unsigned int i = 0; i < items.size(); ++i) {
|
|
|
|
items[i] = unescape_string(items[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
//now really show table
|
|
|
|
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
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
|
|
|
|
e->setDynamicData(data->table_dyndata[fname_w]);
|
|
|
|
}
|
2013-07-07 21:53:40 +02:00
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
if ((str_initial_selection != "") &&
|
|
|
|
(str_initial_selection != "0"))
|
|
|
|
e->setSelected(stoi(str_initial_selection.c_str()));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
m_tables.push_back(std::pair<FieldSpec,GUITable*>(spec, e));
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
|
|
|
|
{
|
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],',');
|
|
|
|
std::string str_initial_selection = "";
|
|
|
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = stof(v_geom[0]) * (float)spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
|
|
|
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring fname_w = narrow_to_wide(name);
|
2013-08-23 12:24:11 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2013-08-23 12:24:11 +02:00
|
|
|
fname_w,
|
|
|
|
L"",
|
|
|
|
L"",
|
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
spec.ftype = f_Table;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < items.size(); ++i) {
|
|
|
|
items[i] = unescape_string(items[i]);
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
//now really show list
|
|
|
|
GUITable *e = new GUITable(Environment, this, spec.fid, rect,
|
|
|
|
m_tsrc);
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
|
|
|
|
e->setDynamicData(data->table_dyndata[fname_w]);
|
2013-11-10 19:37:45 +01:00
|
|
|
}
|
2013-08-16 00:54:38 +02:00
|
|
|
|
2013-11-10 19:37:45 +01:00
|
|
|
if ((str_initial_selection != "") &&
|
|
|
|
(str_initial_selection != "0"))
|
2013-08-23 12:24:11 +02:00
|
|
|
e->setSelected(stoi(str_initial_selection.c_str()));
|
2013-07-07 21:53:40 +02:00
|
|
|
|
2013-08-23 12:24:11 +02:00
|
|
|
m_tables.push_back(std::pair<FieldSpec,GUITable*>(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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
|
|
|
|
{
|
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],',');
|
|
|
|
std::string str_initial_selection = "";
|
|
|
|
str_initial_selection = parts[4];
|
|
|
|
|
|
|
|
MY_CHECKPOS("dropdown",0);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
s32 width = stof(parts[1]) * (float)spacing.Y;
|
|
|
|
|
2014-06-14 11:22:09 +02:00
|
|
|
core::rect<s32> 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
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring fname_w = narrow_to_wide(name);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2013-06-23 18:30:21 +02:00
|
|
|
fname_w,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
spec.ftype = f_DropDown;
|
|
|
|
spec.send = true;
|
|
|
|
|
|
|
|
//now really show list
|
|
|
|
gui::IGUIComboBox *e = Environment->addComboBox(rect, 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
|
|
|
|
|
|
|
for (unsigned int i=0; i < items.size(); i++) {
|
|
|
|
e->addItem(narrow_to_wide(items[i]).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (str_initial_selection != "")
|
|
|
|
e->setSelected(stoi(str_initial_selection.c_str())-1);
|
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream << "Invalid dropdown element(" << parts.size() << "): '"
|
|
|
|
<< element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
|
|
|
|
{
|
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("pwdfield",0);
|
|
|
|
MY_CHECKGEOM("pwdfield",1);
|
|
|
|
|
|
|
|
v2s32 pos;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
|
|
|
|
|
|
|
|
pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
|
2014-06-14 11:22:09 +02:00
|
|
|
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);
|
|
|
|
|
|
|
|
label = unescape_string(label);
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring wlabel = narrow_to_wide(label);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(name),
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
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
|
|
|
|
2013-12-17 01:13:16 +01:00
|
|
|
if (label.length() >= 1)
|
2013-06-23 18:30:21 +02:00
|
|
|
{
|
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;
|
2013-06-23 18:30:21 +02:00
|
|
|
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
e->setPasswordBox(true,L'*');
|
|
|
|
|
|
|
|
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;
|
|
|
|
evt.KeyInput.Control = 0;
|
|
|
|
evt.KeyInput.Shift = 0;
|
2013-06-23 18:30:21 +02:00
|
|
|
evt.KeyInput.PressedDown = true;
|
|
|
|
e->OnEvent(evt);
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseSimpleField(parserData* data,
|
|
|
|
std::vector<std::string> &parts)
|
|
|
|
{
|
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;
|
|
|
|
|
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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
|
|
|
|
|
|
|
|
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
|
|
|
|
pos.Y = ((m_fields.size()+2)*60);
|
|
|
|
v2s32 size = DesiredRect.getSize();
|
|
|
|
|
2014-06-14 11:22:09 +02: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
|
|
|
|
|
|
|
|
|
|
|
if(m_form_src)
|
|
|
|
default_val = m_form_src->resolveText(default_val);
|
|
|
|
|
|
|
|
default_val = unescape_string(default_val);
|
|
|
|
label = unescape_string(label);
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring wlabel = narrow_to_wide(label);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(name),
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(default_val),
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
if (name == "")
|
|
|
|
{
|
|
|
|
// spec field id to 0, this stops submit searching for a value that isn't there
|
|
|
|
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spec.send = true;
|
2014-05-30 03:04:10 +02:00
|
|
|
gui::IGUIEditBox *e =
|
|
|
|
Environment->addEditBox(spec.fdefault.c_str(), 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
|
|
|
|
|
|
|
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;
|
|
|
|
evt.KeyInput.Control = 0;
|
|
|
|
evt.KeyInput.Shift = 0;
|
2013-06-23 18:30:21 +02:00
|
|
|
evt.KeyInput.PressedDown = true;
|
|
|
|
e->OnEvent(evt);
|
|
|
|
|
2013-12-17 01:13:16 +01:00
|
|
|
if (label.length() >= 1)
|
2013-06-23 18:30:21 +02:00
|
|
|
{
|
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;
|
2013-06-23 18:30:21 +02:00
|
|
|
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseTextArea(parserData* data,
|
|
|
|
std::vector<std::string>& parts,std::string type)
|
|
|
|
{
|
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);
|
|
|
|
|
|
|
|
v2s32 pos;
|
|
|
|
pos.X = stof(v_pos[0]) * (float) spacing.X;
|
|
|
|
pos.Y = stof(v_pos[1]) * (float) spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
|
|
|
|
geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
|
|
|
|
|
|
|
|
if (type == "textarea")
|
|
|
|
{
|
|
|
|
geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
|
2014-06-14 11:22:09 +02:00
|
|
|
pos.Y += m_btn_height;
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
|
2014-06-14 11:22:09 +02:00
|
|
|
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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
|
|
|
|
|
|
|
|
if(m_form_src)
|
|
|
|
default_val = m_form_src->resolveText(default_val);
|
|
|
|
|
|
|
|
|
|
|
|
default_val = unescape_string(default_val);
|
|
|
|
label = unescape_string(label);
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring wlabel = narrow_to_wide(label);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(name),
|
2013-06-23 18:30:21 +02:00
|
|
|
wlabel,
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(default_val),
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
if (name == "")
|
|
|
|
{
|
|
|
|
// spec field id to 0, this stops submit searching for a value that isn't there
|
|
|
|
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
spec.send = true;
|
2014-05-30 03:04:10 +02:00
|
|
|
gui::IGUIEditBox *e =
|
|
|
|
Environment->addEditBox(spec.fdefault.c_str(), 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
|
|
|
|
|
|
|
if (type == "textarea")
|
|
|
|
{
|
|
|
|
e->setMultiLine(true);
|
2014-04-22 13:25:02 +02:00
|
|
|
e->setWordWrap(true);
|
2013-06-23 18:30:21 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-12-17 01:13:16 +01:00
|
|
|
if (label.length() >= 1)
|
2013-06-23 18:30:21 +02:00
|
|
|
{
|
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;
|
2013-06-23 18:30:21 +02:00
|
|
|
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseField(parserData* data,std::string element,
|
|
|
|
std::string type)
|
|
|
|
{
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
parseTextArea(data,parts,type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
|
|
|
|
{
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.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
|
|
|
pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of label without a size[] element"<<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
|
|
|
text = unescape_string(text);
|
|
|
|
std::vector<std::string> lines = split(text, '\n');
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i != lines.size(); i++) {
|
|
|
|
// 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.
|
|
|
|
s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring wlabel = narrow_to_wide(lines[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
|
|
|
core::rect<s32> rect = core::rect<s32>(
|
2015-01-09 18:01:59 +01:00
|
|
|
pos.X, posy - 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
|
|
|
pos.X + m_font->getDimension(wlabel.c_str()).Width,
|
2015-01-09 18:01:59 +01:00
|
|
|
posy + 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(
|
|
|
|
L"",
|
|
|
|
wlabel,
|
|
|
|
L"",
|
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
gui::IGUIStaticText *e =
|
|
|
|
Environment->addStaticText(spec.flabel.c_str(),
|
|
|
|
rect, false, false, this, spec.fid);
|
|
|
|
e->setTextAlignment(gui::EGUIA_UPPERLEFT,
|
|
|
|
gui::EGUIA_CENTER);
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
}
|
2014-06-18 22:38:29 +02:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
|
|
|
|
{
|
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],',');
|
2013-08-31 21:07:53 +02:00
|
|
|
std::wstring text = narrow_to_wide(unescape_string(parts[1]));
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
MY_CHECKPOS("vertlabel",1);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
|
2014-06-18 22:38:29 +02:00
|
|
|
core::rect<s32> rect = core::rect<s32>(
|
2014-06-14 11:22:09 +02:00
|
|
|
pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
|
2014-06-18 22:38:29 +02:00
|
|
|
pos.X+15, pos.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
|
|
|
font_line_height(m_font)
|
2014-11-23 13:40:43 +01:00
|
|
|
* (text.length()+1)
|
|
|
|
+((imgsize.Y/2)- m_btn_height));
|
2014-06-18 22:38:29 +02:00
|
|
|
//actually text.length() would be correct but adding +1 avoids to break all mods
|
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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
|
|
|
|
|
2013-08-31 21:07:53 +02:00
|
|
|
std::wstring label = L"";
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
for (unsigned int i=0; i < text.length(); i++) {
|
2013-08-31 21:07:53 +02:00
|
|
|
label += text[i];
|
|
|
|
label += L"\n";
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
2013-08-31 21:07:53 +02:00
|
|
|
label,
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
gui::IGUIStaticText *t =
|
2014-06-20 21:30:48 +02:00
|
|
|
Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid);
|
2013-06-23 18:30:21 +02:00
|
|
|
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
|
|
|
|
std::string type)
|
|
|
|
{
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
|
|
|
|
geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
|
|
|
|
|
2014-06-19 00:22:03 +02:00
|
|
|
bool noclip = false;
|
2013-06-23 18:30:21 +02:00
|
|
|
bool drawborder = true;
|
2014-06-19 00:22:03 +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
|
|
|
|
|
|
|
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)
|
2013-10-07 22:47:13 +02:00
|
|
|
errorstream<<"WARNING: 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
|
|
|
label = unescape_string(label);
|
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
std::wstring wlabel = narrow_to_wide(label);
|
2013-07-17 21:18:24 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(name),
|
2013-07-17 21:18:24 +02:00
|
|
|
wlabel,
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(image_name),
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
spec.ftype = f_Button;
|
|
|
|
if(type == "image_button_exit")
|
|
|
|
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);
|
2014-06-19 00:22:03 +02:00
|
|
|
if (pressed_image_name != "")
|
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
|
|
|
|
|
|
|
gui::IGUIButton *e = Environment->addButton(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
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
e->setUseAlphaChannel(true);
|
|
|
|
e->setImage(texture);
|
2013-07-18 21:20:37 +02:00
|
|
|
e->setPressedImage(pressed_texture);
|
2013-06-23 18:30:21 +02:00
|
|
|
e->setScaleImage(true);
|
|
|
|
e->setNotClipped(noclip);
|
|
|
|
e->setDrawBorder(drawborder);
|
|
|
|
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
|
|
|
|
{
|
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() == 6)) ||
|
|
|
|
((parts.size() > 6) && (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::vector<std::string> buttons = split(parts[2],',');
|
|
|
|
std::string str_index = parts[3];
|
|
|
|
bool show_background = true;
|
|
|
|
bool show_border = true;
|
|
|
|
int tab_index = stoi(str_index) -1;
|
|
|
|
|
|
|
|
MY_CHECKPOS("tabheader",0);
|
|
|
|
|
|
|
|
if (parts.size() == 6) {
|
|
|
|
if (parts[4] == "true")
|
|
|
|
show_background = false;
|
|
|
|
if (parts[5] == "false")
|
|
|
|
show_border = false;
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(name),
|
2013-08-03 17:57:51 +02:00
|
|
|
L"",
|
|
|
|
L"",
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
spec.ftype = f_TabHeader;
|
|
|
|
|
2014-06-14 11:22:09 +02:00
|
|
|
v2s32 pos(0,0);
|
2013-06-23 18:30:21 +02:00
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
2014-06-14 11:22:09 +02:00
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2;
|
2013-06-23 18:30:21 +02:00
|
|
|
v2s32 geom;
|
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
|
|
|
geom.X = DesiredRect.getWidth();
|
2014-06-14 11:22:09 +02:00
|
|
|
geom.Y = m_btn_height*2;
|
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,
|
|
|
|
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);
|
|
|
|
e->setTabHeight(m_btn_height*2);
|
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
|
|
|
e->setNotClipped(true);
|
|
|
|
|
|
|
|
for (unsigned int i=0; i< buttons.size(); i++) {
|
2014-05-30 03:07:48 +02:00
|
|
|
e->addTab(narrow_to_wide(buttons[i]).c_str(), -1);
|
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
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
if (m_gamedef == 0) {
|
2014-05-30 03:07:48 +02:00
|
|
|
errorstream <<
|
|
|
|
"WARNING: invalid use of item_image_button with m_gamedef==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() == 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];
|
|
|
|
|
|
|
|
MY_CHECKPOS("itemimagebutton",0);
|
|
|
|
MY_CHECKGEOM("itemimagebutton",1);
|
|
|
|
|
|
|
|
v2s32 pos = padding;
|
|
|
|
pos.X += stof(v_pos[0]) * (float)spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
|
|
|
|
geom.Y = (stof(v_geom[1]) * (float)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);
|
|
|
|
|
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)
|
2013-06-23 18:30:21 +02:00
|
|
|
errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
|
|
|
|
|
|
|
|
IItemDefManager *idef = m_gamedef->idef();
|
|
|
|
ItemStack item;
|
|
|
|
item.deSerialize(item_name, idef);
|
|
|
|
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
|
2014-06-24 12:28:24 +02:00
|
|
|
|
2015-01-10 12:05:42 +01:00
|
|
|
m_tooltips[narrow_to_wide(name)] =
|
2014-06-24 12:28:24 +02:00
|
|
|
TooltipSpec (item.getDefinition(idef).description,
|
|
|
|
m_default_tooltip_bgcolor,
|
|
|
|
m_default_tooltip_color);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
label = unescape_string(label);
|
2014-05-30 03:04:10 +02:00
|
|
|
FieldSpec spec(
|
2015-01-10 12:05:42 +01:00
|
|
|
narrow_to_wide(name),
|
|
|
|
narrow_to_wide(label),
|
|
|
|
narrow_to_wide(item_name),
|
2013-06-23 18:30:21 +02:00
|
|
|
258+m_fields.size()
|
|
|
|
);
|
|
|
|
|
|
|
|
gui::IGUIButton *e = Environment->addButton(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);
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
e->setUseAlphaChannel(true);
|
|
|
|
e->setImage(texture);
|
|
|
|
e->setPressedImage(texture);
|
|
|
|
e->setScaleImage(true);
|
|
|
|
spec.ftype = f_Button;
|
|
|
|
rect+=data->basepos-padding;
|
|
|
|
spec.rect=rect;
|
|
|
|
m_fields.push_back(spec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseBox(parserData* data,std::string element)
|
|
|
|
{
|
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);
|
|
|
|
|
|
|
|
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
|
|
|
|
pos.X += stof(v_pos[0]) * (float) spacing.X;
|
|
|
|
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
|
|
|
|
|
|
|
|
v2s32 geom;
|
|
|
|
geom.X = stof(v_geom[0]) * (float)spacing.X;
|
|
|
|
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
|
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
video::SColor tmp_color;
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-10-03 06:11:21 +02:00
|
|
|
if (parseColorString(parts[2], tmp_color, false)) {
|
2013-11-02 04:26:44 +01:00
|
|
|
BoxDrawSpec spec(pos, geom, tmp_color);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
m_boxes.push_back(spec);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element)
|
|
|
|
{
|
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)) ||
|
|
|
|
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
|
|
|
|
{
|
2014-10-03 06:11:21 +02:00
|
|
|
parseColorString(parts[0],m_bgcolor,false);
|
2013-11-02 04:26:44 +01:00
|
|
|
|
|
|
|
if (parts.size() == 2) {
|
|
|
|
std::string fullscreen = parts[1];
|
|
|
|
m_bgfullscreen = is_yes(fullscreen);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-03-04 19:57:39 +01:00
|
|
|
void GUIFormSpecMenu::parseListColors(parserData* data,std::string element)
|
|
|
|
{
|
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;
|
|
|
|
}
|
|
|
|
|
2014-06-24 12:28:24 +02:00
|
|
|
void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
|
|
|
|
{
|
|
|
|
std::vector<std::string> parts = split(element,';');
|
|
|
|
if (parts.size() == 2) {
|
|
|
|
std::string name = parts[0];
|
2015-01-10 12:05:42 +01:00
|
|
|
m_tooltips[narrow_to_wide(name)] = TooltipSpec (parts[1], m_default_tooltip_bgcolor, m_default_tooltip_color);
|
2014-06-24 12:28:24 +02:00
|
|
|
return;
|
|
|
|
} else if (parts.size() == 4) {
|
|
|
|
std::string name = parts[0];
|
|
|
|
video::SColor tmp_color1, tmp_color2;
|
2014-10-03 06:11:21 +02:00
|
|
|
if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
|
2015-01-10 12:05:42 +01:00
|
|
|
m_tooltips[narrow_to_wide(name)] = TooltipSpec (parts[1], tmp_color1, tmp_color2);
|
2014-06-24 12:28:24 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'" << std::endl;
|
|
|
|
}
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
bool GUIFormSpecMenu::parseVersionDirect(std::string data)
|
|
|
|
{
|
|
|
|
//some prechecks
|
|
|
|
if (data == "")
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element)
|
|
|
|
{
|
|
|
|
if (element == "")
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-06-25 19:04:47 +02:00
|
|
|
void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
|
2014-03-04 19:57:39 +01:00
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
//some prechecks
|
|
|
|
if (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]);
|
|
|
|
|
|
|
|
if (type == "list") {
|
|
|
|
parseList(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "checkbox") {
|
|
|
|
parseCheckbox(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "image") {
|
|
|
|
parseImage(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "item_image") {
|
|
|
|
parseItemImage(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((type == "button") || (type == "button_exit")) {
|
|
|
|
parseButton(data,description,type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "background") {
|
|
|
|
parseBackground(data,description);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == "pwdfield") {
|
|
|
|
parsePwdField(data,description);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((type == "field") || (type == "textarea")){
|
|
|
|
parseField(data,description,type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
// Ignore others
|
|
|
|
infostream
|
|
|
|
<< "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
|
|
|
|
<<std::endl;
|
2010-12-25 15:04:51 +01:00
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02: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
|
|
|
|
for (u32 i = 0; i < m_tables.size(); ++i) {
|
|
|
|
std::wstring tablename = m_tables[i].first.fname;
|
|
|
|
GUITable *table = m_tables[i].second;
|
|
|
|
mydata.table_dyndata[tablename] = table->getDynamicData();
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
for (u32 i=0; i<m_fields.size(); i++) {
|
|
|
|
if (m_fields[i].fid == focused_id) {
|
|
|
|
mydata.focused_fieldname =
|
|
|
|
m_fields[i].fname;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-25 15:04:51 +01:00
|
|
|
// Remove children
|
|
|
|
removeChildren();
|
2013-06-23 18:30:21 +02:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
for (u32 i = 0; i < m_tables.size(); ++i) {
|
|
|
|
GUITable *table = m_tables[i].second;
|
|
|
|
table->drop();
|
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
mydata.size= v2s32(100,100);
|
|
|
|
mydata.screensize = screensize;
|
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();
|
|
|
|
m_images.clear();
|
2012-11-08 19:22:37 +01:00
|
|
|
m_backgrounds.clear();
|
2012-12-01 18:36:42 +01:00
|
|
|
m_itemimages.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();
|
2013-06-23 18:30:21 +02:00
|
|
|
m_boxes.clear();
|
2014-06-24 12:28:24 +02:00
|
|
|
m_tooltips.clear();
|
2014-07-06 16:17:46 +02:00
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
// Set default values (fits old formspec values)
|
|
|
|
m_bgcolor = video::SColor(140,0,0,0);
|
|
|
|
m_bgfullscreen = false;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
m_clipbackground = false;
|
|
|
|
// Add tooltip
|
|
|
|
{
|
2014-05-30 03:04:10 +02:00
|
|
|
assert(m_tooltip_element == NULL);
|
2013-11-02 04:26:44 +01:00
|
|
|
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle
|
|
|
|
m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
|
|
|
|
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 */
|
|
|
|
if (elements.size() >= 1) {
|
|
|
|
if ( parseVersionDirect(elements[0]) ) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mydata.explicit_size) {
|
|
|
|
// compute scaling for specified form size
|
|
|
|
if (m_lock) {
|
|
|
|
v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
|
|
|
|
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");
|
|
|
|
double screen_dpi = porting::getDisplayDensity() * 96;
|
|
|
|
|
|
|
|
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.
|
|
|
|
double prefer_imgsize = mydata.screensize.Y / 15 *
|
|
|
|
gui_scaling;
|
|
|
|
double fitx_imgsize = mydata.screensize.X /
|
|
|
|
((5.0/4.0) * (0.5 + mydata.invsize.X));
|
|
|
|
double fity_imgsize = mydata.screensize.Y /
|
|
|
|
((15.0/13.0) * (0.85 * mydata.invsize.Y));
|
|
|
|
double screen_dpi = porting::getDisplayDensity() * 96;
|
|
|
|
double min_imgsize = 0.3 * screen_dpi * gui_scaling;
|
|
|
|
use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
|
|
|
|
MYMIN(fitx_imgsize, fity_imgsize)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
spacing = v2s32(use_imgsize*5.0/4, use_imgsize*15.0/13);
|
|
|
|
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
|
|
|
|
|
|
|
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
|
|
|
|
);
|
|
|
|
DesiredRect = mydata.rect = core::rect<s32>(
|
|
|
|
mydata.screensize.X/2 - mydata.size.X/2 + offset.X,
|
|
|
|
mydata.screensize.Y/2 - mydata.size.Y/2 + offset.Y,
|
|
|
|
mydata.screensize.X/2 + mydata.size.X/2 + offset.X,
|
|
|
|
mydata.screensize.Y/2 + mydata.size.Y/2 + offset.Y
|
|
|
|
);
|
|
|
|
} 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>(
|
|
|
|
mydata.screensize.X/2 - 580/2,
|
|
|
|
mydata.screensize.Y/2 - 300/2,
|
|
|
|
mydata.screensize.X/2 + 580/2,
|
|
|
|
mydata.screensize.Y/2 + 300/2
|
|
|
|
);
|
|
|
|
}
|
|
|
|
recalculateAbsolutePosition(false);
|
|
|
|
mydata.basepos = getBasePos();
|
|
|
|
m_tooltip_element->setOverrideFont(m_font);
|
|
|
|
|
|
|
|
gui::IGUISkin* skin = Environment->getSkin();
|
|
|
|
assert(skin != NULL);
|
|
|
|
gui::IGUIFont *old_font = skin->getFont();
|
|
|
|
skin->setFont(m_font);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
if (m_fields.size() && !mydata.explicit_size) {
|
2013-06-23 18:30:21 +02:00
|
|
|
mydata.rect = core::rect<s32>(
|
|
|
|
mydata.screensize.X/2 - 580/2,
|
|
|
|
mydata.screensize.Y/2 - 300/2,
|
|
|
|
mydata.screensize.X/2 + 580/2,
|
|
|
|
mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
|
2012-07-15 18:19:38 +02: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;
|
2012-07-15 18:19:38 +02:00
|
|
|
pos.Y = ((m_fields.size()+2)*60);
|
|
|
|
|
|
|
|
v2s32 size = DesiredRect.getSize();
|
2014-06-14 11:22:09 +02:00
|
|
|
mydata.rect =
|
|
|
|
core::rect<s32>(size.X/2-70, pos.Y,
|
|
|
|
(size.X/2-70)+140, pos.Y + (m_btn_height*2));
|
2013-04-07 19:15:17 +02:00
|
|
|
wchar_t* text = wgettext("Proceed");
|
2013-06-23 18:30:21 +02:00
|
|
|
Environment->addButton(mydata.rect, this, 257, text);
|
2013-04-07 19:15:17 +02:00
|
|
|
delete[] text;
|
2012-07-15 18:19:38 +02:00
|
|
|
}
|
2013-11-03 17:28:16 +01:00
|
|
|
|
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);
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
|
|
|
|
2014-04-21 14:10:59 +02:00
|
|
|
#ifdef __ANDROID__
|
|
|
|
bool GUIFormSpecMenu::getAndroidUIInput()
|
|
|
|
{
|
|
|
|
/* no dialog shown */
|
|
|
|
if (m_JavaDialogFieldName == L"") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* still waiting */
|
|
|
|
if (porting::getInputDialogState() == -1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::wstring fieldname = m_JavaDialogFieldName;
|
|
|
|
m_JavaDialogFieldName = L"";
|
|
|
|
|
|
|
|
/* no value abort dialog processing */
|
|
|
|
if (porting::getInputDialogState() != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
|
|
|
|
iter != m_fields.end(); iter++) {
|
|
|
|
|
|
|
|
if (iter->fname != fieldname) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
IGUIElement* tochange = getElementFromId(iter->fid);
|
|
|
|
|
|
|
|
if (tochange == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string text = porting::getInputDialogValue();
|
|
|
|
|
|
|
|
((gui::IGUIEditBox*) tochange)->
|
|
|
|
setText(narrow_to_wide(text).c_str());
|
|
|
|
}
|
|
|
|
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
|
|
|
{
|
|
|
|
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2012-06-03 16:18:08 +02:00
|
|
|
for(u32 i=0; i<m_inventorylists.size(); i++)
|
2010-12-22 15:30:23 +01:00
|
|
|
{
|
2012-06-03 16:18:08 +02:00
|
|
|
const ListDrawSpec &s = m_inventorylists[i];
|
2010-12-22 15:30:23 +01:00
|
|
|
|
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;
|
2010-12-22 15:30:23 +01:00
|
|
|
s32 x = (i%s.geom.X) * spacing.X;
|
|
|
|
s32 y = (i/s.geom.X) * spacing.Y;
|
|
|
|
v2s32 p0(x,y);
|
|
|
|
core::rect<s32> rect = imgrect + s.pos + p0;
|
|
|
|
if(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
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
|
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);
|
2012-06-02 14:28:12 +02:00
|
|
|
if(!inv){
|
2012-07-15 18:19:38 +02:00
|
|
|
infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
|
2012-06-02 14:28:12 +02:00
|
|
|
<<"The inventory location "
|
|
|
|
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
|
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2011-04-04 14:13:19 +02:00
|
|
|
InventoryList *ilist = inv->getList(s.listname);
|
2012-06-02 14:28:12 +02:00
|
|
|
if(!ilist){
|
2012-07-15 18:19:38 +02:00
|
|
|
infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
|
2012-06-02 14:28:12 +02:00
|
|
|
<<"The inventory list \""<<s.listname<<"\" @ \""
|
|
|
|
<<s.inventoryloc.dump()<<"\" doesn't exist"
|
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2010-12-22 15:30:23 +01:00
|
|
|
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2010-12-22 15:30:23 +01: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;
|
|
|
|
if(item_i >= (s32) ilist->getSize())
|
2012-07-24 19:57:17 +02:00
|
|
|
break;
|
2010-12-22 15:30:23 +01:00
|
|
|
s32 x = (i%s.geom.X) * spacing.X;
|
|
|
|
s32 y = (i/s.geom.X) * spacing.Y;
|
|
|
|
v2s32 p(x,y);
|
|
|
|
core::rect<s32> rect = imgrect + s.pos + p;
|
2012-01-12 06:10:39 +01:00
|
|
|
ItemStack item;
|
2010-12-22 15:30:23 +01:00
|
|
|
if(ilist)
|
2012-07-24 19:57:17 +02:00
|
|
|
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;
|
2012-01-13 12:35:55 +01:00
|
|
|
bool hovering = rect.isPointInside(m_pointer);
|
|
|
|
|
|
|
|
if(phase == 0)
|
2011-12-01 10:25:55 +01:00
|
|
|
{
|
2013-11-02 04:26:44 +01:00
|
|
|
if(hovering)
|
|
|
|
driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
|
2012-01-13 12:35:55 +01:00
|
|
|
else
|
2013-11-02 04:26:44 +01:00
|
|
|
driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
//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),
|
|
|
|
v2s32(x2 + border, y1)), NULL);
|
|
|
|
driver->draw2DRectangle(m_slotbordercolor,
|
|
|
|
core::rect<s32>(v2s32(x1 - border, y2),
|
|
|
|
v2s32(x2 + border, y2 + border)), NULL);
|
|
|
|
driver->draw2DRectangle(m_slotbordercolor,
|
|
|
|
core::rect<s32>(v2s32(x1 - border, y1),
|
|
|
|
v2s32(x1, y2)), NULL);
|
|
|
|
driver->draw2DRectangle(m_slotbordercolor,
|
|
|
|
core::rect<s32>(v2s32(x2, y1),
|
|
|
|
v2s32(x2 + border, y2)), NULL);
|
2011-12-01 10:25:55 +01:00
|
|
|
}
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
if(phase == 1)
|
2011-12-01 10:25:55 +01:00
|
|
|
{
|
2012-01-21 21:21:41 +01:00
|
|
|
// Draw item stack
|
|
|
|
if(selected)
|
2012-01-13 12:35:55 +01:00
|
|
|
{
|
2012-01-21 21:21:41 +01:00
|
|
|
item.takeItem(m_selected_amount);
|
2012-01-13 12:35:55 +01:00
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
if(!item.empty())
|
2012-01-13 12:35:55 +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
|
|
|
drawItemStack(driver, m_font, item,
|
2012-01-21 21:21:41 +01:00
|
|
|
rect, &AbsoluteClippingRect, m_gamedef);
|
2012-01-13 12:35:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Draw tooltip
|
|
|
|
std::string tooltip_text = "";
|
2014-09-28 08:44:05 +02:00
|
|
|
if (hovering && !m_selected_item)
|
2012-01-13 12:35:55 +01:00
|
|
|
tooltip_text = item.getDefinition(m_gamedef->idef()).description;
|
2014-09-28 08:44:05 +02:00
|
|
|
if (tooltip_text != "") {
|
2014-09-28 10:10:43 +02:00
|
|
|
std::vector<std::string> tt_rows = str_split(tooltip_text, '\n');
|
2014-06-24 12:28:24 +02:00
|
|
|
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
|
|
|
|
m_tooltip_element->setOverrideColor(m_default_tooltip_color);
|
2012-01-13 12:35:55 +01:00
|
|
|
m_tooltip_element->setVisible(true);
|
|
|
|
this->bringToFront(m_tooltip_element);
|
|
|
|
m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
|
2014-06-14 11:22:09 +02:00
|
|
|
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
|
2014-09-28 08:44:05 +02:00
|
|
|
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
|
2014-12-02 04:32:41 +01:00
|
|
|
v2u32 screenSize = driver->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) * -1;
|
|
|
|
#endif
|
|
|
|
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;
|
2012-01-13 12:35:55 +01:00
|
|
|
m_tooltip_element->setRelativePosition(core::rect<s32>(
|
|
|
|
core::position2d<s32>(tooltip_x, tooltip_y),
|
|
|
|
core::dimension2d<s32>(tooltip_width, tooltip_height)));
|
|
|
|
}
|
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
|
|
|
{
|
|
|
|
if(!m_selected_item)
|
|
|
|
return;
|
|
|
|
|
|
|
|
video::IVideoDriver* driver = Environment->getVideoDriver();
|
|
|
|
|
|
|
|
Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
|
|
|
|
assert(inv);
|
|
|
|
InventoryList *list = inv->getList(m_selected_item->listname);
|
|
|
|
assert(list);
|
|
|
|
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());
|
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, stack, rect, NULL, m_gamedef);
|
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
|
|
|
{
|
2012-06-03 17:30:34 +02:00
|
|
|
if(m_form_src){
|
|
|
|
std::string newform = m_form_src->getForm();
|
|
|
|
if(newform != m_formspec_string){
|
|
|
|
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();
|
|
|
|
assert(skin != NULL);
|
|
|
|
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
|
|
|
|
2013-11-02 04:26:44 +01:00
|
|
|
v2u32 screenSize = driver->getScreenSize();
|
|
|
|
core::rect<s32> allbg(0, 0, screenSize.X , screenSize.Y);
|
2013-12-11 23:07:38 +01:00
|
|
|
if (m_bgfullscreen)
|
2013-11-02 04:26:44 +01:00
|
|
|
driver->draw2DRectangle(m_bgcolor, allbg, &allbg);
|
|
|
|
else
|
|
|
|
driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
|
2010-12-22 02:34:21 +01:00
|
|
|
|
2012-01-13 12:35:55 +01:00
|
|
|
m_tooltip_element->setVisible(false);
|
|
|
|
|
2012-11-08 19:22:37 +01:00
|
|
|
/*
|
|
|
|
Draw backgrounds
|
|
|
|
*/
|
|
|
|
for(u32 i=0; i<m_backgrounds.size(); i++)
|
2012-11-12 15:35:10 +01:00
|
|
|
{
|
2012-11-08 19:22:37 +01:00
|
|
|
const ImageDrawSpec &spec = m_backgrounds[i];
|
2013-08-20 22:38:14 +02:00
|
|
|
video::ITexture *texture = m_tsrc->getTexture(spec.name);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
if (texture != 0) {
|
|
|
|
// Image size on screen
|
|
|
|
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
|
|
|
|
// Image rectangle on screen
|
|
|
|
core::rect<s32> rect = imgrect + spec.pos;
|
2013-11-02 04:26:44 +01:00
|
|
|
|
|
|
|
if (m_clipbackground) {
|
|
|
|
core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
|
|
|
|
rect = core::rect<s32>(AbsoluteRect.UpperLeftCorner.X - spec.pos.X,
|
|
|
|
AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y,
|
2013-11-14 19:03:55 +01:00
|
|
|
AbsoluteRect.UpperLeftCorner.X + absrec_size.Width + spec.pos.X,
|
|
|
|
AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y);
|
2013-11-02 04:26:44 +01:00
|
|
|
}
|
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
const video::SColor color(255,255,255,255);
|
|
|
|
const video::SColor colors[] = {color,color,color,color};
|
|
|
|
driver->draw2DImage(texture, rect,
|
|
|
|
core::rect<s32>(core::position2d<s32>(0,0),
|
|
|
|
core::dimension2di(texture->getOriginalSize())),
|
|
|
|
NULL/*&AbsoluteClippingRect*/, colors, true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
|
|
|
|
errorstream << "\t" << spec.name << std::endl;
|
|
|
|
}
|
2012-11-12 15:35:10 +01:00
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2013-06-23 18:30:21 +02:00
|
|
|
/*
|
|
|
|
Draw Boxes
|
|
|
|
*/
|
|
|
|
for(u32 i=0; i<m_boxes.size(); i++)
|
|
|
|
{
|
|
|
|
const BoxDrawSpec &spec = m_boxes[i];
|
|
|
|
|
|
|
|
irr::video::SColor todraw = spec.color;
|
|
|
|
|
|
|
|
todraw.setAlpha(140);
|
|
|
|
|
|
|
|
core::rect<s32> rect(spec.pos.X,spec.pos.Y,
|
|
|
|
spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
|
|
|
|
|
|
|
|
driver->draw2DRectangle(todraw, rect, 0);
|
|
|
|
}
|
2010-12-22 10:29:06 +01:00
|
|
|
/*
|
2012-11-04 21:18:28 +01:00
|
|
|
Draw images
|
2010-12-22 10:29:06 +01:00
|
|
|
*/
|
2012-06-03 16:18:08 +02:00
|
|
|
for(u32 i=0; i<m_images.size(); i++)
|
|
|
|
{
|
|
|
|
const ImageDrawSpec &spec = m_images[i];
|
2013-08-20 22:38:14 +02:00
|
|
|
video::ITexture *texture = m_tsrc->getTexture(spec.name);
|
2013-06-23 18:30:21 +02:00
|
|
|
|
|
|
|
if (texture != 0) {
|
|
|
|
const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
|
|
|
|
// Image size on screen
|
|
|
|
core::rect<s32> imgrect;
|
|
|
|
|
|
|
|
if (spec.scale)
|
|
|
|
imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
|
|
|
|
else {
|
|
|
|
|
|
|
|
imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
|
|
|
|
}
|
|
|
|
// Image rectangle on screen
|
|
|
|
core::rect<s32> rect = imgrect + spec.pos;
|
|
|
|
const video::SColor color(255,255,255,255);
|
|
|
|
const video::SColor colors[] = {color,color,color,color};
|
|
|
|
driver->draw2DImage(texture, rect,
|
|
|
|
core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
|
|
|
|
NULL/*&AbsoluteClippingRect*/, colors, true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
|
|
|
|
errorstream << "\t" << spec.name << std::endl;
|
|
|
|
}
|
2010-12-22 10:29:06 +01:00
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2013-01-02 00:03:47 +01:00
|
|
|
/*
|
|
|
|
Draw item images
|
|
|
|
*/
|
|
|
|
for(u32 i=0; i<m_itemimages.size(); i++)
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
if (m_gamedef == 0)
|
|
|
|
break;
|
|
|
|
|
2013-01-02 00:03:47 +01:00
|
|
|
const ImageDrawSpec &spec = m_itemimages[i];
|
|
|
|
IItemDefManager *idef = m_gamedef->idef();
|
|
|
|
ItemStack item;
|
|
|
|
item.deSerialize(spec.name, idef);
|
2013-12-11 23:07:38 +01:00
|
|
|
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
|
2013-01-02 00:03:47 +01:00
|
|
|
// Image size on screen
|
|
|
|
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
|
|
|
|
// Image rectangle on screen
|
|
|
|
core::rect<s32> rect = imgrect + spec.pos;
|
|
|
|
const video::SColor color(255,255,255,255);
|
|
|
|
const video::SColor colors[] = {color,color,color,color};
|
|
|
|
driver->draw2DImage(texture, rect,
|
|
|
|
core::rect<s32>(core::position2d<s32>(0,0),
|
|
|
|
core::dimension2di(texture->getOriginalSize())),
|
|
|
|
NULL/*&AbsoluteClippingRect*/, colors, true);
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
2012-11-04 21:18:28 +01:00
|
|
|
/*
|
|
|
|
Draw items
|
|
|
|
Phase 0: Item slot rectangles
|
|
|
|
Phase 1: Item images; prepare tooltip
|
|
|
|
*/
|
2012-11-08 19:22:37 +01:00
|
|
|
int start_phase=0;
|
|
|
|
for(int phase=start_phase; phase<=1; phase++)
|
2012-11-04 21:18:28 +01:00
|
|
|
for(u32 i=0; i<m_inventorylists.size(); i++)
|
|
|
|
{
|
|
|
|
drawList(m_inventorylists[i], phase);
|
|
|
|
}
|
|
|
|
|
2010-12-22 10:29:06 +01:00
|
|
|
/*
|
|
|
|
Call base class
|
|
|
|
*/
|
2010-12-22 02:34:21 +01:00
|
|
|
gui::IGUIElement::draw();
|
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
|
|
|
|
m_pointer = m_device->getCursorControl()->getPosition();
|
|
|
|
#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
|
|
|
|
|
|
|
u32 delta = 0;
|
2014-06-24 12:28:24 +02:00
|
|
|
if (id == -1) {
|
|
|
|
m_old_tooltip_id = id;
|
|
|
|
m_old_tooltip = "";
|
2014-10-30 07:53:20 +01:00
|
|
|
} else {
|
|
|
|
if (id == m_old_tooltip_id) {
|
|
|
|
delta = porting::getDeltaMs(m_hovered_time, getTimeMs());
|
|
|
|
} else {
|
|
|
|
m_hovered_time = getTimeMs();
|
|
|
|
m_old_tooltip_id = id;
|
|
|
|
}
|
2014-07-02 15:32:54 +02:00
|
|
|
}
|
2014-10-30 07:53:20 +01:00
|
|
|
|
2014-07-02 15:32:54 +02:00
|
|
|
if (id != -1 && delta >= m_tooltip_show_delay) {
|
2014-06-24 12:28:24 +02:00
|
|
|
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
|
2014-06-19 00:22:03 +02:00
|
|
|
iter != m_fields.end(); iter++) {
|
2014-06-24 12:28:24 +02:00
|
|
|
if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
|
|
|
|
if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
|
|
|
|
m_old_tooltip = m_tooltips[iter->fname].tooltip;
|
|
|
|
m_tooltip_element->setText(narrow_to_wide(m_tooltips[iter->fname].tooltip).c_str());
|
2014-09-28 10:10:43 +02:00
|
|
|
std::vector<std::string> tt_rows = str_split(m_tooltips[iter->fname].tooltip, '\n');
|
2014-12-02 04:32:41 +01:00
|
|
|
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
|
2014-09-28 10:10:43 +02:00
|
|
|
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
|
2014-12-02 04:32:41 +01:00
|
|
|
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) * -1;
|
|
|
|
#endif
|
|
|
|
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;
|
2014-06-24 12:28:24 +02:00
|
|
|
m_tooltip_element->setRelativePosition(core::rect<s32>(
|
|
|
|
core::position2d<s32>(tooltip_x, tooltip_y),
|
|
|
|
core::dimension2d<s32>(tooltip_width, tooltip_height)));
|
|
|
|
}
|
|
|
|
m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
|
|
|
|
m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
|
|
|
|
m_tooltip_element->setVisible(true);
|
|
|
|
this->bringToFront(m_tooltip_element);
|
|
|
|
break;
|
2014-06-25 19:04:47 +02:00
|
|
|
}
|
2012-12-01 18:36:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
void GUIFormSpecMenu::updateSelectedItem()
|
2012-01-21 21:21:41 +01:00
|
|
|
{
|
2013-06-18 01:32:11 +02:00
|
|
|
// 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.
|
|
|
|
ItemStack selected = verifySelectedItem();
|
|
|
|
|
2012-09-02 21:51:38 +02:00
|
|
|
// WARNING: BLACK MAGIC
|
|
|
|
// See if there is a stack suited for our current guess.
|
|
|
|
// If such stack does not exist, clear the guess.
|
2013-06-18 01:32:11 +02:00
|
|
|
if(m_selected_content_guess.name != "" &&
|
|
|
|
selected.name == m_selected_content_guess.name &&
|
|
|
|
selected.count == m_selected_content_guess.count){
|
|
|
|
// Selected item fits the guess. Skip the black magic.
|
|
|
|
}
|
|
|
|
else if(m_selected_content_guess.name != ""){
|
2012-09-02 21:51:38 +02:00
|
|
|
bool found = false;
|
|
|
|
for(u32 i=0; i<m_inventorylists.size() && !found; i++){
|
|
|
|
const ListDrawSpec &s = m_inventorylists[i];
|
|
|
|
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
|
|
|
|
if(!inv)
|
|
|
|
continue;
|
|
|
|
InventoryList *list = inv->getList(s.listname);
|
|
|
|
if(!list)
|
|
|
|
continue;
|
|
|
|
for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
|
|
|
|
u32 item_i = i + s.start_item_i;
|
|
|
|
if(item_i >= list->getSize())
|
|
|
|
continue;
|
|
|
|
ItemStack stack = list->getItem(item_i);
|
|
|
|
if(stack.name == m_selected_content_guess.name &&
|
|
|
|
stack.count == m_selected_content_guess.count){
|
|
|
|
found = true;
|
|
|
|
infostream<<"Client: Changing selected content guess to "
|
|
|
|
<<s.inventoryloc.dump()<<" "<<s.listname
|
|
|
|
<<" "<<item_i<<std::endl;
|
2013-06-18 01:32:11 +02:00
|
|
|
delete m_selected_item;
|
2012-09-02 21:51:38 +02:00
|
|
|
m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
|
|
|
|
m_selected_amount = stack.count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!found){
|
|
|
|
infostream<<"Client: Discarding selected content guess: "
|
|
|
|
<<m_selected_content_guess.getItemString()<<std::endl;
|
|
|
|
m_selected_content_guess.name = "";
|
|
|
|
}
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
// If craftresult is nonempty and nothing else is selected, select it now.
|
|
|
|
if(!m_selected_item)
|
|
|
|
{
|
2012-06-03 16:18:08 +02:00
|
|
|
for(u32 i=0; i<m_inventorylists.size(); i++)
|
2012-01-21 21:21:41 +01:00
|
|
|
{
|
2012-06-03 16:18:08 +02:00
|
|
|
const ListDrawSpec &s = m_inventorylists[i];
|
2012-01-21 21:21:41 +01:00
|
|
|
if(s.listname == "craftpreview")
|
|
|
|
{
|
|
|
|
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
|
|
|
|
InventoryList *list = inv->getList("craftresult");
|
|
|
|
if(list && list->getSize() >= 1 && !list->getItem(0).empty())
|
|
|
|
{
|
|
|
|
m_selected_item = new ItemSpec;
|
|
|
|
m_selected_item->inventoryloc = s.inventoryloc;
|
|
|
|
m_selected_item->listname = "craftresult";
|
|
|
|
m_selected_item->i = 0;
|
|
|
|
m_selected_amount = 0;
|
|
|
|
m_selected_dragging = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If craftresult is selected, keep the whole stack selected
|
|
|
|
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);
|
|
|
|
if(m_selected_amount > stack.count)
|
|
|
|
m_selected_amount = stack.count;
|
|
|
|
if(!stack.empty())
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
std::map<std::string, std::string> 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current_keys_pending.key_escape) {
|
|
|
|
fields["key_escape"] = "true";
|
|
|
|
current_keys_pending.key_escape = false;
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
for(unsigned int i=0; i<m_fields.size(); i++) {
|
2012-07-15 18:19:38 +02:00
|
|
|
const FieldSpec &s = m_fields[i];
|
2014-05-30 03:04:10 +02:00
|
|
|
if(s.send) {
|
|
|
|
std::string name = wide_to_narrow(s.fname);
|
|
|
|
if(s.ftype == f_Button) {
|
|
|
|
fields[name] = wide_to_narrow(s.flabel);
|
2012-07-15 18:19:38 +02:00
|
|
|
}
|
2013-08-23 12:24:11 +02:00
|
|
|
else if(s.ftype == f_Table) {
|
|
|
|
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
|
|
|
|
IGUIElement * element = getElementFromId(s.fid);
|
|
|
|
gui::IGUIComboBox *e = NULL;
|
|
|
|
if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
|
|
|
|
e = static_cast<gui::IGUIComboBox*>(element);
|
|
|
|
}
|
2013-12-11 23:07:38 +01:00
|
|
|
s32 selected = e->getSelected();
|
|
|
|
if (selected >= 0) {
|
2014-05-30 03:04:10 +02:00
|
|
|
fields[name] =
|
2013-12-11 23:07:38 +01:00
|
|
|
wide_to_narrow(e->getItem(selected));
|
|
|
|
}
|
2013-06-23 18:30:21 +02:00
|
|
|
}
|
|
|
|
else if (s.ftype == f_TabHeader) {
|
|
|
|
// no dynamic cast possible due to some distributions shipped
|
|
|
|
// without rtti support in irrlicht
|
|
|
|
IGUIElement * element = getElementFromId(s.fid);
|
|
|
|
gui::IGUITabControl *e = NULL;
|
|
|
|
if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
|
|
|
|
e = static_cast<gui::IGUITabControl*>(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
IGUIElement * element = getElementFromId(s.fid);
|
|
|
|
gui::IGUICheckBox *e = NULL;
|
|
|
|
if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
|
|
|
|
e = static_cast<gui::IGUICheckBox*>(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
IGUIElement * element = getElementFromId(s.fid);
|
|
|
|
gui::IGUIScrollBar *e = NULL;
|
|
|
|
if ((element) && (element->getType() == gui::EGUIET_SCROLL_BAR)) {
|
|
|
|
e = static_cast<gui::IGUIScrollBar*>(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e != 0) {
|
|
|
|
std::stringstream os;
|
|
|
|
os << e->getPos();
|
|
|
|
if (s.fdefault == L"Changed")
|
|
|
|
fields[name] = "CHG:" + os.str();
|
|
|
|
else
|
|
|
|
fields[name] = "VAL:" + os.str();
|
|
|
|
}
|
|
|
|
}
|
2012-07-15 18:19:38 +02:00
|
|
|
else
|
|
|
|
{
|
2013-06-23 18:30:21 +02:00
|
|
|
IGUIElement* e = getElementFromId(s.fid);
|
2014-05-30 03:04:10 +02:00
|
|
|
if(e != NULL) {
|
|
|
|
fields[name] = wide_to_narrow(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();
|
|
|
|
assert(skin != NULL);
|
|
|
|
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 ||
|
|
|
|
focused->getType() == gui::EGUIET_CHECK_BOX)) {
|
|
|
|
OnEvent(event);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Mouse wheel events: send to hovered element instead of focused
|
|
|
|
if(event.EventType==EET_MOUSE_INPUT_EVENT
|
2014-05-30 03:04:10 +02:00
|
|
|
&& event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
|
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);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
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;
|
|
|
|
m_old_tooltip = "";
|
|
|
|
}
|
2014-05-30 03:07:48 +02:00
|
|
|
if (!isChild(hovered,this)) {
|
|
|
|
if (DoubleClickDetection(event)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-21 14:10:59 +02:00
|
|
|
#ifdef __ANDROID__
|
|
|
|
// display software keyboard when clicking edit boxes
|
|
|
|
if (event.EventType == EET_MOUSE_INPUT_EVENT
|
|
|
|
&& event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
|
|
|
gui::IGUIElement *hovered =
|
|
|
|
Environment->getRootGUIElement()->getElementFromPoint(
|
|
|
|
core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
|
|
|
|
if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) {
|
|
|
|
bool retval = hovered->OnEvent(event);
|
|
|
|
if (retval) {
|
|
|
|
Environment->setFocus(hovered);
|
|
|
|
}
|
|
|
|
m_JavaDialogFieldName = getNameByID(hovered->getID());
|
|
|
|
std::string message = gettext("Enter ");
|
|
|
|
std::string label = wide_to_narrow(getLabelByID(hovered->getID()));
|
|
|
|
if (label == "") {
|
|
|
|
label = "text";
|
|
|
|
}
|
|
|
|
message += gettext(label) + ":";
|
|
|
|
|
|
|
|
/* single line text input */
|
|
|
|
int type = 2;
|
|
|
|
|
|
|
|
/* multi line text input */
|
|
|
|
if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) {
|
|
|
|
type = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* passwords are always single line */
|
|
|
|
if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) {
|
|
|
|
type = 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
porting::showInputDialog(gettext("ok"), "",
|
|
|
|
wide_to_narrow(((gui::IGUIEditBox*) hovered)->getText()),
|
|
|
|
type);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.EventType == EET_TOUCH_INPUT_EVENT)
|
|
|
|
{
|
|
|
|
SEvent translated;
|
|
|
|
memset(&translated, 0, sizeof(SEvent));
|
|
|
|
translated.EventType = EET_MOUSE_INPUT_EVENT;
|
|
|
|
gui::IGUIElement* root = Environment->getRootGUIElement();
|
|
|
|
|
|
|
|
if (!root) {
|
|
|
|
errorstream
|
|
|
|
<< "GUIFormSpecMenu::preprocessEvent unable to get root element"
|
|
|
|
<< std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
gui::IGUIElement* hovered = root->getElementFromPoint(
|
|
|
|
core::position2d<s32>(
|
|
|
|
event.TouchInput.X,
|
|
|
|
event.TouchInput.Y));
|
|
|
|
|
|
|
|
translated.MouseInput.X = event.TouchInput.X;
|
|
|
|
translated.MouseInput.Y = event.TouchInput.Y;
|
|
|
|
translated.MouseInput.Control = false;
|
|
|
|
|
|
|
|
bool dont_send_event = false;
|
|
|
|
|
|
|
|
if (event.TouchInput.touchedCount == 1) {
|
|
|
|
switch (event.TouchInput.Event) {
|
|
|
|
case ETIE_PRESSED_DOWN:
|
|
|
|
m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
|
|
|
|
translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
|
|
|
|
translated.MouseInput.ButtonStates = EMBSM_LEFT;
|
|
|
|
m_down_pos = m_pointer;
|
|
|
|
break;
|
|
|
|
case ETIE_MOVED:
|
|
|
|
m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
|
|
|
|
translated.MouseInput.Event = EMIE_MOUSE_MOVED;
|
|
|
|
translated.MouseInput.ButtonStates = EMBSM_LEFT;
|
|
|
|
break;
|
|
|
|
case ETIE_LEFT_UP:
|
|
|
|
translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
|
|
|
|
translated.MouseInput.ButtonStates = 0;
|
|
|
|
hovered = root->getElementFromPoint(m_down_pos);
|
|
|
|
/* we don't have a valid pointer element use last
|
|
|
|
* known pointer pos */
|
|
|
|
translated.MouseInput.X = m_pointer.X;
|
|
|
|
translated.MouseInput.Y = m_pointer.Y;
|
|
|
|
|
|
|
|
/* reset down pos */
|
|
|
|
m_down_pos = v2s32(0,0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dont_send_event = true;
|
|
|
|
//this is not supposed to happen
|
|
|
|
errorstream
|
|
|
|
<< "GUIFormSpecMenu::preprocessEvent unexpected usecase Event="
|
|
|
|
<< event.TouchInput.Event << std::endl;
|
|
|
|
}
|
|
|
|
} else if ( (event.TouchInput.touchedCount == 2) &&
|
|
|
|
(event.TouchInput.Event == ETIE_PRESSED_DOWN) ) {
|
|
|
|
hovered = root->getElementFromPoint(m_down_pos);
|
|
|
|
|
|
|
|
translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
|
|
|
|
translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
|
|
|
|
translated.MouseInput.X = m_pointer.X;
|
|
|
|
translated.MouseInput.Y = m_pointer.Y;
|
|
|
|
|
|
|
|
if (hovered) {
|
|
|
|
hovered->OnEvent(translated);
|
|
|
|
}
|
|
|
|
|
|
|
|
translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
|
|
|
|
translated.MouseInput.ButtonStates = EMBSM_LEFT;
|
|
|
|
|
|
|
|
|
|
|
|
if (hovered) {
|
|
|
|
hovered->OnEvent(translated);
|
|
|
|
}
|
|
|
|
dont_send_event = true;
|
|
|
|
}
|
|
|
|
/* ignore unhandled 2 touch events ... accidental moving for example */
|
|
|
|
else if (event.TouchInput.touchedCount == 2) {
|
|
|
|
dont_send_event = true;
|
|
|
|
}
|
|
|
|
else if (event.TouchInput.touchedCount > 2) {
|
|
|
|
errorstream
|
|
|
|
<< "GUIFormSpecMenu::preprocessEvent to many multitouch events "
|
|
|
|
<< event.TouchInput.touchedCount << " ignoring them" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dont_send_event) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if translated event needs to be preprocessed again */
|
|
|
|
if (preprocessEvent(translated)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (hovered) {
|
|
|
|
grab();
|
|
|
|
bool retval = hovered->OnEvent(translated);
|
|
|
|
|
|
|
|
if (event.TouchInput.Event == ETIE_LEFT_UP) {
|
|
|
|
/* reset pointer */
|
|
|
|
m_pointer = v2s32(0,0);
|
|
|
|
}
|
|
|
|
drop();
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-05-30 03:07:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
m_doubleclickdetect[1].time = getTimeMs();
|
|
|
|
}
|
|
|
|
else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
|
|
|
|
u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, getTimeMs());
|
|
|
|
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;
|
|
|
|
}
|
2013-08-19 11:26:51 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-07-15 18:19:38 +02:00
|
|
|
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
2010-12-22 02:34:21 +01:00
|
|
|
{
|
2014-05-30 03:04:10 +02:00
|
|
|
if(event.EventType==EET_KEY_INPUT_EVENT) {
|
2011-08-13 22:44:31 +02:00
|
|
|
KeyPress kp(event.KeyInput);
|
2014-04-21 14:10:59 +02:00
|
|
|
if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
|
|
|
|
(kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
|
2014-05-30 03:04:10 +02:00
|
|
|
if (m_allowclose) {
|
2014-03-13 14:06:18 +01:00
|
|
|
doPause = false;
|
2014-03-04 19:57:39 +01:00
|
|
|
acceptInput(quit_mode_cancel);
|
2013-06-23 18:30:21 +02:00
|
|
|
quitMenu();
|
2014-03-04 19:57:39 +01:00
|
|
|
} else {
|
2013-06-23 18:30:21 +02:00
|
|
|
m_text_dst->gotText(narrow_to_wide("MenuQuit"));
|
2013-10-26 09:56:38 +02:00
|
|
|
}
|
2010-12-22 02:34:21 +01:00
|
|
|
return true;
|
2014-09-21 02:23:55 +02:00
|
|
|
} else if (m_client != NULL && event.KeyInput.PressedDown &&
|
|
|
|
(kp == getKeySetting("keymap_screenshot"))) {
|
|
|
|
m_client->makeScreenshot(m_device);
|
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!
|
|
|
|
assert("reached a source line that can't ever been reached" == 0);
|
|
|
|
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;
|
2012-01-21 21:21:41 +01:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if(m_selected_item) {
|
2012-01-13 12:35:55 +01:00
|
|
|
inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
|
|
|
|
assert(inv_selected);
|
2012-01-21 21:21:41 +01:00
|
|
|
assert(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;
|
|
|
|
|
2012-01-13 12:35:55 +01: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
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if(!inv_s) {
|
2012-06-01 23:33:51 +02:00
|
|
|
errorstream<<"InventoryMenu: The selected inventory location "
|
|
|
|
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
|
|
|
|
<<std::endl;
|
|
|
|
s.i = -1; // make it invalid again
|
|
|
|
break;
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
InventoryList *list = inv_s->getList(s.listname);
|
2014-05-30 03:04:10 +02:00
|
|
|
if(list == NULL) {
|
2012-07-25 15:30:23 +02:00
|
|
|
verbosestream<<"InventoryMenu: The selected inventory list \""
|
2012-06-01 18:50:00 +02:00
|
|
|
<<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;
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if((u32)s.i >= list->getSize()) {
|
2012-07-25 15:30:23 +02:00
|
|
|
infostream<<"InventoryMenu: The selected inventory list \""
|
2012-06-01 18:50:00 +02:00
|
|
|
<<s.listname<<"\" is too small (i="<<s.i<<", size="
|
2012-06-01 18:33:20 +02:00
|
|
|
<<list->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
|
|
|
|
|
|
|
s_count = list->getItem(s.i).count;
|
2014-05-30 03:04:10 +02:00
|
|
|
} while(0);
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2012-01-22 00:49:02 +01:00
|
|
|
bool identical = (m_selected_item != NULL) && s.isValid() &&
|
|
|
|
(inv_selected == inv_s) &&
|
|
|
|
(m_selected_item->listname == s.listname) &&
|
|
|
|
(m_selected_item->i == s.i);
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
// buttons: 0 = left, 1 = right, 2 = middle
|
2014-09-19 15:27:48 +02:00
|
|
|
// up/down: 0 = down (press), 1 = up (release), 2 = unknown event, -1 movement
|
2012-01-21 21:21:41 +01:00
|
|
|
int button = 0;
|
|
|
|
int updown = 2;
|
2012-01-13 12:35:55 +01:00
|
|
|
if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
|
2012-01-21 21:21:41 +01:00
|
|
|
{ button = 0; updown = 0; }
|
2011-08-25 18:43:19 +02:00
|
|
|
else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
|
2012-01-21 21:21:41 +01:00
|
|
|
{ button = 1; updown = 0; }
|
2011-08-25 18:43:19 +02:00
|
|
|
else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
|
2012-01-21 21:21:41 +01:00
|
|
|
{ button = 2; updown = 0; }
|
|
|
|
else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
|
|
|
|
{ button = 0; updown = 1; }
|
|
|
|
else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
|
|
|
|
{ button = 1; updown = 1; }
|
|
|
|
else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
|
|
|
|
{ button = 2; updown = 1; }
|
2014-09-19 15:27:48 +02:00
|
|
|
else if(event.MouseInput.Event == EMIE_MOUSE_MOVED)
|
|
|
|
{ updown = -1;}
|
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;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if(updown == 0) {
|
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;
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if(s.isValid() && s.listname == "craftpreview") {
|
2012-01-21 21:21:41 +01:00
|
|
|
// Craft preview has been clicked: craft
|
|
|
|
craft_amount = (button == 2 ? 10 : 1);
|
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
else if(m_selected_item == NULL) {
|
|
|
|
if(s_count != 0) {
|
2012-01-21 21:21:41 +01:00
|
|
|
// Non-empty stack has been clicked: select it
|
|
|
|
m_selected_item = new ItemSpec(s);
|
|
|
|
|
|
|
|
if(button == 1) // right
|
|
|
|
m_selected_amount = (s_count + 1) / 2;
|
|
|
|
else if(button == 2) // middle
|
|
|
|
m_selected_amount = MYMIN(s_count, 10);
|
|
|
|
else // left
|
|
|
|
m_selected_amount = s_count;
|
|
|
|
|
|
|
|
m_selected_dragging = true;
|
2014-10-09 09:53:20 +02:00
|
|
|
m_rmouse_auto_place = false;
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
else { // m_selected_item != NULL
|
2012-01-21 21:21:41 +01:00
|
|
|
assert(m_selected_amount >= 1);
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if(s.isValid()) {
|
2012-01-22 00:49:02 +01:00
|
|
|
// Clicked a slot: move
|
2012-01-21 21:21:41 +01:00
|
|
|
if(button == 1) // right
|
|
|
|
move_amount = 1;
|
|
|
|
else if(button == 2) // middle
|
|
|
|
move_amount = MYMIN(m_selected_amount, 10);
|
|
|
|
else // left
|
|
|
|
move_amount = m_selected_amount;
|
2012-01-22 00:49:02 +01:00
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if(identical) {
|
2012-01-22 00:49:02 +01:00
|
|
|
if(move_amount >= m_selected_amount)
|
|
|
|
m_selected_amount = 0;
|
|
|
|
else
|
|
|
|
m_selected_amount -= move_amount;
|
|
|
|
move_amount = 0;
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
|
2012-01-21 21:21:41 +01:00
|
|
|
// Clicked outside of the window: drop
|
|
|
|
if(button == 1) // right
|
|
|
|
drop_amount = 1;
|
|
|
|
else if(button == 2) // middle
|
|
|
|
drop_amount = MYMIN(m_selected_amount, 10);
|
|
|
|
else // left
|
|
|
|
drop_amount = m_selected_amount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
else if(updown == 1) {
|
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;
|
|
|
|
|
2014-05-30 03:04:10 +02:00
|
|
|
if(m_selected_item != NULL && m_selected_dragging && s.isValid()) {
|
|
|
|
if(!identical) {
|
2012-01-21 21:21:41 +01:00
|
|
|
// Dragged to different slot: move all selected
|
|
|
|
move_amount = m_selected_amount;
|
2010-12-22 15:30:23 +01:00
|
|
|
}
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
else if(m_selected_item != NULL && m_selected_dragging &&
|
2014-05-30 03:04:10 +02:00
|
|
|
!(getAbsoluteClippingRect().isPointInside(m_pointer))) {
|
2012-01-21 21:21:41 +01:00
|
|
|
// Dragged outside of window: drop all selected
|
|
|
|
drop_amount = m_selected_amount;
|
|
|
|
}
|
2012-01-13 12:35:55 +01:00
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
m_selected_dragging = false;
|
2014-10-09 09:53:20 +02:00
|
|
|
// Keep count of how many times right mouse button has been
|
|
|
|
// clicked. One click is drag without dropping. Click + release
|
|
|
|
// + click changes to drop one item when moved mode
|
|
|
|
if(button == 1 && m_selected_item != NULL)
|
|
|
|
m_rmouse_auto_place = !m_rmouse_auto_place;
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
2014-09-19 15:27:48 +02:00
|
|
|
else if(updown == -1) {
|
|
|
|
// 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)
|
|
|
|
if(m_selected_item != NULL && s.isValid()){
|
|
|
|
// Move 1 item
|
|
|
|
// TODO: middle mouse to move 10 items might be handy
|
2014-10-09 09:53:20 +02:00
|
|
|
if (m_rmouse_auto_place) {
|
|
|
|
// 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);
|
|
|
|
InventoryList *list_to = inv_s->getList(s.listname);
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
// Possibly send inventory action to server
|
|
|
|
if(move_amount > 0)
|
|
|
|
{
|
|
|
|
// Send IACTION_MOVE
|
|
|
|
|
|
|
|
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);
|
|
|
|
InventoryList *list_to = inv_s->getList(s.listname);
|
|
|
|
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);
|
|
|
|
ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
|
2012-09-02 21:51:38 +02:00
|
|
|
// If source stack cannot be added to destination stack at all,
|
|
|
|
// they are swapped
|
2014-05-30 03:04:10 +02:00
|
|
|
if ((leftover.count == stack_from.count) &&
|
|
|
|
(leftover.name == stack_from.name)) {
|
2012-09-02 19:13:11 +02:00
|
|
|
m_selected_amount = stack_to.count;
|
2012-09-02 21:51:38 +02:00
|
|
|
// In case the server doesn't directly swap them but instead
|
|
|
|
// moves stack_to somewhere else, set this
|
|
|
|
m_selected_content_guess = stack_to;
|
|
|
|
m_selected_content_guess_inventory = s.inventoryloc;
|
2012-01-21 21:21:41 +01:00
|
|
|
}
|
2012-09-02 21:51:38 +02:00
|
|
|
// Source stack goes fully into destination stack
|
2014-05-30 03:04:10 +02:00
|
|
|
else if(leftover.empty()) {
|
2012-01-22 00:49:02 +01:00
|
|
|
m_selected_amount -= move_amount;
|
2012-09-02 21:51:38 +02:00
|
|
|
m_selected_content_guess = ItemStack(); // Clear
|
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-09-02 21:51:38 +02:00
|
|
|
m_selected_content_guess = ItemStack(); // Clear
|
2012-01-13 12:35:55 +01:00
|
|
|
}
|
2012-01-21 21:21:41 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
else if(drop_amount > 0) {
|
2012-09-02 21:51:38 +02:00
|
|
|
m_selected_content_guess = ItemStack(); // Clear
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
// Send IACTION_DROP
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
infostream<<"Handing IACTION_DROP to manager"<<std::endl;
|
|
|
|
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);
|
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
else if(craft_amount > 0) {
|
2012-09-02 21:51:38 +02:00
|
|
|
m_selected_content_guess = ItemStack(); // Clear
|
|
|
|
|
2012-01-21 21:21:41 +01:00
|
|
|
// Send IACTION_CRAFT
|
|
|
|
|
|
|
|
assert(s.isValid());
|
|
|
|
assert(inv_s);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If m_selected_amount has been decreased to zero, deselect
|
2014-05-30 03:04:10 +02:00
|
|
|
if(m_selected_amount == 0) {
|
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;
|
2012-09-02 21:51:38 +02:00
|
|
|
m_selected_content_guess = ItemStack();
|
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
|
|
|
}
|
2014-05-30 03:04:10 +02:00
|
|
|
if(event.EventType==EET_GUI_EVENT) {
|
2013-06-23 18:30:21 +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
|
2014-05-30 03:04:10 +02:00
|
|
|
for(unsigned int i=0; i<m_fields.size(); i++) {
|
2013-06-23 18:30:21 +02:00
|
|
|
FieldSpec &s = m_fields[i];
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-12-22 02:34:21 +01:00
|
|
|
if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
|
2014-05-30 03:04:10 +02:00
|
|
|
&& isVisible()) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2014-06-14 12:27:56 +02:00
|
|
|
if((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
|
|
|
|
(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();
|
2013-06-23 18:30:21 +02:00
|
|
|
m_text_dst->gotText(narrow_to_wide("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
|
2014-05-30 03:04:10 +02:00
|
|
|
for(u32 i=0; i<m_fields.size(); i++) {
|
2012-07-15 18:19:38 +02:00
|
|
|
FieldSpec &s = m_fields[i];
|
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
|
2013-06-23 18:30:21 +02:00
|
|
|
if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
|
2014-05-30 03:04:10 +02:00
|
|
|
(s.fid == event.GUIEvent.Caller->getID())) {
|
2012-07-15 18:19:38 +02:00
|
|
|
s.send = true;
|
2014-05-30 03:04:10 +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 {
|
2013-06-23 18:30:21 +02:00
|
|
|
m_text_dst->gotText(narrow_to_wide("ExitButton"));
|
2013-10-26 09:56:38 +02:00
|
|
|
}
|
2012-07-22 16:28:09 +02:00
|
|
|
return true;
|
2014-05-30 03:04:10 +02:00
|
|
|
} else {
|
2014-06-14 12:27:56 +02:00
|
|
|
acceptInput(quit_mode_no);
|
2012-07-22 16:28:09 +02:00
|
|
|
s.send = false;
|
|
|
|
return true;
|
|
|
|
}
|
2012-07-15 18:19:38 +02:00
|
|
|
}
|
2014-06-19 18:17:35 +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
|
|
|
|
for(u32 i=0; i<m_fields.size(); i++) {
|
|
|
|
FieldSpec &s2 = m_fields[i];
|
|
|
|
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
|
|
|
|
for(u32 i=0; i<m_fields.size(); i++) {
|
|
|
|
FieldSpec &s2 = m_fields[i];
|
|
|
|
if (s2.ftype == f_DropDown) {
|
|
|
|
s2.send = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2014-06-19 18:17:35 +02:00
|
|
|
else if ((s.ftype == f_ScrollBar) &&
|
|
|
|
(s.fid == event.GUIEvent.Caller->getID()))
|
|
|
|
{
|
|
|
|
s.fdefault = L"Changed";
|
|
|
|
acceptInput(quit_mode_no);
|
|
|
|
s.fdefault = L"";
|
|
|
|
}
|
2012-07-15 18:19:38 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-19 18:17:35 +02:00
|
|
|
|
2014-06-14 12:27:56 +02:00
|
|
|
if(event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
|
2014-05-30 03:04:10 +02:00
|
|
|
if(event.GUIEvent.Caller->getID() > 257) {
|
2013-07-07 21:53:40 +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();
|
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
|
|
|
|
2014-06-14 12:27:56 +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();
|
2014-05-30 03:04:10 +02:00
|
|
|
if(current_id > 257) {
|
2013-06-23 18:30:21 +02:00
|
|
|
// find the element that was clicked
|
2014-05-30 03:04:10 +02:00
|
|
|
for(u32 i=0; i<m_fields.size(); i++) {
|
2013-06-23 18:30:21 +02:00
|
|
|
FieldSpec &s = m_fields[i];
|
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
|
|
|
|
*/
|
|
|
|
std::wstring GUIFormSpecMenu::getNameByID(s32 id)
|
|
|
|
{
|
|
|
|
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
|
|
|
|
iter != m_fields.end(); iter++) {
|
|
|
|
if (iter->fid == id) {
|
|
|
|
return iter->fname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return L"";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get label of element by id
|
|
|
|
* @param id of element
|
|
|
|
* @return label string or empty string
|
|
|
|
*/
|
|
|
|
std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
|
|
|
|
{
|
|
|
|
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
|
|
|
|
iter != m_fields.end(); iter++) {
|
|
|
|
if (iter->fid == id) {
|
|
|
|
return iter->flabel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return L"";
|
|
|
|
}
|