新增windows支持
This commit is contained in:
@@ -76,19 +76,27 @@ if(DTS_BUILD_GUI)
|
|||||||
if(DTS_BUILD_RUNTIME_PLUGINS)
|
if(DTS_BUILD_RUNTIME_PLUGINS)
|
||||||
add_dependencies(DateToSpine
|
add_dependencies(DateToSpine
|
||||||
dts_runtime_38 dts_runtime_40 dts_runtime_41 dts_runtime_42 dts_runtime_43)
|
dts_runtime_38 dts_runtime_40 dts_runtime_41 dts_runtime_42 dts_runtime_43)
|
||||||
|
# Runtime plugin destination differs by platform:
|
||||||
|
# - macOS: inside the .app bundle (Contents/PlugIns/runtimes)
|
||||||
|
# - Windows/other: alongside the executable
|
||||||
|
if(APPLE)
|
||||||
|
set(DTS_RUNTIME_PLUGIN_DIR "$<TARGET_BUNDLE_DIR:DateToSpine>/Contents/PlugIns/runtimes")
|
||||||
|
else()
|
||||||
|
set(DTS_RUNTIME_PLUGIN_DIR "$<TARGET_FILE_DIR:DateToSpine>/runtimes")
|
||||||
|
endif()
|
||||||
foreach(runtime_line IN ITEMS 38 40 41 42 43)
|
foreach(runtime_line IN ITEMS 38 40 41 42 43)
|
||||||
add_custom_command(TARGET dts_runtime_${runtime_line} POST_BUILD
|
add_custom_command(TARGET dts_runtime_${runtime_line} POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
"$<TARGET_BUNDLE_DIR:DateToSpine>/Contents/PlugIns/runtimes"
|
"${DTS_RUNTIME_PLUGIN_DIR}"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
"$<TARGET_FILE:dts_runtime_${runtime_line}>"
|
"$<TARGET_FILE:dts_runtime_${runtime_line}>"
|
||||||
"$<TARGET_BUNDLE_DIR:DateToSpine>/Contents/PlugIns/runtimes/")
|
"${DTS_RUNTIME_PLUGIN_DIR}/")
|
||||||
add_custom_command(TARGET DateToSpine POST_BUILD
|
add_custom_command(TARGET DateToSpine POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||||
"$<TARGET_BUNDLE_DIR:DateToSpine>/Contents/PlugIns/runtimes"
|
"${DTS_RUNTIME_PLUGIN_DIR}"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
"$<TARGET_FILE:dts_runtime_${runtime_line}>"
|
"$<TARGET_FILE:dts_runtime_${runtime_line}>"
|
||||||
"$<TARGET_BUNDLE_DIR:DateToSpine>/Contents/PlugIns/runtimes/")
|
"${DTS_RUNTIME_PLUGIN_DIR}/")
|
||||||
endforeach()
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -39,9 +39,11 @@ EditorProbeResult SpineOutputParser::parse(
|
|||||||
static const std::regex launcher(R"(Spine Launcher\s+(\d+\.\d+(?:\.\d+)?))");
|
static const std::regex launcher(R"(Spine Launcher\s+(\d+\.\d+(?:\.\d+)?))");
|
||||||
static const std::regex launching(
|
static const std::regex launching(
|
||||||
R"((?:Launching:|Starting:)\s*Spine\s+(\d+\.\d+(?:\.\d+)?)(?:\s+([^\r\n]+))?)");
|
R"((?:Launching:|Starting:)\s*Spine\s+(\d+\.\d+(?:\.\d+)?)(?:\s+([^\r\n]+))?)");
|
||||||
|
// MSVC's std::regex does not implement std::regex_constants::multiline,
|
||||||
|
// so instead of relying on the '^' line anchor with the multiline flag,
|
||||||
|
// search for "Spine <version>" anywhere (regex_search scans the whole text).
|
||||||
static const std::regex directEditor(
|
static const std::regex directEditor(
|
||||||
R"(^Spine\s+(\d+\.\d+(?:\.\d+)?)(?:\s+([^\r\n]+))?)",
|
R"(Spine\s+(\d+\.\d+(?:\.\d+)?)(?:\s+([^\r\n]+))?)");
|
||||||
std::regex_constants::multiline);
|
|
||||||
|
|
||||||
result.launcherVersion = firstVersion(process.output, launcher);
|
result.launcherVersion = firstVersion(process.output, launcher);
|
||||||
result.editorVersion = firstVersion(process.output, launching, &result.edition);
|
result.editorVersion = firstVersion(process.output, launching, &result.edition);
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ std::vector<fs::path> SpineVersionAvailability::defaultCacheRoots() const {
|
|||||||
roots.emplace_back(fs::path(roaming) / "Spine/updates");
|
roots.emplace_back(fs::path(roaming) / "Spine/updates");
|
||||||
if (const char* local = std::getenv("LOCALAPPDATA"))
|
if (const char* local = std::getenv("LOCALAPPDATA"))
|
||||||
roots.emplace_back(fs::path(local) / "Spine/updates");
|
roots.emplace_back(fs::path(local) / "Spine/updates");
|
||||||
|
// Spine Launcher also keeps its update cache directly under the user's
|
||||||
|
// profile directory (e.g. C:\Users\<name>\Spine\updates).
|
||||||
|
if (const char* userProfile = std::getenv("USERPROFILE"))
|
||||||
|
roots.emplace_back(fs::path(userProfile) / "Spine/updates");
|
||||||
#endif
|
#endif
|
||||||
return roots;
|
return roots;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
#include "atlas/AtlasParser.hpp"
|
#include "atlas/AtlasParser.hpp"
|
||||||
#include "conversion/ConversionJob.hpp"
|
#include "conversion/ConversionJob.hpp"
|
||||||
#include "editor/SpineEditorLocator.hpp"
|
#include "editor/SpineEditorLocator.hpp"
|
||||||
|
#include "editor/SpineProbe.hpp"
|
||||||
#include "editor/SpineVersionAvailability.hpp"
|
#include "editor/SpineVersionAvailability.hpp"
|
||||||
|
#include "platform/ProcessRunner.hpp"
|
||||||
#include "scan/SpineDataVersionDetector.hpp"
|
#include "scan/SpineDataVersionDetector.hpp"
|
||||||
#include "ui/SpinePreviewWidget.hpp"
|
#include "ui/SpinePreviewWidget.hpp"
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
@@ -134,38 +137,69 @@ void MainWindow::setupUi() {
|
|||||||
connect(convertButton_, &QPushButton::clicked, this, &MainWindow::convertCurrent);
|
connect(convertButton_, &QPushButton::clicked, this, &MainWindow::convertCurrent);
|
||||||
previewTimer_ = new QTimer(this);
|
previewTimer_ = new QTimer(this);
|
||||||
previewTimer_->setInterval(16);
|
previewTimer_->setInterval(16);
|
||||||
|
previewTimer_->setTimerType(Qt::PreciseTimer);
|
||||||
connect(previewTimer_, &QTimer::timeout, this, &MainWindow::advancePreview);
|
connect(previewTimer_, &QTimer::timeout, this, &MainWindow::advancePreview);
|
||||||
|
|
||||||
|
editorProbeWatcher_ = new QFutureWatcher<QPair<int, QString>>(this);
|
||||||
|
connect(editorProbeWatcher_,
|
||||||
|
&QFutureWatcher<QPair<int, QString>>::finished,
|
||||||
|
this, &MainWindow::onEditorProbeFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::showEmptyState() {
|
void MainWindow::showEmptyState() {
|
||||||
previewWidget_->resetViewport();
|
previewWidget_->resetViewport();
|
||||||
previewWidget_->setMessage(
|
previewWidget_->setMessage(
|
||||||
tr("把 .json / .skel、.atlas 和图片拖到这里\n\n也可以拖入整个资源文件夹"));
|
tr("把 .json / .skel / .skel.bytes、.atlas / .atlas.txt 和图片拖到这里\n\n也可以拖入整个资源文件夹"));
|
||||||
statusLabel_->clear();
|
statusLabel_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::chooseResources() {
|
void MainWindow::chooseResources() {
|
||||||
const auto paths = QFileDialog::getOpenFileNames(this, tr("选择导出资源"), {},
|
const auto paths = QFileDialog::getOpenFileNames(this, tr("选择导出资源"), {},
|
||||||
tr("Spine 资源 (*.json *.skel *.atlas *.png *.jpg *.jpeg *.webp);;所有文件 (*)"));
|
tr("Spine 资源 (*.json *.skel *.skel.bytes *.atlas *.atlas.txt *.png *.jpg *.jpeg *.webp);;所有文件 (*)"));
|
||||||
for (const auto& path : paths) addInput(path);
|
for (const auto& path : paths) addInput(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::addInput(const QString& path) {
|
void MainWindow::addInput(const QString& path) {
|
||||||
const QFileInfo info(path);
|
const QFileInfo info(path);
|
||||||
const auto scanDirectory = [this](const QString& directory, bool recursive) {
|
const auto scanDirectory = [this](const QString& directory, bool recursive, bool warnUnsupported) {
|
||||||
QDirIterator iterator(directory, {"*.json", "*.skel"}, QDir::Files,
|
QDirIterator iterator(directory, {"*.json", "*.skel", "*.skel.bytes"}, QDir::Files,
|
||||||
recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
|
recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags);
|
||||||
while (iterator.hasNext()) addSkeleton(iterator.next());
|
while (iterator.hasNext()) addSkeleton(iterator.next(), warnUnsupported);
|
||||||
};
|
};
|
||||||
if (info.isDir()) scanDirectory(path, true);
|
// Only the primary skeleton data (.json / .skel / .skel.bytes) carries a
|
||||||
|
// Spine version and should trigger the "unsupported version" warning.
|
||||||
|
// Companion files (.atlas / .atlas.txt / images) have no version of their
|
||||||
|
// own, so they must not cause a version check when scanned for a skeleton.
|
||||||
|
if (info.isDir()) scanDirectory(path, true, true);
|
||||||
else if (info.suffix().compare("json", Qt::CaseInsensitive) == 0
|
else if (info.suffix().compare("json", Qt::CaseInsensitive) == 0
|
||||||
|| info.suffix().compare("skel", Qt::CaseInsensitive) == 0) addSkeleton(path);
|
|| info.suffix().compare("skel", Qt::CaseInsensitive) == 0
|
||||||
else scanDirectory(info.absolutePath(), false);
|
|| info.completeSuffix().compare("skel.bytes", Qt::CaseInsensitive) == 0) addSkeleton(path, true);
|
||||||
|
else scanDirectory(info.absolutePath(), false, false);
|
||||||
scheduleAvailabilityCheck();
|
scheduleAvailabilityCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::addSkeleton(const QString& path) {
|
void MainWindow::addSkeleton(const QString& path, bool warnInvalid) {
|
||||||
const auto result = SpineDataVersionDetector{}.inspect(localPath(path));
|
const auto result = SpineDataVersionDetector{}.inspect(localPath(path));
|
||||||
|
if (result.status == VersionDetectionStatus::UnsupportedVersion) {
|
||||||
|
if (warnInvalid) {
|
||||||
|
const auto version = result.version
|
||||||
|
? QString::fromStdString(result.version->toString()) : tr("未知");
|
||||||
|
pendingVersionWarnings_.append(
|
||||||
|
tr("%1(版本 %2)").arg(QFileInfo(path).fileName(), version));
|
||||||
|
// Defer to the event loop so multiple unsupported files in a single
|
||||||
|
// import are collected and reported with one dialog.
|
||||||
|
QTimer::singleShot(0, this, &MainWindow::showUnsupportedVersionWarning);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result.status == VersionDetectionStatus::InvalidData
|
||||||
|
|| result.status == VersionDetectionStatus::FileMissing) {
|
||||||
|
if (warnInvalid) {
|
||||||
|
pendingInvalidAssets_.append(QFileInfo(path).fileName());
|
||||||
|
QTimer::singleShot(0, this, &MainWindow::showInvalidAssetWarning);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (result.status != VersionDetectionStatus::Detected || skeletonPaths_.contains(path)) return;
|
if (result.status != VersionDetectionStatus::Detected || skeletonPaths_.contains(path)) return;
|
||||||
skeletonPaths_.append(path);
|
skeletonPaths_.append(path);
|
||||||
resourceCombo_->addItem(QFileInfo(path).completeBaseName(), path);
|
resourceCombo_->addItem(QFileInfo(path).completeBaseName(), path);
|
||||||
@@ -173,6 +207,32 @@ void MainWindow::addSkeleton(const QString& path) {
|
|||||||
resourceCombo_->setCurrentIndex(resourceCombo_->count() - 1);
|
resourceCombo_->setCurrentIndex(resourceCombo_->count() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::showUnsupportedVersionWarning() {
|
||||||
|
if (pendingVersionWarnings_.isEmpty()) return;
|
||||||
|
// Deduplicate so the same resource reported multiple times (e.g. repeated
|
||||||
|
// drag-and-drop events) is only listed once.
|
||||||
|
QStringList unique;
|
||||||
|
for (const auto& item : pendingVersionWarnings_)
|
||||||
|
if (!unique.contains(item)) unique.append(item);
|
||||||
|
pendingVersionWarnings_.clear();
|
||||||
|
const auto details = unique.join(QLatin1String("\n"));
|
||||||
|
QMessageBox::warning(this, tr("版本过低"),
|
||||||
|
tr("以下资源的 Spine 版本不在支持的范围内(支持的版本:3.8、4.0、4.1、4.2、4.3):\n\n%1")
|
||||||
|
.arg(details));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showInvalidAssetWarning() {
|
||||||
|
if (pendingInvalidAssets_.isEmpty()) return;
|
||||||
|
// Deduplicate so the same file reported multiple times is only listed once.
|
||||||
|
QStringList unique;
|
||||||
|
for (const auto& item : pendingInvalidAssets_)
|
||||||
|
if (!unique.contains(item)) unique.append(item);
|
||||||
|
pendingInvalidAssets_.clear();
|
||||||
|
const auto details = unique.join(QLatin1String("\n"));
|
||||||
|
QMessageBox::warning(this, tr("未识别到正确的 Spine 资产"),
|
||||||
|
tr("以下文件未能识别为有效的 Spine 资产:\n\n%1").arg(details));
|
||||||
|
}
|
||||||
|
|
||||||
QString MainWindow::findAtlas(const QString& skeletonPath) const {
|
QString MainWindow::findAtlas(const QString& skeletonPath) const {
|
||||||
const QFileInfo skeleton(skeletonPath);
|
const QFileInfo skeleton(skeletonPath);
|
||||||
QDir directory(skeleton.absolutePath());
|
QDir directory(skeleton.absolutePath());
|
||||||
@@ -256,13 +316,18 @@ void MainWindow::animationChanged(const QString& name) {
|
|||||||
statusLabel_->setText(QString::fromStdString(error));
|
statusLabel_->setText(QString::fromStdString(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
previewClock_.restart();
|
||||||
advancePreview();
|
advancePreview();
|
||||||
previewTimer_->start();
|
previewTimer_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::advancePreview() {
|
void MainWindow::advancePreview() {
|
||||||
std::string error;
|
std::string error;
|
||||||
auto frame = runtime_.updatePreview(0.016F, error);
|
// Use the real elapsed wall-clock time as the frame delta so the animation
|
||||||
|
// matches the actual playback speed even if the timer interval drifts
|
||||||
|
// (Windows timers are not exact). Falls back to 16ms on the first frame.
|
||||||
|
const float delta = previewClock_.isValid() ? previewClock_.restart() / 1000.0F : 0.016F;
|
||||||
|
auto frame = runtime_.updatePreview(delta, error);
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
previewTimer_->stop();
|
previewTimer_->stop();
|
||||||
previewWidget_->setMessage(tr("动画预览暂时不可用"));
|
previewWidget_->setMessage(tr("动画预览暂时不可用"));
|
||||||
@@ -351,9 +416,13 @@ void MainWindow::refreshAvailability() {
|
|||||||
}
|
}
|
||||||
currentEditorVersion_ = availableVersions_.value(line);
|
currentEditorVersion_ = availableVersions_.value(line);
|
||||||
if (currentEditorVersion_.isEmpty()) {
|
if (currentEditorVersion_.isEmpty()) {
|
||||||
showAvailability(tr("缺少 Spine %1").arg(line), QStringLiteral("#ef6b73"));
|
// Run the version check (cache scan + editor probe) in the background
|
||||||
convertButton_->setText(tr("指定 Spine %1 路径").arg(line));
|
// (bounded by the probe timeout) so the UI does not block. If no
|
||||||
convertButton_->setEnabled(true);
|
// matching version is found, the user is warned with a dialog.
|
||||||
|
showAvailability(tr("正在检查 Editor ..."), QStringLiteral("#f2b84b"));
|
||||||
|
convertButton_->setText(tr("检查中..."));
|
||||||
|
convertButton_->setEnabled(false);
|
||||||
|
startEditorProbe(line);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showAvailability(tr("可转换 · Spine %1").arg(currentEditorVersion_),
|
showAvailability(tr("可转换 · Spine %1").arg(currentEditorVersion_),
|
||||||
@@ -362,6 +431,47 @@ void MainWindow::refreshAvailability() {
|
|||||||
convertButton_->setEnabled(true);
|
convertButton_->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::startEditorProbe(const QString& line) {
|
||||||
|
const std::string lineStr = line.toStdString();
|
||||||
|
const auto editorPath = localPath(editorPath_);
|
||||||
|
const int generation = ++editorProbeGeneration_;
|
||||||
|
auto future = QtConcurrent::run([lineStr, editorPath, generation]() -> QPair<int, QString> {
|
||||||
|
// Scan the Spine update cache first; if no matching release is cached,
|
||||||
|
// probe the explicitly selected Editor executable (bounded timeout).
|
||||||
|
SpineVersionAvailability availability;
|
||||||
|
if (const auto cached = availability.newestForLine(lineStr))
|
||||||
|
return {generation, QString::fromStdString(cached->toString())};
|
||||||
|
const auto runner = createDefaultProcessRunner();
|
||||||
|
const SpineProbe probe(*runner);
|
||||||
|
const auto result = probe.inspect(editorPath, std::chrono::seconds(3));
|
||||||
|
if (result.editorVersion && result.editorVersion->line() == lineStr)
|
||||||
|
return {generation, QString::fromStdString(result.editorVersion->toString())};
|
||||||
|
return {generation, QString{}};
|
||||||
|
});
|
||||||
|
editorProbeWatcher_->setFuture(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onEditorProbeFinished() {
|
||||||
|
if (!editorProbeWatcher_ || !editorProbeWatcher_->isFinished()) return;
|
||||||
|
const auto outcome = editorProbeWatcher_->result();
|
||||||
|
if (outcome.first != editorProbeGeneration_) return;
|
||||||
|
const QString version = outcome.second;
|
||||||
|
if (version.isEmpty()) {
|
||||||
|
showAvailability(tr("未找到对应版本"), QStringLiteral("#ef6b73"));
|
||||||
|
convertButton_->setText(tr("指定 Spine 路径"));
|
||||||
|
convertButton_->setEnabled(true);
|
||||||
|
// Warn the user one second after the scan when no matching version is found.
|
||||||
|
QTimer::singleShot(1000, this, [this] {
|
||||||
|
QMessageBox::warning(this, tr("未找到对应版本"), tr("未找到对应版本"));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
currentEditorVersion_ = version;
|
||||||
|
showAvailability(tr("可转换 · Spine %1").arg(version), QStringLiteral("#55d68b"));
|
||||||
|
convertButton_->setText(tr("一键转换为 .spine"));
|
||||||
|
convertButton_->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool MainWindow::chooseEditorPath() {
|
bool MainWindow::chooseEditorPath() {
|
||||||
const auto selected = QFileDialog::getOpenFileName(this,
|
const auto selected = QFileDialog::getOpenFileName(this,
|
||||||
tr("指定 Spine 程序"), {}, tr("所有文件 (*)"));
|
tr("指定 Spine 程序"), {}, tr("所有文件 (*)"));
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QCheckBox;
|
class QCheckBox;
|
||||||
@@ -36,11 +39,12 @@ private slots:
|
|||||||
const QString& error);
|
const QString& error);
|
||||||
void animationChanged(const QString& name);
|
void animationChanged(const QString& name);
|
||||||
void advancePreview();
|
void advancePreview();
|
||||||
|
void onEditorProbeFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUi();
|
void setupUi();
|
||||||
void addInput(const QString& path);
|
void addInput(const QString& path);
|
||||||
void addSkeleton(const QString& path);
|
void addSkeleton(const QString& path, bool warnInvalid = true);
|
||||||
void loadCurrentPreview();
|
void loadCurrentPreview();
|
||||||
QString findAtlas(const QString& skeletonPath) const;
|
QString findAtlas(const QString& skeletonPath) const;
|
||||||
QString runtimeLibrary(const QString& versionLine) const;
|
QString runtimeLibrary(const QString& versionLine) const;
|
||||||
@@ -51,6 +55,9 @@ private:
|
|||||||
void showAvailability(const QString& text, const QString& color);
|
void showAvailability(const QString& text, const QString& color);
|
||||||
void showEmptyState();
|
void showEmptyState();
|
||||||
void setBusy(bool busy);
|
void setBusy(bool busy);
|
||||||
|
void startEditorProbe(const QString& line);
|
||||||
|
void showUnsupportedVersionWarning();
|
||||||
|
void showInvalidAssetWarning();
|
||||||
|
|
||||||
QComboBox* resourceCombo_ = nullptr;
|
QComboBox* resourceCombo_ = nullptr;
|
||||||
QComboBox* animationCombo_ = nullptr;
|
QComboBox* animationCombo_ = nullptr;
|
||||||
@@ -68,6 +75,11 @@ private:
|
|||||||
RuntimePlugin runtime_;
|
RuntimePlugin runtime_;
|
||||||
ConversionJob* conversionJob_ = nullptr;
|
ConversionJob* conversionJob_ = nullptr;
|
||||||
QTimer* previewTimer_ = nullptr;
|
QTimer* previewTimer_ = nullptr;
|
||||||
|
QElapsedTimer previewClock_;
|
||||||
|
QFutureWatcher<QPair<int, QString>>* editorProbeWatcher_ = nullptr;
|
||||||
|
int editorProbeGeneration_ = 0;
|
||||||
|
QStringList pendingVersionWarnings_;
|
||||||
|
QStringList pendingInvalidAssets_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dts
|
} // namespace dts
|
||||||
|
|||||||
Reference in New Issue
Block a user