Files
leocad/common/lc_previewwidget.cpp
T
Leonardo Zide 3ec8b274b1 Unify asset loading priorities and clarify wait APIs
Replace load flags and the loader's separate priority enum with Low, Normal, and Blocking priorities. Blocking loads wait for completion; asynchronous loads and internal scheduling retain promotion-only behavior.

Acquire consumer references in the library, reuse loader loading for stud-style rebuilds, and remove QueuePiece, single-piece readiness wrappers, and the library texture readiness wrapper. Rename scoped readiness APIs to WaitFor functions and document their scheduling and lifetime contracts.

Validated with a Debug build and a temporary lifecycle harness covering priority promotion, blocking loads, reference cleanup, selective style reloads, cancellation, pause/resume, and shutdown.
2026-10-13 23:27:29 -07:00

386 lines
8.8 KiB
C++

#include "lc_global.h"
#include "lc_previewwidget.h"
#include "pieceinf.h"
#include "piece.h"
#include "project.h"
#include "lc_model.h"
#include "lc_library.h"
#include "lc_application.h"
#include "lc_viewwidget.h"
#include "lc_view.h"
lcPreviewDockWidget::lcPreviewDockWidget(QMainWindow* Parent)
: QMainWindow(Parent)
{
mPreview = new lcPreview();
mViewWidget = new lcViewWidget(nullptr, mPreview);
setCentralWidget(mViewWidget);
setMinimumSize(200, 200);
mLockAction = new QAction(QIcon(":/resources/action_preview_unlocked.png"),tr("Lock Preview"), this);
mLockAction->setCheckable(true);
mLockAction->setChecked(false);
mLockAction->setShortcut(tr("Ctrl+L"));
connect(mLockAction, &QAction::triggered, this, &lcPreviewDockWidget::SetPreviewLock);
SetPreviewLock();
mLabel = new QLabel();
lcPiecesLibrary* Library = lcGetPiecesLibrary();
connect(Library, &lcPiecesLibrary::PartLoaded, this, [this](PieceInfo*)
{
if (mAssetRefreshPending)
RefreshPendingAssets();
});
connect(Library, &lcPiecesLibrary::PartLoadFailed, this, [this](PieceInfo*, const QString&)
{
if (mAssetRefreshPending)
RefreshPendingAssets();
});
connect(Library, &lcPiecesLibrary::GeneratedMeshSettled, this, [this](PieceInfo*)
{
if (mAssetRefreshPending)
RefreshPendingAssets();
});
mToolBar = addToolBar(tr("Toolbar"));
mToolBar->setObjectName("Toolbar");
mToolBar->setStatusTip(tr("Preview Toolbar"));
mToolBar->setMovable(false);
mToolBar->addAction(mLockAction);
mToolBar->addSeparator();
mToolBar->addWidget(mLabel);
if (mToolBar->isHidden())
mToolBar->show();
}
bool lcPreviewDockWidget::SetCurrentPiece(PieceInfo* Info, int ColorCode)
{
if (mLockAction->isChecked())
return true;
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
mLabel->setText(tr("Loading..."));
if (mPreview->SetCurrentPiece(Info, ColorCode))
{
RefreshPendingAssets();
return true;
}
mViewWidget->show();
mLabel->setText(tr("Preview unavailable"));
return false;
}
void lcPreviewDockWidget::UpdatePreview()
{
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
mPreview->UpdatePreview();
RefreshPendingAssets();
}
void lcPreviewDockWidget::RefreshDescription()
{
mPreview->RefreshDescription();
mLabel->setText(mPreview->GetDescription());
}
void lcPreviewDockWidget::RefreshModel(const lcModel* Model)
{
if (mLockAction->isChecked() || mRefreshingModel)
return;
const std::vector<PieceInfo*> Required = mPreview->GetModel()->GetRequiredPieces();
if (std::find(Required.begin(), Required.end(), Model->GetPieceInfo()) == Required.end())
return;
mRefreshingModel = true;
if (!mAssetRefreshPending)
mRefitAfterAssetLoad = false;
lcPiecesLibrary* Library = lcGetPiecesLibrary();
for (PieceInfo* Info : Required)
{
if (Info->mState != lcPieceInfoState::Unloaded)
continue;
Library->LoadPieceInfo(Info, lcPieceLoadPriority::Normal);
Library->ReleasePieceInfo(Info);
}
std::vector<lcModel*> UpdatedModels;
mPreview->GetModel()->UpdatePieceInfo(UpdatedModels);
RefreshPendingAssets();
mRefreshingModel = false;
}
void lcPreviewDockWidget::RefreshAfterAssetChange(const std::vector<PieceInfo*>& ChangedPieces)
{
if (mPreview->GetModel()->GetPieces().empty())
return;
const std::vector<PieceInfo*> Required = mPreview->GetModel()->GetRequiredPieces();
bool Affected = std::any_of(Required.begin(), Required.end(), [&ChangedPieces](PieceInfo* Info)
{
return std::find(ChangedPieces.begin(), ChangedPieces.end(), Info) != ChangedPieces.end();
});
if (!Affected)
{
for (const std::unique_ptr<lcPiece>& Piece : mPreview->GetModel()->GetPieces())
{
if (std::find(ChangedPieces.begin(), ChangedPieces.end(), Piece->mPieceInfo) != ChangedPieces.end())
{
Affected = true;
break;
}
}
}
if (!Affected)
return;
RefreshPendingAssets();
}
void lcPreviewDockWidget::RefreshPendingAssets()
{
const std::vector<PieceInfo*> Required = mPreview->GetModel()->GetRequiredPieces();
for (const PieceInfo* Info : Required)
{
if (Info->mState == lcPieceInfoState::Failed || Info->mState == lcPieceInfoState::Cancelled)
{
mAssetRefreshPending = false;
mViewWidget->hide();
mLabel->setText(tr("Preview unavailable"));
return;
}
if (Info->mState != lcPieceInfoState::Loaded)
{
mAssetRefreshPending = true;
mViewWidget->hide();
mLabel->setText(tr("Loading..."));
return;
}
}
for (const lcPiece* Piece : mPreview->GetModel()->GetRequiredSynthPieces())
{
if (Piece->IsGeneratedMeshPending())
{
mAssetRefreshPending = true;
mViewWidget->hide();
mLabel->setText(tr("Loading..."));
return;
}
if (!Piece->HasGeneratedMesh())
{
mAssetRefreshPending = false;
mViewWidget->hide();
mLabel->setText(tr("Preview unavailable"));
return;
}
}
if (mAssetRefreshPending && mRefitAfterAssetLoad)
mPreview->ZoomExtents(lcGeometryBoundsMode::IncludeFallback, false);
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
mViewWidget->show();
mPreview->RefreshDescription();
mPreview->Redraw();
mLabel->setText(mPreview->GetDescription());
}
void lcPreviewDockWidget::RebindPieceInfo(PieceInfo* Previous, PieceInfo* Replacement)
{
int ColorCode = -1;
for (const std::unique_ptr<lcPiece>& Piece : mPreview->GetModel()->GetPieces())
{
if (Piece->mPieceInfo == Previous)
{
ColorCode = Piece->GetColorCode();
break;
}
}
if (ColorCode == -1)
return;
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
if (mPreview->SetCurrentPiece(Replacement, ColorCode))
RefreshPendingAssets();
else
{
mViewWidget->show();
mLabel->setText(tr("Preview unavailable"));
}
}
void lcPreviewDockWidget::ClearPreview()
{
mAssetRefreshPending = false;
mRefitAfterAssetLoad = true;
mViewWidget->show();
if (mPreview->GetModel()->GetPieces().size())
mPreview->ClearPreview();
mLabel->setText(QString());
}
void lcPreviewDockWidget::SetPreviewLock()
{
bool Locked = mLockAction->isChecked();
if (Locked && mPreview->GetModel()->GetPieces().empty())
{
mLockAction->setChecked(false);
return;
}
QIcon LockIcon(Locked ? ":/resources/action_preview_locked.png" : ":/resources/action_preview_unlocked.png");
QString StatusTip(Locked
? tr("Unlock the preview display to enable updates")
: tr("Lock the preview display to disable updates"));
mLockAction->setToolTip(Locked ? tr("Unlock Preview") : tr("Lock Preview"));
mLockAction->setIcon(LockIcon);
mLockAction->setStatusTip(StatusTip);
}
lcPreview::lcPreview()
: lcView(lcViewType::Preview, nullptr), mLoader(new Project(true))
{
mLoader->SetActiveModel(0, false);
mModel = mLoader->GetActiveModel();
}
bool lcPreview::SetCurrentPiece(PieceInfo* Info, int ColorCode)
{
if (!Info)
{
ClearPreview();
return false;
}
for (const std::unique_ptr<lcPiece>& ModelPiece : mModel->GetPieces())
{
if (Info == ModelPiece->mPieceInfo)
{
int ModelColorCode = ModelPiece->GetColorCode();
if (ModelColorCode == ColorCode)
{
RefreshDescription();
return true;
}
}
}
lcPiecesLibrary* Library = lcGetPiecesLibrary();
bool Ready = Library->LoadPieceInfo(Info, lcPieceLoadPriority::Blocking);
if (Ready && Info->IsModel())
Ready = static_cast<bool>(Info->GetModel()->EnsureAssetsReady());
else if (Ready && Info->IsProject())
Ready = static_cast<bool>(Info->GetProject()->EnsureAssetsReady());
if (!Ready)
{
Library->ReleasePieceInfo(Info);
ClearPreview();
return false;
}
mIsModel = Info->IsModel();
mDescription = Info->m_strDescription;
mModel->SelectAllPiecesAction();
mModel->DeleteSelectedObjects();
mModel->SetPreviewPieceInfo(Info, lcGetColorIndex(ColorCode));
std::vector<lcModel*> UpdatedModels;
mModel->UpdatePieceInfo(UpdatedModels);
Library->ReleasePieceInfo(Info);
ZoomExtents(lcGeometryBoundsMode::IncludeFallback, true);
return true;
}
void lcPreview::ClearPreview()
{
mDescription.clear();
mIsModel = false;
mLoader = std::unique_ptr<Project>(new Project(true/*IsPreview*/));
mLoader->SetActiveModel(0, false);
mModel = mLoader->GetActiveModel();
lcPiecesLibrary* Library = lcGetPiecesLibrary();
Library->RemoveTemporaryPieces();
Library->UnloadUnusedParts();
Redraw();
}
void lcPreview::UpdatePreview()
{
PieceInfo* Info = nullptr;
int ColorCode = -1;
for (const std::unique_ptr<lcPiece>& ModelPiece : mModel->GetPieces())
{
if (ModelPiece->mPieceInfo)
{
Info = ModelPiece->mPieceInfo;
ColorCode = ModelPiece->GetColorCode();
break;
}
}
lcPiecesLibrary* Library = lcGetPiecesLibrary();
if (Info)
Library->AddPieceReference(Info);
ClearPreview();
if (Info && ColorCode > -1)
SetCurrentPiece(Info, ColorCode);
if (Info)
Library->ReleasePieceInfo(Info);
}
void lcPreview::RefreshDescription()
{
for (const std::unique_ptr<lcPiece>& Piece : mModel->GetPieces())
{
if (!Piece->mPieceInfo)
continue;
mDescription = QString::fromLatin1(Piece->mPieceInfo->m_strDescription);
return;
}
mDescription.clear();
}