mql5/Experts/Motivated/Graphics/ZoneLevel.mqh

384 lines
14 KiB
MQL5

2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
2026-06-10 18:31:08 +02:00
//| ZoneLevel.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include <ABCD/chartObjects.mqh>
#include "../Utils/TimePrice.mqh"
class VisualSystem; // Forward declaration
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class ZoneLevel
{
protected:
long chartId;
string name;
string levelText;
string levelObjName;
string levelTextObjName;
string levelExtText;
string levelExtObjName;
string levelExtTextObjName;
string suffix;
color levelColor;
color textColor;
color mutedColor;
2026-06-10 23:03:10 +02:00
int width, pxDiff;
2026-06-10 18:31:08 +02:00
TimePrice a;
TimePrice b;
TimePrice c;
2026-06-23 21:44:37 +02:00
TimePrice d;
2026-06-10 18:31:08 +02:00
bool isBull;
2026-06-23 21:44:37 +02:00
bool isDrawExt;
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
// --- Dirty tracking ---
bool m_dirty; // true if level needs redraw
bool m_lastVisible; // previous visibility state
2026-06-23 21:44:37 +02:00
bool m_lastExtVisible; // previous visibility state
datetime m_from_time, m_to_time;
double m_min_price, m_max_price;
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
public:
2026-06-23 21:44:37 +02:00
ZoneLevel() : m_dirty(true), m_lastVisible(false), m_lastExtVisible(false) {}
2026-06-10 23:03:10 +02:00
ZoneLevel(string _name, string text = "", string _levelExtText = "", int _chartId = 0, color clr = clrWhite, int _width = 1);
~ZoneLevel() { Delete(); }
// Core methods
2026-06-23 21:44:37 +02:00
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
{
m_from_time = fromTime;
m_to_time = toTime;
m_min_price = minPrice;
m_max_price = maxPrice;
2026-06-10 23:03:10 +02:00
2026-06-23 21:44:37 +02:00
return false;
}
virtual bool IsExtVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice);
virtual void Draw(long chart_id, VisualSystem* vs) {}
virtual void DrawExt(long chart_id, VisualSystem* vs);
2026-06-10 23:03:10 +02:00
// --- Dirty-aware render ---
void UpdateDraw(VisualSystem* vs, datetime fromTime, datetime toTime, double minPrice, double maxPrice);
// Mark dirty when any property changes
void MarkDirty() { m_dirty = true; }
// Property setters (call MarkDirty when changed)
void SetText(string txt) { if(levelText != txt) { levelText = txt; MarkDirty(); } }
void SetExtText(string txt) { if(levelExtText != txt) { levelExtText = txt; MarkDirty(); } }
void SetColor(color clr) { if(levelColor != clr) { levelColor = clr; MarkDirty(); } }
2026-06-23 21:44:37 +02:00
void SetBothColor(color clr) { if(levelColor != clr) { levelColor = clr; textColor = clr; MarkDirty(); } }
void SetExtEnd(TimePrice &_a) {c = _a; MarkDirty();}
2026-06-10 23:03:10 +02:00
void SetBull(bool _isBull, TimePrice &_a);
2026-06-23 21:44:37 +02:00
void SetDrawExt(bool _isDrawExt);
2026-06-10 23:03:10 +02:00
void SetFontSize(int fontSize);
void SetAtBack(bool back = true);
void SetFill(bool fill = true);
// Getter
2026-06-10 18:31:08 +02:00
string GetName() const { return name; }
2026-06-10 23:03:10 +02:00
// Object deletion
void Delete();
void DeleteText();
void DeleteExtender();
void DeleteExtenderText();
void DeleteLevel();
void DeleteLevelText();
2026-06-10 18:31:08 +02:00
2026-06-23 21:44:37 +02:00
datetime GetFromTime() const { return m_from_time; } // store as member
datetime GetToTime() const { return m_to_time; }
double GetMinPrice() const { return m_min_price; }
double GetMaxPrice() const { return m_max_price; }
2026-06-10 23:03:10 +02:00
protected:
2026-06-23 21:44:37 +02:00
void DrawText(string name, string text, TimePrice &_a, double angle = 0);
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double RadToDeg(double rad)
{
return rad * 180.0 / M_PI;
}//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CalcLineAngleDeg(datetime t1, double p1, datetime t2, double p2, double &angleDeg)
{
int x1, y1, x2, y2;
if(!ChartTimePriceToXY(chartId, 0, t1, p1, x1, y1))
return false;
if(!ChartTimePriceToXY(chartId, 0, t2, p2, x2, y2))
return false;
double dx = (double)(x2 - x1);
double dy = (double)(y1 - y2);
if(MathAbs(dx) < 0.000001 && MathAbs(dy) < 0.000001)
{
angleDeg = 0.0;
return true;
}
angleDeg = RadToDeg(MathArctan2(dy, dx));
return true;
}
2026-06-10 23:03:10 +02:00
};
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
ZoneLevel::ZoneLevel(string _name, string text = "", string _levelExtText = "", int _chartId = 0, color clr = clrWhite, int _width = 1)
: name(_name), levelText(text), levelExtText(_levelExtText), width(_width), chartId(_chartId),
2026-06-23 21:44:37 +02:00
levelColor(clr), textColor(clr), isBull(false), isDrawExt(false), pxDiff(6), m_dirty(true), m_lastVisible(false), m_lastExtVisible(false)
2026-06-10 23:03:10 +02:00
{
suffix = "VIS_";
levelObjName = suffix + _name;
levelTextObjName = suffix + "txt_" + _name;
levelExtTextObjName = suffix + "txt_ext_" + _name;
levelExtObjName = suffix + "ext_" + _name;
2026-06-23 21:44:37 +02:00
2026-06-10 23:03:10 +02:00
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| Dirty-aware update – called by VisualSystem::Render() |
//+------------------------------------------------------------------+
2026-06-23 21:44:37 +02:00
void ZoneLevel::UpdateDraw(VisualSystem* vs,
datetime fromTime, datetime toTime,
double minPrice, double maxPrice)
2026-06-10 23:03:10 +02:00
{
2026-06-23 21:44:37 +02:00
// 1. Check visibility for main level
2026-06-10 23:03:10 +02:00
bool isVisible = IsVisible(fromTime, toTime, minPrice, maxPrice);
bool visibilityChanged = (isVisible != m_lastVisible);
m_lastVisible = isVisible;
2026-06-10 18:31:08 +02:00
2026-06-23 21:44:37 +02:00
// 2. Check visibility for extender
bool isExtVisible = IsExtVisible(fromTime, toTime, minPrice, maxPrice);
bool visibilityExtChanged = (isExtVisible != m_lastExtVisible);
m_lastExtVisible = isExtVisible;
// 3. Handle MAIN level
2026-06-10 23:03:10 +02:00
if(!isVisible)
2026-06-10 18:31:08 +02:00
{
2026-06-10 23:03:10 +02:00
if(visibilityChanged)
2026-06-23 21:44:37 +02:00
DeleteLevel(); // main left visible area → delete objects
}
else
{
// main is visible – redraw if dirty OR just became visible
if(visibilityChanged || m_dirty)
2026-06-10 18:31:08 +02:00
{
2026-06-23 21:44:37 +02:00
Draw(chartId, vs);
2026-06-10 18:31:08 +02:00
}
}
2026-06-23 21:44:37 +02:00
// 4. Handle EXTENDER level (same dirty flag)
if(!isExtVisible)
2026-06-10 18:31:08 +02:00
{
2026-06-23 21:44:37 +02:00
if(visibilityExtChanged)
DeleteExtender(); // ext left visible area → delete objects
}
else
{
// ext is visible – redraw if dirty OR just became visible
if(visibilityExtChanged || m_dirty)
{
DrawExt(chartId, vs);
}
2026-06-10 18:31:08 +02:00
}
2026-06-23 21:44:37 +02:00
// 5. Clear the shared dirty flag only after both components have been processed
if(m_dirty)
m_dirty = false;
}
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| Set Bull direction (moves text anchor) |
//+------------------------------------------------------------------+
void ZoneLevel::SetBull(bool _isBull, TimePrice &_a)
{
if(isBull == _isBull && a.x == _a.x && a.y == _a.y)
return; // no change
isBull = _isBull;
a = _a;
MarkDirty();
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
// Convert original to screen X,Y, adjust, then update object position
int x = 0, y = 0;
ChartTimePriceToXY(chartId, 0, a.x, a.y, x, y);
if(!isBull)
y += pxDiff;
else
y -= pxDiff;
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
int sub_window = 0;
datetime newTime = 0;
double newPrice = 0.0;
ChartXYToTimePrice(chartId, x, y, sub_window, newTime, newPrice);
ObjectSetDouble(chartId, name, OBJPROP_PRICE, newPrice);
}
2026-06-23 21:44:37 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool ZoneLevel:: IsExtVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
{
// Print("[PointLevel]::IsVisible() ",fromTime," :: ", a.x,"-- ", a.x >= fromTime);
return (d.x >= fromTime && c.x <= toTime && d.y >= minPrice && c.y <= maxPrice);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::SetDrawExt(bool _isDrawExt)
{
2026-06-10 18:31:08 +02:00
2026-06-23 21:44:37 +02:00
isDrawExt = _isDrawExt;
MarkDirty();
}
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| Set font size for both text objects |
//+------------------------------------------------------------------+
void ZoneLevel::SetFontSize(int fontSize)
{
ObjectSetInteger(chartId, levelTextObjName, OBJPROP_FONTSIZE, fontSize);
ObjectSetInteger(chartId, levelExtTextObjName, OBJPROP_FONTSIZE, fontSize);
// Font change doesn't require redraw of geometry, but we mark dirty anyway
MarkDirty();
}
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
void ZoneLevel::SetAtBack(bool back = true)
2026-06-10 18:31:08 +02:00
{
2026-06-10 23:03:10 +02:00
ObjectSetInteger(chartId, name, OBJPROP_BACK, back);
MarkDirty();
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::SetFill(bool fill = true)
{
ObjectSetInteger(chartId, name, OBJPROP_FILL, fill);
MarkDirty();
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| Draw text helper |
//+------------------------------------------------------------------+
2026-06-23 21:44:37 +02:00
void ZoneLevel::DrawText(string name, string text, TimePrice &_a, double angle = 0)
2026-06-10 23:03:10 +02:00
{
2026-06-23 21:44:37 +02:00
2026-06-10 23:03:10 +02:00
int x = 0, y = 0;
ChartTimePriceToXY(chartId, 0, _a.x, _a.y, x, y);
if(!isBull)
y += pxDiff;
else
y -= pxDiff;
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
int sub_window = 0;
datetime newTime = 0;
double newPrice = 0.0;
ChartXYToTimePrice(chartId, x, y, sub_window, newTime, newPrice);
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
ObjectCreate(chartId, name, OBJ_TEXT, 0, _a.x, newPrice);
ObjectSetString(chartId, name, OBJPROP_TEXT, text);
ObjectSetInteger(chartId, name, OBJPROP_COLOR, textColor);
2026-06-23 21:44:37 +02:00
ObjectSetDouble(chartId, name, OBJPROP_ANGLE, angle);
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
if(!isBull)
ObjectSetInteger(chartId, name, OBJPROP_ANCHOR, ANCHOR_UPPER);
else
ObjectSetInteger(chartId, name, OBJPROP_ANCHOR, ANCHOR_LOWER);
2026-06-10 18:31:08 +02:00
}
2026-06-23 21:44:37 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::DrawExt(long chart_id, VisualSystem* vs)
{
if(a.Ok() && b.Ok())
d = b;
else
if(a.Ok() && !b.Ok())
{
d = a;
}
2026-06-10 18:31:08 +02:00
2026-06-23 21:44:37 +02:00
if(ObjectFind(chartId, levelExtObjName) < 0)
{
ObjectCreate(chartId, levelExtObjName, OBJ_TREND, 0, d.x, d.y, c.x, c.y);
}
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_COLOR, levelColor);
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_BACK, false);
if(levelExtText != "" && vs.Zoom() >= 2)
{
double angle;
CalcLineAngleDeg(d.x, d.y, c.x, c.y, angle);
DrawText(levelExtTextObjName, levelExtText, d.CenterCoord(c), angle);
// SetFontSize(vs.GetScaledFontSize(12));
}
}
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
//| Delete all objects |
//+------------------------------------------------------------------+
void ZoneLevel::Delete()
{
// Print("Delete");
DeleteExtender();
DeleteLevel();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::DeleteText()
{
DeleteLevelText();
DeleteExtenderText();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::DeleteExtender()
{
DeleteExtenderText();
ObjectDelete(chartId, levelExtObjName);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::DeleteExtenderText()
{
ObjectDelete(chartId, levelExtTextObjName);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::DeleteLevel()
{
ObjectDelete(chartId, levelObjName);
DeleteLevelText();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneLevel::DeleteLevelText()
{
ObjectDelete(chartId, levelTextObjName);
}
//+------------------------------------------------------------------+