103 lines
3.5 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| 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
| |||
//+------------------------------------------------------------------+
|