
MQL5経済指標カレンダーを使った取引(第10回):シームレスなニュースナビゲーションのためのドラッグ可能ダッシュボードとインタラクティブホバー効果
はじめに
本記事では、MQL5経済指標カレンダーの連載をさらに発展させ、ドラッグ可能なダッシュボードとインタラクティブなホバー効果を導入することで、ニュースイベントの操作性とナビゲーションを向上させ、柔軟で直感的なユーザー体験を実現します。第9回で実装した動的スクロールバーと洗練された表示を基盤に、今回はチャート上でカレンダーを再配置できるレスポンシブなユーザーインターフェース(UI)に焦点を当て、ボタン操作の視覚的フィードバックを提供します。これにより、リアルタイムおよびバックテスト環境における経済ニュースへのアクセスが最適化されます。この記事は次のトピックで構成されています。
これらの機能強化について詳しく見ていきましょう。
チャートの柔軟性を高めるドラッグ可能なダッシュボードの追加
MQL5経済指標カレンダーの使いやすさを向上させるため、チャート上でインターフェースを自由に再配置できるドラッグ可能なダッシュボードを導入します。これに加えて、動的に位置が変化するスクロールバーを組み合わせ、ニュースのナビゲーションをスムーズに維持します。トレーダー中心のツールを目指し、従来の固定位置の制約を排除し、ダッシュボード、ニュースイベント、スクロールバーが一体となって動くようにします。これを実現する方法は以下の通りです。
- ドラッグ可能なダッシュボード設定:ヘッダ領域でのマウスクリックを検知するシステムを実装し、ユーザーがダッシュボード全体をドラッグできるようにします。すべてのUI要素はリアルタイムで位置を更新し、整列を維持します。
- 動的スクロールバーの位置調整:スクロールバーをダッシュボードの位置に連動する相対座標で調整し、ドラッグ中も正しく機能するようにします。
- チャート境界の制約:ダッシュボードがチャートの表示範囲外に移動しないよう制限を設け、常にアクセス可能な状態を保ちます。
- 要素の一体的な移動:ニュースイベント、フィルターボタン、トレードラベルがダッシュボードと同期して移動するようにし、統一感のあるプロフェッショナルなインターフェースを提供します。
この戦略的アプローチにより、ダッシュボードは必要に応じて自由に配置できる柔軟なツールとなり、チャートの視認性と操作性を向上させます。簡単に言えば、目指す成果は以下の通りです。
MQL5での実装
MQL5でこれらの改善をおこなうためには、まず動的なインタラクティブ性を強化するためのいくつかの追加関数を定義する必要があります。最も単純なものから複雑なものへ順に進めていきます。具体的には、まずホバー状態とドラッグ状態から始めます。まず、カーソルの位置に応じてボタンがホバー状態かどうかを判定し、ボタンの状態を更新するvoid関数を定義します。通常はボタンを暗くすることでホバー状態を表現しますが、ホバー時の色は任意に設定可能です。まず、色を暗くする処理を担当する関数から定義していきましょう。
//+------------------------------------------------------------------+ //| Helper function to darken a color for hover effect | //+------------------------------------------------------------------+ color ColorToDarken(color clr) { int r = (clr & 0xFF); int g = ((clr >> 8) & 0xFF); int b = ((clr >> 16) & 0xFF); r = MathMax(0, r - 50); g = MathMax(0, g - 50); b = MathMax(0, b - 50); return (color)((b << 16) | (g << 8) | r); }
まず、入力された色を暗くするColorToDarken関数を定義します。これは、ボタンにカーソルを重ねた際に視覚的フィードバックを強化するために使用します。入力として受け取る色clrは、赤、緑、青の各成分を0~255の範囲で組み合わせた単一の数値で、特定の色を表現します。この成分を分離するために、ビット演算を用いてclrの各ビットを操作します。
赤成分「r」については、ビット単位のAND演算子「&」を0xFFと組み合わせて使用します。ここで0xFFは16進数で255に相当し、clrの下位8ビットのみを抽出して赤の強度を取得するフィルターの役割を果たします。緑成分「g」は、右シフト演算子「>>」でclrを8ビット右にシフトし、緑のデータを下位8ビットに移動させた後、「& 0xFF」で分離します。同様に、青成分「b」はclrを16ビット右にシフトして「& 0xFF」を適用し、抽出します。16進数での表記は以下の通りです。
色を暗くするために、各成分「r」「g」「b」から50を減算して強度を下げます。この際、MathMax関数を使用して0未満にならないように制御します(色の値は負になれません)。最後に、暗くした各成分を再び単一の色値に結合します。青成分「b」を16ビット左シフト「<<」、緑成分「g」を8ビット左シフトし、「r」とビット単位のOR演算子「|」で合成します。この新しいcolor値を返し、ボタンに適用することでホバー時に暗くなる効果を実現し、インターフェースをより直感的で操作しやすくします。この関数により、ホバー時の色を動的に設定する処理に活用できるようになります。
//+------------------------------------------------------------------+ //| Update hover states for header and buttons | //+------------------------------------------------------------------+ void updateHoverStates(int mouse_x, int mouse_y) { // Header hover int header_x = (int)ObjectGetInteger(0, HEADER_LABEL, OBJPROP_XDISTANCE); int header_y = (int)ObjectGetInteger(0, HEADER_LABEL, OBJPROP_YDISTANCE); int header_width = 740; int header_height = 30; bool is_header_hovered = (mouse_x >= header_x && mouse_x <= header_x + header_width && mouse_y >= header_y && mouse_y <= header_y + header_height); if (is_header_hovered && !header_hovered) { ObjectSetInteger(0, MAIN_REC, OBJPROP_BGCOLOR, clrDarkGreen); header_hovered = true; } else if (!is_header_hovered && header_hovered) { ObjectSetInteger(0, MAIN_REC, OBJPROP_BGCOLOR, clrSeaGreen); header_hovered = false; } }
ここでは、ヘッダーにホバー効果を追加するupdateHoverStates関数を実装します。まず、ObjectGetIntegerを使用してHEADER_LABELの位置を取得し、OBJPROP_XDISTANCEとOBJPROP_YDISTANCEからheader_xとheader_yを取得します。これにより、幅740ピクセル、高さ30ピクセルの領域を定義します。次に、mouse_xとmouse_yがこの領域内にあるかどうかをチェックし、is_header_hoveredを設定します。
is_header_hoveredがtrueで、かつheader_hoveredがfalseの場合、ObjectSetIntegerを使用してMAIN_RECのOBJPROP_BGCOLORをclrDarkGreenに設定し、header_hoveredをtrueに更新します。逆に、is_header_hoveredがfalseでheader_hoveredがtrueの場合は、背景色をclrSeaGreenに戻し、header_hoveredをfalseに設定します。これにより、ホバー時の視覚的フィードバックが提供されます。同じ手法を他のボタンにも適用することが可能です。
// FILTER_CURR_BTN hover int curr_btn_x = (int)ObjectGetInteger(0, FILTER_CURR_BTN, OBJPROP_XDISTANCE); int curr_btn_y = (int)ObjectGetInteger(0, FILTER_CURR_BTN, OBJPROP_YDISTANCE); int curr_btn_width = 110; int curr_btn_height = 26; bool is_curr_btn_hovered = (mouse_x >= curr_btn_x && mouse_x <= curr_btn_x + curr_btn_width && mouse_y >= curr_btn_y && mouse_y <= curr_btn_y + curr_btn_height); if (is_curr_btn_hovered && !filter_curr_hovered) { ObjectSetInteger(0, FILTER_CURR_BTN, OBJPROP_BGCOLOR, clrDarkGray); filter_curr_hovered = true; } else if (!is_curr_btn_hovered && filter_curr_hovered) { ObjectSetInteger(0, FILTER_CURR_BTN, OBJPROP_BGCOLOR, clrBlack); filter_curr_hovered = false; } // FILTER_IMP_BTN hover int imp_btn_x = (int)ObjectGetInteger(0, FILTER_IMP_BTN, OBJPROP_XDISTANCE); int imp_btn_y = (int)ObjectGetInteger(0, FILTER_IMP_BTN, OBJPROP_YDISTANCE); int imp_btn_width = 120; int imp_btn_height = 26; bool is_imp_btn_hovered = (mouse_x >= imp_btn_x && mouse_x <= imp_btn_x + imp_btn_width && mouse_y >= imp_btn_y && mouse_y <= imp_btn_y + imp_btn_height); if (is_imp_btn_hovered && !filter_imp_hovered) { ObjectSetInteger(0, FILTER_IMP_BTN, OBJPROP_BGCOLOR, clrDarkGray); filter_imp_hovered = true; } else if (!is_imp_btn_hovered && filter_imp_hovered) { ObjectSetInteger(0, FILTER_IMP_BTN, OBJPROP_BGCOLOR, clrBlack); filter_imp_hovered = false; } // FILTER_TIME_BTN hover int time_btn_x = (int)ObjectGetInteger(0, FILTER_TIME_BTN, OBJPROP_XDISTANCE); int time_btn_y = (int)ObjectGetInteger(0, FILTER_TIME_BTN, OBJPROP_YDISTANCE); int time_btn_width = 70; int time_btn_height = 26; bool is_time_btn_hovered = (mouse_x >= time_btn_x && mouse_x <= time_btn_x + time_btn_width && mouse_y >= time_btn_y && mouse_y <= time_btn_y + time_btn_height); if (is_time_btn_hovered && !filter_time_hovered) { ObjectSetInteger(0, FILTER_TIME_BTN, OBJPROP_BGCOLOR, clrDarkGray); filter_time_hovered = true; } else if (!is_time_btn_hovered && filter_time_hovered) { ObjectSetInteger(0, FILTER_TIME_BTN, OBJPROP_BGCOLOR, clrBlack); filter_time_hovered = false; } // CANCEL_BTN hover int cancel_btn_x = (int)ObjectGetInteger(0, CANCEL_BTN, OBJPROP_XDISTANCE); int cancel_btn_y = (int)ObjectGetInteger(0, CANCEL_BTN, OBJPROP_YDISTANCE); int cancel_btn_width = 50; int cancel_btn_height = 30; bool is_cancel_btn_hovered = (mouse_x >= cancel_btn_x && mouse_x <= cancel_btn_x + cancel_btn_width && mouse_y >= cancel_btn_y && mouse_y <= cancel_btn_y + cancel_btn_height); if (is_cancel_btn_hovered && !cancel_hovered) { ObjectSetInteger(0, CANCEL_BTN, OBJPROP_BGCOLOR, clrDarkRed); cancel_hovered = true; } else if (!is_cancel_btn_hovered && cancel_hovered) { ObjectSetInteger(0, CANCEL_BTN, OBJPROP_BGCOLOR, clrRed); cancel_hovered = false; } // CURRENCY_BTNS hover int curr_size = 51, button_height = 22, spacing_x = 0, spacing_y = 3, max_columns = 4; for (int i = 0; i < ArraySize(curr_filter); i++) { int row = i / max_columns; int col = i % max_columns; int x_pos = panel_x + 525 + col * (curr_size + spacing_x); int y_pos = panel_y + 33 + row * (button_height + spacing_y); bool is_curr_hovered = (mouse_x >= x_pos && mouse_x <= x_pos + curr_size && mouse_y >= y_pos && mouse_y <= y_pos + button_height); string btn_name = CURRENCY_BTNS+IntegerToString(i); if (is_curr_hovered && !currency_btns_hovered[i]) { ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, clrLightGray); currency_btns_hovered[i] = true; } else if (!is_curr_hovered && currency_btns_hovered[i]) { ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, clrNONE); currency_btns_hovered[i] = false; } } // IMPACT_LABEL buttons hover int impact_size = 100; for (int i = 0; i < ArraySize(impact_labels); i++) { int x_pos = panel_x + 90 + impact_size * i; int y_pos = panel_y + 55; bool is_impact_hovered = (mouse_x >= x_pos && mouse_x <= x_pos + impact_size && mouse_y >= y_pos && mouse_y <= y_pos + 25); string btn_name = IMPACT_LABEL+string(i); color normal_color = clrBlack; if (impact_labels[i] == "None") normal_color = clrBlack; else if (impact_labels[i] == "Low") normal_color = clrYellow; else if (impact_labels[i] == "Medium") normal_color = clrOrange; else if (impact_labels[i] == "High") normal_color = clrRed; color hover_color = normal_color == clrBlack ? clrDarkGray : ColorToDarken(normal_color); if (is_impact_hovered && !impact_btns_hovered[i]) { ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, hover_color); impact_btns_hovered[i] = true; } else if (!is_impact_hovered && impact_btns_hovered[i]) { ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, normal_color); impact_btns_hovered[i] = false; } }
updateHoverStates関数を拡張し、複数のボタンにホバー効果を追加して、ユーザー操作に対する視覚的フィードバックを提供します。まずFILTER_CURR_BTNについては、ObjectGetIntegerを使用してOBJPROP_XDISTANCEおよびOBJPROP_YDISTANCEから座標curr_btn_xとcurr_btn_yを取得し、幅110ピクセル、高さ26ピクセルの領域を定義します。その後、mouse_xとmouse_yがこの領域内にあるかを確認し、is_curr_btn_hoveredを設定します。
is_curr_btn_hoveredがtrueかつfilter_curr_hoveredがfalseの場合、ObjectSetIntegerを使用してFILTER_CURR_BTNのOBJPROP_BGCOLORをclrDarkGrayに設定し、filter_curr_hoveredをtrueに更新します。そうでない場合は、背景色をclrBlackに戻し、filter_curr_hoveredをfalseに設定します。
同様のロジックをFILTER_IMP_BTN(120×26ピクセル)およびFILTER_TIME_BTN(70×26ピクセル)に適用し、それぞれfilter_imp_hoveredとfilter_time_hoveredを更新します。CANCEL_BTN(50×30ピクセル)については、is_cancel_btn_hoveredとcancel_hoveredに応じてclrDarkRedとclrRedを切り替えます。CURRENCY_BTNSについては、各ボタンをループ処理し、panel_xとpanel_yから位置を計算してis_curr_hoveredを確認し、currency_btns_hovered[i]に応じてclrLightGrayとclrNONEを切り替えます。
IMPACT_LABELボタンについては、影響レベルに応じてnormal_colorを割り当て(例:LowならclrYellow)、ColorToDarkenを使用してhover_colorを計算します。その後、is_impact_hoveredとimpact_btns_hovered[i]に基づき色を切り替え、動的なホバー効果を実現します。これらの変更を有効にするため、OnChartEventイベントハンドラ内でこの関数を呼び出します。
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { int mouse_x = (int)lparam; int mouse_y = (int)dparam; int mouse_state = (int)sparam; // Update hover states if (id == CHARTEVENT_MOUSE_MOVE) { updateHoverStates(mouse_x, mouse_y); } }
OnChartEventイベントハンドラでは、まずlparamとdparamからマウス座標mouse_xとmouse_yを取得し、sparamからマウス状態mouse_stateを取得します。これにより、マウスの位置とクリック、リリースの状態がわかります。イベントidがCHARTEVENT_MOUSE_MOVEの場合、updateHoverStates関数を呼び出し、mouse_xとmouse_yを渡して、マウスがヘッダーやボタン上にあるかを確認し、それに応じて視覚表示を更新します。これにより、ダッシュボードのインタラクティブ要素に対してレスポンシブなホバー効果が得られます。その後、クリック時の他の要素の更新を取得し、移動状態を判定する必要があります。
if (id == CHARTEVENT_OBJECT_CLICK) { UpdateFilterInfo(); CheckForNewsTrade(); if (sparam == CANCEL_BTN) { isDashboardUpdate = false; destroy_Dashboard(); } if (sparam == FILTER_CURR_BTN) { bool btn_state = ObjectGetInteger(0,sparam,OBJPROP_STATE); enableCurrencyFilter = btn_state; if (debugLogging) Print(sparam+" STATE = "+(string)btn_state+", FLAG = "+(string)enableCurrencyFilter); string filter_curr_text = enableCurrencyFilter ? ShortToString(0x2714)+"Currency" : ShortToString(0x274C)+"Currency"; color filter_curr_txt_color = enableCurrencyFilter ? clrLime : clrRed; ObjectSetString(0,FILTER_CURR_BTN,OBJPROP_TEXT,filter_curr_text); ObjectSetInteger(0,FILTER_CURR_BTN,OBJPROP_COLOR,filter_curr_txt_color); if (MQLInfoInteger(MQL_TESTER)) filters_changed = true; update_dashboard_values(curr_filter_selected,imp_filter_selected); // Recalculate scrollbar ObjectDelete(0, SCROLL_LEADER); ObjectDelete(0, SCROLL_UP_REC); ObjectDelete(0, SCROLL_UP_LABEL); ObjectDelete(0, SCROLL_DOWN_REC); ObjectDelete(0, SCROLL_DOWN_LABEL); ObjectDelete(0, SCROLL_SLIDER); scroll_visible = totalEvents_Filtered > VISIBLE_ITEMS; if (debugLogging) Print("Scrollbar visibility: ", scroll_visible ? "Visible" : "Hidden"); if (scroll_visible) { createRecLabel(SCROLL_LEADER, panel_x + SCROLLBAR_X_OFFSET, panel_y + SCROLLBAR_Y_OFFSET, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, clrSilver, 1, clrNONE); int max_scroll = MathMax(0, ArraySize(displayableEvents) - VISIBLE_ITEMS); color up_color = (scroll_pos == 0) ? clrLightGray : clrBlack; color down_color = (scroll_pos >= max_scroll) ? clrLightGray : clrBlack; createRecLabel(SCROLL_UP_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_UP_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET-5, CharToString(0x35), up_color, 15, "Webdings"); int down_y = panel_y + SCROLLBAR_Y_OFFSET + SCROLLBAR_HEIGHT - BUTTON_SIZE; createRecLabel(SCROLL_DOWN_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_DOWN_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y-5, CharToString(0x36), down_color, 15, "Webdings"); slider_height = calculateSliderHeight(); int slider_y = panel_y + SCROLLBAR_Y_OFFSET + BUTTON_SIZE; createButton(SCROLL_SLIDER, panel_x + SCROLLBAR_X_OFFSET + SLIDER_OFFSET_X, slider_y, SLIDER_WIDTH, slider_height, "", clrWhite, 12, clrLightSlateGray, clrDarkGray, "Arial Bold"); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_WIDTH, 2); if (debugLogging) Print("Scrollbar created: totalEvents_Filtered=", totalEvents_Filtered, ", slider_height=", slider_height); updateSliderPosition(); updateButtonColors(); } if (debugLogging) Print("Success. Changes updated! State: "+(string)enableCurrencyFilter); ChartRedraw(0); } if (sparam == FILTER_IMP_BTN) { bool btn_state = ObjectGetInteger(0,sparam,OBJPROP_STATE); enableImportanceFilter = btn_state; if (debugLogging) Print(sparam+" STATE = "+(string)btn_state+", FLAG = "+(string)enableImportanceFilter); string filter_imp_text = enableImportanceFilter ? ShortToString(0x2714)+"Importance" : ShortToString(0x274C)+"Importance"; color filter_imp_txt_color = enableImportanceFilter ? clrLime : clrRed; ObjectSetString(0,FILTER_IMP_BTN,OBJPROP_TEXT,filter_imp_text); ObjectSetInteger(0,FILTER_IMP_BTN,OBJPROP_COLOR,filter_imp_txt_color); if (MQLInfoInteger(MQL_TESTER)) filters_changed = true; update_dashboard_values(curr_filter_selected,imp_filter_selected); // Recalculate scrollbar ObjectDelete(0, SCROLL_LEADER); ObjectDelete(0, SCROLL_UP_REC); ObjectDelete(0, SCROLL_UP_LABEL); ObjectDelete(0, SCROLL_DOWN_REC); ObjectDelete(0, SCROLL_DOWN_LABEL); ObjectDelete(0, SCROLL_SLIDER); scroll_visible = totalEvents_Filtered > VISIBLE_ITEMS; if (debugLogging) Print("Scrollbar visibility: ", scroll_visible ? "Visible" : "Hidden"); if (scroll_visible) { createRecLabel(SCROLL_LEADER, panel_x + SCROLLBAR_X_OFFSET, panel_y + SCROLLBAR_Y_OFFSET, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, clrSilver, 1, clrNONE); int max_scroll = MathMax(0, ArraySize(displayableEvents) - VISIBLE_ITEMS); color up_color = (scroll_pos == 0) ? clrLightGray : clrBlack; color down_color = (scroll_pos >= max_scroll) ? clrLightGray : clrBlack; createRecLabel(SCROLL_UP_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_UP_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET-5, CharToString(0x35), up_color, 15, "Webdings"); int down_y = panel_y + SCROLLBAR_Y_OFFSET + SCROLLBAR_HEIGHT - BUTTON_SIZE; createRecLabel(SCROLL_DOWN_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_DOWN_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y-5, CharToString(0x36), down_color, 15, "Webdings"); slider_height = calculateSliderHeight(); int slider_y = panel_y + SCROLLBAR_Y_OFFSET + BUTTON_SIZE; createButton(SCROLL_SLIDER, panel_x + SCROLLBAR_X_OFFSET + SLIDER_OFFSET_X, slider_y, SLIDER_WIDTH, slider_height, "", clrWhite, 12, clrLightSlateGray, clrDarkGray, "Arial Bold"); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_WIDTH, 2); if (debugLogging) Print("Scrollbar created: totalEvents_Filtered=", totalEvents_Filtered, ", slider_height=", slider_height); updateSliderPosition(); updateButtonColors(); } if (debugLogging) Print("Success. Changes updated! State: "+(string)enableImportanceFilter); ChartRedraw(0); } if (sparam == FILTER_TIME_BTN) { bool btn_state = ObjectGetInteger(0,sparam,OBJPROP_STATE); enableTimeFilter = btn_state; if (debugLogging) Print(sparam+" STATE = "+(string)btn_state+", FLAG = "+(string)enableTimeFilter); string filter_time_text = enableTimeFilter ? ShortToString(0x2714)+"Time" : ShortToString(0x274C)+"Time"; color filter_time_txt_color = enableTimeFilter ? clrLime : clrRed; ObjectSetString(0,FILTER_TIME_BTN,OBJPROP_TEXT,filter_time_text); ObjectSetInteger(0,FILTER_TIME_BTN,OBJPROP_COLOR,filter_time_txt_color); if (MQLInfoInteger(MQL_TESTER)) filters_changed = true; update_dashboard_values(curr_filter_selected,imp_filter_selected); // Recalculate scrollbar ObjectDelete(0, SCROLL_LEADER); ObjectDelete(0, SCROLL_UP_REC); ObjectDelete(0, SCROLL_UP_LABEL); ObjectDelete(0, SCROLL_DOWN_REC); ObjectDelete(0, SCROLL_DOWN_LABEL); ObjectDelete(0, SCROLL_SLIDER); scroll_visible = totalEvents_Filtered > VISIBLE_ITEMS; if (debugLogging) Print("Scrollbar visibility: ", scroll_visible ? "Visible" : "Hidden"); if (scroll_visible) { createRecLabel(SCROLL_LEADER, panel_x + SCROLLBAR_X_OFFSET, panel_y + SCROLLBAR_Y_OFFSET, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, clrSilver, 1, clrNONE); int max_scroll = MathMax(0, ArraySize(displayableEvents) - VISIBLE_ITEMS); color up_color = (scroll_pos == 0) ? clrLightGray : clrBlack; color down_color = (scroll_pos >= max_scroll) ? clrLightGray : clrBlack; createRecLabel(SCROLL_UP_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_UP_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET-5, CharToString(0x35), up_color, 15, "Webdings"); int down_y = panel_y + SCROLLBAR_Y_OFFSET + SCROLLBAR_HEIGHT - BUTTON_SIZE; createRecLabel(SCROLL_DOWN_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_DOWN_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y-5, CharToString(0x36), down_color, 15, "Webdings"); slider_height = calculateSliderHeight(); int slider_y = panel_y + SCROLLBAR_Y_OFFSET + BUTTON_SIZE; createButton(SCROLL_SLIDER, panel_x + SCROLLBAR_X_OFFSET + SLIDER_OFFSET_X, slider_y, SLIDER_WIDTH, slider_height, "", clrWhite, 12, clrLightSlateGray, clrDarkGray, "Arial Bold"); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_WIDTH, 2); if (debugLogging) Print("Scrollbar created: totalEvents_Filtered=", totalEvents_Filtered, ", slider_height=", slider_height); updateSliderPosition(); updateButtonColors(); } if (debugLogging) Print("Success. Changes updated! State: "+(string)enableTimeFilter); ChartRedraw(0); } if (StringFind(sparam,CURRENCY_BTNS) >= 0) { string selected_curr = ObjectGetString(0,sparam,OBJPROP_TEXT); if (debugLogging) Print("BTN NAME = ",sparam,", CURRENCY = ",selected_curr); bool btn_state = ObjectGetInteger(0,sparam,OBJPROP_STATE); if (btn_state == false) { if (debugLogging) Print("BUTTON IS IN UN-SELECTED MODE."); for (int i = 0; i < ArraySize(curr_filter_selected); i++) { if (curr_filter_selected[i] == selected_curr) { for (int j = i; j < ArraySize(curr_filter_selected) - 1; j++) { curr_filter_selected[j] = curr_filter_selected[j + 1]; } ArrayResize(curr_filter_selected, ArraySize(curr_filter_selected) - 1); if (debugLogging) Print("Removed from selected filters: ", selected_curr); break; } } } else { if (debugLogging) Print("BUTTON IS IN SELECTED MODE. TAKE ACTION"); bool already_selected = false; for (int j = 0; j < ArraySize(curr_filter_selected); j++) { if (curr_filter_selected[j] == selected_curr) { already_selected = true; break; } } if (!already_selected) { ArrayResize(curr_filter_selected, ArraySize(curr_filter_selected) + 1); curr_filter_selected[ArraySize(curr_filter_selected) - 1] = selected_curr; if (debugLogging) Print("Added to selected filters: ", selected_curr); } else { if (debugLogging) Print("Currency already selected: ", selected_curr); } } if (debugLogging) Print("SELECTED ARRAY SIZE = ",ArraySize(curr_filter_selected)); if (debugLogging) ArrayPrint(curr_filter_selected); if (MQLInfoInteger(MQL_TESTER)) filters_changed = true; update_dashboard_values(curr_filter_selected,imp_filter_selected); // Recalculate scrollbar ObjectDelete(0, SCROLL_LEADER); ObjectDelete(0, SCROLL_UP_REC); ObjectDelete(0, SCROLL_UP_LABEL); ObjectDelete(0, SCROLL_DOWN_REC); ObjectDelete(0, SCROLL_DOWN_LABEL); ObjectDelete(0, SCROLL_SLIDER); scroll_visible = totalEvents_Filtered > VISIBLE_ITEMS; if (debugLogging) Print("Scrollbar visibility: ", scroll_visible ? "Visible" : "Hidden"); if (scroll_visible) { createRecLabel(SCROLL_LEADER, panel_x + SCROLLBAR_X_OFFSET, panel_y + SCROLLBAR_Y_OFFSET, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, clrSilver, 1, clrNONE); int max_scroll = MathMax(0, ArraySize(displayableEvents) - VISIBLE_ITEMS); color up_color = (scroll_pos == 0) ? clrLightGray : clrBlack; color down_color = (scroll_pos >= max_scroll) ? clrLightGray : clrBlack; createRecLabel(SCROLL_UP_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_UP_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET-5, CharToString(0x35), up_color, 15, "Webdings"); int down_y = panel_y + SCROLLBAR_Y_OFFSET + SCROLLBAR_HEIGHT - BUTTON_SIZE; createRecLabel(SCROLL_DOWN_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_DOWN_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y-5, CharToString(0x36), down_color, 15, "Webdings"); slider_height = calculateSliderHeight(); int slider_y = panel_y + SCROLLBAR_Y_OFFSET + BUTTON_SIZE; createButton(SCROLL_SLIDER, panel_x + SCROLLBAR_X_OFFSET + SLIDER_OFFSET_X, slider_y, SLIDER_WIDTH, slider_height, "", clrWhite, 12, clrLightSlateGray, clrDarkGray, "Arial Bold"); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_WIDTH, 2); if (debugLogging) Print("Scrollbar created: totalEvents_Filtered=", totalEvents_Filtered, ", slider_height=", slider_height); updateSliderPosition(); updateButtonColors(); } if (debugLogging) Print("SUCCESS. DASHBOARD UPDATED"); ChartRedraw(0); } if (StringFind(sparam, IMPACT_LABEL) >= 0) { string selected_imp = ObjectGetString(0, sparam, OBJPROP_TEXT); ENUM_CALENDAR_EVENT_IMPORTANCE selected_importance_lvl = get_importance_level(impact_labels,allowed_importance_levels,selected_imp); if (debugLogging) Print("BTN NAME = ", sparam, ", IMPORTANCE LEVEL = ", selected_imp,"(",selected_importance_lvl,")"); bool btn_state = ObjectGetInteger(0, sparam, OBJPROP_STATE); color color_border = btn_state ? clrNONE : clrBlack; if (btn_state == false) { if (debugLogging) Print("BUTTON IS IN UN-SELECTED MODE."); for (int i = 0; i < ArraySize(imp_filter_selected); i++) { if (impact_filter_selected[i] == selected_imp) { for (int j = i; j < ArraySize(imp_filter_selected) - 1; j++) { imp_filter_selected[j] = imp_filter_selected[j + 1]; impact_filter_selected[j] = impact_filter_selected[j + 1]; } ArrayResize(imp_filter_selected, ArraySize(imp_filter_selected) - 1); ArrayResize(impact_filter_selected, ArraySize(impact_filter_selected) - 1); if (debugLogging) Print("Removed from selected importance filters: ", selected_imp,"(",selected_importance_lvl,")"); break; } } } else { if (debugLogging) Print("BUTTON IS IN SELECTED MODE. TAKE ACTION"); bool already_selected = false; for (int j = 0; j < ArraySize(imp_filter_selected); j++) { if (impact_filter_selected[j] == selected_imp) { already_selected = true; break; } } if (!already_selected) { ArrayResize(imp_filter_selected, ArraySize(imp_filter_selected) + 1); ArrayResize(impact_filter_selected, ArraySize(impact_filter_selected) + 1); imp_filter_selected[ArraySize(imp_filter_selected) - 1] = selected_importance_lvl; impact_filter_selected[ArraySize(impact_filter_selected) - 1] = selected_imp; if (debugLogging) Print("Added to selected importance filters: ", selected_imp,"(",selected_importance_lvl,")"); } else { if (debugLogging) Print("Importance level already selected: ", selected_imp,"(",selected_importance_lvl,")"); } } if (debugLogging) Print("SELECTED ARRAY SIZE = ", ArraySize(imp_filter_selected)," >< ",ArraySize(impact_filter_selected)); if (debugLogging) ArrayPrint(imp_filter_selected); if (debugLogging) ArrayPrint(impact_filter_selected); if (MQLInfoInteger(MQL_TESTER)) filters_changed = true; update_dashboard_values(curr_filter_selected,imp_filter_selected); ObjectSetInteger(0,sparam,OBJPROP_BORDER_COLOR,color_border); // Recalculate scrollbar ObjectDelete(0, SCROLL_LEADER); ObjectDelete(0, SCROLL_UP_REC); ObjectDelete(0, SCROLL_UP_LABEL); ObjectDelete(0, SCROLL_DOWN_REC); ObjectDelete(0, SCROLL_DOWN_LABEL); ObjectDelete(0, SCROLL_SLIDER); scroll_visible = totalEvents_Filtered > VISIBLE_ITEMS; if (debugLogging) Print("Scrollbar visibility: ", scroll_visible ? "Visible" : "Hidden"); if (scroll_visible) { createRecLabel(SCROLL_LEADER, panel_x + SCROLLBAR_X_OFFSET, panel_y + SCROLLBAR_Y_OFFSET, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, clrSilver, 1, clrNONE); int max_scroll = MathMax(0, ArraySize(displayableEvents) - VISIBLE_ITEMS); color up_color = (scroll_pos == 0) ? clrLightGray : clrBlack; color down_color = (scroll_pos >= max_scroll) ? clrLightGray : clrBlack; createRecLabel(SCROLL_UP_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_UP_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, panel_y + SCROLLBAR_Y_OFFSET-5, CharToString(0x35), up_color, 15, "Webdings"); int down_y = panel_y + SCROLLBAR_Y_OFFSET + SCROLLBAR_HEIGHT - BUTTON_SIZE; createRecLabel(SCROLL_DOWN_REC, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y, BUTTON_WIDTH, BUTTON_SIZE, clrDarkGray, 1, clrDarkGray); createLabel(SCROLL_DOWN_LABEL, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X, down_y-5, CharToString(0x36), down_color, 15, "Webdings"); slider_height = calculateSliderHeight(); int slider_y = panel_y + SCROLLBAR_Y_OFFSET + BUTTON_SIZE; createButton(SCROLL_SLIDER, panel_x + SCROLLBAR_X_OFFSET + SLIDER_OFFSET_X, slider_y, SLIDER_WIDTH, slider_height, "", clrWhite, 12, clrLightSlateGray, clrDarkGray, "Arial Bold"); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_WIDTH, 2); if (debugLogging) Print("Scrollbar created: totalEvents_Filtered=", totalEvents_Filtered, ", slider_height=", slider_height); updateSliderPosition(); updateButtonColors(); } if (debugLogging) Print("SUCCESS. DASHBOARD UPDATED"); ChartRedraw(0); } // Scrollbar button clicks if (scroll_visible && (sparam == SCROLL_UP_REC || sparam == SCROLL_UP_LABEL)) { scrollUp(); updateButtonColors(); if (debugLogging) Print("Up button clicked (", sparam, "). CurrPos: ", scroll_pos); ChartRedraw(0); } if (scroll_visible && (sparam == SCROLL_DOWN_REC || sparam == SCROLL_DOWN_LABEL)) { scrollDown(); updateButtonColors(); if (debugLogging) Print("Down button clicked (", sparam, "). CurrPos: ", scroll_pos); ChartRedraw(0); } }
動的オブジェクトのクリックイベントを強化するため、イベントidがCHARTEVENT_OBJECT_CLICKの場合、まずUpdateFilterInfoとCheckForNewsTradeを呼び出し、フィルター状態を更新し取引条件を確認します。sparamがCANCEL_BTNの場合、isDashboardUpdateをfalseに設定し、destroy_Dashboardを呼び出してダッシュボードを閉じます。FILTER_CURR_BTN、FILTER_IMP_BTN、またはFILTER_TIME_BTNの場合、ObjectGetIntegerを使用してOBJPROP_STATEからbtn_stateを取得し、enableCurrencyFilter、enableImportanceFilter、またはenableTimeFilterを更新します。その後、ObjectSetStringとObjectSetIntegerを使用してOBJPROP_TEXTおよびOBJPROP_COLORを調整します(例:有効時はclrLime、無効時はclrRed)。続いてupdate_dashboard_valuesを呼び出し、createRecLabel、createLabel、createButtonを用いてSCROLL_LEADERやSCROLL_SLIDERなどの要素をpanel_xとpanel_yに配置し、updateSliderPositionとupdateButtonColorsで状態を更新します。
CURRENCY_BTNSについては、ObjectGetStringでselected_currを取得し、ArrayResizeを使用してcurr_filter_selectedに追加または削除し、ダッシュボードを同様に更新します。IMPACT_LABELボタンについては、get_importance_levelを呼び出してselected_impをselected_importance_lvlにマッピングし、imp_filter_selectedおよびimpact_filter_selected配列を管理します。ObjectSetIntegerでcolor_borderを設定し、ダッシュボードを更新します。sparamがSCROLL_UP_RECまたはSCROLL_UP_LABELの場合はscrollUpを呼び出し、SCROLL_DOWN_RECまたはSCROLL_DOWN_LABELの場合はscrollDownを呼び出します。その後、updateButtonColorsで色を更新し、ChartRedrawでチャートを再描画して変更を反映させます。プログラムをコンパイルして実行すると、次の結果が得られます。
可視化から、定義したボタン上で視覚的なホバーフィードバックが得られることが確認できます。次に取り組むべきは、より複雑な部分であるダッシュボード全体の移動です。これは以下のロジックによって実現されます。
else if (id == CHARTEVENT_MOUSE_MOVE && isDashboardUpdate) { // Handle panel dragging int header_x = (int)ObjectGetInteger(0, HEADER_LABEL, OBJPROP_XDISTANCE); int header_y = (int)ObjectGetInteger(0, HEADER_LABEL, OBJPROP_YDISTANCE); int header_width = 740; int header_height = 30; if (prev_mouse_state == 0 && mouse_state == 1) { // Mouse button down if (mouse_x >= header_x && mouse_x <= header_x + header_width && mouse_y >= header_y && mouse_y <= header_y + header_height) { panel_dragging = true; panel_drag_x = mouse_x; panel_drag_y = mouse_y; panel_start_x = panel_x; panel_start_y = panel_y; ChartSetInteger(0, CHART_MOUSE_SCROLL, false); if (debugLogging) Print("Panel drag started at x=", mouse_x, ", y=", mouse_y); } } if (panel_dragging && mouse_state == 1) { // Dragging panel int dx = mouse_x - panel_drag_x; int dy = mouse_y - panel_drag_y; panel_x = panel_start_x + dx; panel_y = panel_start_y + dy; // Ensure panel stays within chart boundaries int chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); int chart_height = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); panel_x = MathMax(0, MathMin(panel_x, chart_width - 753)); // 753 = panel width panel_y = MathMax(0, MathMin(panel_y, chart_height - 410)); // 410 = panel height // Update positions of all panel objects ObjectSetInteger(0, MAIN_REC, OBJPROP_XDISTANCE, panel_x); ObjectSetInteger(0, MAIN_REC, OBJPROP_YDISTANCE, panel_y); ObjectSetInteger(0, SUB_REC1, OBJPROP_XDISTANCE, panel_x + 3); ObjectSetInteger(0, SUB_REC1, OBJPROP_YDISTANCE, panel_y + 30); ObjectSetInteger(0, SUB_REC2, OBJPROP_XDISTANCE, panel_x + 3 + 5); ObjectSetInteger(0, SUB_REC2, OBJPROP_YDISTANCE, panel_y + 30 + 50 + 27); ObjectSetInteger(0, HEADER_LABEL, OBJPROP_XDISTANCE, panel_x + 3 + 5); ObjectSetInteger(0, HEADER_LABEL, OBJPROP_YDISTANCE, panel_y + 5); ObjectSetInteger(0, TIME_LABEL, OBJPROP_XDISTANCE, panel_x + 20); ObjectSetInteger(0, TIME_LABEL, OBJPROP_YDISTANCE, panel_y + 35); ObjectSetInteger(0, IMPACT_LABEL, OBJPROP_XDISTANCE, panel_x + 20); ObjectSetInteger(0, IMPACT_LABEL, OBJPROP_YDISTANCE, panel_y + 55); ObjectSetInteger(0, FILTER_LABEL, OBJPROP_XDISTANCE, panel_x + 320); ObjectSetInteger(0, FILTER_LABEL, OBJPROP_YDISTANCE, panel_y + 5); ObjectSetInteger(0, FILTER_CURR_BTN, OBJPROP_XDISTANCE, panel_x + 380); ObjectSetInteger(0, FILTER_CURR_BTN, OBJPROP_YDISTANCE, panel_y + 5); ObjectSetInteger(0, FILTER_IMP_BTN, OBJPROP_XDISTANCE, panel_x + 490); ObjectSetInteger(0, FILTER_IMP_BTN, OBJPROP_YDISTANCE, panel_y + 5); ObjectSetInteger(0, FILTER_TIME_BTN, OBJPROP_XDISTANCE, panel_x + 610); ObjectSetInteger(0, FILTER_TIME_BTN, OBJPROP_YDISTANCE, panel_y + 5); ObjectSetInteger(0, CANCEL_BTN, OBJPROP_XDISTANCE, panel_x + 692+10); ObjectSetInteger(0, CANCEL_BTN, OBJPROP_YDISTANCE, panel_y + 1); // Update calendar buttons int startX = panel_x + 9; for (int i = 0; i < ArraySize(array_calendar); i++) { ObjectSetInteger(0, ARRAY_CALENDAR+IntegerToString(i), OBJPROP_XDISTANCE, startX); ObjectSetInteger(0, ARRAY_CALENDAR+IntegerToString(i), OBJPROP_YDISTANCE, panel_y + 82); startX += buttons[i] + 3; } // Update impact buttons for (int i = 0; i < ArraySize(impact_labels); i++) { ObjectSetInteger(0, IMPACT_LABEL+string(i), OBJPROP_XDISTANCE, panel_x + 90 + 100 * i); ObjectSetInteger(0, IMPACT_LABEL+string(i), OBJPROP_YDISTANCE, panel_y + 55); } // Update currency buttons int curr_size = 51, button_height = 22, spacing_x = 0, spacing_y = 3, max_columns = 4; for (int i = 0; i < ArraySize(curr_filter); i++) { int row = i / max_columns; int col = i % max_columns; int x_pos = panel_x + 525 + col * (curr_size + spacing_x); int y_pos = panel_y + 33 + row * (button_height + spacing_y); ObjectSetInteger(0, CURRENCY_BTNS+IntegerToString(i), OBJPROP_XDISTANCE, x_pos); ObjectSetInteger(0, CURRENCY_BTNS+IntegerToString(i), OBJPROP_YDISTANCE, y_pos); } // Update scrollbar if (scroll_visible) { ObjectSetInteger(0, SCROLL_LEADER, OBJPROP_XDISTANCE, panel_x + SCROLLBAR_X_OFFSET); ObjectSetInteger(0, SCROLL_LEADER, OBJPROP_YDISTANCE, panel_y + SCROLLBAR_Y_OFFSET); ObjectSetInteger(0, SCROLL_UP_REC, OBJPROP_XDISTANCE, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X); ObjectSetInteger(0, SCROLL_UP_REC, OBJPROP_YDISTANCE, panel_y + SCROLLBAR_Y_OFFSET); ObjectSetInteger(0, SCROLL_UP_LABEL, OBJPROP_XDISTANCE, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X); ObjectSetInteger(0, SCROLL_UP_LABEL, OBJPROP_YDISTANCE, panel_y + SCROLLBAR_Y_OFFSET - 5); ObjectSetInteger(0, SCROLL_DOWN_REC, OBJPROP_XDISTANCE, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X); ObjectSetInteger(0, SCROLL_DOWN_REC, OBJPROP_YDISTANCE, panel_y + SCROLLBAR_Y_OFFSET + SCROLLBAR_HEIGHT - BUTTON_SIZE); ObjectSetInteger(0, SCROLL_DOWN_LABEL, OBJPROP_XDISTANCE, panel_x + SCROLLBAR_X_OFFSET + BUTTON_OFFSET_X); ObjectSetInteger(0, SCROLL_DOWN_LABEL, OBJPROP_YDISTANCE, panel_y + SCROLLBAR_Y_OFFSET + SCROLLBAR_HEIGHT - BUTTON_SIZE - 5); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_XDISTANCE, panel_x + SCROLLBAR_X_OFFSET + SLIDER_OFFSET_X); // Y-position of slider is managed by updateSliderPosition() } // Update event display update_dashboard_values(curr_filter_selected, imp_filter_selected); // Update trade labels if they exist if (ObjectFind(0, "NewsCountdown") >= 0) { ObjectSetInteger(0, "NewsCountdown", OBJPROP_XDISTANCE, panel_x); ObjectSetInteger(0, "NewsCountdown", OBJPROP_YDISTANCE, panel_y - 33); } if (ObjectFind(0, "NewsTradeInfo") >= 0) { ObjectSetInteger(0, "NewsTradeInfo", OBJPROP_XDISTANCE, panel_x + 305); ObjectSetInteger(0, "NewsTradeInfo", OBJPROP_YDISTANCE, panel_y - 28); } ChartRedraw(0); if (debugLogging) Print("Panel moved to x=", panel_x, ", y=", panel_y); } if (mouse_state == 0) { // Mouse button released if (panel_dragging) { panel_dragging = false; ChartSetInteger(0, CHART_MOUSE_SCROLL, true); if (debugLogging) Print("Panel drag stopped."); ChartRedraw(0); } } }
ここでは、OnChartEvent関数を拡張してパネルのドラッグ機能を実装します。これにより、isDashboardUpdateがtrueでイベントidがCHARTEVENT_MOUSE_MOVEの場合に、インターフェースの位置を再配置できるようになります。まず、ObjectGetIntegerを使用してHEADER_LABELからOBJPROP_XDISTANCEおよびOBJPROP_YDISTANCEを取得し、header_xとheader_yを取得します。これにより、幅740ピクセル、高さ30ピクセルのヘッダー領域を定義します。prev_mouse_stateが0でmouse_stateが1(マウスボタン押下)の場合、mouse_xとmouse_yがヘッダー内にあるかを確認し、panel_draggingをtrueに設定します。また、panel_drag_x、panel_drag_y、panel_start_x、panel_start_yを保存し、ChartSetIntegerとCHART_MOUSE_SCROLLを使用してチャートのスクロールを無効化します。
panel_draggingがtrueでmouse_stateが1の場合、ドラッグオフセットdxとdyを計算し、panel_xとpanel_yを更新します。MathMaxおよびMathMinを使用して、ChartGetIntegerで取得したチャートのchart_widthとchart_height内に制約します(CHART_WIDTH_IN_PIXELSおよびCHART_HEIGHT_IN_PIXELSを使用)。その後、ObjectSetIntegerでOBJPROP_XDISTANCEおよびOBJPROP_YDISTANCEを用いて、MAIN_REC、SUB_REC1、SUB_REC2、HEADER_LABEL、TIME_LABEL、IMPACT_LABEL、FILTER_LABEL、FILTER_CURR_BTN、FILTER_IMP_BTN、FILTER_TIME_BTN、CANCEL_BTN、ARRAY_CALENDAR、IMPACT_LABEL、CURRENCY_BTNSなどすべてのUI要素を再配置します。また、scroll_visibleがtrueの場合は、SCROLL_LEADER、SCROLL_UP_REC、SCROLL_UP_LABEL、SCROLL_DOWN_REC、SCROLL_DOWN_LABEL、SCROLL_SLIDERなどスクロールバー要素もpanel_xとpanel_yおよびオフセットを用いて更新します。
update_dashboard_valuesを呼び出してニュースイベントを更新し、存在する場合はObjectFindを用いて取引ラベルNewsCountdownとNewsTradeInfoを再配置し、ChartRedrawでチャートを再描画します。mouse_stateが0(マウスリリース)の場合、panel_draggingをfalseに設定し、ChartSetIntegerでスクロールを再度有効化してチャートを再描画します。debugLoggingが有効な場合は、Printでドラッグイベントをログに出力します。その後、パネルがドラッグモードでない場合に限り、スクロールバー機能を処理してイベントの競合を防ぎます。
// Scrollbar handling (only if not dragging panel) if (scroll_visible && !panel_dragging) { if (prev_mouse_state == 0 && mouse_state == 1) { int xd = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_XDISTANCE); int yd = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_YDISTANCE); int xs = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_XSIZE); int ys = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_YSIZE); // Skip if mouse is over header to prioritize panel dragging if (mouse_x >= (panel_x + 3 + 5) && mouse_x <= (panel_x + 3 + 5 + 740) && mouse_y >= (panel_y + 5) && mouse_y <= (panel_y + 5 + 30)) { return; } if (mouse_x >= xd && mouse_x <= xd + xs && mouse_y >= yd && mouse_y <= yd + ys) { moving_state_slider = true; mlb_down_x = mouse_x; mlb_down_y = mouse_y; mlb_down_yd_slider = yd; ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_BGCOLOR, clrDodgerBlue); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_YSIZE, slider_height + 2); ChartSetInteger(0, CHART_MOUSE_SCROLL, false); if (debugLogging) Print("Slider drag started at y=", mouse_y); } } if (moving_state_slider && mouse_state == 1) { int delta_y = mouse_y - mlb_down_y; int new_y = mlb_down_yd_slider + delta_y; int scroll_area_y_min = panel_y + SCROLLBAR_Y_OFFSET + BUTTON_SIZE; int scroll_area_y_max = scroll_area_y_min + SCROLL_AREA_HEIGHT - slider_height; new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max)); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y); int max_scroll = MathMax(0, ArraySize(displayableEvents) - VISIBLE_ITEMS); double scroll_ratio = (double)(new_y - scroll_area_y_min) / (scroll_area_y_max - scroll_area_y_min); int new_scroll_pos = (int)MathRound(scroll_ratio * max_scroll); if (new_scroll_pos != scroll_pos) { scroll_pos = new_scroll_pos; update_dashboard_values(curr_filter_selected, imp_filter_selected); updateButtonColors(); if (debugLogging) Print("Slider dragged. CurrPos: ", scroll_pos, ", Total steps: ", max_scroll, ", Slider y=", new_y); } ChartRedraw(0); } if (mouse_state == 0) { if (moving_state_slider) { moving_state_slider = false; ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_BGCOLOR, clrLightSlateGray); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_YSIZE, slider_height); ChartSetInteger(0, CHART_MOUSE_SCROLL, true); if (debugLogging) Print("Slider drag stopped."); ChartRedraw(0); } } } prev_mouse_state = mouse_state;
ここでは、スクロールバーのドラッグ機能を実装します。これにより、scroll_visibleがtrueでpanel_draggingがfalseの場合に、ニュースイベントをスクロールできるようになり、パネルのドラッグとの競合を防ぎます。prev_mouse_stateが0でmouse_stateが1(マウスボタン押下)の場合、既に実装したスクロールロジックおよび他のスライダーやリリース状態の処理を適用します。コンパイルすると以下の出力が得られます。
可視化から、ボタン上でホバーでき、チャートの端に近い範囲内でパネルをドラッグできることが確認できます。 次に残る作業は、システムの徹底的なバックテストであり、それは次のセクションで扱います。
テストと検証
強化されたダッシュボードをテストし、動的な移動、ホバー可能なボタン、およびスクロールバーが意図どおりに機能し、ニュースイベントのナビゲーションがシームレスにおこなえることを確認しました。テストでは、ダッシュボード全体のボタンのホバー時の視覚的フィードバック、チャートの範囲内でのパネル全体の移動、およびライブモードでのスクロールバーの統合性に重点を置きました。これらのテストは、ダッシュボードのパフォーマンスを視覚的に示すため、簡潔なGraphics Interchange Format (GIF)に記録しました。以下に示します。
可視化から、ダッシュボードがシームレスに動作することが確認できます。
結論
結論として、ドラッグ可能なダッシュボードとインタラクティブなホバー効果を導入することで、MQL5経済指標カレンダーの連載をさらに進化させました。これにより、ニュースイベントの柔軟な位置調整と直感的なナビゲーションが可能になりました。これらの強化機能は、第9回に実装した動的スクロールバーと洗練された表示を基盤としており、ライブモードおよびストラテジーテスターモードの両方でシームレスな操作を保証し、ニュース駆動型の取引戦略に適した堅牢で柔軟なプラットフォームを提供します。この多用途ダッシュボードを基盤として活用し、独自のチャートや取引ニーズに合わせてカスタマイズすることが可能です。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18241
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索