mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-13 10:04:13 +02:00
Merge branch 'master' into master-modernize-signal-slot
This commit is contained in:
@@ -2,7 +2,7 @@ name: Windows Build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 2 * * 1' # Every Monday at 2:00 UTC
|
- cron: '0 2 1 * *' #
|
||||||
workflow_dispatch: # Manual trigger available at any time
|
workflow_dispatch: # Manual trigger available at any time
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@@ -343,21 +343,21 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
|
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
|
||||||
path: dist/${{ steps.zip_portable.outputs.zip_name }}
|
path: dist/${{ steps.zip_portable.outputs.zip_name }}
|
||||||
retention-days: 14
|
retention-days: 40
|
||||||
|
|
||||||
- name: Upload NSIS installer
|
- name: Upload NSIS installer
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: qelectrotech-windows-installer
|
name: qelectrotech-windows-installer
|
||||||
path: dist/Installer_*.exe
|
path: dist/Installer_*.exe
|
||||||
retention-days: 14
|
retention-days: 40
|
||||||
|
|
||||||
- name: Upload portable (nom fixe pour le workflow MSI)
|
- name: Upload portable (nom fixe pour le workflow MSI)
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: qelectrotech-windows-portable
|
name: qelectrotech-windows-portable
|
||||||
path: nsis_root/files/
|
path: nsis_root/files/
|
||||||
retention-days: 14
|
retention-days: 40
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Job 2: Qt6 build (EXPERIMENTAL track)
|
# Job 2: Qt6 build (EXPERIMENTAL track)
|
||||||
@@ -687,21 +687,21 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
|
name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse
|
||||||
path: dist/${{ steps.zip_portable.outputs.zip_name }}
|
path: dist/${{ steps.zip_portable.outputs.zip_name }}
|
||||||
retention-days: 14
|
retention-days: 40
|
||||||
|
|
||||||
- name: Upload NSIS installer
|
- name: Upload NSIS installer
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: qelectrotech-windows-installer-qt6
|
name: qelectrotech-windows-installer-qt6
|
||||||
path: dist/Installer_*.exe
|
path: dist/Installer_*.exe
|
||||||
retention-days: 14
|
retention-days: 40
|
||||||
|
|
||||||
- name: Upload portable (nom fixe pour le workflow MSI)
|
- name: Upload portable (nom fixe pour le workflow MSI)
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: qelectrotech-windows-portable-qt6
|
name: qelectrotech-windows-portable-qt6
|
||||||
path: nsis_root/files/
|
path: nsis_root/files/
|
||||||
retention-days: 14
|
retention-days: 40
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Job 3 : Publie les assets nightly (exe + zip, Qt5 et Qt6) sur la release
|
# Job 3 : Publie les assets nightly (exe + zip, Qt5 et Qt6) sur la release
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: qelectrotech-windows-msi-${{ matrix.flavor }}
|
name: qelectrotech-windows-msi-${{ matrix.flavor }}
|
||||||
path: dist\*.msi
|
path: dist\*.msi
|
||||||
retention-days: 14
|
retention-days: 40
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Delete old nightly .msi asset for this flavor
|
- name: Delete old nightly .msi asset for this flavor
|
||||||
|
|||||||
@@ -993,20 +993,47 @@ void TitleBlockTemplateView::updateDisplayedMinMaxWidth()
|
|||||||
int max_width = tbtemplate_ -> maximumWidth();
|
int max_width = tbtemplate_ -> maximumWidth();
|
||||||
|
|
||||||
QString min_max_width_sentence;
|
QString min_max_width_sentence;
|
||||||
if (max_width != -1) {
|
|
||||||
min_max_width_sentence = QString(
|
switch (static_cast<TitleBlockTemplate::WidthConstraintCase>(min_width)) {
|
||||||
tr(
|
case TitleBlockTemplate::WidthConstraintCase::RelativeWidthExceeds100Percent:
|
||||||
"Longueur minimale : %1px\nLongueur maximale : %2px\n",
|
// minimumWidth() and maximumWidth() both check infeasibility
|
||||||
"tooltip showing the minimum and/or maximum width of the edited template"
|
// first, identically, so max_width reports the same case
|
||||||
)
|
// here -- no need to check it separately.
|
||||||
).arg(min_width).arg(max_width);
|
min_max_width_sentence = tr(
|
||||||
} else {
|
"Attention : la somme des largeurs relatives dépasse 100%% de la largeur totale, ce modèle de cartouche ne peut être satisfait par aucune largeur.\n",
|
||||||
min_max_width_sentence = QString(
|
"tooltip warning shown when a template's relative-to-total-length columns alone already exceed 100%% of the total width"
|
||||||
tr(
|
);
|
||||||
"Longueur minimale : %1px\n",
|
break;
|
||||||
"tooltip showing the minimum width of the edited template"
|
case TitleBlockTemplate::WidthConstraintCase::AbsoluteColumnsExceedRemainingWidth:
|
||||||
)
|
min_max_width_sentence = tr(
|
||||||
).arg(min_width);
|
"Attention : les colonnes de largeur fixe ne peuvent pas tenir dans la largeur restante, ce modèle de cartouche ne peut être satisfait par aucune largeur.\n",
|
||||||
|
"tooltip warning shown when a template's relative-to-total-length columns already consume all available width, leaving no room for its fixed-width columns"
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (min_width != static_cast<int>(TitleBlockTemplate::WidthConstraintCase::Unconstrained)) {
|
||||||
|
min_max_width_sentence += QString(
|
||||||
|
tr(
|
||||||
|
"Longueur minimale : %1px\n",
|
||||||
|
"tooltip showing the minimum width of the edited template"
|
||||||
|
)
|
||||||
|
).arg(min_width);
|
||||||
|
}
|
||||||
|
if (max_width != static_cast<int>(TitleBlockTemplate::WidthConstraintCase::Unconstrained)) {
|
||||||
|
min_max_width_sentence += QString(
|
||||||
|
tr(
|
||||||
|
"Longueur maximale : %1px\n",
|
||||||
|
"tooltip showing the maximum width of the edited template"
|
||||||
|
)
|
||||||
|
).arg(max_width);
|
||||||
|
}
|
||||||
|
if (min_max_width_sentence.isEmpty()) {
|
||||||
|
min_max_width_sentence = tr(
|
||||||
|
"Longueur non contrainte.\n",
|
||||||
|
"tooltip shown when the edited template has neither a minimum nor a maximum width constraint"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the tooltip may also display the split label for readability purpose
|
// the tooltip may also display the split label for readability purpose
|
||||||
|
|||||||
+110
-22
@@ -960,42 +960,130 @@ int TitleBlockTemplate::columnTypeTotal(QET::TitleBlockColumnLength type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@return the minimum width for this template
|
@brief TitleBlockTemplate::classifyWidthConstraint
|
||||||
|
Classifies this template's absolute-width (ABS) and
|
||||||
|
relative-to-total-length (RTT) columns, independently of whether a
|
||||||
|
minimum or a maximum width is being computed.
|
||||||
|
|
||||||
|
The classification is derived from the same inequality
|
||||||
|
minimumWidth() solves for a minimum width:
|
||||||
|
@code
|
||||||
|
TOT >= ((sum(RTT)/100)*TOT) + sum(ABS)
|
||||||
|
@endcode
|
||||||
|
Regardless of TOT, the RTT term above scales linearly with TOT. If
|
||||||
|
sum(RTT) alone already accounts for 100% or more of TOT, no choice
|
||||||
|
of TOT -- however large -- can make room for it (and for any ABS
|
||||||
|
columns on top of it): making the template wider grows the RTT
|
||||||
|
columns' pixel footprint by the same proportion, so the shortfall
|
||||||
|
never resolves. This function exists so both minimumWidth() and
|
||||||
|
maximumWidth() report that consistently, instead of maximumWidth()
|
||||||
|
incorrectly treating a misconfigured template as "no upper bound".
|
||||||
|
|
||||||
|
@param[out] abs_total set to columnTypeTotal(QET::Absolute).
|
||||||
|
@param[out] remaining_width_fraction set to the fraction of the
|
||||||
|
total template width left over once the RTT columns have taken
|
||||||
|
their share: (100.0 - sum(RTT)) / 100.0. Zero means the RTT
|
||||||
|
columns claim the entire width, leaving nothing for ABS columns;
|
||||||
|
negative means they claim more than the entire width, which is
|
||||||
|
unsatisfiable regardless of any ABS columns. Only meaningful when
|
||||||
|
this function returns std::nullopt.
|
||||||
|
@return WidthConstraintCase::RelativeWidthExceeds100Percent if
|
||||||
|
sum(RTT) exceeds 100% (unsatisfiable at any width, with or without
|
||||||
|
ABS columns), WidthConstraintCase::AbsoluteColumnsExceedRemainingWidth
|
||||||
|
if sum(RTT) equals exactly 100% and at least one ABS column is also
|
||||||
|
present (unsatisfiable at any width, since the RTT columns leave no
|
||||||
|
room for it), WidthConstraintCase::Unconstrained if sum(RTT) equals
|
||||||
|
exactly 100% with no ABS columns (every term vanishes, any width
|
||||||
|
holds -- an ordinary, valid template), or std::nullopt if this
|
||||||
|
template's columns admit a genuine finite width.
|
||||||
|
*/
|
||||||
|
std::optional<TitleBlockTemplate::WidthConstraintCase> TitleBlockTemplate::classifyWidthConstraint(int &abs_total, qreal &remaining_width_fraction)
|
||||||
|
{
|
||||||
|
abs_total = columnTypeTotal(QET::Absolute);
|
||||||
|
remaining_width_fraction = (100.0 - columnTypeTotal(QET::RelativeToTotalLength)) / 100.0;
|
||||||
|
|
||||||
|
if (remaining_width_fraction < 0.0) {
|
||||||
|
return WidthConstraintCase::RelativeWidthExceeds100Percent;
|
||||||
|
}
|
||||||
|
if (remaining_width_fraction == 0.0) {
|
||||||
|
return abs_total == 0
|
||||||
|
? WidthConstraintCase::Unconstrained
|
||||||
|
: WidthConstraintCase::AbsoluteColumnsExceedRemainingWidth;
|
||||||
|
}
|
||||||
|
return std::nullopt; // a finite width exists: remaining_width_fraction > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief TitleBlockTemplate::minimumWidth
|
||||||
|
@return the minimum width, in pixels, required for this template's
|
||||||
|
absolute-width (ABS) and relative-to-total-length (RTT) columns to
|
||||||
|
all fit.
|
||||||
|
|
||||||
|
Derivation: writing TOT for the (variable) total template width,
|
||||||
|
the minimum size enforced by the ABS and RTT columns is:
|
||||||
|
@code
|
||||||
|
TOT >= ((sum(RTT)/100)*TOT) + sum(ABS)
|
||||||
|
=> (1 - (sum(RTT)/100))*TOT >= sum(ABS)
|
||||||
|
=> TOT >= sum(ABS) / (1 - (sum(RTT)/100))
|
||||||
|
=> TOT >= sum(ABS) / ((100 - sum(RTT))/100)
|
||||||
|
@endcode
|
||||||
|
relative-to-remaining-length (RTR) columns do not constrain the
|
||||||
|
minimum width, since by definition they only ever claim a share of
|
||||||
|
whatever space is left over after the ABS and RTT columns are laid
|
||||||
|
out.
|
||||||
|
|
||||||
|
If no finite minimum width applies, returns one of
|
||||||
|
WidthConstraintCase::Unconstrained,
|
||||||
|
WidthConstraintCase::RelativeWidthExceeds100Percent, or
|
||||||
|
WidthConstraintCase::AbsoluteColumnsExceedRemainingWidth (cast to
|
||||||
|
int) instead -- see classifyWidthConstraint() and that enum's
|
||||||
|
documentation for when each case applies.
|
||||||
*/
|
*/
|
||||||
int TitleBlockTemplate::minimumWidth()
|
int TitleBlockTemplate::minimumWidth()
|
||||||
{
|
{
|
||||||
// Abbreviations: ABS: absolute, RTT: relative to total, RTR:
|
int abs_total;
|
||||||
// relative to remaining,
|
qreal remaining_width_fraction;
|
||||||
// TOT: total diagram/TBT width (variable).
|
if (auto width_case = classifyWidthConstraint(abs_total, remaining_width_fraction)) {
|
||||||
|
return static_cast<int>(*width_case);
|
||||||
// Minimum size may be enforced by ABS and RTT widths:
|
}
|
||||||
// TOT >= ((sum(REL)/100)*TOT)+sum(ABS)
|
return qRound(abs_total / remaining_width_fraction);
|
||||||
// => (1 - (sum(REL)/100))TOT >= sum(ABS)
|
|
||||||
// => TOT >= sum(ABS) / (1 - (sum(REL)/100))
|
|
||||||
// => TOT >= sum(ABS) / ((100 - sum(REL))/100))
|
|
||||||
return(
|
|
||||||
qRound(
|
|
||||||
columnTypeTotal(QET::Absolute)
|
|
||||||
/
|
|
||||||
((100.0 - columnTypeTotal(QET::RelativeToTotalLength))
|
|
||||||
/ 100.0)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief TitleBlockTemplate::maximumWidth
|
@brief TitleBlockTemplate::maximumWidth
|
||||||
@return the maximum width for this template,
|
@return the maximum width, in pixels, this template may be
|
||||||
or -1 if it does not have any.
|
rendered at.
|
||||||
|
|
||||||
|
If this template is composed entirely of absolute-width (ABS)
|
||||||
|
columns, the maximum is fixed: the template cannot extend beyond
|
||||||
|
their sum, since nothing in it scales with the template's total
|
||||||
|
width. Otherwise, at least one column scales with the total width
|
||||||
|
(relative-to-total-length or relative-to-remaining-length), so
|
||||||
|
there is ordinarily no upper bound: returns
|
||||||
|
WidthConstraintCase::Unconstrained (cast to int) -- the common case
|
||||||
|
for most templates.
|
||||||
|
|
||||||
|
The exception is when this template's columns cannot be satisfied
|
||||||
|
by any width at all (see classifyWidthConstraint()) -- in that case
|
||||||
|
there is no width, however large, that works, and this returns
|
||||||
|
WidthConstraintCase::RelativeWidthExceeds100Percent or
|
||||||
|
WidthConstraintCase::AbsoluteColumnsExceedRemainingWidth (cast to
|
||||||
|
int) instead of Unconstrained, matching minimumWidth()'s handling
|
||||||
|
of the same cases.
|
||||||
*/
|
*/
|
||||||
int TitleBlockTemplate::maximumWidth()
|
int TitleBlockTemplate::maximumWidth()
|
||||||
{
|
{
|
||||||
|
int abs_total;
|
||||||
|
qreal remaining_width_fraction;
|
||||||
|
if (auto width_case = classifyWidthConstraint(abs_total, remaining_width_fraction)) {
|
||||||
|
return static_cast<int>(*width_case);
|
||||||
|
}
|
||||||
if (columnTypeCount(QET::Absolute) == columns_width_.count()) {
|
if (columnTypeCount(QET::Absolute) == columns_width_.count()) {
|
||||||
// The template is composed of absolute widths only,
|
// The template is composed of absolute widths only,
|
||||||
// therefore it may not extend beyond their sum.
|
// therefore it may not extend beyond their sum.
|
||||||
return(columnTypeTotal(QET::Absolute));
|
return abs_total; // already computed by classifyWidthConstraint()
|
||||||
}
|
}
|
||||||
return(-1);
|
return static_cast<int>(WidthConstraintCase::Unconstrained);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <QtSvg>
|
#include <QtSvg>
|
||||||
#include <QtXml>
|
#include <QtXml>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief The TitleBlockTemplate class
|
@brief The TitleBlockTemplate class
|
||||||
@@ -36,9 +37,73 @@
|
|||||||
*/
|
*/
|
||||||
class TitleBlockTemplate : public QObject {
|
class TitleBlockTemplate : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
@brief The TitleBlockTemplate::WidthConstraintCase enum Distinguishes
|
||||||
|
the possible outcomes of minimumWidth() or maximumWidth() for a
|
||||||
|
template's column layout: either a genuine finite pixel width, or one
|
||||||
|
of the cases below where no single finite width applies -- some of which are
|
||||||
|
perfectly ordinary (Unconstrained), and some of which indicate the
|
||||||
|
template's columns cannot be laid out at any width.
|
||||||
|
|
||||||
|
Both functions return one of the non-Bounded* cases below,
|
||||||
|
cast to int, in place of a genuine width whenever no finite
|
||||||
|
width applies. (*minimumWidth()/maximumWidth() never return
|
||||||
|
a literal "Bounded" value -- when a finite width exists,
|
||||||
|
they return that width directly. Bounded only appears as a
|
||||||
|
concept in classifyWidthConstraint()'s std::nullopt return.)
|
||||||
|
The int-based encoding exists because this project
|
||||||
|
currently targets C++17.
|
||||||
|
|
||||||
|
@todo Once this project's minimum supported C++ standard
|
||||||
|
reaches C++23, migrate minimumWidth() and maximumWidth() to
|
||||||
|
return std::expected<int, WidthConstraintCase> instead of
|
||||||
|
encoding these cases as negative int sentinels. That removes
|
||||||
|
the possibility of a caller silently misinterpreting a
|
||||||
|
sentinel as a real pixel width -- something the current
|
||||||
|
int-based encoding cannot prevent at compile time.
|
||||||
|
*/
|
||||||
|
enum class WidthConstraintCase : int {
|
||||||
|
/**
|
||||||
|
There is no finite width constraint in this direction:
|
||||||
|
any width is valid. This is a perfectly ordinary case,
|
||||||
|
not a problem with the template -- for minimumWidth(),
|
||||||
|
it happens when the relative-to-total-length (RTT)
|
||||||
|
columns account for exactly 100% of the total width and
|
||||||
|
there are no absolute-width (ABS) columns competing for
|
||||||
|
space -- every term in the minimum-width inequality
|
||||||
|
vanishes (0 >= 0), which holds for any width. For
|
||||||
|
maximumWidth(), it happens whenever at least one column
|
||||||
|
scales with the total width, so nothing caps how wide
|
||||||
|
the template may grow -- the common case for most
|
||||||
|
templates.
|
||||||
|
*/
|
||||||
|
Unconstrained = -1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
The RTT columns alone already exceed 100% of the total
|
||||||
|
width (sum(RTT) > 100), independently of whether any ABS
|
||||||
|
columns exist. The minimum-width inequality then only
|
||||||
|
holds for a non-positive width, which cannot represent a
|
||||||
|
real template: this indicates a genuinely misconfigured
|
||||||
|
template that cannot be laid out at any width.
|
||||||
|
*/
|
||||||
|
RelativeWidthExceeds100Percent = -2,
|
||||||
|
|
||||||
|
/**
|
||||||
|
The RTT columns account for exactly 100% of the total
|
||||||
|
width, but at least one ABS column also needs a nonzero,
|
||||||
|
fixed amount of space on top of that. The minimum-width
|
||||||
|
inequality reduces to "0 >= (a positive number)", which
|
||||||
|
never holds: this also indicates a genuinely
|
||||||
|
misconfigured template that cannot be laid out at any
|
||||||
|
width.
|
||||||
|
*/
|
||||||
|
AbsoluteColumnsExceedRemainingWidth = -3
|
||||||
|
};
|
||||||
|
|
||||||
// constructors, destructor
|
// constructors, destructor
|
||||||
public:
|
|
||||||
TitleBlockTemplate(QObject * = nullptr);
|
TitleBlockTemplate(QObject * = nullptr);
|
||||||
~TitleBlockTemplate() override;
|
~TitleBlockTemplate() override;
|
||||||
private:
|
private:
|
||||||
@@ -146,6 +211,20 @@ class TitleBlockTemplate : public QObject {
|
|||||||
bool checkCell(const QDomElement &, TitleBlockCell ** = nullptr);
|
bool checkCell(const QDomElement &, TitleBlockCell ** = nullptr);
|
||||||
void flushCells();
|
void flushCells();
|
||||||
void initCells();
|
void initCells();
|
||||||
|
/**
|
||||||
|
@brief TitleBlockTemplate::classifyWidthConstraint Classifies this template's absolute-width (ABS) and
|
||||||
|
relative-to-total-length (RTT) columns, independently of whether a minimum or a maximum width is being computed -- see
|
||||||
|
minimumWidth() for the derivation this is based on.
|
||||||
|
@param[out] abs_total set to columnTypeTotal(QET::Absolute).
|
||||||
|
@param[out] remaining_width_fraction set to the fraction of the total template width left over once the RTT columns have taken
|
||||||
|
their share: (100.0 - sum(RTT)) / 100.0. Zero means the RTT columns claim the entire width, leaving nothing for ABS
|
||||||
|
columns; negative means they claim more than the entire width, which is unsatisfiable regardless of any ABS columns. Only
|
||||||
|
meaningful when this function returns std::nullopt.
|
||||||
|
@return WidthConstraintCase::RelativeWidthExceeds100Percent, WidthConstraintCase::AbsoluteColumnsExceedRemainingWidth, or
|
||||||
|
WidthConstraintCase::Unconstrained if no finite width exists for this template's columns, or std::nullopt if a finite width
|
||||||
|
does exist (computable as qRound(abs_total / remaining_width_fraction)).
|
||||||
|
*/
|
||||||
|
std::optional<WidthConstraintCase> classifyWidthConstraint(int &abs_total, qreal &remaining_width_fraction);
|
||||||
int lengthRange(int, int, const QList<int> &) const;
|
int lengthRange(int, int, const QList<int> &) const;
|
||||||
QString finalTextForCell(
|
QString finalTextForCell(
|
||||||
const TitleBlockCell &,
|
const TitleBlockCell &,
|
||||||
|
|||||||
Reference in New Issue
Block a user