Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118189
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
54 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/GUIObject.cpp b/src/GUIObject.cpp
index ce7e50e..e10bf53 100644
--- a/src/GUIObject.cpp
+++ b/src/GUIObject.cpp
@@ -1,953 +1,964 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GUIObject.h"
#include "ThemeManager.h"
#include <algorithm>
#include <iostream>
#include <list>
#include <SDL_ttf.h>
#include "Render.h"
using namespace std;
//Set the GUIObjectRoot to NULL.
GUIObject* GUIObjectRoot=NULL;
//Initialise the event queue.
list<GUIEvent> GUIEventQueue;
void GUIObjectHandleEvents(ImageManager& imageManager, SDL_Renderer& renderer, bool kill){
//NOTE: This was already not doing anything so commenting it for now.
/*
//Check if user resizes the window.
if(event.type==SDL_VIDEORESIZE){
//onVideoResize();
//Don't let other objects process this event (?)
return;
}*/
//Make sure that GUIObjectRoot isn't null.
if(GUIObjectRoot)
GUIObjectRoot->handleEvents(renderer);
//Check for SDL_QUIT.
if(event.type==SDL_QUIT && kill){
//We get a quit event so enter the exit state.
setNextState(STATE_EXIT);
delete GUIObjectRoot;
GUIObjectRoot=NULL;
return;
}
//Keep calling events until there are none left.
while(!GUIEventQueue.empty()){
//Get one event and remove it from the queue.
GUIEvent e=GUIEventQueue.front();
GUIEventQueue.pop_front();
//If an eventCallback exist call it.
if(e.eventCallback){
e.eventCallback->GUIEventCallback_OnEvent(imageManager,renderer,e.name,e.obj,e.eventType);
}
}
//We empty the event queue just to be sure.
GUIEventQueue.clear();
}
GUIObject::~GUIObject(){
//We need to delete every child we have.
for(unsigned int i=0;i<childControls.size();i++){
delete childControls[i];
}
//Deleted the childs now empty the childControls vector.
childControls.clear();
}
bool GUIObject::handleEvents(SDL_Renderer& renderer,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(renderer,x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIObject::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the GUIObject when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(renderer,x,y,draw);
}
}
void GUIObject::refreshCache(bool enabled) {
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//TODO: Only change alpha if only enabled changes.
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
}
//////////////GUIButton///////////////////////////////////////////////////////////////////
bool GUIButton::handleEvents(SDL_Renderer& renderer,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//We don't update button state under keyboard only mode.
if (!isKeyboardOnly) {
//Set state to 0.
state = 0;
//Only check for events when the object is both enabled and visible.
if (enabled && visible) {
//The mouse location (x=i, y=j) and the mouse button (k).
int i, j, k;
k = SDL_GetMouseState(&i, &j);
//Check if the mouse is inside the widget.
if (i >= x && i < x + width && j >= y && j < y + height) {
//We have hover so set state to one.
state = 1;
//Check for a mouse button press.
if (k&SDL_BUTTON(1))
state = 2;
//Check if there's a mouse press and the event hasn't been already processed.
if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT && !b) {
//If event callback is configured then add an event to the queue.
if (eventCallback) {
GUIEvent e = { eventCallback, name, this, GUIEventClick };
GUIEventQueue.push_back(e);
}
//Event has been processed.
b = true;
}
}
}
}
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(renderer,x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIButton::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
refreshCache(enabled);
//Get the text and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
//Update cache if needed.
if(!cacheTex){
SDL_Color color = objThemes.getTextColor(inDialog);
if(!smallFont) {
cacheTex = textureFromText(renderer, *fontGUI, lp, color);
} else {
cacheTex = textureFromText(renderer, *fontGUISmall, lp, color);
}
//Make the widget transparent if it's disabled.
if(!enabled) {
SDL_SetTextureAlphaMod(cacheTex.get(), 128);
}
//Calculate proper size for the widget.
if(width<=0){
width=textureWidth(*cacheTex)+50;
if(gravity==GUIGravityCenter){
gravityX=int(width/2);
}else if(gravity==GUIGravityRight){
gravityX=width;
}else{
gravityX=0;
}
}
}
if(draw){
//Center the text both vertically as horizontally.
const SDL_Rect size = rectFromTexture(*cacheTex);
const int drawX=x-gravityX+(width-size.w)/2;
const int drawY=y+(height-size.h)/2-GUI_FONT_RAISE;
//Check if the arrows don't fall of.
if(size.w+32<=width){
if(state==1){
if(inDialog){
applyTexture(x-gravityX+(width-size.w)/2+4+size.w+5,y+2,*arrowLeft2,renderer);
applyTexture(x-gravityX+(width-size.w)/2-25,y+2,*arrowRight2,renderer);
}else{
applyTexture(x-gravityX+(width-size.w)/2+4+size.w+5,y+2,*arrowLeft1,renderer);
applyTexture(x-gravityX+(width-size.w)/2-25,y+2,*arrowRight1,renderer);
}
}else if(state==2){
if(inDialog){
applyTexture(x-gravityX+(width-size.w)/2+4+size.w,y+2,*arrowLeft2,renderer);
applyTexture(x-gravityX+(width-size.w)/2-20,y+2,*arrowRight2,renderer);
}else{
applyTexture(x-gravityX+(width-size.w)/2+4+size.w,y+2,*arrowLeft1,renderer);
applyTexture(x-gravityX+(width-size.w)/2-20,y+2,arrowRight1,renderer);
}
}
}
//Draw the text.
applyTexture(drawX, drawY, *cacheTex, renderer);
}
}
}
//////////////GUICheckBox///////////////////////////////////////////////////////////////////
bool GUICheckBox::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
- //Set state to 0.
- state=0;
-
- //Only check for events when the object is both enabled and visible.
- if(enabled&&visible){
- //The mouse location (x=i, y=j) and the mouse button (k).
- int i,j,k;
- k=SDL_GetMouseState(&i,&j);
-
- //Check if the mouse is inside the widget.
- if(i>=x&&i<x+width&&j>=y&&j<y+height){
- //We have hover so set state to one.
- state=1;
- //Check for a mouse button press.
- if(k&SDL_BUTTON(1))
- state=2;
-
- //Check if there's a mouse press and the event hasn't been already processed.
- if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT && !b){
- //It's a checkbox so toggle the value.
- value=value?0:1;
-
- //If event callback is configured then add an event to the queue.
- if(eventCallback){
- GUIEvent e={eventCallback,name,this,GUIEventClick};
- GUIEventQueue.push_back(e);
+ //We don't update state under keyboard only mode.
+ if (!isKeyboardOnly) {
+ //Set state to 0.
+ state = 0;
+
+ //Only check for events when the object is both enabled and visible.
+ if (enabled&&visible){
+ //The mouse location (x=i, y=j) and the mouse button (k).
+ int i, j, k;
+ k = SDL_GetMouseState(&i, &j);
+
+ //Check if the mouse is inside the widget.
+ if (i >= x && i < x + width && j >= y && j < y + height){
+ //We have hover so set state to one.
+ state = 1;
+ //Check for a mouse button press.
+ if (k&SDL_BUTTON(1))
+ state = 2;
+
+ //Check if there's a mouse press and the event hasn't been already processed.
+ if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT && !b){
+ //It's a checkbox so toggle the value.
+ value = value ? 0 : 1;
+
+ //If event callback is configured then add an event to the queue.
+ if (eventCallback){
+ GUIEvent e = { eventCallback, name, this, GUIEventClick };
+ GUIEventQueue.push_back(e);
+ }
+
+ //Event has been processed.
+ b = true;
}
-
- //Event has been processed.
- b=true;
}
}
}
return b;
}
void GUICheckBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
refreshCache(enabled);
+ //Draw the highlight in keyboard only mode.
+ if (isKeyboardOnly && state && draw) {
+ drawGUIBox(x, y, width, height, renderer, 0xFFFFFF40);
+ }
+
//Get the text.
const char* lp=caption.c_str();
//Make sure it isn't empty.
if(lp!=NULL && lp[0]){
//Update the cache if needed.
if(!cacheTex){
SDL_Color color = objThemes.getTextColor(inDialog);
cacheTex=textureFromText(renderer,*fontText,lp,color);
}
if(draw){
//Calculate the location, center it vertically.
const int drawX=x;
const int drawY=y+(height - textureHeight(*cacheTex))/2;
//Draw the text
applyTexture(drawX, drawY, *cacheTex, renderer);
}
}
if(draw){
//Draw the check (or not).
//value*16 determines where in the gui textures we draw from.
//if(value==1||value==2)
// r1.x=value*16;
const SDL_Rect srcRect={value*16,0,16,16};
const SDL_Rect dstRect={x+width-20, y+(height-16)/2, 16, 16};
//Get the right image depending on the state of the object.
SDL_RenderCopy(&renderer, bmGuiTex.get(), &srcRect, &dstRect);
}
}
//////////////GUILabel///////////////////////////////////////////////////////////////////
bool GUILabel::handleEvents(SDL_Renderer&,int ,int ,bool ,bool ,bool processed){
return processed;
}
void GUILabel::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
refreshCache(enabled);
//Get the absolute x and y location.
x+=left;
y+=top;
//Rectangle the size of the widget.
SDL_Rect r;
r.x=x;
r.y=y;
r.w=width;
r.h=height;
//Get the caption and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
//Update cache if needed.
if(!cacheTex){
SDL_Color color = objThemes.getTextColor(inDialog);
cacheTex=textureFromText(renderer, *fontText, lp, color);
if(width<=0)
width=textureWidth(*cacheTex);
}
//Align the text properly and draw it.
if(draw){
const SDL_Rect size = rectFromTexture(*cacheTex);
if(gravity==GUIGravityCenter)
gravityX=(width-size.w)/2;
else if(gravity==GUIGravityRight)
gravityX=width-size.w;
else
gravityX=0;
r.y=y+(height - size.h)/2;
r.x+=gravityX;
applyTexture(r.x, r.y, cacheTex, renderer);
}
}
}
//////////////GUITextBox///////////////////////////////////////////////////////////////////
void GUITextBox::backspaceChar(){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0){
if(highlightStart==highlightEnd&&highlightStart>0){
int advance = 0;
// this is proper UTF-8 support
int ch = utf8ReadBackward(caption.c_str(), highlightStart); // we obtain new highlightStart from this
if (ch > 0) TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
highlightEndX = highlightStartX = highlightEndX - advance;
caption.erase(highlightStart, highlightEnd - highlightStart);
highlightEnd = highlightStart;
} else if (highlightStart<highlightEnd){
caption.erase(highlightStart,highlightEnd-highlightStart);
highlightEnd=highlightStart;
highlightEndX=highlightStartX;
}else{
caption.erase(highlightEnd,highlightStart-highlightEnd);
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}
void GUITextBox::deleteChar(){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0){
if(highlightStart==highlightEnd){
// this is proper utf8 support
int i = highlightEnd;
utf8ReadForward(caption.c_str(), i);
if (i > highlightEnd) caption.erase(highlightEnd, i - highlightEnd);
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}else if(highlightStart<highlightEnd){
caption.erase(highlightStart,highlightEnd-highlightStart);
highlightEnd=highlightStart;
highlightEndX=highlightStartX;
}else{
caption.erase(highlightEnd,highlightStart-highlightEnd);
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}
void GUITextBox::moveCarrotLeft(){
if(highlightEnd>0){
int advance = 0;
// this is proper UTF-8 support
int ch = utf8ReadBackward(caption.c_str(), highlightEnd); // we obtain new highlightEnd from this
if (ch > 0) TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
if(SDL_GetModState() & KMOD_SHIFT){
highlightEndX-=advance;
}else{
highlightStart=highlightEnd;
highlightStartX=highlightEndX=highlightEndX-advance;
}
}else{
if((SDL_GetModState() & KMOD_SHIFT)==0){
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
}
tick=15;
}
void GUITextBox::moveCarrotRight(){
if(highlightEnd<caption.length()){
int advance = 0;
// this is proper UTF-8 support
int ch = utf8ReadForward(caption.c_str(), highlightEnd); // we obtain new highlightEnd from this
if (ch > 0) TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
if(SDL_GetModState() & KMOD_SHIFT){
highlightEndX+=advance;
}else{
highlightStartX=highlightEndX=highlightEndX+advance;
highlightStart=highlightEnd;
}
}else{
if((SDL_GetModState() & KMOD_SHIFT)==0){
highlightStart=highlightEnd;
highlightStartX=highlightEndX;
}
}
tick=15;
}
void GUITextBox::inputText(const char* s) {
int m = strlen(s);
if (m > 0){
if (highlightStart == highlightEnd) {
caption.insert((size_t)highlightStart, s);
highlightStart += m;
highlightEnd = highlightStart;
} else if (highlightStart < highlightEnd) {
caption.erase(highlightStart, highlightEnd - highlightStart);
caption.insert((size_t)highlightStart, s);
highlightStart += m;
highlightEnd = highlightStart;
highlightEndX = highlightStartX;
} else {
caption.erase(highlightEnd, highlightStart - highlightEnd);
caption.insert((size_t)highlightEnd, s);
highlightEnd += m;
highlightStart = highlightEnd;
highlightStartX = highlightEndX;
}
int advance = 0;
for (int i = 0;;) {
int a = 0;
int ch = utf8ReadForward(s, i);
if (ch <= 0) break;
TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &a);
advance += a;
}
highlightStartX = highlightEndX = highlightStartX + advance;
//If there is an event callback then call it.
if (eventCallback){
GUIEvent e = { eventCallback, name, this, GUIEventChange };
GUIEventQueue.push_back(e);
}
}
}
bool GUITextBox::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//NOTE: We don't reset the state to have a "focus" effect.
//Only check for events when the object is both enabled and visible.
if(enabled&&visible){
//Check if there's a key press and the event hasn't been already processed.
if(state==2 && event.type==SDL_KEYDOWN && !b){
//Get the keycode.
SDL_Keycode key=event.key.keysym.sym;
if ((event.key.keysym.mod & KMOD_CTRL) == 0) {
//Check if the key is supported.
if (event.key.keysym.sym == SDLK_BACKSPACE){
backspaceChar();
} else if (event.key.keysym.sym == SDLK_DELETE){
deleteChar();
} else if (event.key.keysym.sym == SDLK_RIGHT){
moveCarrotRight();
} else if (event.key.keysym.sym == SDLK_LEFT){
moveCarrotLeft();
}
} else {
//Check hotkey.
if (event.key.keysym.sym == SDLK_a) {
//Select all.
highlightStart = 0;
highlightStartX = 0;
highlightEnd = caption.size();
highlightEndX = 0;
if (highlightEnd > 0) {
TTF_SizeUTF8(fontText, caption.c_str(), &highlightEndX, NULL);
}
} else if (event.key.keysym.sym == SDLK_x || event.key.keysym.sym == SDLK_c) {
//Cut or copy.
int start = highlightStart, end = highlightEnd;
if (start > end) std::swap(start, end);
if (start < end) {
SDL_SetClipboardText(caption.substr(start, end - start).c_str());
if (event.key.keysym.sym == SDLK_x) {
//Cut.
backspaceChar();
}
}
} else if (event.key.keysym.sym == SDLK_v) {
//Paste.
if (SDL_HasClipboardText()) {
char *s = SDL_GetClipboardText();
inputText(s);
SDL_free(s);
}
}
}
//The event has been processed.
b = true;
} else if (state == 2 && event.type == SDL_TEXTINPUT && !b){
inputText(event.text.text);
//The event has been processed.
b = true;
} else if (state == 2 && event.type == SDL_TEXTEDITING && !b){
// TODO: process SDL_TEXTEDITING event
}
-
- //The mouse location (x=i, y=j) and the mouse button (k).
- int i,j,k;
- k=SDL_GetMouseState(&i,&j);
-
- //Check if the mouse is inside the widget.
- if(i>=x&&i<x+width&&j>=y&&j<y+height){
- //We can only increase our state. (nothing->hover->focus).
- if(state!=2){
- state=1;
- }
-
- //Also update the cursor type.
- currentCursor=CURSOR_CARROT;
-
- //Move carrot and highlightning according to mouse input.
- int clickX=i-x-2;
-
- int finalPos=0;
- int finalX=0;
-
- if(cacheTex&&!caption.empty()){
- finalPos=caption.length();
- for (int i = 0;;){
- int advance = 0;
-
- // this is proper UTF-8 support
- int i0 = i;
- int ch = utf8ReadForward(caption.c_str(), i);
- if (ch <= 0) break;
- TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
- finalX+=advance;
-
- if(clickX<finalX-advance/2){
- finalPos = i0;
- finalX-=advance;
- break;
+
+ //Only process mouse event when not in keyboard only mode
+ if (!isKeyboardOnly) {
+ //The mouse location (x=i, y=j) and the mouse button (k).
+ int i, j, k;
+ k = SDL_GetMouseState(&i, &j);
+
+ //Check if the mouse is inside the widget.
+ if (i >= x && i < x + width && j >= y && j < y + height){
+ //We can only increase our state. (nothing->hover->focus).
+ if (state != 2){
+ state = 1;
+ }
+
+ //Also update the cursor type.
+ currentCursor = CURSOR_CARROT;
+
+ //Move carrot and highlightning according to mouse input.
+ int clickX = i - x - 2;
+
+ int finalPos = 0;
+ int finalX = 0;
+
+ if (cacheTex&&!caption.empty()){
+ finalPos = caption.length();
+ for (int i = 0;;){
+ int advance = 0;
+
+ // this is proper UTF-8 support
+ int i0 = i;
+ int ch = utf8ReadForward(caption.c_str(), i);
+ if (ch <= 0) break;
+ TTF_GlyphMetrics(fontText, ch, NULL, NULL, NULL, NULL, &advance);
+ finalX += advance;
+
+ if (clickX < finalX - advance / 2){
+ finalPos = i0;
+ finalX -= advance;
+ break;
+ }
}
}
- }
-
- if(event.type==SDL_MOUSEBUTTONUP){
- state=2;
- highlightEnd=finalPos;
- highlightEndX=finalX;
- }else if(event.type==SDL_MOUSEBUTTONDOWN){
- state=2;
- highlightStart=highlightEnd=finalPos;
- highlightStartX=highlightEndX=finalX;
- }else if(event.type==SDL_MOUSEMOTION&&(k&SDL_BUTTON(1))){
- state=2;
- highlightEnd=finalPos;
- highlightEndX=finalX;
- }
- }else{
- //The mouse is outside the TextBox.
- //If we don't have focus but only hover we lose it.
- if(state==1){
- state=0;
- }
-
- //If it's a click event outside the textbox then we blur.
- if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
- //Set state to 0.
- state=0;
+
+ if (event.type == SDL_MOUSEBUTTONUP){
+ state = 2;
+ highlightEnd = finalPos;
+ highlightEndX = finalX;
+ } else if (event.type == SDL_MOUSEBUTTONDOWN){
+ state = 2;
+ highlightStart = highlightEnd = finalPos;
+ highlightStartX = highlightEndX = finalX;
+ } else if (event.type == SDL_MOUSEMOTION && (k&SDL_BUTTON(1))){
+ state = 2;
+ highlightEnd = finalPos;
+ highlightEndX = finalX;
+ }
+ } else{
+ //The mouse is outside the TextBox.
+ //If we don't have focus but only hover we lose it.
+ if (state == 1){
+ state = 0;
+ }
+
+ //If it's a click event outside the textbox then we blur.
+ if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT){
+ //Set state to 0.
+ state = 0;
+ }
}
}
}
return b;
}
void GUITextBox::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
refreshCache(enabled);
if(draw){
//Default background opacity
int clr=50;
//If hovering or focused make background more visible.
if(state==1)
clr=128;
else if (state==2)
clr=100;
//Draw the box.
Uint32 color=0xFFFFFF00|clr;
drawGUIBox(x,y,width,height,renderer,color);
}
//Rectangle used for drawing.
SDL_Rect r{0,0,0,0};
//Get the text and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
if(!cacheTex) {
//Draw the text.
cacheTex=textureFromText(renderer,*fontText,lp,objThemes.getTextColor(true));
}
if(draw){
//Only draw the carrot and highlight when focus.
if(state==2){
//Place the highlighted area.
r.x=x+4;
r.y=y+3;
r.h=height-6;
if(highlightStart<highlightEnd){
r.x+=highlightStartX;
r.w=highlightEndX-highlightStartX;
}else{
r.x+=highlightEndX;
r.w=highlightStartX-highlightEndX;
}
//Draw the area.
//SDL_FillRect(screen,&r,SDL_MapRGB(screen->format,128,128,128));
SDL_SetRenderDrawColor(&renderer, 128,128,128,255);
SDL_RenderFillRect(&renderer, &r);
//Ticking carrot.
if(tick<16){
//Show carrot: 15->0.
r.x=x+highlightEndX+2;
r.y=y+3;
r.h=height-6;
r.w=2;
//SDL_FillRect(screen,&r,SDL_MapRGB(screen->format,0,0,0));
SDL_SetRenderDrawColor(&renderer,0,0,0,255);
SDL_RenderFillRect(&renderer, &r);
//Reset: 32 or count down.
if(tick<=0)
tick=32;
else
tick--;
}else{
//Hide carrot: 32->16.
tick--;
}
}
//Calculate the location, center it vertically.
SDL_Rect dstRect=rectFromTexture(*cacheTex);
dstRect.x=x+4;
dstRect.y=y+(height-dstRect.h)/2;
dstRect.w=std::min(width-2, dstRect.w);
//Draw the text.
const SDL_Rect srcRect={0,0,width-2,25};
SDL_RenderCopy(&renderer, cacheTex.get(), &srcRect, &dstRect);
}
}else{
//Only draw the carrot when focus.
if(state==2&&draw){
//Ticking carrot.
if (tick<16){
//Show carrot: 15->0.
r.x = x + 4;
r.y = y + 4;
r.w = 2;
r.h = height - 8;
//SDL_FillRect(screen,&r,SDL_MapRGB(screen->format,0,0,0));
SDL_SetRenderDrawColor(&renderer, 0, 0, 0, 255);
SDL_RenderFillRect(&renderer, &r);
//Reset: 32 or count down.
if (tick <= 0)
tick = 32;
else
tick--;
} else{
//Hide carrot: 32->16.
tick--;
}
}
}
}
//////////////GUIFrame///////////////////////////////////////////////////////////////////
bool GUIFrame::handleEvents(SDL_Renderer& renderer,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The widget is only enabled when its parent are enabled.
enabled=enabled && this->enabled;
//The widget is only enabled when its parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left;
y+=top;
//Also let the children handle their events.
for(unsigned int i=0;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(renderer,x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
void GUIFrame::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing this widget when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget.
if(autoWidth)
width=-1;
}
//Draw fill and borders.
if(draw){
Uint32 color=0xDDDDDDFF;
drawGUIBox(x,y,width,height,renderer,color);
}
//Get the title text and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
//Update cache if needed.
if(!cacheTex) {
cacheTex = textureFromText(renderer, *fontGUI, lp, objThemes.getTextColor(true));
}
//Draw the text.
if(draw) {
applyTexture(x+(width-textureWidth(*cacheTex))/2, y+6-GUI_FONT_RAISE, *cacheTex, renderer);
}
}
//We now need to draw all the children.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(renderer,x,y,draw);
}
}
//////////////GUIImage///////////////////////////////////////////////////////////////////
GUIImage::~GUIImage(){
}
bool GUIImage::handleEvents(SDL_Renderer&,int ,int ,bool ,bool ,bool processed){
return processed;
}
void GUIImage::fitToImage(){
const SDL_Rect imageSize = rectFromTexture(*image);
//Increase or decrease the width and height to fully show the image.
if(clip.w!=0) {
width=clip.w;
} else {
width=imageSize.w;
}
if(clip.h!=0) {
height=clip.h;
} else {
height=imageSize.h;
}
}
void GUIImage::render(SDL_Renderer& renderer, int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
//Also make sure the image isn't null.
if(!visible || !image)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Create a clip rectangle.
SDL_Rect r=clip;
//The width and height are capped by the GUIImage itself.
if(r.w>width || r.w==0) {
r.w=width;
}
if(r.h>height || r.h==0) {
r.h=height;
}
const SDL_Rect dstRect={x,y,r.w,r.h};
SDL_RenderCopy(&renderer, image.get(), &r, &dstRect);
}
diff --git a/src/GUIOverlay.cpp b/src/GUIOverlay.cpp
index c38165b..b2840c5 100644
--- a/src/GUIOverlay.cpp
+++ b/src/GUIOverlay.cpp
@@ -1,332 +1,340 @@
/*
* Copyright (C) 2012-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GameState.h"
#include "Globals.h"
#include "GUIOverlay.h"
#include "InputManager.h"
#include "GUIObject.h"
#include "GUITextArea.h"
#include "GUISpinBox.h"
#include "GUIListBox.h"
//#include "StatisticsManager.h"
using namespace std;
GUIOverlay::GUIOverlay(SDL_Renderer& renderer, GUIObject* root,bool dim)
: root(root), dim(dim), keyboardNavigationMode(0)
{
//First keep the pointer to the current GUIObjectRoot and currentState.
parentState=currentState;
tempGUIObjectRoot=GUIObjectRoot;
//Now set the GUIObject root to the new root.
currentState=this;
GUIObjectRoot=root;
//Dim the background.
if(dim){
dimScreen(renderer);
}
}
GUIOverlay::~GUIOverlay(){
//We need to place everything back.
currentState=parentState;
parentState=NULL;
//Delete the GUI if present.
if(GUIObjectRoot)
delete GUIObjectRoot;
//Now put back the parent gui.
GUIObjectRoot=tempGUIObjectRoot;
tempGUIObjectRoot=NULL;
}
void GUIOverlay::enterLoop(ImageManager& imageManager, SDL_Renderer& renderer, bool skipByEscape, bool skipByReturn){
//Keep the last resize event, this is to only process one.
SDL_Event lastResize = {};
while (GUIObjectRoot){
while(SDL_PollEvent(&event)){
//Check for a resize event.
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
lastResize = event;
continue;
}
//Check if it's mouse event. If it's true then we quit the keyboard-only mode.
if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) {
isKeyboardOnly = false;
}
//Set the cursor type to the default one, the GUI can change that if needed.
currentCursor = CURSOR_POINTER;
//Let the input manager handle the events.
inputMgr.updateState(true);
//Let the currentState handle the events. (???)
currentState->handleEvents(imageManager, renderer);
//Also pass the events to the GUI.
GUIObjectHandleEvents(imageManager, renderer, true);
//Also check for the escape/return button.
if ((skipByEscape && inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)) || (skipByReturn && inputMgr.isKeyDownEvent(INPUTMGR_SELECT))) {
delete GUIObjectRoot;
GUIObjectRoot=NULL;
}
}
//Process the resize event.
if (lastResize.type == SDL_WINDOWEVENT){
//TODO - used to be SDL_VIDEORESIZE
// so this may trigger on more events than intended
event = lastResize;
onVideoResize(imageManager, renderer);
//After resize we erase the event type
//TODO - used to be SDL_NOEVENT
lastResize.type = SDL_FIRSTEVENT;
}
//update input state (??)
inputMgr.updateState(false);
//Render the gui.
if(GUIObjectRoot)
GUIObjectRoot->render(renderer);
/*//draw new achievements (if any)
statsMgr.render();*/
//display it
flipScreen(renderer);
SDL_Delay(30);
}
//We broke out so clean up.
delete this;
}
// internal function which is used in keyboard only mode
static int getSelectedControl() {
if (GUIObjectRoot == NULL) return -1;
for (int i = 0; i < (int)GUIObjectRoot->childControls.size(); i++) {
GUIObject *obj = GUIObjectRoot->childControls[i];
if (obj && obj->visible && obj->enabled && obj->state) {
- if (dynamic_cast<GUIButton*>(obj)
+ if (dynamic_cast<GUIButton*>(obj) || dynamic_cast<GUICheckBox*>(obj)
|| dynamic_cast<GUITextBox*>(obj) || dynamic_cast<GUISpinBox*>(obj)
|| dynamic_cast<GUISingleLineListBox*>(obj)
)
{
return i;
}
}
}
return -1;
}
// internal function which is used in keyboard only mode
static void selectNextControl(int direction) {
if (GUIObjectRoot == NULL) return;
//Get the index of currently selected control.
int selected = getSelectedControl();
if (selected >= 0) GUIObjectRoot->childControls[selected]->state = 0;
//Find the next control.
for (int i = 0; i < (int)GUIObjectRoot->childControls.size(); i++) {
if (selected < 0) {
selected = 0;
} else {
selected += direction;
if (selected >= (int)GUIObjectRoot->childControls.size()) {
selected -= GUIObjectRoot->childControls.size();
} else if (selected < 0) {
selected += GUIObjectRoot->childControls.size();
}
}
GUIObject *obj = GUIObjectRoot->childControls[selected];
if (obj && obj->visible && obj->enabled) {
- if (dynamic_cast<GUIButton*>(obj)) {
+ if (dynamic_cast<GUIButton*>(obj) || dynamic_cast<GUICheckBox*>(obj)) {
//It's a button.
obj->state = 1;
return;
} else if (dynamic_cast<GUITextBox*>(obj) || dynamic_cast<GUISpinBox*>(obj)) {
//It's a text box.
obj->state = 2;
return;
} else if (dynamic_cast<GUISingleLineListBox*>(obj)) {
//It's a single line list box.
obj->state = 0x100;
return;
}
}
}
}
void GUIOverlay::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer){
//Check if we need to quit, if so we enter the exit state.
if(event.type==SDL_QUIT){
setNextState(STATE_EXIT);
}
//Experimental code for keyboard navigation.
if (keyboardNavigationMode) {
//Check operation on focused control. These have higher priority.
if (isKeyboardOnly) {
//Check enter key.
if ((keyboardNavigationMode & 8) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_SELECT)) {
int index = getSelectedControl();
if (index >= 0) {
GUIObject *obj = GUIObjectRoot->childControls[index];
if (dynamic_cast<GUIButton*>(obj)) {
//It's a button.
if (obj->eventCallback) {
obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
}
return;
}
+ if (dynamic_cast<GUICheckBox*>(obj)) {
+ //It's a check box.
+ obj->value = obj->value ? 0 : 1;
+ if (obj->eventCallback) {
+ obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
+ }
+ return;
+ }
}
}
//Check left/right key.
if ((keyboardNavigationMode & 16) != 0 && (inputMgr.isKeyDownEvent(INPUTMGR_LEFT) || inputMgr.isKeyDownEvent(INPUTMGR_RIGHT))) {
int index = getSelectedControl();
if (index >= 0) {
GUIObject *obj = GUIObjectRoot->childControls[index];
auto sllb = dynamic_cast<GUISingleLineListBox*>(obj);
if (sllb) {
//It's a single line list box.
int newValue = sllb->value + (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT) ? 1 : -1);
if (newValue >= (int)sllb->item.size()) {
newValue -= sllb->item.size();
} else if (newValue < 0) {
newValue += sllb->item.size();
}
if (sllb->value != newValue) {
sllb->value = newValue;
if (obj->eventCallback) {
obj->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, obj->name, obj, GUIEventClick);
}
}
return;
}
}
}
}
//Check focus movement
int m = SDL_GetModState();
if (((keyboardNavigationMode & 1) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_RIGHT))
|| ((keyboardNavigationMode & 2) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_DOWN))
|| ((keyboardNavigationMode & 4) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_TAB) && (m & KMOD_SHIFT) == 0)
)
{
isKeyboardOnly = true;
selectNextControl(1);
} else if (((keyboardNavigationMode & 1) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_LEFT))
|| ((keyboardNavigationMode & 2) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_UP))
|| ((keyboardNavigationMode & 4) != 0 && inputMgr.isKeyDownEvent(INPUTMGR_TAB) && (m & KMOD_SHIFT) != 0)
)
{
isKeyboardOnly = true;
selectNextControl(-1);
}
}
}
//Nothing to do here
void GUIOverlay::logic(ImageManager&, SDL_Renderer&){
//Check if the GUIObjectRoot (of the overlay) is deleted.
if(!GUIObjectRoot)
delete this;
}
void GUIOverlay::render(ImageManager&, SDL_Renderer&){}
void GUIOverlay::resize(ImageManager& imageManager, SDL_Renderer& renderer){
//We recenter the GUI.
GUIObjectRoot->left=(SCREEN_WIDTH-GUIObjectRoot->width)/2;
GUIObjectRoot->top=(SCREEN_HEIGHT-GUIObjectRoot->height)/2;
//Now let the parent state resize.
GUIObjectRoot=tempGUIObjectRoot;
parentState->resize(imageManager, renderer);
//NOTE: After the resize it's likely that the GUIObjectRoot is new so we need to update our tempGUIObjectRoot pointer.
tempGUIObjectRoot=GUIObjectRoot;
//Now render the parentState.
parentState->render(imageManager,renderer);
if(GUIObjectRoot)
GUIObjectRoot->render(renderer);
//And set the GUIObjectRoot back to the overlay gui.
GUIObjectRoot=root;
//Dim the background.
if(dim){
dimScreen(renderer);
}
}
AddonOverlay::AddonOverlay(SDL_Renderer &renderer, GUIObject* root, GUIButton *cancelButton, GUITextArea *textArea)
: GUIOverlay(renderer, root), cancelButton(cancelButton), textArea(textArea)
{
keyboardNavigationMode = 4 | 8 | 16;
}
void AddonOverlay::handleEvents(ImageManager& imageManager, SDL_Renderer& renderer) {
GUIOverlay::handleEvents(imageManager, renderer);
//Do our own stuff.
//Scroll the text area.
if (textArea) {
if (inputMgr.isKeyDownEvent(INPUTMGR_RIGHT)){
isKeyboardOnly = true;
textArea->scrollScrollbar(20, 0);
} else if (inputMgr.isKeyDownEvent(INPUTMGR_LEFT)){
isKeyboardOnly = true;
textArea->scrollScrollbar(-20, 0);
} else if (inputMgr.isKeyDownEvent(INPUTMGR_UP)){
isKeyboardOnly = true;
textArea->scrollScrollbar(0, -1);
} else if (inputMgr.isKeyDownEvent(INPUTMGR_DOWN)){
isKeyboardOnly = true;
textArea->scrollScrollbar(0, 1);
}
}
//Check escape key.
if (cancelButton && cancelButton->eventCallback && inputMgr.isKeyDownEvent(INPUTMGR_ESCAPE)){
cancelButton->eventCallback->GUIEventCallback_OnEvent(imageManager, renderer, cancelButton->name, cancelButton, GUIEventClick);
return;
}
}
diff --git a/src/GUISpinBox.cpp b/src/GUISpinBox.cpp
index 5e79de8..57238e0 100644
--- a/src/GUISpinBox.cpp
+++ b/src/GUISpinBox.cpp
@@ -1,351 +1,354 @@
/*
* Copyright (C) 2011-2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Functions.h"
#include "GUISpinBox.h"
#include "ThemeManager.h"
#include <algorithm>
#include <SDL_ttf.h>
bool GUISpinBox::handleEvents(SDL_Renderer&,int x,int y,bool enabled,bool visible,bool processed){
//Boolean if the event is processed.
bool b=processed;
//The GUIObject is only enabled when he and his parent are enabled.
enabled=enabled && this->enabled;
//The GUIObject is only enabled when he and his parent are enabled.
visible=visible && this->visible;
//Get the absolute position.
x+=left-gravityX;
y+=top;
//Reset "key" to stop contant update of "number" in render().
//If the mouse is still on the button, the "key" will be reassigned later.
key=-1;
//Only check for events when the object is both enabled and visible.
if(enabled&&visible){
//Check if there's a key press and the event hasn't been already processed.
if(state==2 && event.type==SDL_KEYDOWN && !b){
//Get the keycode.
SDL_Keycode key=event.key.keysym.sym;
//Check if the key is supported.
if(key>=32&&key<=126){
//Add the key to the text after the carrot.
caption.insert((size_t)value,1,char(key));
value=clamp(value+1,0,caption.length());
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}else if(event.key.keysym.sym==SDLK_BACKSPACE){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0&&value>0){
//Remove the character before the carrot.
value=clamp(value-1,0,caption.length());
caption.erase((size_t)value,1);
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}else if(event.key.keysym.sym==SDLK_DELETE){
//We need to remove a character so first make sure that there is text.
if(caption.length()>0){
//Remove the character after the carrot.
value=clamp(value,0,caption.length());
caption.erase((size_t)value,1);
//If there is an event callback then call it.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
}else if(event.key.keysym.sym==SDLK_RIGHT){
value=clamp(value+1,0,caption.length());
}else if(event.key.keysym.sym==SDLK_LEFT){
value=clamp(value-1,0,caption.length());
}
//The event has been processed.
b=true;
}
- //The mouse location (x=i, y=j) and the mouse button (k).
- int i,j,k;
- k=SDL_GetMouseState(&i,&j);
-
- //Check if the mouse is inside the GUIObject.
- if(i>=x&&i<x+width&&j>=y&&j<y+height){
- //We can only increase our state. (nothing->hover->focus).
- if(state!=2){
- state=1;
- }
-
- //Also update the cursor type.
- if(i<x+width-16)
- currentCursor=CURSOR_CARROT;
-
- //Check for a mouse button press.
- if(k&SDL_BUTTON(1)){
- //We have focus.
- state=2;
-
- //Handle buttons.
- if(i>x+width-16){
- if(j<y+17){
- //Set the key values correct.
- this->key=SDLK_UP;
- keyHoldTime=0;
- keyTime=5;
-
- //Update once to prevent a lag.
- updateValue(true);
- }else{
- //Set the key values correct.
- this->key=SDLK_DOWN;
- keyHoldTime=0;
- keyTime=5;
-
- //Update once to prevent a lag.
- updateValue(false);
- }
- }else{
- //Move caret to the place clicked
- int click=i-x;
-
- if(!cacheTex){
- value=0;
- }else if(click>textureWidth(*cacheTex)){
- value=caption.length();
- }else{
- unsigned int wid=0;
- for(unsigned int i=0;i<caption.length();i++){
- int advance;
- TTF_GlyphMetrics(fontText,caption[i],NULL,NULL,NULL,NULL,&advance);
- wid+=advance;
-
- if(click<(int)wid-(int)advance/2){
- value=i;
- break;
+ //Only process mouse event when not in keyboard only mode
+ if (!isKeyboardOnly) {
+ //The mouse location (x=i, y=j) and the mouse button (k).
+ int i, j, k;
+ k = SDL_GetMouseState(&i, &j);
+
+ //Check if the mouse is inside the GUIObject.
+ if (i >= x&&i < x + width&&j >= y&&j < y + height){
+ //We can only increase our state. (nothing->hover->focus).
+ if (state != 2){
+ state = 1;
+ }
+
+ //Also update the cursor type.
+ if (i < x + width - 16)
+ currentCursor = CURSOR_CARROT;
+
+ //Check for a mouse button press.
+ if (k&SDL_BUTTON(1)){
+ //We have focus.
+ state = 2;
+
+ //Handle buttons.
+ if (i > x + width - 16){
+ if (j < y + 17){
+ //Set the key values correct.
+ this->key = SDLK_UP;
+ keyHoldTime = 0;
+ keyTime = 5;
+
+ //Update once to prevent a lag.
+ updateValue(true);
+ } else{
+ //Set the key values correct.
+ this->key = SDLK_DOWN;
+ keyHoldTime = 0;
+ keyTime = 5;
+
+ //Update once to prevent a lag.
+ updateValue(false);
+ }
+ } else{
+ //Move caret to the place clicked
+ int click = i - x;
+
+ if (!cacheTex){
+ value = 0;
+ } else if (click > textureWidth(*cacheTex)){
+ value = caption.length();
+ } else{
+ unsigned int wid = 0;
+ for (unsigned int i = 0; i < caption.length(); i++){
+ int advance;
+ TTF_GlyphMetrics(fontText, caption[i], NULL, NULL, NULL, NULL, &advance);
+ wid += advance;
+
+ if (click < (int)wid - (int)advance / 2){
+ value = i;
+ break;
+ }
}
}
}
}
- }
-
- //Allow mouse wheel to change value.
- if(event.type==SDL_MOUSEWHEEL){
- if(event.wheel.y > 0){
- updateValue(true);
- }else if(event.wheel.y < 0){
- updateValue(false);
+
+ //Allow mouse wheel to change value.
+ if (event.type == SDL_MOUSEWHEEL){
+ if (event.wheel.y > 0){
+ updateValue(true);
+ } else if (event.wheel.y < 0){
+ updateValue(false);
+ }
+ }
+ } else{
+ //The mouse is outside the TextBox.
+ //If we don't have focus but only hover we lose it.
+ if (state == 1){
+ state = 0;
+ update();
+ }
+
+ //If it's a click event outside the textbox then we blur.
+ if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT){
+ //Set state to 0.
+ state = 0;
+ update();
}
- }
- }else{
- //The mouse is outside the TextBox.
- //If we don't have focus but only hover we lose it.
- if(state==1){
- state=0;
- update();
- }
-
- //If it's a click event outside the textbox then we blur.
- if(event.type==SDL_MOUSEBUTTONUP && event.button.button==SDL_BUTTON_LEFT){
- //Set state to 0.
- state=0;
- update();
}
}
}
return b;
}
void GUISpinBox::render(SDL_Renderer &renderer, int x, int y, bool draw){
//FIXME: Logic in the render method since that is update constant.
if(key!=-1){
//Increase the key time.
keyHoldTime++;
//Make sure the deletionTime isn't to short.
if(keyHoldTime>=keyTime){
keyHoldTime=0;
keyTime--;
if(keyTime<1)
keyTime=1;
//Now check the which key it was.
switch(key){
case SDLK_UP:
{
updateValue(true);
break;
}
case SDLK_DOWN:
{
updateValue(false);
break;
}
}
}
}
//There's no need drawing when it's invisible.
if(!visible)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Check if the enabled state changed or the caption, if so we need to clear the (old) cache.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
cacheTex.reset(nullptr);
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
if(draw){
//Default background opacity.
int clr=50;
//If hovering or focused make background more visible.
if(state==1)
clr=128;
else if (state==2)
clr=100;
//Draw a background box.
const Uint32 color=0xFFFFFF00|clr;
drawGUIBox(x,y,width,height,renderer,color);
SDL_Rect srcRect={80,0,16,16};
//Draw arrow buttons.
SDL_Rect dstRect = {x+width-18, y+1, srcRect.w, srcRect.h};
SDL_RenderCopy(&renderer, bmGuiTex.get(), &srcRect, &dstRect);
srcRect.x=96;
dstRect.y+=16;
SDL_RenderCopy(&renderer, bmGuiTex.get(), &srcRect, &dstRect);
}
if(!caption.empty()){
//Update graphic cache if empty.
if(!cacheTex){
cacheTex=textureFromText(renderer,*fontText,caption.c_str(),objThemes.getTextColor(true));
}
//Cache the render color
Uint8 r,g,b,a;
SDL_GetRenderDrawColor(&renderer, &r,&g,&b,&a);
//Set it to black for drawing the caret.
SDL_SetRenderDrawColor(&renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
if(draw){
const SDL_Rect srcRect={0,0,width-2,25};
SDL_Rect dstRect = rectFromTexture(x+2,
y+(height-textureHeight(*cacheTex))/2,
*cacheTex);
dstRect.w = std::min(dstRect.w, width - 4);
SDL_RenderCopy(&renderer, cacheTex.get(), &srcRect, &dstRect);
//Only draw the caret when focus.
if(state==2){
SDL_Rect caretRect{x, y+4, 2, height-8};
int advance;
for(int n=0;n<value;n++){
TTF_GlyphMetrics(fontText,caption[n],NULL,NULL,NULL,NULL,&advance);
caretRect.x+=advance;
}
//Make sure that the carrot is inside the textbox.
if(caretRect.x<x+width) {
//Draw a box representing the caret.
SDL_RenderFillRect(&renderer, &caretRect);
}
}
}else{
//Only draw the caret when focus.
if(state==2&&draw){
const SDL_Rect caretRect{x+4, y+4, 2, height-8};
SDL_RenderFillRect(&renderer, &caretRect);
}
}
//Reset the render color.
SDL_SetRenderDrawColor(&renderer, r, g, b, a);
}
}
void GUISpinBox::update(){
//Read number from the caption string.
float number=(float)atof(caption.c_str());
//Stay in the limits.
if(number>limitMax){
number=limitMax;
}else if(number<limitMin){
number=limitMin;
}
//Write the number to the caption string.
char str[32];
sprintf(str,format,number);
caption=str;
}
void GUISpinBox::updateValue(bool positive){
//Read number from the caption string.
float number=(float)atof(caption.c_str());
//Apply change.
if(positive)
number+=change;
else
number-=change;
//Stay in the limits.
if(number>limitMax){
number=limitMax;
}else if(number<limitMin){
number=limitMin;
}
//Write the number to the caption string.
char str[32];
sprintf(str,format,number);
caption=str;
// restrict the caret position
value = clamp(value, 0, caption.length());
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, May 15, 10:52 PM (3 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63983
Default Alt Text
(54 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline