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.

master
kts 2015-03-07 22:56:27 -08:00
parent 549b391c4e
commit b8d6462723
7 changed files with 100 additions and 60 deletions

View File

@ -94,6 +94,7 @@ int Gui::onEvent(SDL_Event event) {
std::vector<GuiElement*>::reverse_iterator element_it, element_end; std::vector<GuiElement*>::reverse_iterator element_it, element_end;
GuiElement *element = NULL; GuiElement *element = NULL;
float x, y; float x, y;
SDL_Event hit_event; // event to send to the events queue if a gui element is hit
switch (event.type) { switch (event.type) {
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: 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) { for (element_it = elements.rbegin(), element_end = elements.rend(); element_it != element_end; ++element_it) {
if ((*element_it)->getFlags() & GuiElement::HIDDEN) continue; if ((*element_it)->getFlags() & GuiElement::HIDDEN) continue;
element = (*element_it)->getHit(x, y); 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; break;
case SDL_FINGERDOWN: 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) { for (element_it = elements.rbegin(), element_end = elements.rend(); element_it != element_end; ++element_it) {
if ((*element_it)->getFlags() & GuiElement::HIDDEN) continue; if ((*element_it)->getFlags() & GuiElement::HIDDEN) continue;
element = (*element_it)->getHit(x, y); 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; break;
default: default:
@ -150,7 +165,7 @@ int Gui::addElement(GuiElement *element) {
}*/ }*/
if (element->parent == NULL) { if (element->parent == NULL) {
elements.push_back(element); 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; return elements.size()-1;
} }
@ -159,7 +174,7 @@ int Gui::delElement(const char *name) {
std::vector<GuiElement*>::iterator element_it, element_end; std::vector<GuiElement*>::iterator element_it, element_end;
for (element_it = elements.begin(), element_end = elements.end(); element_it != element_end;) { for (element_it = elements.begin(), element_end = elements.end(); element_it != element_end;) {
element = *element_it; 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); if (element->object != NULL) set_basic->remObject(element->object);
delete element; delete element;
element_it = elements.erase(element_it); element_it = elements.erase(element_it);
@ -192,7 +207,6 @@ RenderCamera* Gui::getChildCamera() {
void Gui::updateElements() { void Gui::updateElements() {
std::vector<GuiElement*>::iterator element_it, element_end; std::vector<GuiElement*>::iterator element_it, element_end;
GuiElement *p_element = NULL; GuiElement *p_element = NULL;
GuiElement *c_element = NULL;
for (element_it = elements.begin(), element_end = elements.end(); element_it != element_end; ++element_it) { for (element_it = elements.begin(), element_end = elements.end(); element_it != element_end; ++element_it) {
p_element = *element_it; p_element = *element_it;
if (p_element->getFlags() & GuiElement::HIDDEN) continue; if (p_element->getFlags() & GuiElement::HIDDEN) continue;

View File

@ -11,6 +11,15 @@ Gui.cpp/Gui.hpp provide the class responsible for creating, destroying, and hand
#include "RenderCamera.hpp" #include "RenderCamera.hpp"
#include "AssetManager.hpp" #include "AssetManager.hpp"
#include <vector> #include <vector>
// **** 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 { class Gui {
friend class Core; friend class Core;
public: public:

View File

@ -1,8 +1,8 @@
#include "GuiButton.hpp" #include "GuiButton.hpp"
#include "Log.hpp" #include "Log.hpp"
GuiButton::GuiButton(const char *name, Texture *texture_) { GuiButton::GuiButton(const char *name_, Texture *texture_) {
text.assign(name); name.assign(name_);
texture = texture_; texture = texture_;
} }
GuiButton::~GuiButton() { GuiButton::~GuiButton() {

View File

@ -23,6 +23,12 @@ GuiElement::~GuiElement() {
if (view != NULL) delete view; if (view != NULL) delete view;
if (object != NULL) delete object; 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 ======== */ /* ======== Relationships ======== */
int GuiElement::addChild(GuiElement *child) { int GuiElement::addChild(GuiElement *child) {
// adopt the child! // adopt the child!
@ -33,6 +39,7 @@ int GuiElement::addChild(GuiElement *child) {
children.push_back(child); children.push_back(child);
return children.size()-1; return children.size()-1;
} }
// Disown thine seed!
int GuiElement::remChild(GuiElement *child) { int GuiElement::remChild(GuiElement *child) {
child->parent = NULL; child->parent = NULL;
std::vector<GuiElement*>::iterator it; std::vector<GuiElement*>::iterator it;
@ -43,33 +50,34 @@ int GuiElement::remChild(GuiElement *child) {
return 1; return 1;
} }
int GuiElement::setParent(GuiElement *parent_) { int GuiElement::setParent(GuiElement *parent_) {
if (parent) parent->remChild(this);
parent = parent_; parent = parent_;
// TODO: detach from old parent
return 0; return 0;
} }
/* ======== Interactions ======== */ /* ======== Interactions ======== */
/* getHit /* 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* GuiElement::getHit(float xhit, float yhit) {
GuiElement *element = NULL; 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) ) { 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 // check the children for hits and return them if so
GuiElement *child_element = NULL; GuiElement *child_element = NULL;
// Start from the end of the vector and walk backwards // Start from the end of the vector and walk backwards
std::vector<GuiElement*>::reverse_iterator element_it, element_end; std::vector<GuiElement*>::reverse_iterator element_it, element_end;
for (element_it = children.rbegin(), element_end = children.rend(); element_it != element_end; ++element_it) { for (element_it = children.rbegin(), element_end = children.rend(); element_it != element_end; ++element_it) {
child_element = (*element_it)->getHit(xhit, yhit); 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; return element;
} }
@ -87,7 +95,6 @@ int GuiElement::setGeometry(float x_, float y_, float w_, float h_) {
base_h = h_; base_h = h_;
//view->setView(w, h); //view->setView(w, h);
object->setScale(base_w, 1.0, base_h); object->setScale(base_w, 1.0, base_h);
//object->setTranslation(x, 0.0, y);
object->calcMatrix(); object->calcMatrix();
return 0; return 0;
} }
@ -109,8 +116,8 @@ void GuiElement::setMargin(float l, float r, float t, float b) {
margin_t = t; margin_t = t;
margin_b = b; margin_b = b;
} }
/* ======== Update ======== */
/* ======== Render, eugh ======== */ // 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 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_x = 0;
int current_y = 0; int current_y = 0;
@ -127,18 +134,18 @@ int GuiElement::doUpdate(int container_x, int container_y, int container_w, int
} }
// get origin // get origin
if (origin_flags & LEFT) { if (origin_flags & LEFT) {
current_x = container_x; current_x = container_x + margin_l;
} else if (origin_flags & RIGHT) { } else if (origin_flags & RIGHT) {
current_x = container_x+container_w; current_x = container_x + container_w + margin_r;
} else if (origin_flags & HCENTER) { } 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) { if (origin_flags & TOP) {
current_y = container_y; current_y = container_y + margin_t;
} else if (origin_flags & BOTTOM) { } else if (origin_flags & BOTTOM) {
current_y = container_y+container_h; current_y = container_y + container_h + margin_b;
} else if (origin_flags & VCENTER) { } 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 // set our width and height
current_w = base_w; current_w = base_w;
@ -246,4 +253,4 @@ void GuiElement::setFlags(int new_flags) {
} }
int GuiElement::getFlags() { int GuiElement::getFlags() {
return flags; return flags;
} }

View File

@ -9,10 +9,8 @@ This file describes the GuiElement class. This is the base class for further GUI
#include <vector> #include <vector>
#include "RenderObject.hpp" #include "RenderObject.hpp"
#include "RenderView.hpp" #include "RenderView.hpp"
#include "RenderCamera.hpp"
class GuiElement { class GuiElement {
friend class Gui; friend class Gui;
friend class GuiList;
public: public:
enum Type { enum Type {
DEFAULT = 0, DEFAULT = 0,
@ -44,48 +42,52 @@ class GuiElement {
GuiElement(); GuiElement();
virtual ~GuiElement(); virtual ~GuiElement();
// //
int setGeometry(float x, float y, float w, float h); virtual int doUpdate(int container_x, int container_y, int container_w, int container_h, int c_width, int c_height);
// int setText(const char *string); int doRender();
//
void setName(const char *string);
const char* getName();
GuiElement *getHit(float xhit, float yhit); GuiElement *getHit(float xhit, float yhit);
void hide();
void show();
// relationship functions
int addChild(GuiElement *child); int addChild(GuiElement *child);
int remChild(GuiElement *child); int remChild(GuiElement *child);
int setParent(GuiElement *parent); int setParent(GuiElement *parent);
int setPFlags(int pflags); // getters
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();
float getWidth(); float getWidth();
float getHeight(); float getHeight();
void setSize(float width, float height); int getFlags();
void setPosition(float newx, float newy); RenderView *getView();
void setOffset(float offx, float offy); // 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 setMargin(float l, float r, float t, float b);
void setPadding(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 setOrigin(int origin_flags);
void setDirection(int direction_flags); void setDirection(int direction_flags);
int getFlags();
void setFlags(int new_flags); void setFlags(int new_flags);
void hide();
void show();
protected: protected:
int type; // flag for this type // behavioral
int origin_flags; // origin flags int origin_flags; // origin flags
int direction_flags;// direction flags int direction_flags;// direction flags
int flags; // flags int flags; // flags
std::string text; // text of this element // relationships
GuiElement *parent; // parent of this element GuiElement *parent; // parent of this element
std::vector<GuiElement*> children; // children of this element std::vector<GuiElement*> children; // children of this element
RenderObject *object;// render object // rendering data
RenderView *view; // Ugh, we use FBOs for GUI elements cuz otherwise it's too annoying. 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 gid; // group id for this element
int id; // id for this element int id; // id for this element
// // positioning
float x, y, w, h; // position and dimensions for this element 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 offset_x, offset_y; // offset x and y
float base_w, base_h; float base_w, base_h; // base width and height
float margin_l, margin_r, margin_t, margin_b; 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; float padding_l, padding_r, padding_t, padding_b; // padding to use internally if applicable
float bound_l, bound_r, bound_t, bound_b; float bound_l, bound_r, bound_t, bound_b; // bounding box, indicates the full size(virtual) that this element and its children take
}; };
#endif #endif

View File

@ -1,8 +1,8 @@
#include "GuiList.hpp" #include "GuiList.hpp"
#include "Log.hpp" #include "Log.hpp"
GuiList::GuiList(const char *name) { GuiList::GuiList(const char *name_) {
text.assign(name); name.assign(name_);
type = LIST; type = LIST;
dflags = HORIZONTAL|RIGHT; dflags = HORIZONTAL|RIGHT;
} }

View File

@ -16,6 +16,13 @@ int MenuState::doProcess(unsigned int ticks) {
return 0; return 0;
} }
int MenuState::onEvent(SDL_Event event) { 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; return 0;
} }
int MenuState::onCede() { int MenuState::onCede() {
@ -43,16 +50,17 @@ int MenuState::onRise() {
gbutton->setGeometry(0.0f, 0.0f, 128.0f, 128.0f); gbutton->setGeometry(0.0f, 0.0f, 128.0f, 128.0f);
gbutton->setDirection(GuiElement::DOWN); gbutton->setDirection(GuiElement::DOWN);
gbutton->setOrigin(GuiElement::VCENTER|GuiElement::HCENTER); 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")); GuiButton *gbutton2 = new GuiButton("logo2", core.getTexture("ui/icon_perscube.png"));
gbutton2->setGeometry(0.0f, 0.0f, 128.0f, 128.0f); gbutton2->setGeometry(0.0f, 0.0f, 128.0f, 128.0f);
gbutton2->setDirection(GuiElement::DOWN|GuiElement::RIGHT); gbutton2->setDirection(GuiElement::DOWN|GuiElement::RIGHT);
gbutton2->setOrigin(GuiElement::BOTTOM|GuiElement::RIGHT); gbutton2->setOrigin(GuiElement::BOTTOM|GuiElement::RIGHT);
gbutton2->setMargin(25.0f, 25.0f, 25.0f, 25.0f);
gbutton->addChild(gbutton2); gbutton->addChild(gbutton2);
gbutton2 = new GuiButton("logo3", core.getTexture("ui/icon_token.png")); gbutton2 = new GuiButton("logo3", core.getTexture("ui/icon_token.png"));
gbutton2->setGeometry(0.0f, 0.0f, 128.0f, 128.0f); 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); gbutton2->setOrigin(GuiElement::TOP|GuiElement::RIGHT);
gbutton->addChild(gbutton2); gbutton->addChild(gbutton2);