//+------------------------------------------------------------------+ //| 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 #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 6 //--- 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) //--- one action per button click; the EA's OnChartEvent() consumes this right after ExtPanel.ChartEvent() enum ENUM_CP_ACTION { CP_ACTION_NONE, CP_ACTION_TOGGLE_SIGNALS, CP_ACTION_TOGGLE_PAUSE, CP_ACTION_TOGGLE_STOP, CP_ACTION_SAVE, CP_ACTION_LOAD, CP_ACTION_RESET }; class CControlPanel : public CAppDialog { private: CButton m_signalsBtn; CButton m_pauseBtn; CButton m_stopBtn; CButton m_saveBtn; CButton m_loadBtn; CButton m_resetBtn; ENUM_CP_ACTION m_pendingAction; 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 OnClickSave(void) { m_pendingAction = CP_ACTION_SAVE; } void OnClickLoad(void) { m_pendingAction = CP_ACTION_LOAD; } void OnClickReset(void) { m_pendingAction = CP_ACTION_RESET; } public: CControlPanel(void) : m_pendingAction(CP_ACTION_NONE) {} ~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(); } //--- 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); } //--- 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_saveBtn, OnClickSave) ON_EVENT(ON_CLICK, m_loadBtn, OnClickLoad) ON_EVENT(ON_CLICK, m_resetBtn, OnClickReset) 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; 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; 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)) return false; return true; } //+------------------------------------------------------------------+