Rev 17 | Blame | Last modification | View Log | RSS feed
/*
* Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __TBUTTON_H__
#define __TBUTTON_H__
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <thread>
#include <include/core/SkImage.h>
#include <include/core/SkCanvas.h>
#include <include/core/SkBitmap.h>
#include "treadxml.h"
#include "tpalette.h"
#include "tcolor.h"
#include "tfont.h"
#include "tamxnet.h"
#include "ttimer.h"
#include "timagerefresh.h"
#define ORD_ELEM_COUNT 5
extern bool prg_stopped;
class SkFont;
struct RESOURCE_T;
namespace Button
{
typedef enum BUTTONTYPE
{
NONE,
GENERAL,
MULTISTATE_GENERAL,
BARGRAPH,
MULTISTATE_BARGRAPH,
JOISTICK,
TEXT_INPUT,
COMPUTER_CONTROL,
TAKE_NOTE,
SUBPAGE_VIEW
} BUTTONTYPE;
typedef struct SYSBORDER_t
{
int id{0};
char *name{nullptr};
int number{0};
char *style{nullptr};
int width{0};
int radius{0};
}SYSBORDER_t;
typedef struct SYSBUTTONS_t
{
int channel{0}; // Channel number
BUTTONTYPE type{NONE}; // Type of button
int states{0}; // Maximum number of states
int ll{0}; // Level low range
int lh{0}; // Level high range
}SYSBUTTONS_t;
typedef enum TEXT_ORIENTATION
{
ORI_ABSOLUT,
ORI_TOP_LEFT,
ORI_TOP_MIDDLE,
ORI_TOP_RIGHT,
ORI_CENTER_LEFT,
ORI_CENTER_MIDDLE, // default
ORI_CENTER_RIGHT,
ORI_BOTTOM_LEFT,
ORI_BOTTOM_MIDDLE,
ORI_BOTTOM_RIGHT
} TEXT_ORIENTATION;
typedef enum TEXT_EFFECT
{
EFFECT_NONE,
EFFECT_OUTLINE_S,
EFFECT_OUTLINE_M,
EFFECT_OUTLINE_L,
EFFECT_OUTLINE_X,
EFFECT_GLOW_S,
EFFECT_GLOW_M,
EFFECT_GLOW_L,
EFFECT_GLOW_X,
EFFECT_SOFT_DROP_SHADOW_1,
EFFECT_SOFT_DROP_SHADOW_2,
EFFECT_SOFT_DROP_SHADOW_3,
EFFECT_SOFT_DROP_SHADOW_4,
EFFECT_SOFT_DROP_SHADOW_5,
EFFECT_SOFT_DROP_SHADOW_6,
EFFECT_SOFT_DROP_SHADOW_7,
EFFECT_SOFT_DROP_SHADOW_8,
EFFECT_MEDIUM_DROP_SHADOW_1,
EFFECT_MEDIUM_DROP_SHADOW_2,
EFFECT_MEDIUM_DROP_SHADOW_3,
EFFECT_MEDIUM_DROP_SHADOW_4,
EFFECT_MEDIUM_DROP_SHADOW_5,
EFFECT_MEDIUM_DROP_SHADOW_6,
EFFECT_MEDIUM_DROP_SHADOW_7,
EFFECT_MEDIUM_DROP_SHADOW_8,
EFFECT_HARD_DROP_SHADOW_1,
EFFECT_HARD_DROP_SHADOW_2,
EFFECT_HARD_DROP_SHADOW_3,
EFFECT_HARD_DROP_SHADOW_4,
EFFECT_HARD_DROP_SHADOW_5,
EFFECT_HARD_DROP_SHADOW_6,
EFFECT_HARD_DROP_SHADOW_7,
EFFECT_HARD_DROP_SHADOW_8,
EFFECT_SOFT_DROP_SHADOW_1_WITH_OUTLINE,
EFFECT_SOFT_DROP_SHADOW_2_WITH_OUTLINE,
EFFECT_SOFT_DROP_SHADOW_3_WITH_OUTLINE,
EFFECT_SOFT_DROP_SHADOW_4_WITH_OUTLINE,
EFFECT_SOFT_DROP_SHADOW_5_WITH_OUTLINE,
EFFECT_SOFT_DROP_SHADOW_6_WITH_OUTLINE,
EFFECT_SOFT_DROP_SHADOW_7_WITH_OUTLINE,
EFFECT_SOFT_DROP_SHADOW_8_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_1_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_2_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_3_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_4_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_5_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_6_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_7_WITH_OUTLINE,
EFFECT_MEDIUM_DROP_SHADOW_8_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_1_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_2_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_3_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_4_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_5_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_6_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_7_WITH_OUTLINE,
EFFECT_HARD_DROP_SHADOW_8_WITH_OUTLINE
}TEXT_EFFECT;
typedef enum DRAW_ORDER
{
ORD_ELEM_NONE,
ORD_ELEM_FILL,
ORD_ELEM_BITMAP,
ORD_ELEM_ICON,
ORD_ELEM_TEXT,
ORD_ELEM_BORDER
}DRAW_ORDER;
typedef struct SR_T
{
int number{0};
std::string _do; // Order on how to show a multistate bargraph (010203...)
std::string bs; // Frame type (circle, ...)
std::string mi; // Chameleon image
int mi_width{0}; // Width of image
int mi_height{0}; // Height of image
std::string cb; // Border color
std::string cf; // Fill color
std::string ct; // Text Color
std::string ec; // Text effect color
std::string bm; // bitmap file name
std::string sd; // Sound file to play
int bm_width{0}; // Width of image
int bm_height{0}; // Height of image
bool dynamic{false}; // TRUE = moving image
int sb{0}; // Index to external graphics download
int ii{0}; // Icon index number
int ix{0}; // Icon X position
int iy{0}; // Icon Y position
int ji{5}; // Icon style / position like "jt", default 5 = center+middle
int jb{5}; // Image position (center, left, ...), default 5 = center+middle
int bx{0}; // Absolute image position x
int by{0}; // Absolute image position y
int fi{0}; // Font index
std::string te; // Text
TEXT_ORIENTATION jt{ORI_CENTER_MIDDLE}; // Text orientation
int tx{0}; // Text X position
int ty{0}; // Text Y position
int ww{0}; // line break when 1
int et{0}; // Text effect (^TEF)
int oo{-1}; // Over all opacity
} SR_T;
typedef enum FEEDBACK
{
FB_NONE,
FB_CHANNEL,
FB_INV_CHANNEL, // inverted channel
FB_ALWAYS_ON,
FB_MOMENTARY,
FB_BLINK
} FEEDBACK;
typedef struct PUSH_FUNC
{
std::string pfType; // command to execute when button was pushed
std::string pfName; // Name of popup
} PUSH_FUNC_T;
typedef enum CENTER_CODE
{
SC_ICON = 0,
SC_BITMAP,
SC_TEXT
}CENTER_CODE;
typedef struct POSITION_t
{
int width{0};
int height{0};
int left{1};
int top{1};
bool overflow{false};
bool valid{false};
}POSITION_t;
typedef struct THR_REFRESH_t
{
ulong handle{0};
ulong parent{0};
int bi{0};
TImageRefresh *mImageRefresh{nullptr};
THR_REFRESH_t *next{nullptr};
}THR_REFRESH_t;
class TButton
{
public:
TButton();
~TButton();
/**
* The following function parses the parameters of a particular
* button and creates a new button. This function is called either
* from class TPage or TSubPage when a page or subpage is created.
*
* xml A pointer to the XML reader
* node A pointer to the actual node in the XML tree.
*/
void initialize(TReadXML *xml, mxml_node_t *node);
/**
* Returns the button index. This is a unique number inside a page
* or subpage.
*/
int getButtonIndex() { return bi; }
/**
* Returns the name of the button.
*/
std::string& getButtonName() { return na; }
/**
* Returns the description of the button, if there is one.
*/
std::string& getButtonDescription() { return bd; }
/**
* Returns the width of the button in pixels.
*/
int getWidth() { return wt; }
/**
* Returns the height of the button in pixels.
*/
int getHeight() { return ht; }
/**
* Returns the left position in pixels.
*/
int getLeftPosition() { return lt; }
/**
* Returns the top position of the button in pixels.
*/
int getTopPosition() { return tp; }
/**
* Returns the Z-order. This number marks the order the buttons
* are drawed on the screen. Inside a page or subpage the buttons
* are always sorted.
*/
int getZOrder() { return zo; }
/**
* Returns the type of the button.
*/
BUTTONTYPE getButtonType() { return type; }
int getRangeLow() { return rl; }
int getRangeHigh() { return rh; }
int getStateCount() { return stateCount; }
int getAddressPort() { return ap; }
int getAddressChannel() { return ad; }
int getChannelNumber() { return ch; }
int getChannelPort() { return cp; }
int getLevelPort() { return lp; }
int getLevelValue() { return lv; }
int getNumberInstances() { return (int)sr.size(); }
int getActiveInstance() { return mActInstance; }
ulong getHandle() { return mHandle; }
void setEnable(bool en) { mEnabled = en; }
bool isEnabled() { return mEnabled; }
void setHandle(ulong handle);
void setPalette(TPalette *pal) { mPalette = pal; }
void setParentWidth(int width) { mParentWidth = width; }
void setParentHeight(int height) { mParentHeight = height; }
void setParentSize(int width, int height) { mParentWidth = width; mParentHeight = height; }
void setFonts(TFont *ft) { mFonts = ft; }
void setGlobalOpacity(int oo) { if (oo >= 0 && oo <= 255) mGlobalOO = oo; }
void setVisible(bool v) { visible = v; }
bool setBitmap(const std::string& file, int instance);
bool setOpacity(int op, int instance);
bool setWorWrap(bool state, int instance);
bool setFont(int id, int instance);
void setTop(int top);
void setLeft(int left);
void setLeftTop(int left, int top);
void setResourceName(const std::string& name, int instance);
void addPushFunction(std::string& func, std::string& page);
void registerSystemButton();
bool isSystemButton();
void clearPushFunctions() { pushFunc.clear(); }
void clearPushFunction(const std::string& action);
void refresh();
/**
* Sets a particular instance of the button active. This implies
* a redraw of the button in case the instance is different from
* the one already visible.
*
* @param instance
* The instance of the button to be activated.
*
* @return
* On error returns FALSE.
*/
bool setActive(int instance);
/**
* Sets an Icon on the button. This implies a redraw of the button
* in case the instance is different from the one already visible.
*
* @param id
* The id number of the icon.
*
* @param instance
* The instance where the icon should be drawed
*
* @return On error returns FALSE.
*/
bool setIcon(int id, int instance);
/**
* Sets an Icon on the button. This implies a redraw of the button
* in case the instance is different from the one already visible.
*
* @param icon
* The file name of the icon.
*
* @param instance
* The instance where the icon should be drawed
*
* @return
* On error returns FALSE.
*/
bool setIcon(const std::string& icon, int instance);
/**
* Removes an icon from a button. This implies a redraw of the
* button.
*
* @param instance
* The instance number involved.
*
* @return
* On error returns FALSE.
*/
bool revokeIcon(int instance);
/**
* Set a string to a button.
*
* @param txt
* The text to write on top of a button.
*
* @param instance
* The instance number involved.
*
* @return
* On error returns FALSE.
*/
bool setText(const std::string& txt, int instance);
/**
* Read the images, if any, from files and create an image. If there
* are no files, draw the image as defined.
*
* @return
* On error returns FALSE.
*/
bool createButtons();
/**
* Register a callback function to display a ready image.
*/
void registerCallback(std::function<void (ulong handle, ulong parent, unsigned char *buffer, int width, int height, int pixline, int left, int top)> displayButton)
{
_displayButton = displayButton;
}
void registerCallbackFT(std::function<void (ulong handle, const std::string& text, const std::string& font, const std::string& family, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, TEXT_ORIENTATION ori, TEXT_EFFECT effect, bool ww)> setText)
{ _setText = setText; }
void regCallPlayVideo(std::function<void (ulong handle, ulong parent, int left, int top, int width, int height, const std::string& url, const std::string& user, const std::string& pw)> playVideo) { _playVideo = playVideo; };
/**
* Make a pixel array and call the callback function to display the
* image. If there is no callback function registered, nothing
* happens.
* This method draws a general button. It is used for most
* specialized buttons too.
*
* @param instance
* Optional. The instance of the button to draw. If the base
* instance should be drawn, the parameter can be omitted.
*
* @param show
* Optional. By defualt TRUE. If set to FALSE, the button image is
* not send to GUI. The image is available in the variable
* "mLastImage" immediately after ending the method.
*
* @return
* On error returns FALSE.
*/
bool drawButton(int instance = 0, bool show=true);
/**
* Method to draw a multistate animated button. It creates a thread
* with a timer and starts an animation by drawing every instance in
* the defined order.
*
* @return
* On error returns FALSE.
*/
bool drawButtonMultistateAni();
/**
* Draws a normal bargraph with an ON and OFF state. Takes care
* about cameleon images. If registered, it calls a callback
* function to the GUI to display the ready image.
*
* @param instance
* The instance of the bargraph to draw. This value must be in the
* range of 0 to "stateCount".
*
* @param level
* This is the level to show on the bargraph. It must be a value
* the range of "rl" (range low) and "rh" (range high). A lower
* value than "rl" is interpreted as "rl" and a value higher than
* "rh" is interpreted as "rh".
*
* @param show
* Optional. By default TRUE. If set to FALSE, the bargraph image is
* not send to GUI. The image is available in the variable
* "mLastImage" immediately after ending the method.
*
* @return
* On error returns FALSE.
*/
bool drawBargraph(int instance, int level, bool show=true);
/**
* Show the button with it's current state.
*/
void show();
/**
* Hide the button. In case of an animated button, the animation is
* stopped.
*
* @param total
* Optional. When set to TRUE, a transparent button is displayed.
*/
void hide(bool total=false);
/**
* This method sends the image together with all data to the GUI,
* where it will be shown. The variable mLastImage must not be
* empty for the method to succeed.
*/
void showLastButton();
/**
* Handle a mouse click.
*
* @param pressed
* TRUE = Button was pressed, FALSE = Button was released.
*
* @return
* TRUE = Button was clickable and processed. FALSE = Button was not
* clickable.
*/
bool doClick(int x, int y, bool pressed);
protected:
BUTTONTYPE getButtonType(const std::string& bt);
FEEDBACK getButtonFeedback(const std::string& fb);
SkBitmap drawImageButton(SkBitmap& imgRed, SkBitmap& imgMask, int width, int height, SkColor col1, SkColor col2);
void funcTimer(const amx::ANET_BLINK& blink);
void funcNetwork(int state);
void funcResource(const RESOURCE_T *resource, const std::string& url, SkBitmap* bm, int instance);
private:
typedef struct IMAGE_t
{
int number{0};
SkBitmap imageMi;
SkBitmap imageBm;
void clear()
{
number = 0;
imageMi.reset();
imageBm.reset();
}
}IMAGE_t;
std::function<void (ulong handle, ulong parent, unsigned char *buffer, int width, int height, int pixline, int left, int top)> _displayButton{nullptr};
std::function<void (ulong handle, const std::string& text, const std::string& font, const std::string& family, int size, int x, int y, ulong color, ulong effectColor, FONT_STYLE style, TEXT_ORIENTATION ori, TEXT_EFFECT effect, bool ww)> _setText{nullptr};
std::function<void (ulong handle, ulong parent, int left, int top, int width, int height, const std::string& url, const std::string& user, const std::string& pw)> _playVideo{nullptr};
POSITION_t calcImagePosition(int width, int height, CENTER_CODE cc, int number);
int getBorderSize(const std::string& name);
void calcImageSizePercent(int imWidth, int imHeight, int btWidth, int btHeight, int btFrame, int *realX, int *realY);
SkColor baseColor(SkColor basePix, SkColor maskPix, SkColor col1, SkColor col2);
TEXT_EFFECT textEffect(const std::string& effect);
int numberLines(const std::string& str);
std::vector<std::string> splitLine(const std::string& str);
std::vector<std::string> splitLine(const std::string& str, int width, int height, SkFont& font, SkPaint& paint);
SkRect calcRect(int width, int height, int pen);
void runAnimation(); // Method started as thread for button animation
void getDrawOrder(const std::string& sdo, DRAW_ORDER *order);
bool buttonFill(SkBitmap *bm, int instance);
bool buttonBitmap(SkBitmap *bm, int instance);
bool buttonDynamic(SkBitmap *bm, int instance);
bool buttonIcon(SkBitmap *bm, int instance);
bool buttonText(SkBitmap *bm, int instance);
bool buttonBorder(SkBitmap *bm, int instance);
bool isClickable();
bool isPixelTransparent(int x, int y);
bool barLevel(SkBitmap *bm, int instance, int level);
bool makeElement(int instance=-1);
bool loadImage(SkBitmap *bm, SkBitmap& image, int instance);
void _TimerCallback(ulong counter);
void _imageRefresh(const std::string& url);
static THR_REFRESH_t *_addResource(TImageRefresh *refr, ulong handle, ulong parent, int bi);
static THR_REFRESH_t *_findResource(ulong handle, ulong parent, int bi);
BUTTONTYPE type;
int bi{0}; // button ID
std::string na; // name
std::string bd; // Description --> ignored
int lt{0}; // pixel from left
int tp{0}; // pixel from top
int wt{0}; // width
int ht{0}; // height
int zo{0}; // Z-Order
std::string hs; // bounding, ...
std::string bs; // Border style (circle, ...)
FEEDBACK fb{FB_NONE}; // momentary, ...
int ap{1}; // Address port (default: 1)
int ad{0}; // Address channel
int ch{0}; // Channel number
int cp{1}; // Channel port (default: 1)
int lp{1}; // Level port (default: 1)
int lv{0}; // Level code
std::string dr; // Level "horizontal" or "vertical"
int va{0};
int stateCount{0}; // State count with multistate buttons
int rm{0}; // State count with multistate buttons?
int nu{2}; // Animate time up
int nd{2}; // Animate time down
int ar{0}; // Auto repeat (1 = true)
int ru{0}; // Animate time up (bargraph)
int rd{0}; // Animate time down (bargraph)
int lu{0}; // Animate time up (Bargraph)
int ld{0}; // Animate time down (Bargraph)
int rv{0};
int rl{0}; // Range low
int rh{0}; // Range high
int ri{0}; // Bargraph inverted (0 = normal, 1 = inverted)
int rn{0}; // Bargraph: Range drag increment
std::string _if; // Bargraph function: empty = display only, active, active centering, drag, drag centering
std::string sd; // Name/Type of slider for a bargraph
std::string sc; // Color of slider (for bargraph)
int mt{0}; // Length of text area (0 = 2000)
std::string dt; // "multiple" textarea has multiple lines, else single line
std::string im; // Input mask of a text area
std::string op; // String the button send
bool visible{true}; // TRUE=Button is visible
std::vector<PUSH_FUNC_T> pushFunc; // Push functions: This are executed on button press
std::vector<SR_T> sr; // The elements the button consists of
TPalette *mPalette{nullptr}; // The color palette
// Image management
std::map<int, IMAGE_t> mImages; // Contains the images in ready to use format, if there any
SkBitmap mLastImage; // The last calculated image
ulong mHandle{0}; // internal used handle to identify button
int mParentHeight{0}; // The height of the parent page / subpage
int mParentWidth{0}; // The width of the parent page / subpage
bool mEnabled{true}; // By default a button is enabled (TRUE); FALSE = Button disabled
TFont *mFonts{nullptr}; // The font table
int mGlobalOO{-1}; // Opacity of the whole subpage, if any
int mActInstance{0}; // Active instance
DRAW_ORDER mDOrder[ORD_ELEM_COUNT]; // The order to draw the elements of a button
std::thread mThrAni; // Thread handle for animation
std::thread mThrRes; // A resouce (download of a remote image/video) running in background
static std::atomic<bool> mAniRunning; // TRUE = Animation is running
int mLastLevel{0}; // The last level value for a bargraph
bool mSystemReg{false}; // TRUE = registered as system button
amx::ANET_BLINK mLastBlink; // This is used for the system clock buttons
TTimer *mTimer{nullptr}; // This is for buttons displaying the time or a date. It's a thread running in background.
static THR_REFRESH_t *mThrRefresh; // If we have a source to reread periodicaly, this starts a thread to do that.
bool mResourceRead{false}; // TRUE if the resource should be read only once on startup and this is done.
};
typedef struct BUTTONS_T
{
TButton *button{nullptr};
BUTTONS_T *previous{nullptr};
BUTTONS_T *next{nullptr};
} BUTTONS_T;
}
#endif