From b8d646272320b29ac65a9c4adbe63bf557faa840 Mon Sep 17 00:00:00 2001 From: kts Date: Sat, 7 Mar 2015 22:56:27 -0800 Subject: [PATCH] Added basic code for creating and handling GUI events - at the moment just (SDL_USEREVENT + 10) using the SDL_USEREVENT union struct. Maybe define a custom struct that is equal in size to SDL_Event (and has type in the same position) and cast when needed? It'd make it a bit easier/nicer than using data1/data2. Cleaned up GuiElement and added margin functionality. --- src/Gui.cpp | 24 +++++++++++++---- src/Gui.hpp | 9 +++++++ src/gui/GuiButton.cpp | 4 +-- src/gui/GuiElement.cpp | 49 ++++++++++++++++++--------------- src/gui/GuiElement.hpp | 58 +++++++++++++++++++++------------------- src/gui/GuiList.cpp | 4 +-- src/states/MenuState.cpp | 12 +++++++-- 7 files changed, 100 insertions(+), 60 deletions(-) diff --git a/src/Gui.cpp b/src/Gui.cpp index 94f7064..bb23dd0 100644 --- a/src/Gui.cpp +++ b/src/Gui.cpp @@ -94,6 +94,7 @@ int Gui::onEvent(SDL_Event event) { std::vector::reverse_iterator element_it, element_end; GuiElement *element = NULL; float x, y; + SDL_Event hit_event; // event to send to the events queue if a gui element is hit switch (event.type) { case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: @@ -103,7 +104,14 @@ int Gui::onEvent(SDL_Event event) { for (element_it = elements.rbegin(), element_end = elements.rend(); element_it != element_end; ++element_it) { if ((*element_it)->getFlags() & GuiElement::HIDDEN) continue; element = (*element_it)->getHit(x, y); - if (element != NULL) break; + if (element != NULL) { + hit_event.type = GUI_EVENT; + hit_event.user.code = GUI_HIT; + hit_event.user.data1 = (void*)element; + hit_event.user.data2 = NULL; + SDL_PushEvent(&hit_event); + break; + } } break; case SDL_FINGERDOWN: @@ -114,7 +122,14 @@ int Gui::onEvent(SDL_Event event) { for (element_it = elements.rbegin(), element_end = elements.rend(); element_it != element_end; ++element_it) { if ((*element_it)->getFlags() & GuiElement::HIDDEN) continue; element = (*element_it)->getHit(x, y); - if (element != NULL) break; + if (element != NULL) { + hit_event.type = GUI_EVENT; + hit_event.user.code = GUI_HIT; + hit_event.user.data1 = (void*)element; + hit_event.user.data2 = NULL; + SDL_PushEvent(&hit_event); + break; + } } break; default: @@ -150,7 +165,7 @@ int Gui::addElement(GuiElement *element) { }*/ if (element->parent == NULL) { elements.push_back(element); - LOG(LOG_INFO) << FUNC_NAME << " added element " << element->text; + LOG(LOG_INFO) << FUNC_NAME << " added element " << element->name; } return elements.size()-1; } @@ -159,7 +174,7 @@ int Gui::delElement(const char *name) { std::vector::iterator element_it, element_end; for (element_it = elements.begin(), element_end = elements.end(); element_it != element_end;) { element = *element_it; - if (strcmp(element->text.c_str(), name) == 0) { + if (strcmp(element->name.c_str(), name) == 0) { if (element->object != NULL) set_basic->remObject(element->object); delete element; element_it = elements.erase(element_it); @@ -192,7 +207,6 @@ RenderCamera* Gui::getChildCamera() { void Gui::updateElements() { std::vector::iterator element_it, element_end; GuiElement *p_element = NULL; - GuiElement *c_element = NULL; for (element_it = elements.begin(), element_end = elements.end(); element_it != element_end; ++element_it) { p_element = *element_it; if (p_element->getFlags() & GuiElement::HIDDEN) continue; diff --git a/src/Gui.hpp b/src/Gui.hpp index 664dbf5..4d27c43 100644 --- a/src/Gui.hpp +++ b/src/Gui.hpp @@ -11,6 +11,15 @@ Gui.cpp/Gui.hpp provide the class responsible for creating, destroying, and hand #include "RenderCamera.hpp" #include "AssetManager.hpp" #include +// **** GUI Event **** +// TODO: check if this look up is safe +#define GUI_EVENT SDL_USEREVENT + 10 +// NOTE: what type of events do we want? +// PRESS, RELEASE - mouse/touch events +// FOCUS, UNFOCUS - when focusable elements (text input) switch to/from (should also emit press/release?) +// CREATE, DESTROY - maybe unneeded, but when the element is destroyed/created by Gui (would need separate IDs for this) +#define GUI_HIT 1 + class Gui { friend class Core; public: diff --git a/src/gui/GuiButton.cpp b/src/gui/GuiButton.cpp index f8f5296..4bd1cb8 100644 --- a/src/gui/GuiButton.cpp +++ b/src/gui/GuiButton.cpp @@ -1,8 +1,8 @@ #include "GuiButton.hpp" #include "Log.hpp" -GuiButton::GuiButton(const char *name, Texture *texture_) { - text.assign(name); +GuiButton::GuiButton(const char *name_, Texture *texture_) { + name.assign(name_); texture = texture_; } GuiButton::~GuiButton() { diff --git a/src/gui/GuiElement.cpp b/src/gui/GuiElement.cpp index 0ffe6af..af38bde 100644 --- a/src/gui/GuiElement.cpp +++ b/src/gui/GuiElement.cpp @@ -23,6 +23,12 @@ GuiElement::~GuiElement() { if (view != NULL) delete view; if (object != NULL) delete object; } +void GuiElement::setName(const char *new_name) { + name.assign(new_name); +} +const char* GuiElement::getName() { + return name.c_str(); +} /* ======== Relationships ======== */ int GuiElement::addChild(GuiElement *child) { // adopt the child! @@ -33,6 +39,7 @@ int GuiElement::addChild(GuiElement *child) { children.push_back(child); return children.size()-1; } +// Disown thine seed! int GuiElement::remChild(GuiElement *child) { child->parent = NULL; std::vector::iterator it; @@ -43,33 +50,34 @@ int GuiElement::remChild(GuiElement *child) { return 1; } int GuiElement::setParent(GuiElement *parent_) { + if (parent) parent->remChild(this); parent = parent_; - // TODO: detach from old parent return 0; } /* ======== Interactions ======== */ /* getHit - This function takes in normalized coordinates and returns either NULL or the element hit if within the element's bounding box. If this element is hit, it will call getHit to all children elements until either a match is found or NULL is returned. If a match is found, that is returned, otherwise this element is returned. +This function takes in absolute X and Y coordinates, originating from the top-left of the display area. +The hit coordinates are checked against the GuiElement's bounds and, if a match is found, it is then first checked against +the GuiElement's own x/y and x+w/x+h coordinates before calling each child's getHit method. If getHit successfully matches, +a pointer to GuiElement is returned. */ GuiElement* GuiElement::getHit(float xhit, float yhit) { GuiElement *element = NULL; - LOG(LOG_INFO) << text << " " << xhit << "x" << yhit << " vs " << bound_l << "x" << bound_t << "|" << bound_r << "x" << bound_b; + LOG(LOG_DEBUG) << name << " " << xhit << "x" << yhit << " vs " << bound_l << "x" << bound_t << "|" << bound_r << "x" << bound_b; if ( (xhit >= bound_l && xhit <= bound_r) && (yhit >= bound_t && yhit <= bound_b) ) { - LOG(LOG_INFO) << FUNC_NAME << ": HIT " << text <<": " << xhit << "x" << yhit; + LOG(LOG_DEBUG) << FUNC_NAME << ": HIT bounding box"; + if ( (xhit >= x && xhit <= x+w) && (yhit >= y && yhit <= y+h) ) { + LOG(LOG_DEBUG) << FUNC_NAME << ": HIT " << name; + return this; + } // check the children for hits and return them if so GuiElement *child_element = NULL; // Start from the end of the vector and walk backwards std::vector::reverse_iterator element_it, element_end; for (element_it = children.rbegin(), element_end = children.rend(); element_it != element_end; ++element_it) { child_element = (*element_it)->getHit(xhit, yhit); + if (child_element != NULL) return child_element; } - if (child_element != NULL) { - element = child_element; - } else { - element = this; - } - } else { - element = NULL; } return element; } @@ -87,7 +95,6 @@ int GuiElement::setGeometry(float x_, float y_, float w_, float h_) { base_h = h_; //view->setView(w, h); object->setScale(base_w, 1.0, base_h); - //object->setTranslation(x, 0.0, y); object->calcMatrix(); return 0; } @@ -109,8 +116,8 @@ void GuiElement::setMargin(float l, float r, float t, float b) { margin_t = t; margin_b = b; } - -/* ======== Render, eugh ======== */ +/* ======== Update ======== */ +// TODO: should this embrace int(s) or float(s) all the way? A danger is that float(s) would enable half pixel values thereby making things fuzzy. Maybe use int for building matrix? int GuiElement::doUpdate(int container_x, int container_y, int container_w, int container_h, int c_width, int c_height) { int current_x = 0; int current_y = 0; @@ -127,18 +134,18 @@ int GuiElement::doUpdate(int container_x, int container_y, int container_w, int } // get origin if (origin_flags & LEFT) { - current_x = container_x; + current_x = container_x + margin_l; } else if (origin_flags & RIGHT) { - current_x = container_x+container_w; + current_x = container_x + container_w + margin_r; } else if (origin_flags & HCENTER) { - current_x = container_x + (container_w/2); + current_x = container_x + (container_w/2) + ((margin_l+margin_r)/2); } if (origin_flags & TOP) { - current_y = container_y; + current_y = container_y + margin_t; } else if (origin_flags & BOTTOM) { - current_y = container_y+container_h; + current_y = container_y + container_h + margin_b; } else if (origin_flags & VCENTER) { - current_y = container_y + (container_h/2); + current_y = container_y + (container_h/2) + ((margin_t+margin_b)/2); } // set our width and height current_w = base_w; @@ -246,4 +253,4 @@ void GuiElement::setFlags(int new_flags) { } int GuiElement::getFlags() { return flags; -} +} \ No newline at end of file diff --git a/src/gui/GuiElement.hpp b/src/gui/GuiElement.hpp index 6661fbd..2177d3d 100644 --- a/src/gui/GuiElement.hpp +++ b/src/gui/GuiElement.hpp @@ -9,10 +9,8 @@ This file describes the GuiElement class. This is the base class for further GUI #include #include "RenderObject.hpp" #include "RenderView.hpp" -#include "RenderCamera.hpp" class GuiElement { friend class Gui; - friend class GuiList; public: enum Type { DEFAULT = 0, @@ -44,48 +42,52 @@ class GuiElement { GuiElement(); virtual ~GuiElement(); // - int setGeometry(float x, float y, float w, float h); - // int setText(const char *string); + virtual int doUpdate(int container_x, int container_y, int container_w, int container_h, int c_width, int c_height); + int doRender(); + // + void setName(const char *string); + const char* getName(); GuiElement *getHit(float xhit, float yhit); + void hide(); + void show(); + // relationship functions int addChild(GuiElement *child); int remChild(GuiElement *child); int setParent(GuiElement *parent); - int setPFlags(int pflags); - RenderView *getView(); - virtual int doUpdate(int container_x, int container_y, int container_w, int container_h, int c_width, int c_height); - int doRender(); + // getters float getWidth(); float getHeight(); - void setSize(float width, float height); - void setPosition(float newx, float newy); - void setOffset(float offx, float offy); + int getFlags(); + RenderView *getView(); + // positional/sizing/flag setting functions + int setGeometry(float x, float y, float w, float h); void setMargin(float l, float r, float t, float b); void setPadding(float l, float r, float t, float b); - void setBehavior(int behavior_flags); void setOrigin(int origin_flags); void setDirection(int direction_flags); - int getFlags(); void setFlags(int new_flags); - void hide(); - void show(); protected: - int type; // flag for this type + // behavioral int origin_flags; // origin flags int direction_flags;// direction flags int flags; // flags - std::string text; // text of this element - GuiElement *parent; // parent of this element - std::vector children; // children of this element - RenderObject *object;// render object - RenderView *view; // Ugh, we use FBOs for GUI elements cuz otherwise it's too annoying. + // relationships + GuiElement *parent; // parent of this element + std::vector children; // children of this element + // rendering data + RenderObject *object; // render object + RenderView *view; // Ugh, we use FBOs for GUI elements cuz otherwise it's too annoying. + // identity + int type; // flag for this type + std::string name; // name of this element int gid; // group id for this element int id; // id for this element - // - float x, y, w, h; // position and dimensions for this element - float offset_x, offset_y; // offset x and y - float base_w, base_h; - float margin_l, margin_r, margin_t, margin_b; - float padding_l, padding_r, padding_t, padding_b; - float bound_l, bound_r, bound_t, bound_b; + // positioning + float x, y, w, h; // final position/dimensions for this element, acquired from positioning, offset, margin, padding, and base + float offset_x, offset_y; // offset x and y + float base_w, base_h; // base width and height + float margin_l, margin_r, margin_t, margin_b; // margin from other element(s) to add + float padding_l, padding_r, padding_t, padding_b; // padding to use internally if applicable + float bound_l, bound_r, bound_t, bound_b; // bounding box, indicates the full size(virtual) that this element and its children take }; #endif diff --git a/src/gui/GuiList.cpp b/src/gui/GuiList.cpp index bb991a1..80ea561 100644 --- a/src/gui/GuiList.cpp +++ b/src/gui/GuiList.cpp @@ -1,8 +1,8 @@ #include "GuiList.hpp" #include "Log.hpp" -GuiList::GuiList(const char *name) { - text.assign(name); +GuiList::GuiList(const char *name_) { + name.assign(name_); type = LIST; dflags = HORIZONTAL|RIGHT; } diff --git a/src/states/MenuState.cpp b/src/states/MenuState.cpp index c708109..1d76b21 100644 --- a/src/states/MenuState.cpp +++ b/src/states/MenuState.cpp @@ -16,6 +16,13 @@ int MenuState::doProcess(unsigned int ticks) { return 0; } int MenuState::onEvent(SDL_Event event) { + switch (event.type) { + case GUI_EVENT: + if (event.user.code == GUI_HIT) { + LOG(LOG_ERROR) << FUNC_NAME << ": recv'd hit from " << ((GuiElement*)event.user.data1)->getName(); + } + break; + } return 0; } int MenuState::onCede() { @@ -43,16 +50,17 @@ int MenuState::onRise() { gbutton->setGeometry(0.0f, 0.0f, 128.0f, 128.0f); gbutton->setDirection(GuiElement::DOWN); gbutton->setOrigin(GuiElement::VCENTER|GuiElement::HCENTER); - gbutton->setFlags(GuiElement::CAN_RESIZE); + //gbutton->setFlags(GuiElement::CAN_RESIZE); GuiButton *gbutton2 = new GuiButton("logo2", core.getTexture("ui/icon_perscube.png")); gbutton2->setGeometry(0.0f, 0.0f, 128.0f, 128.0f); gbutton2->setDirection(GuiElement::DOWN|GuiElement::RIGHT); gbutton2->setOrigin(GuiElement::BOTTOM|GuiElement::RIGHT); + gbutton2->setMargin(25.0f, 25.0f, 25.0f, 25.0f); gbutton->addChild(gbutton2); gbutton2 = new GuiButton("logo3", core.getTexture("ui/icon_token.png")); gbutton2->setGeometry(0.0f, 0.0f, 128.0f, 128.0f); - gbutton2->setDirection(GuiElement::LEFT|GuiElement::UP); + gbutton2->setDirection(GuiElement::RIGHT|GuiElement::UP); gbutton2->setOrigin(GuiElement::TOP|GuiElement::RIGHT); gbutton->addChild(gbutton2);