diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 79497dca1..6c34ecc2c 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -184,6 +184,38 @@ bool GUIFormSpecMenu::checkListboxClick(std::wstring wlistboxname, 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 children = listbox->getChildren(); + for(core::list::Iterator it = children.begin(); + it != children.end(); ++it) { + gui::IGUIElement* child = *it; + if (child && child->getType() == gui::EGUIET_SCROLL_BAR) { + return static_cast(child); + } + } + + verbosestream<<"getListboxScrollbar: WARNING: " + <<"listbox has no scrollbar"< split(const std::string &s, char delim) { std::vector tokens; @@ -616,6 +648,13 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) { 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]); + } + } + if (str_initial_selection != "") e->setSelected(stoi(str_initial_selection.c_str())-1); @@ -1417,11 +1456,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) //preserve listboxes for (unsigned int i = 0; i < m_listboxes.size(); i++) { - int selection = m_listboxes[i].second->getSelected(); + std::wstring listboxname = m_listboxes[i].first.fname; + gui::IGUIListBox *listbox = m_listboxes[i].second; + + int selection = listbox->getSelected(); if (selection != -1) { - std::wstring listboxname = m_listboxes[i].first.fname; mydata.listbox_selections[listboxname] = selection; } + + gui::IGUIScrollBar *scrollbar = getListboxScrollbar(listbox); + if (scrollbar) { + mydata.listbox_scroll[listboxname] = scrollbar->getPos(); + } } // Remove children diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 28f11d2e7..f8d7ff1fb 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -294,6 +294,7 @@ private: int bp_set; v2u32 screensize; std::map listbox_selections; + std::map listbox_scroll; } parserData; typedef struct { @@ -311,6 +312,8 @@ private: // (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 parseSize(parserData* data,std::string element);