From 003634e049d599b8ed5ae3ba6b3250e9eb7741ca Mon Sep 17 00:00:00 2001 From: sapier Date: Fri, 30 May 2014 03:07:48 +0200 Subject: [PATCH] Add support for exiting formspecs by doubleclicking outside --- builtin/fstk/ui.lua | 1 - src/guiFormSpecMenu.cpp | 123 +++++++++++++++++++++++++++++++++------- src/guiFormSpecMenu.h | 24 ++++++-- src/porting.h | 15 +++++ 4 files changed, 136 insertions(+), 27 deletions(-) diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua index e0438247c..708ea19cf 100644 --- a/builtin/fstk/ui.lua +++ b/builtin/fstk/ui.lua @@ -134,7 +134,6 @@ function ui.handle_events(event) local retval = value:handle_events(event) if retval then - print("event handled by: " .. key) return retval end end diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 5a6099a30..48bdf2d50 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -46,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gettime.h" #include "gettext.h" #include "scripting_game.h" +#include "porting.h" #define MY_CHECKPOS(a,b) \ if (v_pos.size() != 2) { \ @@ -91,6 +92,12 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, current_keys_pending.key_enter = false; current_keys_pending.key_escape = false; + m_doubleclickdetect[0].time = 0; + m_doubleclickdetect[1].time = 0; + + m_doubleclickdetect[0].pos = v2s32(0, 0); + m_doubleclickdetect[1].pos = v2s32(0, 0); + } GUIFormSpecMenu::~GUIFormSpecMenu() @@ -1171,9 +1178,9 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element, if (parts[6] == "false") drawborder = false; } - + std::string pressed_image_name = ""; - + if ((parts.size() == 8)) { pressed_image_name = parts[7]; } @@ -1265,9 +1272,11 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) geom.X = data->screensize.Y; geom.Y = 30; - core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, + pos.Y+geom.Y); - gui::IGUITabControl *e = Environment->addTabControl(rect,this,show_background,show_border,spec.fid); + gui::IGUITabControl *e = Environment->addTabControl(rect, this, + show_background, show_border, spec.fid); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1276,7 +1285,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) e->setNotClipped(true); for (unsigned int i=0; i< buttons.size(); i++) { - e->addTab(narrow_to_wide(buttons[i]).c_str(),-1); + e->addTab(narrow_to_wide(buttons[i]).c_str(), -1); } if ((tab_index >= 0) && @@ -1287,14 +1296,17 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) m_fields.push_back(spec); return; } - errorstream<< "Invalid TabHeader element(" << parts.size() << "): '" << element << "'" << std::endl; + errorstream << "Invalid TabHeader element(" << parts.size() << "): '" + << element << "'" << std::endl; } void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) { if (m_gamedef == 0) { - errorstream<<"WARNING: invalid use of item_image_button with m_gamedef==0"<= 3) { if (parseColor(parts[2], m_slotbordercolor, false)) { m_slotborder = true; @@ -1628,9 +1640,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // A proceed button is added if there is no size[] element mydata.bp_set = 0; - + /* Convert m_init_draw_spec to m_inventorylists */ - + m_inventorylists.clear(); m_images.clear(); m_backgrounds.clear(); @@ -1710,7 +1722,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const { core::rect imgrect(0,0,imgsize.X,imgsize.Y); - + for(u32 i=0; igetSkin(); if (skin) font = skin->getFont(); - + Inventory *inv = m_invmgr->getInventory(s.inventoryloc); if(!inv){ infostream<<"GUIFormSpecMenu::drawList(): WARNING: " @@ -1758,9 +1770,9 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) < imgrect(0,0,imgsize.X,imgsize.Y); - + for(s32 i=0; igetSkin(); if (skin) font = skin->getFont(); - + Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); assert(inv); InventoryList *list = inv->getList(m_selected_item->listname); @@ -1886,7 +1898,7 @@ void GUIFormSpecMenu::drawMenu() if (!skin) return; video::IVideoDriver* driver = Environment->getVideoDriver(); - + v2u32 screenSize = driver->getScreenSize(); core::rect allbg(0, 0, screenSize.X , screenSize.Y); if (m_bgfullscreen) @@ -1930,7 +1942,7 @@ void GUIFormSpecMenu::drawMenu() errorstream << "\t" << spec.name << std::endl; } } - + /* Draw Boxes */ @@ -1979,7 +1991,7 @@ void GUIFormSpecMenu::drawMenu() errorstream << "\t" << spec.name << std::endl; } } - + /* Draw item images */ @@ -2004,7 +2016,7 @@ void GUIFormSpecMenu::drawMenu() core::dimension2di(texture->getOriginalSize())), NULL/*&AbsoluteClippingRect*/, colors, true); } - + /* Draw items Phase 0: Item slot rectangles @@ -2021,7 +2033,7 @@ void GUIFormSpecMenu::drawMenu() Call base class */ gui::IGUIElement::draw(); - + /* Draw fields/buttons tooltips */ @@ -2046,7 +2058,7 @@ void GUIFormSpecMenu::drawMenu() } } } - + /* Draw dragged item stack */ @@ -2276,6 +2288,17 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) } } +static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent) +{ + while(tocheck != NULL) { + if (tocheck == parent) { + return true; + } + tocheck = tocheck->getParent(); + } + return false; +} + bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) { // Fix Esc/Return key being eaten by checkboxen and tables @@ -2306,6 +2329,64 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) } } + if (event.EventType == EET_MOUSE_INPUT_EVENT) { + s32 x = event.MouseInput.X; + s32 y = event.MouseInput.Y; + gui::IGUIElement *hovered = + Environment->getRootGUIElement()->getElementFromPoint( + core::position2d(x, y)); + + if (!isChild(hovered,this)) { + if (DoubleClickDetection(event)) { + return true; + } + } + } + + return false; +} + +/******************************************************************************/ +bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) +{ + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { + m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos; + m_doubleclickdetect[0].time = m_doubleclickdetect[1].time; + + m_doubleclickdetect[1].pos = m_pointer; + m_doubleclickdetect[1].time = getTimeMs(); + } + else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { + u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, getTimeMs()); + if (delta > 400) { + return false; + } + + double squaredistance = + m_doubleclickdetect[0].pos + .getDistanceFromSQ(m_doubleclickdetect[1].pos); + + if (squaredistance > (30*30)) { + return false; + } + + SEvent* translated = new SEvent(); + assert(translated != 0); + //translate doubleclick to escape + memset(translated, 0, sizeof(SEvent)); + translated->EventType = irr::EET_KEY_INPUT_EVENT; + translated->KeyInput.Key = KEY_ESCAPE; + translated->KeyInput.Control = false; + translated->KeyInput.Shift = false; + translated->KeyInput.PressedDown = true; + translated->KeyInput.Char = 0; + OnEvent(*translated); + + // no need to send the key up event as we're already deleted + // and no one else did notice this event + delete translated; + return true; + } return false; } diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 016eb0e7e..474f13cb1 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -143,7 +143,7 @@ class GUIFormSpecMenu : public GUIModalMenu v2s32 geom; bool scale; }; - + struct FieldSpec { FieldSpec() @@ -205,7 +205,7 @@ public: m_current_inventory_location = current_inventory_location; regenerateGui(m_screensize_old); } - + // form_src is deleted by this GUIFormSpecMenu void setFormSource(IFormSource *form_src) { @@ -240,7 +240,7 @@ public: Remove and re-add (or reposition) stuff */ void regenerateGui(v2u32 screensize); - + ItemSpec getItemAtPos(v2s32 p) const; void drawList(const ListDrawSpec &s, int phase); void drawSelectedItem(); @@ -269,7 +269,7 @@ protected: v2s32 spacing; v2s32 imgsize; v2s32 offset; - + irr::IrrlichtDevice* m_device; InventoryManager *m_invmgr; IGameDef *m_gamedef; @@ -290,7 +290,7 @@ protected: ItemSpec *m_selected_item; u32 m_selected_amount; bool m_selected_dragging; - + // WARNING: BLACK MAGIC // Used to guess and keep up with some special things the server can do. // If name is "", no guess exists. @@ -366,6 +366,20 @@ private: void parseBox(parserData* data,std::string element); void parseBackgroundColor(parserData* data,std::string element); void parseListColors(parserData* data,std::string element); + + /** + * check if event is part of a double click + * @param event event to evaluate + * @return true/false if a doubleclick was detected + */ + bool DoubleClickDetection(const SEvent event); + + struct clickpos + { + v2s32 pos; + s32 time; + }; + clickpos m_doubleclickdetect[2]; }; class FormspecFormSource: public IFormSource diff --git a/src/porting.h b/src/porting.h index 937ca9464..383d4377a 100644 --- a/src/porting.h +++ b/src/porting.h @@ -275,6 +275,21 @@ inline u32 getTime(TimePrecision prec) return 0; } +/** + * Delta calculation function taking two 32bit arguments. + * @param old_time_ms old time for delta calculation (order is relevant!) + * @param new_time_ms new time for delta calculation (order is relevant!) + * @return positive 32bit delta value + */ +inline u32 getDeltaMs(u32 old_time_ms, u32 new_time_ms) +{ + if (new_time_ms >= old_time_ms) { + return (new_time_ms - old_time_ms); + } else { + return (old_time_ms - new_time_ms); + } +} + #if defined(linux) || defined(__linux) #include