Aufbau von KI-gestützten Handelssystemen in MQL5 (Teil 5): Hinzufügen einer ausklappbaren Seitenleiste mit Chat-Popups
Einführung
In unserem vorigen Artikel (Teil 4) haben wir das in ChatGPT integrierte Programm MetaQuotes Language 5 (MQL5) erweitert, indem wir die mehrzeilige Eingabe verbessert, die verschlüsselte Chatspeicherung mit AES256 und ZIP hinzugefügt und Handelssignale aus Chartdaten erzeugt haben. In Teil 5 fügen wir dem KI-gesteuerten Handelssystem eine ausklappbare Seitenleiste hinzu. Dieses Upgrade führt eine dynamische Seitenleiste ein, die zur besseren Verwaltung des Bildschirms zwischen erweitertem und verkleinertem Zustand hin- und herschaltet, enthält einen kleinen und einen großen Verlauf Popups für schnellen Chat-Zugriff und behält nahtlose mehrzeilige Eingaben, verschlüsselte Persistenz und Signalgenerierungsfunktionen bei. Wir werden die folgenden Themen behandeln:
- Die Bedeutung einer ausklappbaren Seitenleiste in KI-Handelsoberflächen
- Die Implementation in MQL5
- Testen der ausklappbaren Seitenleiste
- Schlussfolgerung
Am Ende haben Sie einen MQL5-KI-Handelsassistenten mit optimierter UI-Flexibilität, bereit für Anpassungen – legen wir los!
Die Bedeutung einer ausklappbaren Seitenleiste in KI-Handelsoberflächen
Eine ausklappbare Seitenleiste in KI-Handelsoberflächen, wie z. B. in unserem in ChatGPT integrierten System, ist für die Optimierung der Bildschirmfläche und die Verbesserung der Nutzerfreundlichkeit von entscheidender Bedeutung. Durch die Möglichkeit mit einer ausklappbaren Seitenleiste zwischen einem erweiterten und einem reduzierten Zustand hin und her zu schalten, können wir bei Bedarf Platz für das Chart reservieren oder während der Analyse auf alle Navigationsdetails zugreifen und so die Effizienz des Arbeitsablaufs verbessern, ohne die Funktionalität zu beeinträchtigen. Die Hinzufügung eines kleinen Verlaufs-Popups beim Hovern im Vertragsmodus und eines scrollbaren, großen Verlaufs-Popups für alle Chats gewährleistet einen schnellen Zugriff auf fortlaufende Unterhaltungen, sodass wir nahtlos auf frühere KI-gesteuerte Markteinblicke oder Signale verweisen können. Dies ermöglicht schnellere und fundiertere Handelsentscheidungen bei gleichzeitiger Beibehaltung einer übersichtlichen, intuitiven Nutzeroberfläche.
Unser Ansatz baut auf den Stärken des bestehenden Systems auf, die Sie inzwischen kennen, und falls nicht, lesen Sie bitte die früheren Versionen der Reihe. Die ausklappbare Seitenleiste verbessert dies durch ihre Flexibilität: Im erweiterten Modus bietet sie eine übersichtliche Chat-Navigation, im zusammengeklappten Zustand minimiert sie die Störung. Das kleine Popup ermöglicht einen schnellen Zugriff auf den Verlauf, während das große Popup eine umfassende Chat-Auswahl ermöglicht und den Übergang zwischen den Modi reibungslos und intuitiv gestaltet. Dieses Design kommt Händlern entgegen, die sowohl kompakte als auch detaillierte Ansichten benötigen, und stellt sicher, dass sich die Nutzeroberfläche an unterschiedliche Bedürfnisse anpasst, während die Fähigkeit zur Interaktion mit KI-generierten Erkenntnissen für Marktchancen wie Trendanalysen oder Umkehrungen erhalten bleibt. Wir werden auch eine größere Chat-Ansicht für dynamische Suchfunktionen einrichten, aber im Moment sollten Sie es einfach halten. Nachfolgend finden Sie visuelle Darstellungen der geplanten Konfigurationen.
Kontrahierte Seitenleiste mit kleinem Popup mit dem Verlauf:

Großes Popup des Verlaufs:

Implementation in MQL5
Um die Upgrades zu implementieren, werden wir zunächst die Objekte deklarieren, die wir für die kontrahierte Seitenleiste, das kleine Verlaufs-Popup, das wir auf der Verlaufsschaltfläche anzeigen wollen, die Klapp-Schaltfläche selbst und dann das große Popup, das alle Chat-Verläufe anzeigt, hinzufügen werden. Das Wichtigste ist die Bildlaufleiste, die wir für das große Popup implementieren. Beginnen wir also damit, denn der Rest kann mit dem Präfix, das wir verwendet haben, implementiert werden.
#define BIG_SCROLL_LEADER "ChatGPT_Big_Scroll_Leader" #define BIG_SCROLL_UP_REC "ChatGPT_Big_Scroll_Up_Rec" #define BIG_SCROLL_UP_LABEL "ChatGPT_Big_Scroll_Up_Label" #define BIG_SCROLL_DOWN_REC "ChatGPT_Big_Scroll_Down_Rec" #define BIG_SCROLL_DOWN_LABEL "ChatGPT_Big_Scroll_Down_Label" #define BIG_SCROLL_SLIDER "ChatGPT_Big_Scroll_Slider"
Hier definieren wir Konstanten für die Bildlaufleisten-Elemente des großen Verlaufs-Popups mit #define und vergeben Namen wie „BIG_SCROLL_LEADER“ für die Schiene, „BIG_SCROLL_UP_REC“ und „BIG_SCROLL_UP_LABEL“ für die Aufwärts-Schaltfläche und ihren Pfeil, „BIG_SCROLL_DOWN_REC“ und „BIG_SCROLL_DOWN_LABEL“ für die Abwärtstaste und „BIG_SCROLL_SLIDER“ für den verschiebbaren Daumen, wodurch eine konsistente Referenzierung bei der Objekterstellung und den Verwaltungsfunktionen für das Scrollen in der erweiterten Chat-Verlaufsansicht gewährleistet wird. Als Nächstes müssen wir globale Variablen zur Steuerung der neuen Elemente hinzufügen.
color toggle_original_bg = clrLightGray; color toggle_darker_bg; bool toggle_hover = false; color history_original_bg = clrWhite; color history_darker_bg; bool history_hover = false; color see_more_original_bg = clrRoyalBlue; color see_more_darker_bg; bool see_more_hover = false; color big_close_original_bg = clrLightGray; color big_close_darker_bg; bool big_close_hover = false; int expandedSidebarWidth = 150; int contractedSidebarWidth = 50; bool sidebarExpanded = true; bool showing_small_history_popup = false; int small_popup_x, small_popup_y, small_popup_w, small_popup_h; string small_popup_objects[]; string small_chat_bgs[]; bool showing_big_history_popup = false; int big_popup_x, big_popup_y, big_popup_w, big_popup_h; string big_popup_objects[]; string big_chat_bgs[]; string side_chat_bgs[]; int big_scroll_pos = 0; bool big_scroll_visible = false; int big_total_height = 0; int big_visible_height = 0; int big_slider_height = 20; bool big_movingStateSlider = false; int big_mlbDownX_Slider = 0; int big_mlbDownY_Slider = 0; int big_mlbDown_YD_Slider = 0; bool just_opened_big = false; bool just_opened_small = false;
Weiter geht es mit der Definition von Variablen für die zusammenklappbare Seitenleiste und die Verlaufs-Popups, beginnend mit den Farben für die Umschalt-Schaltfläche („toggle_original_bg“ als hellgrau, „toggle_darker_bg“ für den Schwebezustand), die Verlaufs-Schaltfläche („history_original_bg“ als weiß, „history_darker_bg“), die Schaltfläche „See More“ („see_more_original_bg“ als königsblau, „see_more_darker_bg“), und große Popup-Schaltfläche zum Schließen („big_close_original_bg“ als hellgrau, „big_close_darker_bg“), jeweils gepaart mit Hover-Flags („toggle_hover“, „history_hover“, „see_more_hover“, „big_close_hover“), initialisiert auf false. Für die Größe der Seitenleiste setzen wir „expandedSidebarWidth“ auf 150 Pixel und „contractedSidebarWidth“ auf 50 Pixel, wobei „sidebarExpanded“ für den erweiterten Zustand standardmäßig auf true gesetzt wird.
Wir verwalten zwei Popups des Verlauf: ein kleines Popup mit dem Flag „showing_small_history_popup“ , den Koordinaten („small_popup_x“, „small_popup_y“), der Größe („small_popup_w“, „small_popup_h“) und den Arrays „small_popup_objects“ und „small_chat_bgs“ für die Objekte und die Chat-Hintergründe; und ein großes Popup mit dem Flag „showing_big_history_popup“, den Koordinaten („big_popup_x“, „big_popup_y“), der Größe („big_popup_w“, „big_popup_h“), den Arrays „big_popup_objects“ und „big_chat_bgs“, und der Bildlaufleisten-Variablen („big_scroll_pos“, „big_scroll_visible“, „big_total_height“, „big_visible_height“, „big_slider_height“ bei 20, „big_movingStateSlider“, „big_mlbDownX_Slider“, „big_mlbDownY_Slider“, „big_mlbDown_YD_Slider“). Das Array „side_chat_bgs“ verfolgt Chat-Hintergründe in der Seitenleiste, während die Flags „just_opened_big“ und „just_opened_small“ das sofortige Schließen von Popups beim Öffnen verhindern und so ein dynamisches UI-Management für die klappbare Navigation und den Zugriff auf den Chat-Verlauf ermöglichen. Mit diesen Variablen müssen wir nun die neue Schnittstelle initialisieren.
int OnInit() { //--- initialize the new variables toggle_darker_bg = DarkenColor(toggle_original_bg); history_darker_bg = DarkenColor(history_original_bg, 0.9); see_more_darker_bg = DarkenColor(see_more_original_bg); big_close_darker_bg = DarkenColor(big_close_original_bg); logFileHandle = FileOpen(LogFileName, FILE_READ | FILE_WRITE | FILE_TXT); if (logFileHandle == INVALID_HANDLE) { Print("Failed to open log file: ", GetLastError()); return(INIT_FAILED); } FileSeek(logFileHandle, 0, SEEK_END); uint img_pixels[]; uint orig_width = 0, orig_height = 0; bool image_loaded = ResourceReadImage(resourceImg, img_pixels, orig_width, orig_height); if (image_loaded && orig_width > 0 && orig_height > 0) { ScaleImage(img_pixels, (int)orig_width, (int)orig_height, 104, 40); g_scaled_image_resource = "::ChatGPT_HeaderImageScaled"; if (ResourceCreate(g_scaled_image_resource, img_pixels, 104, 40, 0, 0, 104, COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Scaled image resource created successfully"); } else { Print("Failed to create scaled image resource"); } } else { Print("Failed to load original image resource"); } uint img_pixels_logo[]; uint orig_width_logo = 0, orig_height_logo = 0; bool image_loaded_logo = ResourceReadImage(resourceImgLogo, img_pixels_logo, orig_width_logo, orig_height_logo); if (image_loaded_logo && orig_width_logo > 0 && orig_height_logo > 0) { ScaleImage(img_pixels_logo, (int)orig_width_logo, (int)orig_height_logo, 81, 81); g_scaled_sidebar_resource = "::ChatGPT_SidebarImageScaled"; if (ResourceCreate(g_scaled_sidebar_resource, img_pixels_logo, 81, 81, 0, 0, 81, COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Scaled sidebar image resource created successfully"); } else { Print("Failed to create scaled sidebar image resource"); } uint img_pixels_logo_small[]; ArrayCopy(img_pixels_logo_small, img_pixels_logo); ScaleImage(img_pixels_logo_small, 81, 81, 30, 30); g_scaled_sidebar_small = "::ChatGPT_SidebarImageSmall"; if (ResourceCreate(g_scaled_sidebar_small, img_pixels_logo_small, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Scaled small sidebar image resource created successfully"); } else { Print("Failed to create scaled small sidebar image resource"); } } else { Print("Failed to load sidebar image resource"); } uint img_pixels_newchat[]; uint orig_width_newchat = 0, orig_height_newchat = 0; bool image_loaded_newchat = ResourceReadImage(resourceNewChat, img_pixels_newchat, orig_width_newchat, orig_height_newchat); if (image_loaded_newchat && orig_width_newchat > 0 && orig_height_newchat > 0) { ScaleImage(img_pixels_newchat, (int)orig_width_newchat, (int)orig_height_newchat, 30, 30); g_scaled_newchat_resource = "::ChatGPT_NewChatIconScaled"; if (ResourceCreate(g_scaled_newchat_resource, img_pixels_newchat, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Scaled new chat icon resource created successfully"); } else { Print("Failed to create scaled new chat icon resource"); } } else { Print("Failed to load new chat icon resource"); } uint img_pixels_clear[]; uint orig_width_clear = 0, orig_height_clear = 0; bool image_loaded_clear = ResourceReadImage(resourceClear, img_pixels_clear, orig_width_clear, orig_height_clear); if (image_loaded_clear && orig_width_clear > 0 && orig_height_clear > 0) { ScaleImage(img_pixels_clear, (int)orig_width_clear, (int)orig_height_clear, 30, 30); g_scaled_clear_resource = "::ChatGPT_ClearIconScaled"; if (ResourceCreate(g_scaled_clear_resource, img_pixels_clear, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Scaled clear icon resource created successfully"); } else { Print("Failed to create scaled clear icon resource"); } } else { Print("Failed to load clear icon resource"); } uint img_pixels_history[]; uint orig_width_history = 0, orig_height_history = 0; bool image_loaded_history = ResourceReadImage(resourceHistory, img_pixels_history, orig_width_history, orig_height_history); if (image_loaded_history && orig_width_history > 0 && orig_height_history > 0) { ScaleImage(img_pixels_history, (int)orig_width_history, (int)orig_height_history, 30, 30); g_scaled_history_resource = "::ChatGPT_HistoryIconScaled"; if (ResourceCreate(g_scaled_history_resource, img_pixels_history, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) { Print("Scaled history icon resource created successfully"); } else { Print("Failed to create scaled history icon resource"); } } else { Print("Failed to load history icon resource"); } g_sidebarWidth = sidebarExpanded ? expandedSidebarWidth : contractedSidebarWidth; g_mainContentX = g_dashboardX + g_sidebarWidth; g_dashboardWidth = g_sidebarWidth + g_mainWidth; g_mainHeight = g_headerHeight + 2 * g_padding + g_displayHeight + g_footerHeight; createRecLabel("ChatGPT_DashboardBg", g_dashboardX, g_mainY, g_dashboardWidth, g_mainHeight, clrWhite, 1, clrLightGray); ObjectSetInteger(0, "ChatGPT_DashboardBg", OBJPROP_ZORDER, 0); createRecLabel("ChatGPT_SidebarBg", g_dashboardX+2, g_mainY+2, g_sidebarWidth - 2 - 1, g_mainHeight - 2 - 2, clrGainsboro, 1, clrNONE); ObjectSetInteger(0, "ChatGPT_SidebarBg", OBJPROP_ZORDER, 0); //--- the rest of the functions return(INIT_SUCCEEDED); }
In der Funktion OnInit werden die neuen Elemente initialisiert, indem ihre neuen Farben eingestellt, die kleinen Vertragslogos skaliert und die kleine Seitenleiste initialisiert werden. Wir setzen zunächst dunklere Hover-Farben für die hinzugefügten Schaltflächen, wie „Toggle“, „History“, „See More“ und „Large Popup Close“, indem wir „DarkenColor“ auf deren Originalhintergrund anwenden. Wir erstellen dann eine Ressource „g_scaled_image_resource“ als „::ChatGPT_HeaderImageScaled“ mit ResourceCreate im ARGB-Format und protokollieren Erfolg oder Misserfolg.
In ähnlicher Weise skalieren wir das Logo für die Seitenleiste aus „resourceImgLogo“ auf 81x81 für „g_scaled_sidebar_resource“, wie wir es zuvor getan hatten, und weiter auf 30x30 für eine kleine Version „g_scaled_sidebar_small“, die uns derzeit interessiert, um Ressourcen und Protokollierung zu erstellen. Das Gleiche tun wir auch für die neuen Ressourcen, die wir später bei Bedarf entsprechend nutzen werden. Zur Verdeutlichung haben wir Hervorhebungen hinzugefügt, um die tatsächlich vorgenommenen Änderungen kenntlich zu machen. Als Nächstes müssen wir unsere dynamische Funktion für die Seitenleiste aktualisieren, um die Logik für zusammengezogene Zustände unterzubringen, und die Umschalttaste hinzufügen.
void UpdateSidebarDynamic() { int total = ObjectsTotal(0, 0, -1); for (int j = total - 1; j >= 0; j--) { string name = ObjectName(0, j, 0, -1); if (StringFind(name, "ChatGPT_NewChatButton") == 0 || StringFind(name, "ChatGPT_ClearButton") == 0 || StringFind(name, "ChatGPT_HistoryButton") == 0 || StringFind(name, "ChatGPT_ChatLabel_") == 0 || StringFind(name, "ChatGPT_ChatBg_") == 0 || StringFind(name, "ChatGPT_SidebarLogo") == 0 || StringFind(name, "ChatGPT_NewChatIcon") == 0 || StringFind(name, "ChatGPT_NewChatLabel") == 0 || StringFind(name, "ChatGPT_ClearIcon") == 0 || StringFind(name, "ChatGPT_ClearLabel") == 0 || StringFind(name, "ChatGPT_HistoryIcon") == 0 || StringFind(name, "ChatGPT_HistoryLabel") == 0 || StringFind(name, "ChatGPT_ToggleButton") == 0) { ObjectDelete(0, name); } } ArrayResize(side_chat_bgs, 0); int sidebarX = g_dashboardX; int itemY = g_mainY + 10; string sidebar_logo_resource = sidebarExpanded ? ((StringLen(g_scaled_sidebar_resource) > 0) ? g_scaled_sidebar_resource : resourceImgLogo) : ((StringLen(g_scaled_sidebar_small) > 0) ? g_scaled_sidebar_small : resourceImgLogo); int logo_size = sidebarExpanded ? 81 : 30; createBitmapLabel("ChatGPT_SidebarLogo", sidebarX + (g_sidebarWidth - logo_size)/2, itemY, logo_size, logo_size, sidebar_logo_resource, clrNONE, CORNER_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_SidebarLogo", OBJPROP_ZORDER, 1); itemY += logo_size + 10; createButton("ChatGPT_NewChatButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, "", clrWhite, 11, new_chat_original_bg, clrRoyalBlue); ObjectSetInteger(0, "ChatGPT_NewChatButton", OBJPROP_ZORDER, 1); string newchat_icon_resource = (StringLen(g_scaled_newchat_resource) > 0) ? g_scaled_newchat_resource : resourceNewChat; int iconX = sidebarExpanded ? sidebarX + 5 + 10 : sidebarX + 5 + (g_sidebarWidth - 10 - 30)/2; createBitmapLabel("ChatGPT_NewChatIcon", iconX, itemY + (g_buttonHeight - 30)/2, 30, 30, newchat_icon_resource, clrNONE, CORNER_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_NewChatIcon", OBJPROP_ZORDER, 2); ObjectSetInteger(0, "ChatGPT_NewChatIcon", OBJPROP_SELECTABLE, false); if (sidebarExpanded) { createLabel("ChatGPT_NewChatLabel", sidebarX + 5 + 10 + 30 + 5, itemY + (g_buttonHeight - 20)/2, "New Chat", clrWhite, 11, "Arial", CORNER_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_NewChatLabel", OBJPROP_ZORDER, 2); ObjectSetInteger(0, "ChatGPT_NewChatLabel", OBJPROP_SELECTABLE, false); } itemY += g_buttonHeight + 5; createButton("ChatGPT_ClearButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, "", clrWhite, 11, clear_original_bg, clrIndianRed); ObjectSetInteger(0, "ChatGPT_ClearButton", OBJPROP_ZORDER, 1); string clear_icon_resource = (StringLen(g_scaled_clear_resource) > 0) ? g_scaled_clear_resource : resourceClear; iconX = sidebarExpanded ? sidebarX + 5 + 10 : sidebarX + 5 + (g_sidebarWidth - 10 - 30)/2; createBitmapLabel("ChatGPT_ClearIcon", iconX, itemY + (g_buttonHeight - 30)/2, 30, 30, clear_icon_resource, clrNONE, CORNER_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_ClearIcon", OBJPROP_ZORDER, 2); ObjectSetInteger(0, "ChatGPT_ClearIcon", OBJPROP_SELECTABLE, false); if (sidebarExpanded) { createLabel("ChatGPT_ClearLabel", sidebarX + 5 + 10 + 30 + 5, itemY + (g_buttonHeight - 20)/2, "Clear", clrWhite, 11, "Arial", CORNER_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_ClearLabel", OBJPROP_ZORDER, 2); ObjectSetInteger(0, "ChatGPT_ClearLabel", OBJPROP_SELECTABLE, false); } itemY += g_buttonHeight + 10; createButton("ChatGPT_HistoryButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, "", clrBlack, 12, history_original_bg, clrGray); ObjectSetInteger(0, "ChatGPT_HistoryButton", OBJPROP_ZORDER, 1); string history_icon_resource = (StringLen(g_scaled_history_resource) > 0) ? g_scaled_history_resource : resourceHistory; iconX = sidebarExpanded ? sidebarX + 5 + 10 : sidebarX + 5 + (g_sidebarWidth - 10 - 30)/2; createBitmapLabel("ChatGPT_HistoryIcon", iconX, itemY + (g_buttonHeight - 30)/2, 30, 30, history_icon_resource, clrNONE, CORNER_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_HistoryIcon", OBJPROP_ZORDER, 2); ObjectSetInteger(0, "ChatGPT_HistoryIcon", OBJPROP_SELECTABLE, false); if (sidebarExpanded) { createLabel("ChatGPT_HistoryLabel", sidebarX + 5 + 10 + 30 + 5, itemY + (g_buttonHeight - 20)/2, "History", clrBlack, 12, "Arial", CORNER_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_HistoryLabel", OBJPROP_ZORDER, 2); ObjectSetInteger(0, "ChatGPT_HistoryLabel", OBJPROP_SELECTABLE, false); } itemY += g_buttonHeight + 5; if (sidebarExpanded) { int numChats = MathMin(ArraySize(chats), 7); int chatIndices[7]; for (int i = 0; i < numChats; i++) { chatIndices[i] = ArraySize(chats) - 1 - i; } for (int i = 0; i < numChats; i++) { int chatIdx = chatIndices[i]; string hashed_id = EncodeID(chats[chatIdx].id); string fullText = chats[chatIdx].title + " > " + hashed_id; string labelText = fullText; if (StringLen(fullText) > 19) { labelText = StringSubstr(fullText, 0, 16) + "..."; } string bgName = "ChatGPT_ChatBg_" + hashed_id; createRecLabel(bgName, sidebarX + 5 + 10, itemY, g_sidebarWidth - 10 - 10, 25, clrBeige, 1, DarkenColor(clrBeige, 0.9), BORDER_FLAT, STYLE_SOLID); ObjectSetInteger(0, bgName, OBJPROP_ZORDER, 1); color textColor = (chats[chatIdx].id == current_chat_id) ? clrBlue : clrBlack; createLabel("ChatGPT_ChatLabel_" + hashed_id, sidebarX + 10 + 10, itemY + 3, labelText, textColor, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER); ObjectSetInteger(0, "ChatGPT_ChatLabel_" + hashed_id, OBJPROP_ZORDER, 2); int size = ArraySize(side_chat_bgs); ArrayResize(side_chat_bgs, size + 1); side_chat_bgs[size] = bgName; itemY += 25 + 5; } } itemY += 10; string toggle_text = sidebarExpanded ? "<<" : ">>"; createButton("ChatGPT_ToggleButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, toggle_text, clrBlack, 12, toggle_original_bg, clrGray); ObjectSetInteger(0, "ChatGPT_ToggleButton", OBJPROP_ZORDER, 1); ChartRedraw(); }
Mit der Funktion „UpdateSidebarDynamic“ wird das Erscheinungsbild der Seitenleiste je nach ihrem Zustand (erweitert oder verkleinert) aktualisiert. Wir beginnen mit dem Löschen vorhandener Sidebar-Objekte, die durch Präfixe wie „ChatGPT_NewChatButton“ gekennzeichnet sind, und Toggle-Buttons, indem wir ObjectsTotal und ObjectDelete mit „StringFind“-Prüfungen verwenden und das Array „side_chat_bgs“ zurücksetzen. Wir setzen die x-Position der Seitenleiste auf „g_dashboardX“ und die anfängliche y-Position auf „g_mainY + 10“. Das Logo verwendet „g_scaled_sidebar_resource“ (81x81), wenn es wie zuvor erweitert wird, oder „g_scaled_sidebar_small“ (30x30), wenn es verkleinert wird, erstellt mit „createBitmapLabel“, zentriert über „sidebarX + (g_sidebarWidth – logo_size)/2“, mit z-order 1.
Für Schaltflächen („New Chat“, „Clear“, „History“) erstellen wir jede mit „createButton“ in der Breite „g_sidebarWidth – 10“, Höhe „g_buttonHeight“, z-order 1, unter Verwendung der Farben „new_chat_original_bg“, „clear_original_bg“ und „history_original_bg“. Ihre Symbole werden mit „createBitmapLabel“ bei „iconX“ platziert, linksbündig berechnet, wenn sie expandiert werden, oder zentriert, wenn sie kontrahiert werden, wobei skalierte Ressourcen oder Standardwerte wie „resourceNewChat“ verwendet werden, die auf die z-Reihenfolge 2 eingestellt und nicht auswählbar sind.
Beim Erweitern fügen wir Textbeschriftungen für jede Schaltfläche („New Chat“, „Clear“, „History“) mit „createLabel“ in Arial Größe 11/12, z-order 2, nicht auswählbar, rechts von den Symbolen positioniert. Wenn sie erweitert wird, werden wie bisher bis zu sieben aktuelle Chats aus dem Array „Chats“ angezeigt. Schließlich fügen wir die neue Umschalttaste „ChatGPT_ToggleButton“ mit „createButton“ hinzu, die „<<“ anzeigt, wenn sie ausgeklappt ist, oder „>>“, wenn sie zusammengeklappt ist – wir werden in der nächsten Version zu einer besseren Ansicht wechseln, in Arial Größe 12, z-order 1, und zeichnen das Chart mit ChartRedraw neu, um das aktualisierte Layout der Seitenleiste wiederzugeben. Nachdem wir uns nun um die Umschalttaste gekümmert haben, brauchen wir eine Funktion, die die Positionen/Größen für alle Dashboard-Objekte bei Bedarf neu berechnet und festlegt, um Überlappungslücken zu vermeiden.
void UpdateDashboardPositions() { ObjectSetInteger(0, "ChatGPT_DashboardBg", OBJPROP_XSIZE, g_dashboardWidth); ObjectSetInteger(0, "ChatGPT_SidebarBg", OBJPROP_XSIZE, g_sidebarWidth - 2 - 1); int diff = g_mainContentX - (g_dashboardX + (sidebarExpanded ? contractedSidebarWidth : expandedSidebarWidth)); for (int i = 0; i < objCount; i++) { string obj = dashboardObjects[i]; if (ObjectFind(0, obj) >= 0) { long curX = ObjectGetInteger(0, obj, OBJPROP_XDISTANCE); ObjectSetInteger(0, obj, OBJPROP_XDISTANCE, curX + diff); } } uint date_wid, date_hei; string dateStr = TimeToString(TimeTradeServer(), TIME_MINUTES); TextSetFont("Arial", 12); TextGetSize(dateStr, date_wid, date_hei); int dateX = g_mainContentX + g_mainWidth / 2 - (int)(date_wid / 2) + 20; int dateY = (int)ObjectGetInteger(0, "ChatGPT_DateLabel", OBJPROP_YDISTANCE); ObjectSetInteger(0, "ChatGPT_DateLabel", OBJPROP_XDISTANCE, dateX); int closeWidth = 100; int closeX = g_mainContentX + g_mainWidth - closeWidth - g_sidePadding; ObjectSetInteger(0, "ChatGPT_CloseButton", OBJPROP_XDISTANCE, closeX); int promptW = g_mainWidth - 2 * g_sidePadding; int editX = g_mainContentX + g_sidePadding + g_textPadding; g_editW = promptW - 2 * g_textPadding; ObjectSetInteger(0, "ChatGPT_PromptEdit", OBJPROP_XDISTANCE, editX); ObjectSetInteger(0, "ChatGPT_PromptEdit", OBJPROP_XSIZE, g_editW); int buttonW = 140; int chartX = g_mainContentX + g_sidePadding; int sendX = g_mainContentX + g_mainWidth - g_sidePadding - buttonW; ObjectSetInteger(0, "ChatGPT_GetChartButton", OBJPROP_XDISTANCE, chartX); ObjectSetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_XDISTANCE, sendX); int displayX = g_mainContentX + g_sidePadding; int displayW = g_mainWidth - 2 * g_sidePadding; ObjectSetInteger(0, "ChatGPT_ResponseBg", OBJPROP_XDISTANCE, displayX); ObjectSetInteger(0, "ChatGPT_ResponseBg", OBJPROP_XSIZE, displayW); ObjectSetInteger(0, "ChatGPT_PromptBg", OBJPROP_XDISTANCE, displayX); ObjectSetInteger(0, "ChatGPT_PromptBg", OBJPROP_XSIZE, displayW); int footerY = g_mainY + g_headerHeight + g_padding + g_displayHeight + g_padding; ObjectSetInteger(0, "ChatGPT_FooterBg", OBJPROP_XDISTANCE, g_mainContentX); ObjectSetInteger(0, "ChatGPT_FooterBg", OBJPROP_XSIZE, g_mainWidth); if (ObjectFind(0, "ChatGPT_PromptPlaceholder") >= 0) { int labelY = (int)ObjectGetInteger(0, "ChatGPT_PromptPlaceholder", OBJPROP_YDISTANCE); ObjectSetInteger(0, "ChatGPT_PromptPlaceholder", OBJPROP_XDISTANCE, editX + 2); } if (scroll_visible) { int displayY = g_mainY + g_headerHeight + g_padding; int scrollbar_x = displayX + displayW - 16; int button_size = 16; ObjectSetInteger(0, SCROLL_LEADER, OBJPROP_XDISTANCE, scrollbar_x); ObjectSetInteger(0, SCROLL_UP_REC, OBJPROP_XDISTANCE, scrollbar_x); ObjectSetInteger(0, SCROLL_UP_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2); ObjectSetInteger(0, SCROLL_DOWN_REC, OBJPROP_XDISTANCE, scrollbar_x); ObjectSetInteger(0, SCROLL_DOWN_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2); ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_XDISTANCE, scrollbar_x); UpdateSliderPosition(); } if (p_scroll_visible) { int promptX = g_mainContentX + g_sidePadding; int promptY = footerY + g_margin; int promptW = g_mainWidth - 2 * g_sidePadding; int scrollbar_x = promptX + promptW - 16; int button_size = 16; ObjectSetInteger(0, P_SCROLL_LEADER, OBJPROP_XDISTANCE, scrollbar_x); ObjectSetInteger(0, P_SCROLL_UP_REC, OBJPROP_XDISTANCE, scrollbar_x); ObjectSetInteger(0, P_SCROLL_UP_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2); ObjectSetInteger(0, P_SCROLL_DOWN_REC, OBJPROP_XDISTANCE, scrollbar_x); ObjectSetInteger(0, P_SCROLL_DOWN_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2); ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_XDISTANCE, scrollbar_x); UpdatePromptSliderPosition(); } }
Hier erstellen wir eine Funktion, die sicherstellt, dass sich der Hauptinhalt bei einer Größenänderung der Seitenleiste nach links/rechts verschiebt, um Überschneidungen oder Lücken zu vermeiden. Für nahtlose Übergänge werden wir es „on toggle“ nennen. Wir nennen die Funktion „UpdateDashboardPositions“. Zunächst aktualisieren wir die Breite des Dashboard-Hintergrunds „ChatGPT_DashboardBg“ auf „g_dashboardWidth“ und die Breite des Seitenleisten-Hintergrunds „ChatGPT_SidebarBg“ auf „g_sidebarWidth – 2 – 1“ mit ObjectSetInteger mit OBJPROP_XSIZE.
Wir berechnen eine horizontale Verschiebung „diff“ als die Änderung von „g_mainContentX“ relativ zum Start des Dashboards plus die entgegengesetzte Breite der Seitenleiste (zusammengezogen, wenn sie erweitert ist, und umgekehrt), dann durchlaufen wir eine Schleife durch „objCount“-Elemente in „dashboardObjects“ durch, prüfen jedes mit ObjectFind, rufen sein aktuelles X über ObjectGetInteger auf „OBJPROP_XDISTANCE“ ab und verschieben es um „diff“, um den Hauptinhalt neu zu positionieren. Hier ist es besonders wichtig zu beachten, dass wir den bestehenden Hauptinhalt nicht löschen und neu erstellen; wir verschieben nur seine Position, sodass kein Flackern entsteht.
Für die Datumsbeschriftung holen wir die aktuelle Serverzeit mit TimeToString, setzen die Schriftgröße Arial auf 12, berechnen die Abmessungen mit TextGetSize, zentrieren sie im Hauptbereich, der durch „g_mainContentX“ und „g_mainWidth“ angepasst wird, und setzen die neue X-Position. Wir berechnen den X-Wert der Schließen-Schaltfläche anhand von „g_mainContentX“ und „g_mainWidth“ abzüglich Padding und Width neu und setzen ihn entsprechend. Für die Eingabeaufforderung aktualisieren wir ihre Breite „promptW“ und die X/Breite des Eingabefeldes „g_editW“, wobei wir die Eingabeaufforderung mit „ObjectSetInteger“ auf „OBJPROP_XSIZE“ anwenden. Schaltflächen wie Chart und Senden erhalten neue X-Positionen von „g_mainContentX“ und „g_sidePadding“, die auf ähnliche Weise gesetzt werden.
Hintergründe für Antwort und Eingabeaufforderung werden mit aktualisierten X und Größen entsprechend „displayX“ und „displayW“ neu positioniert. Der Hintergrund der Fußzeile verschiebt sich zu „g_mainContentX“ mit einer Breite von „g_mainWidth“. Wenn der Platzhalter für die Eingabeaufforderung existiert, wird sein X so aktualisiert, dass es mit dem des Bearbeitungsfeldes übereinstimmt. Wenn der Hauptbildlaufbalken sichtbar ist, berechnen wir seine X-Position neu als „displayX + displayW – 16“ und aktualisieren die X-Positionen aller Komponenten (Vorspann, Aufwärts-/Abwärts-Rechtecke/Labels, Schieberegler) und rufen dann „UpdateSliderPosition“ auf. In ähnlicher Weise berechnen wir für die Prompt-Bildlaufleiste ihr X aus „promptX + promptW – 16“ und passen alle ihre Elemente an, indem wir „UpdatePromptSliderPosition“ aufrufen, um die Ausrichtung bei Zustandsänderungen beizubehalten. So weit, so gut. Nun müssen wir die Popups erstellen. Dafür werden wir neue Funktionen brauchen.
void ShowSmallHistoryPopup(int button_y) { ArrayResize(small_popup_objects, 0); ArrayResize(small_chat_bgs, 0); long history_x = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_XDISTANCE); long history_w = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_XSIZE); int popup_x = (int)(history_x + history_w); int popup_y = button_y; int popup_w = 200; int item_height = 25; int num_chats = MathMin(ArraySize(chats), 7); int items_h = num_chats * (item_height + 5) - 5; int popup_h = items_h + g_buttonHeight + 20; string popup_bg = "ChatGPT_SmallHistoryBg"; createRecLabel(popup_bg, popup_x, popup_y, popup_w, popup_h, clrWhite, 1, clrLightGray); int size = ArraySize(small_popup_objects); ArrayResize(small_popup_objects, size + 1); small_popup_objects[size] = popup_bg; int item_y = popup_y + 10; for (int i = 0; i < num_chats; i++) { int chatIdx = ArraySize(chats) - 1 - i; string hashed_id = EncodeID(chats[chatIdx].id); string labelText = chats[chatIdx].title; if (StringLen(labelText) > 25) labelText = StringSubstr(labelText, 0, 22) + "..."; string bgName = "ChatGPT_SmallChatBg_" + hashed_id; createRecLabel(bgName, popup_x + 10, item_y, popup_w - 20, item_height, clrBeige, 1, DarkenColor(clrBeige, 0.9), BORDER_FLAT, STYLE_SOLID); string labelName = "ChatGPT_SmallChatLabel_" + hashed_id; createLabel(labelName, popup_x + 20, item_y + 3, labelText, clrBlack, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER); size = ArraySize(small_popup_objects); ArrayResize(small_popup_objects, size + 2); small_popup_objects[size] = bgName; small_popup_objects[size + 1] = labelName; int bg_size = ArraySize(small_chat_bgs); ArrayResize(small_chat_bgs, bg_size + 1); small_chat_bgs[bg_size] = bgName; item_y += item_height + 5; } string see_more = "ChatGPT_SeeMoreButton"; createButton(see_more, popup_x + 10, item_y, popup_w - 20, g_buttonHeight, "See All", clrWhite, 11, see_more_original_bg, clrDarkBlue); size = ArraySize(small_popup_objects); ArrayResize(small_popup_objects, size + 1); small_popup_objects[size] = see_more; small_popup_x = popup_x; small_popup_y = popup_y; small_popup_w = popup_w; small_popup_h = popup_h; showing_small_history_popup = true; just_opened_small = true; } void DeleteSmallHistoryPopup() { for (int i = 0; i < ArraySize(small_popup_objects); i++) { ObjectDelete(0, small_popup_objects[i]); } ArrayResize(small_popup_objects, 0); ArrayResize(small_chat_bgs, 0); showing_small_history_popup = false; } void ShowBigHistoryPopup() { ArrayResize(big_popup_objects, 0); ArrayResize(big_chat_bgs, 0); big_scroll_pos = 0; big_scroll_visible = false; int popup_w = g_mainWidth - 20; int item_height = 25; int num_chats = ArraySize(chats); big_total_height = num_chats * (item_height + 5) - 5; int max_h = g_displayHeight - 20; int content_h = max_h - 40 - 10; big_visible_height = content_h - 35; int popup_h = MathMin(big_total_height +40+20 + 10, max_h); big_popup_w = popup_w; big_popup_h = popup_h; big_popup_x = g_mainContentX + 10; big_popup_y = g_mainY + g_headerHeight + g_padding + 10; string popup_bg = "ChatGPT_BigHistoryBg"; createRecLabel(popup_bg, big_popup_x, big_popup_y, popup_w, popup_h, C'250,250,250', 1, clrDodgerBlue); int size = ArraySize(big_popup_objects); ArrayResize(big_popup_objects, size + 1); big_popup_objects[size] = popup_bg; string close_button = "ChatGPT_BigCloseButton"; createButton(close_button, big_popup_x + popup_w - 80 - 10 + 40, big_popup_y + 5, 40, 30, "r", clrRed, 13, big_close_original_bg, clrGray,"Webdings"); size = ArraySize(big_popup_objects); ArrayResize(big_popup_objects, size + 1); big_popup_objects[size] = close_button; int content_y = big_popup_y + 40; string content_bg = "ChatGPT_BigContentBg"; createRecLabel(content_bg, big_popup_x + 10, content_y, popup_w - 20, content_h, clrWhite, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID); size = ArraySize(big_popup_objects); ArrayResize(big_popup_objects, size + 1); big_popup_objects[size] = content_bg; bool need_big_scroll = big_total_height > big_visible_height; int reserved_w = need_big_scroll ? 16 : 0; int item_w = popup_w - 20 - 20 - reserved_w; UpdateBigHistoryDisplay(); if (need_big_scroll) { CreateBigScrollbar(); big_scroll_visible = true; UpdateBigSliderPosition(); UpdateBigButtonColors(); } showing_big_history_popup = true; just_opened_big = true; ChartRedraw(); } void CreateBigScrollbar() { int scrollbar_x = big_popup_x + big_popup_w - 10 - 16; int scrollbar_y = big_popup_y + 40 + 16; int scrollbar_width = 16; int scrollbar_height = big_popup_h - 40 - 10 - 2 * 16; int button_size = 16; createRecLabel(BIG_SCROLL_LEADER, scrollbar_x, scrollbar_y, scrollbar_width, scrollbar_height, C'220,220,220', 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER); createRecLabel(BIG_SCROLL_UP_REC, scrollbar_x, big_popup_y + 40, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER); createLabel(BIG_SCROLL_UP_LABEL, scrollbar_x + 2, big_popup_y + 40 + -2, CharToString(0x35), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER); createRecLabel(BIG_SCROLL_DOWN_REC, scrollbar_x, big_popup_y + 40 + (big_popup_h - 40 - 10) - button_size, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER); createLabel(BIG_SCROLL_DOWN_LABEL, scrollbar_x + 2, big_popup_y + 40 + (big_popup_h - 40 - 10) - button_size + -2, CharToString(0x36), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER); big_slider_height = CalculateBigSliderHeight(); createRecLabel(BIG_SCROLL_SLIDER, scrollbar_x, big_popup_y + 40 + (big_popup_h - 40 - 10) - button_size - big_slider_height, scrollbar_width, big_slider_height, clrSilver, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER); int size = ArraySize(big_popup_objects); ArrayResize(big_popup_objects, size + 6); big_popup_objects[size] = BIG_SCROLL_LEADER; big_popup_objects[size + 1] = BIG_SCROLL_UP_REC; big_popup_objects[size + 2] = BIG_SCROLL_UP_LABEL; big_popup_objects[size + 3] = BIG_SCROLL_DOWN_REC; big_popup_objects[size + 4] = BIG_SCROLL_DOWN_LABEL; big_popup_objects[size + 5] = BIG_SCROLL_SLIDER; } int CalculateBigSliderHeight() { int scroll_area_height = big_popup_h - 40 - 25 - 2 * 16; int slider_min_height = 20; if (big_total_height <= big_visible_height) return scroll_area_height; double visible_ratio = (double)big_visible_height / big_total_height; int height = (int)MathFloor(scroll_area_height * visible_ratio); return MathMax(slider_min_height, height); } void UpdateBigSliderPosition() { int scrollbar_x = big_popup_x + big_popup_w - 10 - 16; int scrollbar_y = big_popup_y + 40 + 16; int scroll_area_height = big_popup_h - 40 - 25 - 2 * 16; int max_scroll = MathMax(0, big_total_height - big_visible_height); if (max_scroll <= 0) return; double scroll_ratio = (double)big_scroll_pos / max_scroll; int scroll_area_y_max = scrollbar_y + scroll_area_height - big_slider_height; int scroll_area_y_min = scrollbar_y; int new_y = scroll_area_y_min + (int)(scroll_ratio * (scroll_area_y_max - scroll_area_y_min)); new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max)); ObjectSetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y); } void UpdateBigButtonColors() { int max_scroll = MathMax(0, big_total_height - big_visible_height); if (big_scroll_pos == 0) { ObjectSetInteger(0, BIG_SCROLL_UP_LABEL, OBJPROP_COLOR, clrSilver); } else { ObjectSetInteger(0, BIG_SCROLL_UP_LABEL, OBJPROP_COLOR, clrDimGray); } if (big_scroll_pos == max_scroll) { ObjectSetInteger(0, BIG_SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrSilver); } else { ObjectSetInteger(0, BIG_SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrDimGray); } } void BigScrollUp() { if (big_scroll_pos > 0) { big_scroll_pos = MathMax(0, big_scroll_pos - 30); UpdateBigHistoryDisplay(); if (big_scroll_visible) { UpdateBigSliderPosition(); UpdateBigButtonColors(); } } } void BigScrollDown() { int max_scroll = MathMax(0, big_total_height - big_visible_height); if (big_scroll_pos < max_scroll) { big_scroll_pos = MathMin(max_scroll, big_scroll_pos + 30); UpdateBigHistoryDisplay(); if (big_scroll_visible) { UpdateBigSliderPosition(); UpdateBigButtonColors(); } } } void UpdateBigHistoryDisplay() { int total = ObjectsTotal(0, 0, -1); for (int j = total - 1; j >= 0; j--) { string name = ObjectName(0, j, 0, -1); if (StringFind(name, "ChatGPT_BigChatBg_") == 0 || StringFind(name, "ChatGPT_BigChatLabel_") == 0) { ObjectDelete(0, name); } } int content_y = big_popup_y + 40; int content_h = big_popup_h - 40 - 10; int item_height = 25; int num_chats = ArraySize(chats); big_total_height = num_chats * (item_height + 5) - 5; bool need_big_scroll = big_total_height > big_visible_height; int reserved_w = need_big_scroll ? 16 : 0; int item_w = big_popup_w - 20 - 20 - reserved_w; int item_y = content_y + 10 - big_scroll_pos; int end_y = content_y + content_h - 25; int start_idx = 0; int current_h = 0; for (int i = 0; i < num_chats; i++) { if (current_h >= big_scroll_pos) { start_idx = i; item_y = content_y + 10 + (current_h - big_scroll_pos); break; } current_h += item_height + 5; } int visible_count = 0; current_h = 0; for (int i = start_idx; i < num_chats; i++) { if (current_h + item_height > big_visible_height) break; current_h += item_height + 5; visible_count++; } for (int i = 0; i < visible_count; i++) { int chatIdx = num_chats - 1 - (start_idx + i); string hashed_id = EncodeID(chats[chatIdx].id); string fullText = chats[chatIdx].title + " > " + hashed_id; string labelText = fullText; if (StringLen(fullText) > 35) { labelText = StringSubstr(fullText, 0, 32) + "..."; } string bgName = "ChatGPT_BigChatBg_" + hashed_id; if (item_y >= content_y + 10 && item_y < end_y) { createRecLabel(bgName, big_popup_x + 20, item_y, item_w, item_height, clrBeige, 1, DarkenColor(clrBeige, 0.9), BORDER_FLAT, STYLE_SOLID); } string labelName = "ChatGPT_BigChatLabel_" + hashed_id; if (item_y >= content_y + 10 && item_y < end_y) { createLabel(labelName, big_popup_x + 30, item_y + 3, labelText, clrBlack, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER); } int size = ArraySize(big_popup_objects); ArrayResize(big_popup_objects, size + 2); big_popup_objects[size] = bgName; big_popup_objects[size + 1] = labelName; int bg_size = ArraySize(big_chat_bgs); ArrayResize(big_chat_bgs, bg_size + 1); big_chat_bgs[bg_size] = bgName; item_y += item_height + 5; } ChartRedraw(); } void DeleteBigHistoryPopup() { for (int i = 0; i < ArraySize(big_popup_objects); i++) { ObjectDelete(0, big_popup_objects[i]); } ArrayResize(big_popup_objects, 0); ArrayResize(big_chat_bgs, 0); showing_big_history_popup = false; big_scroll_visible = false; }
Wir implementieren die Funktion „ShowSmallHistoryPopup“, um ein kompaktes Popup für die letzten Chat-Verläufe anzuzeigen, wenn man mit dem Mauszeiger über die Verlaufsschaltfläche im zusammengezogenen Seitenleistenmodus fährt, und setzen die Arrays „small_popup_objects“ und „small_chat_bgs“ zurück. Wir berechnen die Position des Popup-Fensters rechts von der Verlaufsschaltfläche, indem wir ObjectGetInteger für X und Breite verwenden, die Breite auf 200 Pixel und die Elementhöhe auf 25 Pixel setzen und die Höhe dynamisch aus bis zu sieben aktuellen Chats sowie einer Schaltfläche „Alle anzeigen“ berechnen. Wir erstellen den Hintergrund „ChatGPT_SmallHistoryBg“ mit „createRecLabel“ in weiß mit hellgrauem Rand, dann gehen wir in einer Schleife durch die letzten Chats (die neuesten zuerst), erzeugen Hintergründe „ChatGPT_SmallChatBg_“ und Labels „ChatGPT_SmallChatLabel_“ mit „createRecLabel“ und „createLabel“, schneiden, falls erforderlich, die Titel mit Ellipsen auf 22 Zeichen ab, speichern die Namen in Arrays für die Bereinigung und fügen eine Schaltfläche „See More“ am unteren Rand mit „createButton“ in Königsblau hinzu.
Die Funktion „DeleteSmallHistoryPopup“ löscht das kleine Popup, indem sie alle Objekte in „small_popup_objects“ mit ObjectDelete löscht, Arrays zurücksetzt und „showing_small_history_popup“ auf false setzt. Für „ShowBigHistoryPopup“ zeigen wir ein vollständig verschiebbares Verlaufs-Popup an, setzen „big_popup_objects“ und „big_chat_bgs“ zurück, initialisieren „big_scroll_pos“ auf 0 und setzen die Sichtbarkeit auf false. Wir berechnen die Popup-Breite als „g_mainWidth – 20“, die Höhe wird auf den Maximalwert der gesamten Chats geklemmt, indem wir die Elementhöhe 25 plus Polsterung verwenden, die Positionierung wird vom Hauptinhalt abgesetzt und der Hintergrund „ChatGPT_BigHistoryBg“ wird in Hellgrau mit blauem Rand erstellt. Wir ergänzen eine Schaltfläche zum Schließen „ChatGPT_BigCloseButton“ mit „createButton“ unter Verwendung von Webdings 'r' in Rot, einen Inhaltshintergrund „ChatGPT_BigContentBg“ in Weiß mit gainsboro-Rahmen hinzu, bestimmen, ob Scrollen erforderlich ist, basierend auf „big_total_height“ vs. „big_visible_height“. Wir rufen „UpdateBigHistoryDisplay“ auf, um Chats aufzufüllen, und wenn Scrollen erforderlich ist, rufen wir „CreateBigScrollbar“ auf, setzen die Sichtbarkeit auf true und aktualisieren Position und Farben. Wir markieren wie gezeigt mit „showing_big_history_popup“ true und „just_opened_big“ true und zeichnen mit der Funktion ChartRedraw neu.
Die Funktion „CreateBigScrollbar“ erstellt die Bildlaufleiste des großen Popups ähnlich wie andere, indem sie „createRecLabel“ für den Vorspann, Auf-/Ab-Rechtecke in Gainsboro, Beschriftungen mit Webdings-Pfeilen in Dim-Grey und einen Schieberegler in Silber mit Gainsboro-Rand, wobei die Positionen aus „big_popup_x/y/w/h“ berechnet werden, und Namen zur Verwaltung an „big_popup_objects“ angehängt werden. „CalculateBigSliderHeight“ berechnet die Höhe des großen Schiebereglers proportional von der sichtbaren Höhe zur Gesamthöhe, mit mindestens 20 Pixeln, und gibt den vollen Bereich zurück, wenn kein Scrollen erforderlich ist. UpdateBigSliderPosition“ passt das y des großen Schiebereglers auf der Grundlage des Verhältnisses von „big_scroll_pos“ zu „max scroll“ an, indem es den Bildlaufbereich mit Hilfe von „MathMax“ und „MathMin“, die mit der Funktion ObjectSetInteger festgelegt werden, einklemmt. „UpdateBigButtonColors“ graut die Auf-/Abwärtspfeile in Silber aus, wenn sie sich oben/unten befinden, oder in Grau, wenn sie gescrollt werden können. „BigScrollUp“ und „BigScrollDown“ dekrementieren/erhöhen „big_scroll_pos“ um 30, beschränkt zwischen 0/max, dann aktualisieren wir die Anzeige mit „UpdateBigHistoryDisplay“ und, falls sichtbar, Position/Farben mit „UpdateBigSliderPosition“ und „UpdateBigButtonColors“.
In „UpdateBigHistoryDisplay“ löschen wir die vorhandenen großen Chat-Hintergründe und Beschriftungen mit ObjectsTotal und „ObjectDelete“ unter Verwendung der StringFind-Funktion. Wir berechnen „big_total_height“ aus der Anzahl der Chats neu, bestimmen den Scrollbedarf und die reservierte Breite, setzen die Elementbreite entsprechend, berechnen den Startindex und y aus „big_scroll_pos“, zählen die sichtbaren Elemente innerhalb von „big_visible_height“, erstellen dann in einer Schleife Hintergründe „ChatGPT_BigChatBg_“ und Beschriftungen „ChatGPT_BigChatLabel_“ für sichtbare Chats (die neuesten zuerst), Wir beschneiden der Titel auf 32 Zeichen mit Ellipsis, speichern in den Arrays, positionieren nur innerhalb der Ansichtsgrenzen und zeichnen mit „ChartRedraw“ das Chart neu. Die Funktion „DeleteBigHistoryPopup“ entfernt alle Objekte in „big_popup_objects“ mit ObjectDelete, setzt Arrays zurück, setzt „showing_big_history_popup“ und „big_scroll_visible“ auf false zur Bereinigung. Als Nächstes müssen wir diese Funktionen gegebenenfalls im Chartereignis aufrufen.
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- most relevant events if (id == CHARTEVENT_CLICK) { int clickX = (int)lparam; int clickY = (int)dparam; if (showing_big_history_popup) { if (just_opened_big) { just_opened_big = false; } else if (clickX < big_popup_x || clickX > big_popup_x + big_popup_w || clickY < big_popup_y || clickY > big_popup_y + big_popup_h) { DeleteBigHistoryPopup(); ChartRedraw(); } } if (showing_small_history_popup) { if (just_opened_small) { just_opened_small = false; } else if (clickX < small_popup_x || clickX > small_popup_x + small_popup_w || clickY < small_popup_y || clickY > small_popup_y + small_popup_h) { DeleteSmallHistoryPopup(); ChartRedraw(); } } return; } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_ToggleButton") { sidebarExpanded = !sidebarExpanded; g_sidebarWidth = sidebarExpanded ? expandedSidebarWidth : contractedSidebarWidth; g_mainContentX = g_dashboardX + g_sidebarWidth; g_dashboardWidth = g_sidebarWidth + g_mainWidth; if (showing_big_history_popup) DeleteBigHistoryPopup(); if (showing_small_history_popup) DeleteSmallHistoryPopup(); UpdateSidebarDynamic(); UpdateDashboardPositions(); UpdateResponseDisplay(); UpdatePromptDisplay(); ChartRedraw(); } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_HistoryButton" && sidebarExpanded) { ShowBigHistoryPopup(); just_opened_big = true; } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_SeeMoreButton") { DeleteSmallHistoryPopup(); ShowBigHistoryPopup(); just_opened_big = true; } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_BigCloseButton") { DeleteBigHistoryPopup(); ChartRedraw(); } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == BIG_SCROLL_UP_REC || sparam == BIG_SCROLL_UP_LABEL)) { BigScrollUp(); } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == BIG_SCROLL_DOWN_REC || sparam == BIG_SCROLL_DOWN_LABEL)) { BigScrollDown(); } //--- the rest of the hover and mouse move logic implementation for new elements is the same }
Wir verwenden die Ereignisbehandlung durch OnChartEvent, um Nutzerinteraktionen für die ausklappbare Seitenleiste und die Verlaufs-Popups zu verarbeiten, wobei wir uns auf Klicks außerhalb der Popups und auf bestimmte Objektklicks konzentrieren. Bei allgemeinen Klicks (CHARTEVENT_CLICK) werden die Koordinaten als „clickX“ und „clickY“ erfasst, und wenn das große Popup angezeigt, aber nicht gerade geöffnet wird (Überprüfung von „just_opened_big“, um ein versehentliches sofortiges Schließen zu verhindern), wird überprüft, ob der Klick außerhalb seiner Grenzen liegt, und „DeleteBigHistoryPopup“ gefolgt von ChartRedraw aufgerufen, um es zu schließen. Für das kleine Popup verwenden wir ebenfalls „just_opened_small“ und „DeleteSmallHistoryPopup“. Das ist sehr wichtig, denn wir wollen, dass die Popups geschlossen werden, wenn wir außerhalb der Popups klicken und sie geöffnet sind. Ich hielt es für einen genialen Schachzug, einen „Off-Click“-Ereignis-Hörer einzurichten, obwohl dies nicht notwendig war.
Bei Objektklicks (CHARTEVENT_OBJECT_CLICK) auf die Umschalttaste „ChatGPT_ToggleButton“, klappen wir „sidebarExpanded“ um, berechnen „g_sidebarWidth“ neu auf expanded oder contracted, aktualisieren „g_mainContentX“ und „g_dashboardWidth“, schließen alle offenen Popups mit „DeleteBigHistoryPopup“ und „DeleteSmallHistoryPopup“, dann aktualisieren wir die Nutzeroberfläche durch Aufruf der Funktionen „UpdateSidebarDynamic“, „UpdateDashboardPositions“, „UpdateResponseDisplay“, „UpdatePromptDisplay“ und „ChartRedraw“. Wenn bei ausgeklappter Seitenleiste auf die Schaltfläche „ChatGPT_HistoryButton“ geklickt wird, rufen wir „ShowBigHistoryPopup“ auf und setzen „just_opened_big“ auf true.
Für die Schaltfläche „See More“ von „ChatGPT_SeeMoreButton“ löschen wir das kleine Popup mit „DeleteSmallHistoryPopup“, zeigen das große und setzen „just_opened_big“. Auf der großen Schließen-Schaltfläche „ChatGPT_BigCloseButton“ rufen wir „DeleteBigHistoryPopup“ auf und zeichnen es neu. Für große Bildlaufleisten-Elemente nach oben („BIG_SCROLL_UP_REC“ oder Label) lösen wir „BigScrollUp“ aus; ähnlich für unten mit „BigScrollDown“. Die verbleibende Logik für Hover-Effekte und Mausbewegungen auf neuen Elementen folgt demselben Muster wie bei früheren Implementierungen, um eine konsistente Interaktivität zu gewährleisten. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

Wenn wir die Popups geöffnet haben, wollen wir keine Aktualisierungen auf dem Hauptbildschirm vornehmen. Um dies zu erreichen, müssen wir eine bedingte Anweisung in die Antwortaktualisierungsfunktion einfügen, um unnötige Neuzeichnungen/Konflikte zu verhindern, wenn sich das Popup mit Hauptbereichen überschneidet, was die Stabilität verbessert und das Flackern während der Verwendung des Popups reduziert.
void UpdateResponseDisplay() { if (showing_small_history_popup || showing_big_history_popup) return; //--- rest of the function logic }
Hier fügen wir einfach die bedingte Anweisung am Anfang der Funktion hinzu, sodass wir, wenn die Popups geöffnet sind, die Aktualisierung der Antwortanzeige überspringen, nur weil wir die Popups innerhalb ihres Bereichs erstellen. Nach der Kompilierung ergibt sich folgendes Ergebnis für das große Popup.

Anhand des Bildes können wir sehen, dass wir das Programm durch Hinzufügen der neuen Zielelemente, das Zusammenklappen der Seitenleiste und das Hinzufügen von Chat-Popups verbessern und somit unsere Ziele erreichen konnten. Bleiben nur noch die Backtests des Programms, und das wird im nächsten Abschnitt behandelt.
Testen der ausklappbaren Seitenleiste
Wir haben die Tests durchgeführt, und unten sehen Sie die kompilierte Visualisierung in einem einzigen Graphics Interchange Format (GIF) Bitmap-Bildformat.

Schlussfolgerung
Zusammenfassend lässt sich sagen, dass wir unser KI-gestütztes Handelssystem in MQL5 verbessert haben, indem wir eine zusammenklappbare Seitenleiste eingeführt haben, die zur besseren Verwaltung des Bildschirms zwischen erweitertem und verkleinertem Zustand hin- und herschaltet, kleine, auf Hover basierende Verlaufs-Popups und eine große, verschiebbare Verlaufsansicht für eine effiziente Chat-Navigation integriert haben, während wir die mehrzeilige Eingabe, die verschlüsselte Persistenz und die KI-gesteuerte Signalerzeugung aus Chartdaten beibehalten haben. Dieses Upgrade optimiert die Nutzeroberfläche im Hinblick auf Flexibilität und ermöglicht es uns, die Sichtbarkeit von Charts zu maximieren oder bei Bedarf auf detaillierte Steuerelemente zuzugreifen, mit nahtlosen Übergängen und intuitiven Hover-Effekten. In den nächsten Abschnitten werden wir die neue Chat-Suchfunktion zum Durchsuchen der Chats, die zusätzlichen Löschschaltflächen zum Löschen von Chats und die erweiterten Signalausführungs- und Automatisierungsfunktionen zur weiteren Stärkung des KI-gestützten Handels untersuchen. Bleiben Sie dran.
Anlagen
| S/N | Name | Typ | Beschreibung |
|---|---|---|---|
| 1 | AI_JSON_FILE.mqh | JSON-Klassenbibliothek | Klasse zur Handhabung der JSON-Serialisierung und -Deserialisierung |
| 2 | AI_CREATE_OBJECTS_FNS.mqh | Bibliothek der Objektfunktionen | Funktionen zur Erstellung von Visualisierungsobjekten wie Beschriftungen und Schaltflächen |
| 3 | AI_ChatGPT_EA_Part_5.mq5 | Expert Advisor | Haupt-Expertenratgeber für die KI-Integration |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20249
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Entwicklung des Price Action Analysis Toolkit (Teil 51): Revolutionäre Chart-Suchtechnologie für die Entdeckung von Kerzenmustern
Integration von MQL5 mit Datenverarbeitungspaketen (Teil 6): Zusammenführung von Markt-Feedback und Modellanpassung
Entwicklung einer Handelsstrategie: Ansatz der Pseudo-Pearson-Korrelation
Selbstoptimierende Expert Advisors in MQL5 (Teil 17): Ensemble Intelligence
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.