/* =========================================================================== Copyright (C) 2015-2023 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code 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. OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "ui_local.h" #include Event EV_AutoIncrement ( "autoinc", EV_DEFAULT, NULL, NULL, "Used to auto increment the slider" ); Event EV_AutoDecrement ( "autodec", EV_DEFAULT, NULL, NULL, "Used to auto decrement the slider" ); Event EV_Layout_Range ( "setrange", EV_DEFAULT, "ff", "min max", "Used to set the range of a slider" ); Event EV_Layout_SliderType ( "slidertype", EV_DEFAULT, "s", "sliderType(float|integer)", "Set the slider type to be floating point or integral" ); Event EV_Layout_SliderStep ( "stepsize", EV_DEFAULT, "f", "stepsize", "Set the step size of the slider" ); Event EV_Layout_SliderThumbShader ( "thumbshader", EV_DEFAULT, "sS", "shader depressed_shader", "Set the shader of the slider's thumb" ); Event EV_Layout_SliderLeftShader ( "leftshader", EV_DEFAULT, "sS", "shader depressed_shader", "Set the shader of the slider's left arrow" ); Event EV_Layout_SliderRightShader ( "rightshader", EV_DEFAULT, "sS", "shader depressed_shader", "Set the shader of the slider's right arrow" ); CLASS_DECLARATION( UIWidget, UISlider, NULL ) { {&W_LeftMouseDown, &UISlider::Pressed }, {&W_LeftMouseDragged, &UISlider::MouseDragged }, {&W_LeftMouseUp, &UISlider::Released }, {&W_MouseExited, &UISlider::MouseExited }, {&W_MouseEntered, &UISlider::MouseEntered }, {&EV_AutoIncrement, &UISlider::AutoIncrement }, {&EV_AutoDecrement, &UISlider::AutoDecrement }, {&EV_Layout_Range, &UISlider::LayoutSetRange }, {&EV_Layout_SliderType, &UISlider::LayoutSetType }, {&EV_Layout_SliderStep, &UISlider::LayoutSetStep }, // possible FAKK2 remnants below, not used currently {&EV_Layout_SliderThumbShader, &UISlider::LayoutSliderThumbShader }, {&EV_Layout_SliderLeftShader, &UISlider::LayoutSliderLeftShader }, {&EV_Layout_SliderRightShader, &UISlider::LayoutSliderRightShader }, {NULL, NULL } }; UISlider::UISlider() { m_sliderregion = {}; m_prev_arrow_region = {}; m_next_arrow_region = {}; m_minvalue = 0.0f; m_maxvalue = 100.0f; m_value = 0.0f; m_oldvalue = 0.0f; m_sliderwidth = 10.0f; m_stepsize = 1.0f; m_slidertype = SLIDER_INTEGER; m_arrow_width = 15; m_prev_arrow_depressed = qfalse; m_next_arrow_depressed = qfalse; m_thumb_depressed = qfalse; m_initialized = qfalse; m_prev_arrow_material = uWinMan.RegisterShader("textures/menu/arrow_left"); m_prev_arrow_material_pressed = uWinMan.RegisterShader("textures/menu/arrow_left_sel"); m_next_arrow_material = uWinMan.RegisterShader("textures/menu/arrow_right"); m_next_arrow_material_pressed = uWinMan.RegisterShader("textures/menu/arrow_right_sel"); m_thumbmaterial = uWinMan.RegisterShader("textures/menu/slider_thumb"); m_thumbmaterial_pressed = uWinMan.RegisterShader("textures/menu/slider_thumb_sel"); // these are not initialized in the original game m_bar_material = NULL; // seems to be unused m_maxx = 0; m_minx = 0; } void UISlider::setThumb ( void ) { // not present in disassembly } void UISlider::Pressed ( Event *ev ) { float xpos = ev->GetFloat(1); float ypos = ev->GetFloat(2); if (m_prev_arrow_region.contains(xpos, ypos)) // click left arrow { Decrement(); m_prev_arrow_depressed = qtrue; CancelEventsOfType(&EV_AutoDecrement); Event* newEv = new Event(EV_AutoDecrement); PostEvent(newEv, 0.2f); return; } else if (m_next_arrow_region.contains(xpos, ypos)) // click right arrow { Increment(); m_next_arrow_depressed = qtrue; CancelEventsOfType(&EV_AutoIncrement); Event* newEv = new Event(EV_AutoIncrement); PostEvent(newEv, 0.2f); return; } // click thumb m_oldvalue = m_value; m_thumb_depressed = qtrue; if (xpos <= m_minx) { m_value = m_minvalue; } else if (xpos >= m_maxx) { m_value = m_maxvalue; } else { // calculate value based on thumb position m_value = (xpos - m_minx) / (m_maxx - m_minx) * (m_maxvalue - m_minvalue) + m_minvalue; } UpdateData(); } void UISlider::Released ( Event *ev ) { m_prev_arrow_depressed = qfalse; m_next_arrow_depressed = qfalse; m_thumb_depressed = qfalse; } void UISlider::Increment ( void ) { m_value = Q_clamp_float(m_value + m_stepsize, m_minvalue, m_maxvalue); UpdateData(); } void UISlider::Decrement ( void ) { m_value = Q_clamp_float(m_value - m_stepsize, m_minvalue, m_maxvalue); UpdateData(); } void UISlider::AutoIncrement ( Event *ev ) { if (m_next_arrow_depressed) { if (m_next_arrow_region.contains(uid.mouseX, uid.mouseY)) { Increment(); Event *event = new Event(EV_AutoIncrement); PostEvent(event, 0.1f); } else { Released(NULL); } } } void UISlider::AutoDecrement ( Event *ev ) { if (m_prev_arrow_depressed) { if (m_prev_arrow_region.contains(uid.mouseX, uid.mouseY)) { Decrement(); Event *event = new Event(EV_AutoDecrement); PostEvent(event, 0.1f); } else { Released(NULL); } } } void UISlider::MouseExited ( Event *ev ) { if (m_thumb_depressed) { m_value = m_oldvalue; UpdateData(); } m_thumb_depressed = qfalse; m_prev_arrow_depressed = qfalse; m_next_arrow_depressed = qfalse; } void UISlider::MouseDragged ( Event *ev ) { float xpos = ev->GetFloat(1); float ypos = ev->GetFloat(2); if (m_thumb_depressed) { if (xpos <= m_minx) { m_value = m_minvalue; } else if (xpos >= m_maxx) { m_value = m_maxvalue; } else { // calculate value based on position m_value = (xpos - m_minx) / (m_maxx - m_minx) * (m_maxvalue - m_minvalue) + m_minvalue; } UpdateData(); return; } if ((!m_prev_arrow_depressed && m_prev_arrow_region.contains(xpos, ypos)) || (!m_next_arrow_depressed && m_next_arrow_region.contains(xpos, ypos))) { Pressed(ev); } } void UISlider::MouseEntered ( Event *ev ) { int buttons = ev->GetInteger(3); uWinMan.ActivateControl(this); if (buttons & 1) // left mouse button { Pressed(ev); } } void UISlider::UpdateData ( void ) { if (m_cvarname.length()) { if (m_slidertype == SLIDER_INTEGER) { UI_SetCvarInt(m_cvarname, m_value); } else { UI_SetCvarFloat(m_cvarname, m_value); } } if (m_command.length()) { char text[32]; if (m_slidertype == SLIDER_INTEGER) { Q_snprintf(text, 32, "%d", (int)m_value); } else { Q_snprintf(text, 32, "%.3f", m_value); } m_command.append(' '); m_command.append(text); m_command.append('\n'); Cbuf_AddText(m_command); } } void UISlider::UpdateUIElement ( void ) { m_value = UI_GetCvarFloat(m_cvarname, 0.0f); } void UISlider::LayoutSetRange ( Event *ev ) { float min = ev->GetFloat(1); float max = ev->GetFloat(2); setRange(min, max); } void UISlider::LayoutSetType ( Event *ev ) { str type = ev->GetString(1); if (!Q_stricmp(type, "float")) { setType(SLIDER_FLOAT); } else if (!Q_stricmp(type, "integer")) { setType(SLIDER_INTEGER); } } void UISlider::LayoutSetStep ( Event *ev ) { m_stepsize = ev->GetFloat(1); } void UISlider::LayoutSliderBGShader ( Event *ev ) { // not present in disassembly } void UISlider::LayoutSliderThumbShader ( Event *ev ) { str shader = ev->GetString(1); m_thumbmaterial = uWinMan.RegisterShader(shader); if (ev->NumArgs() <= 1) { m_thumbmaterial_pressed = m_thumbmaterial; } else { str pressedShader = ev->GetString(2); m_thumbmaterial_pressed = uWinMan.RegisterShader(pressedShader); } m_sliderwidth = uii.Rend_GetShaderWidth(m_thumbmaterial->GetMaterial()); m_initialized = qfalse; } void UISlider::LayoutSliderLeftShader ( Event *ev ) { str shader = ev->GetString(1); m_prev_arrow_material = uWinMan.RegisterShader(shader); if (ev->NumArgs() <= 1) { m_prev_arrow_material_pressed = m_prev_arrow_material; } else { str pressedShader = ev->GetString(2); m_prev_arrow_material_pressed = uWinMan.RegisterShader(pressedShader); } m_arrow_width = uii.Rend_GetShaderWidth(m_prev_arrow_material->GetMaterial()); m_initialized = qfalse; } void UISlider::LayoutSliderRightShader ( Event *ev ) { str shader = ev->GetString(1); m_next_arrow_material = uWinMan.RegisterShader(shader); if (ev->NumArgs() <= 1) { m_next_arrow_material_pressed = m_next_arrow_material; } else { str pressedShader = ev->GetString(2); m_next_arrow_material_pressed = uWinMan.RegisterShader(pressedShader); } m_arrow_width = uii.Rend_GetShaderWidth(m_next_arrow_material->GetMaterial()); m_initialized = qfalse; } void UISlider::FrameInitialized ( void ) { float cframeX = m_clippedframe.pos.x; float cframeY = m_clippedframe.pos.y; float cframeWidth = m_clippedframe.size.width; float frameHeight = m_frame.size.height; float scaledArrowWidth = m_arrow_width * m_vVirtualScale[0]; float scaledThumbWidth = m_sliderwidth * m_vVirtualScale[0]; m_prev_arrow_region.pos.x = cframeX; m_prev_arrow_region.pos.y = cframeY; m_prev_arrow_region.size.height = frameHeight; m_prev_arrow_region.size.width = scaledArrowWidth; m_next_arrow_region.pos.x = cframeX + cframeWidth - scaledArrowWidth; m_next_arrow_region.pos.y = cframeY; m_next_arrow_region.size.height = frameHeight; m_next_arrow_region.size.width = scaledArrowWidth; m_sliderregion.pos.x = cframeX + scaledArrowWidth; m_sliderregion.pos.y = cframeY; m_sliderregion.size.height = frameHeight; // Bug in original game, width calculation was wrong: //m_sliderregion.size.width = scaledThumbWidth - scaledArrowWidth; m_sliderregion.size.width = cframeWidth - scaledArrowWidth * 2; // width of the "dead zone" of the slider where the thumb cannot reach float unreachableEdgeWidth = scaledArrowWidth + scaledThumbWidth * 0.5f; m_minx = cframeX + unreachableEdgeWidth; m_maxx = cframeX + m_frame.size.width - unreachableEdgeWidth; } void UISlider::Draw ( void ) { if (!m_initialized) { m_initialized = qtrue; FrameInitialized(); } UpdateUIElement(); // current horizontal position of the thumb float sliderpos = (m_value - m_minvalue) / (m_maxvalue - m_minvalue); sliderpos = Q_clamp_float(sliderpos, 0.0f, 1.0f); float scaledArrowWidth = m_arrow_width * m_vVirtualScale[0]; float scaledThumbWidth = m_sliderwidth * m_vVirtualScale[0]; float x = (m_maxx - m_minx) * sliderpos + scaledArrowWidth; // draw the thumb if (m_thumbmaterial) { UIReggedMaterial* material = m_thumb_depressed ? m_thumbmaterial_pressed : m_thumbmaterial; uii.Rend_DrawPicStretched(x, 0.0f, scaledThumbWidth, m_frame.size.height, 0.0f, 0.0f, 1.0f, 1.0f, material->GetMaterial()); } else { Draw3DBox(x, 0.0f, scaledThumbWidth, m_frame.size.height, qfalse, m_border_color, m_local_alpha); } if (m_prev_arrow_material == m_prev_arrow_material_pressed) { Draw3DBox(0.0f, 0.0f, scaledArrowWidth, m_frame.size.height, m_prev_arrow_depressed, m_border_color, m_local_alpha); uii.Rend_DrawPicStretched( 2.0f, 2.0f, scaledArrowWidth - 4.0f, m_frame.size.height - 4.0f, 0.0f, 0.0f, 1.0f, 1.0f, m_prev_arrow_material->GetMaterial() ); } else { uihandle_t material; if (m_prev_arrow_depressed) { material = m_prev_arrow_material_pressed->GetMaterial(); } else { material = m_prev_arrow_material->GetMaterial(); } uii.Rend_DrawPicStretched(0.0f, 0.0f, scaledArrowWidth, m_frame.size.height, 0.0f, 0.0f, 1.0f, 1.0f, material); } if (m_next_arrow_material == m_next_arrow_material_pressed) { Draw3DBox(m_frame.size.width - scaledArrowWidth, 0.0f, scaledArrowWidth, m_frame.size.height, m_next_arrow_depressed, m_border_color, m_local_alpha); uii.Rend_DrawPicStretched( m_frame.size.width - scaledArrowWidth + 2.0f, 2.0f, scaledArrowWidth - 4.0f, m_frame.size.height - 4.0f, 0.0f, 0.0f, 1.0f, 1.0f, m_next_arrow_material->GetMaterial() ); } else { uihandle_t material; if (m_next_arrow_depressed) { material = m_next_arrow_material_pressed->GetMaterial(); } else { material = m_next_arrow_material->GetMaterial(); } uii.Rend_DrawPicStretched( m_frame.size.width - scaledArrowWidth, 0.0f, scaledArrowWidth, m_frame.size.height, 0.0f, 0.0f, 1.0f, 1.0f, material ); } } void UISlider::setType ( slidertype_t type ) { m_slidertype = type; } qboolean UISlider::KeyEvent ( int key, unsigned int time ) { if (key == K_RIGHTARROW) { Increment(); } else if (key == K_LEFTARROW) { Decrement(); } else { return qfalse; } return qtrue; } void UISlider::setRange ( float min, float max ) { m_minvalue = min; m_maxvalue = max; } void UISlider::setStep ( float value ) { m_stepsize = value; }