forked from Mirrorlandia_minetest/minetest
Add formspec table
This commit is contained in:
parent
2b1eff7725
commit
8966c16ad2
@ -31,7 +31,7 @@ end
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function gamemgr.handle_games_buttons(fields)
|
function gamemgr.handle_games_buttons(fields)
|
||||||
if fields["gamelist"] ~= nil then
|
if fields["gamelist"] ~= nil then
|
||||||
local event = explode_textlist_event(fields["gamelist"])
|
local event = engine.explode_textlist_event(fields["gamelist"])
|
||||||
gamemgr.selected_game = event.index
|
gamemgr.selected_game = event.index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -459,8 +459,8 @@ function tabbuilder.handle_multiplayer_buttons(fields)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if fields["favourites"] ~= nil then
|
if fields["favourites"] ~= nil then
|
||||||
local event = explode_textlist_event(fields["favourites"])
|
local event = engine.explode_textlist_event(fields["favourites"])
|
||||||
if event.typ == "DCL" then
|
if event.type == "DCL" then
|
||||||
if event.index <= #menu.favorites then
|
if event.index <= #menu.favorites then
|
||||||
gamedata.address = menu.favorites[event.index].address
|
gamedata.address = menu.favorites[event.index].address
|
||||||
gamedata.port = menu.favorites[event.index].port
|
gamedata.port = menu.favorites[event.index].port
|
||||||
@ -484,7 +484,7 @@ function tabbuilder.handle_multiplayer_buttons(fields)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if event.typ == "CHG" then
|
if event.type == "CHG" then
|
||||||
if event.index <= #menu.favorites then
|
if event.index <= #menu.favorites then
|
||||||
local address = menu.favorites[event.index].address
|
local address = menu.favorites[event.index].address
|
||||||
local port = menu.favorites[event.index].port
|
local port = menu.favorites[event.index].port
|
||||||
@ -586,12 +586,12 @@ function tabbuilder.handle_server_buttons(fields)
|
|||||||
local world_doubleclick = false
|
local world_doubleclick = false
|
||||||
|
|
||||||
if fields["srv_worlds"] ~= nil then
|
if fields["srv_worlds"] ~= nil then
|
||||||
local event = explode_textlist_event(fields["srv_worlds"])
|
local event = engine.explode_textlist_event(fields["srv_worlds"])
|
||||||
|
|
||||||
if event.typ == "DCL" then
|
if event.type == "DCL" then
|
||||||
world_doubleclick = true
|
world_doubleclick = true
|
||||||
end
|
end
|
||||||
if event.typ == "CHG" then
|
if event.type == "CHG" then
|
||||||
engine.setting_set("mainmenu_last_selected_world",
|
engine.setting_set("mainmenu_last_selected_world",
|
||||||
filterlist.get_raw_index(worldlist,engine.get_textlist_index("srv_worlds")))
|
filterlist.get_raw_index(worldlist,engine.get_textlist_index("srv_worlds")))
|
||||||
end
|
end
|
||||||
@ -737,13 +737,13 @@ function tabbuilder.handle_singleplayer_buttons(fields)
|
|||||||
local world_doubleclick = false
|
local world_doubleclick = false
|
||||||
|
|
||||||
if fields["sp_worlds"] ~= nil then
|
if fields["sp_worlds"] ~= nil then
|
||||||
local event = explode_textlist_event(fields["sp_worlds"])
|
local event = engine.explode_textlist_event(fields["sp_worlds"])
|
||||||
|
|
||||||
if event.typ == "DCL" then
|
if event.type == "DCL" then
|
||||||
world_doubleclick = true
|
world_doubleclick = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if event.typ == "CHG" then
|
if event.type == "CHG" then
|
||||||
engine.setting_set("mainmenu_last_selected_world",
|
engine.setting_set("mainmenu_last_selected_world",
|
||||||
filterlist.get_raw_index(worldlist,engine.get_textlist_index("sp_worlds")))
|
filterlist.get_raw_index(worldlist,engine.get_textlist_index("sp_worlds")))
|
||||||
end
|
end
|
||||||
@ -813,8 +813,8 @@ end
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function tabbuilder.handle_texture_pack_buttons(fields)
|
function tabbuilder.handle_texture_pack_buttons(fields)
|
||||||
if fields["TPs"] ~= nil then
|
if fields["TPs"] ~= nil then
|
||||||
local event = explode_textlist_event(fields["TPs"])
|
local event = engine.explode_textlist_event(fields["TPs"])
|
||||||
if event.typ == "CHG" or event.typ=="DCL" then
|
if event.type == "CHG" or event.type == "DCL" then
|
||||||
local index = engine.get_textlist_index("TPs")
|
local index = engine.get_textlist_index("TPs")
|
||||||
engine.setting_set("mainmenu_last_selected_TP",
|
engine.setting_set("mainmenu_last_selected_TP",
|
||||||
index)
|
index)
|
||||||
|
@ -115,26 +115,6 @@ function math.hypot(x, y)
|
|||||||
return x * math.sqrt(1 + t * t)
|
return x * math.sqrt(1 + t * t)
|
||||||
end
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
function explode_textlist_event(text)
|
|
||||||
|
|
||||||
local retval = {}
|
|
||||||
retval.typ = "INV"
|
|
||||||
|
|
||||||
local parts = text:split(":")
|
|
||||||
|
|
||||||
if #parts == 2 then
|
|
||||||
retval.typ = parts[1]:trim()
|
|
||||||
retval.index= tonumber(parts[2]:trim())
|
|
||||||
|
|
||||||
if type(retval.index) ~= "number" then
|
|
||||||
retval.typ = "INV"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return retval
|
|
||||||
end
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function get_last_folder(text,count)
|
function get_last_folder(text,count)
|
||||||
local parts = text:split(DIR_DELIM)
|
local parts = text:split(DIR_DELIM)
|
||||||
@ -368,6 +348,37 @@ if minetest then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
function tbl.explode_table_event(evt)
|
||||||
|
if evt ~= nil then
|
||||||
|
local parts = evt:split(":")
|
||||||
|
if #parts == 3 then
|
||||||
|
local t = parts[1]:trim()
|
||||||
|
local r = tonumber(parts[2]:trim())
|
||||||
|
local c = tonumber(parts[3]:trim())
|
||||||
|
if type(r) == "number" and type(c) == "number" and t ~= "INV" then
|
||||||
|
return {type=t, row=r, column=c}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return {type="INV", row=0, column=0}
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
function tbl.explode_textlist_event(evt)
|
||||||
|
if evt ~= nil then
|
||||||
|
local parts = evt:split(":")
|
||||||
|
if #parts == 2 then
|
||||||
|
local t = parts[1]:trim()
|
||||||
|
local r = tonumber(parts[2]:trim())
|
||||||
|
if type(r) == "number" and t ~= "INV" then
|
||||||
|
return {type=t, index=r}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return {type="INV", index=0}
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- mainmenu only functions
|
-- mainmenu only functions
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
@ -572,7 +572,7 @@ function modmgr.handle_modmgr_buttons(fields)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if fields["modlist"] ~= nil then
|
if fields["modlist"] ~= nil then
|
||||||
local event = explode_textlist_event(fields["modlist"])
|
local event = engine.explode_textlist_event(fields["modlist"])
|
||||||
modmgr.selected_mod = event.index
|
modmgr.selected_mod = event.index
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -693,10 +693,10 @@ end
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
function modmgr.handle_configure_world_buttons(fields)
|
function modmgr.handle_configure_world_buttons(fields)
|
||||||
if fields["world_config_modlist"] ~= nil then
|
if fields["world_config_modlist"] ~= nil then
|
||||||
local event = explode_textlist_event(fields["world_config_modlist"])
|
local event = engine.explode_textlist_event(fields["world_config_modlist"])
|
||||||
modmgr.world_config_selected_mod = event.index
|
modmgr.world_config_selected_mod = event.index
|
||||||
|
|
||||||
if event.typ == "DCL" then
|
if event.type == "DCL" then
|
||||||
modmgr.world_config_enable_mod(nil)
|
modmgr.world_config_enable_mod(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1011,6 +1011,7 @@ textlist[<X>,<Y>;<W>,<H>;<name>;<listelem 1>,<listelem 2>,...,<listelem n>;<sele
|
|||||||
^ if you want a listelement to start with # write ##
|
^ if you want a listelement to start with # write ##
|
||||||
^ index to be selected within textlist
|
^ index to be selected within textlist
|
||||||
^ true/false draw transparent background
|
^ true/false draw transparent background
|
||||||
|
^ see also minetest.explode_textlist_event (main menu: engine.explode_textlist_event)
|
||||||
|
|
||||||
tabheader[<X>,<Y>;<name>;<caption 1>,<caption 2>,...,<caption n>;<current_tab>;<transparent>;<draw_border>]
|
tabheader[<X>,<Y>;<name>;<caption 1>,<caption 2>,...,<caption n>;<current_tab>;<transparent>;<draw_border>]
|
||||||
^ show a tabHEADER at specific position (ignores formsize)
|
^ show a tabHEADER at specific position (ignores formsize)
|
||||||
@ -1043,6 +1044,57 @@ checkbox[<X>,<Y>;<name>;<label>;<selected>]
|
|||||||
^ label to be shown left of checkbox
|
^ label to be shown left of checkbox
|
||||||
^ selected (optional) true/false
|
^ selected (optional) true/false
|
||||||
|
|
||||||
|
table[<X>,<Y>;<W>,<H>;<name>;<cell 1>,<cell 2>,...,<cell n>;<selected idx>]
|
||||||
|
^ show scrollable table using options defined by the previous tableoptions[]
|
||||||
|
^ displays cells as defined by the previous tablecolumns[]
|
||||||
|
^ x and y position the itemlist relative to the top left of the menu
|
||||||
|
^ w and h are the size of the itemlist
|
||||||
|
^ name fieldname sent to server on row select or doubleclick
|
||||||
|
^ cell 1...n cell contents given in row-major order
|
||||||
|
^ selected idx: index of row to be selected within table (first row = 1)
|
||||||
|
^ see also minetest.explode_table_event (main menu: engine.explode_table_event)
|
||||||
|
|
||||||
|
tableoptions[<opt 1>;<opt 2>;...]
|
||||||
|
^ sets options for table[]:
|
||||||
|
^ color=#RRGGBB
|
||||||
|
^^ default text color (HEX-Color), defaults to #FFFFFF
|
||||||
|
^ background=#RRGGBB
|
||||||
|
^^ table background color (HEX-Color), defaults to #000000
|
||||||
|
^ border=<true/false>
|
||||||
|
^^ should the table be drawn with a border? (default true)
|
||||||
|
^ highlight=#RRGGBB
|
||||||
|
^^ highlight background color (HEX-Color), defaults to #466432
|
||||||
|
^ highlight_text=#RRGGBB
|
||||||
|
^^ highlight text color (HEX-Color), defaults to #FFFFFF
|
||||||
|
^ opendepth=<value>
|
||||||
|
^^ all subtrees up to depth < value are open (default value = 0)
|
||||||
|
^^ only useful when there is a column of type "tree"
|
||||||
|
|
||||||
|
tablecolumns[<type 1>,<opt 1a>,<opt 1b>,...;<type 2>,<opt 2a>,<opt 2b>;...]
|
||||||
|
^ sets columns for table[]:
|
||||||
|
^ types: text, image, color, indent, tree
|
||||||
|
^^ text: show cell contents as text
|
||||||
|
^^ image: cell contents are an image index, use column options to define images
|
||||||
|
^^ color: cell contents are a HEX-Color and define color of following cell
|
||||||
|
^^ indent: cell contents are a number and define indentation of following cell
|
||||||
|
^^ tree: same as indent, but user can open and close subtrees (treeview-like)
|
||||||
|
^ column options:
|
||||||
|
^^ align=<value> for "text" and "image": content alignment within cells
|
||||||
|
^^ available values: left (default), center, right, inline
|
||||||
|
^^ width=<value> for "text" and "image": minimum width in em (default 0)
|
||||||
|
^^ for "indent" and "tree": indent width in em (default 1.5)
|
||||||
|
^^ padding=<value> padding left of the column, in em (default 0.5)
|
||||||
|
^^ exception: defaults to 0 for indent columns
|
||||||
|
^^ tooltip=<value> tooltip text (default empty)
|
||||||
|
^ "image" column options:
|
||||||
|
^^ 0=<value> sets image for image index 0
|
||||||
|
^^ 1=<value> sets image for image index 1
|
||||||
|
^^ 2=<value> sets image for image index 2
|
||||||
|
^^ and so on; defined indices need not be contiguous
|
||||||
|
^^ empty or non-numeric cells are treated as 0
|
||||||
|
^ "color" column options:
|
||||||
|
^^ span=<value> number of following columns to affect (default infinite)
|
||||||
|
|
||||||
Note: do NOT use a element name starting with "key_" those names are reserved to
|
Note: do NOT use a element name starting with "key_" those names are reserved to
|
||||||
pass key press events to formspec!
|
pass key press events to formspec!
|
||||||
|
|
||||||
@ -1346,11 +1398,21 @@ minetest.get_inventory(location) -> InvRef
|
|||||||
minetest.create_detached_inventory(name, callbacks) -> InvRef
|
minetest.create_detached_inventory(name, callbacks) -> InvRef
|
||||||
^ callbacks: See "Detached inventory callbacks"
|
^ callbacks: See "Detached inventory callbacks"
|
||||||
^ Creates a detached inventory. If it already exists, it is cleared.
|
^ Creates a detached inventory. If it already exists, it is cleared.
|
||||||
|
|
||||||
|
Formspec:
|
||||||
minetest.show_formspec(playername, formname, formspec)
|
minetest.show_formspec(playername, formname, formspec)
|
||||||
^ playername: name of player to show formspec
|
^ playername: name of player to show formspec
|
||||||
^ formname: name passed to on_player_receive_fields callbacks
|
^ formname: name passed to on_player_receive_fields callbacks
|
||||||
^ should follow "modname:<whatever>" naming convention
|
^ should follow "modname:<whatever>" naming convention
|
||||||
^ formspec: formspec to display
|
^ formspec: formspec to display
|
||||||
|
minetest.formspec_escape(string) -> string
|
||||||
|
^ escapes characters [ ] \ , ; that can not be used in formspecs
|
||||||
|
minetest.explode_table_event(string) -> table
|
||||||
|
^ returns e.g. {type="CHG", row=1, column=2}
|
||||||
|
^ type: "INV" (no row selected), "CHG" (selected) or "DCL" (double-click)
|
||||||
|
minetest.explode_textlist_event(string) -> table
|
||||||
|
^ returns e.g. {type="CHG", index=1}
|
||||||
|
^ type: "INV" (no row selected), "CHG" (selected) or "DCL" (double-click)
|
||||||
|
|
||||||
Item handling:
|
Item handling:
|
||||||
minetest.inventorycube(img1, img2, img3)
|
minetest.inventorycube(img1, img2, img3)
|
||||||
|
@ -89,12 +89,33 @@ engine.sound_play(spec, looped) -> handle
|
|||||||
^ looped = bool
|
^ looped = bool
|
||||||
engine.sound_stop(handle)
|
engine.sound_stop(handle)
|
||||||
|
|
||||||
GUI:
|
Formspec:
|
||||||
engine.update_formspec(formspec)
|
engine.update_formspec(formspec)
|
||||||
- engine.set_background(type, texturepath)
|
engine.get_table_index(tablename) -> index
|
||||||
|
^ can also handle textlists
|
||||||
|
engine.formspec_escape(string) -> string
|
||||||
|
^ escapes characters [ ] \ , ; that can not be used in formspecs
|
||||||
|
engine.explode_table_event(string) -> table
|
||||||
|
^ returns e.g. {type="CHG", row=1, column=2}
|
||||||
|
^ type: "INV" (no row selected), "CHG" (selected) or "DCL" (double-click)
|
||||||
|
engine.explode_textlist_event(string) -> table
|
||||||
|
^ returns e.g. {type="CHG", index=1}
|
||||||
|
^ type: "INV" (no row selected), "CHG" (selected) or "DCL" (double-click)
|
||||||
|
|
||||||
|
GUI:
|
||||||
|
engine.set_background(type, texturepath)
|
||||||
^ type: "background", "overlay", "header" or "footer"
|
^ type: "background", "overlay", "header" or "footer"
|
||||||
engine.set_clouds(<true/false>)
|
engine.set_clouds(<true/false>)
|
||||||
engine.set_topleft_text(text)
|
engine.set_topleft_text(text)
|
||||||
|
engine.show_keys_menu()
|
||||||
|
engine.file_open_dialog(formname,caption)
|
||||||
|
^ shows a file open dialog
|
||||||
|
^ formname is base name of dialog response returned in fields
|
||||||
|
^ -if dialog was accepted "_accepted"
|
||||||
|
^^ will be added to fieldname containing the path
|
||||||
|
^ -if dialog was canceled "_cancelled"
|
||||||
|
^ will be added to fieldname value is set to formname itself
|
||||||
|
^ returns nil or selected file/folder
|
||||||
|
|
||||||
Games:
|
Games:
|
||||||
engine.get_game(index)
|
engine.get_game(index)
|
||||||
@ -155,22 +176,7 @@ engine.get_worlds() -> list of worlds (possible in async calls)
|
|||||||
engine.create_world(worldname, gameid)
|
engine.create_world(worldname, gameid)
|
||||||
engine.delete_world(index)
|
engine.delete_world(index)
|
||||||
|
|
||||||
|
|
||||||
UI:
|
|
||||||
engine.get_textlist_index(textlistname) -> index
|
|
||||||
engine.show_keys_menu()
|
|
||||||
engine.file_open_dialog(formname,caption)
|
|
||||||
^ shows a file open dialog
|
|
||||||
^ formname is base name of dialog response returned in fields
|
|
||||||
^ -if dialog was accepted "_accepted"
|
|
||||||
^^ will be added to fieldname containing the path
|
|
||||||
^ -if dialog was canceled "_cancelled"
|
|
||||||
^ will be added to fieldname value is set to formname itself
|
|
||||||
^ returns nil or selected file/folder
|
|
||||||
|
|
||||||
Helpers:
|
Helpers:
|
||||||
engine.formspec_escape(string) -> string
|
|
||||||
^ escapes characters [ ] \ , ; that can not be used in formspecs
|
|
||||||
engine.gettext(string) -> string
|
engine.gettext(string) -> string
|
||||||
^ look up the translation of a string in the gettext message catalog
|
^ look up the translation of a string in the gettext message catalog
|
||||||
fgettext(string, ...) -> string
|
fgettext(string, ...) -> string
|
||||||
|
@ -374,6 +374,7 @@ set(minetest_SRCS
|
|||||||
guiMessageMenu.cpp
|
guiMessageMenu.cpp
|
||||||
guiTextInputMenu.cpp
|
guiTextInputMenu.cpp
|
||||||
guiFormSpecMenu.cpp
|
guiFormSpecMenu.cpp
|
||||||
|
guiTable.cpp
|
||||||
guiPauseMenu.cpp
|
guiPauseMenu.cpp
|
||||||
guiPasswordChange.cpp
|
guiPasswordChange.cpp
|
||||||
guiVolumeChange.cpp
|
guiVolumeChange.cpp
|
||||||
|
@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include "guiFormSpecMenu.h"
|
#include "guiFormSpecMenu.h"
|
||||||
|
#include "guiTable.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "gamedef.h"
|
#include "gamedef.h"
|
||||||
#include "keycode.h"
|
#include "keycode.h"
|
||||||
@ -33,9 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <IGUIButton.h>
|
#include <IGUIButton.h>
|
||||||
#include <IGUIStaticText.h>
|
#include <IGUIStaticText.h>
|
||||||
#include <IGUIFont.h>
|
#include <IGUIFont.h>
|
||||||
#include <IGUIListBox.h>
|
|
||||||
#include <IGUITabControl.h>
|
#include <IGUITabControl.h>
|
||||||
#include <IGUIScrollBar.h>
|
|
||||||
#include <IGUIComboBox.h>
|
#include <IGUIComboBox.h>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "tile.h" // ITextureSource
|
#include "tile.h" // ITextureSource
|
||||||
@ -83,10 +82,6 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
|
|||||||
m_selected_item(NULL),
|
m_selected_item(NULL),
|
||||||
m_selected_amount(0),
|
m_selected_amount(0),
|
||||||
m_selected_dragging(false),
|
m_selected_dragging(false),
|
||||||
m_listbox_click_fname(),
|
|
||||||
m_listbox_click_index(-1),
|
|
||||||
m_listbox_click_time(0),
|
|
||||||
m_listbox_doubleclick(false),
|
|
||||||
m_tooltip_element(NULL),
|
m_tooltip_element(NULL),
|
||||||
m_allowclose(true),
|
m_allowclose(true),
|
||||||
m_lock(false)
|
m_lock(false)
|
||||||
@ -142,7 +137,7 @@ void GUIFormSpecMenu::setInitialFocus()
|
|||||||
// Set initial focus according to following order of precedence:
|
// Set initial focus according to following order of precedence:
|
||||||
// 1. first empty editbox
|
// 1. first empty editbox
|
||||||
// 2. first editbox
|
// 2. first editbox
|
||||||
// 3. first listbox
|
// 3. first table
|
||||||
// 4. last button
|
// 4. last button
|
||||||
// 5. first focusable (not statictext, not tabheader)
|
// 5. first focusable (not statictext, not tabheader)
|
||||||
// 6. first child element
|
// 6. first child element
|
||||||
@ -177,10 +172,10 @@ void GUIFormSpecMenu::setInitialFocus()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. first listbox
|
// 3. first table
|
||||||
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
||||||
it != children.end(); ++it) {
|
it != children.end(); ++it) {
|
||||||
if ((*it)->getType() == gui::EGUIET_LIST_BOX) {
|
if ((*it)->getTypeName() == std::string("GUITable")) {
|
||||||
Environment->setFocus(*it);
|
Environment->setFocus(*it);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -212,86 +207,13 @@ void GUIFormSpecMenu::setInitialFocus()
|
|||||||
Environment->setFocus(*(children.begin()));
|
Environment->setFocus(*(children.begin()));
|
||||||
}
|
}
|
||||||
|
|
||||||
int GUIFormSpecMenu::getListboxIndex(std::string listboxname) {
|
GUITable* GUIFormSpecMenu::getTable(std::wstring tablename)
|
||||||
|
|
||||||
std::wstring wlistboxname = narrow_to_wide(listboxname.c_str());
|
|
||||||
|
|
||||||
for(unsigned int i=0; i < m_listboxes.size(); i++) {
|
|
||||||
|
|
||||||
std::wstring name(m_listboxes[i].first.fname.c_str());
|
|
||||||
if ( name == wlistboxname) {
|
|
||||||
return m_listboxes[i].second->getSelected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GUIFormSpecMenu::checkListboxClick(std::wstring wlistboxname,
|
|
||||||
int eventtype)
|
|
||||||
{
|
{
|
||||||
// WARNING: BLACK IRRLICHT MAGIC
|
for (u32 i = 0; i < m_tables.size(); ++i) {
|
||||||
// Used to fix Irrlicht's subpar reporting of single clicks and double
|
if (tablename == m_tables[i].first.fname)
|
||||||
// clicks in listboxes (gui::EGET_LISTBOX_CHANGED,
|
return m_tables[i].second;
|
||||||
// gui::EGET_LISTBOX_SELECTED_AGAIN):
|
|
||||||
// 1. IGUIListBox::setSelected() is counted as a click.
|
|
||||||
// Including the initial setSelected() done by parseTextList().
|
|
||||||
// 2. Clicking on a the selected item and then dragging for less
|
|
||||||
// than 500ms is counted as a doubleclick, no matter when the
|
|
||||||
// item was previously selected (e.g. more than 500ms ago)
|
|
||||||
|
|
||||||
// So when Irrlicht reports a doubleclick, we need to check
|
|
||||||
// for ourselves if really was a doubleclick. Or just a fake.
|
|
||||||
|
|
||||||
for(unsigned int i=0; i < m_listboxes.size(); i++) {
|
|
||||||
std::wstring name(m_listboxes[i].first.fname.c_str());
|
|
||||||
int selected = m_listboxes[i].second->getSelected();
|
|
||||||
if (name == wlistboxname && selected >= 0) {
|
|
||||||
u32 now = getTimeMs();
|
|
||||||
bool doubleclick =
|
|
||||||
(eventtype == gui::EGET_LISTBOX_SELECTED_AGAIN)
|
|
||||||
&& (name == m_listbox_click_fname)
|
|
||||||
&& (selected == m_listbox_click_index)
|
|
||||||
&& (m_listbox_click_time >= now - 500);
|
|
||||||
m_listbox_click_fname = name;
|
|
||||||
m_listbox_click_index = selected;
|
|
||||||
m_listbox_click_time = now;
|
|
||||||
m_listbox_doubleclick = doubleclick;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
return 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
gui::IGUIScrollBar* GUIFormSpecMenu::getListboxScrollbar(
|
|
||||||
gui::IGUIListBox *listbox)
|
|
||||||
{
|
|
||||||
// WARNING: BLACK IRRLICHT MAGIC
|
|
||||||
// Ordinarily, due to how formspecs work (recreating the entire GUI
|
|
||||||
// when something changes), when you select an item in a textlist
|
|
||||||
// with more items than fit in the visible area, the newly selected
|
|
||||||
// item is scrolled to the bottom of the visible area. This is
|
|
||||||
// annoying and breaks GUI designs that use double clicks.
|
|
||||||
|
|
||||||
// This function helps fixing this problem by giving direct access
|
|
||||||
// to a listbox's scrollbar. This works because CGUIListBox doesn't
|
|
||||||
// cache the scrollbar position anywhere.
|
|
||||||
|
|
||||||
// If this stops working in a future irrlicht version, consider
|
|
||||||
// maintaining a local copy of irr::gui::CGUIListBox, possibly also
|
|
||||||
// fixing the other reasons why black irrlicht magic is needed.
|
|
||||||
|
|
||||||
core::list<gui::IGUIElement*> children = listbox->getChildren();
|
|
||||||
for(core::list<gui::IGUIElement*>::Iterator it = children.begin();
|
|
||||||
it != children.end(); ++it) {
|
|
||||||
gui::IGUIElement* child = *it;
|
|
||||||
if (child && child->getType() == gui::EGUIET_SCROLL_BAR) {
|
|
||||||
return static_cast<gui::IGUIScrollBar*>(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verbosestream<<"getListboxScrollbar: WARNING: "
|
|
||||||
<<"listbox has no scrollbar"<<std::endl;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> split(const std::string &s, char delim) {
|
std::vector<std::string> split(const std::string &s, char delim) {
|
||||||
@ -643,10 +565,109 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) {
|
|||||||
errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
|
errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUIFormSpecMenu::parseTableOptions(parserData* data,std::string element) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element) {
|
||||||
|
std::vector<std::string> parts = split(element,';');
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUIFormSpecMenu::parseTable(parserData* data,std::string element) {
|
||||||
|
std::vector<std::string> parts = split(element,';');
|
||||||
|
|
||||||
|
if ((parts.size() == 4) || (parts.size() == 5)) {
|
||||||
|
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];
|
||||||
|
|
||||||
|
MY_CHECKPOS("table",0);
|
||||||
|
MY_CHECKGEOM("table",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);
|
||||||
|
|
||||||
|
std::wstring fname_w = narrow_to_wide(name.c_str());
|
||||||
|
|
||||||
|
FieldSpec spec = FieldSpec(
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//now really show table
|
||||||
|
GUITable *e = new GUITable(Environment, this, spec.fid, rect,
|
||||||
|
m_tsrc);
|
||||||
|
e->drop(); // IGUIElement maintains the remaining reference
|
||||||
|
|
||||||
|
if (spec.fname == data->focused_fieldname) {
|
||||||
|
Environment->setFocus(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
e->setTable(data->table_options, data->table_columns, items);
|
||||||
|
|
||||||
|
if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
|
||||||
|
e->setDynamicData(data->table_dyndata[fname_w]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((str_initial_selection != "") &&
|
||||||
|
(str_initial_selection != "0"))
|
||||||
|
e->setSelected(stoi(str_initial_selection.c_str()));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
|
void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
|
||||||
std::vector<std::string> parts = split(element,';');
|
std::vector<std::string> parts = split(element,';');
|
||||||
|
|
||||||
if ((parts.size() == 5) || (parts.size() == 6)) {
|
if ((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) {
|
||||||
std::vector<std::string> v_pos = split(parts[0],',');
|
std::vector<std::string> v_pos = split(parts[0],',');
|
||||||
std::vector<std::string> v_geom = split(parts[1],',');
|
std::vector<std::string> v_geom = split(parts[1],',');
|
||||||
std::string name = parts[2];
|
std::string name = parts[2];
|
||||||
@ -683,63 +704,32 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) {
|
|||||||
258+m_fields.size()
|
258+m_fields.size()
|
||||||
);
|
);
|
||||||
|
|
||||||
spec.ftype = f_ListBox;
|
spec.ftype = f_Table;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < items.size(); ++i) {
|
||||||
|
items[i] = unescape_string(items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
//now really show list
|
//now really show list
|
||||||
gui::IGUIListBox *e = Environment->addListBox(rect, this,spec.fid);
|
GUITable *e = new GUITable(Environment, this, spec.fid, rect,
|
||||||
|
m_tsrc);
|
||||||
|
e->drop(); // IGUIElement maintains the remaining reference
|
||||||
|
|
||||||
if (spec.fname == data->focused_fieldname) {
|
if (spec.fname == data->focused_fieldname) {
|
||||||
Environment->setFocus(e);
|
Environment->setFocus(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str_transparent == "false")
|
e->setTextList(items, is_yes(str_transparent));
|
||||||
e->setDrawBackground(true);
|
|
||||||
|
|
||||||
for (unsigned int i=0; i < items.size(); i++) {
|
if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
|
||||||
if (items[i].c_str()[0] == '#') {
|
e->setDynamicData(data->table_dyndata[fname_w]);
|
||||||
if (items[i].c_str()[1] == '#') {
|
|
||||||
e->addItem(narrow_to_wide(unescape_string(items[i])).c_str() +1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::string color = items[i].substr(0,7);
|
|
||||||
std::wstring toadd =
|
|
||||||
narrow_to_wide(unescape_string(items[i]).c_str() + 7);
|
|
||||||
|
|
||||||
e->addItem(toadd.c_str());
|
|
||||||
|
|
||||||
video::SColor tmp_color;
|
|
||||||
|
|
||||||
if (parseColor(color, tmp_color, false))
|
|
||||||
e->setItemOverrideColor(i,tmp_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
e->addItem(narrow_to_wide(unescape_string(items[i])).c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->listbox_selections.find(fname_w) != data->listbox_selections.end()) {
|
|
||||||
e->setSelected(data->listbox_selections[fname_w]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->listbox_scroll.find(fname_w) != data->listbox_scroll.end()) {
|
|
||||||
gui::IGUIScrollBar *scrollbar = getListboxScrollbar(e);
|
|
||||||
if (scrollbar) {
|
|
||||||
scrollbar->setPos(data->listbox_scroll[fname_w]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gui::IGUIScrollBar *scrollbar = getListboxScrollbar(e);
|
|
||||||
if (scrollbar) {
|
|
||||||
scrollbar->setPos(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((str_initial_selection != "") &&
|
if ((str_initial_selection != "") &&
|
||||||
(str_initial_selection != "0"))
|
(str_initial_selection != "0"))
|
||||||
e->setSelected(stoi(str_initial_selection.c_str())-1);
|
e->setSelected(stoi(str_initial_selection.c_str()));
|
||||||
|
|
||||||
m_listboxes.push_back(std::pair<FieldSpec,gui::IGUIListBox*>(spec,e));
|
m_tables.push_back(std::pair<FieldSpec,GUITable*>(spec, e));
|
||||||
m_fields.push_back(spec);
|
m_fields.push_back(spec);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1478,6 +1468,21 @@ void GUIFormSpecMenu::parseElement(parserData* data,std::string element) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == "tableoptions"){
|
||||||
|
parseTableOptions(data,description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == "tablecolumns"){
|
||||||
|
parseTableColumns(data,description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == "table"){
|
||||||
|
parseTable(data,description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (type == "textlist"){
|
if (type == "textlist"){
|
||||||
parseTextList(data,description);
|
parseTextList(data,description);
|
||||||
return;
|
return;
|
||||||
@ -1550,20 +1555,11 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||||||
{
|
{
|
||||||
parserData mydata;
|
parserData mydata;
|
||||||
|
|
||||||
//preserve listboxes
|
//preserve tables
|
||||||
for (unsigned int i = 0; i < m_listboxes.size(); i++) {
|
for (u32 i = 0; i < m_tables.size(); ++i) {
|
||||||
std::wstring listboxname = m_listboxes[i].first.fname;
|
std::wstring tablename = m_tables[i].first.fname;
|
||||||
gui::IGUIListBox *listbox = m_listboxes[i].second;
|
GUITable *table = m_tables[i].second;
|
||||||
|
mydata.table_dyndata[tablename] = table->getDynamicData();
|
||||||
int selection = listbox->getSelected();
|
|
||||||
if (selection != -1) {
|
|
||||||
mydata.listbox_selections[listboxname] = selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
gui::IGUIScrollBar *scrollbar = getListboxScrollbar(listbox);
|
|
||||||
if (scrollbar) {
|
|
||||||
mydata.listbox_scroll[listboxname] = scrollbar->getPos();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//preserve focus
|
//preserve focus
|
||||||
@ -1603,7 +1599,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||||||
m_images.clear();
|
m_images.clear();
|
||||||
m_backgrounds.clear();
|
m_backgrounds.clear();
|
||||||
m_itemimages.clear();
|
m_itemimages.clear();
|
||||||
m_listboxes.clear();
|
m_tables.clear();
|
||||||
m_checkboxes.clear();
|
m_checkboxes.clear();
|
||||||
m_fields.clear();
|
m_fields.clear();
|
||||||
m_boxes.clear();
|
m_boxes.clear();
|
||||||
@ -2175,17 +2171,12 @@ void GUIFormSpecMenu::acceptInput(bool quit=false)
|
|||||||
{
|
{
|
||||||
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
|
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
|
||||||
}
|
}
|
||||||
else if(s.ftype == f_ListBox) {
|
else if(s.ftype == f_Table) {
|
||||||
std::stringstream ss;
|
GUITable *table = getTable(s.fname);
|
||||||
|
if (table) {
|
||||||
if (m_listbox_doubleclick) {
|
fields[wide_to_narrow(s.fname.c_str())]
|
||||||
ss << "DCL:";
|
= table->checkEvent();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
ss << "CHG:";
|
|
||||||
}
|
|
||||||
ss << (getListboxIndex(wide_to_narrow(s.fname.c_str()))+1);
|
|
||||||
fields[wide_to_narrow(s.fname.c_str())] = ss.str();
|
|
||||||
}
|
}
|
||||||
else if(s.ftype == f_DropDown) {
|
else if(s.ftype == f_DropDown) {
|
||||||
// no dynamic cast possible due to some distributions shipped
|
// no dynamic cast possible due to some distributions shipped
|
||||||
@ -2249,7 +2240,7 @@ void GUIFormSpecMenu::acceptInput(bool quit=false)
|
|||||||
|
|
||||||
bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
||||||
{
|
{
|
||||||
// Fix Esc/Return key being eaten by checkboxen and listboxen
|
// Fix Esc/Return key being eaten by checkboxen and tables
|
||||||
if(event.EventType==EET_KEY_INPUT_EVENT)
|
if(event.EventType==EET_KEY_INPUT_EVENT)
|
||||||
{
|
{
|
||||||
KeyPress kp(event.KeyInput);
|
KeyPress kp(event.KeyInput);
|
||||||
@ -2706,8 +2697,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN) ||
|
if(event.GUIEvent.EventType==gui::EGET_TABLE_CHANGED)
|
||||||
(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED))
|
|
||||||
{
|
{
|
||||||
int current_id = event.GUIEvent.Caller->getID();
|
int current_id = event.GUIEvent.Caller->getID();
|
||||||
if(current_id > 257)
|
if(current_id > 257)
|
||||||
@ -2716,13 +2706,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
for(u32 i=0; i<m_fields.size(); i++)
|
for(u32 i=0; i<m_fields.size(); i++)
|
||||||
{
|
{
|
||||||
FieldSpec &s = m_fields[i];
|
FieldSpec &s = m_fields[i];
|
||||||
// if its a listbox, set the send field so
|
// if it's a table, set the send field
|
||||||
// lua knows which listbox was changed
|
// so lua knows which table was changed
|
||||||
// checkListboxClick() is black magic
|
if ((s.ftype == f_Table) && (s.fid == current_id))
|
||||||
// for properly handling double clicks
|
|
||||||
if ((s.ftype == f_ListBox) && (s.fid == current_id)
|
|
||||||
&& checkListboxClick(s.fname,
|
|
||||||
event.GUIEvent.EventType))
|
|
||||||
{
|
{
|
||||||
s.send = true;
|
s.send = true;
|
||||||
acceptInput();
|
acceptInput();
|
||||||
@ -2737,7 +2723,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||||||
return Parent ? Parent->OnEvent(event) : false;
|
return Parent ? Parent->OnEvent(event) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GUIFormSpecMenu::parseColor(std::string &value, video::SColor &color, bool quiet)
|
bool GUIFormSpecMenu::parseColor(const std::string &value, video::SColor &color, bool quiet)
|
||||||
{
|
{
|
||||||
const char *hexpattern = NULL;
|
const char *hexpattern = NULL;
|
||||||
if (value[0] == '#') {
|
if (value[0] == '#') {
|
||||||
|
@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "inventory.h"
|
#include "inventory.h"
|
||||||
#include "inventorymanager.h"
|
#include "inventorymanager.h"
|
||||||
#include "modalMenu.h"
|
#include "modalMenu.h"
|
||||||
|
#include "guiTable.h"
|
||||||
|
|
||||||
class IGameDef;
|
class IGameDef;
|
||||||
class InventoryManager;
|
class InventoryManager;
|
||||||
@ -34,7 +35,7 @@ class ISimpleTextureSource;
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
f_Button,
|
f_Button,
|
||||||
f_ListBox,
|
f_Table,
|
||||||
f_TabHeader,
|
f_TabHeader,
|
||||||
f_CheckBox,
|
f_CheckBox,
|
||||||
f_DropDown,
|
f_DropDown,
|
||||||
@ -231,7 +232,10 @@ public:
|
|||||||
bool preprocessEvent(const SEvent& event);
|
bool preprocessEvent(const SEvent& event);
|
||||||
bool OnEvent(const SEvent& event);
|
bool OnEvent(const SEvent& event);
|
||||||
|
|
||||||
int getListboxIndex(std::string listboxname);
|
GUITable* getTable(std::wstring tablename);
|
||||||
|
|
||||||
|
static bool parseColor(const std::string &value,
|
||||||
|
video::SColor &color, bool quiet);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
v2s32 getBasePos() const
|
v2s32 getBasePos() const
|
||||||
@ -260,7 +264,7 @@ protected:
|
|||||||
std::vector<ImageDrawSpec> m_itemimages;
|
std::vector<ImageDrawSpec> m_itemimages;
|
||||||
std::vector<BoxDrawSpec> m_boxes;
|
std::vector<BoxDrawSpec> m_boxes;
|
||||||
std::vector<FieldSpec> m_fields;
|
std::vector<FieldSpec> m_fields;
|
||||||
std::vector<std::pair<FieldSpec,gui::IGUIListBox*> > m_listboxes;
|
std::vector<std::pair<FieldSpec,GUITable*> > m_tables;
|
||||||
std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
|
std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
|
||||||
|
|
||||||
ItemSpec *m_selected_item;
|
ItemSpec *m_selected_item;
|
||||||
@ -273,12 +277,6 @@ protected:
|
|||||||
ItemStack m_selected_content_guess;
|
ItemStack m_selected_content_guess;
|
||||||
InventoryLocation m_selected_content_guess_inventory;
|
InventoryLocation m_selected_content_guess_inventory;
|
||||||
|
|
||||||
// WARNING: BLACK IRRLICHT MAGIC, see checkListboxClick()
|
|
||||||
std::wstring m_listbox_click_fname;
|
|
||||||
int m_listbox_click_index;
|
|
||||||
u32 m_listbox_click_time;
|
|
||||||
bool m_listbox_doubleclick;
|
|
||||||
|
|
||||||
v2s32 m_pointer;
|
v2s32 m_pointer;
|
||||||
gui::IGUIStaticText *m_tooltip_element;
|
gui::IGUIStaticText *m_tooltip_element;
|
||||||
|
|
||||||
@ -302,8 +300,10 @@ private:
|
|||||||
int bp_set;
|
int bp_set;
|
||||||
v2u32 screensize;
|
v2u32 screensize;
|
||||||
std::wstring focused_fieldname;
|
std::wstring focused_fieldname;
|
||||||
std::map<std::wstring,int> listbox_selections;
|
GUITable::TableOptions table_options;
|
||||||
std::map<std::wstring,int> listbox_scroll;
|
GUITable::TableColumns table_columns;
|
||||||
|
// used to restore table selection/scroll/treeview state
|
||||||
|
std::map<std::wstring,GUITable::DynamicData> table_dyndata;
|
||||||
} parserData;
|
} parserData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -315,12 +315,6 @@ private:
|
|||||||
|
|
||||||
fs_key_pendig current_keys_pending;
|
fs_key_pendig current_keys_pending;
|
||||||
|
|
||||||
// Determine whether listbox click was double click
|
|
||||||
// (Using some black Irrlicht magic)
|
|
||||||
bool checkListboxClick(std::wstring wlistboxname, int eventtype);
|
|
||||||
|
|
||||||
gui::IGUIScrollBar* getListboxScrollbar(gui::IGUIListBox *listbox);
|
|
||||||
|
|
||||||
void parseElement(parserData* data,std::string element);
|
void parseElement(parserData* data,std::string element);
|
||||||
|
|
||||||
void parseSize(parserData* data,std::string element);
|
void parseSize(parserData* data,std::string element);
|
||||||
@ -330,6 +324,9 @@ private:
|
|||||||
void parseItemImage(parserData* data,std::string element);
|
void parseItemImage(parserData* data,std::string element);
|
||||||
void parseButton(parserData* data,std::string element,std::string typ);
|
void parseButton(parserData* data,std::string element,std::string typ);
|
||||||
void parseBackground(parserData* data,std::string element);
|
void parseBackground(parserData* data,std::string element);
|
||||||
|
void parseTableOptions(parserData* data,std::string element);
|
||||||
|
void parseTableColumns(parserData* data,std::string element);
|
||||||
|
void parseTable(parserData* data,std::string element);
|
||||||
void parseTextList(parserData* data,std::string element);
|
void parseTextList(parserData* data,std::string element);
|
||||||
void parseDropDown(parserData* data,std::string element);
|
void parseDropDown(parserData* data,std::string element);
|
||||||
void parsePwdField(parserData* data,std::string element);
|
void parsePwdField(parserData* data,std::string element);
|
||||||
@ -344,8 +341,6 @@ private:
|
|||||||
void parseBox(parserData* data,std::string element);
|
void parseBox(parserData* data,std::string element);
|
||||||
void parseBackgroundColor(parserData* data,std::string element);
|
void parseBackgroundColor(parserData* data,std::string element);
|
||||||
void parseListColors(parserData* data,std::string element);
|
void parseListColors(parserData* data,std::string element);
|
||||||
|
|
||||||
bool parseColor(std::string &value, video::SColor &color, bool quiet);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FormspecFormSource: public IFormSource
|
class FormspecFormSource: public IFormSource
|
||||||
|
1212
src/guiTable.cpp
Normal file
1212
src/guiTable.cpp
Normal file
File diff suppressed because it is too large
Load Diff
269
src/guiTable.h
Normal file
269
src/guiTable.h
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
Minetest
|
||||||
|
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GUITABLE_HEADER
|
||||||
|
#define GUITABLE_HEADER
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "irrlichttypes_extrabloated.h"
|
||||||
|
|
||||||
|
class ISimpleTextureSource;
|
||||||
|
|
||||||
|
/*
|
||||||
|
A table GUI element for GUIFormSpecMenu.
|
||||||
|
|
||||||
|
Sends a EGET_TABLE_CHANGED event to the parent when
|
||||||
|
an item is selected or double-clicked.
|
||||||
|
Call checkEvent() to get info.
|
||||||
|
|
||||||
|
Credits: The interface and implementation of this class are (very)
|
||||||
|
loosely based on the Irrlicht classes CGUITable and CGUIListBox.
|
||||||
|
CGUITable and CGUIListBox are licensed under the Irrlicht license;
|
||||||
|
they are Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||||||
|
*/
|
||||||
|
class GUITable : public gui::IGUIElement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
Stores dynamic data that should be preserved
|
||||||
|
when updating a formspec
|
||||||
|
*/
|
||||||
|
struct DynamicData
|
||||||
|
{
|
||||||
|
s32 selected;
|
||||||
|
s32 scrollpos;
|
||||||
|
s32 keynav_time;
|
||||||
|
core::stringw keynav_buffer;
|
||||||
|
std::set<s32> opened_trees;
|
||||||
|
|
||||||
|
DynamicData()
|
||||||
|
{
|
||||||
|
selected = 0;
|
||||||
|
scrollpos = 0;
|
||||||
|
keynav_time = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
An option of the form <name>=<value>
|
||||||
|
*/
|
||||||
|
struct Option
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
Option(const std::string &name_, const std::string &value_)
|
||||||
|
{
|
||||||
|
name = name_;
|
||||||
|
value = value_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
A list of options that concern the entire table
|
||||||
|
*/
|
||||||
|
typedef std::vector<Option> TableOptions;
|
||||||
|
|
||||||
|
/*
|
||||||
|
A column with options
|
||||||
|
*/
|
||||||
|
struct TableColumn
|
||||||
|
{
|
||||||
|
std::string type;
|
||||||
|
std::vector<Option> options;
|
||||||
|
};
|
||||||
|
typedef std::vector<TableColumn> TableColumns;
|
||||||
|
|
||||||
|
|
||||||
|
GUITable(gui::IGUIEnvironment *env,
|
||||||
|
gui::IGUIElement *parent, s32 id,
|
||||||
|
core::rect<s32> rectangle,
|
||||||
|
ISimpleTextureSource *tsrc);
|
||||||
|
|
||||||
|
virtual ~GUITable();
|
||||||
|
|
||||||
|
/* Split a string of the form "name=value" into name and value */
|
||||||
|
static Option splitOption(const std::string &str);
|
||||||
|
|
||||||
|
/* Set textlist-like options, columns and data */
|
||||||
|
void setTextList(const std::vector<std::string> &content,
|
||||||
|
bool transparent);
|
||||||
|
|
||||||
|
/* Set generic table options, columns and content */
|
||||||
|
// Adds empty strings to end of content if there is an incomplete row
|
||||||
|
void setTable(const TableOptions &options,
|
||||||
|
const TableColumns &columns,
|
||||||
|
std::vector<std::string> &content);
|
||||||
|
|
||||||
|
/* Clear the table */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/* Get info about last event (string such as "CHG:1:2") */
|
||||||
|
// Call this after EGET_TABLE_CHANGED
|
||||||
|
std::string checkEvent();
|
||||||
|
|
||||||
|
/* Get index of currently selected row (first=1; 0 if none selected) */
|
||||||
|
s32 getSelected() const;
|
||||||
|
|
||||||
|
/* Set currently selected row (first=1; 0 if none selected) */
|
||||||
|
// If given index is not visible at the moment, select its parent
|
||||||
|
// Autoscroll to make the selected row fully visible
|
||||||
|
void setSelected(s32 index);
|
||||||
|
|
||||||
|
/* Get selection, scroll position and opened (sub)trees */
|
||||||
|
DynamicData getDynamicData() const;
|
||||||
|
|
||||||
|
/* Set selection, scroll position and opened (sub)trees */
|
||||||
|
void setDynamicData(const DynamicData &dyndata);
|
||||||
|
|
||||||
|
/* Returns "GUITable" */
|
||||||
|
virtual const c8* getTypeName() const;
|
||||||
|
|
||||||
|
/* Must be called when position or size changes */
|
||||||
|
virtual void updateAbsolutePosition();
|
||||||
|
|
||||||
|
/* Irrlicht draw method */
|
||||||
|
virtual void draw();
|
||||||
|
|
||||||
|
/* Irrlicht event handler */
|
||||||
|
virtual bool OnEvent(const SEvent &event);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum ColumnType {
|
||||||
|
COLUMN_TYPE_TEXT,
|
||||||
|
COLUMN_TYPE_IMAGE,
|
||||||
|
COLUMN_TYPE_COLOR,
|
||||||
|
COLUMN_TYPE_INDENT,
|
||||||
|
COLUMN_TYPE_TREE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Cell {
|
||||||
|
s32 xmin;
|
||||||
|
s32 xmax;
|
||||||
|
s32 xpos;
|
||||||
|
ColumnType content_type;
|
||||||
|
s32 content_index;
|
||||||
|
s32 tooltip_index;
|
||||||
|
video::SColor color;
|
||||||
|
bool color_defined;
|
||||||
|
s32 reported_column;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Row {
|
||||||
|
Cell *cells;
|
||||||
|
s32 cellcount;
|
||||||
|
s32 indent;
|
||||||
|
// visible_index >= 0: is index of row in m_visible_rows
|
||||||
|
// visible_index == -1: parent open but other ancestor closed
|
||||||
|
// visible_index == -2: parent closed
|
||||||
|
s32 visible_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Texture source
|
||||||
|
ISimpleTextureSource *m_tsrc;
|
||||||
|
|
||||||
|
// Table content (including hidden rows)
|
||||||
|
std::vector<Row> m_rows;
|
||||||
|
// Table content (only visible; indices into m_rows)
|
||||||
|
std::vector<s32> m_visible_rows;
|
||||||
|
bool m_is_textlist;
|
||||||
|
bool m_has_tree_column;
|
||||||
|
|
||||||
|
// Selection status
|
||||||
|
s32 m_selected; // index of row (1...n), or 0 if none selected
|
||||||
|
s32 m_sel_column;
|
||||||
|
bool m_sel_doubleclick;
|
||||||
|
|
||||||
|
// Keyboard navigation stuff
|
||||||
|
s32 m_keynav_time;
|
||||||
|
core::stringw m_keynav_buffer;
|
||||||
|
|
||||||
|
// Drawing and geometry information
|
||||||
|
bool m_border;
|
||||||
|
video::SColor m_color;
|
||||||
|
video::SColor m_background;
|
||||||
|
video::SColor m_highlight;
|
||||||
|
video::SColor m_highlight_text;
|
||||||
|
s32 m_rowheight;
|
||||||
|
gui::IGUIFont *m_font;
|
||||||
|
gui::IGUIScrollBar *m_scrollbar;
|
||||||
|
|
||||||
|
// Allocated strings and images
|
||||||
|
std::vector<core::stringw> m_strings;
|
||||||
|
std::vector<video::ITexture*> m_images;
|
||||||
|
std::map<std::string, s32> m_alloc_strings;
|
||||||
|
std::map<std::string, s32> m_alloc_images;
|
||||||
|
|
||||||
|
s32 allocString(const std::string &text);
|
||||||
|
s32 allocImage(const std::string &imagename);
|
||||||
|
void allocationComplete();
|
||||||
|
|
||||||
|
// Helper for draw() that draws a single cell
|
||||||
|
void drawCell(const Cell *cell, video::SColor color,
|
||||||
|
const core::rect<s32> &rowrect,
|
||||||
|
const core::rect<s32> &client_clip);
|
||||||
|
|
||||||
|
// Returns the i-th visible row (NULL if i is invalid)
|
||||||
|
const Row *getRow(s32 i) const;
|
||||||
|
|
||||||
|
// Key navigation helper
|
||||||
|
bool doesRowStartWith(const Row *row, const core::stringw &str) const;
|
||||||
|
|
||||||
|
// Returns the row at a given screen Y coordinate
|
||||||
|
// Returns index i such that m_rows[i] is valid (or -1 on error)
|
||||||
|
s32 getRowAt(s32 y, bool &really_hovering) const;
|
||||||
|
|
||||||
|
// Returns the cell at a given screen X coordinate within m_rows[row_i]
|
||||||
|
// Returns index j such that m_rows[row_i].cells[j] is valid
|
||||||
|
// (or -1 on error)
|
||||||
|
s32 getCellAt(s32 x, s32 row_i) const;
|
||||||
|
|
||||||
|
// Make the selected row fully visible
|
||||||
|
void autoScroll();
|
||||||
|
|
||||||
|
// Should be called when m_rowcount or m_rowheight changes
|
||||||
|
void updateScrollBar();
|
||||||
|
|
||||||
|
// Sends EET_GUI_EVENT / EGET_TABLE_CHANGED to parent
|
||||||
|
void sendTableEvent(s32 column, bool doubleclick);
|
||||||
|
|
||||||
|
// Functions that help deal with hidden rows
|
||||||
|
// The following functions take raw row indices (hidden rows not skipped)
|
||||||
|
void getOpenedTrees(std::set<s32> &opened_trees) const;
|
||||||
|
void setOpenedTrees(const std::set<s32> &opened_trees);
|
||||||
|
void openTree(s32 to_open);
|
||||||
|
void closeTree(s32 to_close);
|
||||||
|
// The following function takes a visible row index (hidden rows skipped)
|
||||||
|
// dir: -1 = left (close), 0 = auto (toggle), 1 = right (open)
|
||||||
|
void toggleVisibleTree(s32 row_i, int dir, bool move_selection);
|
||||||
|
|
||||||
|
// Aligns cell content in column according to alignment specification
|
||||||
|
// align = 0: left aligned, 1: centered, 2: right aligned, 3: inline
|
||||||
|
static void alignContent(Cell *cell, s32 xmax, s32 content_width,
|
||||||
|
s32 align);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -183,18 +183,25 @@ int ModApiMainMenu::l_set_clouds(lua_State *L)
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
int ModApiMainMenu::l_get_textlist_index(lua_State *L)
|
int ModApiMainMenu::l_get_textlist_index(lua_State *L)
|
||||||
|
{
|
||||||
|
// get_table_index accepts both tables and textlists
|
||||||
|
return l_get_table_index(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
int ModApiMainMenu::l_get_table_index(lua_State *L)
|
||||||
{
|
{
|
||||||
GUIEngine* engine = getGuiEngine(L);
|
GUIEngine* engine = getGuiEngine(L);
|
||||||
assert(engine != 0);
|
assert(engine != 0);
|
||||||
|
|
||||||
std::string listboxname(luaL_checkstring(L, 1));
|
std::wstring tablename(narrow_to_wide(luaL_checkstring(L, 1)));
|
||||||
|
GUITable *table = engine->m_menu->getTable(tablename);
|
||||||
int selection = engine->m_menu->getListboxIndex(listboxname);
|
s32 selection = table ? table->getSelected() : 0;
|
||||||
|
|
||||||
if (selection >= 0)
|
|
||||||
selection++;
|
|
||||||
|
|
||||||
|
if (selection >= 1)
|
||||||
lua_pushinteger(L, selection);
|
lua_pushinteger(L, selection);
|
||||||
|
else
|
||||||
|
lua_pushnil(L);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1026,6 +1033,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
|
|||||||
API_FCT(update_formspec);
|
API_FCT(update_formspec);
|
||||||
API_FCT(set_clouds);
|
API_FCT(set_clouds);
|
||||||
API_FCT(get_textlist_index);
|
API_FCT(get_textlist_index);
|
||||||
|
API_FCT(get_table_index);
|
||||||
API_FCT(get_worlds);
|
API_FCT(get_worlds);
|
||||||
API_FCT(get_games);
|
API_FCT(get_games);
|
||||||
API_FCT(start);
|
API_FCT(start);
|
||||||
|
@ -97,6 +97,8 @@ private:
|
|||||||
|
|
||||||
static int l_get_textlist_index(lua_State *L);
|
static int l_get_textlist_index(lua_State *L);
|
||||||
|
|
||||||
|
static int l_get_table_index(lua_State *L);
|
||||||
|
|
||||||
static int l_set_background(lua_State *L);
|
static int l_set_background(lua_State *L);
|
||||||
|
|
||||||
static int l_update_formspec(lua_State *L);
|
static int l_update_formspec(lua_State *L);
|
||||||
|
Loading…
Reference in New Issue
Block a user