DearMQL5/Core/ULayout.mqh

132 lines
4.3 KiB
MQL5

feat: initial release of DearMQL5 v1.0.0 - Immediate-Mode GUI Engine for MetaTrader 5 DearMQL5 is an institutional-grade, immediate-mode graphical user interface (GUI) engine designed from the ground up for MetaTrader 5 (MQL5), inspired by the ergonomics and performance of Dear ImGui. ### Core Architecture & Execution Pipeline - Immediate-Mode Paradigm: Declarative frame-based rendering pipeline with zero memory allocation churn during render loops and deterministic execution. - Modular Per-Hub Canvas Surfaces (CUWindowSurface): Replaces full-screen canvas overlays with isolated, window-bounded CCanvas bitmaps, reducing memory usage by up to 93% (~570 KB vs 8.3 MB for 1080p) and preventing click-swallowing. - 0-Cost Hardware Texture Drag Engine: Absolute-anchor drag mechanics update native MT5 OBJPROP_XDISTANCE and OBJPROP_YDISTANCE properties directly on the compositor, bypassing CPU rasterization completely (0.00% CPU usage). - Chart Scroll Protection: Automatically suppresses chart history sliding (CHART_MOUSE_SCROLL) during active HUD dragging and safely restores it on release. - Two-Phase Hit-Testing: Interactive controls (buttons, sliders, toggles) receive first-priority click focus, cleanly separating widget actions from window drags. ### Subsystems & Components - Core State & Input (Core/): * UInputState: Mouse/keyboard tracking, deltas, and chart scroll lock. * UState: Retained state hash map in pure MQL5 with 64-bit FNV-1a hashing. * UIdStack: Hierarchical identifier scoping preventing widget ID collisions. * ULayout: Automatic cursor placement supporting SameLine, Indent, and Unindent. * UEventQueue: Non-blocking circular UI event queue for decoupled handling. * UTheme: Institutional OneDark/Nord dark color palette and typography metrics. - Rendering Backends (Render/): * URendererCanvas: Isolated per-hub surfaces with 32-bit ARGB alpha transparency. * URendererNative: Native MT5 object pool (OBJ_RECTANGLE_LABEL) with diff caching. * UFont: Anti-aliased font metrics and width cache. - Complete Widget Suite (Widgets/): * UButton: Click-on-release standard, accent, and emergency panic buttons. * UToggle: Smooth draggable/clickable iOS-style toggle switches. * USlider: Draggable horizontal float and integer sliders. * UProgressBar: Adaptive progress bars with dynamic green->amber->red color ramps. * UHeader: Collapsible section headers with immediate-mode state retention. * ULabel & USeparator: Formatted text, colored metrics, and horizontal dividers. - Window Management (Windows/): * UWindow: Window container with titlebar, collapse toggle, and coordinate clipping. * UWindowManager: Multi-window coordinator with GlobalVariable persistence across chart timeframe changes and backward-compatible key resolution. ### Tooling, Documentation & Reference Implementation - Examples/EA_DearMQL5_Demo.mq5: Complete reference Prop-Firm cockpit HUD. - README.md: Comprehensive institutional English documentation for GitHub and Algo Forge. - Docs/ARCHITECTURE.md: Technical architecture specification with Mermaid lifecycle.
2026-10-12 08:18:03 -03:00
//+------------------------------------------------------------------+
//| ULayout.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 "UTheme.mqh"
/// Automatic cursor positioning and layout coordinator for immediate-mode rendering.
class ULayout
{
private:
static int s_window_x;
static int s_window_y;
static int s_window_w;
static int s_window_h;
static int s_cursor_x;
static int s_cursor_y;
static int s_prev_item_x;
static int s_prev_item_y;
static int s_prev_item_w;
static int s_prev_item_h;
static int s_indent_level;
static bool s_same_line;
static int s_same_line_spacing;
public:
/// Begins layout for a window boundary.
static void BeginWindow(const int x, const int y, const int w, const int h, const int title_h)
{
SUTheme th = UTheme::Get();
s_window_x = x;
s_window_y = y;
s_window_w = w;
s_window_h = h;
s_cursor_x = x + th.padding_x;
s_cursor_y = y + title_h + th.padding_y;
s_indent_level = 0;
s_same_line = false;
s_same_line_spacing = 0;
s_prev_item_x = s_cursor_x;
s_prev_item_y = s_cursor_y;
s_prev_item_w = 0;
s_prev_item_h = 0;
}
/// Directs the next widget to be placed on the same horizontal line.
static void SameLine(const int spacing = -1)
{
SUTheme th = UTheme::Get();
s_same_line = true;
s_same_line_spacing = (spacing >= 0) ? spacing : th.item_spacing_x;
}
/// Increases the horizontal indentation for subsequent widgets.
static void Indent(const int amount = 16)
{
s_indent_level += amount;
s_cursor_x += amount;
}
/// Restores indentation.
static void Unindent(const int amount = 16)
{
s_indent_level = MathMax(0, s_indent_level - amount);
s_cursor_x = MathMax(s_window_x + UTheme::Get().padding_x, s_cursor_x - amount);
}
/// Reserves a bounding box for a widget and advances cursor accordingly.
static void AllocateSpace(const int w, const int h, int &out_x, int &out_y)
{
SUTheme th = UTheme::Get();
if(s_same_line)
{
out_x = s_prev_item_x + s_prev_item_w + s_same_line_spacing;
out_y = s_prev_item_y;
s_same_line = false;
}
else
{
out_x = s_window_x + th.padding_x + s_indent_level;
out_y = s_cursor_y;
}
// Record this item's metrics
s_prev_item_x = out_x;
s_prev_item_y = out_y;
s_prev_item_w = w;
s_prev_item_h = h;
// Advance cursor for next item
s_cursor_x = out_x + w + th.item_spacing_x;
s_cursor_y = MathMax(s_cursor_y, out_y + h + th.item_spacing_y);
}
/// Returns the available horizontal width for widgets in the current window.
static int GetAvailableWidth(void)
{
SUTheme th = UTheme::Get();
return(MathMax(20, s_window_w - (th.padding_x * 2) - s_indent_level));
}
/// Returns the current cursor X position.
static int GetCursorX(void) { return(s_cursor_x); }
/// Returns the current cursor Y position.
static int GetCursorY(void) { return(s_cursor_y); }
/// Manually sets cursor X position.
static void SetCursorX(const int x) { s_cursor_x = x; }
/// Manually sets cursor Y position.
static void SetCursorY(const int y) { s_cursor_y = y; }
};
int ULayout::s_window_x = 0;
int ULayout::s_window_y = 0;
int ULayout::s_window_w = 0;
int ULayout::s_window_h = 0;
int ULayout::s_cursor_x = 0;
int ULayout::s_cursor_y = 0;
int ULayout::s_prev_item_x = 0;
int ULayout::s_prev_item_y = 0;
int ULayout::s_prev_item_w = 0;
int ULayout::s_prev_item_h = 0;
int ULayout::s_indent_level = 0;
bool ULayout::s_same_line = false;
int ULayout::s_same_line_spacing = 0;