#include <includes.h>
//AARRGGBB
struct button_color
{
DWORD button;
DWORD border;
DWORD text;
button_color(){}
~button_color(){}
button_color(DWORD color_button,DWORD color_border,DWORD color_text)
: button(color_button),border(color_border),text(color_text)
{}
};
class menu;
class button
{
public:
std::map<std::string,std::shared_ptr<menu>> Submenu;
std::string selected_submenu_item;
std::string text_;
button_color appearance_;
button_color highlighted_;
button_color mousedown_;
bool clickable_;
bool highlight_;
unsigned short vkkey_;
bool holding_;
int fontid_;
button()
{
SetOnClickCode([]{});
SetOnRMBHoldCode([]{});
SetOnRMBHoldReleasedCode([]{});
SetOnRMBReleasedCode([]{});
holding_=false;
}
~button()
{
Submenu.clear();
}
button(std::string button_text,button_color &appearance, button_color &highlighted, button_color &mousedown, bool clickable, bool highlight, int fontid = 1, unsigned short vkkey = VK_LBUTTON)
: text_(button_text), appearance_(appearance), highlighted_(highlighted), clickable_(clickable), highlight_(highlight), mousedown_(mousedown), vkkey_(vkkey), fontid_(fontid)
{
SetOnClickCode([]{});
SetOnRMBHoldCode([]{});
SetOnRMBHoldReleasedCode([]{});
SetOnRMBReleasedCode([]{});
holding_=false;
}
std::function<void(void)> OnClick;
template<class Functor> void SetOnClickCode(Functor&& f)
{
OnClick = std::forward<Functor>(f);
}
std::function<void(void)> OnRMBHold;
template<class Functor> void SetOnRMBHoldCode(Functor&& f)
{
OnRMBHold = std::forward<Functor>(f);
}
std::function<void(void)> OnRMBHoldReleased;
template<class Functor> void SetOnRMBHoldReleasedCode(Functor&& f)
{
OnRMBHoldReleased = std::forward<Functor>(f);
}
std::function<void(void)> OnRMBReleased;
template<class Functor> void SetOnRMBReleasedCode(Functor&& f)
{
OnRMBReleased = std::forward<Functor>(f);
}
void SetInfo(std::string button_text,button_color &appearance, button_color &highlighted, button_color &mousedown, bool clickable, bool highlight, int fontid = 1, unsigned short vkkey = VK_LBUTTON)
{
text_.assign(button_text);
appearance_ = appearance;
highlighted_ = highlighted;
clickable_ = clickable;
highlight_ = highlight;
mousedown_ = mousedown;
vkkey_ = vkkey;
fontid_ = fontid;
}
void SetSelected()
{
appearance_ = selected_appearance;
highlighted_ = selected_highlighted;
mousedown_ = selected_mousedown;
}
void SetUnselected()
{
appearance_ = default_appearance;
highlighted_ = default_highlighted;
mousedown_ = default_mousedown;
}
float GetLenght()
{
return DirectXFont::Access(fontid_)->DrawLength(text_.c_str());
}
float GetHeight()
{
return DirectXFont::Access(fontid_)->DrawHeight();
}
void Display(float btn_x, float btn_y, float btn_w, float btn_h)
{
if(!Submenu.empty())
{
if(holding_)
{
if(Keys(VK_RBUTTON).Down)
{
OnRMBHold();//dunno, diplay submenu
}else
if(Keys(VK_RBUTTON).Released)
{
OnRMBHoldReleased();//select submenu item here
holding_ = false;
}
}
}
if(!IsPointInArea(cursorPos.x,cursorPos.y,btn_x,btn_y,btn_w,btn_h))
{
render->D3DBoxBorder(btn_x,btn_y,btn_w,btn_h,appearance_.border,appearance_.button);
DirectXFont::Access(fontid_)->Print(btn_x+((btn_w-GetLenght())/2.0f),btn_y+((btn_h-GetHeight())/2.0f),appearance_.text,text_.c_str(),true);
return;
}
else
{
if(!Submenu.empty())
{
if(Keys(VK_RBUTTON).Pressed)
{
holding_ = true;
}
}
if(Keys(VK_RBUTTON).Released)
{
OnRMBReleased();
}
}
if(!clickable_)
{
render->D3DBoxBorder(btn_x,btn_y,btn_w,btn_h,(highlight_)? highlighted_.border : appearance_.border,(highlight_)? highlighted_.button : appearance_.button);
DirectXFont::Access(fontid_)->Print(btn_x+((btn_w-GetLenght())/2.0f),btn_y+((btn_h-GetHeight())/2.0f),(highlight_)? highlighted_.text : appearance_.text,text_.c_str(),true);
return;
}
if(!OldKeys(vkkey_).Down)
{
render->D3DBoxBorder(btn_x,btn_y,btn_w,btn_h,(highlight_)? highlighted_.border : appearance_.border,(highlight_)? highlighted_.button : appearance_.button);
DirectXFont::Access(fontid_)->Print(btn_x+((btn_w-GetLenght())/2.0f),btn_y+((btn_h-GetHeight())/2.0f),(highlight_)? highlighted_.text : appearance_.text,text_.c_str(),true);
return;
}
if(!Keys(vkkey_).Released)
{
render->D3DBoxBorder(btn_x,btn_y,btn_w,btn_h, mousedown_.border,mousedown_.button);
DirectXFont::Access(fontid_)->Print(btn_x+((btn_w-GetLenght())/2.0f),btn_y+((btn_h-GetHeight())/2.0f), mousedown_.text,text_.c_str(),true);
return;
}
OnClick();
return;
}
void DisplayEx(float btn_x, float btn_y, float btn_w, float btn_h)
{
Display(btn_x, btn_y,GetLenght()+btn_w, GetHeight()+btn_h);
}
};
template<class T = int> struct slider_point
{
slider_point(){}
slider_point(T x, T y): X(x), Y(y){}
~slider_point(){}
T X;
T Y;
};
template<class T = int> struct slider_values
{
slider_values(){}
slider_values(slider_point<T> min_values, slider_point<T> max_values, slider_point<T> default_values)
:minimum(min_values),maximum(max_values),default(default_values),current(default_values)
{}
~slider_values(){}
slider_point<T> minimum;
slider_point<T> maximum;
slider_point<T> default;
slider_point<T> current;
};
struct slider_box
{
slider_box(){}
~slider_box(){}
slider_box(slider_point<float> position, slider_point<float> size, DWORD color_inner, DWORD color_outline):
pos(position), size(size), color_inner(color_inner), color_outline(color_outline)
{}
slider_point<float> pos;
slider_point<float> size;
DWORD color_inner;
DWORD color_outline;
void draw()
{
return render->D3DBoxBorder(pos.X,pos.Y,size.X,size.Y,color_outline,color_inner);
}
};
struct slider_button
{
slider_button(){}
slider_button(slider_point<float> size, slider_point<float> pos, button_color _appearance = default_appearance, button_color _highlight = default_highlighted, button_color _mousedown = default_mousedown)
: size(size), pos(pos), appearance(_appearance), highlight(_highlight), mousedown(_mousedown)
{}
~slider_button(){}
button_color appearance;
button_color highlight;
button_color mousedown;
slider_point<float> size;
slider_point<float> pos;
bool IsPointOnThis(slider_point<float> point)
{
return IsPointInArea(point.X,point.Y,pos.X,pos.Y,size.X,size.Y);
}
bool IsMouseOnThis()
{
return IsPointOnThis(slider_point<float>((float)cursorPos.x,(float)cursorPos.y));
}
void SetPos(slider_point<float> point)
{
pos = point;
}
void SetCenter(slider_point<float> point)
{
pos.X = (point.X - (size.X/2.0f));
pos.Y = (point.Y - (size.Y/2.0f));
}
void SetTopLeft(slider_point<float> point)
{
pos = point;
}
void SetTopRight(slider_point<float> point)
{
pos.Y = point.Y;
pos.X = point.X-size.X;
}
void SetBottomLeft(slider_point<float> point)
{
pos.X = point.X;
pos.Y = point.Y-size.Y;
}
void SetBottomRight(slider_point<float> point)
{
pos.X = point.X-size.X;
pos.Y = point.Y-size.Y;
}
float GetLeft()
{
return pos.X;
}
float GetRight()
{
return pos.X+size.X;
}
float GetTop()
{
return pos.Y;
}
float GetBottom()
{
return pos.Y+size.Y;
}
slider_point<float> GetCenter()
{
return slider_point<float>(pos.X+(size.X/2.0f),pos.Y+(size.Y/2.0f));
}
void draw()
{
if(!IsPointInArea(cursorPos.x,cursorPos.y,pos.X,pos.Y,size.X,size.Y))
return render->D3DBoxBorder(pos.X,pos.Y,size.X,size.Y,appearance.border,appearance.button);
if(Keys(VK_LBUTTON).Down)
return render->D3DBoxBorder(pos.X,pos.Y,size.X,size.Y,mousedown.border,mousedown.button);
return render->D3DBoxBorder(pos.X,pos.Y,size.X,size.Y,highlight.border,highlight.button);
}
};
//not only left/right or up/down, but also both at once!
template<class T = int> class slider
{
private:
slider_values<T> data;
bool Dragging;
slider_point<float> DragMouseStart;
slider_point<float> DragSliderStart;
public:
bool Show;
bool LockXMovement;
bool LockYMovement;
slider_box box;
slider_button sliding;
slider(){Show = true;Dragging=false;}
slider(slider_point<float> pos, slider_point<float> size, slider_point<T> minimum, slider_point<T> maximum, slider_point<T> default, slider_point<float> slider_size, DWORD _color_inner, DWORD _color_outline, bool LockX = false, bool LockY = false, button_color _appearance = default_appearance, button_color _highlighted = default_highlighted, button_color _mousedown = default_mousedown)
: data(minimum,maximum,default),LockXMovement(LockX),LockYMovement(LockY),box(pos,size,_color_inner,_color_outline), sliding(slider_size,pos,_appearance,_highlighted,_mousedown)
{
Dragging = false;
if(sliding.size.Y > box.size.Y)
{
sliding.size.Y = box.size.Y;
LockYMovement = true;
}
if(sliding.size.X > box.size.X)
{
sliding.size.X = box.size.X;
LockXMovement = true;
}
sliding.SetCenter( slider_point<float>( pos.X+( (float)(((float)(default.X-minimum.X))/((float)(maximum.X-minimum.X))) *size.X), pos.Y+( (float)(((float)(default.Y-minimum.Y))/((float)(maximum.Y-minimum.Y))) *size.Y)));
if(sliding.pos.X < box.pos.X)
sliding.pos = slider_point<float>( box.pos.X , sliding.pos.Y );
if(sliding.GetRight() > (box.pos.X + box.size.X))
sliding.SetTopRight(slider_point<float>( box.pos.X+box.size.X , sliding.pos.Y ));
if(sliding.pos.Y < box.pos.Y)
sliding.pos = slider_point<float>( sliding.pos.X , box.pos.Y );
if(sliding.GetBottom() > (box.pos.Y+box.size.Y))
sliding.SetBottomLeft(slider_point<float>( sliding.pos.X , box.pos.Y+box.size.Y ));
Show = true;
}
slider_values<T> GetValue()
{
return data.current;
}
void SetPosByValue(slider_point<T> value)
{
sliding.pos.X = (box.pos.X + ((box.size.X-sliding.size.X)*(((float)(value.X-data.minimum.X))/((float)(data.maximum.X-data.minimum.X)))));
sliding.pos.Y = (box.pos.Y + ((box.size.Y-sliding.size.Y)*(((float)(value.Y-data.minimum.Y))/((float)(data.maximum.Y-data.minimum.Y)))));
}
void SetPosByValueX(T value)
{
sliding.pos.X = (box.pos.X + ((box.size.X-sliding.size.X)*(((float)(value-data.minimum.X))/((float)(data.maximum.X-data.minimum.X)))));
}
void SetPosByValueY(T value)
{
sliding.pos.Y = (box.pos.Y + ((box.size.Y-sliding.size.Y)*(((float)(value-data.minimum.Y))/((float)(data.maximum.Y-data.minimum.Y)))));
}
void Process()
{
if(Dragging)
{
if(Keys(VK_LBUTTON).Up)
{
Dragging = false;
}
if(!LockXMovement)
{
float mousepos = (float)cursorPos.x;
sliding.pos = slider_point<float>( DragSliderStart.X+(mousepos-DragMouseStart.X) , sliding.pos.Y );
if(sliding.pos.X < box.pos.X)
sliding.pos = slider_point<float>( box.pos.X , sliding.pos.Y );
if(sliding.GetRight() > (box.pos.X+box.size.X))
sliding.SetTopRight(slider_point<float>( box.pos.X+box.size.X , sliding.pos.Y ));
data.current.X = data.minimum.X + (T)((float)(data.maximum.X-data.minimum.X) * ((sliding.pos.X-box.pos.X)/(box.size.X-sliding.size.X)));
}
if(!LockYMovement)
{
float mousepos = (float)cursorPos.y;
sliding.pos = slider_point<float>( sliding.pos.X , DragSliderStart.Y+(mousepos-DragMouseStart.Y) );
if(sliding.pos.Y < box.pos.Y)
sliding.pos = slider_point<float>( sliding.pos.X , box.pos.Y );
if(sliding.GetBottom() > (box.pos.Y+box.size.Y))
sliding.SetBottomLeft(slider_point<float>( sliding.pos.X , box.pos.Y+box.size.Y ));
data.current.Y = data.minimum.Y + (T)((float)(data.maximum.Y-data.minimum.Y) * ((sliding.pos.Y-box.pos.Y)/(box.size.Y-sliding.size.Y)));
}
DirectXFont::Access(4)->Print(0.0f,50.0f,0xFF00FF00,string_format("[%04d,%04d]",(int)data.current.X,(int)data.current.Y).c_str());
}else
if(Keys(VK_LBUTTON).Pressed)
{
if(sliding.IsMouseOnThis())
{
Dragging = true;
DragMouseStart.X = (float)cursorPos.x;
DragMouseStart.Y = (float)cursorPos.y;
DragSliderStart = sliding.pos;
}
}
box.draw();
sliding.draw();
}
~slider()
{
}
};
class menu
{
public:
typedef boost::variant<std::shared_ptr<slider<int>>,std::shared_ptr<slider<float>>,std::shared_ptr<slider<double>>,std::shared_ptr<slider<char>>> slider_type;
std::map<std::string,slider_type> Sliders;
std::map<std::string,std::shared_ptr<button>> Buttons;
float pos_top_;
float pos_left_;
float spacing_;
float width_;
float height_;
DWORD BoxColor;
DWORD BorderColor;
bool horizontal_;
bool central_;
bool manual_draw_;
std::string name_;
menu()
{
pos_top_ = 0.0f;
pos_left_ = 0.0f;
spacing_ = 5.0f;
width_ = 0.0f;
height_ = 0.0f;
BoxColor = 0xFF330088;
BorderColor = 0xFF338800;
manual_draw_ = false;
horizontal_ = true;
central_ = false;
SetOnProcessExtraCode([]{});
}
~menu()
{
Buttons.clear();
Sliders.clear();
}
void SetProcessParameters(bool horizontal, bool central, bool manual_draw = false)
{
horizontal_ = horizontal;
central_ = central;
manual_draw_ = manual_draw;
}
std::function<void(void)> OnProcessExtra;
template<class Functor> void SetOnProcessExtraCode(Functor&& f)
{
OnProcessExtra = std::forward<Functor>(f);
}
void SetupWidth()
{
width_ = 0.0f;
height_ = 0.0f;
for(auto i = Buttons.begin(); i != Buttons.end(); ++i)
{
if(width_ < i->second->GetLenght())
{
width_ = i->second->GetLenght();
}
if(height_ < i->second->GetHeight())
{
height_ = i->second->GetHeight();
}
}
}
std::shared_ptr<button>& AddButton(std::string name,std::shared_ptr<button> button)
{
Buttons.emplace(name,button);
SetupWidth();
return Buttons[name];
}
void RemoveButton(std::shared_ptr<button> button)
{
for(auto i = Buttons.begin(); i != Buttons.end(); ++i)
{
if(i->second == button)
{
Buttons.erase(i);
break;
}
}
SetupWidth();
}
void RemoveButton(std::string name)
{
for(auto i = Buttons.begin(); i != Buttons.end(); ++i)
{
if(i->first == name)
{
Buttons.erase(i);
break;
}
}
SetupWidth();
}
float GetWidth()
{
if(!horizontal_)
return (width_ + (spacing_ * 4.0f));
if(Buttons.empty())
return spacing_;
return spacing_ + ((width_ + (3.0f * spacing_)) * ((float)Buttons.size()));
}
float GetHeight()
{
if(horizontal_)
return (3.0f * spacing_) + height_;
if(Buttons.empty())
return spacing_;
return spacing_ + (((float)Buttons.size())*spacing_)+((spacing_ + height_)*((float)Buttons.size()));
}
void Process()
{
OnProcessExtra();
if(!Sliders.empty())
{
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
case 0://slider<int>
{
boost::get<std::shared_ptr<slider<int>>>(i->second)->Process();
break;
}
case 1://slider<float>
{
boost::get<std::shared_ptr<slider<float>>>(i->second)->Process();
break;
}
case 2://slider<double>
{
boost::get<std::shared_ptr<slider<double>>>(i->second)->Process();
break;
}
case 3://slider<char>
{
boost::get<std::shared_ptr<slider<char>>>(i->second)->Process();
break;
}
}
}
}
if(!manual_draw_)
{
if(!Buttons.empty())
{
if(central_)
{
Pos((((float)ScreenX)/2.0f)-(GetWidth()/2.0f),(((float)ScreenY)/2.0f)-(GetHeight()/2.0f));
}
if(!horizontal_)
{
render->D3DBoxBorder(pos_left_,pos_top_,(width_ + (spacing_ * 4.0f)),spacing_ + (((float)Buttons.size())*spacing_)+((spacing_ + height_)*((float)Buttons.size())), BorderColor,BoxColor);
int x = 0;
for(auto i = Buttons.begin(); i != Buttons.end(); ++i)
{
i->second->Display(pos_left_ + spacing_,pos_top_ + spacing_ + spacing_*((float)x) + ((spacing_ + height_)*(float)x),(width_ + (2.0f * spacing_)),height_+spacing_);
++x;
}
}
else
{
render->D3DBoxBorder(pos_left_,pos_top_,spacing_ + ((width_ + (3.0f * spacing_)) * ((float)Buttons.size())),(3.0f * spacing_) + height_, BorderColor,BoxColor);
int x = 0;
for(auto i = Buttons.begin(); i != Buttons.end(); ++i)
{
i->second->Display(pos_left_ + spacing_ + ((width_ + (3.0f * spacing_)) * ((float)x)),pos_top_ + spacing_ ,(width_ + (2.0f * spacing_)),height_+spacing_);
++x;
}
}
}
}
}
void Pos(float x, float y)
{
pos_left_ = x;
pos_top_ = y;
}
void Colors(DWORD border,DWORD background)
{
BorderColor = border;
BoxColor = background;
}
};