# pragma region License
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Copyright 2024 Joshua Sigona < sigonasr2 @ gmail . com >
Redistribution and use in source and binary forms , with or without modification ,
are permitted provided that the following conditions are met :
1. Redistributions or derivations of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice . This list of conditions and the following disclaimer must be
reproduced in the documentation and / or other materials provided with the distribution .
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED
TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
Portions of this software are copyright © 2024 The FreeType
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
*/
# pragma endregion
# pragma once
# include "MenuComponent.h"
# include "AdventuresInLestoria.h"
# include "Error.h"
INCLUDE_game
INCLUDE_GFX
class Slider : public MenuComponent {
bool dragging = false ;
float & val ; //0.f-1.f
float prevVal = 0.f ;
std : : pair < int , int > minMaxDisplayValues ;
std : : function < void ( float ) > onValChange ;
public :
//A slider component with a right-aligned label to the left, and a value display on the right-hand side.
//valRef is between 0-1.
//minMaxDisplayValues are visual only.
inline Slider ( const geom2d : : rect < float > & rect , const std : : string & label , float & valRef , const std : : pair < int , int > & minMaxDisplayValues = { 0 , 100 } , const ButtonAttr & attributes = ButtonAttr : : NONE )
: Slider ( rect , label , valRef , [ ] ( float val ) { } , minMaxDisplayValues , attributes ) { }
inline Slider ( const geom2d : : rect < float > & rect , const std : : string & label , float & valRef , std : : function < void ( float ) > onValChange , const std : : pair < int , int > & minMaxDisplayValues = { 0 , 100 } , const ButtonAttr & attributes = ButtonAttr : : NONE )
: MenuComponent ( rect , label , DO_NOTHING , attributes ) , val ( valRef ) , minMaxDisplayValues ( minMaxDisplayValues ) , prevVal ( valRef ) , onValChange ( onValChange ) {
if ( minMaxDisplayValues . first > minMaxDisplayValues . second ) ERR ( std : : format ( " WARNING! The first value provided for component {} is greater than the second! {} > {} " , name , minMaxDisplayValues . first , minMaxDisplayValues . second ) ) ;
}
//Gets the absolute X position of the slider.
float GetSliderX ( ) {
return rect . pos . x + 4 + ( rect . size . x - 8 ) * val ;
}
float GetSliderVal ( float x ) {
return ( x - rect . pos . x + 4 ) / ( rect . size . x - 8 ) ;
}
int GetValueDisplay ( ) {
int diff = minMaxDisplayValues . second - minMaxDisplayValues . first ;
return diff * val + minMaxDisplayValues . first ;
}
inline virtual void Update ( AiL * game ) override final {
MenuComponent : : Update ( game ) ;
if ( disabled ) return ;
if ( hovered & & ( game - > KEY_CONFIRM . Pressed ( ) | | game - > GetMouse ( Mouse : : LEFT ) . bPressed ) ) {
dragging = true ;
}
if ( dragging ) {
if ( Menu : : UsingMouseNavigation ( ) ) {
val = GetSliderVal ( game - > GetMouseX ( ) - Menu : : menus [ parentMenu ] - > pos . x - 8 ) ;
} else {
if ( game - > KEY_LEFT . Held ( ) ) {
val - = 1.f * game - > GetElapsedTime ( ) ;
}
if ( game - > KEY_RIGHT . Held ( ) ) {
val + = 1.f * game - > GetElapsedTime ( ) ;
}
}
val = std : : clamp ( val , 0.f , 1.f ) ;
if ( abs ( prevVal - val ) > = 0.01f ) {
onValChange ( int ( val * 100 ) / 100.f ) ;
prevVal = val ;
}
if ( game - > KEY_CONFIRM . Released ( ) | | game - > GetMouse ( Mouse : : LEFT ) . bReleased ) {
dragging = false ;
}
}
}
inline virtual void DrawDecal ( ViewPort & window , bool focused ) override final {
Pixel backCol = PixelLerp ( Menu : : themes [ Menu : : themeSelection ] . GetButtonCol ( ) , Menu : : themes [ Menu : : themeSelection ] . GetHighlightCol ( ) , hoverEffect / " ThemeGlobal.HighlightTime " _F ) ;
if ( grayedOut ) {
backCol = DARK_GREY ;
}
vf2d labelSize = game - > GetTextSizeProp ( label ) ;
window . DrawShadowStringPropDecal ( vf2d { rect . pos . x - 8 - labelSize . x , rect . pos . y + rect . size . y / 2 - labelSize . y / 2 } , label ) ;
window . FillRectDecal ( vf2d { rect . pos . x + 4 , rect . pos . y + rect . size . y / 2 - 1 } + vf2d { 0.f , 1.f } , { rect . size . x - 8 , 2 } , BLACK ) ;
window . FillRectDecal ( vf2d { rect . pos . x + 4 , rect . pos . y + rect . size . y / 2 - 1 } , { rect . size . x - 8 , 2 } ) ;
window . DrawRotatedDecal ( vf2d { GetSliderX ( ) , rect . pos . y + rect . size . y / 2 } , GFX [ " circle.png " ] . Decal ( ) , 0.f , vf2d ( GFX [ " circle.png " ] . Sprite ( ) - > Size ( ) ) / 2.f , { 5.f , 5.f } , BLACK ) ;
window . DrawRotatedDecal ( vf2d { GetSliderX ( ) , rect . pos . y + rect . size . y / 2 } , GFX [ " circle.png " ] . Decal ( ) , 0.f , vf2d ( GFX [ " circle.png " ] . Sprite ( ) - > Size ( ) ) / 2.f , { 4.f , 4.f } , backCol ) ;
vf2d valDisplayTextSize = game - > GetTextSize ( std : : to_string ( minMaxDisplayValues . second ) ) ;
vf2d valDisplayOutlinePos = { rect . pos . x + rect . size . x + 2 , rect . pos . y } ;
Pixel valueBackCol = Menu : : themes [ Menu : : themeSelection ] . GetButtonCol ( ) ;
Pixel valueTextCol = WHITE ;
if ( grayedOut ) {
valueBackCol = GREY ;
valueTextCol = VERY_DARK_GREY ;
}
window . FillRectDecal ( valDisplayOutlinePos + vf2d { 7.f , 1.f } , valDisplayTextSize + vf2d { 4 , 4 } , valueBackCol ) ;
window . DrawShadowStringDecal ( valDisplayOutlinePos + vf2d { 9.f , 3.f } , std : : format ( " {:03} " , GetValueDisplay ( ) ) , valueTextCol ) ;
}
} ;