geopro/src/app/AxesSettingsPanel.cpp

201 lines
8.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "AxesSettingsPanel.hpp"
#include <algorithm>
#include <QCheckBox>
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QMouseEvent>
#include <QPushButton>
#include <QSlider>
#include <QStyle>
#include <QStyleOptionSlider>
#include <QVBoxLayout>
#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<double>(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<int>(scale + 0.5)));
QSignalBlocker block(scaleSlider_); // 回灌不触发 sliderReleased
scaleSlider_->setValue(v);
scaleLabel_->setText(QStringLiteral("%1.0×").arg(v));
}
} // namespace geopro::app