aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/lua_api.txt26
-rw-r--r--src/gui/guiFormSpecMenu.cpp106
-rw-r--r--src/gui/guiFormSpecMenu.h9
3 files changed, 102 insertions, 39 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 2ac4d6766..4e6983be8 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -2492,7 +2492,7 @@ Elements
* There are two ways to use it:
1. handle the changed event (only changed scrollbar is available)
2. read the value on pressing a button (all scrollbars are available)
-* `orientation`: `vertical`/`horizontal`
+* `orientation`: `vertical`/`horizontal`. Default horizontal.
* Fieldname data is transferred to Lua
* Value of this trackbar is set to (`0`-`1000`) by default
* See also `minetest.explode_scrollbar_event`
@@ -2606,6 +2606,28 @@ Elements
* All provided states must be active for the style to apply.
* See [Styling Formspecs].
+### `set_focus[<name>;<force>]`
+
+* Sets the focus to the element with the same `name` parameter.
+* **Note**: This element must be placed before the element it focuses.
+* `force` (optional, default `false`): By default, focus is not applied for
+ re-sent formspecs with the same name so that player-set focus is kept.
+ `true` sets the focus to the specified element for every sent formspec.
+* The following elements have the ability to be focused:
+ * checkbox
+ * button
+ * button_exit
+ * image_button
+ * image_button_exit
+ * item_image_button
+ * table
+ * textlist
+ * dropdown
+ * field
+ * pwdfield
+ * textarea
+ * scrollbar
+
Migrating to Real Coordinates
-----------------------------
@@ -4485,7 +4507,7 @@ Call these functions only at load time!
* a button was pressed,
* Enter was pressed while the focus was on a text field
* a checkbox was toggled,
- * something was selecteed in a drop-down list,
+ * something was selected in a dropdown list,
* a different tab was selected,
* selection was changed in a textlist or table,
* an entry was double-clicked in a textlist or table,
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index 54fc94993..9618e2134 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -627,7 +627,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
auto style = getDefaultStyleForElement("checkbox", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -703,6 +703,10 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size);
+ if (spec.fname == m_focused_element) {
+ Environment->setFocus(e);
+ }
+
m_scrollbars.emplace_back(spec,e);
m_fields.push_back(spec);
return;
@@ -1029,7 +1033,7 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
e->setStyles(style);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1218,7 +1222,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
rect, m_tsrc);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1295,7 +1299,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
rect, m_tsrc);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1373,7 +1377,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent,
spec.fid);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1460,7 +1464,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true,
data->current_parent, spec.fid);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1537,7 +1541,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname);
if (e) {
- if (is_editable && spec.fname == data->focused_fieldname)
+ if (is_editable && spec.fname == m_focused_element)
Environment->setFocus(e);
if (is_multiline) {
@@ -1986,7 +1990,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc,
data->current_parent, spec.fid, spec.flabel.c_str());
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -2099,10 +2103,6 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
e->setTabHeight(geom.Y);
- if (spec.fname == data->focused_fieldname) {
- Environment->setFocus(e);
- }
-
auto style = getDefaultStyleForElement("tabheader", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
@@ -2194,7 +2194,7 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button");
e_btn->setStyles(style);
- if (spec_btn.fname == data->focused_fieldname) {
+ if (spec_btn.fname == m_focused_element) {
Environment->setFocus(e_btn);
}
@@ -2665,6 +2665,27 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b
return true;
}
+void GUIFormSpecMenu::parseSetFocus(const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() <= 2 ||
+ (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION))
+ {
+ if (m_is_form_regenerated)
+ return; // Never focus on resizing
+
+ bool force_focus = parts.size() >= 2 && is_yes(parts[1]);
+ if (force_focus || m_text_dst->m_formname != m_last_formname)
+ setFocus(parts[0]);
+
+ return;
+ }
+
+ errorstream << "Invalid set_focus element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+}
+
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
@@ -2856,6 +2877,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
+ if (type == "set_focus") {
+ parseSetFocus(description);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
@@ -2863,36 +2889,38 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{
- /* useless to regenerate without a screensize */
+ // Useless to regenerate without a screensize
if ((screensize.X <= 0) || (screensize.Y <= 0)) {
return;
}
parserData mydata;
- //preserve tables
- for (auto &m_table : m_tables) {
- std::string tablename = m_table.first.fname;
- GUITable *table = m_table.second;
- mydata.table_dyndata[tablename] = table->getDynamicData();
- }
-
- //set focus
- if (!m_focused_element.empty())
- mydata.focused_fieldname = m_focused_element;
+ // Preserve stuff only on same form, not on a new form.
+ if (m_text_dst->m_formname == m_last_formname) {
+ // Preserve tables/textlists
+ for (auto &m_table : m_tables) {
+ std::string tablename = m_table.first.fname;
+ GUITable *table = m_table.second;
+ mydata.table_dyndata[tablename] = table->getDynamicData();
+ }
- //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 (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
- if (field.fid == focused_id) {
- mydata.focused_fieldname = field.fname;
- break;
+ // 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 (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
+ if (field.fid == focused_id) {
+ m_focused_element = field.fname;
+ break;
+ }
}
}
}
+ } else {
+ // Don't keep old focus value
+ m_focused_element = "";
}
// Remove children
@@ -3253,8 +3281,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
}
}
- //set initial focus if parser didn't set it
- focused_element = Environment->getFocus();
+ // Set initial focus if parser didn't set it
+ gui::IGUIElement *focused_element = Environment->getFocus();
if (!focused_element
|| !isMyChild(focused_element)
|| focused_element->getType() == gui::EGUIET_TAB_CONTROL)
@@ -3265,6 +3293,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
// legacy sorting
if (m_formspec_version < 3)
legacySortElements(legacy_sort_start);
+
+ // Formname and regeneration setting
+ if (!m_is_form_regenerated) {
+ // Only set previous form name if we purposefully showed a new formspec
+ m_last_formname = m_text_dst->m_formname;
+ m_is_form_regenerated = true;
+ }
}
void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator from)
@@ -3382,6 +3417,7 @@ void GUIFormSpecMenu::drawMenu()
const std::string &newform = m_form_src->getForm();
if (newform != m_formspec_string) {
m_formspec_string = newform;
+ m_is_form_regenerated = false;
regenerateGui(m_screensize_old);
}
}
diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h
index a91afd2f7..19026bd34 100644
--- a/src/gui/guiFormSpecMenu.h
+++ b/src/gui/guiFormSpecMenu.h
@@ -168,6 +168,7 @@ public:
{
m_formspec_string = formspec_string;
m_current_inventory_location = current_inventory_location;
+ m_is_form_regenerated = false;
regenerateGui(m_screensize_old);
}
@@ -299,6 +300,10 @@ protected:
std::string m_formspec_prepend;
InventoryLocation m_current_inventory_location;
+ // Default true because we can't control regeneration on resizing, but
+ // we can control cases when the formspec is shown intentionally.
+ bool m_is_form_regenerated = true;
+
std::vector<GUIInventoryList *> m_inventorylists;
std::vector<ListRingSpec> m_inventory_rings;
std::vector<gui::IGUIElement *> m_backgrounds;
@@ -339,10 +344,10 @@ protected:
video::SColor m_default_tooltip_bgcolor;
video::SColor m_default_tooltip_color;
-
private:
IFormSource *m_form_src;
TextDest *m_text_dst;
+ std::string m_last_formname;
u16 m_formspec_version = 1;
std::string m_focused_element = "";
JoystickController *m_joystick;
@@ -359,7 +364,6 @@ private:
core::rect<s32> rect;
v2s32 basepos;
v2u32 screensize;
- std::string focused_fieldname;
GUITable::TableOptions table_options;
GUITable::TableColumns table_columns;
gui::IGUIElement *current_parent = nullptr;
@@ -439,6 +443,7 @@ private:
bool parseAnchorDirect(parserData *data, const std::string &element);
void parseAnchor(parserData *data, const std::string &element);
bool parseStyle(parserData *data, const std::string &element, bool style_type);
+ void parseSetFocus(const std::string &element);
void tryClose();