Compare commits

...

29 Commits

Author SHA1 Message Date
Roman Telezhynskyi 7286516cdc Fix build on Linux. 2024-04-13 12:37:11 +03:00
Roman Telezhynskyi e154ba7440 Support for OneDrive on Windows.
Move settings, "Svg fonts", "Font corrections" and "Known measurements" folders to user Documents. This will map to OneDrive's documents folder if OneDrive activated.
2024-04-13 12:34:09 +03:00
Roman Telezhynskyi b4b26b115b Fix path to Crashpad data on Windows. 2024-04-13 12:31:19 +03:00
Roman Telezhynskyi 08c4d1e411 Fix path to logs on Windows. 2024-04-13 12:30:43 +03:00
Roman Telezhynskyi 7378cfbe95 Fix piece rotation with enabled Follow grainline. 2024-04-12 17:37:13 +03:00
Roman Telezhynskyi e4481754f0 Fix calculating label position for flipped piece. 2024-04-12 09:51:49 +03:00
Roman Telezhynskyi 55cc3a7d54 Fix opening a layout file when current file is not empty. 2024-04-11 20:17:06 +03:00
Roman Telezhynskyi 39cdbd1c70 Show piece copy number. 2024-04-11 20:15:58 +03:00
Roman Telezhynskyi 14e542b412 Optimize calling position validations. 2024-04-11 18:38:43 +03:00
Roman Telezhynskyi b4c009502b Log file on Windows is empty in release mode.
Disable only Qt's debug logging.
2024-04-11 10:49:43 +03:00
Roman Telezhynskyi bc31838319 Create sym files with includes. 2024-04-11 10:48:46 +03:00
Roman Telezhynskyi 8f5c5b8cae Declutter pattern by increasing transparency of label line. 2024-04-09 20:46:37 +03:00
Roman Telezhynskyi 3aec5bf341 Reduce path to file in logs. Show only part inside of root folder. 2024-04-09 19:22:19 +03:00
Roman Telezhynskyi d331b5dc01 Fix path to QStandardPaths::ConfigLocation. 2024-04-09 16:28:49 +03:00
Roman Telezhynskyi f72d235344 Validate pieces each time we activate a sheet. 2024-04-08 20:18:33 +03:00
Roman Telezhynskyi c5a3375d78 Animate piece transformation with stickying animation. 2024-04-08 18:58:23 +03:00
Roman Telezhynskyi 9e3659830e New warning "Piece gape position". 2024-04-08 16:26:09 +03:00
Roman Telezhynskyi 39d56e34aa Fix color of fold line. 2024-04-08 15:58:09 +03:00
Roman Telezhynskyi d3bdd68c46 Make sure revision hash always has 'g' at the beginning. 2024-04-08 15:32:00 +03:00
Roman Telezhynskyi f681b5ccaf Toggling Cut on Fold check must always trigger the check. 2024-04-08 12:30:49 +03:00
Roman Telezhynskyi 2bdd023afe Optimize sticking sensitivity. 2024-04-08 12:26:42 +03:00
Roman Telezhynskyi 1a6a830119 Using QtConcurrent::blockingMappedReduced to parallelize the computation of finding closest distance between two polygons. 2024-04-08 11:55:59 +03:00
Roman Telezhynskyi 0eef58f2cf Crash settings must be disabled/empty when application doesn't support sending automatic crash reports to avoid user confusion. 2024-04-08 09:23:13 +03:00
Roman Telezhynskyi cbc137f4e9 Make valgrind happy. 2024-04-06 18:53:24 +03:00
Roman Telezhynskyi fbaf5f0f38 Refactoring. 2024-04-06 18:52:43 +03:00
Roman Telezhynskyi 54ee9e0629 Refactoring. 2024-04-06 12:09:38 +03:00
Roman Telezhynskyi a4e2e1f1bf Disable piece gap if option Sticky edges is not active. 2024-04-06 11:54:37 +03:00
Roman Telezhynskyi b25df043bf Disable explicit sheet's grainline orientation if option Follow grainline is not active. 2024-04-06 11:51:26 +03:00
Roman Telezhynskyi 5186e80adb Change direction of automatic layout grainline to avoid user confusion. 2024-04-06 08:28:28 +03:00
54 changed files with 894 additions and 566 deletions

View File

@ -66,6 +66,7 @@
- Updated Windows installer.
- Automatic crash reports.
- Improve compatibility with Richpeace CAD.
- New warning "Piece gape position".
# Valentina 0.7.52 September 12, 2022
- Fix crash when default locale is ru.

View File

@ -44,7 +44,7 @@ def generate_sym_files(install_root):
print(f"Generating symbols for: {os.path.basename(debug_file)}")
sym_file = os.path.splitext(debug_file)[0] + ".sym"
dump_syms_cmd = ["dump_syms", '-o', sym_file, debug_file]
dump_syms_cmd = ["dump_syms", '-o', sym_file, '--inlines', debug_file]
subprocess.run(dump_syms_cmd, check=True)
sym_files.append((debug_file, zip_sym(sym_file)))

View File

@ -1,2 +1,2 @@
[Rules]
*.debug=false
qt.*.debug=false

View File

@ -119,8 +119,9 @@ PuzzlePreferencesConfigurationPage::PuzzlePreferencesConfigurationPage(QWidget *
#if !defined(CRASH_REPORTING)
ui->groupBoxCrashReports->setDisabled(true);
#endif
ui->checkBoxSendCrashReports->setChecked(false);
ui->lineEditCrashUserEmail->setText(QString());
#else
ui->checkBoxSendCrashReports->setChecked(settings->IsSendCrashReport());
connect(ui->checkBoxSendCrashReports, &QCheckBox::stateChanged, this,
[this]() { m_sendCrashReportsChanged = true; });
@ -131,6 +132,7 @@ PuzzlePreferencesConfigurationPage::PuzzlePreferencesConfigurationPage(QWidget *
ui->lineEditCrashUserEmail->setText(settings->GetCrashEmail());
connect(ui->lineEditCrashUserEmail, &QLineEdit::editingFinished, this,
[this]() { m_crashUserEmailChanged = true; });
#endif
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -128,6 +128,8 @@ PuzzlePreferencesLayoutPage::PuzzlePreferencesLayoutPage(QWidget *parent)
connect(ui->checkBoxWarningPiecesOutOfBound, &QCheckBox::stateChanged, this,
[this]() { m_settingsChanged = true; });
connect(ui->checkBoxFollowGrainline, &QCheckBox::stateChanged, this, [this]() { m_settingsChanged = true; });
connect(ui->checkBoxWarningPieceGapePosition, &QCheckBox::stateChanged, this,
[this]() { m_settingsChanged = true; });
}
//---------------------------------------------------------------------------------------------------------------------
@ -166,6 +168,7 @@ auto PuzzlePreferencesLayoutPage::Apply() -> QStringList
settings->SetLayoutStickyEdges(ui->checkBoxStickyEdges->isChecked());
settings->SetLayoutWarningPiecesOutOfBound(ui->checkBoxWarningPiecesOutOfBound->isChecked());
settings->SetLayoutFollowGrainline(ui->checkBoxFollowGrainline->isChecked());
settings->SetLayoutWarningPieceGapePosition(ui->checkBoxWarningPieceGapePosition->isChecked());
settings->SetLayoutLineWidth(ui->spinBoxLineWidth->value());
@ -661,6 +664,7 @@ void PuzzlePreferencesLayoutPage::ReadSettings()
ui->checkBoxStickyEdges->setChecked(settings->GetLayoutStickyEdges());
ui->checkBoxWarningPiecesOutOfBound->setChecked(settings->GetLayoutWarningPiecesOutOfBound());
ui->checkBoxFollowGrainline->setChecked(settings->GetLayoutFollowGrainline());
ui->checkBoxWarningPieceGapePosition->setChecked(settings->GetLayoutWarningPieceGapePosition());
ui->doubleSpinBoxPiecesGap->setMaximum(UnitConvertor(VPSettings::GetMaxLayoutPieceGap(), Unit::Px, LayoutUnit()));
SetPieceGap(settings->GetLayoutPieceGap());

View File

@ -87,7 +87,7 @@
<item>
<widget class="QToolButton" name="toolButtonSheetPortraitOritation">
<property name="text">
<string notr="true"></string>
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../../libs/vmisc/share/resources/icon.qrc">
@ -107,7 +107,7 @@
<item>
<widget class="QToolButton" name="toolButtonSheetLandscapeOrientation">
<property name="text">
<string notr="true"></string>
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../../libs/vmisc/share/resources/icon.qrc">
@ -309,6 +309,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxWarningPieceGapePosition">
<property name="text">
<string>Warning piece gape position</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxStickyEdges">
<property name="text">
@ -475,7 +482,7 @@
<item>
<widget class="QToolButton" name="toolButtonTilePortraitOrientation">
<property name="text">
<string notr="true"></string>
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../../libs/vmisc/share/resources/icon.qrc">
@ -495,7 +502,7 @@
<item>
<widget class="QToolButton" name="toolButtonTileLandscapeOrientation">
<property name="text">
<string notr="true"></string>
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../../libs/vmisc/share/resources/icon.qrc">

View File

@ -90,6 +90,7 @@ auto VPLayout::CreateLayout(QUndoStack *undoStack) -> VPLayoutPtr
layout->LayoutSettings().SetWarningSuperpositionOfPieces(settings->GetLayoutWarningPiecesSuperposition());
layout->LayoutSettings().SetWarningPiecesOutOfBound(settings->GetLayoutWarningPiecesOutOfBound());
layout->LayoutSettings().SetWarningPieceGapePosition(settings->GetLayoutWarningPieceGapePosition());
layout->LayoutSettings().SetFollowGrainline(settings->GetLayoutFollowGrainline());
layout->LayoutSettings().SetStickyEdges(settings->GetLayoutStickyEdges());
layout->LayoutSettings().SetPiecesGap(settings->GetLayoutPieceGap());
@ -122,13 +123,14 @@ void VPLayout::AddPiece(const VPPiecePtr &piece)
VPPiece::CleanPosition(piece);
if (not m_pieces.contains(piece->GetUniqueID()))
const QString uniqueId = piece->GetUniqueID();
if (not m_pieces.contains(uniqueId))
{
m_pieces.insert(piece->GetUniqueID(), piece);
m_pieces.insert(uniqueId, piece);
}
else
{
VPPiecePtr const oldPiece = m_pieces.value(piece->GetUniqueID());
VPPiecePtr const oldPiece = m_pieces.value(uniqueId);
if (not oldPiece.isNull())
{
oldPiece->Update(piece);
@ -136,7 +138,7 @@ void VPLayout::AddPiece(const VPPiecePtr &piece)
}
else
{
m_pieces.insert(piece->GetUniqueID(), piece);
m_pieces.insert(uniqueId, piece);
}
}
}
@ -326,6 +328,8 @@ void VPLayout::SetFocusedSheet(const VPSheetPtr &focusedSheet)
m_focusedSheet = focusedSheet.isNull() ? m_sheets.constFirst() : focusedSheet;
}
CheckPiecesPositionValidity(m_focusedSheet);
emit ActiveSheetChanged(m_focusedSheet);
}
@ -341,6 +345,12 @@ auto VPLayout::GetTrashSheet() -> VPSheetPtr
return m_trashSheet;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPLayout::LayoutSettings() const -> const VPLayoutSettings &
{
return m_layoutSettings;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPLayout::LayoutSettings() -> VPLayoutSettings &
{
@ -417,10 +427,30 @@ void VPLayout::CheckPiecesPositionValidity() const
{
for (const auto &sheet : m_sheets)
{
if (not sheet.isNull())
CheckPiecesPositionValidity(sheet);
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayout::CheckPiecesPositionValidity(const VPSheetPtr &sheet) const
{
if (not sheet.isNull())
{
const VPLayoutSettings &settings = LayoutSettings();
if (settings.GetWarningPiecesOutOfBound())
{
sheet->ValidatePiecesOutOfBound();
}
if (settings.GetWarningSuperpositionOfPieces())
{
sheet->ValidateSuperpositionOfPieces();
sheet->ValidatePiecesOutOfBound();
}
if (settings.GetWarningPieceGapePosition())
{
sheet->ValidatePieceGapePosition();
}
}
}

View File

@ -78,6 +78,7 @@ public:
void AddTrashSheet(const VPSheetPtr &sheet);
auto GetTrashSheet() -> VPSheetPtr;
auto LayoutSettings() const -> const VPLayoutSettings &;
auto LayoutSettings() -> VPLayoutSettings &;
auto PiecesForSheet(const VPSheetPtr &sheet) const -> QList<VPPiecePtr>;
@ -90,6 +91,7 @@ public:
void Clear();
void CheckPiecesPositionValidity() const;
void CheckPiecesPositionValidity(const VPSheetPtr &sheet) const;
auto TileFactory() const -> QSharedPointer<VPTileFactory>;
void SetTileFactory(const QSharedPointer<VPTileFactory> &newTileFactory);

View File

@ -66,6 +66,18 @@ auto VPLayoutSettings::GetWarningPiecesOutOfBound() const -> bool
return m_warningPiecesOutOfBound;
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutSettings::SetWarningPieceGapePosition(bool state)
{
m_warningPieceGapePosition = state;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPLayoutSettings::GetWarningPieceGapePosition() const -> bool
{
return m_warningPieceGapePosition;
}
//---------------------------------------------------------------------------------------------------------------------
void VPLayoutSettings::SetTitle(const QString &title)
{
@ -244,7 +256,7 @@ void VPLayoutSettings::SetStickyEdges(bool state)
}
//---------------------------------------------------------------------------------------------------------------------
auto VPLayoutSettings::GetStickyEdges() const -> bool
auto VPLayoutSettings::IsStickyEdges() const -> bool
{
return m_stickyEdges;
}

View File

@ -81,7 +81,7 @@ public:
auto GetDescription() const -> QString;
void SetStickyEdges(bool state);
auto GetStickyEdges() const -> bool;
auto IsStickyEdges() const -> bool;
// Piece
@ -91,6 +91,9 @@ public:
void SetWarningPiecesOutOfBound(bool state);
auto GetWarningPiecesOutOfBound() const -> bool;
void SetWarningPieceGapePosition(bool state);
auto GetWarningPieceGapePosition() const -> bool;
/**
* @brief SetFollowGrainline Sets the type of grainline for the pieces to follow
* @param state the type of grainline
@ -330,6 +333,7 @@ private:
bool m_warningSuperpositionOfPieces{false};
bool m_warningPiecesOutOfBound{false};
bool m_warningPieceGapePosition{false};
QString m_title{};
QString m_description{};

View File

@ -41,6 +41,7 @@
#include <QLoggingCategory>
#include <QPainter>
#include <QPainterPath>
#include <QtConcurrent>
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
#include "../vmisc/compatibility.h"
@ -59,7 +60,7 @@ QT_WARNING_POP
namespace
{
constexpr qreal minStickyDistance = MmToPixel(3.);
constexpr qreal maxStickyDistance = MmToPixel(10.);
constexpr qreal maxStickyDistance = MmToPixel(15.);
constexpr qreal stickyShift = MmToPixel(1.);
//---------------------------------------------------------------------------------------------------------------------
@ -94,46 +95,6 @@ auto CutEdge(const QLineF &edge) -> QVector<QPointF>
}
return points;
}
//---------------------------------------------------------------------------------------------------------------------
auto PrepareStickyPath(const QVector<QPointF> &path) -> QVector<QPointF>
{
if (path.size() < 2)
{
return path;
}
QVector<QPointF> stickyPath;
for (int i = 0; i < path.size(); ++i)
{
stickyPath += CutEdge(QLineF(path.at(i), path.at(i < path.size() - 1 ? i + 1 : 0)));
}
return stickyPath;
}
//---------------------------------------------------------------------------------------------------------------------
auto ClosestDistance(const QVector<QPointF> &path1, const QVector<QPointF> &path2) -> QLineF
{
qreal distance = INT_MAX;
QLineF closestDistance;
for (auto p1 : path1)
{
for (auto p2 : path2)
{
QLineF const d(p1, p2);
if (d.length() <= distance)
{
distance = d.length();
closestDistance = d;
}
}
}
return closestDistance;
}
} // namespace
//---------------------------------------------------------------------------------------------------------------------
@ -262,40 +223,62 @@ void VPPiece::RotateToGrainline(const VPTransformationOrigon &origin)
}
QVector<qreal> angles;
angles.reserve(4);
angles.reserve(8);
const VPieceGrainline pieceGrainline = GetGrainline();
if (pieceGrainline.IsArrowUpEnabled())
{
angles.append(grainline.angleTo(fabricGrainline));
qreal const angle = grainline.angleTo(fabricGrainline);
angles.append(angle);
angles.append(-(360. - angle));
}
if (pieceGrainline.IsArrowDownEnabled())
{
QLineF arrow = grainline;
arrow.setAngle(arrow.angle() + 180);
angles.append(arrow.angleTo(fabricGrainline));
qreal const angle = arrow.angleTo(fabricGrainline);
angles.append(angle);
angles.append(-(360. - angle));
}
if (pieceGrainline.IsArrowLeftEnabled())
{
QLineF arrow = grainline;
arrow.setAngle(arrow.angle() + 90);
angles.append(arrow.angleTo(fabricGrainline));
qreal const angle = arrow.angleTo(fabricGrainline);
angles.append(angle);
angles.append(-(360. - angle));
}
if (pieceGrainline.IsArrowRightEnabled())
{
QLineF arrow = grainline;
arrow.setAngle(arrow.angle() - 90);
angles.append(arrow.angleTo(fabricGrainline));
qreal const angle = arrow.angleTo(fabricGrainline);
angles.append(angle);
angles.append(-(360. - angle));
}
qreal degrees = 0;
if (not angles.isEmpty())
{
degrees = *std::min_element(angles.constBegin(), angles.constEnd());
qreal minAbsAngle = qAbs(angles.constFirst());
degrees = angles.constFirst();
for (int i = 1; i < angles.size(); ++i)
{
qreal const absAngle = qAbs(angles.at(i));
if (absAngle < minAbsAngle)
{
minAbsAngle = absAngle;
degrees = angles.at(i);
}
}
}
Rotate(origin.custom ? MappedDetailBoundingRect().center() : origin.origin, degrees);
@ -339,10 +322,22 @@ void VPPiece::FlipHorizontally()
SetHorizontallyFlipped(!IsHorizontallyFlipped());
}
//---------------------------------------------------------------------------------------------------------------------
auto VPPiece::HasInvalidPieceGapPosition() const -> bool
{
return m_invalidPieceGapPosition;
}
//---------------------------------------------------------------------------------------------------------------------
void VPPiece::SetHasInvalidPieceGapPosition(bool status)
{
m_invalidPieceGapPosition = status;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPPiece::StickyPosition(qreal &dx, qreal &dy) const -> bool
{
if (VPLayoutPtr const layout = Layout(); layout.isNull() || not layout->LayoutSettings().GetStickyEdges())
if (VPLayoutPtr const layout = Layout(); layout.isNull() || not layout->LayoutSettings().IsStickyEdges())
{
return false;
}
@ -374,7 +369,11 @@ auto VPPiece::StickyPosition(qreal &dx, qreal &dy) const -> bool
return false;
}
const qreal extraZone = qBound(minStickyDistance, match.m_pieceGap * 50 / 100, maxStickyDistance);
qreal extraZone = qBound(minStickyDistance, match.m_pieceGap * 50 / 100, maxStickyDistance);
if (qFuzzyIsNull(match.m_pieceGap))
{
extraZone = maxStickyDistance;
}
const qreal length = match.m_closestDistance.length();
if (length > match.m_pieceGap && length <= match.m_pieceGap + extraZone)
@ -419,6 +418,58 @@ auto VPPiece::PathsSuperposition(const QVector<QPointF> &path1, const QVector<QP
return false;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPPiece::PrepareStickyPath(const QVector<QPointF> &path) -> QVector<QPointF>
{
if (path.size() < 2)
{
return path;
}
QVector<QPointF> stickyPath;
for (int i = 0; i < path.size(); ++i)
{
stickyPath += CutEdge(QLineF(path.at(i), path.at(i < path.size() - 1 ? i + 1 : 0)));
}
return stickyPath;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPPiece::ClosestDistance(const QVector<QPointF> &path1, const QVector<QPointF> &path2) -> QLineF
{
return QtConcurrent::blockingMappedReduced(
path1,
[path2](const QPointF &p1)
{
qreal minLocalDistance = std::numeric_limits<qreal>::max();
QLineF localClosestDistance;
for (const auto &p2 : path2)
{
QLineF const d(p1, p2);
qreal const length = d.length();
if (length < minLocalDistance)
{
minLocalDistance = length;
localClosestDistance = d;
}
}
return localClosestDistance;
},
[](QLineF &result, const QLineF &next)
{
qreal const dist1 = result.length();
qreal const dist2 = next.length();
if (result.isNull() || dist2 < dist1)
{
result = next;
}
});
}
//---------------------------------------------------------------------------------------------------------------------
auto VPPiece::IsValid(QString &error) const -> bool
{

View File

@ -110,9 +110,14 @@ public:
auto HasSuperpositionWithPieces() const -> bool;
void SetHasSuperpositionWithPieces(bool newHasSuperpositionWithPieces);
auto HasInvalidPieceGapPosition() const -> bool;
void SetHasInvalidPieceGapPosition(bool status);
auto StickyPosition(qreal &dx, qreal &dy) const -> bool;
static auto PathsSuperposition(const QVector<QPointF> &path1, const QVector<QPointF> &path2) -> bool;
static auto PrepareStickyPath(const QVector<QPointF> &path) -> QVector<QPointF>;
static auto ClosestDistance(const QVector<QPointF> &path1, const QVector<QPointF> &path2) -> QLineF;
static void CleanPosition(const VPPiecePtr &piece);
auto IsValid(QString &error) const -> bool;
@ -134,6 +139,7 @@ private:
bool m_isSelected{false};
bool m_outOfBound{false};
bool m_hasSuperpositionWithPieces{false};
bool m_invalidPieceGapPosition{false};
quint16 m_copyNumber{1};

View File

@ -164,6 +164,9 @@ void VPSheetSceneData::PrepareForExport()
m_pieceSuperpositionTmp = layout->LayoutSettings().GetWarningSuperpositionOfPieces();
layout->LayoutSettings().SetWarningSuperpositionOfPieces(false);
m_pieceGapePositionTmp = layout->LayoutSettings().GetWarningPieceGapePosition();
layout->LayoutSettings().SetWarningPieceGapePosition(false);
}
RefreshLayout();
@ -197,6 +200,7 @@ void VPSheetSceneData::CleanAfterExport()
layout->LayoutSettings().SetWarningPiecesOutOfBound(m_outOfBoundTmp);
layout->LayoutSettings().SetWarningSuperpositionOfPieces(m_pieceSuperpositionTmp);
layout->LayoutSettings().SetWarningPieceGapePosition(m_pieceGapePositionTmp);
}
RefreshLayout();
@ -511,7 +515,7 @@ void VPSheet::ValidateSuperpositionOfPieces() const
for (const auto &piece : pieces)
{
if (piece.isNull())
if (piece.isNull() || piece->OutOfBound())
{
continue;
}
@ -552,6 +556,77 @@ void VPSheet::ValidateSuperpositionOfPieces() const
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPSheet::ValidatePieceGapePosition(const VPPiecePtr &piece) const
{
VPLayoutPtr const layout = GetLayout();
if (layout.isNull())
{
return;
}
const qreal pieceGap = layout->LayoutSettings().GetPiecesGap();
if (pieceGap <= 0)
{
return;
}
if (piece.isNull() || piece->HasSuperpositionWithPieces() || piece->OutOfBound())
{
return;
}
const bool oldInvalidPieceGapPosition = piece->HasInvalidPieceGapPosition();
QVector<QPointF> path1;
CastTo(piece->GetMappedExternalContourPoints(), path1);
path1 = VPPiece::PrepareStickyPath(path1);
bool hasInvalidPieceGapPosition = false;
QList<VPPiecePtr> const pieces = GetPieces();
for (const auto &p : pieces)
{
if (p.isNull() || piece == p)
{
continue;
}
QVector<QPointF> path2;
CastTo(p->GetMappedExternalContourPoints(), path2);
path2 = VPPiece::PrepareStickyPath(path2);
QLineF const distance = VPPiece::ClosestDistance(path1, path2);
if (distance.length() < pieceGap - accuracyPointOnLine)
{
hasInvalidPieceGapPosition = true;
break;
}
}
piece->SetHasInvalidPieceGapPosition(hasInvalidPieceGapPosition);
if (oldInvalidPieceGapPosition != piece->HasInvalidPieceGapPosition())
{
VPLayoutPtr const layout = GetLayout();
if (not layout.isNull())
{
emit layout->PiecePositionValidityChanged(piece);
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPSheet::ValidatePieceGapePosition() const
{
QList<VPPiecePtr> const pieces = GetPieces();
for (const auto &piece : pieces)
{
ValidatePieceGapePosition(piece);
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPSheet::ValidatePieceOutOfBound(const VPPiecePtr &piece) const
{
@ -699,6 +774,11 @@ void VPSheet::CheckPiecePositionValidity(const VPPiecePtr &piece) const
{
ValidateSuperpositionOfPieces();
}
if (layout->LayoutSettings().GetWarningPieceGapePosition())
{
ValidatePieceGapePosition(piece);
}
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -128,6 +128,7 @@ private:
bool m_outOfBoundTmp{false};
bool m_pieceSuperpositionTmp{false};
bool m_pieceGapePositionTmp{false};
void ConnectPiece(VPGraphicsPiece *piece) const;
};
@ -183,6 +184,8 @@ public:
void SetTrashSheet(bool newTrashSheet);
void ValidateSuperpositionOfPieces() const;
void ValidatePieceGapePosition(const VPPiecePtr &piece) const;
void ValidatePieceGapePosition() const;
void ValidatePieceOutOfBound(const VPPiecePtr &piece) const;
void ValidatePiecesOutOfBound() const;

View File

@ -5,7 +5,7 @@ import qbs.Utilities
VToolApp {
Depends { name: "buildconfig" }
Depends { name: "ib"; condition: qbs.targetOS.contains("macos") }
Depends { name: "Qt"; submodules: ["core", "widgets", "svg"] }
Depends { name: "Qt"; submodules: ["core", "widgets", "svg", "concurrent"] }
Depends { name: "VMiscLib" }
Depends { name: "VLayoutLib" }
Depends { name: "IFCLib" }

View File

@ -72,46 +72,6 @@ QT_WARNING_POP
namespace
{
//---------------------------------------------------------------------------------------------------------------------
inline auto LineMatrix(const VPPiecePtr &piece, const QPointF &topLeft, qreal angle, const QPointF &linePos,
int maxLineWidth) -> QTransform
{
if (piece.isNull())
{
return {};
}
QTransform labelMatrix;
labelMatrix.translate(topLeft.x(), topLeft.y());
if ((piece->IsVerticallyFlipped() && piece->IsHorizontallyFlipped()) ||
(!piece->IsVerticallyFlipped() && !piece->IsHorizontallyFlipped()))
{
labelMatrix.rotate(angle);
}
else if (piece->IsVerticallyFlipped() || piece->IsHorizontallyFlipped())
{
if (piece->IsVerticallyFlipped() && !piece->IsHorizontallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(-angle);
labelMatrix.translate(-maxLineWidth, 0);
}
if (piece->IsHorizontallyFlipped() && !piece->IsVerticallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(-angle);
labelMatrix.translate(-maxLineWidth, 0);
}
}
labelMatrix.translate(linePos.x(), linePos.y()); // Each string has own position
labelMatrix *= piece->GetMatrix();
return labelMatrix;
}
//---------------------------------------------------------------------------------------------------------------------
inline auto LineFont(const TextLine &tl, const QFont &base) -> QFont
{
@ -297,7 +257,7 @@ void VPGraphicsPiece::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (VPLayoutPtr const layout = piece->Layout(); not layout.isNull())
{
if (layout->LayoutSettings().GetStickyEdges() && m_hasStickyPosition)
if (layout->LayoutSettings().IsStickyEdges() && m_hasStickyPosition)
{
auto *command =
new VPUndoPieceMove(piece, m_stickyTranslateX, m_stickyTranslateY, m_allowChangeMerge);
@ -448,7 +408,6 @@ void VPGraphicsPiece::InitPieceLabelSVGFont(const QVector<QPointF> &labelShape,
const qreal dH = QLineF(labelShape.at(1), labelShape.at(2)).length();
const qreal angle = -QLineF(labelShape.at(0), labelShape.at(1)).angle();
const QColor color = PieceColor();
const int maxLineWidth = tm.MaxLineWidthSVGFont(static_cast<int>(dW), penWidth);
qreal dY = penWidth;
@ -467,7 +426,7 @@ void VPGraphicsPiece::InitPieceLabelSVGFont(const QVector<QPointF> &labelShape,
const QString qsText = tl.m_qsText;
const qreal dX = LineAlign(tl, qsText, engine, dW, penWidth);
// set up the rotation around top-left corner matrix
const QTransform lineMatrix = LineMatrix(piece, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth);
const QTransform lineMatrix = piece->LineMatrix(labelShape.at(0), angle, QPointF(dX, dY), dW);
auto *item = new QGraphicsPathItem(this);
item->setPath(engine.DrawPath(QPointF(), qsText));
@ -505,7 +464,6 @@ void VPGraphicsPiece::InitPieceLabelOutlineFont(const QVector<QPointF> &labelSha
const qreal dH = QLineF(labelShape.at(1), labelShape.at(2)).length();
const qreal angle = -QLineF(labelShape.at(0), labelShape.at(1)).angle();
const QColor color = PieceColor();
const int maxLineWidth = tm.MaxLineWidthOutlineFont(static_cast<int>(dW));
qreal const penWidth = VPApplication::VApp()->PuzzleSettings()->GetLayoutLineWidth();
qreal dY = 0;
@ -533,7 +491,7 @@ void VPGraphicsPiece::InitPieceLabelOutlineFont(const QVector<QPointF> &labelSha
const qreal dX = LineAlign(tl, tl.m_qsText, fm, dW);
// set up the rotation around top-left corner matrix
const QTransform lineMatrix = LineMatrix(piece, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth);
const QTransform lineMatrix = piece->LineMatrix(labelShape.at(0), angle, QPointF(dX, dY), dW);
if (textAsPaths)
{
@ -1013,6 +971,8 @@ void VPGraphicsPiece::PaintFoldLine(QPainter *painter, const VPPiecePtr &piece)
}
fLine.UpdateFoldLineLabel(m_foldLineLabelText);
m_foldLineLabelText->setBrush(QBrush(PieceColor()));
}
else
{
@ -1058,6 +1018,9 @@ void VPGraphicsPiece::PaintFoldLine(QPainter *painter, const VPPiecePtr &piece)
painter->save();
QPen pen = painter->pen();
pen.setWidthF(penWidth * qMin(piece->GetXScale(), piece->GetYScale()));
pen.setColor(PieceColor());
pen.setCapStyle(Qt::RoundCap);
pen.setJoinStyle(Qt::RoundJoin);
painter->setPen(pen);
painter->setBrush(singleLineFont ? Qt::NoBrush : Qt::SolidPattern);
painter->drawPath(m_foldLineLabelPath);
@ -1106,7 +1069,7 @@ void VPGraphicsPiece::GroupMove(const QPointF &pos)
auto *command = new VPUndoPieceMove(piece, newPos.x(), newPos.y(), m_allowChangeMerge);
layout->UndoStack()->push(command);
if (layout->LayoutSettings().GetStickyEdges())
if (layout->LayoutSettings().IsStickyEdges())
{
QVector<QPointF> path;
if (not p.isNull() && p->StickyPosition(m_stickyTranslateX, m_stickyTranslateY))
@ -1159,7 +1122,13 @@ auto VPGraphicsPiece::PieceColor() const -> QColor
superposition = piece->HasSuperpositionWithPieces();
}
if (outOfBound || superposition)
bool pieceGape = false;
if (layout->LayoutSettings().GetWarningPieceGapePosition())
{
pieceGape = piece->HasInvalidPieceGapPosition();
}
if (outOfBound || superposition || pieceGape)
{
return VSceneStylesheet::ManualLayoutStyle().PieceErrorColor();
}

View File

@ -109,6 +109,11 @@ void VPGraphicsTransformationOrigin::on_HideHandles(bool hide)
void VPGraphicsTransformationOrigin::on_ShowOrigin(bool show)
{
setVisible(show);
if (not show)
{
m_hoverMode = false;
}
}
//---------------------------------------------------------------------------------------------------------------------
@ -404,7 +409,6 @@ void VPGraphicsPieceControls::mousePressEvent(QGraphicsSceneMouseEvent *event)
if (event->button() == Qt::LeftButton && event->type() != QEvent::GraphicsSceneMouseDoubleClick)
{
m_rotationStartPoint = event->scenePos();
m_rotationSum = 0;
m_controlsVisible = false;
m_handleCorner = SelectedHandleCorner(event->pos());
m_ignorePieceTransformation = true;
@ -433,7 +437,7 @@ void VPGraphicsPieceControls::mousePressEvent(QGraphicsSceneMouseEvent *event)
//---------------------------------------------------------------------------------------------------------------------
void VPGraphicsPieceControls::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
PrepareTransformationOrigin(event->modifiers() & Qt::ShiftModifier);
PrepareTransformationOrigin(event->modifiers() & Qt::ShiftModifier); // NOLINT(readability-implicit-bool-conversion)
QPointF const rotationNewPoint = event->scenePos();
@ -451,24 +455,18 @@ void VPGraphicsPieceControls::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
if (not qFuzzyIsNull(rotateOn))
{
QList<VPPiecePtr> const pieces = SelectedPieces();
VPLayoutPtr const layout = m_layout.toStrongRef();
if (not layout.isNull())
if (VPLayoutPtr const layout = m_layout.toStrongRef(); not layout.isNull())
{
CorrectRotationSum(layout, rotationOrigin, rotateOn);
QList<VPPiecePtr> const pieces = SelectedPieces();
if (pieces.size() == 1)
{
auto *command = new VPUndoPieceRotate(pieces.constFirst(), rotationOrigin, rotateOn, m_rotationSum,
allowChangeMerge);
layout->UndoStack()->push(command);
layout->UndoStack()->push(
new VPUndoPieceRotate(pieces.constFirst(), rotationOrigin, rotateOn, allowChangeMerge));
}
else if (pieces.size() > 1)
{
auto *command =
new VPUndoPiecesRotate(pieces, rotationOrigin, rotateOn, m_rotationSum, allowChangeMerge);
layout->UndoStack()->push(command);
layout->UndoStack()->push(new VPUndoPiecesRotate(pieces, rotationOrigin, rotateOn, allowChangeMerge));
}
}
}
@ -490,6 +488,19 @@ void VPGraphicsPieceControls::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
VPLayoutPtr const layout = m_layout.toStrongRef();
if (not layout.isNull() && layout->LayoutSettings().GetFollowGrainline())
{
VPTransformationOrigon const rotationOrigin = TransformationOrigin(m_layout, m_pieceRect);
QList<VPPiecePtr> const pieces = SelectedPieces();
for (const auto &piece : qAsConst(pieces))
{
piece->RotateToGrainline(rotationOrigin);
emit layout->PieceTransformationChanged(piece);
}
}
m_controlsVisible = true;
m_ignorePieceTransformation = false;
@ -505,22 +516,19 @@ void VPGraphicsPieceControls::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
}
}
if (m_originSaved)
if (m_originSaved && not layout.isNull())
{
if (VPLayoutPtr const layout = m_layout.toStrongRef(); not layout.isNull())
if (VPSheetPtr const sheet = layout->GetFocusedSheet(); not sheet.isNull())
{
if (VPSheetPtr const sheet = layout->GetFocusedSheet(); not sheet.isNull())
if (not m_savedOrigin.custom)
{
if (not m_savedOrigin.custom)
{
m_pieceRect = PiecesBoundingRect(m_selectedPieces);
m_savedOrigin.origin = m_pieceRect.center();
}
sheet->SetTransformationOrigin(m_savedOrigin);
emit TransformationOriginChanged();
m_pieceRect = PiecesBoundingRect(m_selectedPieces);
m_savedOrigin.origin = m_pieceRect.center();
}
m_originSaved = false;
sheet->SetTransformationOrigin(m_savedOrigin);
emit TransformationOriginChanged();
}
m_originSaved = false;
}
on_UpdateControls();
@ -853,32 +861,6 @@ void VPGraphicsPieceControls::PrepareTransformationOrigin(bool shiftPressed)
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPGraphicsPieceControls::CorrectRotationSum(const VPLayoutPtr &layout,
const VPTransformationOrigon &rotationOrigin, qreal rotateOn)
{
if (layout.isNull())
{
return;
}
if (layout->LayoutSettings().GetFollowGrainline() && not rotationOrigin.custom)
{
if (m_rotationSum > 90 || m_rotationSum < -90)
{
m_rotationSum = rotateOn;
}
else
{
m_rotationSum += rotateOn;
}
}
else
{
m_rotationSum = rotateOn;
}
}
//---------------------------------------------------------------------------------------------------------------------
auto VPGraphicsPieceControls::SelectedHandleCorner(const QPointF &pos) const -> VPHandleCorner
{

View File

@ -143,7 +143,6 @@ private:
QRectF m_pieceRect{};
QPointF m_rotationStartPoint{};
qreal m_rotationSum{0};
bool m_controlsVisible{false};
VPLayoutWeakPtr m_layout{};
VPHandleCorner m_handleCorner{VPHandleCorner::Invalid};
@ -185,7 +184,6 @@ private:
void UpdateCursor(VPHandleCorner corner);
void PrepareTransformationOrigin(bool shiftPressed);
void CorrectRotationSum(const VPLayoutPtr &layout, const VPTransformationOrigon &rotationOrigin, qreal rotateOn);
};
#endif // VPGRAPHICSPIECECONTROLS_H

View File

@ -320,8 +320,6 @@ void VPMainGraphicsView::keyReleaseEvent(QKeyEvent *event)
sheet->SceneData()->RotationControls()->on_UpdateControls();
sheet->SceneData()->RotationControls()->on_HideHandles(false);
}
m_rotationSum = 0;
}
}
VMainGraphicsView::keyReleaseEvent(event);
@ -439,31 +437,34 @@ void VPMainGraphicsView::RotatePiecesByAngle(qreal angle)
return pieces;
};
if (layout->LayoutSettings().GetFollowGrainline() && not origin.custom)
{
if (m_rotationSum > 90 || m_rotationSum < -90)
{
m_rotationSum = angle;
}
else
{
m_rotationSum += angle;
}
}
else
{
m_rotationSum = angle;
}
QList<VPPiecePtr> const pieces = PreparePieces();
if (QList<VPPiecePtr> const pieces = PreparePieces(); pieces.size() == 1)
if (pieces.size() == 1)
{
auto *command = new VPUndoPieceRotate(pieces.constFirst(), origin, angle, m_rotationSum, m_allowChangeMerge);
layout->UndoStack()->push(command);
layout->UndoStack()->push(new VPUndoPieceRotate(pieces.constFirst(), origin, angle, m_allowChangeMerge));
}
else if (pieces.size() > 1)
{
auto *command = new VPUndoPiecesRotate(pieces, origin, angle, m_rotationSum, m_allowChangeMerge);
layout->UndoStack()->push(command);
layout->UndoStack()->push(new VPUndoPiecesRotate(pieces, origin, angle, m_allowChangeMerge));
}
QTime const dieTime = QTime::currentTime().addMSecs(150);
while (QTime::currentTime() < dieTime)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
}
for (const auto &piece : qAsConst(pieces))
{
if (not piece.isNull())
{
if (layout->LayoutSettings().GetFollowGrainline() || piece->IsFollowGrainline())
{
piece->RotateToGrainline(origin);
}
emit layout->PieceTransformationChanged(piece);
}
}
m_allowChangeMerge = true;
@ -516,7 +517,7 @@ void VPMainGraphicsView::TranslatePiecesOn(qreal dx, qreal dy)
auto *command = new VPUndoPieceMove(p, dx, dy, m_allowChangeMerge);
layout->UndoStack()->push(command);
if (layout->LayoutSettings().GetStickyEdges())
if (layout->LayoutSettings().IsStickyEdges())
{
QVector<QPointF> path;
if (not p.isNull() && p->StickyPosition(m_stickyTranslateX, m_stickyTranslateY))
@ -677,7 +678,7 @@ void VPMainGraphicsView::MovePiece(QKeyEvent *event)
if (const QList<VPGraphicsPiece *> &graphicsPieces = sheet->SceneData()->GraphicsPieces();
m_hasStickyPosition && not graphicsPieces.isEmpty())
{
if (layout->LayoutSettings().GetStickyEdges())
if (layout->LayoutSettings().IsStickyEdges())
{
auto PreparePieces = [layout]()
{

View File

@ -29,8 +29,8 @@
#ifndef VPMAINGRAPHICSVIEW_H
#define VPMAINGRAPHICSVIEW_H
#include "../vwidgets/vmaingraphicsview.h"
#include "../layout/layoutdef.h"
#include "../vwidgets/vmaingraphicsview.h"
class VMainGraphicsScene;
class VPGraphicsPieceControls;
@ -45,6 +45,7 @@ class VPPiece;
class VPMainGraphicsView : public VMainGraphicsView
{
Q_OBJECT // NOLINT
public:
VPMainGraphicsView(const VPLayoutPtr &layout, QWidget *parent);
~VPMainGraphicsView() override = default;
@ -98,8 +99,6 @@ private:
bool m_allowChangeMerge{false};
qreal m_rotationSum{0};
bool m_hasStickyPosition{false};
qreal m_stickyTranslateX{0};
qreal m_stickyTranslateY{0};

View File

@ -41,12 +41,11 @@ auto RoundAngle(qreal angle) -> qreal
//---------------------------------------------------------------------------------------------------------------------
VPUndoPieceRotate::VPUndoPieceRotate(const VPPiecePtr &piece, const VPTransformationOrigon &origin, qreal angle,
qreal angleSum, bool allowMerge, QUndoCommand *parent)
bool allowMerge, QUndoCommand *parent)
: VPUndoCommand(allowMerge, parent),
m_piece(piece),
m_origin(origin),
m_angle(angle),
m_angleSum(angleSum)
m_angle(angle)
{
SCASSERT(not piece.isNull())
@ -81,6 +80,10 @@ void VPUndoPieceRotate::undo()
}
piece->SetMatrix(m_oldTransform);
if (m_followGrainline || piece->IsFollowGrainline())
{
piece->RotateToGrainline(m_origin);
}
emit layout->PieceTransformationChanged(piece);
}
@ -104,23 +107,9 @@ void VPUndoPieceRotate::redo()
layout->SetFocusedSheet(piece->Sheet());
}
if (m_firstCall)
{
if ((m_followGrainline || piece->IsFollowGrainline()) && piece->IsGrainlineEnabled())
{
piece->Rotate(m_origin.origin, m_angleSum);
}
else
{
piece->Rotate(m_origin.origin, m_angle);
}
}
else
{
piece->Rotate(m_origin.origin, m_angle);
}
piece->Rotate(m_origin.origin, m_angle);
if (m_followGrainline || piece->IsFollowGrainline())
if (!m_firstCall && (m_followGrainline || piece->IsFollowGrainline()))
{
piece->RotateToGrainline(m_origin);
}
@ -168,11 +157,10 @@ auto VPUndoPieceRotate::id() const -> int
// rotate pieces
//---------------------------------------------------------------------------------------------------------------------
VPUndoPiecesRotate::VPUndoPiecesRotate(const QList<VPPiecePtr> &pieces, const VPTransformationOrigon &origin,
qreal angle, qreal angleSum, bool allowMerge, QUndoCommand *parent)
qreal angle, bool allowMerge, QUndoCommand *parent)
: VPUndoCommand(allowMerge, parent),
m_origin(origin),
m_angle(angle),
m_angleSum(angleSum)
m_angle(angle)
{
setText(QObject::tr("rotate pieces"));
@ -219,6 +207,10 @@ void VPUndoPiecesRotate::undo()
if (m_oldTransforms.contains(p->GetUniqueID()))
{
p->SetMatrix(m_oldTransforms.value(p->GetUniqueID()));
if (m_followGrainline || p->IsFollowGrainline())
{
p->RotateToGrainline(m_origin);
}
emit layout->PieceTransformationChanged(p);
}
}
@ -249,23 +241,9 @@ void VPUndoPiecesRotate::redo()
VPPiecePtr const p = piece.toStrongRef();
if (not p.isNull())
{
if (m_firstCall)
{
if ((m_followGrainline || p->IsFollowGrainline()) && p->IsGrainlineEnabled())
{
p->Rotate(m_origin.origin, m_angleSum);
}
else
{
p->Rotate(m_origin.origin, m_angle);
}
}
else
{
p->Rotate(m_origin.origin, m_angle);
}
p->Rotate(m_origin.origin, m_angle);
if (m_followGrainline || p->IsFollowGrainline())
if (!m_firstCall && (m_followGrainline || p->IsFollowGrainline()))
{
p->RotateToGrainline(m_origin);
}

View File

@ -37,11 +37,12 @@
class VPUndoPieceRotate : public VPUndoCommand
{
Q_OBJECT // NOLINT
public:
VPUndoPieceRotate(const VPPiecePtr &piece, const VPTransformationOrigon &origin, qreal angle, qreal angleSum,
VPUndoPieceRotate(const VPPiecePtr &piece, const VPTransformationOrigon &origin, qreal angle,
bool allowMerge = false, QUndoCommand *parent = nullptr);
~VPUndoPieceRotate() override =default;
~VPUndoPieceRotate() override = default;
void undo() override;
void redo() override;
@ -58,13 +59,12 @@ public:
private:
Q_DISABLE_COPY_MOVE(VPUndoPieceRotate) // NOLINT
bool m_firstCall{true};
bool m_firstCall{true};
VPPieceWeakPtr m_piece;
QTransform m_oldTransform{};
QTransform m_oldTransform{};
VPTransformationOrigon m_origin;
qreal m_angle;
qreal m_angleSum;
bool m_followGrainline{false};
qreal m_angle;
bool m_followGrainline{false};
};
//---------------------------------------------------------------------------------------------------------------------
@ -95,10 +95,11 @@ inline auto VPUndoPieceRotate::FollowGrainline() const -> bool
class VPUndoPiecesRotate : public VPUndoCommand
{
Q_OBJECT // NOLINT
public:
explicit VPUndoPiecesRotate(const QList<VPPiecePtr> &pieces, const VPTransformationOrigon &origin, qreal angle,
qreal angleSum, bool allowMerge = false, QUndoCommand *parent = nullptr);
~VPUndoPiecesRotate() override =default;
bool allowMerge = false, QUndoCommand *parent = nullptr);
~VPUndoPiecesRotate() override = default;
void undo() override;
void redo() override;
@ -114,13 +115,12 @@ public:
private:
Q_DISABLE_COPY_MOVE(VPUndoPiecesRotate) // NOLINT
bool m_firstCall{true};
QVector<VPPieceWeakPtr> m_pieces{};
bool m_firstCall{true};
QVector<VPPieceWeakPtr> m_pieces{};
QMap<QString, QTransform> m_oldTransforms{};
VPTransformationOrigon m_origin;
qreal m_angle;
qreal m_angleSum;
bool m_followGrainline{false};
VPTransformationOrigon m_origin;
qreal m_angle;
bool m_followGrainline{false};
auto Layout() const -> VPLayoutPtr;
auto Sheet() const -> VPSheetPtr;

View File

@ -160,40 +160,41 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
{
QString debugdate = "["_L1 + QDateTime::currentDateTime().toString(QStringLiteral("yyyy.MM.dd hh:mm:ss"));
QString const file = VAbstractApplication::ReduceLogContextFilePath(context.file);
switch (type)
{
case QtDebugMsg:
debugdate += QStringLiteral(":DEBUG:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, msg);
vStdOut() << QApplication::translate("mNoisyHandler", "DEBUG:") << msg << "\n";
break;
case QtWarningMsg:
debugdate += QStringLiteral(":WARNING:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, msg);
vStdErr() << QApplication::translate("mNoisyHandler", "WARNING:") << msg << "\n";
break;
case QtCriticalMsg:
debugdate += QStringLiteral(":CRITICAL:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, msg);
vStdErr() << QApplication::translate("mNoisyHandler", "CRITICAL:") << msg << "\n";
break;
case QtFatalMsg:
debugdate += QStringLiteral(":FATAL:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, msg);
vStdErr() << QApplication::translate("mNoisyHandler", "FATAL:") << msg << "\n";
break;
case QtInfoMsg:
debugdate += QStringLiteral(":INFO:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, msg);
vStdOut() << QApplication::translate("mNoisyHandler", "INFO:") << msg << "\n";
@ -491,6 +492,13 @@ auto VPApplication::TrVars() -> const VTranslateVars *
//---------------------------------------------------------------------------------------------------------------------
void VPApplication::OpenSettings()
{
#if defined(Q_OS_WIN)
QString const docPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
if (!docPath.isEmpty())
{
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, docPath);
}
#endif
settings = new VPSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(),
QCoreApplication::applicationName(), this);
connect(settings, &VPSettings::SVGFontsPathChanged, this, &VPApplication::SVGFontsPathChanged);

View File

@ -43,6 +43,7 @@
#include <QtMath>
#include <chrono>
#include <thread>
#include <utility>
#include "../ifc/exception/vexception.h"
#include "../ifc/xml/vlayoutconverter.h"
@ -323,10 +324,10 @@ struct VPExportData
};
//---------------------------------------------------------------------------------------------------------------------
VPMainWindow::VPMainWindow(const VPCommandLinePtr &cmd, QWidget *parent)
VPMainWindow::VPMainWindow(VPCommandLinePtr cmd, QWidget *parent)
: VAbstractMainWindow(parent),
ui(std::make_unique<Ui::VPMainWindow>()),
m_cmd(cmd),
m_cmd(std::move(cmd)),
m_undoStack(new QUndoStack(this)),
m_layout{VPLayout::CreateLayout(m_undoStack)},
m_statusLabel(new QLabel(this)),
@ -686,7 +687,14 @@ void VPMainWindow::SetupMenu()
const QString filePath = senderAction->data().toString();
if (not filePath.isEmpty())
{
LoadFile(filePath);
if (curFile.isEmpty() && !this->isWindowModified())
{
VPApplication::VApp()->MainWindow()->LoadFile(filePath);
}
else
{
VPApplication::VApp()->NewMainWindow()->LoadFile(filePath);
}
}
}
});
@ -769,101 +777,111 @@ void VPMainWindow::InitProperties()
InitPropertyTabLayout();
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::CurrentPieceShowSeamLineToggled(bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
selectedPiece->SetHideMainPath(not checked);
LayoutWasSaved(false);
// nothing changed, but will force redraw
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::ShowFullPieceToggled(bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsShowFullPiece() != checked)
{
selectedPiece->SetShowFullPiece(checked);
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::ShowMirrorLineToggled(bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsShowMirrorLine() != checked)
{
selectedPiece->SetShowMirrorLine(checked);
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::CurrentPieceVerticallyFlippedToggled(bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsVerticallyFlipped() != checked)
{
selectedPiece->FlipVertically();
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::CurrentPieceHorizontallyFlippedToggled(bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsHorizontallyFlipped() != checked)
{
selectedPiece->FlipHorizontally();
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::InitPropertyTabCurrentPiece()
{
connect(ui->checkBoxCurrentPieceShowSeamline, &QCheckBox::toggled, this,
[this](bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
selectedPiece->SetHideMainPath(not checked);
LayoutWasSaved(false);
// nothing changed, but will force redraw
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
});
connect(ui->checkBoxShowFullPiece, &QCheckBox::toggled, this,
[this](bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsShowFullPiece() != checked)
{
selectedPiece->SetShowFullPiece(checked);
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
});
connect(ui->checkBoxShowMirrorLine, &QCheckBox::toggled, this,
[this](bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsShowMirrorLine() != checked)
{
selectedPiece->SetShowMirrorLine(checked);
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
});
&VPMainWindow::CurrentPieceShowSeamLineToggled);
connect(ui->checkBoxShowFullPiece, &QCheckBox::toggled, this, &VPMainWindow::ShowFullPieceToggled);
connect(ui->checkBoxShowMirrorLine, &QCheckBox::toggled, this, &VPMainWindow::ShowMirrorLineToggled);
connect(ui->checkBoxCurrentPieceVerticallyFlipped, &QCheckBox::toggled, this,
[this](bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsVerticallyFlipped() != checked)
{
selectedPiece->FlipVertically();
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
});
&VPMainWindow::CurrentPieceVerticallyFlippedToggled);
connect(ui->checkBoxCurrentPieceHorizontallyFlipped, &QCheckBox::toggled, this,
[this](bool checked)
{
QList<VPPiecePtr> const selectedPieces = SelectedPieces();
if (selectedPieces.size() == 1)
{
const VPPiecePtr &selectedPiece = selectedPieces.constFirst();
if (not selectedPiece.isNull())
{
if (selectedPiece->IsHorizontallyFlipped() != checked)
{
selectedPiece->FlipHorizontally();
LayoutWasSaved(false);
emit m_layout->PieceTransformationChanged(selectedPiece);
}
}
}
});
&VPMainWindow::CurrentPieceHorizontallyFlippedToggled);
// Translate
ui->comboBoxTranslateUnit->addItem(tr("Millimiters"), QVariant(UnitsToStr(Unit::Mm)));
@ -1101,8 +1119,7 @@ void VPMainWindow::InitMarginsData(const QString &suffix)
LayoutWasSaved(false);
m_layout->TileFactory()->RefreshTileInfos();
m_graphicsView->RefreshLayout();
sheet->ValidatePiecesOutOfBound();
m_layout->CheckPiecesPositionValidity(sheet);
}
}
});
@ -1239,6 +1256,8 @@ void VPMainWindow::InitPropertyTabLayout()
connect(ui->checkBoxLayoutWarningPiecesSuperposition, &QCheckBox::toggled, this,
&VPMainWindow::LayoutWarningPiecesSuperposition_toggled);
connect(ui->checkBoxLayoutWarningPieceGapePosition, &QCheckBox::toggled, this,
&VPMainWindow::LayoutWarningPieceGapePosition_toggled);
connect(ui->checkBoxLayoutWarningPiecesOutOfBound, &QCheckBox::toggled, this,
&VPMainWindow::LayoutWarningPiecesOutOfBound_toggled);
connect(ui->checkBoxCutOnFold, &QCheckBox::toggled, this, &VPMainWindow::LayoutCutOnFold_toggled);
@ -1246,6 +1265,8 @@ void VPMainWindow::InitPropertyTabLayout()
connect(ui->checkBoxSheetStickyEdges, &QCheckBox::toggled, this,
[this](bool checked)
{
ui->doubleSpinBoxSheetPiecesGap->setEnabled(checked);
if (not m_layout.isNull())
{
m_layout->LayoutSettings().SetStickyEdges(checked);
@ -1256,6 +1277,9 @@ void VPMainWindow::InitPropertyTabLayout()
connect(ui->checkBoxFollowGainline, &QCheckBox::toggled, this,
[this](bool checked)
{
ui->toolButtonGrainlineHorizontalOrientation->setEnabled(ui->checkBoxFollowGainline->isChecked());
ui->toolButtonGrainlineVerticalOrientation->setEnabled(ui->checkBoxFollowGainline->isChecked());
if (not m_layout.isNull())
{
m_layout->LayoutSettings().SetFollowGrainline(checked);
@ -1347,6 +1371,7 @@ void VPMainWindow::SetPropertyTabCurrentPieceData()
SetLineEditValue(ui->lineEditCurrentPieceName, selectedPiece->GetName());
SetPlainTextEditValue(ui->plainTextEditCurrentPieceUUID, selectedPiece->GetUUID().toString());
SetLineEditValue(ui->lineEditCurrentPieceGradationId, selectedPiece->GetGradationId());
SetLineEditValue(ui->lineEditCopyNumber, QString::number(selectedPiece->CopyNumber()));
SetCheckBoxValue(ui->checkBoxCurrentPieceShowSeamline, not selectedPiece->IsHideMainPath());
SetCheckBoxValue(ui->checkBoxCurrentPieceVerticallyFlipped, selectedPiece->IsVerticallyFlipped());
@ -1454,7 +1479,9 @@ void VPMainWindow::SetPropertyTabSheetData()
GrainlineType const type = sheet->GetGrainlineType();
ui->toolButtonGrainlineHorizontalOrientation->setChecked(type == GrainlineType::Horizontal);
ui->toolButtonGrainlineHorizontalOrientation->setEnabled(ui->checkBoxFollowGainline->isChecked());
ui->toolButtonGrainlineVerticalOrientation->setChecked(type == GrainlineType::Vertical);
ui->toolButtonGrainlineVerticalOrientation->setEnabled(ui->checkBoxFollowGainline->isChecked());
// set placement grid
ui->groupBoxSheetGrid->setDisabled(false);
@ -1593,7 +1620,9 @@ void VPMainWindow::SetPropertyTabLayoutData()
m_layout->LayoutSettings().GetWarningPiecesOutOfBound());
SetCheckBoxValue(ui->checkBoxLayoutWarningPiecesSuperposition,
m_layout->LayoutSettings().GetWarningSuperpositionOfPieces());
SetCheckBoxValue(ui->checkBoxSheetStickyEdges, m_layout->LayoutSettings().GetStickyEdges());
SetCheckBoxValue(ui->checkBoxLayoutWarningPieceGapePosition,
m_layout->LayoutSettings().GetWarningPieceGapePosition());
SetCheckBoxValue(ui->checkBoxSheetStickyEdges, m_layout->LayoutSettings().IsStickyEdges());
SetCheckBoxValue(ui->checkBoxFollowGainline, m_layout->LayoutSettings().GetFollowGrainline());
SetCheckBoxValue(ui->checkBoxTogetherWithNotches, m_layout->LayoutSettings().IsBoundaryTogetherWithNotches());
SetCheckBoxValue(ui->checkBoxCutOnFold, m_layout->LayoutSettings().IsCutOnFold());
@ -1602,6 +1631,7 @@ void VPMainWindow::SetPropertyTabLayoutData()
SetDoubleSpinBoxValue(ui->doubleSpinBoxSheetPiecesGap, m_layout->LayoutSettings().GetPiecesGapConverted());
ui->doubleSpinBoxSheetPiecesGap->setSuffix(" " + UnitsToStr(LayoutUnit(), true));
ui->doubleSpinBoxSheetPiecesGap->setEnabled(ui->checkBoxSheetStickyEdges->isChecked());
ui->groupBoxLayoutScale->setDisabled(false);
@ -2108,11 +2138,9 @@ void VPMainWindow::SheetPaperSizeChanged()
{
RotatePiecesToGrainline();
}
VPSheetPtr const sheet = m_layout->GetFocusedSheet();
if (not sheet.isNull())
else
{
sheet->ValidatePiecesOutOfBound();
m_layout->CheckPiecesPositionValidity(m_layout->GetFocusedSheet());
}
}
@ -2166,6 +2194,7 @@ void VPMainWindow::FindTemplate(QComboBox *box, qreal width, qreal height)
const QSizeF tmplSize =
VAbstractLayoutDialog::GetTemplateSize(static_cast<VAbstractLayoutDialog::PaperSizeTemplate>(i), paperUnit);
if (VAbstractLayoutDialog::RoundTemplateSize(width, height, paperUnit) == tmplSize ||
// NOLINTNEXTLINE(readability-suspicious-call-argument)
VAbstractLayoutDialog::RoundTemplateSize(height, width, paperUnit) == tmplSize)
{
box->blockSignals(true);
@ -2915,7 +2944,8 @@ auto VPMainWindow::DrawTilesScheme(QPrinter *printer, QPainter *painter, const V
auto VPMainWindow::AskLayoutIsInvalid(const QList<VPSheetPtr> &sheets) -> bool
{
if (not m_layout->LayoutSettings().GetWarningPiecesOutOfBound() &&
not m_layout->LayoutSettings().GetWarningSuperpositionOfPieces())
not m_layout->LayoutSettings().GetWarningSuperpositionOfPieces() &&
not m_layout->LayoutSettings().GetWarningPieceGapePosition())
{
return true;
}
@ -2924,6 +2954,7 @@ auto VPMainWindow::AskLayoutIsInvalid(const QList<VPSheetPtr> &sheets) -> bool
{
bool outOfBoundChecked = false;
bool pieceSuperpositionChecked = false;
bool pieceGapePositionChecked = false;
QList<VPPiecePtr> const pieces = sheet->GetPieces();
for (const auto &piece : pieces)
@ -2937,6 +2968,11 @@ auto VPMainWindow::AskLayoutIsInvalid(const QList<VPSheetPtr> &sheets) -> bool
{
return false;
}
if (not CheckPieceGapePosition(piece, pieceGapePositionChecked))
{
return false;
}
}
}
@ -3003,6 +3039,37 @@ auto VPMainWindow::CheckSuperpositionOfPieces(const VPPiecePtr &piece, bool &pie
return true;
}
//---------------------------------------------------------------------------------------------------------------------
auto VPMainWindow::CheckPieceGapePosition(const VPPiecePtr &piece, bool &pieceGapePositionChecked) -> bool
{
if (m_layout->LayoutSettings().GetWarningPieceGapePosition())
{
if (not pieceGapePositionChecked && not piece.isNull() && piece->HasInvalidPieceGapPosition())
{
QMessageBox msgBox(this);
msgBox.setIcon(QMessageBox::Question);
msgBox.setWindowTitle(tr("The layout is invalid."));
msgBox.setText(tr("The layout is invalid. One or several pieces are closer than minimally allowed. Do you "
"want to continue export?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
const int width = 500;
auto *horizontalSpacer = new QSpacerItem(width, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
auto *layout = qobject_cast<QGridLayout *>(msgBox.layout());
SCASSERT(layout != nullptr)
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
if (msgBox.exec() == QMessageBox::No)
{
return false;
}
pieceGapePositionChecked = true; // no need to ask more
}
}
return true;
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::PrintLayoutSheets(QPrinter *printer, const QList<VPSheetPtr> &sheets)
{
@ -3417,18 +3484,22 @@ void VPMainWindow::TranslatePieceRelatively(const VPPiecePtr &piece, const QRect
pieceDy += dy * ((pieceRect.topLeft().y() - rect.topLeft().y()) / rect.height()) * 2.;
}
auto *command = new VPUndoPieceMove(piece, pieceDx, pieceDy);
m_layout->UndoStack()->push(command);
m_layout->UndoStack()->push(new VPUndoPieceMove(piece, pieceDx, pieceDy));
if (m_layout->LayoutSettings().GetStickyEdges())
if (m_layout->LayoutSettings().IsStickyEdges())
{
qreal stickyTranslateX = 0;
qreal stickyTranslateY = 0;
if (piece->StickyPosition(stickyTranslateX, stickyTranslateY))
{
QTime const dieTime = QTime::currentTime().addMSecs(150);
while (QTime::currentTime() < dieTime)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
}
bool const allowMerge = selectedPiecesCount == 1;
auto *stickyCommand = new VPUndoPieceMove(piece, stickyTranslateX, stickyTranslateY, allowMerge);
m_layout->UndoStack()->push(stickyCommand);
m_layout->UndoStack()->push(new VPUndoPieceMove(piece, stickyTranslateX, stickyTranslateY, allowMerge));
}
}
}
@ -3450,6 +3521,25 @@ void VPMainWindow::RotatePieces()
return;
}
auto StickyRotateToGrainline = [this](const VPPiecePtr &piece, const VPTransformationOrigon &origin)
{
QTime const dieTime = QTime::currentTime().addMSecs(150);
while (QTime::currentTime() < dieTime)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
}
if (not piece.isNull())
{
if (m_layout->LayoutSettings().GetFollowGrainline() || piece->IsFollowGrainline())
{
piece->RotateToGrainline(origin);
}
emit m_layout->PieceTransformationChanged(piece);
}
};
if (ui->checkBoxTransformSeparately->isChecked())
{
m_layout->UndoStack()->beginMacro(tr("rotate pieces"));
@ -3463,8 +3553,9 @@ void VPMainWindow::RotatePieces()
origin.origin = rect.center();
origin.custom = true;
auto *command = new VPUndoPieceRotate(piece, origin, angle, angle);
m_layout->UndoStack()->push(command);
m_layout->UndoStack()->push(new VPUndoPieceRotate(piece, origin, angle));
StickyRotateToGrainline(piece, origin);
}
}
m_layout->UndoStack()->endMacro();
@ -3478,8 +3569,12 @@ void VPMainWindow::RotatePieces()
}
VPTransformationOrigon const origin = sheet->TransformationOrigin();
auto *command = new VPUndoPiecesRotate(selectedPieces, origin, angle, angle);
m_layout->UndoStack()->push(command);
m_layout->UndoStack()->push(new VPUndoPiecesRotate(selectedPieces, origin, angle));
for (const auto &piece : qAsConst(selectedPieces))
{
StickyRotateToGrainline(piece, origin);
}
}
}
@ -3813,7 +3908,7 @@ void VPMainWindow::on_SheetMarginChanged()
LayoutWasSaved(false);
sheet->ValidatePiecesOutOfBound();
m_layout->CheckPiecesPositionValidity(sheet);
}
m_graphicsView->RefreshLayout();
@ -4806,6 +4901,21 @@ void VPMainWindow::VerticalScaleChanged(double value)
VMainGraphicsView::NewSceneRect(m_graphicsView->scene(), m_graphicsView);
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::LayoutWarningPieceGapePosition_toggled(bool checked)
{
if (not m_layout.isNull())
{
m_layout->LayoutSettings().SetWarningPieceGapePosition(checked);
LayoutWasSaved(false);
if (checked)
{
m_layout->CheckPiecesPositionValidity(m_layout->GetFocusedSheet());
}
m_graphicsView->RefreshPieces();
}
}
//---------------------------------------------------------------------------------------------------------------------
void VPMainWindow::LayoutWarningPiecesSuperposition_toggled(bool checked)
{
@ -4815,11 +4925,7 @@ void VPMainWindow::LayoutWarningPiecesSuperposition_toggled(bool checked)
LayoutWasSaved(false);
if (checked)
{
VPSheetPtr const sheet = m_layout->GetFocusedSheet();
if (not sheet.isNull())
{
sheet->ValidateSuperpositionOfPieces();
}
m_layout->CheckPiecesPositionValidity(m_layout->GetFocusedSheet());
}
m_graphicsView->RefreshPieces();
}
@ -4835,11 +4941,7 @@ void VPMainWindow::LayoutWarningPiecesOutOfBound_toggled(bool checked)
if (checked)
{
VPSheetPtr const sheet = m_layout->GetFocusedSheet();
if (not sheet.isNull())
{
sheet->ValidatePiecesOutOfBound();
}
m_layout->CheckPiecesPositionValidity(m_layout->GetFocusedSheet());
}
m_graphicsView->RefreshPieces();
}
@ -4852,15 +4954,7 @@ void VPMainWindow::LayoutCutOnFold_toggled(bool checked)
{
m_layout->LayoutSettings().SetCutOnFold(checked);
LayoutWasSaved(false);
if (checked)
{
VPSheetPtr const sheet = m_layout->GetFocusedSheet();
if (not sheet.isNull())
{
sheet->ValidatePiecesOutOfBound();
}
}
m_layout->CheckPiecesPositionValidity(m_layout->GetFocusedSheet());
m_graphicsView->RefreshLayout();
}
}

View File

@ -61,7 +61,7 @@ class VPMainWindow : public VAbstractMainWindow
Q_OBJECT // NOLINT
public:
explicit VPMainWindow(const VPCommandLinePtr &cmd, QWidget *parent = nullptr);
explicit VPMainWindow(VPCommandLinePtr cmd, QWidget *parent = nullptr);
~VPMainWindow() override;
auto CurrentFile() const -> QString;
@ -293,6 +293,7 @@ private slots:
void HorizontalScaleChanged(double value);
void VerticalScaleChanged(double value);
void LayoutWarningPieceGapePosition_toggled(bool checked);
void LayoutWarningPiecesSuperposition_toggled(bool checked);
void LayoutWarningPiecesOutOfBound_toggled(bool checked);
void LayoutCutOnFold_toggled(bool checked);
@ -300,6 +301,11 @@ private slots:
void UpdateShortcuts();
void TogetherWithNotchesChanged(bool checked);
void CurrentPieceShowSeamLineToggled(bool checked);
void ShowFullPieceToggled(bool checked);
void ShowMirrorLineToggled(bool checked);
void CurrentPieceVerticallyFlippedToggled(bool checked);
void CurrentPieceHorizontallyFlippedToggled(bool checked);
private:
Q_DISABLE_COPY_MOVE(VPMainWindow) // NOLINT
@ -501,6 +507,7 @@ private:
auto AskLayoutIsInvalid(const QList<VPSheetPtr> &sheets) -> bool;
auto CheckPiecesOutOfBound(const VPPiecePtr &piece, bool &outOfBoundChecked) -> bool;
auto CheckSuperpositionOfPieces(const VPPiecePtr &piece, bool &pieceSuperpositionChecked) -> bool;
auto CheckPieceGapePosition(const VPPiecePtr &piece, bool &pieceGapePositionChecked) -> bool;
void PrintLayoutSheets(QPrinter *printer, const QList<VPSheetPtr> &sheets);
static auto PrintLayoutSheetPage(QPrinter *printer, QPainter &painter, const VPSheetPtr &sheet) -> bool;

View File

@ -280,7 +280,7 @@
<x>0</x>
<y>0</y>
<width>378</width>
<height>721</height>
<height>752</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_23">
@ -378,6 +378,20 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Copy number:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineEditCopyNumber">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -1645,8 +1659,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>392</width>
<height>700</height>
<width>378</width>
<height>710</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_27">
@ -1720,6 +1734,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxLayoutWarningPieceGapePosition">
<property name="text">
<string>Warning piece gape position</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxSheetStickyEdges">
<property name="text">
@ -2434,8 +2455,8 @@
</resources>
<connections/>
<buttongroups>
<buttongroup name="buttonGroupRotationDirection"/>
<buttongroup name="buttonGroupSheetOrientation"/>
<buttongroup name="buttonGroupTileOrientation"/>
<buttongroup name="buttonGroupSheetOrientation"/>
<buttongroup name="buttonGroupRotationDirection"/>
</buttongroups>
</ui>

View File

@ -63,6 +63,8 @@ Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutTileShowWatermark, ("layou
// NOLINTNEXTLINE
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutWarningPiecesSuperposition,
("layout/warningPiecesSuperposition"_L1))
// NOLINTNEXTLINE
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutWarningPieceGapePosition, ("layout/warningPieceGapePosition"_L1))
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutStickyEdges, ("layout/stickyEdges"_L1)) // NOLINT
// NOLINTNEXTLINE
Q_GLOBAL_STATIC_WITH_ARGS(const QString, settingLayoutWarningPiecesOutOfBound, ("layout/warningPiecesOutOfBound"_L1))
@ -292,6 +294,18 @@ auto VPSettings::GetLayoutWarningPiecesSuperposition() const -> bool
return value(*settingLayoutWarningPiecesSuperposition, true).toBool();
}
//---------------------------------------------------------------------------------------------------------------------
void VPSettings::SetLayoutWarningPieceGapePosition(bool value)
{
setValue(*settingLayoutWarningPieceGapePosition, value);
}
//---------------------------------------------------------------------------------------------------------------------
auto VPSettings::GetLayoutWarningPieceGapePosition() const -> bool
{
return value(*settingLayoutWarningPieceGapePosition, true).toBool();
}
//---------------------------------------------------------------------------------------------------------------------
void VPSettings::SetLayoutStickyEdges(bool value)
{

View File

@ -89,6 +89,9 @@ public:
void SetLayoutWarningPiecesSuperposition(bool value);
auto GetLayoutWarningPiecesSuperposition() const -> bool;
void SetLayoutWarningPieceGapePosition(bool value);
auto GetLayoutWarningPieceGapePosition() const -> bool;
void SetLayoutStickyEdges(bool value);
auto GetLayoutStickyEdges() const -> bool;

View File

@ -333,6 +333,7 @@ void VPLayoutFileReader::ReadControl(const VPLayoutPtr &layout)
QXmlStreamAttributes const attribs = attributes();
layout->LayoutSettings().SetWarningSuperpositionOfPieces(
ReadAttributeBool(attribs, ML::AttrWarningSuperposition, trueStr));
layout->LayoutSettings().SetWarningPieceGapePosition(ReadAttributeBool(attribs, ML::AttrWarningPieceGape, trueStr));
layout->LayoutSettings().SetWarningPiecesOutOfBound(ReadAttributeBool(attribs, ML::AttrWarningOutOfBound, trueStr));
layout->LayoutSettings().SetStickyEdges(ReadAttributeBool(attribs, ML::AttrStickyEdges, trueStr));
layout->LayoutSettings().SetPiecesGap(qMax(ReadAttributeDouble(attribs, ML::AttrPiecesGap, QChar('0')), 0.0));

View File

@ -172,7 +172,8 @@ void VPLayoutFileWriter::WriteLayoutProperties(const VPLayoutPtr &layout)
writeStartElement(ML::TagControl);
SetAttribute(ML::AttrWarningSuperposition, layout->LayoutSettings().GetWarningSuperpositionOfPieces());
SetAttribute(ML::AttrWarningOutOfBound, layout->LayoutSettings().GetWarningPiecesOutOfBound());
SetAttribute(ML::AttrStickyEdges, layout->LayoutSettings().GetStickyEdges());
SetAttribute(ML::AttrWarningPieceGape, layout->LayoutSettings().GetWarningPieceGapePosition());
SetAttribute(ML::AttrStickyEdges, layout->LayoutSettings().IsStickyEdges());
SetAttribute(ML::AttrPiecesGap, layout->LayoutSettings().GetPiecesGap());
SetAttribute(ML::AttrFollowGrainline, layout->LayoutSettings().GetFollowGrainline());
SetAttribute(ML::AttrBoundaryTogetherWithNotches, layout->LayoutSettings().IsBoundaryTogetherWithNotches());

View File

@ -71,6 +71,7 @@ const QString TagMirrorLine = QStringLiteral("mirrorLine"); // NOLINT(ce
const QString AttrWarningSuperposition = QStringLiteral("warningSuperposition"); // NOLINT(cert-err58-cpp)
const QString AttrWarningOutOfBound = QStringLiteral("warningOutOfBound"); // NOLINT(cert-err58-cpp)
const QString AttrWarningPieceGape = QStringLiteral("warningPieceGape"); // NOLINT(cert-err58-cpp)
const QString AttrStickyEdges = QStringLiteral("stickyEdges"); // NOLINT(cert-err58-cpp)
const QString AttrPiecesGap = QStringLiteral("piecesGap"); // NOLINT(cert-err58-cpp)
const QString AttrVisible = QStringLiteral("visible"); // NOLINT(cert-err58-cpp)

View File

@ -70,6 +70,7 @@ extern const QString TagMirrorLine;
extern const QString AttrWarningSuperposition;
extern const QString AttrWarningOutOfBound;
extern const QString AttrWarningPieceGape;
extern const QString AttrStickyEdges;
extern const QString AttrPiecesGap;
extern const QString AttrVisible;

View File

@ -118,8 +118,9 @@ TapePreferencesConfigurationPage::TapePreferencesConfigurationPage(QWidget *pare
#if !defined(CRASH_REPORTING)
ui->groupBoxCrashReports->setDisabled(true);
#endif
ui->checkBoxSendCrashReports->setChecked(false);
ui->lineEditCrashUserEmail->setText(QString());
#else
ui->checkBoxSendCrashReports->setChecked(settings->IsSendCrashReport());
connect(ui->checkBoxSendCrashReports, &QCheckBox::stateChanged, this,
[this]() { m_sendCrashReportsChanged = true; });
@ -130,6 +131,7 @@ TapePreferencesConfigurationPage::TapePreferencesConfigurationPage(QWidget *pare
ui->lineEditCrashUserEmail->setText(settings->GetCrashEmail());
connect(ui->lineEditCrashUserEmail, &QLineEdit::editingFinished, this,
[this]() { m_crashUserEmailChanged = true; });
#endif
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -196,40 +196,41 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
{
QString debugdate = "["_L1 + QDateTime::currentDateTime().toString(QStringLiteral("yyyy.MM.dd hh:mm:ss"));
QString const file = VAbstractApplication::ReduceLogContextFilePath(context.file);
switch (type)
{
case QtDebugMsg:
debugdate += QStringLiteral(":DEBUG:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdOut() << QApplication::translate("mNoisyHandler", "DEBUG:") << logMsg << "\n";
break;
case QtWarningMsg:
debugdate += QStringLiteral(":WARNING:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdErr() << QApplication::translate("mNoisyHandler", "WARNING:") << logMsg << "\n";
break;
case QtCriticalMsg:
debugdate += QStringLiteral(":CRITICAL:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdErr() << QApplication::translate("mNoisyHandler", "CRITICAL:") << logMsg << "\n";
break;
case QtFatalMsg:
debugdate += QStringLiteral(":FATAL:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdErr() << QApplication::translate("mNoisyHandler", "FATAL:") << logMsg << "\n";
break;
case QtInfoMsg:
debugdate += QStringLiteral(":INFO:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdOut() << QApplication::translate("mNoisyHandler", "INFO:") << logMsg << "\n";
@ -613,6 +614,13 @@ void MApplication::AboutToQuit()
//---------------------------------------------------------------------------------------------------------------------
void MApplication::OpenSettings()
{
#if defined(Q_OS_WIN)
QString const docPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
if (!docPath.isEmpty())
{
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, docPath);
}
#endif
settings = new VTapeSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(),
QCoreApplication::applicationName(), this);
connect(settings, &VTapeSettings::SVGFontsPathChanged, this, &MApplication::SVGFontsPathChanged);

View File

@ -225,12 +225,13 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
{
QString debugdate = "["_L1 + QDateTime::currentDateTime().toString(QStringLiteral("yyyy.MM.dd hh:mm:ss"));
QString const file = VAbstractApplication::ReduceLogContextFilePath(context.file);
switch (type)
{
case QtDebugMsg:
debugdate += QStringLiteral(":DEBUG:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdOut() << QApplication::translate("vNoisyHandler", "DEBUG:") << logMsg << "\n";
@ -241,7 +242,7 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
VAbstractValApplication::VApp()->PostWarningMessage(logMsg, type);
}
debugdate += QStringLiteral(":WARNING:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdErr() << QApplication::translate("vNoisyHandler", "WARNING:") << logMsg << "\n";
@ -252,14 +253,14 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
VAbstractValApplication::VApp()->PostWarningMessage(logMsg, type);
}
debugdate += QStringLiteral(":CRITICAL:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdErr() << QApplication::translate("vNoisyHandler", "CRITICAL:") << logMsg << "\n";
break;
case QtFatalMsg:
debugdate += QStringLiteral(":FATAL:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdErr() << QApplication::translate("vNoisyHandler", "FATAL:") << logMsg << "\n";
@ -270,7 +271,7 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
VAbstractValApplication::VApp()->PostWarningMessage(logMsg, type);
}
debugdate += QStringLiteral(":INFO:%1(%2)] %3: %4: %5")
.arg(context.file)
.arg(file)
.arg(context.line)
.arg(context.function, context.category, logMsg);
vStdOut() << QApplication::translate("vNoisyHandler", "INFO:") << logMsg << "\n";

View File

@ -180,8 +180,9 @@ PreferencesConfigurationPage::PreferencesConfigurationPage(QWidget *parent)
#if !defined(CRASH_REPORTING)
ui->groupBoxCrashReports->setDisabled(true);
#endif
ui->checkBoxSendCrashReports->setChecked(false);
ui->lineEditCrashUserEmail->setText(QString());
#else
ui->checkBoxSendCrashReports->setChecked(settings->IsSendCrashReport());
connect(ui->checkBoxSendCrashReports, &QCheckBox::stateChanged, this,
[this]() { m_sendCrashReportsChanged = true; });
@ -192,6 +193,7 @@ PreferencesConfigurationPage::PreferencesConfigurationPage(QWidget *parent)
ui->lineEditCrashUserEmail->setText(settings->GetCrashEmail());
connect(ui->lineEditCrashUserEmail, &QLineEdit::editingFinished, this,
[this]() { m_crashUserEmailChanged = true; });
#endif
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -12,6 +12,7 @@
<xs:complexType>
<xs:attribute type="xs:boolean" name="warningSuperposition"/>
<xs:attribute type="xs:boolean" name="warningOutOfBound"/>
<xs:attribute type="xs:boolean" name="warningPieceGape"/>
<xs:attribute type="xs:boolean" name="stickyEdges"/>
<xs:attribute type="xs:boolean" name="followGrainline"/>
<xs:attribute type="xs:boolean" name="boundaryTogetherWithNotches"/>

View File

@ -153,42 +153,6 @@ inline auto LineAlign(const TextLine &tl, const QString &text, const QFontMetric
return dX;
}
//---------------------------------------------------------------------------------------------------------------------
inline auto LineMatrix(const VLayoutPiece &piece, const QPointF &topLeft, qreal angle, const QPointF &linePos,
int maxLineWidth) -> QTransform
{
QTransform labelMatrix;
labelMatrix.translate(topLeft.x(), topLeft.y());
if ((piece.IsVerticallyFlipped() && piece.IsHorizontallyFlipped()) ||
(!piece.IsVerticallyFlipped() && !piece.IsHorizontallyFlipped()))
{
labelMatrix.rotate(-angle);
}
else if (piece.IsVerticallyFlipped() || piece.IsHorizontallyFlipped())
{
if (piece.IsVerticallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(angle);
labelMatrix.translate(-maxLineWidth, 0);
}
if (piece.IsHorizontallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(angle);
labelMatrix.translate(-maxLineWidth, 0);
}
}
labelMatrix.translate(linePos.x(), linePos.y()); // Each string has own position
labelMatrix *= piece.GetMatrix();
return labelMatrix;
}
} // namespace
//---------------------------------------------------------------------------------------------------------------------
@ -1159,7 +1123,6 @@ void VDxfEngine::ExportPieceText(const QSharedPointer<dx_ifaceBlock> &detailBloc
VTextManager const tm = detail.GetPieceLabelData();
const QVector<TextLine> labelLines = tm.GetLabelSourceLines(qFloor(dW), tm.GetFont());
const int maxLineWidth = tm.MaxLineWidthOutlineFont(static_cast<int>(dW));
for (const auto &tl : labelLines)
{
@ -1174,7 +1137,7 @@ void VDxfEngine::ExportPieceText(const QSharedPointer<dx_ifaceBlock> &detailBloc
dY += fm.height() * scale / 2;
const qreal dX = LineAlign(tl, tl.m_qsText, fm, dW);
QTransform const lineMatrix = LineMatrix(detail, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth);
QTransform const lineMatrix = detail.LineMatrix(labelShape.at(0), angle, QPointF(dX, dY), dW);
QPointF const pos = lineMatrix.map(QPointF());

View File

@ -158,41 +158,6 @@ inline auto LineAlign(const TextLine &tl, const QString &text, const QFontMetric
return dX;
}
//---------------------------------------------------------------------------------------------------------------------
auto LineMatrix(const VLayoutPiece &piece, const QPointF &topLeft, qreal angle, const QPointF &linePos,
int maxLineWidth) -> QTransform
{
QTransform labelMatrix;
labelMatrix.translate(topLeft.x(), topLeft.y());
if ((piece.IsVerticallyFlipped() && piece.IsHorizontallyFlipped()) ||
(!piece.IsVerticallyFlipped() && !piece.IsHorizontallyFlipped()))
{
labelMatrix.rotate(angle);
}
else if (piece.IsVerticallyFlipped() || piece.IsHorizontallyFlipped())
{
if (piece.IsVerticallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(-angle);
labelMatrix.translate(-maxLineWidth, 0);
}
if (piece.IsHorizontallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(-angle);
labelMatrix.translate(-maxLineWidth, 0);
}
}
labelMatrix.translate(linePos.x(), linePos.y()); // Each string has own position
labelMatrix *= piece.GetMatrix();
return labelMatrix;
}
//---------------------------------------------------------------------------------------------------------------------
auto OptimizePattern(QVector<int> pattern) -> QVector<int>
{
@ -751,7 +716,6 @@ void VHPGLEngine::PlotLabelSVGFont(QTextStream &out, const VLayoutPiece &detail,
const qreal dW = QLineF(labelShape.at(0), labelShape.at(1)).length();
const qreal dH = QLineF(labelShape.at(1), labelShape.at(2)).length();
const qreal angle = -QLineF(labelShape.at(0), labelShape.at(1)).angle();
const int maxLineWidth = tm.MaxLineWidthSVGFont(static_cast<int>(dW), m_penWidthPx);
qreal dY = m_penWidthPx;
@ -770,7 +734,7 @@ void VHPGLEngine::PlotLabelSVGFont(QTextStream &out, const VLayoutPiece &detail,
const QString qsText = tl.m_qsText;
const qreal dX = LineAlign(tl, qsText, engine, dW, m_penWidthPx);
// set up the rotation around top-left corner matrix
const QTransform lineMatrix = LineMatrix(detail, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth);
const QTransform lineMatrix = detail.LineMatrix(labelShape.at(0), angle, QPointF(dX, dY), dW);
QPainterPath const path = lineMatrix.map(engine.DrawPath(QPointF(), qsText));
PlotPainterPath(out, path, Qt::SolidLine);
@ -791,7 +755,6 @@ void VHPGLEngine::PlotLabelOutlineFont(QTextStream &out, const VLayoutPiece &det
const qreal dW = QLineF(labelShape.at(0), labelShape.at(1)).length();
const qreal dH = QLineF(labelShape.at(1), labelShape.at(2)).length();
const qreal angle = -QLineF(labelShape.at(0), labelShape.at(1)).angle();
const int maxLineWidth = tm.MaxLineWidthOutlineFont(static_cast<int>(dW));
qreal dY = 0;
@ -824,7 +787,7 @@ void VHPGLEngine::PlotLabelOutlineFont(QTextStream &out, const VLayoutPiece &det
const QString qsText = tl.m_qsText;
const qreal dX = LineAlign(tl, qsText, fm, dW);
// set up the rotation around top-left corner matrix
const QTransform lineMatrix = LineMatrix(detail, labelShape.at(0), angle, QPointF(dX, dY), maxLineWidth);
const QTransform lineMatrix = detail.LineMatrix(labelShape.at(0), angle, QPointF(dX, dY), dW);
QPainterPath path;

View File

@ -1856,6 +1856,40 @@ auto VLayoutPiece::MapPassmark(VLayoutPassmark passmark, const QTransform &matri
return passmark;
}
//---------------------------------------------------------------------------------------------------------------------
auto VLayoutPiece::LineMatrix(const QPointF &topLeft, qreal angle, const QPointF &linePos, qreal maxLineWidth) const
-> QTransform
{
QTransform labelMatrix;
labelMatrix.translate(topLeft.x(), topLeft.y());
if ((IsVerticallyFlipped() && IsHorizontallyFlipped()) || (!IsVerticallyFlipped() && !IsHorizontallyFlipped()))
{
labelMatrix.rotate(angle);
}
else if (IsVerticallyFlipped() || IsHorizontallyFlipped())
{
if (IsVerticallyFlipped() && !IsHorizontallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(-angle);
labelMatrix.translate(-maxLineWidth, 0);
}
if (IsHorizontallyFlipped() && !IsVerticallyFlipped())
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(-angle);
labelMatrix.translate(-maxLineWidth, 0);
}
}
labelMatrix.translate(linePos.x(), linePos.y()); // Each string has own position
labelMatrix *= GetMatrix();
return labelMatrix;
}
//---------------------------------------------------------------------------------------------------------------------
auto VLayoutPiece::BoundingRect(QVector<QPointF> points) -> QRectF
{
@ -2046,7 +2080,7 @@ void VLayoutPiece::LabelStringsOutlineFont(QGraphicsItem *parent, const QVector<
{
labelMatrix.scale(-1, 1);
labelMatrix.rotate(-angle);
labelMatrix.translate(-tm.MaxLineWidthOutlineFont(static_cast<int>(dW)), 0);
labelMatrix.translate(-qRound(dW), 0);
}
else
{

View File

@ -240,6 +240,9 @@ public:
static auto MapPassmark(VLayoutPassmark passmark, const QTransform &matrix, bool mirror) -> VLayoutPassmark;
auto LineMatrix(const QPointF &topLeft, qreal angle, const QPointF &linePos, qreal maxLineWidth) const
-> QTransform;
protected:
void SetGrainline(const VPieceGrainline &grainline);

View File

@ -141,7 +141,7 @@ QT_WARNING_POP
*/
inline auto VPosition::FabricGrainline() const -> QLineF
{
return m_data.isOriginPaperOrientationPortrait ? QLineF(10, 10, 10, 100) : QLineF(10, 10, 100, 10);
return m_data.isOriginPaperOrientationPortrait ? QLineF(10, 10, 10, -100) : QLineF(10, 10, -100, 10);
}
#endif // VPOSITION_H

View File

@ -786,67 +786,6 @@ auto VTextManager::GetLabelSourceLines(int width, const VSvgFont &font, qreal pe
return lines;
}
//---------------------------------------------------------------------------------------------------------------------
auto VTextManager::MaxLineWidthOutlineFont(int width) const -> int
{
int maxWidth = 0;
for (int i = 0; i < m_liLines.count(); ++i)
{
const TextLine &tl = m_liLines.at(i);
QFont fnt = m_font;
fnt.setPointSize(qMax(fnt.pointSize() + tl.m_iFontSize, 1));
fnt.setBold(tl.m_bold);
fnt.setItalic(tl.m_italic);
QFontMetrics const fm(fnt);
QString qsText = tl.m_qsText;
if (fm.horizontalAdvance(qsText) > width)
{
qsText = fm.elidedText(qsText, Qt::ElideMiddle, width);
}
maxWidth = qMax(fm.horizontalAdvance(qsText), maxWidth);
}
return maxWidth;
}
//---------------------------------------------------------------------------------------------------------------------
auto VTextManager::MaxLineWidthSVGFont(int width, qreal penWidth) const -> int
{
VSvgFontDatabase *db = VAbstractApplication::VApp()->SVGFontDatabase();
VSvgFontEngine engine =
db->FontEngine(m_svgFontFamily, SVGFontStyle::Normal, SVGFontWeight::Normal, m_svgFontPointSize);
VSvgFont const svgFont = engine.Font();
int maxWidth = 0;
for (int i = 0; i < m_liLines.count(); ++i)
{
const TextLine &tl = m_liLines.at(i);
VSvgFont fnt = svgFont;
fnt.SetPointSize(fnt.PointSize() + tl.m_iFontSize);
fnt.SetBold(tl.m_bold);
fnt.SetItalic(tl.m_italic);
engine = db->FontEngine(fnt);
QString qsText = tl.m_qsText;
if (engine.TextWidth(qsText, penWidth) > width)
{
qsText = engine.ElidedText(qsText, SVGTextElideMode::ElideMiddle, width);
}
maxWidth = qMax(qRound(engine.TextWidth(qsText, penWidth)), maxWidth);
}
return maxWidth;
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief VTextManager::Update updates the text lines with detail data

View File

@ -104,9 +104,6 @@ public:
auto GetLabelSourceLines(int width, const QFont &font) const -> QVector<TextLine>;
auto GetLabelSourceLines(int width, const VSvgFont &font, qreal penWidth) const -> QVector<TextLine>;
auto MaxLineWidthOutlineFont(int width) const -> int;
auto MaxLineWidthSVGFont(int width, qreal penWidth) const -> int;
void Update(const QString &qsName, const VPieceLabelData &data, const VContainer *pattern);
void Update(VAbstractPattern *pDoc, const VContainer *pattern);

View File

@ -92,7 +92,13 @@ auto AppCrashVersion() -> QString
QString const platform = QStringLiteral("unknown");
#endif
return QStringLiteral("%1-%2-%3-%4%5").arg(version, VCS_REPO_STATE_REVISION, qtVersion, platform, multibundle);
QString revision = QStringLiteral(VCS_REPO_STATE_REVISION);
if (!revision.startsWith('g'))
{ // assume always git
revision.prepend('g');
}
return QStringLiteral("%1-%2-%3-%4%5").arg(version, revision, qtVersion, platform, multibundle);
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -33,6 +33,36 @@
#include "../projectversion.h"
#include "vcrashpaths.h"
#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
#include "../compatibility.h"
#endif
using namespace Qt::Literals::StringLiterals;
namespace
{
auto LogDirPath(const QString &appName) -> QString
{
const QString logs = QStringLiteral("Logs");
QString logDirPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
if (logDirPath.isEmpty())
{
#if defined(Q_OS_WINDOWS)
return QStringList{QCoreApplication::applicationDirPath(), logs, appName}.join(QDir::separator());
#else
return QStringList{QDir::homePath(), VER_COMPANYNAME_STR, logs, appName}.join(QDir::separator());
#endif
}
#if defined(Q_OS_WINDOWS)
QString path = QStringList{logDirPath, logs}.join(QDir::separator());
#else
QString path = QStringList{logDirPath, VER_COMPANYNAME_STR, logs, appName}.join(QDir::separator());
#endif
return path;
}
} // namespace
//---------------------------------------------------------------------------------------------------------------------
VCrashPaths::VCrashPaths(QString exeDir)
: m_exeDir(std::move(exeDir))
@ -42,10 +72,9 @@ VCrashPaths::VCrashPaths(QString exeDir)
//---------------------------------------------------------------------------------------------------------------------
auto VCrashPaths::GetAttachmentPath(const QString &appName) -> QString
{
const QString logDirPath =
QStandardPaths::locate(QStandardPaths::ConfigLocation, QString(), QStandardPaths::LocateDirectory) +
QStringLiteral(VER_COMPANYNAME_STR);
return QStringLiteral("%1/%2-pid%3.log").arg(logDirPath, appName.toLower()).arg(QCoreApplication::applicationPid());
return QStringLiteral("%1/%2-pid%3.log")
.arg(LogDirPath(appName), appName.toLower())
.arg(QCoreApplication::applicationPid());
}
//---------------------------------------------------------------------------------------------------------------------
@ -64,15 +93,25 @@ auto VCrashPaths::GetHandlerPath() const -> QString
//---------------------------------------------------------------------------------------------------------------------
auto VCrashPaths::GetReportsPath() -> QString
{
return QStandardPaths::locate(QStandardPaths::AppConfigLocation, QString(), QStandardPaths::LocateDirectory) +
#if defined(Q_OS_WINDOWS)
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QDir::separator() +
QStringList{"User Data", "Crashpad", "Reports"}.join(QDir::separator());
#else
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QDir::separator() +
QStringList{VER_COMPANYNAME_STR, "User Data", "Crashpad", "Reports"}.join(QDir::separator());
#endif
}
//---------------------------------------------------------------------------------------------------------------------
auto VCrashPaths::GetMetricsPath() -> QString
{
return QStandardPaths::locate(QStandardPaths::AppConfigLocation, QString(), QStandardPaths::LocateDirectory) +
#if defined(Q_OS_WINDOWS)
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QDir::separator() +
QStringList{"User Data", "Crashpad", "Metrics"}.join(QDir::separator());
#else
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QDir::separator() +
QStringList{VER_COMPANYNAME_STR, "User Data", "Crashpad", "Metrics"}.join(QDir::separator());
#endif
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -31,7 +31,9 @@
#include <QRegularExpression>
#include <QRegularExpressionValidator>
#if defined(CRASH_REPORTING)
#include "../vabstractapplication.h"
#endif
//---------------------------------------------------------------------------------------------------------------------
DialogAskCollectStatistic::DialogAskCollectStatistic(QWidget *parent)
@ -42,14 +44,16 @@ DialogAskCollectStatistic::DialogAskCollectStatistic(QWidget *parent)
#if !defined(CRASH_REPORTING)
ui->groupBoxCrashReports->setDisabled(true);
#endif
ui->checkBoxSendCrashReports->setChecked(false);
ui->lineEditCrashUserEmail->setText(QString());
#else
VCommonSettings *settings = VAbstractApplication::VApp()->Settings();
QRegularExpression const rx(QStringLiteral("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b"),
QRegularExpression::CaseInsensitiveOption);
ui->lineEditCrashUserEmail->setValidator(new QRegularExpressionValidator(rx, this));
ui->lineEditCrashUserEmail->setText(settings->GetCrashEmail());
#endif
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -8,7 +8,7 @@
"PointColor": "#d9d9d9",
"LabelColor": "#d9d9d9",
"LabelHoverColor": "#ffd900",
"LabelLineColor": "#80ffd900",
"LabelLineColor": [255, 217, 0, 32],
"AccuracyRadiusColor": "#ffd900",
"ControlLineColor": "#80ffd900",
"ControlPointColor": "#ffd900",

View File

@ -5,7 +5,7 @@
"PointColor": "black",
"LabelColor": "black",
"LabelHoverColor": "green",
"LabelLineColor": [0, 0, 0],
"LabelLineColor": [0, 0, 0, 16],
"AccuracyRadiusColor": "blue",
"ControlLineColor": [0, 0, 0],
"ControlPointColor": "red",

View File

@ -35,6 +35,7 @@
#include "QtConcurrent/qtconcurrentrun.h"
#include <QDir>
#include <QDirIterator>
#include <QFileSystemWatcher>
#include <QFuture>
#include <QLibraryInfo>
@ -286,6 +287,20 @@ auto VAbstractApplication::QtTranslationsPath(const QString &locale) -> QString
#endif // defined(Q_OS_LINUX)
}
//---------------------------------------------------------------------------------------------------------------------
auto VAbstractApplication::ReduceLogContextFilePath(QString path) -> QString
{
// Find the position of the 'src' folder in the path
vsizetype const srcIndex = path.indexOf(QDir::toNativeSeparators(QStringLiteral("/src/")));
if (srcIndex != -1)
{
// Extract the substring starting from 'src' folder
path = path.mid(srcIndex);
}
return path;
}
//---------------------------------------------------------------------------------------------------------------------
auto VAbstractApplication::getUndoStack() const -> QUndoStack *
{
@ -554,7 +569,7 @@ void VAbstractApplication::CheckSystemLocale()
auto CheckLanguage = [](QStandardPaths::StandardLocation type, const QStringList &test)
{
const QString path = QStandardPaths::locate(type, QString(), QStandardPaths::LocateDirectory);
const QString path = QStandardPaths::writableLocation(type);
return std::any_of(test.begin(), test.end(), [path](const QString &t) { return path.contains(t); });
};
@ -634,10 +649,28 @@ void VAbstractApplication::InitHighDpiScaling(int argc, char *argv[])
//---------------------------------------------------------------------------------------------------------------------
auto VAbstractApplication::LogDirPath() -> QString
{
const QString logDirPath =
QStandardPaths::locate(QStandardPaths::ConfigLocation, QString(), QStandardPaths::LocateDirectory) +
QCoreApplication::organizationName();
return logDirPath;
const QString logs = QStringLiteral("Logs");
QString const logDirPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
if (logDirPath.isEmpty())
{
#if defined(Q_OS_WINDOWS)
return QStringList{QCoreApplication::applicationDirPath(), logs, QCoreApplication::applicationName()}.join(
QDir::separator());
#else
return QStringList{QDir::homePath(), QCoreApplication::organizationName(), logs,
QCoreApplication::applicationName()}
.join(QDir::separator());
#endif
}
#if defined(Q_OS_WINDOWS)
QString path = QStringList{logDirPath, logs}.join(QDir::separator());
#else
QString path =
QStringList{logDirPath, QCoreApplication::organizationName(), logs, QCoreApplication::applicationName()}.join(
QDir::separator());
#endif
return path;
}
//---------------------------------------------------------------------------------------------------------------------
@ -669,7 +702,13 @@ void VAbstractApplication::ClearOldLogs()
// Restore working directory
auto restore = qScopeGuard([workingDirectory] { QDir::setCurrent(workingDirectory); });
const QStringList allFiles = logsDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
QDirIterator it(logsDir.absolutePath(), QDir::Files | QDir::NoDotAndDotDot, QDirIterator::NoIteratorFlags);
QStringList allFiles;
while (it.hasNext())
{
allFiles << it.next();
}
if (allFiles.isEmpty())
{
qDebug("There are no old logs.");

View File

@ -78,6 +78,8 @@ public:
static auto translationsPath(const QString &locale = QString()) -> QString;
static auto QtTranslationsPath(const QString &locale = QString()) -> QString;
static auto ReduceLogContextFilePath(QString path) -> QString;
void LoadTranslation(QString locale);
virtual void OpenSettings() = 0;

View File

@ -71,6 +71,13 @@ void VAbstractValApplication::PostWarningMessage(const QString &message, QtMsgTy
*/
void VAbstractValApplication::OpenSettings()
{
#if defined(Q_OS_WIN)
QString const docPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
if (!docPath.isEmpty())
{
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, docPath);
}
#endif
settings = new VValentinaSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(),
QCoreApplication::applicationName(), this);
connect(settings, &VValentinaSettings::SVGFontsPathChanged, this, &VAbstractValApplication::SVGFontsPathChanged);

View File

@ -347,7 +347,19 @@ void VCommonSettings::SetPathCustomImage(const QString &value)
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::GetDefPathSVGFonts() -> QString
{
return QDir::homePath() + QStringLiteral("/valentina/svg fonts");
QString const defPath = QStringList{QDir::homePath(), "Valentina"_L1, "Svg fonts"_L1}.join(QDir::separator());
#if defined(Q_OS_WIN)
QString const docPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
if (docPath.isEmpty())
{
return defPath;
}
return QStringList{docPath, "Valentina"_L1, "Svg fonts"_L1}.join(QDir::separator());
#else
return defPath;
#endif
}
//---------------------------------------------------------------------------------------------------------------------
@ -375,7 +387,20 @@ void VCommonSettings::SetPathSVGFonts(const QString &value)
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::GetDefPathFontCorrections() -> QString
{
return QDir::homePath() + "/valentina/font corrections"_L1;
QString const defPath =
QStringList{QDir::homePath(), "Valentina"_L1, "Font corrections"_L1}.join(QDir::separator());
#if defined(Q_OS_WIN)
QString const docPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
if (docPath.isEmpty())
{
return defPath;
}
return QStringList{docPath, "Valentina"_L1, "Font corrections"_L1}.join(QDir::separator());
#else
return defPath;
#endif
}
//---------------------------------------------------------------------------------------------------------------------
@ -396,7 +421,20 @@ void VCommonSettings::SetPathFontCorrections(const QString &value)
//---------------------------------------------------------------------------------------------------------------------
auto VCommonSettings::GetDefPathKnownMeasurements() -> QString
{
return QDir::homePath() + "/valentina/known measurements"_L1;
QString const defPath =
QStringList{QDir::homePath(), "Valentina"_L1, "Known measurements"_L1}.join(QDir::separator());
#if defined(Q_OS_WIN)
QString const docPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
if (docPath.isEmpty())
{
return defPath;
}
return QStringList{docPath, "Valentina"_L1, "Known measurements"_L1}.join(QDir::separator());
#else
return defPath;
#endif
}
//---------------------------------------------------------------------------------------------------------------------

View File

@ -2298,11 +2298,11 @@ auto VToolSeamAllowance::FindGrainlineGeometry(const VGrainlineData &geom, const
const auto centerPinPoint = VAbstractTool::data.GeometricObject<VPointF>(centerPin);
const qreal cLength = ToPixel(length, *VDataTool::data.GetPatternUnit());
QLineF grainline(centerPinPoint->x(), centerPinPoint->y(), centerPinPoint->x() + cLength / 2.0,
QLineF grainline(centerPinPoint->x(), centerPinPoint->y(), centerPinPoint->x() - cLength / 2.0,
centerPinPoint->y());
grainline.setAngle(rotationAngle);
grainline = QLineF(grainline.p2(), grainline.p1());
Swap(grainline);
grainline.setLength(cLength);
pos = grainline.p2();