Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
80 KB
Referenced Files
None
Subscribers
None
diff --git a/src/GUIObject.cpp b/src/GUIObject.cpp
index 1854ec1..98d88f0 100644
--- a/src/GUIObject.cpp
+++ b/src/GUIObject.cpp
@@ -1,987 +1,1013 @@
/*
* 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 "GUIObject.h"
#include <iostream>
#include <list>
using namespace std;
//Set the GUIObjectRoot to NULL.
GUIObject* GUIObjectRoot=NULL;
//Initialise the event queue.
list<GUIEvent> GUIEventQueue;
void GUIObjectHandleEvents(bool kill){
//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();
//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(e.name,e.obj,e.eventType);
}
}
//We empty the event queue just to be sure.
GUIEventQueue.clear();
}
GUIObject::~GUIObject(){
//The cache is used as the actual image for GUIObjectImage and shouldn't be freed.
if(cache){
SDL_FreeSurface(cache);
cache=NULL;
}
//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(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(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(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(x,y,draw);
}
}
//////////////GUIButton///////////////////////////////////////////////////////////////////
bool GUIButton::handleEvents(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){
//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(x,y,enabled,visible,b);
//The event is processed when either our or the childs is true (or both).
b=b||b1;
}
return b;
}
//Found on gmane.comp.lib.sdl mailing list, see: http://comments.gmane.org/gmane.comp.lib.sdl/33664
//Original code by "Patricia Curtis" and later modified by "Jason"
static void SetSurfaceTrans(SDL_Surface* Src,double PercentTrans){
Uint8 Sbpp = Src->format->BytesPerPixel;
Uint8 *Sbits;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
int amask = 0x000000ff;
int cmask = 0xffffff00;
#else
int amask = 0xff000000;
int cmask = 0x00ffffff;
int Shift = 24;
#endif
int x,y;
- Uint32 Pixels;
+ Uint32 Pixels;
Uint32 Alpha;
for(y=0;y<Src->h;y++)
{
for(x=0;x<Src->w;x++)
{
Sbits = ((Uint8 *)Src->pixels+(y*Src->pitch)+(x*Sbpp));
Pixels = *((Uint32 *)(Sbits));
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Alpha = Pixels & mask;
#else
Alpha = (Pixels&amask)>>Shift;
#endif
Alpha*=PercentTrans;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
*((Uint32 *)(Sbits)) = (Pixels & cmask)|Alpha;
#else
*((Uint32 *)(Sbits)) = (Pixels & cmask)|(Alpha<<Shift);
#endif
}
}
}
void GUIButton::render(int x,int y,bool draw){
//There's no need drawing the widget when it's invisible.
if(!visible)
return;
//Rectangle the size of the widget.
SDL_Rect r;
//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.
SDL_FreeSurface(cache);
cache=NULL;
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
//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(!cache){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
if(!smallFont)
cache=TTF_RenderUTF8_Blended(fontGUI,lp,color);
else
cache=TTF_RenderUTF8_Blended(fontGUISmall,lp,color);
- //Make the widget transparent if it's disabled.
+ //Make the widget transparent if it's disabled.
if(!enabled)
SetSurfaceTrans(cache,0.5);
//Calculate proper size for the widget.
if(width<=0){
width=cache->w+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.
r.x=x-gravityX+(width-cache->w)/2;
r.y=y+(height-cache->h)/2-GUI_FONT_RAISE;
//Check if the arrows don't fall of.
if(cache->w+32<=width){
//Create a rectangle that selects the right image from bmGUI.
SDL_Rect r2={64,0,16,16};
if(state==1){
if(inDialog){
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w+5,y+2,arrowLeft2,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-25,y+2,arrowRight2,screen,NULL);
}else{
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w+5,y+2,arrowLeft1,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-25,y+2,arrowRight1,screen,NULL);
}
}else if(state==2){
if(inDialog){
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w,y+2,arrowLeft2,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-20,y+2,arrowRight2,screen,NULL);
}else{
applySurface(x-gravityX+(width-cache->w)/2+4+cache->w,y+2,arrowLeft1,screen,NULL);
applySurface(x-gravityX+(width-cache->w)/2-20,y+2,arrowRight1,screen,NULL);
}
}
}
//Draw the text and free the surface.
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
}
//////////////GUICheckBox///////////////////////////////////////////////////////////////////
bool GUICheckBox::handleEvents(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);
}
//Event has been processed.
b=true;
}
}
}
return b;
}
void GUICheckBox::render(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.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
SDL_FreeSurface(cache);
cache=NULL;
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
//Rectangle the size of the widget.
SDL_Rect r;
r.x=x;
r.y=y;
r.w=width;
r.h=height;
//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(!cache){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
cache=TTF_RenderUTF8_Blended(fontText,lp,color);
}
if(draw){
//Calculate the location, center it vertically.
r.x=x;
r.y=y+(height - cache->h)/2;
//Draw the text and free the surface.
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
if(draw){
//Draw the check (or not).
SDL_Rect r1={0,0,16,16};
if(value==1||value==2)
r1.x=value*16;
r.x=x+width-20;
r.y=y+(height-16)/2;
SDL_BlitSurface(bmGUI,&r1,screen,&r);
}
}
//////////////GUILabel///////////////////////////////////////////////////////////////////
bool GUILabel::handleEvents(int x,int y,bool enabled,bool visible,bool processed){
return processed;
}
void GUILabel::render(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.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
SDL_FreeSurface(cache);
cache=NULL;
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
//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(cache==NULL){
SDL_Color color;
if(inDialog)
color=themeTextColorDialog;
else
color=themeTextColor;
cache=TTF_RenderUTF8_Blended(fontText,lp,color);
if(width<=0)
width=cache->w;
}
//Align the text properly and draw it.
if(draw){
if(gravity==GUIGravityCenter)
gravityX=(width-cache->w)/2;
else if(gravity==GUIGravityRight)
gravityX=width-cache->w;
else
gravityX=0;
r.y=y+(height - cache->h)/2;
r.x+=gravityX;
SDL_BlitSurface(cache,NULL,screen,&r);
}
}
}
//////////////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;
+ TTF_GlyphMetrics(fontText,caption[highlightEnd-1],NULL,NULL,NULL,NULL,&advance);
+ highlightEndX=highlightStartX=highlightEndX-advance;
+
+ highlightEnd=highlightStart=highlightEnd-1;
+ caption.erase((size_t)highlightEnd,1);
+ }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){
+ caption.erase((size_t)highlightEnd,1);
+
+ 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){
+ highlightEnd--;
+ int advance;
+ TTF_GlyphMetrics(fontText,caption.at(highlightEnd),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;
+ TTF_GlyphMetrics(fontText,caption.at(highlightEnd),NULL,NULL,NULL,NULL,&advance);
+ if(SDL_GetModState() & KMOD_SHIFT){
+ highlightEndX+=advance;
+ highlightEnd++;
+ }else{
+ highlightStartX=highlightEndX=highlightEndX+advance;
+ highlightEnd=highlightStart=highlightEnd+1;
+ }
+ }else{
+ if((SDL_GetModState() & KMOD_SHIFT)==0){
+ highlightStart=highlightEnd;
+ highlightStartX=highlightEndX;
+ }
+ }
+ tick=15;
+}
+
bool GUITextBox::handleEvents(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.
int key=(int)event.key.keysym.unicode;
//Check if the key is supported.
if(key>=32&&key<=126){
if(highlightStart==highlightEnd){
caption.insert((size_t)highlightStart,1,char(key));
highlightStart++;
highlightEnd=highlightStart;
}else if(highlightStart<highlightEnd){
caption.erase(highlightStart,highlightEnd-highlightStart);
caption.insert((size_t)highlightStart,1,char(key));
highlightStart++;
highlightEnd=highlightStart;
+ highlightEndX=highlightStartX;
}else{
caption.erase(highlightEnd,highlightStart-highlightEnd);
caption.insert((size_t)highlightEnd,1,char(key));
highlightEnd++;
highlightStart=highlightEnd;
+ highlightStartX=highlightEndX;
}
-
+ int advance;
+ TTF_GlyphMetrics(fontText,char(key),NULL,NULL,NULL,NULL,&advance);
+ 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);
}
}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){
- if(highlightStart==highlightEnd&&highlightStart>0){
- highlightEnd=highlightStart=clamp(highlightEnd-1,0,caption.length());
- caption.erase((size_t)highlightEnd,1);
- }else if(highlightStart<highlightEnd){
- caption.erase(highlightStart,highlightEnd-highlightStart);
- highlightEnd=highlightStart;
- }else{
- caption.erase(highlightEnd,highlightStart-highlightEnd);
- highlightStart=highlightEnd;
- }
-
- this->key=SDLK_BACKSPACE;
- keyHoldTime=0;
- keyTime=5;
-
- //If there is an event callback then call it.
- if(eventCallback){
- GUIEvent e={eventCallback,name,this,GUIEventChange};
- GUIEventQueue.push_back(e);
- }
- }
+ //Set the key values correctly.
+ this->key=SDLK_BACKSPACE;
+ keyHoldTime=0;
+ keyTime=5;
+
+ //Delete one character direct to prevent a lag.
+ backspaceChar();
}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){
- if(highlightStart==highlightEnd){
- highlightEnd=highlightStart=clamp(highlightEnd,0,caption.length());
- caption.erase((size_t)highlightEnd,1);
- }else if(highlightStart<highlightEnd){
- caption.erase(highlightStart,highlightEnd-highlightStart);
- highlightEnd=highlightStart;
- }else{
- caption.erase(highlightEnd,highlightStart-highlightEnd);
- highlightStart=highlightEnd;
- }
-
- this->key=SDLK_DELETE;
- keyHoldTime=0;
- keyTime=5;
-
- //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){
- highlightEnd=highlightStart=clamp(highlightEnd+1,0,caption.length());
+ //Set the key values correctly.
+ this->key=SDLK_DELETE;
+ keyHoldTime=0;
+ keyTime=5;
+ //Delete one character direct to prevent a lag.
+ deleteChar();
+ }else if(event.key.keysym.sym==SDLK_RIGHT){
+ //Set the key values correctly.
this->key=SDLK_RIGHT;
keyHoldTime=0;
keyTime=5;
- }else if(event.key.keysym.sym==SDLK_LEFT){
- highlightEnd=highlightStart=clamp(highlightEnd-1,0,caption.length());
+ //Move directly to prevent a lag.
+ moveCarrotRight();
+ }else if(event.key.keysym.sym==SDLK_LEFT){
+ //Set the key values correctly.
this->key=SDLK_LEFT;
keyHoldTime=0;
keyTime=5;
- }
+
+ //Move directly to prevent a lag.
+ moveCarrotLeft();
+ }
//The event has been processed.
b=true;
}else if(state==2 && event.type==SDL_KEYUP && !b){
//Check if released key is the same as the holded key.
if(event.key.keysym.sym==key){
//It is so stop the key.
- key=-1;
+ key=-1;
}
}
//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 click=i-x-2;
- int clickPos=0;
+ int clickX=i-x-2;
+
+ int finalPos=0;
+ int finalX=0;
if(cache&&!caption.empty()){
- clickPos=caption.length();
- unsigned int wid=0;
+ finalPos=caption.length();
for(unsigned int i=0;i<caption.length();i++){
int advance;
TTF_GlyphMetrics(fontText,caption[i],NULL,NULL,NULL,NULL,&advance);
- wid+=advance;
+ finalX+=advance;
- if(click<(int)wid-(int)advance/2){
- clickPos=i;
+ if(clickX<finalX-advance/2){
+ finalPos=i;
+ finalX-=advance;
break;
}
}
}
if(event.type==SDL_MOUSEBUTTONUP){
state=2;
- highlightEnd=clickPos;
+ highlightEnd=finalPos;
+ highlightEndX=finalX;
}else if(event.type==SDL_MOUSEBUTTONDOWN){
state=2;
- highlightStart=clickPos;
- highlightEnd=clickPos;
+ highlightStart=highlightEnd=finalPos;
+ highlightStartX=highlightEndX=finalX;
}else if(event.type==SDL_MOUSEMOTION&&(k&SDL_BUTTON(1))){
state=2;
- highlightEnd=clickPos;
+ 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(int x,int y,bool draw){
+void GUITextBox::render(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.
if(enabled!=cachedEnabled || caption.compare(cachedCaption)!=0 || width<=0){
//Free the cache.
SDL_FreeSurface(cache);
cache=NULL;
//And cache the new values.
cachedEnabled=enabled;
cachedCaption=caption;
//Finally resize the widget
if(autoWidth)
width=-1;
}
//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_BACKSPACE:
- {
- if(caption.length()>0){
- if(highlightStart==highlightEnd&&highlightStart>0){
- highlightEnd=highlightStart=clamp(highlightEnd-1,0,caption.length());
- caption.erase((size_t)highlightEnd,1);
- }else if(highlightStart<highlightEnd){
- caption.erase(highlightStart,highlightEnd-highlightStart);
- highlightEnd=highlightStart;
- }else{
- caption.erase(highlightEnd,highlightStart-highlightEnd);
- highlightStart=highlightEnd;
- }
- }
+ backspaceChar();
break;
- }
case SDLK_DELETE:
- {
- if(caption.length()>0){
- if(highlightStart==highlightEnd){
- highlightEnd=highlightStart=clamp(highlightEnd,0,caption.length());
- caption.erase((size_t)highlightEnd,1);
- }else if(highlightStart<highlightEnd){
- caption.erase(highlightStart,highlightEnd-highlightStart);
- highlightEnd=highlightStart;
- }else{
- caption.erase(highlightEnd,highlightStart-highlightEnd);
- highlightStart=highlightEnd;
- }
- }
+ deleteChar();
break;
- }
case SDLK_LEFT:
- {
- highlightEnd=highlightStart=clamp(highlightEnd-1,0,caption.length());
- tick=15;
+ moveCarrotLeft();
break;
- }
case SDLK_RIGHT:
- {
- highlightEnd=highlightStart=clamp(highlightEnd+1,0,caption.length());
- tick=15;
+ moveCarrotRight();
break;
- }
}
}
}
if(draw){
//Default background opacity
int clr=50;
//If hovering or focused make background more visible.
- if(state==1)
+ if(state==1)
clr=128;
else if (state==2)
clr=100;
//Draw the box.
Uint32 color=0xFFFFFF00|clr;
drawGUIBox(x,y,width,height,screen,color);
}
//Rectangle used for drawing.
SDL_Rect r;
//Get the text and make sure it isn't empty.
const char* lp=caption.c_str();
if(lp!=NULL && lp[0]){
if(!cache){
//Draw the black text.
SDL_Color black={0,0,0,0};
cache=TTF_RenderUTF8_Blended(fontText,lp,black);
}
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;
- r.w=0;
- int advance;
- int carrotX=2;
-
- //Find out the highlighted area.
- //NOTE: Start and end positions can be in any order so we have different code for both options.
- if(highlightStart>highlightEnd){
- for(int n=0;n<highlightStart;n++){
- TTF_GlyphMetrics(fontText,caption[n],NULL,NULL,NULL,NULL,&advance);
- if(n<highlightEnd){
- r.x+=advance;
- carrotX+=advance;
- }else{
- r.w+=advance;
- }
- }
+ if(highlightStart<highlightEnd){
+ r.x+=highlightStartX;
+ r.w=highlightEndX-highlightStartX;
}else{
- for(int n=0;n<highlightEnd;n++){
- TTF_GlyphMetrics(fontText,caption[n],NULL,NULL,NULL,NULL,&advance);
- if(n<highlightStart){
- r.x+=advance;
- }else{
- r.w+=advance;
- }
- carrotX+=advance;
- }
+ r.x+=highlightEndX;
+ r.w=highlightStartX-highlightEndX;
}
- //Draw the highlighted area.
+ //Draw the area.
SDL_FillRect(screen,&r,SDL_MapRGB(screen->format,128,128,128));
//Ticking carrot.
if(tick<16){
//Show carrot: 15->0.
- r.x=x+carrotX;
+ 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));
//Reset: 32 or count down.
if(tick<=0)
tick=32;
else
tick--;
}else{
//Hide carrot: 32->16.
tick--;
}
}
//Calculate the location, center it vertically.
r.x=x+4;
r.y=y+(height - cache->h)/2;
//Draw the text.
SDL_Rect tmp={0,0,width-2,25};
SDL_BlitSurface(cache,&tmp,screen,&r);
}
}else{
//Only draw the carrot when focus.
if(state==2&&draw){
r.x=x+4;
r.y=y+4;
r.w=2;
r.h=height-8;
SDL_FillRect(screen,&r,0);
}
}
}
//////////////GUIFrame///////////////////////////////////////////////////////////////////
bool GUIFrame::handleEvents(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(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(int x,int y,bool draw){
+void GUIFrame::render(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.
SDL_FreeSurface(cache);
cache=NULL;
//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,screen,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(!cache)
cache=TTF_RenderUTF8_Blended(fontGUI,lp,themeTextColorDialog);
//Draw the text.
if(draw)
applySurface(x+(width-cache->w)/2,y+6-GUI_FONT_RAISE,cache,screen,NULL);
}
//We now need to draw all the children.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(x,y,draw);
}
}
//////////////GUIImage///////////////////////////////////////////////////////////////////
GUIImage::~GUIImage(){
//Check if the surface is managed, if so free it.
if(managed)
SDL_FreeSurface(image);
}
bool GUIImage::handleEvents(int x,int y,bool enabled,bool visible,bool processed){
return processed;
}
void GUIImage::fitToImage(){
//Increase or decrease the width and height to fully show the image.
if(clip.w!=0)
width=clip.w;
else
width=image->w;
if(clip.h!=0)
height=clip.h;
else
height=image->h;
}
void GUIImage::render(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;
//Create a clip rectangle.
SDL_Rect r;
//The width and height are capped by the GUIImage itself.
r=clip;
if(r.w>width || r.w==0)
r.w=width;
if(r.h>height || r.h==0)
r.h=height;
//Make sure the image isn't null.
if(image)
applySurface(x,y,image,screen,&r);
}
diff --git a/src/GUIObject.h b/src/GUIObject.h
index 9114e62..1ea69f6 100644
--- a/src/GUIObject.h
+++ b/src/GUIObject.h
@@ -1,385 +1,396 @@
/*
* 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/>.
*/
#ifndef GUIOBJECT_H
#define GUIOBJECT_H
#include "Globals.h"
#include "Functions.h"
#include "FileManager.h"
#include <string>
#include <vector>
#include <list>
//Widget gravity properties
const int GUIGravityLeft=0;
const int GUIGravityCenter=1;
const int GUIGravityRight=2;
//The event id's.
//A click event used for e.g. buttons.
const int GUIEventClick=0;
//A change event used for e.g. textboxes.
const int GUIEventChange=1;
class GUIObject;
//Class that is used as event callback.
class GUIEventCallback{
public:
//This method is called when an event is fired.
//name: The name of the event.
//obj: Pointer to the GUIObject which caused this event.
//eventType: The type of event as defined above.
virtual void GUIEventCallback_OnEvent(std::string name,GUIObject* obj,int eventType)=0;
};
//Class containing the
class GUIObject{
public:
//The relative x location of the GUIObject.
int left;
//The relative y location of the GUIObject.
int top;
//The width of the GUIObject.
int width;
//The height of the GUIObject.
int height;
//The type of the GUIObject.
int type;
//The value of the GUIObject.
//It depends on the type of GUIObject what it means.
int value;
//The name of the GUIObject.
std::string name;
//The caption of the GUIObject.
//It depends on the type of GUIObject what it is.
std::string caption;
//Boolean if the GUIObject is enabled.
bool enabled;
//Boolean if the GUIObject is visible.
bool visible;
//Vector containing the children of the GUIObject.
std::vector<GUIObject*> childControls;
//Event callback used to invoke events.
GUIEventCallback* eventCallback;
//Widget's gravity to centering
int gravity;
int gravityX;
bool autoWidth;
//Is the parent widget a dialog?
bool inDialog;
protected:
//The state of the GUIObject.
//It depends on the type of GUIObject where it's used for.
int state;
//Surface containing some gui images.
SDL_Surface* bmGUI;
//Surface that can be used to cache rendered text.
SDL_Surface* cache;
//String containing the old caption to detect if it changed.
std::string cachedCaption;
//Boolean containing the previous enabled state.
bool cachedEnabled;
public:
//Constructor.
//left: The relative x location of the GUIObject.
//top: The relative y location of the GUIObject.
//witdh: The width of the GUIObject.
//height: The height of the GUIObject.
//caption: The text on the GUIObject.
//value: The value of the GUIObject.
//enabled: Boolean if the GUIObject is enabled or not.
//visible: Boolean if the GUIObject is visisble or not.
//gravity: The way the GUIObject needs to be aligned.
GUIObject(int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
left(left),top(top),width(width),height(height),
gravity(gravity),value(value),
enabled(enabled),visible(visible),
eventCallback(NULL),state(0),
cache(NULL),cachedEnabled(enabled),gravityX(0)
{
//Make sure that caption isn't NULL before setting it.
if(caption){
GUIObject::caption=caption;
//And set the cached caption.
cachedCaption=caption;
}
if(width<=0)
autoWidth=true;
else
autoWidth=false;
inDialog=false;
//Load the gui images.
bmGUI=loadImage(getDataPath()+"gfx/gui.png");
}
//Destructor.
virtual ~GUIObject();
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIObject.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Draw widget or just update it without drawing
virtual void render(int x=0,int y=0,bool draw=true);
void addChild(GUIObject* obj){
//Add widget add a child
childControls.push_back(obj);
//Copy inDialog boolean from parent.
obj->inDialog=inDialog;
}
//Method for getting a child from a GUIObject.
//NOTE: This method doesn't search recursively.
//name: The name of the child to return.
//Returns: Pointer to the requested child, NULL otherwise.
GUIObject* getChild(std::string name){
//Look for a child with the name.
for(unsigned int i=0;i<childControls.size();i++)
if(childControls[i]->name==name)
return childControls[i];
//Not found so return NULL.
return NULL;
}
};
//Method used to handle the GUIEvents from the GUIEventQueue.
//kill: Boolean if an SDL_QUIT event may kill the GUIObjectRoot.
void GUIObjectHandleEvents(bool kill=false);
//A structure containing the needed variables to call an event.
struct GUIEvent{
//Event callback used to invoke the event.
GUIEventCallback* eventCallback;
//The name of the event.
std::string name;
//Pointer to the object which invoked the event.
GUIObject* obj;
//The type of event.
int eventType;
};
//List used to queue the gui events.
extern std::list<GUIEvent> GUIEventQueue;
class GUIButton:public GUIObject{
public:
GUIButton(int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(left,top,width,height,caption,value,enabled,visible,gravity),
smallFont(false){ };
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(int x=0,int y=0,bool draw=true);
//Boolean if small font is used.
bool smallFont;
};
class GUICheckBox:public GUIObject{
public:
GUICheckBox(int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(left,top,width,height,caption,value,enabled,visible,gravity){ };
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(int x=0,int y=0,bool draw=true);
};
class GUILabel:public GUIObject{
public:
GUILabel(int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(left,top,width,height,caption,value,enabled,visible,gravity){ };
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(int x=0,int y=0,bool draw=true);
};
class GUITextBox:public GUIObject{
public:
GUITextBox(int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(left,top,width,height,caption,value,enabled,visible,gravity),
highlightStart(0),highlightEnd(0),tick(15),key(-1),keyHoldTime(0),keyTime(0){ };
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(int x=0,int y=0,bool draw=true);
private:
//Text highlights.
int highlightStart;
int highlightEnd;
+ int highlightStartX;
+ int highlightEndX;
+
//Carrot ticking.
int tick;
//Integer containing the key that is holded.
int key;
//Integer containing the time the key is pressed.
int keyHoldTime;
//The time it takes to invoke the key action again.
int keyTime;
+
+ //Functions for modifying the text.
+ void backspaceChar();
+ void deleteChar();
+
+ //Functions for moving the carrot.
+ void moveCarrotLeft();
+ void moveCarrotRight();
};
class GUIFrame:public GUIObject{
public:
GUIFrame(int left=0,int top=0,int width=0,int height=0,
const char* caption=NULL,int value=0,
bool enabled=true,bool visible=true,int gravity=0):
GUIObject(left,top,width,height,caption,value,enabled,visible,gravity){
inDialog=true;
};
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(int x=0,int y=0,bool draw=true);
};
//A GUIObject that holds an SDL_Surface for rendering.
//NOTE: The image is not freed by the GUIImage.
class GUIImage:public GUIObject{
public:
GUIImage(int left=0,int top=0,int width=0,int height=0,
SDL_Surface* image=NULL,SDL_Rect clip=SDL_Rect(),bool managed=false,
bool enabled=true,bool visible=true):
GUIObject(left,top,width,height,NULL,0,enabled,visible,0),
image(image),clip(clip),managed(managed){ };
//Destructor.
~GUIImage();
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUIScrollBar.
//x: The x location to draw the GUIObject. (x+left)
//y: The y location to draw the GUIObject. (y+top)
//draw: Whether displey the widget or not.
virtual void render(int x=0,int y=0,bool draw=true);
//Method that will change the dimensions of the GUIImage so that the full image is shown.
//OR in case of a clip rect that the selected section of the image is shown.
void fitToImage();
//Method for setting the image of the widget.
//image: SDL_Surface containing the image.
void setImage(SDL_Surface* surface){
image=surface;
}
//Method for setting the clip rectangle for the GUIImager.
//rect: The new clip rectangle.
void setClipRect(SDL_Rect rect){
clip=rect;
}
private:
//Boolean if the image should be managed by the GUIImage.
//If set to true the image's surface will be freed upon deletion.
bool managed;
//Pointer to the SDL_Surface to draw.
SDL_Surface* image;
//Optional rectangle for defining the section of the surface that should be drawn.
//NOTE: This doesn't have to correspond with the dimensions of the GUIObject.
SDL_Rect clip;
};
#endif
diff --git a/src/GUITextArea.cpp b/src/GUITextArea.cpp
index 20952e7..857f364 100644
--- a/src/GUITextArea.cpp
+++ b/src/GUITextArea.cpp
@@ -1,744 +1,897 @@
/*
* 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 "GUITextArea.h"
#include <cmath>
+#include <ctype.h>
using namespace std;
GUITextArea::GUITextArea(int left,int top,int width,int height,bool enabled,bool visible):
GUIObject(left,top,width,height,NULL,-1,enabled,visible),editable(true){
key=-1;
keyHoldTime=keyTime=0;
//Set some default values.
- state=value=currentLine=0;
+ state=0;
setFont(fontText);
+
+ highlightLineStart=highlightLineEnd=0;
+ highlightStartX=highlightEndX=0;
+ highlightStart=highlightEnd=0;
//Add empty text.
lines.push_back("");
linesCache.push_back(NULL);
//Create scrollbar widget.
scrollBar=new GUIScrollBar(width-16,0,16,height,1,0,0,0);
childControls.push_back(scrollBar);
scrollBarH=new GUIScrollBar(0,height-16,width-16,16,0,0,0,0,100,500,true,false);
childControls.push_back(scrollBarH);
}
GUITextArea::~GUITextArea(){
//Free cached images.
for(unsigned int i=0;i<linesCache.size();i++){
SDL_FreeSurface(linesCache[i]);
}
linesCache.clear();
}
void GUITextArea::setFont(TTF_Font* font){
//NOTE: This fuction shouldn't be called after adding items, so no need to update the whole cache.
widgetFont=font;
fontHeight=TTF_FontHeight(font)+1;
}
bool GUITextArea::handleEvents(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;
y+=top;
//Update the vertical scrollbar.
b=b||scrollBar->handleEvents(x,y,enabled,visible,b);
if(!editable)
- currentLine=scrollBar->value;
+ highlightLineStart=scrollBar->value;
- //NOTE: We don't reset the state to have a "focus" effect.
+ //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 && editable){
//Get the keycode.
int key=(int)event.key.keysym.unicode;
-
+
//Check if the key is supported.
if(key>=32&&key<=126){
- //Add the key to the string.
- string* str=&lines.at(currentLine);
- str->insert((size_t)value,1,char(key));
- value++;
-
+ removeHighlight();
+ string* str=&lines.at(highlightLineStart);
+ str->insert((size_t)highlightEnd,1,char(key));
+ highlightEnd++;
+ highlightStart=highlightEnd;
+ int advance;
+ TTF_GlyphMetrics(widgetFont,char(key),NULL,NULL,NULL,NULL,&advance);
+ highlightStartX=highlightEndX=highlightStartX+advance;
+
//Update cache.
- SDL_Surface** c=&linesCache.at(currentLine);
+ SDL_Surface** c=&linesCache.at(highlightLineStart);
if(*c) SDL_FreeSurface(*c);
SDL_Color black={0,0,0,0};
*c=TTF_RenderUTF8_Blended(widgetFont,str->c_str(),black);
-
+
//Update view if needed.
adjustView();
-
+
//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){
//Set the key values correct.
this->key=SDLK_BACKSPACE;
keyHoldTime=0;
keyTime=5;
-
+
//Delete one character direct to prevent a lag.
backspaceChar();
}else if(event.key.keysym.sym==SDLK_DELETE){
//Set the key values correct.
this->key=SDLK_DELETE;
keyHoldTime=0;
keyTime=5;
-
+
//Delete one character direct to prevent a lag.
deleteChar();
- }else if(event.key.keysym.sym==SDLK_RETURN){
+ }else if(event.key.keysym.sym==SDLK_RETURN){
+ removeHighlight();
//Split the current line and update.
- string str2=lines.at(currentLine).substr(value);
- lines.at(currentLine)=lines.at(currentLine).substr(0,value);
-
- SDL_Surface** c=&linesCache.at(currentLine);
+ string str2=lines.at(highlightLineEnd).substr(highlightStart);
+ lines.at(highlightLineStart)=lines.at(highlightLineStart).substr(0,highlightStart);
+
+ SDL_Surface** c=&linesCache.at(highlightLineStart);
if(*c) SDL_FreeSurface(*c);
SDL_Color black={0,0,0,0};
- *c=TTF_RenderUTF8_Blended(widgetFont,lines.at(currentLine).c_str(),black);
-
+ *c=TTF_RenderUTF8_Blended(widgetFont,lines.at(highlightLineStart).c_str(),black);
+
+ //Calculate indentation.
+ int indent=0;
+ for (int i=0; i<lines.at(highlightLineStart).length(); i++){
+ if (isspace(lines.at(highlightLineStart)[i]))
+ indent++;
+ else
+ break;
+ }
+ str2.insert(0,indent,' ');
+
//Add the rest in a new line.
- currentLine++;
- value=0;
- lines.insert(lines.begin()+currentLine,str2);
-
+ highlightLineStart++;
+ highlightStart=indent;
+ highlightEnd=highlightStart;
+ highlightLineEnd++;
+
+ highlightStartX=0;
+ for(int i=0; i<indent; i++){
+ int advance;
+ TTF_GlyphMetrics(widgetFont,str2.at(i),NULL,NULL,NULL,NULL,&advance);
+ highlightStartX+=advance;
+ }
+ highlightEndX=highlightStartX;
+
+ lines.insert(lines.begin()+highlightLineStart,str2);
+
SDL_Surface* c2;
c2=TTF_RenderUTF8_Blended(widgetFont,str2.c_str(),black);
- linesCache.insert(linesCache.begin()+currentLine,c2);
-
- //Adjust view.
+ linesCache.insert(linesCache.begin()+highlightLineStart,c2);
+
adjustView();
-
+
//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_TAB){
+ removeHighlight();
//Add a tabulator or here just 2 spaces to the string.
- string* str=&lines.at(currentLine);
- str->insert((size_t)value,2,char(' '));
- value+=2;
+ string* str=&lines.at(highlightLineStart);
+ str->insert((size_t)highlightStart,2,char(' '));
+
+ int advance;
+ TTF_GlyphMetrics(widgetFont,' ',NULL,NULL,NULL,NULL,&advance);
+ highlightStart+=2;
+ highlightStartX=advance*2;
+ highlightEnd=highlightStart;
+ highlightEndX=highlightStartX;
//Update cache.
- SDL_Surface** c=&linesCache.at(currentLine);
+ SDL_Surface** c=&linesCache.at(highlightLineStart);
if(*c) SDL_FreeSurface(*c);
SDL_Color black={0,0,0,0};
*c=TTF_RenderUTF8_Blended(widgetFont,str->c_str(),black);
- //Adjust view.
adjustView();
}else if(event.key.keysym.sym==SDLK_RIGHT){
//Set the key values correct.
this->key=SDLK_RIGHT;
keyHoldTime=0;
keyTime=5;
//Move the carrot once to prevent a lag.
moveCarrotRight();
}else if(event.key.keysym.sym==SDLK_LEFT){
//Set the key values correct.
this->key=SDLK_LEFT;
keyHoldTime=0;
keyTime=5;
//Move the carrot once to prevent a lag.
moveCarrotLeft();
}else if(event.key.keysym.sym==SDLK_DOWN){
//Set the key values correct.
this->key=SDLK_DOWN;
keyHoldTime=0;
keyTime=5;
//Move the carrot once to prevent a lag.
moveCarrotDown();
}else if(event.key.keysym.sym==SDLK_UP){
//Set the key values correct.
this->key=SDLK_UP;
keyHoldTime=0;
keyTime=5;
//Move the carrot once to prevent a lag.
moveCarrotUp();
}
//The event has been processed.
b=true;
}else if(state==2 && event.type==SDL_KEYUP && !b){
//Check if released key is the same as the holded key.
if(event.key.keysym.sym==key){
//It is so stop the key.
key=-1;
}
}
//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;
}
//Check for mouse wheel scrolling.
//Scroll horizontally if mouse is over the horizontal scrollbar.
//Otherwise scroll vertically.
if(j>=y+height-16&&scrollBarH->visible){
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN){
scrollBarH->value+=20;
if(scrollBarH->value>scrollBarH->maxValue)
scrollBarH->value=scrollBarH->maxValue;
}else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP){
scrollBarH->value-=20;
if(scrollBarH->value<0)
scrollBarH->value=0;
}
}else{
if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELDOWN){
scrollBar->value++;
if(scrollBar->value>scrollBar->maxValue)
scrollBar->value=scrollBar->maxValue;
}else if(event.type==SDL_MOUSEBUTTONDOWN && event.button.button==SDL_BUTTON_WHEELUP){
scrollBar->value--;
if(scrollBar->value<0)
scrollBar->value=0;
}
}
//When mouse is not over the scrollbar.
if(i<x+width-16&&j<(scrollBarH->visible?y+height-16:y+height)&&editable){
//Update the cursor type.
currentCursor=CURSOR_CARROT;
- //Check for a mouse button press.
- if(k&SDL_BUTTON(1)){
- //We have focus.
- state=2;
-
- //Move carrot to the place clicked.
- currentLine=clamp((int)floor(float(j-y)/float(fontHeight))+scrollBar->value,0,lines.size()-1);
- string* str=&lines.at(currentLine);
- value=str->length();
-
- int clickX=i-x+scrollBarH->value;
- int xPos=0;
+ //Move carrot to the place clicked.
+ int mouseLine=clamp((int)floor(float(j-y)/float(fontHeight))+scrollBar->value,0,lines.size()-1);
+ string* str=&lines.at(mouseLine);
+ value=str->length();
+
+ int clickX=i-x+scrollBarH->value;
+ int finalX=0;
+ int finalPos=str->length();
+
+ for(unsigned int i=0;i<str->length();i++){
+ int advance;
+ TTF_GlyphMetrics(widgetFont,str->at(i),NULL,NULL,NULL,NULL,&advance);
+ finalX+=advance;
- for(unsigned int i=0;i<str->length();i++){
- int advance;
- TTF_GlyphMetrics(widgetFont,str->at(i),NULL,NULL,NULL,NULL,&advance);
- xPos+=advance;
-
- if(clickX<xPos-advance/2){
- value=i;
- break;
- }
+ if(clickX<finalX-advance/2){
+ finalPos=i;
+ finalX-=advance;
+ break;
}
}
+ //if(k&SDL_BUTTON(1)){
+ if(event.type==SDL_MOUSEBUTTONUP){
+ state=2;
+ highlightEnd=finalPos;
+ highlightEndX=finalX;
+ highlightLineEnd=mouseLine;
+ }else if(event.type==SDL_MOUSEBUTTONDOWN){
+ state=2;
+ highlightStart=highlightEnd=finalPos;
+ highlightStartX=highlightEndX=finalX;
+ highlightLineStart=mouseLine;
+ }else if(event.type==SDL_MOUSEMOTION&&(k&SDL_BUTTON(1))){
+ state=2;
+ highlightEnd=finalPos;
+ highlightEndX=finalX;
+ highlightLineEnd=mouseLine;
+ }
+ //}
}
}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;
}
}
}
//Process child controls event except for the scrollbar.
//That's why i starts at one.
for(unsigned int i=1;i<childControls.size();i++){
bool b1=childControls[i]->handleEvents(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 GUITextArea::deleteChar(){
- //Remove a character after the carrot.
- if(value<(int)lines.at(currentLine).length()){
- //Normal delete inside of a line.
- //Update string.
- string* str=&lines.at(currentLine);
- str->erase((size_t)value,1);
-
- //Update cache.
- SDL_Surface** c=&linesCache.at(currentLine);
+void GUITextArea::removeHighlight(){
+ if (highlightLineStart==highlightLineEnd) {
+ int start=highlightStart, end=highlightEnd, startx=highlightStartX;
+ if(highlightStart>highlightEnd){
+ start=highlightEnd;
+ end=highlightStart;
+ startx=highlightEndX;
+ }
+ string *str=&lines.at(highlightLineStart);
+ str->erase(start,end-start);
+
+ highlightStart=highlightEnd=start;
+ highlightStartX=highlightEndX=startx;
+
+ // Update cache.
+ SDL_Surface** c=&linesCache.at(highlightLineStart);
if(*c) SDL_FreeSurface(*c);
SDL_Color black={0,0,0,0};
*c=TTF_RenderUTF8_Blended(widgetFont,str->c_str(),black);
}else{
- //Make sure there's a line after currentLine.
- if(currentLine<(int)lines.size()-1){
- //Append next line.
- string* str=&lines.at(currentLine);
- str->append(lines.at(currentLine+1));
-
- //Remove the unused line.
- SDL_Surface** c=&linesCache.at(currentLine+1);
- if(*c) SDL_FreeSurface(*c);
- lines.erase(lines.begin()+currentLine+1);
- linesCache.erase(linesCache.begin()+currentLine+1);
-
- //Update cache.
- c=&linesCache.at(currentLine);
- if(*c) SDL_FreeSurface(*c);
- SDL_Color black={0,0,0,0};
- *c=TTF_RenderUTF8_Blended(widgetFont,str->c_str(),black);
+ int startLine=highlightLineStart, endLine=highlightLineEnd,
+ start=highlightStart, end=highlightEnd, startx=highlightStartX;
+ if(startLine>endLine){
+ startLine=highlightLineEnd;
+ endLine=highlightLineStart;
+ start=highlightEnd;
+ end=highlightStart;
+ startx=highlightEndX;
+ }
+ string *str=&lines.at(startLine);
+
+ str->erase(start,str->length()-start);
+
+ if(endLine-startLine>=2){
+ for(int i=startLine+1; i < endLine; i++){
+ SDL_Surface** c=&linesCache.at(i);
+ if(*c) SDL_FreeSurface(*c);
+ lines.erase(lines.begin()+i);
+ linesCache.erase(linesCache.begin()+i);
+ endLine--;
+ i--;
+ }
}
+
+ string *str2=&lines.at(endLine);
+
+ str2->erase(0, end);
+ str->append(*str2);
+
+ SDL_Surface** c=&linesCache.at(endLine);
+ if(*c) SDL_FreeSurface(*c);
+ lines.erase(lines.begin()+endLine);
+ linesCache.erase(linesCache.begin()+endLine);
+
+ highlightLineStart=highlightLineEnd=startLine;
+ highlightStart=highlightEnd=start;
+ highlightStartX=highlightEndX=startx;
+
+ // Update cache.
+ c=&linesCache.at(startLine);
+ if(*c) SDL_FreeSurface(*c);
+ SDL_Color black={0,0,0,0};
+ *c=TTF_RenderUTF8_Blended(widgetFont,str->c_str(),black);
}
-
- //Adjust view.
adjustView();
-
+}
+
+void GUITextArea::deleteChar(){
+ if (highlightLineStart==highlightLineEnd && highlightStart==highlightEnd){
+ highlightEnd++;
+ if(highlightEnd>lines.at(highlightLineEnd).length()){
+ if(highlightLineEnd==lines.size()-1){
+ highlightEnd--;
+ }else{
+ highlightLineEnd++;
+ highlightEnd=0;
+ }
+ }
+ }
+ removeHighlight();
//If there is an event callback.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
void GUITextArea::backspaceChar(){
- //Remove a character before the carrot.
- value--;
- if(value<0){
- //Remove a line but append it's content to the previous.
- //However we can't do this to the first line.
- if(currentLine>0){
- //Remove line from display but store the string.
- string str=lines.at(currentLine);
- lines.erase(lines.begin()+currentLine);
- SDL_Surface** c=&linesCache.at(currentLine);
- if(*c) SDL_FreeSurface(*c);
- linesCache.erase(linesCache.begin()+currentLine);
-
- //Append that string to the previous line.
- currentLine--;
- string* str2=&lines.at(currentLine);
- value=str2->length();
- str2->append(str);
-
- //Update cache.
- c=&linesCache.at(currentLine);
- if(*c) SDL_FreeSurface(*c);
- SDL_Color black={0,0,0,0};
- *c=TTF_RenderUTF8_Blended(widgetFont,str2->c_str(),black);
+ if(highlightLineStart==highlightLineEnd && highlightStart==highlightEnd){
+ highlightStart--;
+ if(highlightStart<0){
+ if(highlightLineStart==0){
+ highlightStart=0;
+ }else{
+ highlightLineStart--;
+ highlightStart=lines.at(highlightLineStart).length();
+ highlightStartX=0;
+ SDL_Surface** c=&linesCache.at(highlightLineStart);
+ if (*c) highlightStartX=(*c)->w;
+ highlightEndX=highlightStartX;
+ }
}else{
- //Don't let the value become negative.
- value=0;
+ int advance;
+ TTF_GlyphMetrics(widgetFont,lines.at(highlightLineStart).at(highlightStart),NULL,NULL,NULL,NULL,&advance);
+ highlightStartX-=advance;
+ highlightEndX=highlightStartX;
}
- }else{
- //Normal delete inside of a line.
- //Update string.
- string* str=&lines.at(currentLine);
- str->erase((size_t)value,1);
-
- //Update cache.
- SDL_Surface** c=&linesCache.at(currentLine);
- if(*c) SDL_FreeSurface(*c);
- SDL_Color black={0,0,0,0};
- *c=TTF_RenderUTF8_Blended(widgetFont,str->c_str(),black);
}
-
- //Adjust view.
- adjustView();
-
+ removeHighlight();
+
//If there is an event callback.
if(eventCallback){
GUIEvent e={eventCallback,name,this,GUIEventChange};
GUIEventQueue.push_back(e);
}
}
void GUITextArea::moveCarrotRight(){
- //Move carrot.
- value++;
-
- //Check if over the current line.
- if(value>(int)lines.at(currentLine).length()){
- //Check if the last line.
- if(currentLine==lines.size()-1){
- value=lines.at(currentLine).length();
+ highlightEnd++;
+ if (highlightEnd>lines.at(highlightLineEnd).length()){
+ if (highlightLineEnd==lines.size()-1){
+ highlightEnd--;
}else{
- //Can move to the next line.
- currentLine++;
- value=0;
+ highlightEnd=0;
+ highlightEndX=0;
+ highlightLineEnd++;
}
+ }else{
+ int advance;
+ TTF_GlyphMetrics(widgetFont,lines.at(highlightLineEnd).at(highlightEnd-1),NULL,NULL,NULL,NULL,&advance);
+ highlightEndX+=advance;
}
-
- //Adjust view.
+ if((SDL_GetModState()&KMOD_SHIFT)==0){
+ highlightLineStart=highlightLineEnd;
+ highlightStart=highlightEnd;
+ highlightStartX=highlightEndX;
+ }
adjustView();
}
void GUITextArea::moveCarrotLeft(){
- //Move carrot.
- value--;
-
- //Check if below the current line.
- if(value<0){
- //Check if the first line.
- if(currentLine==0){
- value=0;
+ highlightEnd--;
+ if (highlightEnd<0){
+ if (highlightLineEnd==0){
+ highlightEnd++;
}else{
- //Can move to the previous line.
- currentLine--;
- value=lines.at(currentLine).length();
+ highlightLineEnd--;
+ highlightEnd=lines.at(highlightLineEnd).length();
+ highlightEndX=0;
+ SDL_Surface** c=&linesCache.at(highlightLineEnd);
+ if(*c) highlightEndX=(*c)->w;
}
+ }else{
+ int advance;
+ TTF_GlyphMetrics(widgetFont,lines.at(highlightLineEnd).at(highlightEnd),NULL,NULL,NULL,NULL,&advance);
+ highlightEndX-=advance;
}
-
- //Adjust view.
+ if((SDL_GetModState()&KMOD_SHIFT)==0){
+ highlightLineStart=highlightLineEnd;
+ highlightStart=highlightEnd;
+ highlightStartX=highlightEndX;
+ }
adjustView();
}
void GUITextArea::moveCarrotUp(){
- if(currentLine==0){
- value=0;
+ if(highlightLineEnd==0){
+ highlightEnd=0;
+ highlightEndX=0;
}else{
- //Calculate carrot x position.
- int carrotX=0;
- for(int n=0;n<value;n++){
- int advance;
- TTF_GlyphMetrics(widgetFont,lines.at(currentLine).at(n),NULL,NULL,NULL,NULL,&advance);
- carrotX+=advance;
- }
-
+ highlightLineEnd--;
+ string* str=&lines.at(highlightLineEnd);
+
//Find out closest match.
- currentLine--;
- string* str=&lines.at(currentLine);
- value=str->length();
-
int xPos=0;
- for(unsigned int i=0;i<str->length();i++){
+ size_t i;
+ for(i=0;i<str->length();i++){
int advance;
TTF_GlyphMetrics(widgetFont,str->at(i),NULL,NULL,NULL,NULL,&advance);
xPos+=advance;
-
- if(carrotX<xPos-advance/2){
- value=i;
+
+ if(highlightEndX<xPos-advance/2){
+ highlightEnd=i;
+ highlightEndX=xPos-advance;
break;
}
}
+ if(i==str->length()){
+ highlightEnd=str->length();
+ highlightEndX=0;
+ SDL_Surface** c=&linesCache.at(highlightLineEnd);
+ if(*c) highlightEndX=(*c)->w;
+ }
}
-
- //Adjust view.
+ if((SDL_GetModState()&KMOD_SHIFT)==0){
+ highlightStart=highlightEnd;
+ highlightStartX=highlightEndX;
+ highlightLineStart=highlightLineEnd;
+ }
adjustView();
}
-
+
void GUITextArea::moveCarrotDown(){
- if(currentLine==lines.size()-1){
- value=lines.at(currentLine).length();
+ if(highlightLineEnd==lines.size()-1){
+ highlightEnd=lines.at(highlightLineEnd).length();
+ highlightEndX=0;
+ SDL_Surface** c=&linesCache.at(highlightLineEnd);
+ if(*c) highlightEndX=(*c)->w;
}else{
- //Calculate carrot x position.
- int carrotX=0;
- for(int n=0;n<value;n++){
- int advance;
- TTF_GlyphMetrics(widgetFont,lines.at(currentLine).at(n),NULL,NULL,NULL,NULL,&advance);
- carrotX+=advance;
- }
-
+ highlightLineEnd++;
+ string* str=&lines.at(highlightLineEnd);
+
//Find out closest match.
- currentLine++;
- string* str=&lines.at(currentLine);
- value=str->length();
-
int xPos=0;
- for(unsigned int i=0;i<str->length();i++){
+ size_t i;
+ for(i=0;i<str->length();i++){
int advance;
TTF_GlyphMetrics(widgetFont,str->at(i),NULL,NULL,NULL,NULL,&advance);
xPos+=advance;
-
- if(carrotX<xPos-advance/2){
- value=i;
+
+ if(highlightEndX<xPos-advance/2){
+ highlightEnd=i;
+ highlightEndX=xPos-advance;
break;
}
}
+ if(i==str->length()){
+ highlightEnd=str->length();
+ highlightEndX=0;
+ SDL_Surface** c=&linesCache.at(highlightLineEnd);
+ if(*c) highlightEndX=(*c)->w;
+ }
}
-
- //Adjust view.
+ if((SDL_GetModState()&KMOD_SHIFT)==0){
+ highlightStart=highlightEnd;
+ highlightStartX=highlightEndX;
+ highlightLineStart=highlightLineEnd;
+ }
adjustView();
}
void GUITextArea::adjustView(){
//Adjust view to current line.
- if(fontHeight*(currentLine-scrollBar->value)+4>height-4)
- scrollBar->value=currentLine-3;
- else if(currentLine-scrollBar->value<0)
- scrollBar->value=currentLine;
+ if(fontHeight*(highlightLineEnd-scrollBar->value)+4>height-4)
+ scrollBar->value=highlightLineEnd-3;
+ else if(highlightLineEnd-scrollBar->value<0)
+ scrollBar->value=highlightLineEnd;
//Find out the lenght of the longest line.
int maxWidth=0;
for(vector<SDL_Surface*>::iterator it=linesCache.begin();it!=linesCache.end();++it){
if((*it)&&(*it)->w>width-16&&(*it)->w>maxWidth)
maxWidth=(*it)->w;
}
//We need the horizontal scrollbar if any line is too long.
if(maxWidth>0){
scrollBar->height=height-16;
scrollBarH->visible=true;
scrollBarH->maxValue=maxWidth-width+24;
}else{
scrollBar->height=height;
scrollBarH->visible=false;
scrollBarH->value=0;
scrollBarH->maxValue=0;
}
//Adjust the horizontal view.
int carrotX=0;
- for(int n=0;n<value;n++){
+ for(int n=0;n<highlightEnd;n++){
int advance;
- TTF_GlyphMetrics(widgetFont,lines.at(currentLine).at(n),NULL,NULL,NULL,NULL,&advance);
+ TTF_GlyphMetrics(widgetFont,lines.at(highlightLineEnd).at(n),NULL,NULL,NULL,NULL,&advance);
carrotX+=advance;
}
if(carrotX>width-24)
scrollBarH->value=scrollBarH->maxValue;
else
scrollBarH->value=0;
//Update vertical scrollbar.
int rh=height-(scrollBarH->visible?16:0);
int m=lines.size(),n=(int)floor((float)rh/(float)fontHeight);
if(m>n){
scrollBar->maxValue=m-n;
scrollBar->smallChange=1;
scrollBar->largeChange=n;
}else{
scrollBar->value=0;
scrollBar->maxValue=0;
}
}
+void GUITextArea::drawHighlight(int x,int y,SDL_Rect* r,Uint32 color){
+ if(r->x<x) {
+ int tmp_w = r->w - x + r->x;
+ if(tmp_w<0) return;
+ r->w = tmp_w;
+ r->x = left;
+ }
+ if(r->x+r->w > x+width){
+ int tmp_w=width-r->x+x;
+ if(tmp_w<=0) return;
+ r->w=tmp_w;
+ }
+ if(r->y<y){
+ int tmp_h=r->h - y + r->y;
+ if(tmp_h<=0) return;
+ r->h=tmp_h;
+ }
+ if(r->y+r->h > y+height){
+ int tmp_h=height-r->y+y;
+ if(tmp_h<=0) return;
+ r->h=tmp_h;
+ }
+ SDL_FillRect(screen,r,color);
+}
+
void GUITextArea::render(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_BACKSPACE:
backspaceChar();
break;
case SDLK_DELETE:
deleteChar();
break;
case SDLK_LEFT:
moveCarrotLeft();
break;
case SDLK_RIGHT:
moveCarrotRight();
break;
case SDLK_UP:
moveCarrotUp();
break;
case SDLK_DOWN:
moveCarrotDown();
break;
}
}
}
//There's no need drawing the GUIObject when it's invisible.
- if(!visible||!draw)
+ if(!visible||!draw)
return;
//Get the absolute x and y location.
x+=left;
y+=top;
//Draw the box.
Uint32 color=0xFFFFFFFF;
drawGUIBox(x,y,width,height,screen,color);
-
+
+ //Place the highlighted area.
+ SDL_Rect r;
+ color=SDL_MapRGB(screen->format,128,128,128);
+ if(highlightLineStart==highlightLineEnd){
+ r.x=x-scrollBarH->value;
+ r.y=y+((highlightLineStart-scrollBar->value)*fontHeight);
+ r.h=fontHeight;
+ if(highlightStart<highlightEnd){
+ r.x+=highlightStartX;
+ r.w=highlightEndX-highlightStartX;
+ }else{
+ r.x+=highlightEndX;
+ r.w=highlightStartX-highlightEndX;
+ }
+ drawHighlight(x,y,&r,color);
+ }else if(highlightLineStart<highlightLineEnd){
+ int lnc=highlightLineEnd-highlightLineStart;
+ for(int i=0;i<=lnc;i++){
+ r.x=x-scrollBarH->value;
+ r.y=y+((i+highlightLineStart-scrollBar->value)*fontHeight);
+ r.w=width+scrollBarH->maxValue;
+ r.h=fontHeight;
+ if(i==0){
+ r.x+=highlightStartX;
+ r.w-=highlightStartX;
+ }else if(i==lnc){
+ r.w=highlightEndX;
+ }
+ if(lines.at(i+highlightLineStart).empty()){
+ r.w=fontHeight/4;
+ }
+ drawHighlight(x,y,&r,color);
+ }
+ }else{
+ int lnc=highlightLineStart-highlightLineEnd;
+ for(int i=0;i<=lnc;i++){
+ r.x=x-scrollBarH->value;
+ r.y=y+((i+highlightLineEnd-scrollBar->value)*fontHeight);
+ r.w=width+scrollBarH->maxValue;
+ r.h=fontHeight;
+ if(i==0){
+ r.x+=highlightEndX;
+ r.w-=highlightEndX;
+ }else if(i==lnc){
+ r.w=highlightStartX;
+ }
+ if(lines.at(i+highlightLineEnd).empty()){
+ r.w=fontHeight/4;
+ }
+ drawHighlight(x,y,&r,color);
+ }
+ }
+
//Draw text.
int lineY=0;
for(std::vector<SDL_Surface*>::iterator it=linesCache.begin()+scrollBar->value;it!=linesCache.end();++it){
if(*it){
if(lineY<height){
SDL_Rect r={scrollBarH->value,0,width-17,(*it)->h};
int over=-height+lineY+fontHeight;
if(over>0) r.h-=over;
applySurface(x+1,y+1+lineY,*it,screen,&r);
}else{
break;
}
}
lineY+=fontHeight;
}
//Only draw the carrot when focus.
if(state==2&&editable){
- SDL_Rect r;
- r.x=x-scrollBarH->value;
- r.y=y+4+fontHeight*(currentLine-scrollBar->value);
+ r.x=x-scrollBarH->value+highlightEndX;
+ r.y=y+4+fontHeight*(highlightLineEnd-scrollBar->value);
r.w=2;
r.h=fontHeight-4;
//Make sure that the carrot is inside the textbox.
- if((r.y<y+height-4)&&(r.y>y)){
- //Calculate position for the carrot.
- for(int n=0;n<value;n++){
- int advance;
- TTF_GlyphMetrics(widgetFont,lines.at(currentLine).at(n),NULL,NULL,NULL,NULL,&advance);
- r.x+=advance;
- }
-
- //Draw the carrot.
- if(r.x>x-1&&r.x<x+width-16)
- SDL_FillRect(screen,&r,0);
+ if((r.y<y+height-4)&&(r.y>y)&&(r.x>x-1)&&(r.x<x+width-16)){
+ //SDL_FillRect(screen,&r,0);
+ drawHighlight(x,y,&r,0x00000000);
}
}
//We now need to draw all the children of the GUIObject.
for(unsigned int i=0;i<childControls.size();i++){
childControls[i]->render(x,y,draw);
}
}
void GUITextArea::setString(std::string input){
//Clear previous content if any.
//Delete every line.
lines.clear();
//Free cached images.
for(unsigned int i=0;i<linesCache.size();i++){
SDL_FreeSurface(linesCache[i]);
}
linesCache.clear();
size_t linePos=0,lineLen=0;
SDL_Color black={0,0,0,0};
SDL_Surface* bm=NULL;
//Loop through the input string.
- for(size_t i=0;i<input.length();++i)
- {
+ for(size_t i=0;i<input.length();++i){
//Check when we come in end of a line.
if(input.at(i)=='\n'){
//Check if the line is empty.
if(lineLen==0){
lines.push_back("");
- linesCache.push_back(NULL);
+ linesCache.push_back(NULL);
}else{
//Read the whole line.
string line=input.substr(linePos,lineLen);
lines.push_back(line);
//Render and cache text.
bm=TTF_RenderUTF8_Blended(widgetFont,line.c_str(),black);
linesCache.push_back(bm);
}
//Skip '\n' in end of the line.
linePos=i+1;
lineLen=0;
}else{
lineLen++;
}
}
//The string might not end with a newline.
//That's why we're going to add end rest of the string as one line.
string line=input.substr(linePos);
lines.push_back(line);
bm=TTF_RenderUTF8_Blended(widgetFont,line.c_str(),black);
linesCache.push_back(bm);
- //Adjust view.
adjustView();
}
void GUITextArea::setStringArray(std::vector<std::string> input){
//Free cached images.
for(unsigned int i=0;i<linesCache.size();i++){
SDL_FreeSurface(linesCache[i]);
}
linesCache.clear();
//Copy values.
lines=input;
//Draw new strings.
SDL_Color black={0,0,0,0};
for(vector<string>::iterator it=lines.begin();it!=lines.end();++it){
SDL_Surface* bm=TTF_RenderUTF8_Blended(widgetFont,(*it).c_str(),black);
linesCache.push_back(bm);
}
- //Adjust view.
adjustView();
}
string GUITextArea::getString(){
string tmp;
for(vector<string>::iterator it=lines.begin();it!=lines.end();++it){
//Append a newline only if not the first line.
if(it!=lines.begin())
tmp.append(1,'\n');
//Append the line.
tmp.append(*it);
}
return tmp;
}
void GUITextArea::resize(){
scrollBar->left=width-16;
scrollBar->height=height;
if(scrollBarH->visible)
scrollBar->height-=16;
scrollBarH->top=height-16;
scrollBarH->width=width-16;
adjustView();
}
diff --git a/src/GUITextArea.h b/src/GUITextArea.h
index 2395743..aa7cf38 100644
--- a/src/GUITextArea.h
+++ b/src/GUITextArea.h
@@ -1,122 +1,130 @@
/*
* 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/>.
*/
#ifndef GUITEXTAREA_H
#define GUITEXTAREA_H
#ifdef __APPLE__
#include <SDL_ttf/SDL_ttf.h>
#else
#include <SDL/SDL_ttf.h>
#endif
#include "GUIObject.h"
#include "GUIScrollBar.h"
-//GUIObject based widget for multiline text input.
-//It extends GUIObject because it's a special GUIObject.
+//Widget for multiline text input.
class GUITextArea:public GUIObject{
private:
//Method that will remove the last character of the text.
void backspaceChar();
void deleteChar();
//Methods to move the carrot by one character/line.
void moveCarrotLeft();
void moveCarrotRight();
void moveCarrotUp();
void moveCarrotDown();
+
+ // Move all highlighted text.
+ void removeHighlight();
//Method to adjust view so carrot stays visible.
void adjustView();
//Pointer to the font used in the widget.
TTF_Font* widgetFont;
//Widget's text.
//One line per vector element.
std::vector<std::string> lines;
//Cache for rendered lines.
//Will be updated alongside with variable text.
std::vector<SDL_Surface*> linesCache;
//Variable for carrot position.
- //NOTE: We will use variable "value" from GUIObject for position within the current line.
- int currentLine;
-
+ int highlightLineStart;
+ int highlightLineEnd;
+ int highlightStart;
+ int highlightStartX;
+ int highlightEnd;
+ int highlightEndX;
+
//Height of the font.
int fontHeight;
//Scrollbar widget.
GUIScrollBar* scrollBar;
GUIScrollBar* scrollBarH;
//Integer containing the key that is holded.
int key;
//Integer containing the time the key is pressed.
int keyHoldTime;
//The time it takes to invoke the key action again.
int keyTime;
+
+ void drawHighlight(int x,int y,SDL_Rect* r,Uint32 color);
public:
//Constructor.
//left: The relative x location of the GUITextArea.
//top: The relative y location of the GUITextArea.
//witdh: The width of the GUITextArea.
//height: The height of the GUITextArea.
//enabled: Boolean if the GUITextArea is enabled or not.
//visible: Boolean if the GUITextArea is visisble or not.
GUITextArea(int left=0,int top=0,int width=0,int height=0,bool enabled=true,bool visible=true);
//Destructor
~GUITextArea();
//Method used to change the font.
//font: Pointer to the font
void setFont(TTF_Font* font);
//Method used to reposition scrollbars after a resize.
void resize();
//Method used to get widget's text in a single string.
std::string getString();
//Method used to set widget's text.
void setString(std::string input);
void setStringArray(std::vector<std::string> input);
//Bool if user can edit text in the widget.
bool editable;
//Method used to handle mouse and/or key events.
//x: The x mouse location.
//y: The y mouse location.
//enabled: Boolean if the parent is enabled or not.
//visible: Boolean if the parent is visible or not.
//processed: Boolean if the event has been processed (by the parent) or not.
//Returns: Boolean if the event is processed by the child.
virtual bool handleEvents(int x=0,int y=0,bool enabled=true,bool visible=true,bool processed=false);
//Method that will render the GUITextArea.
//x: The x location to draw the GUITextArea. (x+left)
//y: The y location to draw the GUITextArea. (y+top)
virtual void render(int x=0,int y=0,bool draw=true);
};
#endif

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 16, 7:17 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63412
Default Alt Text
(80 KB)

Event Timeline