pagination.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. #include "pagination.h"
  2. #include <QVector>
  3. #include <QComboBox>
  4. #include <QDebug>
  5. #include <QMouseEvent>
  6. #include <QPainter>
  7. #include <QPushButton>
  8. #include <QStyleOptionButton>
  9. enum PageNavigation {
  10. PreviousPage = -2, // <
  11. NextPage = -1, // >
  12. PreviousExPage = -4, // <<
  13. NextExPage = -3, // >>
  14. NoSelection = 0
  15. };
  16. class PaginationPrivate
  17. {
  18. public:
  19. PaginationPrivate(QWidget *_q)
  20. : q(_q)
  21. , prevText("<")
  22. , nextText(">")
  23. , layout(static_cast<Pagination::Layout>(Pagination::TOTAL | Pagination::PREV
  24. | Pagination::PAGER | Pagination::NEXT))
  25. {}
  26. QWidget *q;
  27. int total = 0; //总数
  28. int pageCount = 0; //页面数量
  29. int currentPage = 1; //当前页面
  30. int pagerCount = 7; //翻页大小
  31. int pageSizes = 10; //分页大小
  32. int defaultSize = 30; //控件默认大小
  33. int spacing = 5; //控件默认间距
  34. int hoverNumber = PageNavigation::NoSelection; //焦点控件
  35. QString prevText; //上一页文本
  36. QString nextText; //下一页文本
  37. Pagination::Layout layout;
  38. QSize buttonSize(const QString &text)
  39. { //
  40. QMargins margins = q->contentsMargins();
  41. const QFontMetrics &met = q->fontMetrics();
  42. #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
  43. int w = met.width(text);
  44. #else
  45. int w = met.horizontalAdvance(text);
  46. #endif
  47. // 10 边距 但是获取不正确 先固定
  48. int width = std::max(defaultSize, w + margins.left() + margins.right() + 10);
  49. int height = std::max(defaultSize, met.height() + margins.top() + margins.bottom());
  50. // width = std::max(width, height);
  51. //qDebug() << text << QSize{width, height};
  52. return {width, height};
  53. }
  54. void drawNumber(const QRect &rect, int number, QPainter *painter)
  55. {
  56. QString text;
  57. if (PageNavigation::PreviousPage == number) {
  58. text = prevText;
  59. } else if (PageNavigation::NextPage == number) {
  60. text = nextText;
  61. } else if (PageNavigation::PreviousExPage == number) {
  62. if (hoverNumber == number) {
  63. text = "<<";
  64. } else {
  65. text = "...";
  66. }
  67. } else if (PageNavigation::NextExPage == number) {
  68. if (hoverNumber == number) {
  69. text = ">>";
  70. } else {
  71. text = "...";
  72. }
  73. } else {
  74. text = QString::number(number);
  75. }
  76. QStyleOptionButton opt;
  77. opt.initFrom(q);
  78. opt.text = text;
  79. opt.rect = rect;
  80. opt.state.setFlag(QStyle::State_MouseOver, hoverNumber == number);
  81. if (number > 0) {
  82. opt.state.setFlag(QStyle::State_On, currentPage == number);
  83. opt.state.setFlag(QStyle::State_Selected, currentPage == number);
  84. }
  85. q->style()->drawControl(QStyle::CE_PushButton, &opt, painter, q);
  86. }
  87. void drawText(const QRect &rect, const QString &text, QPainter *painter)
  88. {
  89. QStyle *style = q->style();
  90. QStyleOption opt;
  91. opt.initFrom(q);
  92. style->drawItemText(painter,
  93. rect,
  94. Qt::AlignCenter,
  95. opt.palette,
  96. true,
  97. text,
  98. q->foregroundRole());
  99. }
  100. QVector<int> numbers()
  101. {
  102. QVector<int> page;
  103. int halfPagerCount = (pagerCount - 1) / 2;
  104. bool showPrevMore = false;
  105. bool showNextMore = false;
  106. if (pageCount > pagerCount) {
  107. if (currentPage > pagerCount - halfPagerCount) {
  108. showPrevMore = true;
  109. }
  110. if (currentPage < pageCount - halfPagerCount) {
  111. showNextMore = true;
  112. }
  113. }
  114. if (pageCount > 0) {
  115. page.push_back(1);
  116. }
  117. if (showPrevMore && !showNextMore) {
  118. int startPage = pageCount - (pagerCount - 2);
  119. page.push_back(PageNavigation::PreviousExPage);
  120. for (int i = startPage; i < pageCount; ++i) {
  121. page.push_back(i);
  122. }
  123. } else if (!showPrevMore && showNextMore) {
  124. for (int i = 2; i < pagerCount; ++i) {
  125. page.push_back(i);
  126. }
  127. page.push_back(PageNavigation::NextExPage);
  128. } else if (showPrevMore && showNextMore) {
  129. int offset = pagerCount / 2 - 1;
  130. page.push_back(PageNavigation::PreviousExPage);
  131. for (int i = currentPage - offset; i <= currentPage + offset; ++i) {
  132. page.push_back(i);
  133. }
  134. page.push_back(PageNavigation::NextExPage);
  135. } else {
  136. for (int i = 2; i < pageCount; ++i) {
  137. page.push_back(i);
  138. }
  139. }
  140. if (pageCount > 1) {
  141. page.push_back(pageCount);
  142. }
  143. return page;
  144. }
  145. };
  146. Pagination::Pagination(QWidget *parent)
  147. : QWidget(parent)
  148. , d(new PaginationPrivate(this))
  149. {
  150. setMouseTracking(true); // 启用鼠标跟踪
  151. setContentsMargins(7, 7, 7, 7);
  152. }
  153. Pagination::Pagination(int total, int pageSize, QWidget *parent)
  154. : QWidget(parent)
  155. , d(new PaginationPrivate(this))
  156. {
  157. setMouseTracking(true); // 启用鼠标跟踪
  158. setContentsMargins(7, 7, 7, 7);
  159. d->total = total;
  160. d->pageSizes = pageSize;
  161. d->pageCount = (total + d->pageSizes - 1) / d->pageSizes;
  162. }
  163. Pagination::~Pagination()
  164. {
  165. delete d;
  166. }
  167. int Pagination::currentPage() const
  168. {
  169. return d->currentPage;
  170. }
  171. void Pagination::setCurrentPage(int page)
  172. {
  173. d->currentPage = page;
  174. update();
  175. }
  176. QString Pagination::prevText() const
  177. {
  178. return d->prevText;
  179. }
  180. void Pagination::setPrevText(const QString &text)
  181. {
  182. d->prevText = text;
  183. update();
  184. }
  185. QString Pagination::nextText() const
  186. {
  187. return d->nextText;
  188. }
  189. void Pagination::setNextText(const QString &text)
  190. {
  191. d->nextText = text;
  192. update();
  193. }
  194. int Pagination::spacing() const
  195. {
  196. return d->spacing;
  197. }
  198. void Pagination::setSpacing(int spacing)
  199. {
  200. d->spacing = spacing;
  201. }
  202. Pagination::Layout Pagination::layout() const
  203. {
  204. return d->layout;
  205. }
  206. void Pagination::setLayout(Pagination::Layout layout)
  207. {
  208. d->layout = layout;
  209. }
  210. int Pagination::pagerCount() const
  211. {
  212. return d->pagerCount;
  213. }
  214. void Pagination::setPagerCount(int count)
  215. {
  216. if (count <= 0) {
  217. d->pagerCount = 7;
  218. } else {
  219. d->pagerCount = count;
  220. }
  221. update();
  222. }
  223. int Pagination::pageSizes() const
  224. {
  225. return d->pageSizes;
  226. }
  227. void Pagination::setPageSizes(int count)
  228. {
  229. d->pageSizes = count;
  230. d->pageCount = (d->total + d->pageSizes - 1) / d->pageSizes;
  231. update();
  232. }
  233. int Pagination::total() const
  234. {
  235. return d->total;
  236. }
  237. void Pagination::setTotal(int count)
  238. {
  239. d->total = count;
  240. d->pageCount = (count + d->pageSizes - 1) / d->pageSizes;
  241. update();
  242. }
  243. QSize Pagination::sizeHint() const
  244. {
  245. QMargins margins = this->contentsMargins();
  246. const QFontMetrics &met = this->fontMetrics();
  247. int height = std::max(d->defaultSize, met.height() + margins.top() + margins.bottom());
  248. return QSize{150, height};
  249. }
  250. QSize Pagination::minimumSizeHint() const
  251. {
  252. QMargins margins = this->contentsMargins();
  253. const QFontMetrics &met = this->fontMetrics();
  254. int height = std::max(d->defaultSize, met.height() + margins.top() + margins.bottom());
  255. return QSize{150, height};
  256. }
  257. void Pagination::paintEvent(QPaintEvent *event)
  258. {
  259. QPainter painter(this);
  260. const auto pages = d->numbers();
  261. int x = 0;
  262. int y = 0;
  263. if (d->layout & TOTAL) {
  264. const QString &text = QString(tr("Total:%1")).arg(d->total);
  265. QSize size = d->buttonSize(text);
  266. QRect rect(x, y, size.width(), size.height());
  267. d->drawText(rect, text, &painter);
  268. x = x + size.width() + d->spacing;
  269. }
  270. if (d->layout & PREV) {
  271. QSize size = d->buttonSize(d->prevText);
  272. QRect rect(x, y, size.width(), size.height());
  273. d->drawNumber(rect, PageNavigation::PreviousPage, &painter);
  274. x = x + size.width() + d->spacing;
  275. }
  276. if (d->layout & PAGER) {
  277. for (int p : pages) {
  278. QSize size = d->buttonSize(QString::number(p));
  279. QRect rect(x, y, size.width(), size.height());
  280. d->drawNumber(rect, p, &painter);
  281. x = x + size.width() + d->spacing;
  282. }
  283. }
  284. if (d->layout & NEXT) {
  285. QSize size = d->buttonSize(d->nextText);
  286. QRect rect(x, y, size.width(), size.height());
  287. d->drawNumber(rect, PageNavigation::NextPage, &painter);
  288. x = x + size.width() + d->spacing;
  289. }
  290. Q_UNUSED(x)
  291. setMinimumWidth(x);
  292. //painter.drawRect(rect());
  293. return QWidget::paintEvent(event);
  294. }
  295. void Pagination::mouseMoveEvent(QMouseEvent *event)
  296. {
  297. const QPoint &point = event->pos();
  298. int x = 0;
  299. int y = 0;
  300. if (d->layout & TOTAL) {
  301. const QString &text = QString(tr("Total:%1")).arg(d->total);
  302. QSize size = d->buttonSize(text);
  303. QRect rect(x, y, size.width(), size.height());
  304. x = x + size.width() + d->spacing;
  305. if (rect.contains(point)) {
  306. d->hoverNumber = PageNavigation::NoSelection;
  307. update();
  308. return QWidget::mouseMoveEvent(event);
  309. }
  310. }
  311. if (d->layout & PREV) {
  312. QSize size = d->buttonSize(d->prevText);
  313. QRect rect(x, y, size.width(), size.height());
  314. x = x + size.width() + d->spacing;
  315. if (rect.contains(point)) {
  316. d->hoverNumber = PageNavigation::PreviousPage;
  317. update();
  318. return QWidget::mouseMoveEvent(event);
  319. }
  320. }
  321. if (d->layout & PAGER) {
  322. const auto pages = d->numbers();
  323. for (int p : pages) {
  324. QSize size = d->buttonSize(QString::number(p));
  325. QRect rect(x, y, size.width(), size.height());
  326. x = x + size.width() + d->spacing;
  327. if (rect.contains(point)) {
  328. d->hoverNumber = p;
  329. update();
  330. return QWidget::mouseMoveEvent(event);
  331. }
  332. }
  333. }
  334. if (d->layout & NEXT) {
  335. QSize size = d->buttonSize(d->nextText);
  336. QRect rect(x, y, size.width(), size.height());
  337. x = x + size.width() + d->spacing;
  338. if (rect.contains(point)) {
  339. d->hoverNumber = PageNavigation::NextPage;
  340. update();
  341. return QWidget::mouseMoveEvent(event);
  342. }
  343. }
  344. Q_UNUSED(x)
  345. return QWidget::mouseMoveEvent(event);
  346. }
  347. void Pagination::mousePressEvent(QMouseEvent *event)
  348. {
  349. if (event->button() == Qt::LeftButton) {
  350. const QPoint &point = event->pos();
  351. int x = 0;
  352. int y = 0;
  353. if (d->layout & TOTAL) {
  354. const QString &text = QString(tr("Total:%1")).arg(d->total);
  355. QSize size = d->buttonSize(text);
  356. QRect rect(x, y, size.width(), size.height());
  357. x = x + size.width() + d->spacing;
  358. if (rect.contains(point)) {
  359. update();
  360. return QWidget::mouseMoveEvent(event);
  361. }
  362. }
  363. if (d->layout & PREV) {
  364. QSize size = d->buttonSize(d->prevText);
  365. QRect rect(x, y, size.width(), size.height());
  366. x = x + size.width() + d->spacing;
  367. if (rect.contains(point)) {
  368. int newPage = d->currentPage - 1;
  369. if (newPage < 1) {
  370. newPage = 1;
  371. }
  372. if (newPage > d->pageCount) {
  373. newPage = d->pageCount;
  374. }
  375. //发送信号
  376. if (d->currentPage != newPage) {
  377. d->currentPage = newPage;
  378. emit valueChanged(d->currentPage);
  379. }
  380. update();
  381. return QWidget::mouseMoveEvent(event);
  382. }
  383. }
  384. if (d->layout & PAGER) {
  385. const auto pages = d->numbers();
  386. for (int p : pages) {
  387. QSize size = d->buttonSize(QString::number(p));
  388. QRect rect(x, y, size.width(), size.height());
  389. x = x + size.width() + d->spacing;
  390. if (rect.contains(point)) {
  391. int newPage = d->currentPage;
  392. if (p > 0) {
  393. newPage = p;
  394. } else {
  395. const int pagerCountOffset = d->pagerCount - 2;
  396. if (p == PageNavigation::PreviousExPage) {
  397. newPage = d->currentPage - pagerCountOffset;
  398. } else if (p == PageNavigation::NextExPage) {
  399. newPage = d->currentPage + pagerCountOffset;
  400. } else if (p == PageNavigation::PreviousExPage) {
  401. newPage = d->currentPage - 1;
  402. } else if (p == PageNavigation::NextPage) {
  403. newPage = d->currentPage + 1;
  404. }
  405. }
  406. if (newPage < 1) {
  407. newPage = 1;
  408. }
  409. if (newPage > d->pageCount) {
  410. newPage = d->pageCount;
  411. }
  412. //发送信号
  413. if (d->currentPage != newPage) {
  414. d->currentPage = newPage;
  415. emit valueChanged(d->currentPage);
  416. }
  417. update();
  418. return QWidget::mouseMoveEvent(event);
  419. }
  420. }
  421. }
  422. if (d->layout & NEXT) {
  423. QSize size = d->buttonSize(d->nextText);
  424. QRect rect(x, y, size.width(), size.height());
  425. x = x + size.width() + d->spacing;
  426. if (rect.contains(point)) {
  427. int newPage = d->currentPage + 1;
  428. if (newPage < 1) {
  429. newPage = 1;
  430. }
  431. if (newPage > d->pageCount) {
  432. newPage = d->pageCount;
  433. }
  434. //发送信号
  435. if (d->currentPage != newPage) {
  436. d->currentPage = newPage;
  437. emit valueChanged(d->currentPage);
  438. }
  439. update();
  440. return QWidget::mouseMoveEvent(event);
  441. }
  442. }
  443. Q_UNUSED(x)
  444. }
  445. QWidget::mousePressEvent(event);
  446. }
  447. void Pagination::leaveEvent(QEvent *event)
  448. {
  449. d->hoverNumber = PageNavigation::NoSelection;
  450. update();
  451. QWidget::leaveEvent(event);
  452. }