diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 79497dca1814ff35d59d4aab0504962fd2af1332..6c34ecc2cbc2bbfb8566ec71cf947cfbea15d25d 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<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> 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 28f11d2e70390be6f000c227b7f2f5ba3b3c4d68..f8d7ff1fb24136332209e92e040f456c4077c9e1 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -294,6 +294,7 @@ class GUIFormSpecMenu : public GUIModalMenu
 		int bp_set;
 		v2u32 screensize;
 		std::map<std::wstring,int> listbox_selections;
+		std::map<std::wstring,int> listbox_scroll;
 	} parserData;
 
 	typedef struct {
@@ -311,6 +312,8 @@ class GUIFormSpecMenu : public GUIModalMenu
 	// (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);