//+------------------------------------------------------------------+ //| Screen.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include #include "ZoneObject.mqh" #include "ScreenListener.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class Screen { private: long m_chart_id; int m_width, m_height; long m_zoom; int m_fontSize; long m_min_zoom_to_keep_texts; bool m_min_zoom_reached_already; datetime m_from_time, m_to_time; double m_min_price, m_max_price; CArrayList* objects; void UpdateViewBounds(); void OnZoomChanged(); void OnViewChanged(); void OnMinZoomReached(); int FindExist(ZoneObject* zoneObject) { for(int i = objects.Size() - 1; i >= 0; i--) { ZoneObject* z = objects.At(i); if(z.GetName() == zoneObject.GetName() && z.GetName() == zoneObject.GetName()) { return i; } } return -1; } int GetScaledFontSize(int baseSizePoints) const { return (int)MathMax(6.0, baseSizePoints - (m_zoom - 2)); } public: Screen(): m_fontSize(12), m_chart_id(ChartID()) { objects = new CArrayList(); UpdateViewBounds(); } ~Screen() {delete objects;} void SetMinZoomForTexts(long zoomLevel) { m_min_zoom_to_keep_texts = zoomLevel; } void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); void AddLevel(ZoneObject* zoneObject); void Delete(string name) { for(int i = objects.Size() - 1; i >= 0; i--) { ZoneObject* z = objects.At(i); // returns pointer if(z == NULL) continue; // ✅ Use . for pointers in MQL5 if(z.GetName() == name) // ❌ change to z.GetName() { z.Delete(); //Print(z.GetText()); // ✅ also use . here bool removed = objects.RemoveAt(i); // Print("RemoveAt(", i, ") returned: ", removed); } } } void Render(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Screen::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_CHART_CHANGE) { UpdateViewBounds(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Screen::AddLevel(ZoneObject* zoneObject) { if(zoneObject == NULL) return; if(zoneObject.GetSymbol() != _Symbol) { Print("Error Adding Levels"); return; } int ind = FindExist(zoneObject); if(ind < 0) { objects.Add(zoneObject); Render(); return; } ZoneObject* z = objects.At(ind); PointLevel* supply = dynamic_cast(z); TrendLevel* trend = dynamic_cast(zoneObject); if(supply != NULL && trend != NULL) { objects.RemoveAt(ind); delete z; objects.AddAt(ind, zoneObject); Render(); return; } z.SetA(zoneObject.GetA()); z.SetB(zoneObject.GetB()); delete zoneObject; Render(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Screen::Render() { UpdateViewBounds(); bool boundsValid = (m_from_time < m_to_time && m_min_price < m_max_price); for(int i = objects.Size() - 1; i >= 0; i--) { objects.At(i).UpdateDraw((int)ChartGetInteger(m_chart_id, CHART_SCALE), m_from_time, m_to_time, m_min_price, m_max_price, (int)m_min_zoom_to_keep_texts); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Screen::OnZoomChanged() { if(!m_min_zoom_reached_already) { for(int i = objects.Size() - 1; i >= 0; i--) { objects.At(i).SetFontSize(GetScaledFontSize(m_fontSize)); } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Screen::OnViewChanged() { Render(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Screen::OnMinZoomReached() { for(int i = objects.Size() - 1; i >= 0; i--) { objects.At(i).DeleteAllText(); } } //+------------------------------------------------------------------+ //| View bounds update | //+------------------------------------------------------------------+ void Screen::UpdateViewBounds() { long old_zoom = m_zoom; datetime old_from = m_from_time; datetime old_to = m_to_time; double old_minp = m_min_price; double old_maxp = m_max_price; 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); if(m_zoom != old_zoom) { OnZoomChanged(); bool below_threshold = (m_zoom < m_min_zoom_to_keep_texts); if(below_threshold && !m_min_zoom_reached_already) { OnMinZoomReached(); m_min_zoom_reached_already = true; } else if(!below_threshold) { m_min_zoom_reached_already = false; // Render(); } } if(m_from_time != old_from || m_to_time != old_to || m_min_price != old_minp || m_max_price != old_maxp) { OnViewChanged(); } } //+------------------------------------------------------------------+