Improved the pointer's(mouse) widget selection method, so that instead of using a bounding box to know if the pointer is on top of a widget, it draws the widget's rect into OpenGL'sselection buffer; this way, the widgets can be selected with precision regardless of the rotation or if it has rounded corners or not.
git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@1612 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
32d4e3f117
commit
573694016c
286
src/widget.cpp
286
src/widget.cpp
@ -103,15 +103,9 @@ void Widget::update(const float DELTA)
|
||||
*/
|
||||
|
||||
glClear( GL_STENCIL_BUFFER_BIT );
|
||||
glTranslatef ( (GLfloat)(m_x + m_width * 0.5f), (GLfloat)(m_y + m_height * 0.5f), 0);
|
||||
|
||||
m_rotation_angle += m_rotation_speed * DELTA;
|
||||
if( m_enable_rotation )
|
||||
{
|
||||
glRotatef( (GLfloat)m_rotation_angle, 0.0f, 0.0f, (GLfloat)1.0f );
|
||||
}
|
||||
|
||||
|
||||
if( m_enable_rotation ) m_rotation_angle += m_rotation_speed * DELTA;
|
||||
applyTransformations();
|
||||
|
||||
/*Handle delta time dependant features*/
|
||||
if(m_text_scale > MIN_TEXT_SCALE)
|
||||
@ -370,6 +364,145 @@ void Widget::update(const float DELTA)
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Widget::resizeToText()
|
||||
{
|
||||
if( !m_text.empty() )
|
||||
{
|
||||
float left, right, bottom, top;
|
||||
m_font->getBBox(m_text.c_str(), m_text_size, false, &left, &right, &bottom, &top);
|
||||
|
||||
const int TEXT_WIDTH = (int)(right - left);
|
||||
const int TEXT_HEIGHT = (int)(top - bottom);
|
||||
|
||||
if( TEXT_WIDTH > m_width ) m_width = TEXT_WIDTH;
|
||||
if( TEXT_HEIGHT > m_height ) m_height = TEXT_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/* Please note that this function only lightens 'non-light' colors */
|
||||
void Widget::lightenColor()
|
||||
{
|
||||
if(m_rect_color == WGT_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_GRAY;
|
||||
}
|
||||
if(m_rect_color == WGT_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_RED)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_BLUE;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_GRAY;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_RED)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_BLUE;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/* Please note that this function only darkens 'light' colors. */
|
||||
void Widget::darkenColor()
|
||||
{
|
||||
if(m_rect_color == WGT_LIGHT_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_GRAY;
|
||||
}
|
||||
if(m_rect_color == WGT_LIGHT_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_RED)
|
||||
{
|
||||
m_rect_color = WGT_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_BLUE;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_GRAY;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_RED)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_BLUE;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Widget::setFont( const WidgetFont FONT )
|
||||
{
|
||||
switch( FONT )
|
||||
{
|
||||
case WGT_FONT_GUI:
|
||||
m_font = font_gui;
|
||||
break;
|
||||
|
||||
case WGT_FONT_RACE:
|
||||
m_font = font_race;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
/** Initialize a display list containing a rectangle that can have rounded
|
||||
* corners, with texture coordinates to properly apply a texture
|
||||
* map to the rectangle as though the corners were not rounded . Returns
|
||||
@ -541,140 +674,13 @@ bool Widget::createRect(int radius)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Widget::resizeToText()
|
||||
void Widget::applyTransformations()
|
||||
{
|
||||
if( !m_text.empty() )
|
||||
glTranslatef ( (GLfloat)(m_x + m_width * 0.5f), (GLfloat)(m_y + m_height * 0.5f), 0);
|
||||
|
||||
if( m_enable_rotation )
|
||||
{
|
||||
float left, right, bottom, top;
|
||||
m_font->getBBox(m_text.c_str(), m_text_size, false, &left, &right, &bottom, &top);
|
||||
|
||||
const int TEXT_WIDTH = (int)(right - left);
|
||||
const int TEXT_HEIGHT = (int)(top - bottom);
|
||||
|
||||
if( TEXT_WIDTH > m_width ) m_width = TEXT_WIDTH;
|
||||
if( TEXT_HEIGHT > m_height ) m_height = TEXT_HEIGHT;
|
||||
glRotatef( (GLfloat)m_rotation_angle, 0.0f, 0.0f, (GLfloat)1.0f );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/* Please note that this function only lightens 'non-light' colors */
|
||||
void Widget::lightenColor()
|
||||
{
|
||||
if(m_rect_color == WGT_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_GRAY;
|
||||
}
|
||||
if(m_rect_color == WGT_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_RED)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_BLUE;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_GRAY;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_RED)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_TRANS_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_LIGHT_TRANS_BLUE;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/* Please note that this function only darkens 'light' colors. */
|
||||
void Widget::darkenColor()
|
||||
{
|
||||
if(m_rect_color == WGT_LIGHT_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_GRAY;
|
||||
}
|
||||
if(m_rect_color == WGT_LIGHT_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_RED)
|
||||
{
|
||||
m_rect_color = WGT_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_BLUE;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_GRAY)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_GRAY;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_BLACK)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_BLACK;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_YELLOW)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_YELLOW;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_RED)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_RED;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_GREEN)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_GREEN;
|
||||
}
|
||||
else if (m_rect_color == WGT_LIGHT_TRANS_BLUE)
|
||||
{
|
||||
m_rect_color = WGT_TRANS_BLUE;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Widget::setFont( const WidgetFont FONT )
|
||||
{
|
||||
switch( FONT )
|
||||
{
|
||||
case WGT_FONT_GUI:
|
||||
m_font = font_gui;
|
||||
break;
|
||||
|
||||
case WGT_FONT_RACE:
|
||||
m_font = font_race;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
@ -183,19 +183,22 @@ class Widget
|
||||
|
||||
void update(const float DELTA);
|
||||
|
||||
void resizeToText(); //This checks if the widget is smaller than the
|
||||
//text, and if so, changes the width and height.
|
||||
bool createRect(int radius);
|
||||
|
||||
/* Time limited features' functions. */
|
||||
void pulse() {m_text_scale = MAX_TEXT_SCALE;}
|
||||
|
||||
/* Convenience functions. */
|
||||
void resizeToText(); //This checks if the widget is smaller than the
|
||||
//text, and if so, changes the width and height.
|
||||
|
||||
void lightenColor();
|
||||
void darkenColor();
|
||||
|
||||
void setFont( const WidgetFont FONT);
|
||||
|
||||
/* Functions created simply to organize the code */
|
||||
bool createRect(int radius);
|
||||
void applyTransformations();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -20,6 +20,12 @@
|
||||
|
||||
#include "user_config.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <OpenGL/glu.h>
|
||||
#else
|
||||
# include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
@ -1247,40 +1253,100 @@ void WidgetManager::darkenWgtColor(const int TOKEN)
|
||||
*/
|
||||
int WidgetManager::handlePointer(const int X, const int Y )
|
||||
{
|
||||
//Search if the given x and y positions are on top of any widget. Please
|
||||
//note that the bounding box for each widget is used instead of the
|
||||
//real widget shape.
|
||||
const int NUM_WGTS = m_widgets.size();
|
||||
if( NUM_WGTS < 1 ) return WGT_NONE;
|
||||
|
||||
//The search starts with the current selected widget(since it's most
|
||||
//probable that the mouse is on top of it )
|
||||
const int NUM_WIDGETS = (int)m_widgets.size();
|
||||
//OpenGL provides a mechanism to select objects; simply 'draw' named
|
||||
//objects, and for each non-culled visible object a 'hit' will be saved
|
||||
//into a selection buffer. Objects are named by using OpenGL's name
|
||||
//stack. The information in each hit is the number of names in the name
|
||||
//stack, two hard-to-explain depth values that aren't used in this
|
||||
//function, and the contents of the name stack at the time of the hit.
|
||||
|
||||
if( m_selected_wgt_token != WGT_NONE )
|
||||
{
|
||||
const int SELECTED_WGT_ID = findId(m_selected_wgt_token);
|
||||
//This function loads 1 name into the stack (because if you pop an empty
|
||||
//stack you get an error), then uses glLoadName (which is a shortcut for
|
||||
//popping then pushing) to change the name. That means that each time a
|
||||
//hit is recorded, only the last drawn widget will be on the name stack.
|
||||
|
||||
if(( X > m_widgets[SELECTED_WGT_ID].widget->m_x ) &&
|
||||
( X < m_widgets[SELECTED_WGT_ID].widget->m_x + m_widgets[SELECTED_WGT_ID].widget->m_width ) &&
|
||||
( Y > m_widgets[SELECTED_WGT_ID].widget->m_y ) &&
|
||||
( Y < m_widgets[SELECTED_WGT_ID].widget->m_y + m_widgets[SELECTED_WGT_ID].widget->m_height ))
|
||||
{
|
||||
return WGT_NONE;
|
||||
}
|
||||
}
|
||||
const int HIT_SIZE = 4; //1 Gluint for the number of names, 2 depth
|
||||
//values, and 1 for the token of the widget.
|
||||
const int BUFFER_SIZE = HIT_SIZE * NUM_WGTS;
|
||||
|
||||
for( int i = 0; i < NUM_WIDGETS; ++i )
|
||||
GLuint select_buffer[BUFFER_SIZE];
|
||||
glSelectBuffer(BUFFER_SIZE, select_buffer);
|
||||
|
||||
glRenderMode(GL_SELECT);
|
||||
|
||||
//Set the viewport to draw only what's under the mouse
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
GLint viewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT,viewport);
|
||||
gluPickMatrix(X, Y, 1,1,viewport);
|
||||
glOrtho(0.0, user_config->m_width, 0.0, user_config->m_height, -1.0, 1.0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glInitNames();
|
||||
glPushName(WGT_NONE);
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
for( int i = 0; i < NUM_WGTS; ++i )
|
||||
{
|
||||
if(!(m_widgets[i].active)) continue;
|
||||
|
||||
//Simple bounding box test
|
||||
if(( X > m_widgets[i].widget->m_x ) &&
|
||||
( X < m_widgets[i].widget->m_x + m_widgets[i].widget->m_width ) &&
|
||||
( Y > m_widgets[i].widget->m_y ) &&
|
||||
( Y < m_widgets[i].widget->m_y + m_widgets[i].widget->m_height ))
|
||||
glLoadName( m_widgets[i].token );
|
||||
|
||||
glPushMatrix();
|
||||
m_widgets[i].widget->applyTransformations();
|
||||
//In case this ever becomes a performance bottleneck:
|
||||
//the m_rect_list includes texture coordinates, which are not
|
||||
//needed for selection.
|
||||
glCallList( m_widgets[i].widget->m_rect_list );
|
||||
glPopMatrix();
|
||||
}
|
||||
glFlush();
|
||||
glPopMatrix();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glFlush();
|
||||
|
||||
GLuint* position = select_buffer;
|
||||
const GLint NUM_HITS = glRenderMode(GL_RENDER);
|
||||
|
||||
if( NUM_HITS > 0 )
|
||||
{
|
||||
float dist;
|
||||
float near_dist = 9999999.0f;
|
||||
int nearest = WGT_NONE;
|
||||
int wgt_x_center, wgt_y_center;
|
||||
int curr_wgt;
|
||||
for( int i = 0; i < NUM_HITS; ++i )
|
||||
{
|
||||
m_selected_wgt_token = m_widgets[i].token;
|
||||
return m_selected_wgt_token;
|
||||
position += 3;
|
||||
curr_wgt = *position;
|
||||
|
||||
wgt_x_center = m_widgets[curr_wgt].widget->m_x + m_widgets[i].widget->m_width / 2;
|
||||
wgt_y_center = m_widgets[curr_wgt].widget->m_y + m_widgets[i].widget->m_height / 2;
|
||||
//Check if it's the closest one to the mouse
|
||||
dist = ( fabsf(X - wgt_x_center) + fabsf(Y - wgt_y_center));
|
||||
if(dist < near_dist )
|
||||
{
|
||||
near_dist = dist;
|
||||
nearest = curr_wgt;
|
||||
}
|
||||
|
||||
++position;
|
||||
}
|
||||
if( nearest == m_selected_wgt_token ) return WGT_NONE;
|
||||
|
||||
m_selected_wgt_token = nearest;
|
||||
return m_selected_wgt_token;
|
||||
}
|
||||
|
||||
return WGT_NONE;
|
||||
@ -1293,7 +1359,7 @@ int
|
||||
WidgetManager::handleLeft()
|
||||
{
|
||||
if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE;
|
||||
|
||||
|
||||
return handleFinish(findLeftWidget(findId(m_selected_wgt_token)));
|
||||
}
|
||||
|
||||
@ -1301,7 +1367,7 @@ WidgetManager::handleLeft()
|
||||
WidgetManager::handleRight()
|
||||
{
|
||||
if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE;
|
||||
|
||||
|
||||
return handleFinish(findRightWidget(findId(m_selected_wgt_token)));
|
||||
}
|
||||
|
||||
@ -1309,7 +1375,7 @@ int
|
||||
WidgetManager::handleUp()
|
||||
{
|
||||
if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE;
|
||||
|
||||
|
||||
return handleFinish(findTopWidget(findId(m_selected_wgt_token)));
|
||||
}
|
||||
|
||||
@ -1317,7 +1383,7 @@ int
|
||||
WidgetManager::handleDown()
|
||||
{
|
||||
if( m_selected_wgt_token == WGT_NONE ) return WGT_NONE;
|
||||
|
||||
|
||||
return handleFinish(findBottomWidget(findId(m_selected_wgt_token)));
|
||||
}
|
||||
|
||||
@ -1326,9 +1392,9 @@ WidgetManager::handleFinish(const int next_wgt)
|
||||
{
|
||||
if( next_wgt == WGT_NONE)
|
||||
return WGT_NONE;
|
||||
|
||||
|
||||
m_selected_wgt_token = m_widgets[next_wgt].token;
|
||||
|
||||
|
||||
return m_selected_wgt_token;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user