@dusted/anqst 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/emit.js CHANGED
@@ -8,9 +8,11 @@ exports.writeGeneratedOutputs = writeGeneratedOutputs;
8
8
  exports.installTypeScriptOutputs = installTypeScriptOutputs;
9
9
  exports.installEmbeddedWebBundle = installEmbeddedWebBundle;
10
10
  exports.installQtIntegrationCMake = installQtIntegrationCMake;
11
+ exports.installQtDesignerPluginCMake = installQtDesignerPluginCMake;
11
12
  const node_fs_1 = __importDefault(require("node:fs"));
12
13
  const node_path_1 = __importDefault(require("node:path"));
13
14
  const typescript_1 = __importDefault(require("typescript"));
15
+ const pngjs_1 = require("pngjs");
14
16
  function stripAnQstType(typeText) {
15
17
  return typeText
16
18
  .replace(/\bAnQst\.Type\.stringArray\b/g, "string[]")
@@ -158,6 +160,87 @@ function cppToVariantExpression(cppType, expr) {
158
160
  }
159
161
  return `QVariant::fromValue(${expr})`;
160
162
  }
163
+ function splitTopLevelTemplateArgs(text) {
164
+ const args = [];
165
+ let start = 0;
166
+ let depth = 0;
167
+ for (let i = 0; i < text.length; i++) {
168
+ const ch = text[i];
169
+ if (ch === "<") {
170
+ depth++;
171
+ continue;
172
+ }
173
+ if (ch === ">") {
174
+ depth--;
175
+ continue;
176
+ }
177
+ if (ch === "," && depth === 0) {
178
+ args.push(text.slice(start, i).trim());
179
+ start = i + 1;
180
+ }
181
+ }
182
+ const tail = text.slice(start).trim();
183
+ if (tail.length > 0) {
184
+ args.push(tail);
185
+ }
186
+ return args;
187
+ }
188
+ function templateTypeArgs(cppType, containerName) {
189
+ const trimmed = cppType.trim();
190
+ const prefix = `${containerName}<`;
191
+ if (!trimmed.startsWith(prefix) || !trimmed.endsWith(">")) {
192
+ return null;
193
+ }
194
+ const inner = trimmed.slice(prefix.length, -1).trim();
195
+ if (inner.length === 0)
196
+ return [];
197
+ return splitTopLevelTemplateArgs(inner);
198
+ }
199
+ function isNumericCppType(cppType) {
200
+ return [
201
+ "double",
202
+ "qint64",
203
+ "quint64",
204
+ "qint32",
205
+ "quint32",
206
+ "qint16",
207
+ "quint16",
208
+ "qint8",
209
+ "quint8",
210
+ "int8_t",
211
+ "uint8_t",
212
+ "int16_t",
213
+ "uint16_t",
214
+ "int32_t",
215
+ "uint32_t"
216
+ ].includes(cppType);
217
+ }
218
+ function designerPlaceholderCppExpression(cppType, memberName) {
219
+ const escapedMember = escapeCppStringLiteral(memberName);
220
+ const stringLiteral = `QStringLiteral("${escapedMember} value")`;
221
+ if (cppType === "QString")
222
+ return stringLiteral;
223
+ if (cppType === "bool")
224
+ return "true";
225
+ if (isNumericCppType(cppType))
226
+ return `static_cast<${cppType}>(1)`;
227
+ if (cppType === "QStringList")
228
+ return `QStringList{${stringLiteral}}`;
229
+ if (cppType === "QVariantMap") {
230
+ return `QVariantMap{{QStringLiteral("${escapedMember}"), QVariant(${stringLiteral})}}`;
231
+ }
232
+ const optionalArgs = templateTypeArgs(cppType, "std::optional");
233
+ if (optionalArgs && optionalArgs.length === 1) {
234
+ const inner = optionalArgs[0];
235
+ return `std::optional<${inner}>{${designerPlaceholderCppExpression(inner, memberName)}}`;
236
+ }
237
+ const listArgs = templateTypeArgs(cppType, "QList");
238
+ if (listArgs && listArgs.length === 1) {
239
+ const inner = listArgs[0];
240
+ return `QList<${inner}>{${designerPlaceholderCppExpression(inner, memberName)}}`;
241
+ }
242
+ return `${cppType}{}`;
243
+ }
161
244
  function collectStructDecls(spec) {
162
245
  const out = new Map();
163
246
  for (const d of spec.namespaceTypeDecls)
@@ -746,7 +829,12 @@ function renderCppStub(spec, cppTypes) {
746
829
  const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
747
830
  const pascal = pascalCase(member.name);
748
831
  lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
749
- lines.push(` if (!m_${member.name}Handler) return QVariant();`);
832
+ lines.push(` if (!m_${member.name}Handler) {`);
833
+ lines.push(` if (property("anqstDesignerContext").toBool()) {`);
834
+ lines.push(` return ${cppToVariantExpression(cppType, designerPlaceholderCppExpression(cppType, member.name))};`);
835
+ lines.push(` }`);
836
+ lines.push(` return QVariant();`);
837
+ lines.push(` }`);
750
838
  for (let i = 0; i < member.parameters.length; i++) {
751
839
  const p = member.parameters[i];
752
840
  const pType = cppTypes.mapTypeText(p.typeText, [service.name, member.name, p.name]);
@@ -913,6 +1001,57 @@ function escapeXml(text) {
913
1001
  function normalizeSlashes(value) {
914
1002
  return value.split(node_path_1.default.sep).join("/");
915
1003
  }
1004
+ function resolveActiveBuildStamp() {
1005
+ const fromEnv = process.env.ANQST_BUILD_STAMP?.trim();
1006
+ if (fromEnv && fromEnv.length > 0) {
1007
+ return fromEnv;
1008
+ }
1009
+ const activePath = node_path_1.default.resolve(__dirname, "..", "..", ".anqstgen-version-active.json");
1010
+ if (!node_fs_1.default.existsSync(activePath)) {
1011
+ return "";
1012
+ }
1013
+ try {
1014
+ const parsed = JSON.parse(node_fs_1.default.readFileSync(activePath, "utf8"));
1015
+ if (typeof parsed.active === "string" && parsed.active.trim().length > 0) {
1016
+ return parsed.active.trim();
1017
+ }
1018
+ }
1019
+ catch {
1020
+ return "";
1021
+ }
1022
+ return "";
1023
+ }
1024
+ function withBuildStamp(relativePath, content) {
1025
+ const stamp = resolveActiveBuildStamp();
1026
+ if (!stamp) {
1027
+ return content;
1028
+ }
1029
+ const marker = `Built by AnQst ${stamp}`;
1030
+ const rel = normalizeSlashes(relativePath);
1031
+ const lower = rel.toLowerCase();
1032
+ if (lower.endsWith(".json")) {
1033
+ try {
1034
+ const parsed = JSON.parse(content);
1035
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1036
+ const next = { "//": marker, ...parsed };
1037
+ return `${JSON.stringify(next, null, 2)}\n`;
1038
+ }
1039
+ }
1040
+ catch {
1041
+ // If JSON parsing fails, fall through to plain comment prefix.
1042
+ }
1043
+ }
1044
+ if (lower.endsWith(".qrc") || lower.endsWith(".xml") || lower.endsWith(".html")) {
1045
+ return `<!-- ${marker} -->\n${content}`;
1046
+ }
1047
+ if (lower.endsWith(".cmake")) {
1048
+ return `# ${marker}\n${content}`;
1049
+ }
1050
+ if (lower.endsWith(".h") || lower.endsWith(".cpp") || lower.endsWith(".ts") || lower.endsWith(".js") || lower.endsWith(".d.ts")) {
1051
+ return `// ${marker}\n${content}`;
1052
+ }
1053
+ return `# ${marker}\n${content}`;
1054
+ }
916
1055
  function renderEmbeddedQrc(widgetName, embeddedWebFiles) {
917
1056
  const files = [...embeddedWebFiles].sort();
918
1057
  const lines = [];
@@ -1231,6 +1370,11 @@ class WebSocketBridgeAdapter implements BridgeAdapter {
1231
1370
  }
1232
1371
  if (type === "hostError") {
1233
1372
  console.error("AnQst host error:", message["payload"]);
1373
+ return;
1374
+ }
1375
+ if (type === "widgetReattached") {
1376
+ document.body.textContent = "Widget Reattached";
1377
+ this.socket.close();
1234
1378
  }
1235
1379
  });
1236
1380
  }
@@ -1240,12 +1384,22 @@ class WebSocketBridgeAdapter implements BridgeAdapter {
1240
1384
  if (!configResponse.ok) {
1241
1385
  throw new Error("AnQst host bootstrap missing: unable to read /anqst-dev-config.json");
1242
1386
  }
1243
- const config = (await configResponse.json()) as { wsUrl?: string };
1244
- if (!config.wsUrl) {
1245
- throw new Error("AnQst host bootstrap missing: wsUrl is unavailable.");
1387
+ const config = (await configResponse.json()) as { wsUrl?: string; wsPath?: string };
1388
+ let wsUrl = config.wsUrl;
1389
+ if (!wsUrl && config.wsPath) {
1390
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
1391
+ wsUrl = protocol + "//" + window.location.host + config.wsPath;
1392
+ }
1393
+ if (!wsUrl) {
1394
+ throw new Error("AnQst host bootstrap missing: wsUrl/wsPath is unavailable.");
1395
+ }
1396
+ if (wsUrl.startsWith("http://")) {
1397
+ wsUrl = "ws://" + wsUrl.slice("http://".length);
1398
+ } else if (wsUrl.startsWith("https://")) {
1399
+ wsUrl = "wss://" + wsUrl.slice("https://".length);
1246
1400
  }
1247
1401
  return await new Promise<WebSocketBridgeAdapter>((resolve, reject) => {
1248
- const socket = new WebSocket(config.wsUrl!);
1402
+ const socket = new WebSocket(wsUrl!);
1249
1403
  socket.addEventListener("open", () => resolve(new WebSocketBridgeAdapter(socket)));
1250
1404
  socket.addEventListener("error", () => reject(new Error("Failed to connect to AnQst WebSocket bridge.")));
1251
1405
  });
@@ -2224,7 +2378,7 @@ function writeGeneratedOutputs(cwd, outputs) {
2224
2378
  for (const [relPath, content] of Object.entries(outputs)) {
2225
2379
  const filePath = node_path_1.default.join(outputRoot, relPath);
2226
2380
  node_fs_1.default.mkdirSync(node_path_1.default.dirname(filePath), { recursive: true });
2227
- node_fs_1.default.writeFileSync(filePath, content, "utf8");
2381
+ node_fs_1.default.writeFileSync(filePath, withBuildStamp(relPath, content), "utf8");
2228
2382
  }
2229
2383
  }
2230
2384
  function installTypeScriptOutputs(cwd) {
@@ -2393,7 +2547,7 @@ function installEmbeddedWebBundle(cwd, widgetName) {
2393
2547
  normalizeEmbeddedIndexHtml(node_path_1.default.join(cppLibraryWebRoot, "index.html"), cppLibraryWebRoot);
2394
2548
  const embeddedFiles = listFilesRecursively(cppLibraryWebRoot);
2395
2549
  const qrcPath = node_path_1.default.join(cppLibraryRoot, `${widgetName}.qrc`);
2396
- node_fs_1.default.writeFileSync(qrcPath, renderEmbeddedQrc(widgetName, embeddedFiles), "utf8");
2550
+ node_fs_1.default.writeFileSync(qrcPath, withBuildStamp(`${widgetName}.qrc`, renderEmbeddedQrc(widgetName, embeddedFiles)), "utf8");
2397
2551
  return true;
2398
2552
  }
2399
2553
  function normalizeEmbeddedIndexHtml(indexPath, webRoot) {
@@ -2496,5 +2650,299 @@ target_link_libraries(${widgetTarget}
2496
2650
  function installQtIntegrationCMake(cwd, widgetName) {
2497
2651
  const integrationDir = node_path_1.default.join(cwd, "anqst-cmake");
2498
2652
  node_fs_1.default.mkdirSync(integrationDir, { recursive: true });
2499
- node_fs_1.default.writeFileSync(node_path_1.default.join(integrationDir, "CMakeLists.txt"), renderQtIntegrationCMake(widgetName), "utf8");
2653
+ node_fs_1.default.writeFileSync(node_path_1.default.join(integrationDir, "CMakeLists.txt"), withBuildStamp("anqst-cmake/CMakeLists.txt", renderQtIntegrationCMake(widgetName)), "utf8");
2654
+ }
2655
+ function normalizeIcoSize(dim) {
2656
+ return dim === 0 ? 256 : dim;
2657
+ }
2658
+ function escapeCppStringLiteral(value) {
2659
+ return value
2660
+ .replace(/\\/g, "\\\\")
2661
+ .replace(/"/g, '\\"')
2662
+ .replace(/\r/g, "\\r")
2663
+ .replace(/\n/g, "\\n");
2664
+ }
2665
+ function readDistFavicon(cwd) {
2666
+ const distRoot = node_path_1.default.join(cwd, "dist");
2667
+ if (!node_fs_1.default.existsSync(distRoot) || !node_fs_1.default.statSync(distRoot).isDirectory()) {
2668
+ return null;
2669
+ }
2670
+ const stack = [distRoot];
2671
+ while (stack.length > 0) {
2672
+ const current = stack.shift();
2673
+ const entries = node_fs_1.default.readdirSync(current, { withFileTypes: true })
2674
+ .sort((a, b) => a.name.localeCompare(b.name));
2675
+ for (const entry of entries) {
2676
+ const fullPath = node_path_1.default.join(current, entry.name);
2677
+ if (entry.isDirectory()) {
2678
+ stack.push(fullPath);
2679
+ continue;
2680
+ }
2681
+ if (entry.isFile() && entry.name.toLowerCase() === "favicon.ico") {
2682
+ return node_fs_1.default.readFileSync(fullPath);
2683
+ }
2684
+ }
2685
+ }
2686
+ return null;
2687
+ }
2688
+ function resolveFaviconIcoBuffer(cwd) {
2689
+ const distFavicon = readDistFavicon(cwd);
2690
+ if (distFavicon !== null) {
2691
+ return distFavicon;
2692
+ }
2693
+ const fallbackFiles = [
2694
+ node_path_1.default.join(cwd, "res", "favicon.ico"),
2695
+ node_path_1.default.join(cwd, "src", "favicon.ico"),
2696
+ node_path_1.default.join(cwd, "favicon.ico")
2697
+ ];
2698
+ for (const filePath of fallbackFiles) {
2699
+ if (node_fs_1.default.existsSync(filePath) && node_fs_1.default.statSync(filePath).isFile()) {
2700
+ return node_fs_1.default.readFileSync(filePath);
2701
+ }
2702
+ }
2703
+ return null;
2704
+ }
2705
+ function decodeIcoBmpToPng(imageData) {
2706
+ if (imageData.length < 40) {
2707
+ throw new Error("ICO BMP frame too small.");
2708
+ }
2709
+ const headerSize = imageData.readUInt32LE(0);
2710
+ if (headerSize < 40 || imageData.length < headerSize) {
2711
+ throw new Error("ICO BMP frame has unsupported DIB header.");
2712
+ }
2713
+ const width = imageData.readInt32LE(4);
2714
+ const heightTotal = imageData.readInt32LE(8);
2715
+ const planes = imageData.readUInt16LE(12);
2716
+ const bitCount = imageData.readUInt16LE(14);
2717
+ const compression = imageData.readUInt32LE(16);
2718
+ if (width <= 0 || heightTotal <= 0) {
2719
+ throw new Error("ICO BMP frame has invalid dimensions.");
2720
+ }
2721
+ const height = Math.floor(heightTotal / 2);
2722
+ if (height <= 0) {
2723
+ throw new Error("ICO BMP frame has invalid mask height.");
2724
+ }
2725
+ if (planes !== 1 || bitCount !== 32 || compression !== 0) {
2726
+ throw new Error("ICO BMP frame format unsupported; expected 32-bit BI_RGB.");
2727
+ }
2728
+ const pixelOffset = headerSize;
2729
+ const rowBytes = width * 4;
2730
+ const pixelBytes = rowBytes * height;
2731
+ if (imageData.length < pixelOffset + pixelBytes) {
2732
+ throw new Error("ICO BMP frame is truncated.");
2733
+ }
2734
+ const png = new pngjs_1.PNG({ width, height });
2735
+ for (let y = 0; y < height; y += 1) {
2736
+ const srcY = height - 1 - y;
2737
+ const srcRow = pixelOffset + srcY * rowBytes;
2738
+ const dstRow = y * rowBytes;
2739
+ for (let x = 0; x < width; x += 1) {
2740
+ const src = srcRow + x * 4;
2741
+ const dst = dstRow + x * 4;
2742
+ const b = imageData[src];
2743
+ const g = imageData[src + 1];
2744
+ const r = imageData[src + 2];
2745
+ const a = imageData[src + 3];
2746
+ png.data[dst] = r;
2747
+ png.data[dst + 1] = g;
2748
+ png.data[dst + 2] = b;
2749
+ png.data[dst + 3] = a;
2750
+ }
2751
+ }
2752
+ return pngjs_1.PNG.sync.write(png);
2753
+ }
2754
+ function convertIcoToPngBuffer(icoBytes) {
2755
+ if (icoBytes.length < 6) {
2756
+ throw new Error("favicon.ico is too small.");
2757
+ }
2758
+ const reserved = icoBytes.readUInt16LE(0);
2759
+ const iconType = icoBytes.readUInt16LE(2);
2760
+ const count = icoBytes.readUInt16LE(4);
2761
+ if (reserved !== 0 || iconType !== 1 || count === 0) {
2762
+ throw new Error("favicon.ico has invalid ICO header.");
2763
+ }
2764
+ if (icoBytes.length < 6 + count * 16) {
2765
+ throw new Error("favicon.ico has truncated directory entries.");
2766
+ }
2767
+ const frames = [];
2768
+ for (let i = 0; i < count; i += 1) {
2769
+ const entryOffset = 6 + i * 16;
2770
+ const width = normalizeIcoSize(icoBytes[entryOffset]);
2771
+ const height = normalizeIcoSize(icoBytes[entryOffset + 1]);
2772
+ const bytesInRes = icoBytes.readUInt32LE(entryOffset + 8);
2773
+ const imageOffset = icoBytes.readUInt32LE(entryOffset + 12);
2774
+ if (bytesInRes === 0)
2775
+ continue;
2776
+ if (imageOffset + bytesInRes > icoBytes.length)
2777
+ continue;
2778
+ frames.push({ width, height, bytesInRes, imageOffset });
2779
+ }
2780
+ if (frames.length === 0) {
2781
+ throw new Error("favicon.ico contains no readable image frames.");
2782
+ }
2783
+ frames.sort((a, b) => {
2784
+ const areaDiff = b.width * b.height - a.width * a.height;
2785
+ if (areaDiff !== 0)
2786
+ return areaDiff;
2787
+ return b.bytesInRes - a.bytesInRes;
2788
+ });
2789
+ const pngSignature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
2790
+ for (const frame of frames) {
2791
+ const imageData = icoBytes.subarray(frame.imageOffset, frame.imageOffset + frame.bytesInRes);
2792
+ if (imageData.subarray(0, 8).equals(pngSignature)) {
2793
+ return Buffer.from(imageData);
2794
+ }
2795
+ }
2796
+ return decodeIcoBmpToPng(icoBytes.subarray(frames[0].imageOffset, frames[0].imageOffset + frames[0].bytesInRes));
2797
+ }
2798
+ function renderDesignerPluginQrc() {
2799
+ return `<RCC>
2800
+ <qresource prefix="/anqstdesignerplugin">
2801
+ <file>plugin-icon.png</file>
2802
+ </qresource>
2803
+ </RCC>
2804
+ `;
2805
+ }
2806
+ function installDesignerPluginIconAssets(cwd, pluginDir) {
2807
+ const iconTargetPath = node_path_1.default.join(pluginDir, "plugin-icon.png");
2808
+ const qrcTargetPath = node_path_1.default.join(pluginDir, "designerplugin.qrc");
2809
+ const icoBytes = resolveFaviconIcoBuffer(cwd);
2810
+ if (icoBytes === null) {
2811
+ if (node_fs_1.default.existsSync(iconTargetPath))
2812
+ node_fs_1.default.rmSync(iconTargetPath, { force: true });
2813
+ if (node_fs_1.default.existsSync(qrcTargetPath))
2814
+ node_fs_1.default.rmSync(qrcTargetPath, { force: true });
2815
+ return { hasIcon: false };
2816
+ }
2817
+ const pngBytes = convertIcoToPngBuffer(icoBytes);
2818
+ node_fs_1.default.writeFileSync(iconTargetPath, pngBytes);
2819
+ node_fs_1.default.writeFileSync(qrcTargetPath, renderDesignerPluginQrc(), "utf8");
2820
+ return { hasIcon: true };
2821
+ }
2822
+ function renderQtDesignerPluginCpp(widgetName, widgetCategory, hasIcon) {
2823
+ const pluginClass = `${widgetName}DesignerPlugin`;
2824
+ const widgetClass = `${widgetName}::${widgetName}`;
2825
+ const groupName = escapeCppStringLiteral(widgetCategory);
2826
+ const iconExpression = hasIcon
2827
+ ? 'QIcon(QStringLiteral(":/anqstdesignerplugin/plugin-icon.png"))'
2828
+ : "QIcon()";
2829
+ return `#include <QtUiPlugin/QDesignerCustomWidgetInterface>
2830
+ #include <QIcon>
2831
+ #include <QObject>
2832
+ #include <QString>
2833
+ #include <QWidget>
2834
+ #include "include/${widgetName}.h"
2835
+
2836
+ class ${pluginClass} final : public QObject, public QDesignerCustomWidgetInterface {
2837
+ Q_OBJECT
2838
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")
2839
+ Q_INTERFACES(QDesignerCustomWidgetInterface)
2840
+
2841
+ public:
2842
+ explicit ${pluginClass}(QObject* parent = nullptr) : QObject(parent) {}
2843
+
2844
+ QString name() const override { return QStringLiteral("${widgetClass}"); }
2845
+ QString group() const override { return QStringLiteral("${groupName}"); }
2846
+ QIcon icon() const override { return ${iconExpression}; }
2847
+ QString toolTip() const override { return QStringLiteral("${widgetName} generated by AnQst."); }
2848
+ QString whatsThis() const override { return QStringLiteral("${widgetName} generated by AnQst."); }
2849
+ bool isContainer() const override { return false; }
2850
+ QString includeFile() const override { return QStringLiteral("include/${widgetName}.h"); }
2851
+ QWidget* createWidget(QWidget* parent) override {
2852
+ auto* widget = new ${widgetClass}(parent);
2853
+ widget->setProperty("anqstDesignerContext", true);
2854
+ return widget;
2855
+ }
2856
+ bool isInitialized() const override { return true; }
2857
+ void initialize(QDesignerFormEditorInterface*) override {}
2858
+
2859
+ QString domXml() const override {
2860
+ return QStringLiteral(
2861
+ "<ui language=\\"c++\\">\\n"
2862
+ " <widget class=\\"${widgetClass}\\" name=\\"${widgetName.toLowerCase()}\\">\\n"
2863
+ " </widget>\\n"
2864
+ "</ui>\\n");
2865
+ }
2866
+ };
2867
+
2868
+ #include "${pluginClass}.moc"
2869
+ `;
2870
+ }
2871
+ function renderQtDesignerPluginCMake(widgetName, hasIcon) {
2872
+ const widgetTarget = `${widgetName}Widget`;
2873
+ const pluginTarget = `${widgetName}DesignerPlugin`;
2874
+ const resourceLine = hasIcon ? " \"${CMAKE_CURRENT_LIST_DIR}/designerplugin.qrc\"\n" : "";
2875
+ return `cmake_minimum_required(VERSION 3.21)
2876
+ project(${pluginTarget} LANGUAGES CXX)
2877
+
2878
+ set(CMAKE_CXX_STANDARD 17)
2879
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
2880
+ set(CMAKE_AUTOMOC ON)
2881
+ set(CMAKE_AUTOUIC ON)
2882
+ set(CMAKE_AUTORCC ON)
2883
+
2884
+ set(ANQST_WEBAPP_ROOT "\${CMAKE_CURRENT_LIST_DIR}/../..")
2885
+ set(ANQST_WIDGET_DIR "\${ANQST_WEBAPP_ROOT}/generated_output/${generatedCppLibraryDirName(widgetName)}")
2886
+ set(ANQST_WEBBASE_DIR "" CACHE PATH "Path to AnQstWebBase source directory")
2887
+
2888
+ if(NOT EXISTS "\${ANQST_WIDGET_DIR}/CMakeLists.txt")
2889
+ message(FATAL_ERROR "Missing generated widget CMake project at \${ANQST_WIDGET_DIR}. Run 'anqst build' first.")
2890
+ endif()
2891
+
2892
+ if(NOT ANQST_WEBBASE_DIR)
2893
+ foreach(candidate
2894
+ "\${ANQST_WEBAPP_ROOT}/AnQstWidget/AnQstWebBase"
2895
+ "\${ANQST_WEBAPP_ROOT}/../AnQstWidget/AnQstWebBase"
2896
+ "\${ANQST_WEBAPP_ROOT}/../../AnQstWidget/AnQstWebBase"
2897
+ "\${ANQST_WEBAPP_ROOT}/../../../AnQstWidget/AnQstWebBase")
2898
+ if(EXISTS "\${candidate}/CMakeLists.txt")
2899
+ set(ANQST_WEBBASE_DIR "\${candidate}")
2900
+ break()
2901
+ endif()
2902
+ endforeach()
2903
+ endif()
2904
+
2905
+ if(NOT ANQST_WEBBASE_DIR OR NOT EXISTS "\${ANQST_WEBBASE_DIR}/CMakeLists.txt")
2906
+ message(FATAL_ERROR "Unable to locate AnQstWebBase sources. Set -DANQST_WEBBASE_DIR=<path/to/AnQstWidget/AnQstWebBase>.")
2907
+ endif()
2908
+
2909
+ find_package(Qt5 REQUIRED COMPONENTS Core Widgets UiPlugin)
2910
+
2911
+ set(ANQSTWEBBASE_BUILD_TESTS OFF CACHE BOOL "Build AnQstWebBase unit tests" FORCE)
2912
+ if(NOT TARGET anqstwebhostbase)
2913
+ add_subdirectory("\${ANQST_WEBBASE_DIR}" "\${CMAKE_CURRENT_BINARY_DIR}/anqstwebbase")
2914
+ endif()
2915
+
2916
+ if(NOT TARGET ${widgetTarget})
2917
+ add_subdirectory("\${ANQST_WIDGET_DIR}" "\${CMAKE_CURRENT_BINARY_DIR}/generated-widget")
2918
+ endif()
2919
+
2920
+ add_library(${pluginTarget} MODULE
2921
+ "\${CMAKE_CURRENT_LIST_DIR}/${pluginTarget}.cpp"
2922
+ ${resourceLine})
2923
+ target_include_directories(${pluginTarget}
2924
+ PRIVATE
2925
+ "\${ANQST_WIDGET_DIR}"
2926
+ "\${ANQST_WIDGET_DIR}/include"
2927
+ )
2928
+ target_link_libraries(${pluginTarget}
2929
+ PRIVATE
2930
+ ${widgetTarget}
2931
+ Qt5::Core
2932
+ Qt5::Widgets
2933
+ Qt5::UiPlugin
2934
+ )
2935
+ set_target_properties(${pluginTarget} PROPERTIES
2936
+ PREFIX ""
2937
+ )
2938
+ `;
2939
+ }
2940
+ function installQtDesignerPluginCMake(cwd, widgetName, options = {}) {
2941
+ const pluginDir = node_path_1.default.join(cwd, "anqst-cmake", "designerplugin");
2942
+ node_fs_1.default.mkdirSync(pluginDir, { recursive: true });
2943
+ const assets = installDesignerPluginIconAssets(cwd, pluginDir);
2944
+ const pluginTarget = `${widgetName}DesignerPlugin`;
2945
+ const widgetCategory = options.widgetCategory ?? "AnQst Widgets";
2946
+ node_fs_1.default.writeFileSync(node_path_1.default.join(pluginDir, "CMakeLists.txt"), withBuildStamp("anqst-cmake/designerplugin/CMakeLists.txt", renderQtDesignerPluginCMake(widgetName, assets.hasIcon)), "utf8");
2947
+ node_fs_1.default.writeFileSync(node_path_1.default.join(pluginDir, `${pluginTarget}.cpp`), withBuildStamp(`anqst-cmake/designerplugin/${pluginTarget}.cpp`, renderQtDesignerPluginCpp(widgetName, widgetCategory, assets.hasIcon)), "utf8");
2500
2948
  }