--- a/qmlfrontend/CMakeLists.txt Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/CMakeLists.txt Mon Feb 17 16:37:59 2025 +0100
@@ -32,9 +32,15 @@
"net_session.cpp" "net_session.h"
"players_model.cpp" "players_model.h"
"rooms_model.cpp" "rooms_model.h"
+ "renderer/tiled_image_item.h" "renderer/tiled_image_item.cpp"
)
-target_link_libraries(${PROJECT_NAME}
+target_include_directories(${PROJECT_NAME} PRIVATE
+ renderer
+)
+
+
+target_link_libraries(${PROJECT_NAME}
PRIVATE
Qt6::Core
Qt6::Network
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlfrontend/GameScene.qml Mon Feb 17 16:37:59 2025 +0100
@@ -0,0 +1,112 @@
+import QtQuick 2.15
+import Hedgewars 1.0
+
+Item {
+ id: control
+
+ property string dataPrefix: "share/hedgewars/Data/"
+ property url themePath: "file://%1Themes/Stage/".arg(dataPrefix)
+
+ Flickable {
+ id: backgroundContainer
+
+ anchors.fill: parent
+
+ contentWidth: map.implicitWidth
+ contentHeight: map.implicitHeight
+
+ interactive: false
+ contentX: mapContainer.contentX * 0.7 + (contentWidth - width) * 0.15
+ contentY: mapContainer.contentY * 0.7 + (contentHeight - height) * 0.3
+
+ Rectangle {
+ anchors.fill: parent
+ color: "black"
+ }
+
+ Item {
+ width: parent.width
+ height: parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+
+ Image {
+ id: skyLeft
+ anchors.left: parent.left
+ anchors.right: skyCenter.left
+ anchors.bottom: parent.bottom
+ fillMode: Image.TileHorizontally
+ horizontalAlignment: Image.AlignRight
+ source: control.themePath + "SkyL.png"
+ }
+
+ Image {
+ id: skyCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ fillMode: Image.Pad
+ source: control.themePath + "Sky.png"
+ }
+
+ Image {
+ id: skyRight
+ anchors.left: skyCenter.right
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ fillMode: Image.TileHorizontally
+ horizontalAlignment: Image.AlignLeft
+ source: control.themePath + "SkyL.png"
+ }
+ }
+ }
+
+ Flickable {
+ id: mapContainer
+
+ anchors.fill: parent
+
+ boundsMovement: Flickable.StopAtBounds
+
+ contentWidth: map.implicitWidth
+ contentHeight: map.implicitHeight
+
+ onContentXChanged: map.update()
+ onContentYChanged: map.update()
+ onWidthChanged: map.update()
+ onHeightChanged: map.update()
+
+ pixelAligned: true
+
+ TiledImageItem {
+ id: map
+
+ }
+ }
+
+ MouseArea {
+ anchors.fill: mapContainer
+
+ property double zoom: 1.0
+
+ acceptedButtons: Qt.NoButton
+
+ enabled: false
+
+ onWheel: wheel => {
+ zoom = zoom * Math.exp(wheel.angleDelta.y * 0.001)
+ var zoomPoint = Qt.point(wheel.x + mapContainer.contentX,
+ wheel.y + mapContainer.contentY);
+
+ map.scale = zoom
+ mapContainer.resizeContent(map.implicitWidth * zoom, map.implicitHeight * zoom, zoomPoint);
+ mapContainer.returnToBounds();
+ console.log(mapContainer.scale)
+ }
+ }
+
+
+ Component.onCompleted: {
+ map.test(dataPrefix + "Maps/Ropes/map.png")
+ console.log(Screen.devicePixelRatio)
+ }
+}
--- a/qmlfrontend/engine_instance.cpp Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/engine_instance.cpp Mon Feb 17 16:37:59 2025 +0100
@@ -56,7 +56,7 @@
advance_simulation && move_camera && simple_event && long_event &&
positioned_event;
- emit isValidChanged(m_isValid);
+ Q_EMIT isValidChanged(m_isValid);
if (isValid()) {
qDebug() << "Loaded engine library with protocol version"
--- a/qmlfrontend/hwengine.cpp Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/hwengine.cpp Mon Feb 17 16:37:59 2025 +0100
@@ -14,7 +14,7 @@
HWEngine::~HWEngine() {}
void HWEngine::getPreview() {
- emit previewIsRendering();
+ Q_EMIT previewIsRendering();
m_gameConfig = GameConfig();
m_gameConfig.cmdSeed(QUuid::createUuid().toByteArray());
@@ -29,7 +29,7 @@
if (m_previewAcceptor) m_previewAcceptor->setImage(previewImage);
- emit previewImageChanged();
+ Q_EMIT previewImageChanged();
// m_runQueue->queue(m_gameConfig);
}
@@ -60,14 +60,14 @@
if (m_previewAcceptor == previewAcceptor) return;
m_previewAcceptor = previewAcceptor;
- emit previewAcceptorChanged(m_previewAcceptor);
+ Q_EMIT previewAcceptorChanged(m_previewAcceptor);
}
void HWEngine::setEngineLibrary(const QString& engineLibrary) {
if (m_engineLibrary == engineLibrary) return;
m_engineLibrary = engineLibrary;
- emit engineLibraryChanged(m_engineLibrary);
+ Q_EMIT engineLibraryChanged(m_engineLibrary);
}
const QString &HWEngine::dataPath() const
@@ -80,5 +80,5 @@
if (m_dataPath == newDataPath)
return;
m_dataPath = newDataPath;
- emit dataPathChanged();
+ Q_EMIT dataPathChanged();
}
--- a/qmlfrontend/hwengine.h Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/hwengine.h Mon Feb 17 16:37:59 2025 +0100
@@ -38,11 +38,11 @@
const QString &dataPath() const;
void setDataPath(const QString &newDataPath);
-public slots:
+public Q_SLOTS:
void setPreviewAcceptor(PreviewAcceptor* previewAcceptor);
void setEngineLibrary(const QString& engineLibrary);
- signals:
+ Q_SIGNALS:
void previewIsRendering();
void previewImageChanged();
void previewHogCountChanged(int count);
--- a/qmlfrontend/main.cpp Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/main.cpp Mon Feb 17 16:37:59 2025 +0100
@@ -7,26 +7,6 @@
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
- /*
- qRegisterMetaType<Engine::SimpleEventType>();
- qRegisterMetaType<Engine::LongEventType>();
- qRegisterMetaType<Engine::LongEventState>();
- qRegisterMetaType<Engine::PositionedEventType>();
-
- qmlRegisterSingletonType<PreviewAcceptor>(
- "Hedgewars.Engine", 1, 0, "PreviewAcceptor",
- previewacceptor_singletontype_provider);
- qmlRegisterType<HWEngine>("Hedgewars.Engine", 1, 0, "HWEngine");
- qmlRegisterType<GameView>("Hedgewars.Engine", 1, 0, "GameView");
- qmlRegisterType<NetSession>("Hedgewars.Engine", 1, 0, "NetSession");
- qmlRegisterUncreatableType<EngineInstance>(
- "Hedgewars.Engine", 1, 0, "EngineInstance",
- QStringLiteral("Create by HWEngine run methods"));
-
- qmlRegisterUncreatableMetaObject(Engine::staticMetaObject,
- "Hedgewars.Engine", 1, 0, "Engine", QStringLiteral("Namespace: only
- enums"));
- */
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty()) {
return -1;
--- a/qmlfrontend/main.qml Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/main.qml Mon Feb 17 16:37:59 2025 +0100
@@ -8,11 +8,7 @@
height: 480
title: qsTr("Hello World")
- SwipeView {
- id: swipeView
+ GameScene {
anchors.fill: parent
-
- Page1 {
- }
}
}
--- a/qmlfrontend/net_session.cpp Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/net_session.cpp Mon Feb 17 16:37:59 2025 +0100
@@ -48,21 +48,21 @@
if (m_url == url) return;
m_url = url;
- emit urlChanged(m_url);
+ Q_EMIT urlChanged(m_url);
}
void NetSession::setNickname(const QString &nickname) {
if (m_nickname == nickname) return;
m_nickname = nickname;
- emit nicknameChanged(m_nickname);
+ Q_EMIT nicknameChanged(m_nickname);
}
void NetSession::setPasswordHash(const QString &passwordHash) {
if (m_passwordHash == passwordHash) return;
m_passwordHash = passwordHash;
- emit passwordHashChanged(m_passwordHash);
+ Q_EMIT passwordHashChanged(m_passwordHash);
if (m_sessionState == Authentication) sendPassword();
}
@@ -71,7 +71,7 @@
if (m_room == room) return;
m_room = room;
- emit roomChanged(m_room);
+ Q_EMIT roomChanged(m_room);
}
void NetSession::close() {
@@ -155,7 +155,7 @@
void NetSession::handleConnected(const QStringList ¶meters) {
if (parameters.length() < 2 || parameters[1].toInt() < cMinServerVersion) {
send("QUIT", "Server too old");
- emit error(tr("Server too old"));
+ Q_EMIT error(tr("Server too old"));
close();
} else {
setSessionState(Login);
@@ -191,7 +191,7 @@
m_clientSalt = QUuid::createUuid().toString();
if (m_passwordHash.isEmpty()) {
- emit passwordAsked();
+ Q_EMIT passwordAsked();
} else {
sendPassword();
}
@@ -227,7 +227,7 @@
// check if server is authenticated or no authentication was performed at
// all
if (!m_serverAuthHash.isEmpty()) {
- emit error(tr("Server authentication error"));
+ Q_EMIT error(tr("Server authentication error"));
close();
}
@@ -363,5 +363,5 @@
m_sessionState = sessionState;
- emit sessionStateChanged(sessionState);
+ Q_EMIT sessionStateChanged(sessionState);
}
--- a/qmlfrontend/players_model.cpp Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/players_model.cpp Mon Feb 17 16:37:59 2025 +0100
@@ -36,7 +36,7 @@
m_data[index.row()].insert(role, value);
- emit dataChanged(index, index);
+ Q_EMIT dataChanged(index, index);
return true;
}
@@ -87,15 +87,15 @@
checkFriendIgnore(mi);
- emit nickAddedLobby(nickname, notify);
+ Q_EMIT nickAddedLobby(nickname, notify);
}
void PlayersListModel::removePlayer(const QString &nickname,
const QString &msg) {
if (msg.isEmpty())
- emit nickRemovedLobby(nickname, QString());
+ Q_EMIT nickRemovedLobby(nickname, QString());
else
- emit nickRemovedLobby(nickname, msg);
+ Q_EMIT nickRemovedLobby(nickname, msg);
QModelIndex mi = nicknameIndex(nickname);
@@ -111,11 +111,11 @@
updateSortData(mi);
}
- emit nickAdded(nickname, notify);
+ Q_EMIT nickAdded(nickname, notify);
}
void PlayersListModel::playerLeftRoom(const QString &nickname) {
- emit nickRemoved(nickname);
+ Q_EMIT nickRemoved(nickname);
QModelIndex mi = nicknameIndex(nickname);
--- a/qmlfrontend/players_model.h Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/players_model.h Mon Feb 17 16:37:59 2025 +0100
@@ -49,7 +49,7 @@
QModelIndex nicknameIndex(const QString &nickname);
- public slots:
+ public Q_SLOTS:
void addPlayer(const QString &nickname, bool notify);
void removePlayer(const QString &nickname, const QString &msg = QString());
void playerJoinedRoom(const QString &nickname, bool notify);
@@ -57,7 +57,7 @@
void resetRoomFlags();
void setNickname(const QString &nickname);
- signals:
+ Q_SIGNALS:
void nickAdded(const QString &nick, bool notifyNick);
void nickRemoved(const QString &nick);
void nickAddedLobby(const QString &nick, bool notifyNick);
--- a/qmlfrontend/qml.qrc Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/qml.qrc Mon Feb 17 16:37:59 2025 +0100
@@ -5,5 +5,6 @@
<file>Page1Form.ui.qml</file>
<file>qtquickcontrols2.conf</file>
<file>res/iconTime.png</file>
+ <file>GameScene.qml</file>
</qresource>
</RCC>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlfrontend/renderer/tiled_image_item.cpp Mon Feb 17 16:37:59 2025 +0100
@@ -0,0 +1,124 @@
+#include "tiled_image_item.h"
+
+#include <QSGImageNode>
+#include <QSGTexture>
+
+TiledImageItem::TiledImageItem(QQuickItem *parent) : QQuickItem{parent} {
+ setFlag(ItemHasContents, true);
+ setFlag(ItemObservesViewport, true);
+}
+
+TiledImageItem::~TiledImageItem() {}
+
+void TiledImageItem::setImageData(int width, int height, uchar *pixels,
+ QImage::Format format) {
+ m_texture = QImage{pixels, width, height, width * 4, format};
+
+ setImplicitWidth(width / window()->effectiveDevicePixelRatio());
+ setImplicitHeight(height / window()->effectiveDevicePixelRatio());
+
+ buildTiles();
+
+ update();
+}
+
+void TiledImageItem::buildTiles() {
+ m_tiles.clear();
+ if (m_texture.isNull()) {
+ return;
+ }
+
+ const auto imageWidth = m_texture.width();
+ const auto imageHeight = m_texture.height();
+
+ for (int y = 0; y < imageHeight; y += m_tileSize) {
+ for (int x = 0; x < imageWidth; x += m_tileSize) {
+ int w = qMin(m_tileSize, imageWidth - x);
+ int h = qMin(m_tileSize, imageHeight - y);
+ QImage tileImage{&m_texture.scanLine(y)[x * m_texture.depth() / 8],
+ m_tileSize, m_tileSize, m_texture.bytesPerLine(),
+ m_texture.format()};
+
+ QRectF tileRect = QRect{x, y, w, h}.toRectF();
+ tileRect.setTopLeft(tileRect.topLeft() /
+ window()->effectiveDevicePixelRatio());
+ tileRect.setBottomRight(tileRect.bottomRight() /
+ window()->effectiveDevicePixelRatio());
+
+ Tile tile;
+ tile.outRect = tileRect;
+ tile.image = tileImage;
+ tile.dirty = true;
+ m_tiles.emplace_back(std::move(tile));
+ }
+ }
+}
+int TiledImageItem::tileSize() const { return m_tileSize; }
+
+void TiledImageItem::setTileSize(int newTileSize) {
+ if (m_tileSize == newTileSize) {
+ return;
+ }
+
+ m_tileSize = newTileSize;
+
+ m_tiles.clear();
+ m_dirty = true;
+
+ Q_EMIT tileSizeChanged();
+}
+
+void TiledImageItem::test(const QString &fileName) {
+ auto image = new QImage(fileName);
+
+ setImageData(image->width(), image->height(), image->bits(), image->format());
+}
+
+QSGNode *TiledImageItem::updatePaintNode(QSGNode *oldNode,
+ UpdatePaintNodeData *) {
+ auto rootNode = oldNode;
+ if (!rootNode) {
+ rootNode = new QSGNode{};
+ }
+
+ if (!window()) {
+ return rootNode;
+ }
+
+ const auto rect = clipRect();
+
+ for (auto &tile : m_tiles) {
+ if (!rect.intersects(tile.outRect)) {
+ if (tile.node) {
+ rootNode->removeChildNode(tile.node.get());
+ tile.node.reset();
+ }
+
+ continue;
+ }
+
+ if (tile.dirty) {
+ tile.texture.reset(window()->createTextureFromImage(tile.image));
+ if (tile.node) {
+ tile.node->setTexture(tile.texture.get());
+ }
+
+ tile.dirty = false;
+ }
+
+ if (!tile.node) {
+ tile.node.reset(window()->createImageNode());
+ tile.node->setRect(tile.outRect);
+ tile.node->setTexture(tile.texture.get());
+ rootNode->appendChildNode(tile.node.get());
+ }
+ }
+
+ return rootNode;
+}
+
+TiledImageItem::Tile::Tile(Tile &&other) noexcept
+ : outRect{other.outRect},
+ image{std::move(other.image)},
+ dirty{other.dirty},
+ texture{std::move(other.texture)} {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmlfrontend/renderer/tiled_image_item.h Mon Feb 17 16:37:59 2025 +0100
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <QImage>
+#include <QQuickItem>
+#include <QQuickWindow>
+
+class TiledImageItem : public QQuickItem {
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(
+ int tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged FINAL)
+
+ public:
+ explicit TiledImageItem(QQuickItem *parent = nullptr);
+ ~TiledImageItem();
+
+ void setImageData(int width, int height, uchar *pixels,
+ QImage::Format format);
+
+ int tileSize() const;
+ void setTileSize(int newTileSize);
+
+ Q_INVOKABLE void test(const QString &fileName);
+
+ protected:
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
+
+ Q_SIGNALS:
+
+ void tileSizeChanged();
+
+ private:
+ Q_DISABLE_COPY_MOVE(TiledImageItem)
+
+ QImage m_texture;
+ bool m_dirty;
+
+ struct Tile {
+ Tile() = default;
+ Tile(Tile &&other) noexcept;
+ ~Tile() = default;
+ QRectF outRect;
+ QImage image;
+ bool dirty = false;
+ std::unique_ptr<QSGImageNode> node;
+ std::unique_ptr<QSGTexture, QScopedPointerDeleteLater> texture;
+ };
+
+ void buildTiles();
+ int m_tileSize{512};
+ std::vector<Tile> m_tiles;
+};
--- a/qmlfrontend/rooms_model.cpp Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/rooms_model.cpp Mon Feb 17 16:37:59 2025 +0100
@@ -227,5 +227,5 @@
m_data[i] = info;
- emit dataChanged(index(i, 0), index(i, columnCount(QModelIndex()) - 1));
+ Q_EMIT dataChanged(index(i, 0), index(i, columnCount(QModelIndex()) - 1));
}
--- a/qmlfrontend/rooms_model.h Tue Feb 04 17:31:55 2025 +0100
+++ b/qmlfrontend/rooms_model.h Mon Feb 17 16:37:59 2025 +0100
@@ -51,7 +51,7 @@
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
- public slots:
+ public Q_SLOTS:
void setRoomsList(const QStringList &rooms);
void addRoom(const QStringList &info);
void removeRoom(const QString &name);