Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F118709
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
80 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline