无极缩放滚动条

滚动条分析

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();
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值