滚动条分析
1、无极缩放滚动条,滚动条包含滑块、左右拉伸按钮, 最左、最右存在两个sigle step按钮
2、value计算
3、缩放计算(滚动条特有)
滑块占整个滚动条的长度比例
4、节目布局
5、代码
#pragma once
#include <QFrame>
#include <QToolButton>
#include <QBoxLayout>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QMouseEvent>
/*
* 总长度length = maximum() - minimum() + pageStep()
* 修改滑块的长度,相当于改变了滚动的长度,相当于缩放整个滚动条
*
*/
namespace Ui
{
class ScrollBar;
}
class ScrollBar : public QFrame
{
Q_OBJECT
Q_PROPERTY(int minimum READ minimum WRITE setMinimum)
Q_PROPERTY(int maximum READ maximum WRITE setMaximum)
Q_PROPERTY(int singleStep READ singleStep WRITE setSingleStep)
Q_PROPERTY(int pageStep READ pageStep WRITE setPageStep)
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged USER true)
Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation)
public:
enum ScrollBarMouseType
{
ScrollBarMouseType_normorl, // 默认移动
ScrollBarMouseType_left, // 拖拽左边缩放按钮
ScrollBarMouseType_right, // 拖拽右边缩放按钮
ScrollBarMouseType_move, // 移动按钮
};
public:
explicit ScrollBar(QWidget *parent = Q_NULLPTR);
~ScrollBar();
Qt::Orientation orientation() const;
void setMinimum(int);
int minimum() const;
void setMaximum(int);
int maximum() const;
void setSingleStep(int);
int singleStep() const;
void setPageStep(int);
int pageStep() const;
void setSliderDown(bool);
bool isSliderDown() const;
void setScale(double dScale);
double getScale() const;
void setScaleButtonVisible(bool visible);
bool getScaleButtonVisible() const;
void setSingleButtonVisible(bool visible);
bool getSingleButtonVisible() const;
int value() const;
public Q_SLOTS:
void setValue(int);
void setOrientation(Qt::Orientation);
void setRange(int min, int max);
void doMouseMoveEvent(QMouseEvent *event);
Q_SIGNALS:
void valueChanged(int value);
void sliderPressed();
void sliderMoved(int position);
void sliderReleased();
void rangeChanged(int min, int max);
void scaleChanged(double scale); // 0~1
protected:
virtual void mouseReleaseEvent(QMouseEvent *) override;
virtual void mouseMoveEvent(QMouseEvent *) override;
virtual bool eventFilter(QObject *obj, QEvent *event) override;
virtual void resizeEvent(QResizeEvent *event) override;
private:
void calcPosition();
void caclScale();
// UI部分
protected:
void resetUi();
protected:
Qt::Orientation mOrientation; // 格式
ScrollBarMouseType mMouseType;
QPoint mPointPress; // 鼠标按下位置
protected:
Ui::ScrollBar* mUi;
private:
int mMinimum; // 最大值
int mMaximum; // 最小值
int mPageStep; //
int mSingleStep; // 跳动距离
int mValue; // 进度值
bool mSliderDown; // slider 按下
double mScale; // 缩放值,用来处理 实际显示的大小
bool mScaleBtnVisible; // 缩放按钮是否显示
bool mSingleBtnVisible; // 左右移动btn是否显示
};
#include "ScrollBar.h"
#include "ui_ScrollBar.h"
#include <QDebug>
#define MIN_LENTH 50
ScrollBar::ScrollBar(QWidget *parent)
: QFrame(parent)
, mOrientation(Qt::Horizontal)
, mSliderDown(false)
, mMinimum(0)
, mMaximum(100)
, mPageStep(1)
, mSingleStep(1)
, mValue(0)
, mUi(new Ui::ScrollBar)
, mMouseType(ScrollBarMouseType_normorl)
, mScale(1)
, mScaleBtnVisible(true)
, mSingleBtnVisible(true)
{
this->setContentsMargins(0, 0, 0, 0);
mUi->setupUi(this);
caclScale();
mUi->mSliderLeftArrow->installEventFilter(this);
mUi->mSliderToolButton->installEventFilter(this);
mUi->mSliderRightArrow->installEventFilter(this);
connect(mUi->mSliderToolButton, &QToolButton::pressed, this, [this]
{
mMouseType = ScrollBarMouseType_move;
});
connect(mUi->mSliderLeftArrow, &QToolButton::pressed, this, [this]
{
mMouseType = ScrollBarMouseType_left;
});
connect(mUi->mSliderRightArrow, &QToolButton::pressed, this, [this]
{
mMouseType = ScrollBarMouseType_right;
});
connect(mUi->mRightArrow, &QToolButton::clicked, this, [this]
{
setValue(mValue + mSingleStep);
});
connect(mUi->mLeftArrow, &QToolButton::clicked, this, [this]
{
setValue(mValue - mSingleStep);
});
}
ScrollBar::~ScrollBar()
{
}
Qt::Orientation ScrollBar::orientation() const
{
return mOrientation;
}
void ScrollBar::setMinimum(int min)
{
setRange(min, mMaximum);
}
int ScrollBar::minimum() const
{
return mMinimum;
}
void ScrollBar::setMaximum(int max)
{
setRange(mMinimum, max);
}
int ScrollBar::maximum() const
{
return mMaximum;
}
void ScrollBar::setSingleStep(int step)
{
if (mSingleStep != step)
{
mSingleStep = step;
}
}
int ScrollBar::singleStep() const
{
return mSingleStep;
}
void ScrollBar::setPageStep(int step)
{
if (mPageStep != step)
{
mPageStep = step;
}
}
int ScrollBar::pageStep() const
{
return mPageStep;
}
void ScrollBar::setSliderDown(bool bDown)
{
mSliderDown = bDown;
this->repaint();
}
bool ScrollBar::isSliderDown() const
{
return mSliderDown;
}
void ScrollBar::setScale(double dScale)
{
if (dScale < 0 || dScale > 1)
{
return;
}
if (mScale != dScale)
{
mScale = dScale;
emit scaleChanged(dScale);
qDebug() << mScale << endl;
if (mMouseType == ScrollBarMouseType_normorl)
{
caclScale();
}
}
}
double ScrollBar::getScale() const
{
return mScale;
}
void ScrollBar::setScaleButtonVisible(bool visible)
{
if (mScaleBtnVisible != visible)
{
mScaleBtnVisible = visible;
mUi->mSliderLeftArrow->setVisible(mScaleBtnVisible);
mUi->mSliderRightArrow->setVisible(mScaleBtnVisible);
caclScale();
}
}
bool ScrollBar::getScaleButtonVisible() const
{
return mScaleBtnVisible;
}
void ScrollBar::setSingleButtonVisible(bool visible)
{
if (mSingleBtnVisible != visible)
{
mSingleBtnVisible = visible;
mUi->mLeftArrow->setVisible(mScaleBtnVisible);
mUi->mRightArrow->setVisible(mScaleBtnVisible);
caclScale();
}
}
bool ScrollBar::getSingleButtonVisible() const
{
return mSingleBtnVisible;
}
int ScrollBar::value() const
{
return mValue;
}
void ScrollBar::setValue(int value)
{
if (value < mMinimum)
{
value = mMinimum;
}
else if (value > mMaximum)
{
value = mMaximum;
}
if (mValue != value)
{
mValue = value;
emit valueChanged(value);
if (mMouseType == ScrollBarMouseType_normorl)
{
calcPosition();
}
qDebug() << mValue << endl;
}
}
void ScrollBar::setOrientation(Qt::Orientation ori)
{
if (ori != mOrientation)
{
mOrientation = ori;
resetUi();
}
}
void ScrollBar::setRange(int min, int max)
{
if (min != mMinimum || max != mMaximum)
{
mMinimum = qMin(min, max);
mMaximum = qMax(min, max);
emit rangeChanged(mMinimum, mMaximum);
}
}
// 这个应该计算按下点在mouse的哪个位置
void ScrollBar::doMouseMoveEvent(QMouseEvent *event)
{
if (mMouseType != ScrollBarMouseType_move)
{
return;
}
QRect rect = mUi->frame->frameGeometry();
QRect sliderRect = mUi->mSliderFrame->frameGeometry();
QPoint point = event->globalPos();
if (mOrientation == Qt::Horizontal)
{
// 鼠标移动了多少个像素
int dx = point.x() - mPointPress.x() + rect.x();
if (dx >= 0 && (dx + rect.width()) <= sliderRect.width())
{
mUi->frame->setGeometry(QRect(dx, 0, rect.width(), sliderRect.height()));
}
}
else
{
// 鼠标移动了多少个像素
int dy = point.y() - mPointPress.y() + rect.y();
if (dy >= 0 && (dy + rect.height()) <= sliderRect.height())
{
mUi->frame->setGeometry(QRect(0, dy, sliderRect.width(), rect.height()));
}
}
mPointPress = point;
calcPosition();
}
void ScrollBar::mouseReleaseEvent(QMouseEvent *event)
{
mMouseType = ScrollBarMouseType_normorl;
mPointPress = event->globalPos();
}
void ScrollBar::mouseMoveEvent(QMouseEvent *event)
{
QPoint point = event->globalPos();
// frame在sliderRect上的位置,不能超过sliderRect的宽高
QRect rect = mUi->frame->frameGeometry();
// 只有宽高是有用值
QRect sliderRect = mUi->mSliderFrame->frameGeometry();
switch (mMouseType)
{
case ScrollBar::ScrollBarMouseType_normorl:
{
break;
}
case ScrollBar::ScrollBarMouseType_left:
{
if (mOrientation == Qt::Horizontal)
{
int nDx = point.x() - mPointPress.x();
if (rect.x() < -nDx)
{
nDx = -rect.x();
}
int x = rect.x() + nDx;
if (x >= 0 && MIN_LENTH < (rect.width() - nDx))
{
mUi->frame->setGeometry(QRect(x, 0, rect.width() - nDx, sliderRect.height()));
}
}
else
{
int nDy = point.y() - mPointPress.y();
if (rect.y() < -nDy)
{
nDy = -rect.y();
}
int y = rect.y() + nDy;
if (y >= 0 && MIN_LENTH < (rect.height() - nDy))
{
mUi->frame->setGeometry(QRect(0, y, sliderRect.width(), rect.height() - nDy));
}
}
caclScale();
break;
}
case ScrollBar::ScrollBarMouseType_right:
{
if (mOrientation == Qt::Horizontal)
{
int nDx = point.x() - mPointPress.x();
if ((rect.topRight().x() + nDx) > sliderRect.width())
{
nDx = sliderRect.width() - rect.topRight().x();
}
if ((rect.width() + nDx) > (MIN_LENTH))
{
mUi->frame->setGeometry(QRect(rect.x(), 0, rect.width() + nDx, sliderRect.height()));
}
}
else
{
int nDy = point.y() - mPointPress.y();
if ((rect.bottomRight().y() + nDy) > sliderRect.height())
{
nDy = sliderRect.height() - rect.bottomRight().y();
}
if ((rect.height() + nDy) > (MIN_LENTH))
{
mUi->frame->setGeometry(QRect(0, rect.y(), sliderRect.width(), rect.height() + nDy));
}
}
caclScale();
break;
}
case ScrollBar::ScrollBarMouseType_move:
{
doMouseMoveEvent(event);
break;
}
default:
break;
}
mPointPress = point;
}
bool ScrollBar::eventFilter(QObject *obj, QEvent *event)
{
switch (event->type())
{
case QEvent::Enter:
{
if (obj == mUi->mSliderLeftArrow || obj == mUi->mSliderRightArrow)
{
if (mOrientation == Qt::Horizontal)
{
setCursor(Qt::SizeHorCursor);
}
else
{
setCursor(Qt::SizeVerCursor);
}
}
else
{
setCursor(Qt::ArrowCursor);
}
break;
}
case QEvent::Leave:
{
setCursor(Qt::ArrowCursor);
break;
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
{
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (mouseEvent)
{
mPointPress = mouseEvent->globalPos();
}
break;
}
case QEvent::MouseMove:
{
if (obj == mUi->mSliderToolButton)
{
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent*>(event);
if (mouseEvent)
{
doMouseMoveEvent(mouseEvent);
}
}
break;
}
default:
break;
}
return QFrame::eventFilter(obj, event);
}
void ScrollBar::resizeEvent(QResizeEvent *event)
{
caclScale();
}
void ScrollBar::calcPosition()
{
// 计算Value
if (mMouseType != ScrollBarMouseType_normorl)
{
// 计算位置
QRect framRect = mUi->frame->frameGeometry();
QRect rect = mUi->mSliderFrame->frameGeometry();
if (mOrientation == Qt::Horizontal)
{
if (framRect.width() == rect.width())
{
setValue(mMinimum);
}
else
{
if ((framRect.width() + framRect.x()) >= rect.width())
{
setValue(mMaximum);
}
else
{
double dValue = double(mMaximum - mMinimum) / double(rect.width() - framRect.width());
setValue(mMinimum + dValue * framRect.x());
}
}
}
else
{
if (framRect.height() == rect.height())
{
setValue(mMinimum);
}
else
{
if ((framRect.height() + framRect.y()) >= rect.height())
{
setValue(mMaximum);
}
else
{
double dValue = double(mMaximum - mMinimum) / double(rect.height() - framRect.height());
setValue(mMinimum + dValue * framRect.y());
}
}
}
}
else // 根据value计算滑块的位置
{
QRect framRect = mUi->frame->frameGeometry();
QRect rect = mUi->mSliderFrame->frameGeometry();
if (mOrientation == Qt::Horizontal)
{
if (mValue == mMinimum)
{
mUi->frame->setGeometry(0, 0, framRect.width(), rect.height());
}
else if (mValue == mMaximum)
{
mUi->frame->setGeometry(rect.width() - framRect.width(), 0, framRect.width(), rect.height());
}
else if (framRect.width() != rect.width())
{
double dValue = double(mMaximum - mMinimum) / double(rect.width() - framRect.width()); // 每一个像素代表的值
int nPos = (mValue - mMinimum) / dValue;
if (nPos >= 0 && (nPos + framRect.width()) <= rect.width())
{
mUi->frame->setGeometry(nPos, 0, framRect.width(), rect.height());
}
}
}
else
{
if (mValue == mMinimum)
{
mUi->frame->setGeometry(0, 0, framRect.width(), framRect.height());
}
else if (mValue == mMaximum)
{
mUi->frame->setGeometry(0, rect.height() - framRect.height(), rect.width(), framRect.height());
}
else if (framRect.height() != rect.height())
{
double dValue = double(mMaximum - mMinimum) / double(rect.height() - framRect.height()); // 每一个像素代表的值
int nPos = (mValue - mMinimum) / dValue;
if (nPos >= 0 && (nPos + framRect.height()) <= rect.height())
{
mUi->frame->setGeometry(0, nPos, rect.width(), framRect.height());
}
}
}
}
}
void ScrollBar::caclScale()
{
QRect framRect = mUi->frame->frameGeometry();
QRect rect = mUi->mSliderFrame->frameGeometry();
if (mMouseType == ScrollBarMouseType_normorl)
{
if (mOrientation == Qt::Horizontal)
{
double nWidth = rect.width() * mScale;
if (nWidth >= MIN_LENTH)
{
mUi->frame->setGeometry(framRect.x(), 0, nWidth, rect.height());
}
}
else
{
double nHeight = rect.height() * mScale;
if (nHeight >= MIN_LENTH)
{
mUi->frame->setGeometry(0, framRect.y(), rect.width(), nHeight);
}
}
}
else
{
if (mOrientation == Qt::Horizontal)
{
setScale((double)framRect.width() / (double)rect.width());
}
else
{
setScale((double)framRect.height() / (double)rect.height());
}
}
calcPosition();
}
void ScrollBar::resetUi()
{
{
QLayout* oldLayout = this->layout();
if (oldLayout)
{
delete oldLayout;
oldLayout = nullptr;
}
oldLayout = mUi->frame->layout();
if(oldLayout)
{
delete oldLayout;
}
}
switch (mOrientation)
{
case Qt::Horizontal:
{
QHBoxLayout *horizontalLayout = new QHBoxLayout(this);
horizontalLayout->setSpacing(0);
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
horizontalLayout->setContentsMargins(0, 1, 0, 1);
horizontalLayout->addWidget(mUi->mLeftArrow);
horizontalLayout->addWidget(mUi->mSliderFrame, 1);
horizontalLayout->addWidget(mUi->mRightArrow);
{
QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(mUi->frame);
horizontalLayout_2->setSpacing(0);
horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2"));
horizontalLayout_2->setContentsMargins(0, 0, 0, 0);
horizontalLayout_2->setMargin(0);
horizontalLayout_2->addWidget(mUi->mSliderLeftArrow);
horizontalLayout_2->addWidget(mUi->mSliderToolButton,1);
horizontalLayout_2->addWidget(mUi->mSliderRightArrow);
mUi->frame->setLayout(horizontalLayout_2);
mUi->frame->setContentsMargins(0, 0, 0, 0);
}
{
this->setMaximumHeight(20);
this->setMaximumWidth(16777215);
mUi->mSliderLeftArrow->setMinimumSize(QSize(10, 0));
mUi->mSliderLeftArrow->setMaximumSize(QSize(10, 16777215));
mUi->mSliderRightArrow->setMinimumSize(QSize(10, 0));
mUi->mSliderRightArrow->setMaximumSize(QSize(10, 16777215));
}
break;
}
case Qt::Vertical:
{
QVBoxLayout *vLayout = new QVBoxLayout(this);
vLayout->setSpacing(0);
vLayout->setObjectName(QStringLiteral("vLayout"));
vLayout->setContentsMargins(1, 0, 1, 0);
vLayout->addWidget(mUi->mLeftArrow);
vLayout->addWidget(mUi->mSliderFrame, 1);
vLayout->addWidget(mUi->mRightArrow);
{
QVBoxLayout* vLayout_2 = new QVBoxLayout(mUi->frame);
vLayout_2->setSpacing(0);
vLayout_2->setObjectName(QStringLiteral("vLayout_2"));
vLayout_2->setMargin(0);
vLayout_2->setContentsMargins(0, 0, 0, 0);
vLayout_2->addWidget(mUi->mSliderLeftArrow);
vLayout_2->addWidget(mUi->mSliderToolButton, 1);
vLayout_2->addWidget(mUi->mSliderRightArrow);
mUi->frame->setLayout(vLayout_2);
mUi->frame->setContentsMargins(0,0,0,0);
}
{
this->setMaximumHeight(16777215);
this->setMaximumWidth(20);
mUi->mSliderLeftArrow->setMinimumSize(QSize(0, 0));
mUi->mSliderLeftArrow->setMaximumSize(QSize(16777215, 10));
mUi->mSliderToolButton->setMinimumSize(QSize(0, 0));
mUi->mSliderToolButton->setMaximumSize(QSize(16777215, 16777215));
mUi->mSliderRightArrow->setMinimumSize(QSize(0, 0));
mUi->mSliderRightArrow->setMaximumSize(QSize(16777215, 10));
}
break;
}
default:
break;
}
caclScale();
}