#include "IMGUIControls.h" #include "Engine.h" #include "ControllerBase.h" #include "ViewBase.h" #include #include "DrawingsGL.h" #include "keytable.h" #include Engine::ControllerBase *controller = NULL; Engine::ViewBase *view = NULL; namespace Engine { namespace GUI { /** \brief Checks whether the mouse is in the given rectangle */ bool regionhit (int x, int y, int w, int h) { controller = EngineGetController(); assert (controller); int mouse_pos[2]; controller->GetMouseScreenPosition(mouse_pos); if (mouse_pos[0] < x || mouse_pos[1] < y || mouse_pos[0] >= x + w || mouse_pos[1] >= y + h) return false; return true; } void DrawBlock (int x, int y, int w, int h) { const float shading_dark = 0.5f; // const float shading_light = 1.3f; float color[4]; glGetFloatv (GL_CURRENT_COLOR, color); glBegin(GL_QUADS); // middle part glVertex3i (x, y, 0); glVertex3i (x, y + h, 0); glVertex3i (x + w, y + h, 0); glVertex3i (x + w, y, 0); glEnd(); glColor4fv (color); // "Shading" glLineWidth(3); glColor3f (color[0] * shading_dark, color[1] * shading_dark, color[2] * shading_dark); glBegin(GL_LINE_STRIP); glVertex3i (x, y + 2, 0); glVertex3i (x, y + h, 0); glVertex3i (x + w - 2, y + h, 0); glEnd(); } void DrawRoundedBlock (int x, int y, int w, int h) { const int d = 16; const float shading_dark = 0.5f; const float shading_light = 1.3f; assert (h > d); assert (w > d); glBegin(GL_QUADS); // lower part glVertex3i (x, y, 0); glVertex3i (x, y + d, 0); glVertex3i (x + w, y + d, 0); glVertex3i (x + w - d, y, 0); // middle part glVertex3i (x, y + d, 0); glVertex3i (x, y + d + h - 2 * d, 0); glVertex3i (x + w, y + d + h - 2 * d, 0); glVertex3i (x + w, y + d, 0); // bottom part glVertex3i (x, y + h - d, 0); glVertex3i (x + d, y + h, 0); glVertex3i (x + w, y + h, 0); glVertex3i (x + w, y + h - d, 0); glEnd(); // "Shading" float color[4]; glGetFloatv (GL_CURRENT_COLOR, color); glLineWidth(3); glColor3f (color[0] * shading_dark, color[1] * shading_dark, color[2] * shading_dark); glBegin(GL_LINE_STRIP); glVertex3i (x,y + 2, 0); glVertex3i (x,y + h - d, 0); glVertex3i (x + d, y + h, 0); glVertex3i (x + w - 2, y + h, 0); glEnd(); glColor3f (color[0] * shading_light, color[1] * shading_light, color[2] * shading_light); glBegin(GL_LINE_STRIP); glVertex3i (x + w - d, y, 0); glVertex3i (x + w, y + d, 0); glEnd(); glColor4fv (color); } /** \brief Draws a label with a given caption at the position (vertically centered) * * This function draws the label at the horizontal x position and centers the * label with regard to the height of the rendered caption. * * \TODO [med] The vertical alignment around the center of the vertical height * of the string is rather unfortunate as different contents will be rendered * at different vertical positions (e.g. "aaa" and "ggg") */ void Label (int id, const char* caption, int x, int y) { if (caption != NULL) { float width, height; view = EngineGetView (); assert (view); view->DrawGLStringMeasure(caption, &width, &height); std::stringstream font_spec(""); float font_size = view->GetCurrentFontSize(); // we shift the gray a little left and up depending on the font size font_spec << "console.ttf size=" << font_size << " color=#808080"; SelectFont(font_spec.str().c_str()); view->DrawGLString(x - font_size / 20.f , y + height * 0.5f + font_size / 20.f, caption); font_spec.str(""); font_spec << "console.ttf size=" << font_size << " color=#ffffff"; SelectFont(font_spec.str().c_str()); view->DrawGLString(x , y + height * 0.5f, caption); } } /** \brief Draws a label with a given caption centered at the position (vertically centered) * * This function draws the label at the horizontal x position and centers the * label with regard to the height of the rendered caption. * * \TODO [med] The vertical alignment around the center of the vertical height * of the string is rather unfortunate as different contents will be rendered * at different vertical positions (e.g. "aaa" and "ggg") */ void LabelCentered (int id, const char* caption, int x, int y) { if (caption != NULL) { float width, height; view = EngineGetView (); assert (view); view->DrawGLStringMeasure(caption, &width, &height); height = fabs (height); width = fabs (width); std::stringstream font_spec(""); float font_size = view->GetCurrentFontSize(); // we shift the gray a little left and up depending on the font size font_spec << "console.ttf size=" << font_size << " color=#808080"; SelectFont(font_spec.str().c_str()); view->DrawGLString(x - 0.5f * width - font_size / 20.f , y + height * 0.5f + font_size / 20.f, caption); font_spec.str(""); font_spec << "console.ttf size=" << font_size << " color=#ffffff"; SelectFont(font_spec.str().c_str()); view->DrawGLString(x - 0.5f * width, y + height * 0.5f, caption); } } /** \brief Draws a button at the given position * * The area defined by the parameters defines the clickable area. However * decorations might be drawn on a smaller or bigger area. * * \returns true if it was clicked * */ bool Button (int id, const char* caption, int x, int y, int w, int h) { controller = EngineGetController(); assert (controller); // LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem); // Check for hotness if (regionhit (x, y, w, h)) { controller->uistate.hotitem = id; if (controller->uistate.activeitem == 0 && controller->GetButtonState(MouseButtonLeft)) controller->uistate.activeitem = id; } // If nothing is selected if (controller->uistate.hotitem != 0) { controller->uistate.kbditem = controller->uistate.hotitem; } if (controller->uistate.kbditem == 0) { controller->uistate.hotitem = id; controller->uistate.kbditem = id; } // Render glColor3f (0.2f, 0.2f, 0.2f); // DrawRoundedBlock (x + 4, y + 4, w, h); if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) { if (controller->uistate.activeitem == id) { glColor3f (0.8f, 0.8f, 0.8f); DrawRoundedBlock (x, y, w, h); } else { glColor3f (0.7f, 0.7f, 0.7f); DrawRoundedBlock (x, y, w, h); } } else { glColor3f (0.4f, 0.4f, 0.4f); DrawRoundedBlock (x, y, w, h); } // Caption if (caption != NULL) { float width, height; view = EngineGetView (); assert (view); // we have to load the font prior to measuring it SelectFont("console.ttf size=23"); view->DrawGLStringMeasure(caption, &width, &height); float xpos = x + w * 0.5f - width * 0.5f; float ypos = y + h * 0.5f - height * 0.5f; // LogDebug ("measure '%s' width = %f height = %f", caption, width, height); if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) { SelectFont("console.ttf size=23 color=#666666"); view->DrawGLString( xpos - 2.f, ypos + 2.f, caption); SelectFont("console.ttf size=23 color=#ffffff"); view->DrawGLString( xpos, ypos, caption); } else { SelectFont("console.ttf size=23 color=#444444"); view->DrawGLString( xpos - 2.f, ypos + 2.f, caption); SelectFont("console.ttf size=23 color=#b3b3b3"); view->DrawGLString( xpos, ypos, caption); } } // Mouse Logic if (controller->GetButtonState(MouseButtonLeft) == false && controller->uistate.hotitem == id && controller->uistate.activeitem == id) { controller->uistate.lastwidget = id; return true; } // Keyboard Logic if (controller->uistate.kbditem == id) { // Any unicode keys that were sent get cleared controller->uistate.last_unicode = 0; // We have to make sure, that we always clear the uistate.last_keysym // value, otherwise the same action might be repeated over and over. switch (controller->uistate.last_keysym) { case SDLK_DOWN: controller->uistate.kbditem = 0; controller->uistate.hotitem = 0; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_UP: controller->uistate.kbditem = controller->uistate.lastwidget; controller->uistate.hotitem = controller->uistate.lastwidget; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_TAB: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; break; case SDLK_RETURN: controller->uistate.last_keysym = SDLK_CLEAR; // As we (probably) exit the current set of widgets, we have to clear // the uistate.kbditem value. controller->uistate.kbditem = 0; return true; break; default: break; } } controller->uistate.lastwidget = id; return false; } bool CheckButton (int id, const char* caption, bool checked, int x, int y, int w, int h) { controller = EngineGetController(); assert (controller); // LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem); // Check for hotness if (regionhit (x, y, w, h)) { controller->uistate.hotitem = id; if (controller->uistate.activeitem == 0 && controller->GetButtonState(MouseButtonLeft)) controller->uistate.activeitem = id; } // If nothing is selected if (controller->uistate.hotitem != 0) { controller->uistate.kbditem = controller->uistate.hotitem; } if (controller->uistate.kbditem == 0) { controller->uistate.hotitem = id; controller->uistate.kbditem = id; } // Render if (checked) { glColor3f (0.91f, 0.84f, 0.); DrawRoundedBlock (x - 2, y - 2, w + 4, h + 4); } glColor3f (0.2f, 0.2f, 0.2f); if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) { if (controller->uistate.activeitem == id) { glColor3f (0.8f, 0.8f, 0.8f); DrawRoundedBlock (x, y, w, h); } else { glColor3f (0.7f, 0.7f, 0.7f); DrawRoundedBlock (x, y, w, h); } } else { glColor3f (0.4f, 0.4f, 0.4f); DrawRoundedBlock (x, y, w, h); } // Caption if (caption != NULL) { float width, height; view = EngineGetView (); assert (view); // we have to load the font prior to measuring it SelectFont("console.ttf size=23"); view->DrawGLStringMeasure(caption, &width, &height); float xpos = x + w * 0.5f - width * 0.5f; float ypos = y + h * 0.5f - height * 0.5f; // LogDebug ("measure '%s' width = %f height = %f", caption, width, height); if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) { SelectFont("console.ttf size=23 color=#666666"); view->DrawGLString( xpos - 2.f, ypos + 2.f, caption); SelectFont("console.ttf size=23 color=#ffffff"); view->DrawGLString( xpos, ypos, caption); } else { SelectFont("console.ttf size=23 color=#444444"); view->DrawGLString( xpos - 2.f, ypos + 2.f, caption); SelectFont("console.ttf size=23 color=#b3b3b3"); view->DrawGLString( xpos, ypos, caption); } } // Mouse Logic if (controller->GetButtonState(MouseButtonLeft) == false && controller->uistate.hotitem == id && controller->uistate.activeitem == id) { controller->uistate.lastwidget = id; return true; } // Keyboard Logic if (controller->uistate.kbditem == id) { // Any unicode keys that were sent get cleared controller->uistate.last_unicode = 0; // We have to make sure, that we always clear the uistate.last_keysym // value, otherwise the same action might be repeated over and over. switch (controller->uistate.last_keysym) { case SDLK_DOWN: controller->uistate.kbditem = 0; controller->uistate.hotitem = 0; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_UP: controller->uistate.kbditem = controller->uistate.lastwidget; controller->uistate.hotitem = controller->uistate.lastwidget; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_TAB: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; break; case SDLK_RETURN: controller->uistate.last_keysym = SDLK_CLEAR; // As we (probably) exit the current set of widgets, we have to clear // the uistate.kbditem value. return true; break; default: break; } } controller->uistate.lastwidget = id; return false; } /** \brief An Edit widget that allows editing of a string consisting of one line * * \TODO [med] vertical alignment (especially of text) is a bit whacky * * \returns true if it was clicked * */ bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxlength) { controller = EngineGetController(); assert (controller); int textpos_x = x + 8; int textpos_y = y + 5; y -= 16; int w = maxlength * 16; int h = 30; // LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d key = %s", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem, convert_keycode (controller->uistate.last_keysym)); // Check for hotness if (regionhit (x, y, w, h)) { controller->uistate.hotitem = id; if (controller->uistate.activeitem == 0 && controller->GetButtonState(MouseButtonLeft)) { controller->uistate.activeitem = id; } } if (controller->uistate.kbditem == 0) { controller->uistate.hotitem = id; controller->uistate.kbditem = id; controller->uistate.last_unicode = 0; return false; } // If we have keyboard focus, we highlight the widget if ( controller->uistate.kbditem == id) { if (controller->uistate.activeitem == id) { glColor3f (0.8f, 0.8f, 0.8f); DrawBlock (x, y, w, h); } else { glColor3f (0.7f, 0.7f, 0.7f); DrawBlock (x, y, w, h); } } else { glColor3f (0.4f, 0.4f, 0.4f); DrawBlock (x, y, w, h); } // Rendering of the current value float width, height; view = EngineGetView (); assert (view); glColor3f (1.f, 1.f, 1.f); std::string text_output = text_value; if (controller->uistate.kbditem == id && SDL_GetTicks() >> 9 & 1) text_output += "_"; SelectFont("console.ttf size=23"); view->DrawGLStringMeasure(text_value.c_str(), &width, &height); view->DrawGLString(textpos_x, textpos_y, text_output.c_str()); // Keyboard Logic if (controller->uistate.kbditem == id) { switch (controller->uistate.last_keysym) { case SDLK_DOWN: controller->uistate.kbditem = 0; controller->uistate.hotitem = 0; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_UP: controller->uistate.kbditem = controller->uistate.lastwidget; controller->uistate.hotitem = controller->uistate.lastwidget; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_CLEAR: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; return false; break; case SDLK_ESCAPE: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; return false; break; case SDLK_TAB: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; return false; break; case SDLK_RETURN: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; return true; break; case SDLK_BACKSPACE: if (text_value.size() > 0) { text_value = text_value.substr(0, text_value.size() - 1); controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; return true; } break; default: // The raw input processing if (maxlength > 0 && static_cast(text_value.size()) < maxlength) { if (controller->uistate.last_unicode) { if ((controller->uistate.last_unicode & 0xFF80) == 0) { // we do not want to add special characters such as backspaces // etc. if ((controller->uistate.last_unicode & 0xFFFF) >= 0x20) text_value += controller->uistate.last_unicode & 0x7F; controller->uistate.last_unicode = 0; return true; } else { LogWarning ("Input key not supported!"); return false; } } } break; } } controller->uistate.lastwidget = id; // Mouse Logic if (controller->GetButtonState(MouseButtonLeft) == false && controller->uistate.hotitem == id && controller->uistate.activeitem == id) { controller->uistate.kbditem = id; controller->uistate.last_unicode = 0; } return false; } bool LineEditMasked (int id, int x, int y, std::string &text_value, const int &maxlength, const std::string &valid_chars) { controller = EngineGetController(); assert (controller); int textpos_x = x + 8; int textpos_y = y + 5; y -= 16; int w = maxlength * 16; int h = 30; // LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d key = %s", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem, convert_keycode (controller->uistate.last_keysym)); // Check for hotness if (regionhit (x, y, w, h)) { controller->uistate.hotitem = id; if (controller->uistate.activeitem == 0 && controller->GetButtonState(MouseButtonLeft)) { controller->uistate.activeitem = id; } } if (controller->uistate.kbditem == 0) { controller->uistate.hotitem = id; controller->uistate.kbditem = id; controller->uistate.last_unicode = 0; return false; } // If we have keyboard focus, we highlight the widget if ( controller->uistate.kbditem == id) { if (controller->uistate.activeitem == id) { glColor3f (0.8f, 0.8f, 0.8f); DrawBlock (x, y, w, h); } else { glColor3f (0.7f, 0.7f, 0.7f); DrawBlock (x, y, w, h); } } else { glColor3f (0.4f, 0.4f, 0.4f); DrawBlock (x, y, w, h); } // Rendering of the current value float width, height; view = EngineGetView (); assert (view); glColor3f (1.f, 1.f, 1.f); std::string text_output = text_value; if (controller->uistate.kbditem == id && SDL_GetTicks() >> 9 & 1) text_output += "_"; SelectFont("console.ttf size=23"); view->DrawGLStringMeasure(text_value.c_str(), &width, &height); view->DrawGLString(textpos_x, textpos_y, text_output.c_str()); // Keyboard Logic if (controller->uistate.kbditem == id) { switch (controller->uistate.last_keysym) { case SDLK_DOWN: controller->uistate.kbditem = 0; controller->uistate.hotitem = 0; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_UP: controller->uistate.kbditem = controller->uistate.lastwidget; controller->uistate.hotitem = controller->uistate.lastwidget; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_CLEAR: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; return false; break; case SDLK_ESCAPE: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; return false; break; case SDLK_TAB: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; return false; break; case SDLK_RETURN: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; controller->uistate.hotitem = 0; controller->uistate.kbditem = 0; return true; break; case SDLK_BACKSPACE: if (text_value.size() > 0) { text_value = text_value.substr(0, text_value.size() - 1); controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; return true; } break; default: // The raw input processing if (maxlength > 0 && static_cast(text_value.size()) < maxlength) { if (controller->uistate.last_unicode) { if ((controller->uistate.last_unicode & 0xFF80) == 0) { char c = controller->uistate.last_unicode & 0x7F; // we do not want to add special characters such as backspaces // etc. if (c >= 0x20 && valid_chars.find(c) != std::string::npos ) { text_value += c; } controller->uistate.last_unicode = 0; return true; } else { LogWarning ("Input key not supported!"); return false; } } } break; } } controller->uistate.lastwidget = id; // Mouse Logic if (controller->GetButtonState(MouseButtonLeft) == false && controller->uistate.hotitem == id && controller->uistate.activeitem == id) { controller->uistate.kbditem = id; controller->uistate.last_unicode = 0; } return false; } float VerticalSlider (int id, int x, int y, int w, int h, float min_value, float max_value, float &value) { const int knob_width = 16; const int knob_height = h * 2; int slider_pos = (w * value) / max_value - knob_width / 2; // Check for hotness if (regionhit (x, y + h * 0.5f - knob_height * 0.5f, w, knob_height)) { controller->uistate.hotitem = id; if (controller->uistate.activeitem == 0 && controller->GetButtonState(MouseButtonLeft)) { controller->uistate.activeitem = id; } } // If nothing is selected if (controller->uistate.hotitem != 0) { controller->uistate.kbditem = controller->uistate.hotitem; } if (controller->uistate.kbditem == 0) { controller->uistate.hotitem = id; controller->uistate.kbditem = id; } // If we have keyboard focus, we highlight the widget if ( controller->uistate.kbditem == id) { if (controller->uistate.activeitem == id) { glColor3f (0.6f, 0.6f, 0.6f); DrawBlock (x, y, w, h); glColor3f (0.8f, 0.8f, 0.8f); DrawBlock (x + slider_pos, y + h * 0.5f - knob_height * 0.5f, knob_width, knob_height); } else { glColor3f (0.6f, 0.6f, 0.6f); DrawBlock (x, y, w, h); glColor3f (0.7f, 0.7f, 0.7f); DrawBlock (x + slider_pos, y + h * 0.5f - knob_height * 0.5f, knob_width, knob_height); } } else { glColor3f (0.4f, 0.4f, 0.4f); DrawBlock (x, y, w, h); glColor3f (0.5f, 0.5f, 0.5f); DrawBlock (x + slider_pos, y + h * 0.5f - knob_height * 0.5f, knob_width, knob_height); } if (controller->uistate.kbditem == id) { switch (controller->uistate.last_keysym) { case SDLK_DOWN: controller->uistate.kbditem = 0; controller->uistate.hotitem = 0; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_UP: controller->uistate.kbditem = controller->uistate.lastwidget; controller->uistate.hotitem = controller->uistate.lastwidget; controller->uistate.last_keysym = SDLK_CLEAR; break; case SDLK_LEFT: value -= (max_value - min_value) * 0.1f; if ( value < min_value) value = min_value; controller->uistate.last_keysym = SDLK_CLEAR; return true; break; case SDLK_RIGHT: value += (max_value - min_value) * 0.1f; if ( value > max_value) value = max_value; controller->uistate.last_keysym = SDLK_CLEAR; return true; break; default: break; } } controller->uistate.lastwidget = id; if (controller->uistate.activeitem == id) { int mouse_pos[2]; controller->GetMouseScreenPosition(mouse_pos); int rel_mouse_pos = mouse_pos[0] - x; if (rel_mouse_pos < 0) rel_mouse_pos = 0; if (rel_mouse_pos > w) rel_mouse_pos = w; float mouse_value = rel_mouse_pos * max_value / static_cast(w); if (mouse_value != value) { value = mouse_value; return true; } } return false; } bool CheckKeyPress (int keycode) { if (controller->GetButtonState(keycode)) return true; return false; } bool CheckKeyPressed (int keycode) { if (controller->uistate.keypressed_set.find(keycode) != controller->uistate.keypressed_set.end()) { controller->uistate.keypressed_set.erase(keycode); return true; } return false; } }; };