#pragma once #include "../gui/CIntObject.h" #include "../gui/SDL_Extensions.h" #include "../../lib/FunctionList.h" struct SDL_Surface; struct Rect; class CAnimImage; class CLabel; class CAnimation; class CDefHandler; namespace config { struct ButtonInfo; } /* * Buttons.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ class ClickableArea : public CIntObject //TODO: derive from LRCLickableArea? Or somehow use its right-click/hover data? { CFunctionList callback; CIntObject * area; protected: void onClick(); public: ClickableArea(CIntObject * object, CFunctionList callback); void addCallback(std::function callback); void setArea(CIntObject * object); void clickLeft(tribool down, bool previousState) override; }; /// Typical Heroes 3 button which can be inactive or active and can /// hold further information if you right-click it class CButton : public CKeyShortcut { CFunctionList callback; public: enum ButtonState { NORMAL=0, PRESSED=1, BLOCKED=2, HIGHLIGHTED=3 }; private: std::vector imageNames;//store list of images that can be used by this button size_t currentImage; ButtonState state;//current state of button from enum std::array stateToIndex; // mapping of button state to index of frame in animation std::array hoverTexts; //texts for statusbar, if empty - first entry will be used std::string helpBox; //for right-click help CAnimImage * image; //image for this button CIntObject * overlay;//object-overlay, can be null protected: void onButtonClicked(); // calls callback void update();//to refresh button after image or text change // internal method to change state. Public change can be done only via block() void setState(ButtonState newState); ButtonState getState(); public: bool actOnDown,//runs when mouse is pressed down over it, not when up hoverable,//if true, button will be highlighted when hovered (e.g. main menu) soundDisabled; // if set, button will have 1-px border around it with this color boost::optional borderColor; /// adds one more callback to on-click actions void addCallback(std::function callback); /// adds overlay on top of button image. Only one overlay can be active at once void addOverlay(CIntObject * newOverlay); void addTextOverlay(const std::string &Text, EFonts font, SDL_Color color = Colors::WHITE); void addImage(std::string filename); void addHoverText(ButtonState state, std::string text); void setImageOrder(int state1, int state2, int state3, int state4); void block(bool on); /// State modifiers bool isBlocked(); bool isHighlighted(); /// Constructor CButton(Point position, const std::string &defName, const std::pair &help, CFunctionList Callback = 0, int key=0, bool playerColoredButton = false ); /// Appearance modifiers void setIndex(size_t index, bool playerColoredButton=false); void setImage(CAnimation* anim, bool playerColoredButton=false, int animFlags=0); void setPlayerColor(PlayerColor player); /// CIntObject overrides void clickRight(tribool down, bool previousState) override; void clickLeft(tribool down, bool previousState) override; void hover (bool on) override; void showAll(SDL_Surface * to) override; /// generates tooltip that can be passed into constructor static std::pair tooltip(); static std::pair tooltip(const JsonNode & localizedTexts); static std::pair tooltip(const std::string & hover, const std::string & help = ""); }; class CToggleBase { CFunctionList callback; protected: bool selected; // internal method for overrides virtual void doSelect(bool on); // returns true if toggle can change its state bool canActivate(); public: /// if set to false - button can not be deselected normally bool allowDeselection; CToggleBase(CFunctionList callback); virtual ~CToggleBase(); /// Changes selection to "on", and calls callback void setSelected(bool on); void addCallback(std::function callback); }; class ClickableToggle : public ClickableArea, public CToggleBase { public: ClickableToggle(CIntObject * object, CFunctionList selectFun, CFunctionList deselectFun); void clickLeft(tribool down, bool previousState) override; }; /// A button which can be selected/deselected, checkbox class CToggleButton : public CButton, public CToggleBase { void doSelect(bool on) override; public: CToggleButton(Point position, const std::string &defName, const std::pair &help, CFunctionList Callback = 0, int key=0, bool playerColoredButton = false ); void clickLeft(tribool down, bool previousState) override; // bring overrides into scope //using CButton::addCallback; using CToggleBase::addCallback; }; class CToggleGroup : public CIntObject { CFunctionList onChange; //called when changing selected button with new button's id int selectedID; void selectionChanged(int to); public: std::map buttons; CToggleGroup(const CFunctionList & OnChange); void addCallback(std::function callback); /// add one toggle/button into group void addToggle(int index, CToggleBase * button); /// Changes selection to specific value. Will select toggle with this ID, if present void setSelected(int id); }; /// A typical slider for volume with an animated indicator class CVolumeSlider : public CIntObject { int value; CFunctionList onChange; CAnimImage * animImage; const std::pair * const helpHandlers; void setVolume(const int v); public: /// @param position coordinates of slider /// @param defName name of def animation for slider /// @param value initial value for volume /// @param help pointer to first helptext of slider CVolumeSlider(const Point &position, const std::string &defName, const int value, const std::pair * const help); void moveTo(int id); void addCallback(std::function callback); void clickLeft(tribool down, bool previousState) override; void clickRight(tribool down, bool previousState) override; void wheelScrolled(bool down, bool in) override; }; /// A typical slider which can be orientated horizontally/vertically. class CSlider : public CIntObject { CButton *left, *right, *slider; //if vertical then left=up int capacity;//how many elements can be active at same time (e.g. hero list = 5) int positions; //number of highest position (0 if there is only one) bool horizontal; bool wheelScrolling; bool keyScrolling; int amount; //total amount of elements (e.g. hero list = 0-8) int value; //first active element int scrollStep; // how many elements will be scrolled via one click, default = 1 CFunctionList moved; void updateSliderPos(); void sliderClicked(); public: enum EStyle { BROWN, BLUE }; void block(bool on); /// Controls how many items wil be scrolled via one click void setScrollStep(int to); /// Value modifiers void moveLeft(); void moveRight(); void moveTo(int value); void moveBy(int amount); void moveToMin(); void moveToMax(); /// Amount modifier void setAmount(int to); /// Accessors int getAmount(); int getValue(); void addCallback(std::function callback); void keyPressed(const SDL_KeyboardEvent & key) override; void wheelScrolled(bool down, bool in) override; void clickLeft(tribool down, bool previousState) override; void mouseMoved (const SDL_MouseMotionEvent & sEvent) override; void showAll(SDL_Surface * to) override; /// @param position coordinates of slider /// @param length length of slider ribbon, including left/right buttons /// @param Moved function that will be called whenever slider moves /// @param Capacity maximal number of visible at once elements /// @param Amount total amount of elements, including not visible /// @param Value starting position CSlider(Point position, int length, std::function Moved, int Capacity, int Amount, int Value=0, bool Horizontal=true, EStyle style = BROWN); ~CSlider(); };