Browse Source

Add image provider for map previews and integrate with QML

Co-authored-by: djeada <[email protected]>
copilot-swe-agent[bot] 4 days ago
parent
commit
f6bb078fa6

+ 2 - 0
CMakeLists.txt

@@ -139,6 +139,7 @@ if(QT_VERSION_MAJOR EQUAL 6)
         app/models/hover_tracker.cpp
         app/models/selected_units_model.cpp
         app/models/minimap_image_provider.cpp
+        app/models/map_preview_image_provider.cpp
         app/controllers/command_controller.cpp
         app/controllers/action_vfx.cpp
         app/utils/json_vec_utils.cpp
@@ -164,6 +165,7 @@ else()
         app/models/hover_tracker.cpp
         app/models/selected_units_model.cpp
         app/models/minimap_image_provider.cpp
+        app/models/map_preview_image_provider.cpp
         app/controllers/command_controller.cpp
         app/controllers/action_vfx.cpp
         app/utils/json_vec_utils.cpp

+ 48 - 0
app/models/map_preview_image_provider.cpp

@@ -0,0 +1,48 @@
+#include "map_preview_image_provider.h"
+
+#include <QMutexLocker>
+
+MapPreviewImageProvider::MapPreviewImageProvider()
+    : QQuickImageProvider(QQuickImageProvider::Image) {}
+
+QImage MapPreviewImageProvider::requestImage(const QString &id, QSize *size,
+                                             const QSize &requested_size) {
+  QImage image_copy;
+  {
+    QMutexLocker locker(&m_mutex);
+    if (m_preview_images.contains(id)) {
+      image_copy = m_preview_images[id];
+    }
+  }
+
+  if (image_copy.isNull()) {
+    QImage placeholder(200, 200, QImage::Format_RGBA8888);
+    placeholder.fill(QColor(40, 40, 40));
+    if (size) {
+      *size = placeholder.size();
+    }
+    return placeholder;
+  }
+
+  if (size) {
+    *size = image_copy.size();
+  }
+
+  if (requested_size.isValid() && !requested_size.isEmpty()) {
+    return image_copy.scaled(requested_size, Qt::KeepAspectRatio,
+                             Qt::SmoothTransformation);
+  }
+
+  return image_copy;
+}
+
+void MapPreviewImageProvider::set_preview_image(const QString &map_id,
+                                                const QImage &image) {
+  QMutexLocker locker(&m_mutex);
+  m_preview_images[map_id] = image;
+}
+
+void MapPreviewImageProvider::clear_preview(const QString &map_id) {
+  QMutexLocker locker(&m_mutex);
+  m_preview_images.remove(map_id);
+}

+ 22 - 0
app/models/map_preview_image_provider.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#include <QImage>
+#include <QMap>
+#include <QMutex>
+#include <QQuickImageProvider>
+#include <QString>
+
+class MapPreviewImageProvider : public QQuickImageProvider {
+public:
+  MapPreviewImageProvider();
+
+  QImage requestImage(const QString &id, QSize *size,
+                      const QSize &requested_size) override;
+
+  void set_preview_image(const QString &map_id, const QImage &image);
+  void clear_preview(const QString &map_id);
+
+private:
+  QMap<QString, QImage> m_preview_images;
+  QMutex m_mutex;
+};

+ 8 - 0
main.cpp

@@ -39,6 +39,7 @@
 #include "app/core/language_manager.h"
 #include "app/models/graphics_settings_proxy.h"
 #include "app/models/minimap_image_provider.h"
+#include "app/models/map_preview_image_provider.h"
 #include "ui/gl_view.h"
 #include "ui/theme.h"
 
@@ -295,10 +296,17 @@ auto main(int argc, char *argv[]) -> int {
   auto *minimap_provider = new MinimapImageProvider();
   engine->addImageProvider("minimap", minimap_provider);
 
+  // Register map preview image provider
+  qInfo() << "Registering map preview image provider...";
+  auto *map_preview_provider = new MapPreviewImageProvider();
+  engine->addImageProvider("mappreview", map_preview_provider);
+
   qInfo() << "Adding context properties...";
   engine->rootContext()->setContextProperty("languageManager",
                                             language_manager.get());
   engine->rootContext()->setContextProperty("game", game_engine.get());
+  engine->rootContext()->setContextProperty("mapPreviewProvider",
+                                            map_preview_provider);
   engine->rootContext()->setContextProperty("graphicsSettings",
                                             graphics_settings.get());
 

+ 18 - 4
ui/qml/MapPreview.qml

@@ -7,6 +7,7 @@ Rectangle {
     property var mapPath: ""
     property var playerConfigs: []
     property bool loading: false
+    property string previewId: ""
 
     radius: Theme.radiusLarge
     color: Theme.cardBase
@@ -17,6 +18,7 @@ Rectangle {
     function refreshPreview() {
         if (!mapPath || mapPath === "" || !playerConfigs || playerConfigs.length === 0) {
             previewImage.source = "";
+            previewId = "";
             return;
         }
 
@@ -26,8 +28,19 @@ Rectangle {
 
         loading = true;
         try {
+            // Generate a unique ID based on map path and player configs
+            var configStr = JSON.stringify(playerConfigs);
+            var newId = mapPath + "_" + configStr.length + "_" + Date.now();
+            
             var preview = game.generate_map_preview(mapPath, playerConfigs);
-            previewImage.image = preview;
+            
+            if (typeof mapPreviewProvider !== "undefined") {
+                mapPreviewProvider.set_preview_image(newId, preview);
+                previewId = newId;
+                // Force image reload by changing the source
+                previewImage.source = "image://mappreview/" + newId;
+            }
+            
             loading = false;
         } catch (e) {
             console.error("MapPreview: Failed to generate preview:", e);
@@ -76,7 +89,8 @@ Rectangle {
             height: width
             fillMode: Image.PreserveAspectFit
             smooth: true
-            visible: !loading && source !== ""
+            cache: false
+            visible: !loading && status === Image.Ready
         }
 
         Text {
@@ -85,7 +99,7 @@ Rectangle {
             color: Theme.textHint
             font.pixelSize: 13
             horizontalAlignment: Text.AlignHCenter
-            visible: !loading && previewImage.source === "" && mapPath === ""
+            visible: !loading && previewImage.status !== Image.Ready && mapPath === ""
         }
 
         Text {
@@ -94,7 +108,7 @@ Rectangle {
             color: Theme.textHint
             font.pixelSize: 13
             horizontalAlignment: Text.AlignHCenter
-            visible: !loading && previewImage.source === "" && mapPath !== ""
+            visible: !loading && previewImage.status !== Image.Ready && mapPath !== ""
         }
 
         Item {