mql5/Experts/Motivated/Graphics/VisualSystem.mqh

360 lines
12 KiB
MQL5

2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
//| VisualSystem.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include "VisualInterfaces.mqh"
#include "ZoneLevel.mqh"
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
//| |
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
class VisualSystem : public ViewEvents
{
private:
long m_chart_id;
int m_width, m_height;
long m_zoom;
datetime m_from_time, m_to_time;
double m_min_price, m_max_price;
bool m_auto_update;
long m_min_zoom_to_keep_texts;
bool m_min_zoom_reached_already;
2026-06-10 23:03:10 +02:00
// Manual dynamic array
ZoneLevel* m_levels[];
int m_count;
int m_capacity;
void ResizeArray(int newCapacity);
2026-06-10 18:31:08 +02:00
public:
VisualSystem();
~VisualSystem();
void SetMinZoomForTexts(long zoomLevel) { m_min_zoom_to_keep_texts = zoomLevel; }
void SetAutoUpdate(bool enable) { m_auto_update = enable; }
2026-06-10 23:03:10 +02:00
long Zoom() const { return m_zoom; }
2026-06-10 18:31:08 +02:00
int GetScaledFontSize(int baseSizePoints) const;
// Level management
void AddLevel(ZoneLevel *level);
2026-06-10 23:03:10 +02:00
bool RemoveLevel(int index);
bool RemoveLevel(ZoneLevel *level);
2026-06-23 21:44:37 +02:00
2026-06-10 23:03:10 +02:00
ZoneLevel* GetLevel(int index) const;
2026-06-23 21:44:37 +02:00
ZoneLevel* GetLevelByName(string levelName);
2026-06-10 23:03:10 +02:00
int GetSize() const { return m_count; }
int GetCapacity() const { return m_capacity; }
void SetCapacity(int newCapacity);
2026-06-10 18:31:08 +02:00
void ClearLevels();
2026-06-10 23:03:10 +02:00
void DeleteLevel(int index);
// Rendering – now uses dirty tracking
2026-06-10 18:31:08 +02:00
void Render();
2026-06-10 23:03:10 +02:00
// Event handlers
2026-06-10 18:31:08 +02:00
void UpdateViewBounds();
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
// Virtual overrides
2026-06-10 23:03:10 +02:00
virtual void OnViewChanged() override;
virtual void OnZoomChanged(long newZoom, long oldZoom) override;
2026-06-10 18:31:08 +02:00
virtual void OnMinZoomReached() override { }
2026-06-10 23:03:10 +02:00
virtual void OnBeforeRender() override { UpdateViewBounds(); }
2026-06-10 18:31:08 +02:00
virtual void OnAfterRender() override { }
private:
void DeleteAllTextObjects();
2026-06-10 23:03:10 +02:00
// DeleteAllVisualObjects no longer used in Render, kept for emergency
2026-06-10 18:31:08 +02:00
void DeleteAllVisualObjects();
};
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
//| Constructor |
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
VisualSystem::VisualSystem()
: m_chart_id(ChartID()), m_width(0), m_height(0), m_zoom(0),
m_auto_update(true), m_min_zoom_to_keep_texts(2), m_min_zoom_reached_already(false),
m_count(0), m_capacity(0)
2026-06-10 18:31:08 +02:00
{
UpdateViewBounds();
}
2026-06-10 23:03:10 +02:00
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
VisualSystem::~VisualSystem()
2026-06-10 18:31:08 +02:00
{
ClearLevels();
}
2026-06-10 23:03:10 +02:00
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
//| Array management |
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
void VisualSystem::ResizeArray(int newCapacity)
2026-06-10 18:31:08 +02:00
{
2026-06-10 23:03:10 +02:00
if(newCapacity <= 0)
2026-06-10 18:31:08 +02:00
{
2026-06-10 23:03:10 +02:00
ArrayResize(m_levels, 0);
m_capacity = 0;
return;
2026-06-10 18:31:08 +02:00
}
2026-06-10 23:03:10 +02:00
ArrayResize(m_levels, newCapacity);
m_capacity = newCapacity;
2026-06-10 18:31:08 +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
void VisualSystem::SetCapacity(int newCapacity)
2026-06-10 18:31:08 +02:00
{
2026-06-10 23:03:10 +02:00
if(newCapacity <= m_count)
return;
ResizeArray(newCapacity);
2026-06-10 18:31:08 +02:00
}
2026-06-10 23:03:10 +02:00
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void VisualSystem::AddLevel(ZoneLevel *level)
{
if(CheckPointer(level) == POINTER_INVALID)
return;
2026-06-10 23:03:10 +02:00
if(m_count >= m_capacity)
{
int newCap = (m_capacity == 0) ? 4 : m_capacity * 2;
ResizeArray(newCap);
}
m_levels[m_count] = level;
m_count++;
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
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool VisualSystem::RemoveLevel(int index)
{
if(index < 0 || index >= m_count)
return false;
for(int i = index; i < m_count - 1; i++)
m_levels[i] = m_levels[i + 1];
m_count--;
if(m_count > 0)
m_levels[m_count] = NULL;
return true;
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool VisualSystem::RemoveLevel(ZoneLevel *level)
{
if(level == NULL)
return false;
for(int i = 0; i < m_count; i++)
if(m_levels[i] == level)
return RemoveLevel(i);
return false;
2026-06-10 18:31:08 +02:00
}
2026-06-10 23:03:10 +02:00
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-06-23 21:44:37 +02:00
ZoneLevel* VisualSystem::GetLevelByName(string levelName)
{
for(int i = 0; i < m_count; i++)
if(m_levels[i].GetName() == levelName)
return m_levels[i];
return NULL;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
void VisualSystem::DeleteLevel(int index)
2026-06-10 18:31:08 +02:00
{
2026-06-10 23:03:10 +02:00
if(index < 0 || index >= m_count)
return;
ZoneLevel* p = m_levels[index];
if(CheckPointer(p) != POINTER_INVALID)
delete p;
RemoveLevel(index);
2026-06-10 18:31:08 +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
ZoneLevel* VisualSystem::GetLevel(int index) const
2026-06-10 18:31:08 +02:00
{
2026-06-10 23:03:10 +02:00
if(index < 0 || index >= m_count)
return NULL;
return m_levels[index];
2026-06-10 18:31:08 +02:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void VisualSystem::ClearLevels()
{
2026-06-10 23:03:10 +02:00
for(int i = 0; i < m_count; i++)
{
if(CheckPointer(m_levels[i]) != POINTER_INVALID)
delete m_levels[i];
m_levels[i] = NULL;
}
m_count = 0;
2026-06-10 18:31:08 +02:00
}
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
//| Optimized Render – uses dirty flags |
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
2026-06-23 21:44:37 +02:00
//+------------------------------------------------------------------+
//| Optimized Render – reverse iteration + early culling |
//+------------------------------------------------------------------+
2026-06-10 18:31:08 +02:00
void VisualSystem::Render()
{
2026-06-23 21:44:37 +02:00
OnBeforeRender(); // <-- updates m_from_time, m_to_time, m_min_price, m_max_price
2026-06-10 18:31:08 +02:00
2026-06-23 21:44:37 +02:00
// If bounds are invalid (chart not ready), just update all levels without culling
bool boundsValid = (m_from_time < m_to_time && m_min_price < m_max_price);
for(int i = m_count - 1; i >= 0; i--) // reverse iteration: draw newest first
{
2026-06-10 23:03:10 +02:00
ZoneLevel* p = m_levels[i];
2026-06-23 21:44:37 +02:00
if(p == NULL)
continue;
// Fast culling using level's stored time/price range
/* datetime levelFrom = p.GetFromTime();
datetime levelTo = p.GetToTime();
double levelMin = p.GetMinPrice();
double levelMax = p.GetMaxPrice();
// Skip if completely outside visible time OR price range
if(levelTo < m_from_time || levelFrom > m_to_time ||
levelMax < m_min_price || levelMin > m_max_price)
{
// Level not visible at all – skip UpdateDraw entirely
// (Objects will be deleted when they become invisible inside UpdateDraw later)
continue;
}
//} if(boundsValid)
//{*/
// Level intersects view (or bounds invalid) – update its drawing
p.UpdateDraw(GetPointer(this), m_from_time, m_to_time, m_min_price, m_max_price);
2026-06-10 23:03:10 +02:00
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
OnAfterRender();
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| View bounds update |
//+------------------------------------------------------------------+
2026-06-23 21:44:37 +02:00
void VisualSystem::UpdateViewBounds()
2026-06-10 23:03:10 +02:00
{
long old_zoom = m_zoom;
datetime old_from = m_from_time, old_to = m_to_time;
double old_minp = m_min_price, old_maxp = m_max_price;
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
m_width = (int)ChartGetInteger(m_chart_id, CHART_WIDTH_IN_PIXELS);
m_height = (int)ChartGetInteger(m_chart_id, CHART_HEIGHT_IN_PIXELS);
m_zoom = ChartGetInteger(m_chart_id, CHART_SCALE);
int sub_window = 0;
ChartXYToTimePrice(m_chart_id, 0, 0, sub_window, m_from_time, m_max_price);
ChartXYToTimePrice(m_chart_id, m_width, m_height, sub_window, m_to_time, m_min_price);
2026-06-23 21:44:37 +02:00
2026-06-10 23:03:10 +02:00
if(m_zoom != old_zoom)
{
OnZoomChanged(m_zoom, old_zoom);
bool below_threshold = (m_zoom < m_min_zoom_to_keep_texts);
2026-06-23 21:44:37 +02:00
2026-06-10 23:03:10 +02:00
if(below_threshold && !m_min_zoom_reached_already)
2026-06-23 21:44:37 +02:00
{
2026-06-10 23:03:10 +02:00
DeleteAllTextObjects();
OnMinZoomReached();
m_min_zoom_reached_already = true;
2026-06-23 21:44:37 +02:00
2026-06-10 23:03:10 +02:00
}
else
if(!below_threshold)
{
m_min_zoom_reached_already = false;
}
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
if(m_from_time != old_from || m_to_time != old_to ||
m_min_price != old_minp || m_max_price != old_maxp)
{
OnViewChanged();
2026-06-10 18:31:08 +02:00
}
2026-06-10 23:03:10 +02:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int VisualSystem::GetScaledFontSize(int baseSizePoints) const
{
return (int)MathMax(6.0, baseSizePoints - (m_zoom - 2));
}
2026-06-10 18:31:08 +02:00
2026-06-10 23:03:10 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void VisualSystem::DeleteAllTextObjects()
{
for(int i = 0; i < m_count; i++)
if(CheckPointer(m_levels[i]) != POINTER_INVALID)
m_levels[i].DeleteText();
2026-06-23 21:44:37 +02:00
2026-06-10 23:03:10 +02:00
}
// Emergency cleanup – not used in normal render but kept for completeness
void VisualSystem::DeleteAllVisualObjects()
{
for(int i = 0; i < m_count; i++)
if(CheckPointer(m_levels[i]) != POINTER_INVALID)
m_levels[i].Delete();
2026-06-10 18:31:08 +02:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void VisualSystem::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
if(id == CHARTEVENT_CHART_CHANGE)
UpdateViewBounds();
}
2026-06-10 23:03:10 +02:00
2026-06-10 18:31:08 +02:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void VisualSystem::OnViewChanged(void)
{
Render();
2026-06-23 21:44:37 +02:00
// Print("OnViewChanged");
2026-06-10 18:31:08 +02:00
}
//+------------------------------------------------------------------+
2026-06-10 23:03:10 +02:00
//| |
//+------------------------------------------------------------------+
2026-06-23 21:44:37 +02:00
void VisualSystem::OnZoomChanged(long newZoom, long oldZoom)
2026-06-10 23:03:10 +02:00
{
if(!m_min_zoom_reached_already)
for(int i = 0; i < m_count; i++)
if(CheckPointer(m_levels[i]) != POINTER_INVALID)
m_levels[i].SetFontSize(GetScaledFontSize(12));
}
2026-06-23 21:44:37 +02:00
//+------------------------------------------------------------------+