Allow FormSpec elements to be focused with set_focus
(#9353)
This allows you to specify a FormSpec element to set the focus of with "set_focus[<name>;<always set>]".
This commit is contained in:
parent
d80def5bbf
commit
e0499731a8
@ -2492,7 +2492,7 @@ Elements
|
|||||||
* There are two ways to use it:
|
* There are two ways to use it:
|
||||||
1. handle the changed event (only changed scrollbar is available)
|
1. handle the changed event (only changed scrollbar is available)
|
||||||
2. read the value on pressing a button (all scrollbars are 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
|
* Fieldname data is transferred to Lua
|
||||||
* Value of this trackbar is set to (`0`-`1000`) by default
|
* Value of this trackbar is set to (`0`-`1000`) by default
|
||||||
* See also `minetest.explode_scrollbar_event`
|
* See also `minetest.explode_scrollbar_event`
|
||||||
@ -2606,6 +2606,28 @@ Elements
|
|||||||
* All provided states must be active for the style to apply.
|
* All provided states must be active for the style to apply.
|
||||||
* See [Styling Formspecs].
|
* 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
|
Migrating to Real Coordinates
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
@ -4485,7 +4507,7 @@ Call these functions only at load time!
|
|||||||
* a button was pressed,
|
* a button was pressed,
|
||||||
* Enter was pressed while the focus was on a text field
|
* Enter was pressed while the focus was on a text field
|
||||||
* a checkbox was toggled,
|
* a checkbox was toggled,
|
||||||
* something was selecteed in a drop-down list,
|
* something was selected in a dropdown list,
|
||||||
* a different tab was selected,
|
* a different tab was selected,
|
||||||
* selection was changed in a textlist or table,
|
* selection was changed in a textlist or table,
|
||||||
* an entry was double-clicked in a textlist or table,
|
* an entry was double-clicked in a textlist or table,
|
||||||
|
@ -627,7 +627,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
|
|||||||
auto style = getDefaultStyleForElement("checkbox", name);
|
auto style = getDefaultStyleForElement("checkbox", name);
|
||||||
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
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);
|
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_scrollbars.emplace_back(spec,e);
|
||||||
m_fields.push_back(spec);
|
m_fields.push_back(spec);
|
||||||
return;
|
return;
|
||||||
@ -1029,7 +1033,7 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
|
|||||||
auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
|
auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
|
||||||
e->setStyles(style);
|
e->setStyles(style);
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
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,
|
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
|
||||||
rect, m_tsrc);
|
rect, m_tsrc);
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
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,
|
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
|
||||||
rect, m_tsrc);
|
rect, m_tsrc);
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
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,
|
gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent,
|
||||||
spec.fid);
|
spec.fid);
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
Environment->setFocus(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1460,7 +1464,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
|
|||||||
gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true,
|
gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true,
|
||||||
data->current_parent, spec.fid);
|
data->current_parent, spec.fid);
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
Environment->setFocus(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1537,7 +1541,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
|
|||||||
auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname);
|
auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname);
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
if (is_editable && spec.fname == data->focused_fieldname)
|
if (is_editable && spec.fname == m_focused_element)
|
||||||
Environment->setFocus(e);
|
Environment->setFocus(e);
|
||||||
|
|
||||||
if (is_multiline) {
|
if (is_multiline) {
|
||||||
@ -1986,7 +1990,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
|
|||||||
GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc,
|
GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc,
|
||||||
data->current_parent, spec.fid, spec.flabel.c_str());
|
data->current_parent, spec.fid, spec.flabel.c_str());
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
Environment->setFocus(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2099,10 +2103,6 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
|
|||||||
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
|
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
|
||||||
e->setTabHeight(geom.Y);
|
e->setTabHeight(geom.Y);
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
|
||||||
Environment->setFocus(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto style = getDefaultStyleForElement("tabheader", name);
|
auto style = getDefaultStyleForElement("tabheader", name);
|
||||||
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
|
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");
|
auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button");
|
||||||
e_btn->setStyles(style);
|
e_btn->setStyles(style);
|
||||||
|
|
||||||
if (spec_btn.fname == data->focused_fieldname) {
|
if (spec_btn.fname == m_focused_element) {
|
||||||
Environment->setFocus(e_btn);
|
Environment->setFocus(e_btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2665,6 +2665,27 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b
|
|||||||
return true;
|
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)
|
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
||||||
{
|
{
|
||||||
//some prechecks
|
//some prechecks
|
||||||
@ -2856,6 +2877,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == "set_focus") {
|
||||||
|
parseSetFocus(description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore others
|
// Ignore others
|
||||||
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
|
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
@ -2863,37 +2889,39 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
|||||||
|
|
||||||
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
||||||
{
|
{
|
||||||
/* useless to regenerate without a screensize */
|
// Useless to regenerate without a screensize
|
||||||
if ((screensize.X <= 0) || (screensize.Y <= 0)) {
|
if ((screensize.X <= 0) || (screensize.Y <= 0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parserData mydata;
|
parserData mydata;
|
||||||
|
|
||||||
//preserve tables
|
// 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) {
|
for (auto &m_table : m_tables) {
|
||||||
std::string tablename = m_table.first.fname;
|
std::string tablename = m_table.first.fname;
|
||||||
GUITable *table = m_table.second;
|
GUITable *table = m_table.second;
|
||||||
mydata.table_dyndata[tablename] = table->getDynamicData();
|
mydata.table_dyndata[tablename] = table->getDynamicData();
|
||||||
}
|
}
|
||||||
|
|
||||||
//set focus
|
// Preserve focus
|
||||||
if (!m_focused_element.empty())
|
|
||||||
mydata.focused_fieldname = m_focused_element;
|
|
||||||
|
|
||||||
//preserve focus
|
|
||||||
gui::IGUIElement *focused_element = Environment->getFocus();
|
gui::IGUIElement *focused_element = Environment->getFocus();
|
||||||
if (focused_element && focused_element->getParent() == this) {
|
if (focused_element && focused_element->getParent() == this) {
|
||||||
s32 focused_id = focused_element->getID();
|
s32 focused_id = focused_element->getID();
|
||||||
if (focused_id > 257) {
|
if (focused_id > 257) {
|
||||||
for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
|
for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
|
||||||
if (field.fid == focused_id) {
|
if (field.fid == focused_id) {
|
||||||
mydata.focused_fieldname = field.fname;
|
m_focused_element = field.fname;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Don't keep old focus value
|
||||||
|
m_focused_element = "";
|
||||||
|
}
|
||||||
|
|
||||||
// Remove children
|
// Remove children
|
||||||
removeChildren();
|
removeChildren();
|
||||||
@ -3253,8 +3281,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//set initial focus if parser didn't set it
|
// Set initial focus if parser didn't set it
|
||||||
focused_element = Environment->getFocus();
|
gui::IGUIElement *focused_element = Environment->getFocus();
|
||||||
if (!focused_element
|
if (!focused_element
|
||||||
|| !isMyChild(focused_element)
|
|| !isMyChild(focused_element)
|
||||||
|| focused_element->getType() == gui::EGUIET_TAB_CONTROL)
|
|| focused_element->getType() == gui::EGUIET_TAB_CONTROL)
|
||||||
@ -3265,6 +3293,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||||||
// legacy sorting
|
// legacy sorting
|
||||||
if (m_formspec_version < 3)
|
if (m_formspec_version < 3)
|
||||||
legacySortElements(legacy_sort_start);
|
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)
|
void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator from)
|
||||||
@ -3382,6 +3417,7 @@ void GUIFormSpecMenu::drawMenu()
|
|||||||
const std::string &newform = m_form_src->getForm();
|
const std::string &newform = m_form_src->getForm();
|
||||||
if (newform != m_formspec_string) {
|
if (newform != m_formspec_string) {
|
||||||
m_formspec_string = newform;
|
m_formspec_string = newform;
|
||||||
|
m_is_form_regenerated = false;
|
||||||
regenerateGui(m_screensize_old);
|
regenerateGui(m_screensize_old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,6 +168,7 @@ public:
|
|||||||
{
|
{
|
||||||
m_formspec_string = formspec_string;
|
m_formspec_string = formspec_string;
|
||||||
m_current_inventory_location = current_inventory_location;
|
m_current_inventory_location = current_inventory_location;
|
||||||
|
m_is_form_regenerated = false;
|
||||||
regenerateGui(m_screensize_old);
|
regenerateGui(m_screensize_old);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,6 +300,10 @@ protected:
|
|||||||
std::string m_formspec_prepend;
|
std::string m_formspec_prepend;
|
||||||
InventoryLocation m_current_inventory_location;
|
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<GUIInventoryList *> m_inventorylists;
|
||||||
std::vector<ListRingSpec> m_inventory_rings;
|
std::vector<ListRingSpec> m_inventory_rings;
|
||||||
std::vector<gui::IGUIElement *> m_backgrounds;
|
std::vector<gui::IGUIElement *> m_backgrounds;
|
||||||
@ -339,10 +344,10 @@ protected:
|
|||||||
video::SColor m_default_tooltip_bgcolor;
|
video::SColor m_default_tooltip_bgcolor;
|
||||||
video::SColor m_default_tooltip_color;
|
video::SColor m_default_tooltip_color;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IFormSource *m_form_src;
|
IFormSource *m_form_src;
|
||||||
TextDest *m_text_dst;
|
TextDest *m_text_dst;
|
||||||
|
std::string m_last_formname;
|
||||||
u16 m_formspec_version = 1;
|
u16 m_formspec_version = 1;
|
||||||
std::string m_focused_element = "";
|
std::string m_focused_element = "";
|
||||||
JoystickController *m_joystick;
|
JoystickController *m_joystick;
|
||||||
@ -359,7 +364,6 @@ private:
|
|||||||
core::rect<s32> rect;
|
core::rect<s32> rect;
|
||||||
v2s32 basepos;
|
v2s32 basepos;
|
||||||
v2u32 screensize;
|
v2u32 screensize;
|
||||||
std::string focused_fieldname;
|
|
||||||
GUITable::TableOptions table_options;
|
GUITable::TableOptions table_options;
|
||||||
GUITable::TableColumns table_columns;
|
GUITable::TableColumns table_columns;
|
||||||
gui::IGUIElement *current_parent = nullptr;
|
gui::IGUIElement *current_parent = nullptr;
|
||||||
@ -439,6 +443,7 @@ private:
|
|||||||
bool parseAnchorDirect(parserData *data, const std::string &element);
|
bool parseAnchorDirect(parserData *data, const std::string &element);
|
||||||
void parseAnchor(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);
|
bool parseStyle(parserData *data, const std::string &element, bool style_type);
|
||||||
|
void parseSetFocus(const std::string &element);
|
||||||
|
|
||||||
void tryClose();
|
void tryClose();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user