#include "AxesSettingsPanel.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "Theme.hpp" namespace geopro::app { namespace { // 点击轨道直接定位到点击处(默认 QSlider 点轨道只按 pageStep 步进;用户要求点哪跳哪)。 class ClickJumpSlider : public QSlider { public: using QSlider::QSlider; protected: void mousePressEvent(QMouseEvent* e) override { if (e->button() == Qt::LeftButton && orientation() == Qt::Horizontal) { QStyleOptionSlider opt; initStyleOption(&opt); const QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); if (!handle.contains(e->pos())) { // 点在轨道(非手柄) → 跳到点击位置 const int span = width() - handle.width(); const int x = e->pos().x() - handle.width() / 2; setValue(QStyle::sliderValueFromPosition(minimum(), maximum(), x, span)); e->accept(); return; } } QSlider::mousePressEvent(e); // 点手柄 → 正常拖动 } }; } // namespace AxesSettingsPanel::AxesSettingsPanel(QWidget* parent) : QFrame(parent) { setFrameShape(QFrame::StyledPanel); applyTokenizedStyleSheet( this, QStringLiteral("QFrame{background:{{bg/panel}};border:1px solid {{border/default}};" "border-radius:8px;}")); // radius/lg=8(规范§3.2 画布浮窗) setFixedWidth(320); auto* v = new QVBoxLayout(this); v->setContentsMargins(space::kMd, space::kMd, space::kMd, space::kMd); v->setSpacing(space::kSm); // 标题行:坐标轴设置 + × 关闭。 auto* titleRow = new QHBoxLayout(); auto* title = new QLabel(QStringLiteral("坐标轴设置"), this); title->setStyleSheet(QStringLiteral("font-weight:600;font-size:15px;border:none;")); auto* close = new QPushButton(QStringLiteral("✕"), this); close->setFixedSize(24, 24); close->setCursor(Qt::PointingHandCursor); // 显式覆盖全局 QPushButton 的 padding(6px 14px)/border——否则 24×24 容不下 padding,× 被挤出不可见。 // 颜色走 token(避免深色模式失效)。 applyTokenizedStyleSheet( close, QStringLiteral( "QPushButton{border:none;background:transparent;padding:0;margin:0;font-size:16px;" "color:{{text/secondary}};}" "QPushButton:hover{color:{{accent/primary}};}")); connect(close, &QPushButton::clicked, this, &AxesSettingsPanel::closed); titleRow->addWidget(title); titleRow->addStretch(1); titleRow->addWidget(close); v->addLayout(titleRow); // 坐标轴单位。 auto* unitLbl = new QLabel(QStringLiteral("坐标轴单位"), this); unitLbl->setStyleSheet(QStringLiteral("border:none;")); v->addWidget(unitLbl); unit_ = new QComboBox(this); unit_->addItems({QStringLiteral("米"), QStringLiteral("英尺")}); v->addWidget(unit_); // X / Y / Z 三轴(各:显示开关 + 最小值/最大值)。与上方单位组留间距。 v->addSpacing(space::kSm); x_ = addAxisRow(v, QStringLiteral("X轴"), {true, -500, 500}); y_ = addAxisRow(v, QStringLiteral("Y轴"), {true, -500, 500}); z_ = addAxisRow(v, QStringLiteral("Z轴"), {true, 0, 200}); // 放大系数(=垂直夸张):滑块 1~10×。仅改面板内数值/标签,点「应用」才统一生效(不再实时)。 // 与上方坐标轴组留出间距(用户反馈:滑块离上面项目太近)。 v->addSpacing(space::kMd); auto* scaleRow = new QHBoxLayout(); auto* scaleLbl = new QLabel(QStringLiteral("放大系数"), this); scaleLbl->setStyleSheet(QStringLiteral("border:none;")); scaleRow->addWidget(scaleLbl); scaleSlider_ = new ClickJumpSlider(Qt::Horizontal, this); scaleSlider_->setMinimum(1); scaleSlider_->setMaximum(10); scaleSlider_->setValue(1); scaleSlider_->setSingleStep(1); scaleSlider_->setPageStep(1); // 点击轨道按 1 步移动(默认 pageStep=10 → 点一下直接跳到 10 的 bug) scaleLabel_ = new QLabel(QStringLiteral("1.0×"), this); applyTokenizedStyleSheet(scaleLabel_, QStringLiteral("border:none;color:{{text/secondary}};min-width:36px;")); connect(scaleSlider_, &QSlider::valueChanged, this, [this](int v) { scaleLabel_->setText(QStringLiteral("%1.0×").arg(v)); }); scaleRow->addWidget(scaleSlider_, 1); scaleRow->addWidget(scaleLabel_); v->addLayout(scaleRow); // 取消 / 应用。 auto* btns = new QHBoxLayout(); auto* cancel = new QPushButton(QStringLiteral("取消"), this); auto* apply = new QPushButton(QStringLiteral("应用"), this); apply->setDefault(true); // 主按钮:走全局 QPushButton:default 样式(accent/primary,随主题),不再硬编码蓝 connect(cancel, &QPushButton::clicked, this, &AxesSettingsPanel::closed); connect(apply, &QPushButton::clicked, this, [this] { auto rd = [](const Row& r) { return AxisRange{r.show->isChecked(), r.lo->value(), r.hi->value()}; }; emit applied(rd(x_), rd(y_), rd(z_), unit_->currentIndex(), static_cast(scaleSlider_->value())); }); btns->addStretch(1); btns->addWidget(cancel); btns->addWidget(apply); v->addLayout(btns); } AxesSettingsPanel::Row AxesSettingsPanel::addAxisRow(QVBoxLayout* col, const QString& title, const AxisRange& def) { Row r; auto* head = new QHBoxLayout(); auto* lbl = new QLabel(title, this); lbl->setStyleSheet(QStringLiteral("font-weight:600;border:none;")); head->addWidget(lbl); head->addStretch(1); auto* showLbl = new QLabel(QStringLiteral("显示"), this); showLbl->setStyleSheet(QStringLiteral("border:none;")); head->addWidget(showLbl); r.show = new QCheckBox(this); r.show->setChecked(def.show); head->addWidget(r.show); col->addLayout(head); auto* range = new QHBoxLayout(); auto* loCol = new QVBoxLayout(); auto* loLbl = new QLabel(QStringLiteral("最小值"), this); applyTokenizedStyleSheet(loLbl, QStringLiteral("border:none;color:{{text/tertiary}};")); loCol->addWidget(loLbl); r.lo = new QDoubleSpinBox(this); r.lo->setRange(-1e6, 1e6); r.lo->setValue(def.min); loCol->addWidget(r.lo); range->addLayout(loCol); auto* hiCol = new QVBoxLayout(); auto* hiLbl = new QLabel(QStringLiteral("最大值"), this); applyTokenizedStyleSheet(hiLbl, QStringLiteral("border:none;color:{{text/tertiary}};")); hiCol->addWidget(hiLbl); r.hi = new QDoubleSpinBox(this); r.hi->setRange(-1e6, 1e6); r.hi->setValue(def.max); hiCol->addWidget(r.hi); range->addLayout(hiCol); col->addLayout(range); // 取消「显示」时禁用该轴的最小/最大值编辑(隐藏的轴改范围无意义)。 auto* lo = r.lo; auto* hi = r.hi; connect(r.show, &QCheckBox::toggled, this, [lo, hi](bool on) { lo->setEnabled(on); hi->setEnabled(on); }); r.lo->setEnabled(def.show); r.hi->setEnabled(def.show); return r; } void AxesSettingsPanel::setValues(AxisRange x, AxisRange y, AxisRange z, int unitIdx, double scale) { auto wr = [](const Row& r, const AxisRange& a) { r.show->setChecked(a.show); r.lo->setValue(a.min); r.hi->setValue(a.max); r.lo->setEnabled(a.show); // 回灌时同步禁用态(隐藏轴 → 禁编辑) r.hi->setEnabled(a.show); }; wr(x_, x); wr(y_, y); wr(z_, z); unit_->setCurrentIndex(unitIdx); const int v = std::max(1, std::min(10, static_cast(scale + 0.5))); QSignalBlocker block(scaleSlider_); // 回灌不触发 sliderReleased scaleSlider_->setValue(v); scaleLabel_->setText(QStringLiteral("%1.0×").arg(v)); } } // namespace geopro::app