Gui: improve quantitySpinbox / Sketcher OVP (#25978)
* Gui: fix quantity spinbox size not adapting to number of decimals * Gui: fix quantity spinbox units getting cut (force max chars) * Gui: editableDatumLabel remove hardcoded values * fixes * Gui: sketcher ovp auto adjust size * Gui: ovp fix cursor jumping behind the unit * Gui: Refactor changes to QuantitySpinBox The QuantitySpinBox uses d-pointer (pimpl) to store its private data - this commit ensures that all state is stored here and exposed via getters and setters + qproperties. * Gui: sketcher ovp fix cursor jumping after the unit * Gui: allow quantitySpinBox to set max digits allowed Useful for adjustable width in the Sketcher OVP --------- Co-authored-by: Kacper Donat <kadet1090@gmail.com>
This commit is contained in:
@@ -29,9 +29,11 @@
|
||||
#include <Inventor/nodes/SoSwitch.h>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QFontMetrics>
|
||||
#include <QKeyEvent>
|
||||
#include <QPixmap>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QHBoxLayout>
|
||||
#include <QString>
|
||||
|
||||
@@ -41,6 +43,8 @@
|
||||
#include <Gui/View3DInventorViewer.h>
|
||||
|
||||
#include "EditableDatumLabel.h"
|
||||
#include "Base/Console.h"
|
||||
#include "Gui/QuantitySpinBox.h"
|
||||
|
||||
|
||||
using namespace Gui;
|
||||
@@ -175,6 +179,8 @@ void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool
|
||||
spinBox->setAutoNormalize(false);
|
||||
spinBox->setKeyboardTracking(true);
|
||||
spinBox->installEventFilter(this);
|
||||
spinBox->setAutoAdjustWidth(true);
|
||||
spinBox->setMaxExpectedDigits(16);
|
||||
|
||||
if (eventFilteringObj) {
|
||||
spinBox->installEventFilter(eventFilteringObj);
|
||||
@@ -185,10 +191,7 @@ void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool
|
||||
}
|
||||
|
||||
spinBox->show();
|
||||
setSpinboxValue(val);
|
||||
// Note: adjustSize apparently uses the Min/Max values to set the size. So if we don't set them
|
||||
// to INT_MAX, the spinbox are much too big.
|
||||
spinBox->adjustSize();
|
||||
updateGeometry();
|
||||
setFocusToSpinbox();
|
||||
|
||||
const auto validateAndFinish = [this]() {
|
||||
@@ -217,6 +220,9 @@ void EditableDatumLabel::startEdit(double val, QObject* eventFilteringObj, bool
|
||||
};
|
||||
|
||||
connect(spinBox, qOverload<double>(&QuantitySpinBox::valueChanged), this, validateAndFinish);
|
||||
if (auto* edit = spinBox->findChild<QLineEdit*>()) {
|
||||
connect(edit, &QLineEdit::textChanged, this, &EditableDatumLabel::updateGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditableDatumLabel::eventFilter(QObject* watched, QEvent* event)
|
||||
@@ -364,8 +370,9 @@ void EditableDatumLabel::positionSpinbox()
|
||||
|
||||
// Update lock icon position inside the spinbox if it exists and is visible
|
||||
if (lockIconLabel && lockIconLabel->isVisible()) {
|
||||
int iconSize = 14;
|
||||
int padding = 4;
|
||||
const QFontMetrics fm(spinBox->fontMetrics());
|
||||
int iconSize = fm.height();
|
||||
int padding = spinBox->getMargin();
|
||||
QSize spinboxSize = spinBox->size();
|
||||
lockIconLabel->setGeometry(
|
||||
spinboxSize.width() - iconSize - padding,
|
||||
@@ -424,6 +431,14 @@ void EditableDatumLabel::setPlacement(const Base::Placement& plc)
|
||||
label->norm.setValue(SbVec3f(float(RN.x), float(RN.y), float(RN.z)));
|
||||
}
|
||||
|
||||
void EditableDatumLabel::updateGeometry()
|
||||
{
|
||||
if (!spinBox) {
|
||||
return;
|
||||
}
|
||||
spinBox->adjustSize();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void EditableDatumLabel::setColor(SbColor color)
|
||||
{
|
||||
@@ -510,56 +525,47 @@ void EditableDatumLabel::setSpinboxVisibleToMouse(bool val)
|
||||
|
||||
void EditableDatumLabel::setLockedAppearance(bool locked)
|
||||
{
|
||||
if (!locked) {
|
||||
this->hasFinishedEditing = false;
|
||||
}
|
||||
if (!spinBox) {
|
||||
return;
|
||||
}
|
||||
const QFontMetrics fm(spinBox->fontMetrics());
|
||||
int iconSize = fm.height();
|
||||
int padding = spinBox->getMargin();
|
||||
if (locked) {
|
||||
if (spinBox) {
|
||||
spinBox->addIconSpace(true);
|
||||
spinBox->adjustSize();
|
||||
// create lock icon label it it doesn't exist, if it does - show it
|
||||
if (!lockIconLabel) {
|
||||
lockIconLabel = new QLabel(spinBox);
|
||||
lockIconLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
lockIconLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
|
||||
// create lock icon label it it doesn't exist, if it does - show it
|
||||
if (!lockIconLabel) {
|
||||
lockIconLabel = new QLabel(spinBox);
|
||||
lockIconLabel->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
lockIconLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
// load icon and scale it to fit in spinbox
|
||||
QPixmap lockIcon = Gui::BitmapFactory().pixmap("Constraint_Lock");
|
||||
QPixmap scaledIcon
|
||||
= lockIcon.scaled(iconSize, iconSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
lockIconLabel->setPixmap(scaledIcon);
|
||||
|
||||
// load icon and scale it to fit in spinbox
|
||||
QPixmap lockIcon = Gui::BitmapFactory().pixmap("Constraint_Lock");
|
||||
QPixmap scaledIcon
|
||||
= lockIcon.scaled(14, 14, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
lockIconLabel->setPixmap(scaledIcon);
|
||||
|
||||
// position lock icon inside the spinbox
|
||||
int iconSize = 14;
|
||||
int padding = 4;
|
||||
QSize spinboxSize = spinBox->size();
|
||||
lockIconLabel->setGeometry(
|
||||
spinboxSize.width() - iconSize - padding,
|
||||
(spinboxSize.height() - iconSize) / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
// style spinbox and add padding for lock
|
||||
QString styleSheet = QString::fromLatin1(
|
||||
"QSpinBox { "
|
||||
"padding-right: %1px; "
|
||||
"}"
|
||||
)
|
||||
.arg(iconSize + padding + 2);
|
||||
|
||||
spinBox->setStyleSheet(styleSheet);
|
||||
}
|
||||
|
||||
lockIconLabel->show();
|
||||
// position lock icon inside the spinbox
|
||||
QSize spinboxSize = spinBox->size();
|
||||
lockIconLabel->setGeometry(
|
||||
spinboxSize.width() - iconSize - padding,
|
||||
(spinboxSize.height() - iconSize) / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
}
|
||||
lockIconLabel->show();
|
||||
}
|
||||
else {
|
||||
this->hasFinishedEditing = false;
|
||||
|
||||
// if spinbox exists, reset its appearance
|
||||
if (spinBox) {
|
||||
spinBox->setStyleSheet(QString());
|
||||
|
||||
// hide lock icon if it exists for later reuse
|
||||
if (lockIconLabel) {
|
||||
lockIconLabel->hide();
|
||||
}
|
||||
// hide lock icon if it exists for later reuse
|
||||
if (lockIconLabel) {
|
||||
lockIconLabel->hide();
|
||||
spinBox->addIconSpace(false);
|
||||
spinBox->adjustSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ public:
|
||||
void setLockedAppearance(bool locked); ///< Sets visual appearance to indicate locked state
|
||||
///< (finished editing)
|
||||
void resetLockedState(); ///< Resets both hasFinishedEditing flag and locked appearance
|
||||
void updateGeometry();
|
||||
|
||||
Function getFunction();
|
||||
|
||||
|
||||
+99
-26
@@ -66,6 +66,9 @@ public:
|
||||
, pendingEmit(false)
|
||||
, normalize(true)
|
||||
, checkRangeInExpression(false)
|
||||
, adjustableWidth(false)
|
||||
, maxExpectedDigits(4)
|
||||
, addIconSpace(false)
|
||||
, unitValue(0)
|
||||
, maximum(std::numeric_limits<double>::max())
|
||||
, minimum(-std::numeric_limits<double>::max())
|
||||
@@ -306,6 +309,9 @@ public:
|
||||
bool pendingEmit;
|
||||
bool normalize;
|
||||
bool checkRangeInExpression;
|
||||
bool adjustableWidth;
|
||||
int maxExpectedDigits;
|
||||
bool addIconSpace;
|
||||
QString validStr;
|
||||
Base::Quantity quantity;
|
||||
Base::Quantity cached;
|
||||
@@ -341,6 +347,9 @@ void QuantitySpinBox::bind(const App::ObjectIdentifier& _path)
|
||||
|
||||
void QuantitySpinBox::showIcon()
|
||||
{
|
||||
addIconSpace(true);
|
||||
|
||||
adjustSize();
|
||||
iconLabel->show();
|
||||
}
|
||||
|
||||
@@ -444,6 +453,19 @@ void QuantitySpinBox::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QAbstractSpinBox::resizeEvent(event);
|
||||
resizeWidget();
|
||||
moveCursor();
|
||||
}
|
||||
|
||||
void QuantitySpinBox::moveCursor()
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
QLineEdit* edit = lineEdit();
|
||||
int cursor = edit->cursorPosition();
|
||||
const int maxCursor = qMax(0, edit->displayText().size() - 1 /*space*/ - d->unitStr.size());
|
||||
|
||||
if (cursor > maxCursor) {
|
||||
edit->setCursorPosition(maxCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent* event)
|
||||
@@ -467,6 +489,9 @@ void Gui::QuantitySpinBox::keyPressEvent(QKeyEvent* event)
|
||||
if (isEnter) {
|
||||
returnPressed();
|
||||
}
|
||||
else {
|
||||
moveCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void Gui::QuantitySpinBox::paintEvent(QPaintEvent*)
|
||||
@@ -504,7 +529,7 @@ void QuantitySpinBox::updateEdit(const QString& text)
|
||||
edit->setSelection(0, cursor);
|
||||
}
|
||||
else {
|
||||
edit->setCursorPosition(empty ? 0 : cursor);
|
||||
moveCursor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,6 +626,42 @@ void QuantitySpinBox::setAutoNormalize(bool normalize)
|
||||
d->normalize = normalize;
|
||||
}
|
||||
|
||||
bool QuantitySpinBox::autoAdjustWidth() const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
return d->adjustableWidth;
|
||||
}
|
||||
|
||||
void QuantitySpinBox::setAutoAdjustWidth(bool adjust)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
d->adjustableWidth = adjust;
|
||||
}
|
||||
|
||||
bool QuantitySpinBox::isIconSpaceAdded() const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
return d->addIconSpace;
|
||||
}
|
||||
|
||||
void QuantitySpinBox::addIconSpace(bool addIconSpace)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
d->addIconSpace = addIconSpace;
|
||||
}
|
||||
|
||||
int QuantitySpinBox::getMaxExpectedDigits()
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
return d->maxExpectedDigits;
|
||||
}
|
||||
|
||||
void QuantitySpinBox::setMaxExpectedDigits(int digits)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
d->maxExpectedDigits = digits;
|
||||
}
|
||||
|
||||
bool QuantitySpinBox::hasValidInput() const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
@@ -611,7 +672,6 @@ bool QuantitySpinBox::hasValidInput() const
|
||||
void QuantitySpinBox::userInput(const QString& text)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
|
||||
d->pendingEmit = true;
|
||||
|
||||
QString tmp = text;
|
||||
@@ -911,44 +971,57 @@ QSize QuantitySpinBox::sizeForText(const QString& txt) const
|
||||
|
||||
QSize QuantitySpinBox::sizeHint() const
|
||||
{
|
||||
return sizeHintCalculator(lineEdit()->sizeHint().height());
|
||||
Q_D(const QuantitySpinBox);
|
||||
auto le = lineEdit();
|
||||
if (le && d->adjustableWidth) {
|
||||
// limit number of typed characters to keep unit visible
|
||||
le->setMaxLength(getMaxStrLength(d->maxExpectedDigits));
|
||||
}
|
||||
return sizeHintForDigits(d->maxExpectedDigits);
|
||||
}
|
||||
|
||||
QSize QuantitySpinBox::minimumSizeHint() const
|
||||
{
|
||||
return sizeHintCalculator(lineEdit()->minimumSizeHint().height());
|
||||
}
|
||||
|
||||
QSize QuantitySpinBox::sizeHintCalculator(int h) const
|
||||
QSize QuantitySpinBox::sizeHintForDigits(int digits) const
|
||||
{
|
||||
Q_D(const QuantitySpinBox);
|
||||
ensurePolished();
|
||||
|
||||
const int maxLen = getMaxStrLength(digits);
|
||||
int length = maxLen;
|
||||
|
||||
if (d->adjustableWidth) {
|
||||
int currenLen = qMax(lineEdit()->text().length(), 4);
|
||||
length = currenLen < maxLen ? currenLen : maxLen;
|
||||
}
|
||||
|
||||
QString longestString = QStringLiteral("8").repeated(length);
|
||||
|
||||
const QFontMetrics fm(fontMetrics());
|
||||
int w = 0;
|
||||
constexpr int maxStrLen = 9;
|
||||
|
||||
QString s;
|
||||
QString fixedContent = QLatin1String(" ");
|
||||
|
||||
Base::Quantity q(d->quantity);
|
||||
q.setValue(d->maximum);
|
||||
s = textFromValue(q);
|
||||
s.truncate(maxStrLen);
|
||||
s += fixedContent;
|
||||
w = qMax(w, QtTools::horizontalAdvance(fm, s));
|
||||
|
||||
w += 2; // cursor blinking space
|
||||
w += iconHeight;
|
||||
|
||||
int w = qMax(0, QtTools::horizontalAdvance(fm, longestString));
|
||||
w += 4; // cursor blinking space
|
||||
if (d->addIconSpace) {
|
||||
w += iconHeight;
|
||||
}
|
||||
QStyleOptionSpinBox opt;
|
||||
initStyleOption(&opt);
|
||||
QSize hint(w, h);
|
||||
QSize hint(w, lineEdit()->sizeHint().height());
|
||||
|
||||
QSize size = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);
|
||||
return size;
|
||||
}
|
||||
|
||||
int QuantitySpinBox::getMaxStrLength(int digits) const
|
||||
{
|
||||
// Calculates the length of the longest string allowed
|
||||
Q_D(const QuantitySpinBox);
|
||||
ensurePolished();
|
||||
QString unit = QString::fromStdString(d->unit.getString());
|
||||
int decimals = App::GetApplication()
|
||||
.GetUserParameter()
|
||||
.GetGroup("BaseApp/Preferences/Units")
|
||||
->GetInt("Decimals", 2);
|
||||
return digits + 1 /*separator*/ + decimals + 1 /*space*/ + unit.length();
|
||||
}
|
||||
|
||||
void QuantitySpinBox::showEvent(QShowEvent* event)
|
||||
{
|
||||
Q_D(QuantitySpinBox);
|
||||
|
||||
@@ -54,6 +54,15 @@ class GuiExport QuantitySpinBox: public QAbstractSpinBox, public ExpressionSpinB
|
||||
Q_PROPERTY(
|
||||
bool autoNormalize READ autoNormalize WRITE setAutoNormalize
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
Q_PROPERTY(
|
||||
bool autoAdjustWidth READ autoAdjustWidth WRITE setAutoAdjustWidth
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
Q_PROPERTY(
|
||||
bool addIconSpace READ isIconSpaceAdded WRITE addIconSpace
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
Q_PROPERTY(
|
||||
int maxExpectedDigits READ getMaxExpectedDigits WRITE setMaxExpectedDigits
|
||||
) // clazy:exclude=qproperty-without-notify
|
||||
|
||||
public:
|
||||
explicit QuantitySpinBox(QWidget* parent = nullptr);
|
||||
@@ -99,6 +108,11 @@ public:
|
||||
/// Sets the value of the maximum property
|
||||
void setMaximum(double max);
|
||||
|
||||
/// Adjust how many digits in the integer part we should expect
|
||||
/// Affects minimum size if width is not adjustable
|
||||
void setMaxExpectedDigits(int digits);
|
||||
int getMaxExpectedDigits();
|
||||
|
||||
/// Gets the number of decimals
|
||||
int decimals() const;
|
||||
/// Sets the number of decimals
|
||||
@@ -109,6 +123,16 @@ public:
|
||||
/// Enables or disables automatic normalization on enter
|
||||
void setAutoNormalize(bool normalize);
|
||||
|
||||
/// Returns if automatic width adjustment is enabled for this input
|
||||
bool autoAdjustWidth() const;
|
||||
/// Enables or disables automatic width adjustement
|
||||
void setAutoAdjustWidth(bool adjust);
|
||||
|
||||
/// Returns if icon space is added for this input
|
||||
bool isIconSpaceAdded() const;
|
||||
/// Enables or disables icon space addition
|
||||
void addIconSpace(bool addIconSpace);
|
||||
|
||||
/// Sets a specific unit schema to handle quantities.
|
||||
/// The system-wide schema won't be used any more.
|
||||
void setSchema(int s);
|
||||
@@ -142,7 +166,6 @@ public:
|
||||
/// This is a helper function to determine the size this widget requires to fully display the text
|
||||
QSize sizeForText(const QString&) const;
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
void setNumberExpression(App::NumberExpression*) override;
|
||||
@@ -175,13 +198,16 @@ protected:
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
void moveCursor();
|
||||
void validateInput() override;
|
||||
void updateText(const Base::Quantity&);
|
||||
void updateEdit(const QString& text);
|
||||
void updateFromCache(bool notify, bool updateUnit = true);
|
||||
QString getUserString(const Base::Quantity& val, double& factor, QString& unitString) const;
|
||||
QString getUserString(const Base::Quantity& val) const;
|
||||
QSize sizeHintCalculator(int height) const;
|
||||
|
||||
QSize sizeHintForDigits(int digits) const;
|
||||
int getMaxStrLength(int digits) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/** Gets emitted if the user has entered a VALID input
|
||||
|
||||
+1
-1
@@ -45,6 +45,7 @@ public:
|
||||
|
||||
void bind(const App::ObjectIdentifier& _path) override;
|
||||
void setExpression(std::shared_ptr<App::Expression> expr) override;
|
||||
int getMargin();
|
||||
|
||||
protected:
|
||||
/*! Expression handling */
|
||||
@@ -65,7 +66,6 @@ protected:
|
||||
virtual void showIcon();
|
||||
virtual void validateInput();
|
||||
void resizeWidget();
|
||||
int getMargin();
|
||||
|
||||
bool handleKeyEvent(const QString&);
|
||||
virtual void openFormulaDialog();
|
||||
|
||||
Reference in New Issue
Block a user