401 lines
12 KiB
MQL5
401 lines
12 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| URendererCanvas.mqh |
| |||
//| Copyright 2026, DearMQL5 GUI Engine |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Copyright 2026, DearMQL5 GUI Engine"
| |||
#property link "https://www.mql5.com"
| |||
#property strict
| |||
| |||
#include <Canvas\Canvas.mqh>
| |||
#include "..\Util\UDiagnostics.mqh"
| |||
#include "..\Util\UMath.mqh"
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| CUWindowSurface: Dedicated Canvas resource for an isolated HUD |
| |||
//+------------------------------------------------------------------+
| |||
class CUWindowSurface
| |||
{
| |||
private:
| |||
CCanvas m_canvas;
| |||
ulong m_win_id;
| |||
string m_title;
| |||
string m_obj_name;
| |||
long m_chart_id;
| |||
int m_subwindow;
| |||
int m_x;
| |||
int m_y;
| |||
int m_width;
| |||
int m_height;
| |||
bool m_is_created;
| |||
bool m_is_dirty;
| |||
| |||
public:
| |||
CUWindowSurface(void)
| |||
: m_win_id(0), m_title(""), m_obj_name(""), m_chart_id(0), m_subwindow(0),
| |||
m_x(0), m_y(0), m_width(0), m_height(0), m_is_created(false), m_is_dirty(false)
| |||
{
| |||
}
| |||
| |||
~CUWindowSurface(void)
| |||
{
| |||
Destroy();
| |||
}
| |||
| |||
ulong GetWinId(void) const { return(m_win_id); }
| |||
int GetX(void) const { return(m_x); }
| |||
int GetY(void) const { return(m_y); }
| |||
int GetWidth(void) const { return(m_width); }
| |||
int GetHeight(void) const { return(m_height); }
| |||
bool IsDirty(void) const { return(m_is_dirty); }
| |||
void SetDirty(void) { m_is_dirty = true; }
| |||
| |||
bool Configure(const ulong win_id, const string title, const long chart_id, const int subwin,
| |||
const int x, const int y, const int w, const int h)
| |||
{
| |||
m_win_id = win_id;
| |||
m_title = title;
| |||
m_chart_id = (chart_id == 0) ? ChartID() : chart_id;
| |||
m_subwindow = subwin;
| |||
m_x = x;
| |||
m_y = y;
| |||
m_width = (w > 0) ? w : 260;
| |||
m_height = (h > 0) ? h : 320;
| |||
| |||
if(m_obj_name == "")
| |||
m_obj_name = StringFormat("DearMQL5_Hub_%I64u_%I64d", win_id, m_chart_id);
| |||
| |||
return(EnsureCanvas());
| |||
}
| |||
| |||
bool EnsureCanvas(void)
| |||
{
| |||
if(m_is_created)
| |||
{
| |||
if(m_width != m_canvas.Width() || m_height != m_canvas.Height())
| |||
{
| |||
m_canvas.Resize(m_width, m_height);
| |||
m_is_dirty = true;
| |||
}
| |||
return(true);
| |||
}
| |||
| |||
if(ObjectFind(m_chart_id, m_obj_name) >= 0)
| |||
ObjectDelete(m_chart_id, m_obj_name);
| |||
| |||
if(!m_canvas.CreateBitmapLabel(m_chart_id, m_subwindow, m_obj_name, m_x, m_y, m_width, m_height, COLOR_FORMAT_ARGB_NORMALIZE))
| |||
return(false);
| |||
| |||
ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_SELECTABLE, false);
| |||
ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_SELECTED, false);
| |||
ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_ZORDER, 50);
| |||
| |||
m_is_created = true;
| |||
m_is_dirty = true;
| |||
return(true);
| |||
}
| |||
| |||
void SetPosition(const int new_x, const int new_y)
| |||
{
| |||
m_x = new_x;
| |||
m_y = new_y;
| |||
if(m_is_created)
| |||
{
| |||
ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_XDISTANCE, m_x);
| |||
ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_YDISTANCE, m_y);
| |||
}
| |||
}
| |||
| |||
void SetSize(const int new_w, const int new_h)
| |||
{
| |||
if(new_w > 0 && new_h > 0 && (m_width != new_w || m_height != new_h))
| |||
{
| |||
m_width = new_w;
| |||
m_height = new_h;
| |||
if(m_is_created)
| |||
{
| |||
m_canvas.Resize(m_width, m_height);
| |||
m_is_dirty = true;
| |||
}
| |||
}
| |||
}
| |||
| |||
void BringToFront(void)
| |||
{
| |||
if(m_is_created)
| |||
ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_ZORDER, 100);
| |||
}
| |||
| |||
void SetNormalZOrder(void)
| |||
{
| |||
if(m_is_created)
| |||
ObjectSetInteger(m_chart_id, m_obj_name, OBJPROP_ZORDER, 50);
| |||
}
| |||
| |||
void Erase(const uint argb = 0x00000000)
| |||
{
| |||
if(m_is_created)
| |||
m_canvas.Erase(argb);
| |||
}
| |||
| |||
void Update(void)
| |||
{
| |||
if(m_is_created && m_is_dirty)
| |||
{
| |||
m_canvas.Update();
| |||
m_is_dirty = false;
| |||
}
| |||
}
| |||
| |||
void Destroy(void)
| |||
{
| |||
if(m_is_created)
| |||
{
| |||
m_canvas.Destroy();
| |||
ObjectDelete(m_chart_id, m_obj_name);
| |||
m_is_created = false;
| |||
}
| |||
}
| |||
| |||
void FillRectangle(const int x1, const int y1, const int x2, const int y2, const uint clr)
| |||
{
| |||
if(m_is_created)
| |||
{
| |||
m_canvas.FillRectangle(x1, y1, x2, y2, clr);
| |||
m_is_dirty = true;
| |||
}
| |||
}
| |||
| |||
void Rectangle(const int x1, const int y1, const int x2, const int y2, const uint clr)
| |||
{
| |||
if(m_is_created)
| |||
{
| |||
m_canvas.Rectangle(x1, y1, x2, y2, clr);
| |||
m_is_dirty = true;
| |||
}
| |||
}
| |||
| |||
void Line(const int x1, const int y1, const int x2, const int y2, const uint clr)
| |||
{
| |||
if(m_is_created)
| |||
{
| |||
m_canvas.Line(x1, y1, x2, y2, clr);
| |||
m_is_dirty = true;
| |||
}
| |||
}
| |||
| |||
void TextOut(const int x, const int y, const string text, const uint clr,
| |||
const string font_name, const int font_size, const uint flags)
| |||
{
| |||
if(m_is_created && StringLen(text) > 0)
| |||
{
| |||
m_canvas.FontSet(font_name, -font_size * 10);
| |||
m_canvas.TextOut(x, y, text, clr, flags);
| |||
m_is_dirty = true;
| |||
}
| |||
}
| |||
};
| |||
| |||
/// Hardware-accelerated immediate-mode renderer managing per-hub CCanvas surfaces.
| |||
class URendererCanvas
| |||
{
| |||
private:
| |||
static CUWindowSurface* s_surfaces[];
| |||
static int s_surface_count;
| |||
static CUWindowSurface* s_cur_surface;
| |||
static long s_chart_id;
| |||
static int s_subwindow;
| |||
static bool s_clip_active;
| |||
static int s_clip_x, s_clip_y, s_clip_w, s_clip_h;
| |||
| |||
static CUWindowSurface* FindSurface(const ulong win_id)
| |||
{
| |||
for(int i = 0; i < s_surface_count; i++)
| |||
{
| |||
if(CheckPointer(s_surfaces[i]) != POINTER_INVALID && s_surfaces[i].GetWinId() == win_id)
| |||
return(s_surfaces[i]);
| |||
}
| |||
return(NULL);
| |||
}
| |||
| |||
static CUWindowSurface* GetOrCreateSurface(const ulong win_id, const string title,
| |||
const long chart_id, const int subwin,
| |||
const int x, const int y, const int w, const int h)
| |||
{
| |||
CUWindowSurface* surf = FindSurface(win_id);
| |||
if(surf != NULL)
| |||
{
| |||
surf.Configure(win_id, title, chart_id, subwin, x, y, w, h);
| |||
return(surf);
| |||
}
| |||
| |||
surf = new CUWindowSurface();
| |||
surf.Configure(win_id, title, chart_id, subwin, x, y, w, h);
| |||
| |||
int size = ArraySize(s_surfaces);
| |||
if(s_surface_count >= size)
| |||
ArrayResize(s_surfaces, (size == 0) ? 4 : size * 2);
| |||
| |||
s_surfaces[s_surface_count++] = surf;
| |||
return(surf);
| |||
}
| |||
| |||
public:
| |||
static bool Init(const long chart_id = 0, const int subwin = 0)
| |||
{
| |||
s_chart_id = (chart_id == 0) ? ChartID() : chart_id;
| |||
s_subwindow = subwin;
| |||
return(true);
| |||
}
| |||
| |||
static void Shutdown(void)
| |||
{
| |||
for(int i = 0; i < s_surface_count; i++)
| |||
{
| |||
if(CheckPointer(s_surfaces[i]) != POINTER_INVALID)
| |||
{
| |||
s_surfaces[i].Destroy();
| |||
delete s_surfaces[i];
| |||
}
| |||
}
| |||
ArrayFree(s_surfaces);
| |||
s_surface_count = 0;
| |||
s_cur_surface = NULL;
| |||
}
| |||
| |||
static void BeginFrame(void)
| |||
{
| |||
s_clip_active = false;
| |||
}
| |||
| |||
static void EndFrame(void)
| |||
{
| |||
for(int i = 0; i < s_surface_count; i++)
| |||
{
| |||
if(CheckPointer(s_surfaces[i]) != POINTER_INVALID)
| |||
{
| |||
s_surfaces[i].Update();
| |||
}
| |||
}
| |||
s_cur_surface = NULL;
| |||
}
| |||
| |||
static bool BeginWindowSurface(const ulong win_id, const string title,
| |||
const int x, const int y, const int w, const int h,
| |||
const long chart_id = 0, const int subwin = 0)
| |||
{
| |||
long cid = (chart_id == 0) ? s_chart_id : chart_id;
| |||
int sw = (subwin >= 0) ? subwin : s_subwindow;
| |||
| |||
s_cur_surface = GetOrCreateSurface(win_id, title, cid, sw, x, y, w, h);
| |||
if(s_cur_surface != NULL)
| |||
{
| |||
s_cur_surface.SetPosition(x, y);
| |||
s_cur_surface.SetSize(w, h);
| |||
s_cur_surface.Erase(0x00000000);
| |||
return(true);
| |||
}
| |||
return(false);
| |||
}
| |||
| |||
static void EndWindowSurface(void)
| |||
{
| |||
s_cur_surface = NULL;
| |||
}
| |||
| |||
static void SetWindowPosition(const ulong win_id, const int x, const int y)
| |||
{
| |||
CUWindowSurface* surf = FindSurface(win_id);
| |||
if(surf != NULL)
| |||
{
| |||
surf.SetPosition(x, y);
| |||
ChartRedraw(s_chart_id);
| |||
}
| |||
}
| |||
| |||
static void BringWindowToFront(const ulong win_id)
| |||
{
| |||
CUWindowSurface* surf = FindSurface(win_id);
| |||
if(surf != NULL)
| |||
surf.BringToFront();
| |||
}
| |||
| |||
static void ResetWindowZOrder(const ulong win_id)
| |||
{
| |||
CUWindowSurface* surf = FindSurface(win_id);
| |||
if(surf != NULL)
| |||
surf.SetNormalZOrder();
| |||
}
| |||
| |||
static void DrawRectFilled(const int x, const int y, const int w, const int h, const uint argb)
| |||
{
| |||
if(s_cur_surface == NULL || w <= 0 || h <= 0) return;
| |||
int lx = x - s_cur_surface.GetX();
| |||
int ly = y - s_cur_surface.GetY();
| |||
s_cur_surface.FillRectangle(lx, ly, lx + w, ly + h, argb);
| |||
UDiagnostics::IncrementDrawCalls();
| |||
UDiagnostics::SetDirty();
| |||
}
| |||
| |||
static void DrawRect(const int x, const int y, const int w, const int h, const uint argb)
| |||
{
| |||
if(s_cur_surface == NULL || w <= 0 || h <= 0) return;
| |||
int lx = x - s_cur_surface.GetX();
| |||
int ly = y - s_cur_surface.GetY();
| |||
s_cur_surface.Rectangle(lx, ly, lx + w, ly + h, argb);
| |||
UDiagnostics::IncrementDrawCalls();
| |||
UDiagnostics::SetDirty();
| |||
}
| |||
| |||
static void DrawLine(const int x1, const int y1, const int x2, const int y2, const uint argb)
| |||
{
| |||
if(s_cur_surface == NULL) return;
| |||
int lx1 = x1 - s_cur_surface.GetX();
| |||
int ly1 = y1 - s_cur_surface.GetY();
| |||
int lx2 = x2 - s_cur_surface.GetX();
| |||
int ly2 = y2 - s_cur_surface.GetY();
| |||
s_cur_surface.Line(lx1, ly1, lx2, ly2, argb);
| |||
UDiagnostics::IncrementDrawCalls();
| |||
UDiagnostics::SetDirty();
| |||
}
| |||
| |||
static void DrawText(const int x, const int y,
| |||
const string text,
| |||
const uint argb,
| |||
const string font_name = "Segoe UI",
| |||
const int font_size = 9,
| |||
const uint flags = 0)
| |||
{
| |||
if(s_cur_surface == NULL || StringLen(text) == 0) return;
| |||
int lx = x - s_cur_surface.GetX();
| |||
int ly = y - s_cur_surface.GetY();
| |||
s_cur_surface.TextOut(lx, ly, text, argb, font_name, font_size, flags);
| |||
UDiagnostics::IncrementDrawCalls();
| |||
UDiagnostics::SetDirty();
| |||
}
| |||
| |||
static void PushClipRect(const int x, const int y, const int w, const int h)
| |||
{
| |||
s_clip_active = true;
| |||
s_clip_x = x;
| |||
s_clip_y = y;
| |||
s_clip_w = w;
| |||
s_clip_h = h;
| |||
}
| |||
| |||
static void PopClipRect(void)
| |||
{
| |||
s_clip_active = false;
| |||
}
| |||
};
| |||
| |||
CUWindowSurface* URendererCanvas::s_surfaces[];
| |||
int URendererCanvas::s_surface_count = 0;
| |||
CUWindowSurface* URendererCanvas::s_cur_surface = NULL;
| |||
long URendererCanvas::s_chart_id = 0;
| |||
int URendererCanvas::s_subwindow = 0;
| |||
bool URendererCanvas::s_clip_active = false;
| |||
int URendererCanvas::s_clip_x = 0;
| |||
int URendererCanvas::s_clip_y = 0;
| |||
int URendererCanvas::s_clip_w = 0;
| |||
int URendererCanvas::s_clip_h = 0;
|