#include "params/ParamControls.h" #include "widgets/CompactNumericSpinBox.h" #include #include #include #include #include #include #include #include #include #include namespace gui { Float01ParamControl::Float01ParamControl(QWidget* parent) : QWidget(parent) { auto* row = new QHBoxLayout(this); row->setContentsMargins(0, 0, 0, 0); row->setSpacing(4); m_slider = new QSlider(Qt::Horizontal, this); m_slider->setRange(0, 1000); m_slider->setSingleStep(1); m_slider->setPageStep(10); row->addWidget(m_slider, 1); m_spin = new CompactDoubleSpinBox(this, 52); m_spin->setRange(0.0, 1.0); m_spin->setDecimals(3); m_spin->setSingleStep(0.01); row->addWidget(m_spin, 0); connect(m_slider, &QSlider::valueChanged, this, [this]() { syncFromSlider(); }); connect(m_spin, qOverload(&QDoubleSpinBox::valueChanged), this, [this]() { syncFromSpin(); }); setValue01(0.5); } void Float01ParamControl::setEnabled(bool on) { QWidget::setEnabled(on); if (m_slider) m_slider->setEnabled(on); if (m_spin) m_spin->setEnabled(on); } double Float01ParamControl::value01() const { return m_spin ? m_spin->value() : 0.5; } void Float01ParamControl::setValue01(double v) { const double clamped = std::clamp(v, 0.0, 1.0); m_block = true; if (m_spin) m_spin->setValue(clamped); if (m_slider) m_slider->setValue(static_cast(std::lround(clamped * 1000.0))); m_block = false; } void Float01ParamControl::syncFromSlider() { if (m_block || !m_slider || !m_spin) return; m_block = true; const double v = static_cast(m_slider->value()) / 1000.0; m_spin->setValue(v); m_block = false; emit valueChanged01(v); } void Float01ParamControl::syncFromSpin() { if (m_block || !m_slider || !m_spin) return; m_block = true; const double v = m_spin->value(); m_slider->setValue(static_cast(std::lround(v * 1000.0))); m_block = false; emit valueChanged01(v); } Vec2ParamControl::Vec2ParamControl(QWidget* parent) : QWidget(parent) { auto* row = new QHBoxLayout(this); row->setContentsMargins(0, 0, 0, 0); row->setSpacing(3); constexpr int kMaxW = 72; m_sx = new CompactIntSpinBox(this, kMaxW); m_sx->setRange(-10'000'000, 10'000'000); m_sx->setSingleStep(1); m_sx->setAccelerated(true); row->addWidget(m_sx, 0); m_sy = new CompactIntSpinBox(this, kMaxW); m_sy->setRange(-10'000'000, 10'000'000); m_sy->setSingleStep(1); m_sy->setAccelerated(true); row->addWidget(m_sy, 0); row->addStretch(1); connect(m_sx, qOverload(&QSpinBox::valueChanged), this, [this]() { emitIfChanged(); }); connect(m_sy, qOverload(&QSpinBox::valueChanged), this, [this]() { emitIfChanged(); }); setValue(0, 0); } void Vec2ParamControl::setEnabled(bool on) { QWidget::setEnabled(on); if (m_sx) m_sx->setEnabled(on); if (m_sy) m_sy->setEnabled(on); } void Vec2ParamControl::setValue(int x, int y) { m_block = true; if (m_sx) m_sx->setValue(x); if (m_sy) m_sy->setValue(y); m_lastX = x; m_lastY = y; m_block = false; } int Vec2ParamControl::x() const { return m_sx ? m_sx->value() : 0; } int Vec2ParamControl::y() const { return m_sy ? m_sy->value() : 0; } bool Vec2ParamControl::isActivelyEditing() const { QWidget* fw = QApplication::focusWidget(); for (QWidget* w = fw; w; w = w->parentWidget()) { if (w == this) { return true; } } return false; } void Vec2ParamControl::emitIfChanged() { if (m_block || !m_sx || !m_sy) return; const int nx = m_sx->value(); const int ny = m_sy->value(); if (nx == m_lastX && ny == m_lastY) return; m_lastX = nx; m_lastY = ny; emit valueChanged(nx, ny); } Vec2DoubleParamControl::Vec2DoubleParamControl(QWidget* parent) : QWidget(parent) { auto* row = new QHBoxLayout(this); row->setContentsMargins(0, 0, 0, 0); row->setSpacing(4); // 属性栏里的数值输入框不要太宽,否则会挤占面板空间。 // 坐标/中心等字段通常是整数显示,因此固定更紧凑的宽度即可(仍允许键盘输入)。 constexpr int kMaxW = 78; auto makeSpin = [kMaxW](QDoubleSpinBox* box) { box->setObjectName(QStringLiteral("CompactNumericSpin")); box->setButtonSymbols(QAbstractSpinBox::UpDownArrows); box->setFocusPolicy(Qt::StrongFocus); box->setKeyboardTracking(false); // 坐标轴/位置统一用整数(仍可直接键盘输入) box->setDecimals(0); box->setRange(-1.0e7, 1.0e7); box->setSingleStep(1.0); box->setAccelerated(true); box->setMinimumWidth(60); box->setMaximumWidth(kMaxW); box->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); }; m_sx = new QDoubleSpinBox(this); makeSpin(m_sx); row->addWidget(m_sx, 0); m_sy = new QDoubleSpinBox(this); makeSpin(m_sy); row->addWidget(m_sy, 0); connect(m_sx, qOverload(&QDoubleSpinBox::valueChanged), this, [this]() { emitIfChanged(); }); connect(m_sy, qOverload(&QDoubleSpinBox::valueChanged), this, [this]() { emitIfChanged(); }); setValue(0.0, 0.0); } void Vec2DoubleParamControl::setEnabled(bool on) { QWidget::setEnabled(on); if (m_sx) m_sx->setEnabled(on); if (m_sy) m_sy->setEnabled(on); } void Vec2DoubleParamControl::setValue(double x, double y) { m_block = true; const double rx = std::round(x); const double ry = std::round(y); if (m_sx) m_sx->setValue(rx); if (m_sy) m_sy->setValue(ry); m_lastX = rx; m_lastY = ry; m_block = false; } double Vec2DoubleParamControl::x() const { return m_sx ? m_sx->value() : 0.0; } double Vec2DoubleParamControl::y() const { return m_sy ? m_sy->value() : 0.0; } bool Vec2DoubleParamControl::isActivelyEditing() const { QWidget* fw = QApplication::focusWidget(); for (QWidget* w = fw; w; w = w->parentWidget()) { if (w == this) { return true; } } return false; } void Vec2DoubleParamControl::emitIfChanged() { if (m_block || !m_sx || !m_sy) return; const double nx = m_sx->value(); const double ny = m_sy->value(); if (qFuzzyCompare(nx + 1.0, m_lastX + 1.0) && qFuzzyCompare(ny + 1.0, m_lastY + 1.0)) return; m_lastX = nx; m_lastY = ny; emit valueChanged(nx, ny); } } // namespace gui