You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmWxLayerGeometryPreview.cpp

81 lines
4.5 KiB
C++

#include "nmWxLayerGeometryPreview.h"
#include <QPainter>
nmWxLayerGeometryPreview::nmWxLayerGeometryPreview(QWidget* parent)
: QWidget(parent), m_nSelectedRow(-1)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
}
QSize nmWxLayerGeometryPreview::sizeHint() const
{
// 仅给布局一个随字体变化的推荐尺寸;实际宽高由父布局分配,不固定窗口像素大小。
return QSize(fontMetrics().width("Layer 0000") + 24, fontMetrics().height() * 12);
}
void nmWxLayerGeometryPreview::setGeometryData(const nmLayerGeometry& geometry, int selectedRow)
{
// 值复制使绘图不依赖外部对象地址;update() 由事件循环合并重绘请求。
m_oGeometry = geometry;
m_nSelectedRow = selectedRow;
update();
}
QRectF nmWxLayerGeometryPreview::layerRect(int nRow) const
{
const QRectF oArea = QRectF(rect()).adjusted(6, 6, -6, -6); // 与预览边框保持一致的逻辑像素绘图区。
if (oArea.width() <= 0.0 || oArea.height() <= 0.0) return QRectF();
const double dFirstTop = m_oGeometry.m_vecLayers.first().m_dTopDepth; // 米制深度原点。
const double dSpan = m_oGeometry.m_vecLayers.last().m_dBottomDepth - dFirstTop; // 包含历史间隙的米制跨度。
const nmLayerGeometryRow& oLayer = m_oGeometry.m_vecLayers.at(nRow); // 借用待绘制/命中的层快照。
// 两个坐标均为逻辑像素;只用于图形定位,不反向写入米制几何。
const double dTopPixel = oArea.top() + (oLayer.m_dTopDepth - dFirstTop) / dSpan * oArea.height();
const double dBottomPixel = oArea.top() + (oLayer.m_dBottomDepth - dFirstTop) / dSpan * oArea.height();
return QRectF(oArea.left(), dTopPixel, oArea.width(), dBottomPixel - dTopPixel);
}
int nmWxLayerGeometryPreview::layerAtPoint(const QPoint& pos) const
{
if (!m_oGeometry.isValid()) return -1;
const QPointF oPosition(pos); // 鼠标位置与色带矩形共用控件局部逻辑像素坐标。
for (int nRow = 0; nRow < m_oGeometry.m_vecLayers.size(); ++nRow) {
const QRectF oBand = layerRect(nRow); // 复用绘制矩形,不按总厚度另外推算点击层。
// 共用边界归下方层,最后一层包含其底边;间隙内没有任何矩形可命中。
if (!oBand.isEmpty() && oBand.contains(oPosition)
&& (oPosition.y() < oBand.bottom() || nRow == m_oGeometry.m_vecLayers.size() - 1))
return nRow;
}
return -1;
}
void nmWxLayerGeometryPreview::paintEvent(QPaintEvent*)
{
QPainter painter(this); // 本次绘制事件的画笔,坐标采用控件的逻辑像素空间。
painter.fillRect(rect(), palette().base());
painter.setPen(palette().mid().color());
painter.drawRect(rect().adjusted(0, 0, -1, -1));
// 输入处于待修正状态时,窗口会传入空快照;保留背景,不继续显示上一次的旧剖面。
if (!m_oGeometry.isValid()) return;
const QRectF area = QRectF(rect()).adjusted(6, 6, -6, -6); // 留出边框内边距后的有效绘图区,单位为逻辑像素。
if (area.width() <= 0.0 || area.height() <= 0.0) return;
for (int i = 0; i < m_oGeometry.m_vecLayers.size(); ++i) {
const nmLayerGeometryRow& row = m_oGeometry.m_vecLayers.at(i); // 只读层快照,几何数值仍为米。
const QRectF band = layerRect(i); // 绘制与鼠标命中共用同一色带矩形,间隙不填充。
const QColor color = row.m_clrLayer.isValid() ? row.m_clrLayer : palette().midlight().color(); // 缺失颜色时采用主题色。
painter.fillRect(band, color);
painter.setPen(QPen(i == m_nSelectedRow ? palette().highlight().color()
: palette().mid().color(), i == m_nSelectedRow ? 2 : 1));
painter.drawRect(band);
// 过薄层只保留色带;文字放不下时不跨层绘制,长层名则用省略号避免挤出边界。
// 根据填充色明暗选择文字颜色,使深浅色层均能辨认名称。
if (band.height() >= fontMetrics().height() + 4) {
painter.setPen(qGray(color.rgb()) < 128 ? Qt::white : Qt::black);
// 仅用于色带显示的名称,旧数据无名时使用默认值,不反向修改快照。
const QString name = row.m_sName.isEmpty() ? QString("Layer%1").arg(i + 1) : row.m_sName;
painter.drawText(band.adjusted(3, 0, -3, 0), Qt::AlignCenter,
fontMetrics().elidedText(name, Qt::ElideRight, qMax(0, int(band.width()) - 6)));
}
}
}