| //+------------------------------------------------------------------+
|
| //| CkObj.mqh |
|
| //| Copyright 2026, Allan Niñal |
|
| //| https://www.allanninal.dev/mql5 |
|
| //+------------------------------------------------------------------+
|
| //| Chart kit object helpers: create once (hidden from the object |
|
| //| list, not selectable), then set a property only when its value |
|
| //| changed, so a redraw with nothing new sends nothing. Every name |
|
| //| carries the product's own prefix; CkDeleteAll(prefix) cleans up. |
|
| //| g_ck_dirty turns true when something was sent: call ChartRedraw |
|
| //| once per handler, not per object. |
|
| //+------------------------------------------------------------------+
|
| #ifndef CK_OBJ_MQH
|
| #define CK_OBJ_MQH
|
|
|
| bool g_ck_dirty=false;
|
|
|
| //--- the non-visual Strategy Tester has no chart to draw on
|
| bool CkCanDraw(void) { return(!(MQLInfoInteger(MQL_TESTER) && !MQLInfoInteger(MQL_VISUAL_MODE))); }
|
|
|
| bool CkEnsure(const string name,const ENUM_OBJECT type)
|
| {
|
| if(ObjectFind(0,name)>=0)
|
| return(true);
|
| if(!ObjectCreate(0,name,type,0,0,0))
|
| return(false);
|
| ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
|
| ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
|
| g_ck_dirty=true;
|
| return(true);
|
| }
|
|
|
| void CkSetI(const string name,const ENUM_OBJECT_PROPERTY_INTEGER p,const long v,const int mod=0)
|
| {
|
| if(ObjectGetInteger(0,name,p,mod)!=v)
|
| {
|
| ObjectSetInteger(0,name,p,mod,v);
|
| g_ck_dirty=true;
|
| }
|
| }
|
| void CkSetD(const string name,const ENUM_OBJECT_PROPERTY_DOUBLE p,const double v,const int mod=0)
|
| {
|
| if(MathAbs(ObjectGetDouble(0,name,p,mod)-v)>1e-12)
|
| {
|
| ObjectSetDouble(0,name,p,mod,v);
|
| g_ck_dirty=true;
|
| }
|
| }
|
| void CkSetS(const string name,const ENUM_OBJECT_PROPERTY_STRING p,const string v)
|
| {
|
| if(ObjectGetString(0,name,p)!=v)
|
| {
|
| ObjectSetString(0,name,p,v);
|
| g_ck_dirty=true;
|
| }
|
| }
|
|
|
| //--- a filled rectangle behind the candles (time t1..t2, price p1..p2)
|
| void CkRect(const string name,const datetime t1,const double p1,const datetime t2,const double p2,const color fill,const string tip="")
|
| {
|
| if(!CkEnsure(name,OBJ_RECTANGLE))
|
| return;
|
| CkSetI(name,OBJPROP_BACK,1);
|
| CkSetI(name,OBJPROP_FILL,1);
|
| CkSetI(name,OBJPROP_COLOR,fill);
|
| CkSetI(name,OBJPROP_TIME,t1,0);
|
| CkSetD(name,OBJPROP_PRICE,p1,0);
|
| CkSetI(name,OBJPROP_TIME,t2,1);
|
| CkSetD(name,OBJPROP_PRICE,p2,1);
|
| CkSetS(name,OBJPROP_TOOLTIP,tip=="" ? "\n" : tip);
|
| }
|
|
|
| //--- a text anchored at a time and price
|
| void CkText(const string name,const datetime t,const double p,const string text,const color clr,const ENUM_ANCHOR_POINT anchor,
|
| const int size=8,const string font="Segoe UI Semibold",const string tip="")
|
| {
|
| if(!CkEnsure(name,OBJ_TEXT))
|
| return;
|
| CkSetI(name,OBJPROP_BACK,0);
|
| CkSetI(name,OBJPROP_ANCHOR,anchor);
|
| CkSetI(name,OBJPROP_FONTSIZE,size);
|
| CkSetS(name,OBJPROP_FONT,font);
|
| CkSetI(name,OBJPROP_COLOR,clr);
|
| CkSetI(name,OBJPROP_TIME,t,0);
|
| CkSetD(name,OBJPROP_PRICE,p,0);
|
| CkSetS(name,OBJPROP_TEXT,text=="" ? " " : text);
|
| CkSetS(name,OBJPROP_TOOLTIP,tip=="" ? "\n" : tip);
|
| }
|
|
|
| void CkDelete(const string name)
|
| {
|
| if(ObjectFind(0,name)>=0 && ObjectDelete(0,name))
|
| g_ck_dirty=true;
|
| }
|
|
|
| void CkDeleteAll(const string prefix)
|
| {
|
| if(ObjectsDeleteAll(0,prefix)>0)
|
| g_ck_dirty=true;
|
| }
|
|
|
| #endif
|
| //+------------------------------------------------------------------+
|