/* -------------------------------------------------------------------- EXTREME TUXRACER Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer) Copyright (C) 2010 Extreme Tuxracer Team This program 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 2 of the License, or (at your option) any later version. This program 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. ---------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include #endif #include "gui.h" #include "textures.h" #include "font.h" #include "ogl.h" #include "winsys.h" #include #include #define CURSOR_SIZE 10 static vector Widgets; static bool Inside(int x, int y, const TRect& Rect) { return (x >= Rect.left && x <= Rect.left + Rect.width && y >= Rect.top && y <= Rect.top + Rect.height); } TWidget::TWidget(int x, int y, int width, int height) : active(true) , visible(true) , focus(false) { mouseRect.top = y; mouseRect.left = x; mouseRect.height = height; mouseRect.width = width; position.x = x; position.y = y; } bool TWidget::Click(int x, int y) { return active && visible && Inside(x, y, mouseRect); } void TWidget::MouseMove(int x, int y) { focus = active && visible && Inside(x, y, mouseRect); } TTextButton::TTextButton(int x, int y, const string& text_, double ftsize_) : TWidget(x, y, 0, 0) , text(text_) , ftsize(ftsize_) { if (ftsize < 0) ftsize = FT.AutoSizeN(4); double len = FT.GetTextWidth(text); if (x == CENTER) position.x = (int)((Winsys.resolution.width - len) / 2); int offs = (int)(ftsize / 5); mouseRect.left = position.x-20; mouseRect.top = position.y+offs; mouseRect.width = len+40; mouseRect.height = ftsize+offs; } void TTextButton::Draw() const { if (focus) FT.SetColor(colDYell); else FT.SetColor(colWhite); FT.SetSize(ftsize); FT.DrawString(position.x, position.y, text); } TTextButton* AddTextButton(const string& text, int x, int y, double ftsize) { Widgets.push_back(new TTextButton(x, y, text, ftsize)); return static_cast(Widgets.back()); } TTextButton* AddTextButtonN(const string& text, int x, int y, int rel_ftsize) { double siz = FT.AutoSizeN(rel_ftsize); return AddTextButton(text, x, y, siz); } TTextField::TTextField(int x, int y, int width, int height, const string& text_) : TWidget(x, y, width, height) , text(text_) , cursorPos(0) , maxLng(32) , time(0.0) , cursor(false) { } void TTextField::Draw() const { const TColor& col = focus?colDYell:colWhite; FT.SetColor(col); DrawFrameX(mouseRect.left, mouseRect.top, mouseRect.width, mouseRect.height, 3, colMBackgr, col, 1.0); FT.AutoSizeN(5); FT.DrawString(mouseRect.left+20, mouseRect.top, text); if (cursor && focus) { int x = mouseRect.left + 20 + 1; if (cursorPos != 0) { string temp = text.substr(0, cursorPos); x += FT.GetTextWidth(temp); } int w = 3; int h = 26 * Winsys.scale; int scrheight = Winsys.resolution.height; glDisable(GL_TEXTURE_2D); glColor(colYellow); const GLshort vtx[] = { x, scrheight - mouseRect.top - h - 9, x + w, scrheight - mouseRect.top - h - 9, x + w, scrheight - mouseRect.top - 9, x, scrheight - mouseRect.top - 9 }; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_SHORT, 0, vtx); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glEnable(GL_TEXTURE_2D); } } void TTextField::Key(unsigned int key, unsigned int mod, bool released) { if (islower(key)) { if (text.size() < maxLng) { if (mod & KMOD_SHIFT) text.insert(cursorPos, 1, toupper(key)); else text.insert(cursorPos, 1, key); cursorPos++; } } else if (isdigit(key)) { if (text.size() < maxLng) { text.insert(cursorPos, 1, key); cursorPos++; } } else { switch (key) { case SDLK_DELETE: if (cursorPos < text.size()) text.erase(cursorPos, 1); break; case SDLK_BACKSPACE: if (cursorPos > 0) { text.erase(cursorPos-1, 1); cursorPos--; } break; case SDLK_RIGHT: if (cursorPos < text.size()) cursorPos++; break; case SDLK_LEFT: if (cursorPos > 0) cursorPos--; break; case SDLK_HOME: cursorPos = 0; break; case SDLK_END: cursorPos = text.size(); break; case SDLK_SPACE: text.insert(cursorPos, 1, 21); cursorPos++; break; } } } void TTextField::UpdateCursor(double timestep) { time += timestep; if (time > CRSR_PERIODE) { time = 0; cursor = !cursor; } } TTextField* AddTextField(const string& text, int x, int y, int width, int height) { Widgets.push_back(new TTextField(x, y, width, height, text)); return static_cast(Widgets.back()); } void TCheckbox::Draw() const { Tex.Draw(CHECKBOX, position.x + width - 32, position.y, 1.0); if (checked) Tex.Draw(CHECKMARK_SMALL, position.x + width - 32, position.y, 1.0); if (focus) FT.SetColor(colDYell); else FT.SetColor(colWhite); FT.DrawString(position.x, position.y, tag); } bool TCheckbox::Click(int x, int y) { if (active && visible && Inside(x, y, mouseRect)) { checked = !checked; return true; } return false; } void TCheckbox::Key(unsigned int key, unsigned int mod, bool released) { if (released) return; if (key == SDLK_SPACE || key == SDLK_RIGHT || key == SDLK_LEFT) { checked = !checked; } } TCheckbox* AddCheckbox(int x, int y, int width, const string& tag) { Widgets.push_back(new TCheckbox(x, y, width, tag)); return static_cast(Widgets.back()); } void TIconButton::SetValue(int _value) { value = _value; if (value > maximum) value = maximum; } void TIconButton::Draw() const { TColor framecol = colWhite; if (focus) framecol = colDYell; int line = 3; int framesize = size + 2 * line; int t = Winsys.resolution.height - position.y; int y = t - size; int x = position.x; int r = x + size; DrawFrameX(position.x-line, position.y-line, framesize, framesize, line, colBlack, framecol, 1.0); glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->Bind(); glColor4f(1.0, 1.0, 1.0, 1.0); const GLshort vtx[] = { x, y, r, y, r, t, x, t }; static const GLfloat tex[4][8] = { { 0, 0.5, 0.5, 0.5, 0.5, 1, 0, 1 }, { 0.5, 0.5, 1, 0.5, 1, 1, 0.5, 1 }, { 0, 0, 0.5, 0, 0.5, 0.5, 0, 0.5 }, { 0.5, 0, 1, 0, 1, 0.5, 0.5, 0.5 } }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_SHORT, 0, vtx); glTexCoordPointer(2, GL_FLOAT, 0, tex[value]); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } bool TIconButton::Click(int x, int y) { if (Inside(x, y, mouseRect)) { value++; if (value > maximum) value = 0; return true; } return false; } void TIconButton::Key(unsigned int key, unsigned int mod, bool released) { if (released) return; if (key == SDLK_DOWN || key == SDLK_LEFT) { // Arrow down/left value--; if (value < 0) value = maximum; } else if (key == SDLK_UP || key == SDLK_RIGHT) { // Arrow up/right value++; if (value > maximum) value = 0; } } TIconButton* AddIconButton(int x, int y, TTexture* texture, double size, int maximum, int value) { Widgets.push_back(new TIconButton(x, y, texture, size, maximum, value)); return static_cast(Widgets.back()); } void TArrow::Draw() const { static const float textl[6] = { 0.5, 0.0, 0.5, 0.5, 0.0, 0.5 }; static const float textr[6] = { 1.0, 0.5, 1.0, 1.0, 0.5, 1.0 }; static const float texbl[6] = { 0.25, 0.25, 0.75, 0.00, 0.00, 0.50 }; static const float texbr[6] = {0.50, 0.50, 1.00, 0.25, 0.25, 0.75}; int type = 0; if (active) type = 1; if (focus) type++; if (down) type += 3; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); Tex.BindTex(LB_ARROWS); glColor4f(1.0, 1.0, 1.0, 1.0); const GLfloat tex[] = { textl[type], texbl[type], textr[type], texbl[type], textr[type], texbr[type], textl[type], texbr[type] }; const GLshort vtx[] = { position.x, Winsys.resolution.height - position.y - 16, position.x + 32, Winsys.resolution.height - position.y - 16, position.x + 32, Winsys.resolution.height - position.y, position.x, Winsys.resolution.height - position.y }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_SHORT, 0, vtx); glTexCoordPointer(2, GL_FLOAT, 0, tex); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } TArrow* AddArrow(int x, int y, bool down) { Widgets.push_back(new TArrow(x, y, down)); return static_cast(Widgets.back()); } TUpDown::TUpDown(int x, int y, int min_, int max_, int value_, int distance) : TWidget(x, y, 32, 32+distance) , up(x, y+16+distance, true) , down(x, y, false) , value(value_) , minimum(min_) , maximum(max_) { up.SetActive(value < maximum); down.SetActive(value > minimum); } void TUpDown::Draw() const { up.Draw(); down.Draw(); } bool TUpDown::Click(int x, int y) { if (active && visible && up.Click(x, y)) { value++; down.SetActive(true); if (value == maximum) up.SetActive(false); return true; } if (active && visible && down.Click(x, y)) { up.SetActive(true); value--; if (value == minimum) down.SetActive(false); return true; } return false; } void TUpDown::Key(unsigned int key, unsigned int mod, bool released) { if (released) return; if (key == SDLK_UP || key == SDLK_RIGHT) { // Arrow down/left if (value > minimum) { value--; up.SetActive(true); if (value == minimum) down.SetActive(false); } } else if (key == SDLK_DOWN || key == SDLK_LEFT) { // Arrow up/right if (value < maximum) { value++; down.SetActive(true); if (value == maximum) up.SetActive(false); } } } void TUpDown::MouseMove(int x, int y) { focus = active && visible &&Inside(x, y, mouseRect); up.MouseMove(x, y); down.MouseMove(x, y); } void TUpDown::SetValue(int value_) { value = clamp(minimum, value_, maximum); up.SetActive(value < maximum); down.SetActive(value > minimum); } void TUpDown::SetMinimum(int min_) { minimum = min_; value = clamp(minimum, value, maximum); up.SetActive(value < maximum); down.SetActive(value > minimum); } void TUpDown::SetMaximum(int max_) { maximum = max_; value = clamp(minimum, value, maximum); up.SetActive(value < maximum); down.SetActive(value > minimum); } TUpDown* AddUpDown(int x, int y, int minimum, int maximum, int value, int distance) { Widgets.push_back(new TUpDown(x, y, minimum, maximum, value, distance)); return static_cast(Widgets.back()); } // ------------------ Elementary drawing --------------------------------------------- void DrawFrameX(int x, int y, int w, int h, int line, const TColor& backcol, const TColor& framecol, double transp) { float yy = Winsys.resolution.height - y - h; if (x < 0) x = (Winsys.resolution.width -w) / 2; glPushMatrix(); glDisable(GL_TEXTURE_2D); glEnableClientState(GL_VERTEX_ARRAY); glColor(framecol, transp); glTranslatef(x, yy, 0); const GLshort frame [] = { 0, 0, w, 0, w, h, 0, h }; glVertexPointer(2, GL_SHORT, 0, frame); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glColor(backcol, transp); const GLshort back [] = { 0 + line, 0 + line, w - line, 0 + line, w - line, h - line, 0 + line, h - line }; glVertexPointer(2, GL_SHORT, 0, back); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glEnable(GL_TEXTURE_2D); glPopMatrix(); } void DrawBonusExt(int y, size_t numraces, size_t num) { size_t maxtux = numraces * 3; if (num > maxtux) return; TVector2i bl, tr; //TColor col1 = {0.3, 0.5, 0.7, 1}; TColor col2(0.45, 0.65, 0.85, 1); //TColor col3 = {0.6, 0.8, 1.0, 1}; //TColor gold = {1, 1, 0, 1}; int lleft[3]; int framewidth = (int)numraces * 40 + 8; int totalwidth = framewidth * 3 + 8; int xleft = (Winsys.resolution.width - totalwidth) / 2; lleft[0] = xleft; lleft[1] = xleft + framewidth + 4; lleft[2] = xleft + framewidth + framewidth + 8; DrawFrameX(lleft[0], y, framewidth, 40, 1, col2, colBlack, 1); DrawFrameX(lleft[1], y, framewidth, 40, 1, col2, colBlack, 1); DrawFrameX(lleft[2], y, framewidth, 40, 1, col2, colBlack, 1); if (param.use_papercut_font > 0) FT.SetSize(20); else FT.SetSize(15); bl.y = Winsys.resolution.height - y - 32 -4; tr.y = Winsys.resolution.height - y - 0 -4; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); Tex.BindTex(TUXBONUS); glColor4f(1.0, 1.0, 1.0, 1.0); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); for (size_t i=0; i 2) majr = 2; bl.x = lleft[majr] + (int)minr * 40 + 6; tr.x = bl.x + 32; // with tux outlines: // if (iGetVisible()) Widgets[i]->Draw(); if (param.ice_cursor) DrawCursor(); } TWidget* ClickGUI(int x, int y) { TWidget* clicked = NULL; for (size_t i = 0; i < Widgets.size(); i++) if (Widgets[i]->Click(x, y)) clicked = Widgets[i]; return clicked; } static int focussed = -1; TWidget* MouseMoveGUI(int x, int y) { if (x != 0 || y != 0) { focussed = -1; for (size_t i = 0; i < Widgets.size(); i++) { Widgets[i]->MouseMove(cursor_pos.x, cursor_pos.y); if (Widgets[i]->focussed()) focussed = (int)i; } } if (focussed == -1) return 0; return Widgets[focussed]; } TWidget* KeyGUI(unsigned int key, unsigned int mod, bool released) { if (!released) { switch (key) { case SDLK_TAB: IncreaseFocus(); break; default: break; } } if (focussed == -1) return 0; Widgets[focussed]->Key(key, mod, released); return Widgets[focussed]; } void SetFocus(TWidget* widget) { if (!widget) focussed = -1; else for (int i = 0; i < (int)Widgets.size(); i++) if (Widgets[i] == widget) { focussed = i; break; } } void IncreaseFocus() { if (focussed >= 0) Widgets[focussed]->focus = false; focussed++; if (focussed >= (int)Widgets.size()) focussed = 0; int end = focussed; // Select only active widgets do { if (Widgets[focussed]->GetActive()) break; focussed++; if (focussed >= (int)Widgets.size()) focussed = 0; } while (end != focussed); if (focussed >= 0) Widgets[focussed]->focus = true; } void DecreaseFocus() { if (focussed >= 0) Widgets[focussed]->focus = false; if (focussed > 0) focussed--; else focussed = (int)Widgets.size()-1; int end = focussed; // Select only active widgets do { if (Widgets[focussed]->GetActive()) break; if (focussed > 0) focussed--; else focussed = (int)Widgets.size()-1; } while (end != focussed); if (focussed >= 0) Widgets[focussed]->focus = true; } void ResetGUI() { for (size_t i = 0; i < Widgets.size(); i++) delete Widgets[i]; Widgets.clear(); focussed = 0; } // ------------------ new --------------------------------------------- int AutoYPosN(double percent) { double hh = (double)Winsys.resolution.height; double po = hh * percent / 100; return (int)(po); } TArea AutoAreaN(double top_perc, double bott_perc, int w) { TArea res; res.top = AutoYPosN(top_perc); res.bottom = AutoYPosN(bott_perc); if (w > Winsys.resolution.width) w = Winsys.resolution.width; res.left = (Winsys.resolution.width - w) / 2; res.right = Winsys.resolution.width - res.left; return res; }