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.
145 lines
7.2 KiB
Markdown
145 lines
7.2 KiB
Markdown
# DearMQL5 Technical Architecture Specification
| |
| |
This document provides a technical deep-dive into the internal subsystems of **DearMQL5**, including its immediate-mode execution pipeline, input state machine, retained state management, and the per-hub canvas rendering backend.
| |
| |
---
| |
| |
## 1. Subsystem Architecture
| |
| |
DearMQL5 is partitioned into decoupled layers with strict separation of concerns:
| |
| |
```
| |
┌────────────────────────────────────────────────────────┐
| |
│ Public API Facade │
| |
│ (UI.mqh) │
| |
└───────────┬────────────────────────────────┬───────────┘
| |
│ │
| |
▼ ▼
| |
┌───────────────────────┐ ┌───────────────────────┐
| |
│ Window Containers │ │ Widget Suite │
| |
│ (UWindow, ULayout) │ │ (UButton, USlider...) │
| |
└───────────┬───────────┘ └───────────┬───────────┘
| |
│ │
| |
└────────────────┬───────────────┘
| |
▼
| |
┌────────────────────────────────────────────────────────┐
| |
│ Core State & Layout │
| |
│ - UInputState (Mouse, Keyboard, Dragging, Scroll) │
| |
│ - UState (Retained State Hash Map) │
| |
│ - UIdStack (FNV-1a Hierarchical Hashing) │
| |
│ - UTheme (Color Palette & Styling) │
| |
└────────────────────────────┬───────────────────────────┘
| |
▼
| |
┌────────────────────────────────────────────────────────┐
| |
│ Rendering Backend │
| |
│ - URendererCanvas (Isolated CUWindowSurface Pool) │
| |
│ - URendererNative (OBJ_RECTANGLE_LABEL Pool) │
| |
│ - UFont (Font Metrics & Text Width Cache) │
| |
└────────────────────────────────────────────────────────┘
| |
```
| |
| |
---
| |
| |
## 2. The Frame Lifecycle Pipeline
| |
| |
Every execution cycle in an Expert Advisor or Indicator follows a three-stage pipeline:
| |
| |
```mermaid
| |
sequenceDiagram
| |
participant EA as Expert Advisor
| |
participant UI as UI Facade
| |
participant Input as UInputState
| |
participant Win as UWindow
| |
participant Surf as CUWindowSurface
| |
participant MT5 as MT5 Compositor
| |
| |
EA->>UI: OnChartEvent(id, lparam, dparam, sparam)
| |
UI->>Input: OnMouseMove(x, y, flags)
| |
| |
EA->>UI: NewFrame()
| |
UI->>Input: NewFrame() (Compute Deltas, Retain Active)
| |
| |
EA->>UI: Begin("Cockpit", open, x, y, w, h)
| |
UI->>Win: Begin(...) (Resolve Anchor Drag & Collapses)
| |
UI->>Surf: BeginWindowSurface(win_id, x, y, w, h)
| |
| |
EA->>UI: Button / Slider / Text Widgets
| |
UI->>Surf: DrawRect / DrawText (Local Coordinates)
| |
| |
EA->>UI: End()
| |
UI->>Win: End() (Promote Drag Candidates)
| |
| |
EA->>UI: Render()
| |
UI->>Surf: Update() (Commit Dirty Bitmaps)
| |
UI->>MT5: ChartRedraw()
| |
```
| |
| |
### Stage 1: Input Aggregation (`NewFrame`)
| |
1. Receives mouse position `(x, y)` and button flags from MT5's `CHARTEVENT_MOUSE_MOVE`.
| |
2. Computes movement delta: `delta_x = mouse_x - prev_mouse_x` before updating previous coordinates.
| |
3. Retains previous frame's `s_active_id` into `s_prev_active_id` so that release events (`s_mouse_released`) are properly recognized by widgets.
| |
4. Resets `s_hot_id = 0` to prepare for the frame's hit-testing pass.
| |
| |
### Stage 2: Widget Evaluation & Layout (`Begin ... End`)
| |
1. `UI::Begin`:
| |
- Retrieves persistent coordinates `(x, y, w, h)` from `UState` or `UWindowManager`.
| |
- Tests collapse button first to eliminate drag collision.
| |
- Evaluates window drag engine using absolute anchors.
| |
- Binds the active window surface in `URendererCanvas` and initializes `ULayout`.
| |
2. Widgets evaluate their bounding boxes and hit-testing in `UWidget::Behavior`.
| |
- If mouse is within bounding box, registers widget as `Hot`.
| |
- If clicked, claims `Active` and keyboard focus.
| |
- If released while `Active` and `Hot`, returns `clicked = true`.
| |
3. `UI::End`:
| |
- If the window was clicked and no child widget claimed active interaction, the window promotes candidate drag to active drag.
| |
- Restores layout and pops ID stack.
| |
| |
### Stage 3: Rendering & Compositing (`Render`)
| |
1. Only surfaces marked with `is_dirty = true` execute `CCanvas::Update()`.
| |
2. `UInputState::EndFrame()` cleans up any residual active captures.
| |
3. `UDiagnostics` records frame duration and total draw calls.
| |
| |
---
| |
| |
## 3. Retained State Engine (`UState`)
| |
| |
Although DearMQL5's API is immediate-mode, state must persist between frames (e.g., whether a header is open, current slider value, or window positions).
| |
| |
- **64-bit FNV-1a Hash**: Each widget ID is hashed:
| |
$$\text{hash} = (\text{hash} \oplus \text{byte}) \times 1099511628211$$
| |
- **Pre-Allocated Entry Array**: Stored in a contiguous array of `SUStateEntry` with geometric resizing ($64 \to 128 \to 256$), avoiding heap allocations after initial warm-up.
| |
- **Frame Tracking**: Each entry logs `last_frame` for automatic garbage collection of dead widgets.
| |
| |
---
| |
| |
## 4. Per-Hub Canvas Architecture (`CUWindowSurface`)
| |
| |
Unlike traditional full-chart canvas models:
| |
1. Each window owns a dedicated `CCanvas` instance mapped to a uniquely named chart bitmap: `DearMQL5_Hub_<win_id>_<chart_id>`.
| |
2. **Local Coordinate Translation**: Widgets submit global coordinates `(gx, gy)`. `URendererCanvas` automatically translates:
| |
$$lx = gx - \text{surface.GetX()}$$
| |
$$ly = gy - \text{surface.GetY()}$$
| |
3. **Zero-Cost Drag**:
| |
When a window moves from $(x_1, y_1)$ to $(x_2, y_2)$, the canvas pixels are untouched. DearMQL5 only calls:
| |
```mql5
| |
ObjectSetInteger(chart_id, obj_name, OBJPROP_XDISTANCE, x2);
| |
ObjectSetInteger(chart_id, obj_name, OBJPROP_YDISTANCE, y2);
| |
```
| |
MT5's internal DirectX compositor shifts the bitmap directly on the screen at zero CPU rasterization cost.
| |
| |
---
| |
| |
## 5. Chart Scroll Suppression & Interaction Guard
| |
| |
When dragging standard graphical objects on MetaTrader charts, MT5's default handler attempts to scroll price history horizontally.
| |
| |
DearMQL5 mitigates this using automatic scroll locking:
| |
```mql5
| |
// On Drag Start:
| |
ChartSetInteger(chart_id, CHART_MOUSE_SCROLL, false);
| |
| |
// On Drag Release:
| |
ChartSetInteger(chart_id, CHART_MOUSE_SCROLL, true);
| |
```
| |
| |
This guarantees smooth HUD movement without disrupting chart viewports.
|