//+------------------------------------------------------------------+ //| ControlPanel.mqh | //| AnimateDread | //| https://www.mql5.com | //+------------------------------------------------------------------+ //--- Built on the standard MQL5 Controls library (CAppDialog/CButton). Per the library's own source //--- (Controls\Wnd.mqh CWnd::OnEvent, Controls\Dialog.mqh CAppDialog::ChartEvent), every control in //--- this framework - including the dialog's own caption drag and close/minimize buttons - detects //--- clicks exclusively through CHARTEVENT_MOUSE_MOVE hit-testing, which raises an internal //--- EventChartCustom(CONTROLS_SELF_MESSAGE, ON_CLICK, control.Id(), ...) event; CAppDialog::ChartEvent() //--- explicitly ignores CHARTEVENT_OBJECT_CLICK outright ("we are working with the CHARTEVENT_MOUSE_MOVE //--- events"). So button clicks are wired the same way CDialog wires its own Close button - an //--- EVENT_MAP/ON_EVENT(ON_CLICK, control, handler) entry - not by matching object names against //--- CHARTEVENT_OBJECT_CLICK's sparam in the EA's OnChartEvent(). #include "..\Enumerations\GlobalEnums.mqh" #include #include #define CP_PANEL_W 176 #define CP_ROW_H 24 #define CP_MARGIN 8 #define CP_BTN_H 20 #define CP_CAPTION_H 20 #define CP_NUM_BUTTONS 9 //--- total outer height needed for the dialog (caption bar + top margin + N button rows + bottom margin) #define CP_PANEL_H (CP_CAPTION_H + CP_MARGIN + CP_NUM_BUTTONS * CP_ROW_H + CP_MARGIN) //--- gap left between the status label's last line and the minimized panel docked under it #define CP_DOCK_GAP 6 class CControlPanel : public CAppDialog { private: CButton m_signalsBtn; CButton m_pauseBtn; CButton m_stopBtn; CButton m_deployBtn; CButton m_saveBtn; CButton m_loadBtn; CButton m_resetBtn; CButton m_reportBtn; CButton m_resetDbBtn; ENUM_CP_ACTION m_pendingAction; //--- Where the panel was before it was last minimized, so Maximize() can put it back instead of //--- leaving it wherever Minimize() docked it - see Minimize()'s declaration comment. int m_restoreX; int m_restoreY; bool m_restoreSaved; bool CreateOneButton(CButton &btn, const string name, const int y, const string text); bool CreateButtons(void); protected: //--- ON_CLICK handlers (see EVENT_MAP below) - just record which button fired; the EA polls //--- ConsumeAction() once per OnChartEvent() call and performs the actual work void OnClickSignals(void) { m_pendingAction = CP_ACTION_TOGGLE_SIGNALS; } void OnClickPause(void) { m_pendingAction = CP_ACTION_TOGGLE_PAUSE; } void OnClickStop(void) { m_pendingAction = CP_ACTION_TOGGLE_STOP; } void OnClickDeploy(void) { m_pendingAction = CP_ACTION_TOGGLE_DEPLOY; } void OnClickSave(void) { m_pendingAction = CP_ACTION_SAVE; } void OnClickLoad(void) { m_pendingAction = CP_ACTION_LOAD; } void OnClickReset(void) { m_pendingAction = CP_ACTION_RESET; } void OnClickReport(void) { m_pendingAction = CP_ACTION_REPORT; } void OnClickResetDb(void) { m_pendingAction = CP_ACTION_RESET_DB; } public: CControlPanel(void) : m_pendingAction(CP_ACTION_NONE), m_restoreX(0), m_restoreY(0), m_restoreSaved(false) {} ~CControlPanel(void) {} virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); //--- chart event handler (EVENT_MAP macro below generates the body) virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); //--- CAppDialog's default OnClickButtonClose() calls Destroy(), which for a PROGRAM_EXPERT calls //--- ExpertRemove() - i.e. the caption bar's "x" would silently detach this EA from the chart and //--- stop live trading/training. Redirect it to Minimize() so "x" behaves like the native "-" button //--- instead of destroying the EA. virtual void OnClickButtonClose(void) override { Minimize(); } //--- Docks the minimized bar directly under the status label (System\StatusLabel.mqh) rather than //--- leaving it at its maximized position, which - since the label's line count/height changes //--- live with training progress - could otherwise sit ON TOP of the label text (user request //--- 2026-08-16: minimized should go under the label, not overlap it). Position is remembered so //--- Maximize() below can restore it. //--- Return type is VOID, matching CAppDialog's own `virtual void Minimize(void)` / //--- `virtual void Maximize(void)` (Controls\Dialog.mqh). Declaring these `bool` is a hard compile //--- error (265 "overriding virtual function with different return type" + 404 "does not override //--- any base class method", plus 151 on assigning the void call to a bool) - the base pair returns //--- nothing, so there is no success flag to forward. virtual void Minimize(void) override { if(!m_restoreSaved) { m_restoreX = Left(); m_restoreY = Top(); m_restoreSaved = true; } CAppDialog::Minimize(); Move(Left(), StatusLabelBottomY() + CP_DOCK_GAP); } //--- Restores the position Minimize() displaced it from, rather than leaving a maximized panel //--- parked under the label where it was only ever meant to sit while minimized. virtual void Maximize(void) override { CAppDialog::Maximize(); if(m_restoreSaved) { Move(m_restoreX, m_restoreY); m_restoreSaved = false; } } //--- keeps button labels synced with live training/signal-visibility state void SetSignalsText(const string text) { m_signalsBtn.Text(text); } void SetPauseText(const string text) { m_pauseBtn.Text(text); } void SetStopText(const string text) { m_stopBtn.Text(text); } void SetDeployText(const string text) { m_deployBtn.Text(text); } //--- returns and clears whichever button (if any) was clicked since the last call ENUM_CP_ACTION ConsumeAction(void) { ENUM_CP_ACTION a = m_pendingAction; m_pendingAction = CP_ACTION_NONE; return a; } //--- Maximize() is protected in CAppDialog; expose it so the EA can defensively un-minimize on creation void ForceMaximize(void) { Maximize(); } }; //+------------------------------------------------------------------+ //| Event map - same mechanism CDialog uses internally for its own | //| Close button (Controls\Dialog.mqh) | //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CControlPanel) ON_EVENT(ON_CLICK, m_signalsBtn, OnClickSignals) ON_EVENT(ON_CLICK, m_pauseBtn, OnClickPause) ON_EVENT(ON_CLICK, m_stopBtn, OnClickStop) ON_EVENT(ON_CLICK, m_deployBtn, OnClickDeploy) ON_EVENT(ON_CLICK, m_saveBtn, OnClickSave) ON_EVENT(ON_CLICK, m_loadBtn, OnClickLoad) ON_EVENT(ON_CLICK, m_resetBtn, OnClickReset) ON_EVENT(ON_CLICK, m_reportBtn, OnClickReport) ON_EVENT(ON_CLICK, m_resetDbBtn, OnClickResetDb) EVENT_MAP_END(CAppDialog) //+------------------------------------------------------------------+ bool CControlPanel::Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2) { if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2)) { Print("CControlPanel: CAppDialog::Create() failed, error " + IntegerToString(GetLastError())); return false; } if(!Caption("Warrior EA Control Panel")) { Print("CControlPanel: Caption() failed, error " + IntegerToString(GetLastError())); return false; } if(!CreateButtons()) return false; return true; } //+------------------------------------------------------------------+ bool CControlPanel::CreateOneButton(CButton &btn, const string name, const int y, const string text) { if(!btn.Create(m_chart_id, m_name + name, m_subwin, CP_MARGIN, y, CP_PANEL_W - CP_MARGIN, y + CP_BTN_H)) { Print("CControlPanel: button '" + name + "' Create() failed, error " + IntegerToString(GetLastError())); return false; } if(!btn.Text(text)) { Print("CControlPanel: button '" + name + "' Text() failed, error " + IntegerToString(GetLastError())); return false; } if(!Add(btn)) { Print("CControlPanel: button '" + name + "' Add() failed, error " + IntegerToString(GetLastError())); return false; } return true; } //+------------------------------------------------------------------+ bool CControlPanel::CreateButtons(void) { int y = CP_MARGIN; //--- Report first, so the two destructive (red) buttons below end up adjacent at the bottom instead //--- of having Report sandwiched between them. if(!CreateOneButton(m_reportBtn, "Report", y, "Export Trade Journal")) return false; y += CP_ROW_H; if(!CreateOneButton(m_signalsBtn, "Signals", y, "Hide Signals")) return false; y += CP_ROW_H; if(!CreateOneButton(m_pauseBtn, "Pause", y, "Pause Training")) return false; y += CP_ROW_H; if(!CreateOneButton(m_stopBtn, "Stop", y, "Stop Training")) return false; y += CP_ROW_H; //--- Sits directly under Stop: both end the current training run, but Deploy is the one that promotes //--- the result to THE model (and, clicked again, sends it back to training). Green so it reads as the //--- constructive counterpart to the red destructive pair at the bottom. if(!CreateOneButton(m_deployBtn, "Deploy", y, "Deploy Model")) return false; if(!m_deployBtn.ColorBackground(clrDarkGreen) || !m_deployBtn.Color(clrWhite)) { Print("CControlPanel: button 'Deploy' color styling failed, error " + IntegerToString(GetLastError())); return false; } y += CP_ROW_H; if(!CreateOneButton(m_saveBtn, "Save", y, "Save Weights Now")) return false; y += CP_ROW_H; if(!CreateOneButton(m_loadBtn, "Load", y, "Load Weights From Disk")) return false; y += CP_ROW_H; if(!CreateOneButton(m_resetBtn, "Reset", y, "Delete && Reset Weights")) return false; if(!m_resetBtn.ColorBackground(clrDarkRed) || !m_resetBtn.Color(clrWhite)) { Print("CControlPanel: button 'Reset' color styling failed, error " + IntegerToString(GetLastError())); return false; } y += CP_ROW_H; if(!CreateOneButton(m_resetDbBtn, "ResetDB", y, "Delete && Reset Database")) return false; if(!m_resetDbBtn.ColorBackground(clrDarkRed) || !m_resetDbBtn.Color(clrWhite)) { Print("CControlPanel: button 'ResetDB' color styling failed, error " + IntegerToString(GetLastError())); return false; } return true; } //+------------------------------------------------------------------+