calendarwidgetex.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. //qcustomcalendarwidget.cpp
  2. #include "calendarwidgetex.h"
  3. #include <QPainter>
  4. #include <QProxyStyle>
  5. #include <QTableView>
  6. #include <QHeaderView>
  7. #include <QLayout>
  8. #include <QPushButton>
  9. #include <QLabel>
  10. #include <QDebug>
  11. #include <QLayoutItem>
  12. #include <QKeyEvent>
  13. #include <QPainterPath>
  14. #include "scopeselectionmodel.h"
  15. #include "PaintHelper/painthelper.h"
  16. // #include "StyleManager/lhstylemanager.h"
  17. //#include "utility/utility.h"
  18. #include "UIStyleManager.h"
  19. CalendarWidgetEx::CalendarWidgetEx(QWidget *parent)
  20. : QCalendarWidget(parent)
  21. , m_modeSelection(Normal)
  22. , m_pDateScopeModel(nullptr)
  23. , m_nLineHeight(-1)
  24. , m_hasTopSplitLine(false)
  25. {
  26. setWindowFlag(Qt::NoDropShadowWindowHint);
  27. setAttribute(Qt::WA_TranslucentBackground);
  28. setWindowFlag(Qt::FramelessWindowHint);
  29. //使用固定尺寸(无法通过resize控制日历大小, 日历整体大小由layout下的控件的fixSize决定)
  30. //layout()->setSizeConstraint(QLayout::SetFixedSize);
  31. //禁用原有的年月导航
  32. setNavigationBarVisible(false);
  33. //禁用横向纵向表头
  34. setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
  35. setHorizontalHeaderFormat(QCalendarWidget::NoHorizontalHeader);
  36. //取消聚焦虚线框
  37. setStyle(new NoFocusStyle(this));
  38. QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
  39. if(vBodyLayout == nullptr) return;
  40. m_pNav = new CalendarNav(this);
  41. m_pHeader = new CalendarHeader(this);
  42. setFirstDayOfWeek(Qt::Sunday);
  43. m_pHeader->SetFirstDayOfWeek(Qt::Sunday);
  44. vBodyLayout->insertWidget(0, m_pNav);
  45. // 导航和星期标题间距
  46. vBodyLayout->insertSpacing(1, 10);
  47. vBodyLayout->insertWidget(2, m_pHeader);
  48. vBodyLayout->setSpacing(10);
  49. vBodyLayout->setContentsMargins(10,0,10,10);
  50. m_nLineHeight = vBodyLayout->itemAt(4)->widget()->mapTo(this, QPoint(0,0)).y();
  51. //qDebug()<<"m_nLineHeight"<<m_nLineHeight<<vBodyLayout->itemAt(3)->widget()->height();
  52. //开启鼠标监测
  53. QTableView *pCalendarView = dynamic_cast<QTableView*>(vBodyLayout->itemAt(4)->widget());
  54. if (Q_NULLPTR != pCalendarView) {
  55. pCalendarView->setMouseTracking(true);
  56. }
  57. //在构造函数里取消selectionChanged, clicked事件没用, 因为执行QDateTimeEdit的setCalendarWidget方法时, 会重新绑定
  58. //所以必须等setCalendarWidget执行完后再取消事件
  59. //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::selectionChanged, 0, 0);
  60. //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::clicked, 0, 0);
  61. setMouseTracking(true);
  62. // 设置默认字体后,修复单元格变得很宽的问题
  63. for (QWidget* f : findChildren<QWidget*>()) {
  64. if(f->objectName() != "qt_calendar_calendarview") continue;
  65. QTableView* pView = reinterpret_cast<QTableView*>(f);
  66. if (nullptr != pView && nullptr != pView->horizontalHeader()) {
  67. pView->horizontalHeader()->setMaximumSectionSize(WINDOW_WIDTH / 8);
  68. }
  69. }
  70. connect(this, &QCalendarWidget::clicked, this, &CalendarWidgetEx::OnClicked);
  71. // initSkinColor();
  72. setUIStyle();
  73. /* 设置对象名称,加载QSS */
  74. // this->setObjectName("calendarWidget");
  75. this->setStyleSheet("background-color:transparent;");
  76. }
  77. void CalendarWidgetEx::SetSelectMode(SelectMode mode, ScopeSelectionModel *pDataModel)
  78. {
  79. m_listMultiSelectDays.clear();
  80. m_modeSelection = mode;
  81. if(m_pDateScopeModel == nullptr)
  82. {
  83. m_pDateScopeModel = pDataModel;
  84. connect(m_pDateScopeModel, &ScopeSelectionModel::sig_Update, this, static_cast<void(QWidget::*)()>(&QWidget::update));
  85. }
  86. update();
  87. }
  88. void CalendarWidgetEx::hideNavigatioinButton(bool bPreYear, bool bPreMon, bool bNextYear, bool bNextMon)
  89. {
  90. QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
  91. if(vBodyLayout == nullptr) return;
  92. CalendarNav *pNav = qobject_cast<CalendarNav*>(vBodyLayout->itemAt(0)->widget());
  93. if (nullptr != pNav) {
  94. pNav->hideNextMonth(bNextMon);
  95. pNav->hideNextYear(bNextYear);
  96. pNav->hidePreMonth(bPreMon);
  97. pNav->hidePreYear(bPreYear);
  98. }
  99. }
  100. /* 设置UI样式,0亮色,1暗色 */
  101. void CalendarWidgetEx::setUIStyle()
  102. {
  103. if(EPUIStyle.getUIStyle() == enum_UIStyle::UI_Light)
  104. {
  105. m_style = 0;
  106. }
  107. else if(EPUIStyle.getUIStyle() == enum_UIStyle::UI_Dark)
  108. {
  109. m_style = 1;
  110. }
  111. initSkinColor();
  112. m_pNav->setQSS(m_style);
  113. m_pHeader->setQSS(m_style);
  114. update();
  115. }
  116. void CalendarWidgetEx::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
  117. {
  118. PainterEx *painterEx = static_cast<PainterEx*>(painter);
  119. painterEx->setRenderHint(QPainter::Antialiasing);
  120. QColor textColor;
  121. {//正常
  122. textColor = m_normalTextColor;
  123. }
  124. #if 0
  125. // 周六,周日特殊颜色
  126. int nWeek = date.dayOfWeek();
  127. if (6 == nWeek || 7 == nWeek) {
  128. textColor = QColor(255,149,0);
  129. }
  130. #endif
  131. //鼠标移入
  132. if(date == m_dateMouseOver)
  133. {
  134. QPoint center = rect.center();
  135. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  136. painterEx->DrawRoundedRect(rc, 2.0, m_hoverBlockColor);
  137. textColor = m_normalTextColor;
  138. }
  139. //当天
  140. if(date == QDate::currentDate())
  141. {
  142. painter->save();
  143. QPoint center = rect.center();
  144. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  145. painter->setPen(m_todayTextColor);
  146. painter->setBrush(Qt::transparent);
  147. painter->drawRoundedRect(rc, 2.0, 2.0);
  148. textColor = m_todayTextColor;
  149. painter->restore();
  150. }
  151. //选中
  152. if(m_modeSelection == Multi && m_listMultiSelectDays.contains(date))
  153. {
  154. //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9,109,217));
  155. QPoint center = rect.center();
  156. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  157. painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
  158. textColor = m_selectTextColor;
  159. }
  160. if(m_modeSelection == Normal && date == selectedDate())
  161. {
  162. //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9, 109, 217));
  163. QPoint center = rect.center();
  164. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  165. painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
  166. textColor = m_selectTextColor;
  167. }
  168. if(m_modeSelection == Scope && m_pDateScopeModel != nullptr && m_pDateScopeModel->dtFirst.isValid() && m_pDateScopeModel->dtSecond.isValid())
  169. {
  170. QDate scopeStart = qMin(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
  171. QDate scopeEnd = qMax(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
  172. if(date == qBound(scopeStart, date, scopeEnd) && date.month() == monthShown())
  173. {
  174. painterEx->SetBrushOnly(m_hoverBlockColor);
  175. if(date == scopeStart || date == scopeEnd)
  176. {
  177. textColor = m_selectTextColor;
  178. QPoint center = rect.center();
  179. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  180. painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
  181. }
  182. else
  183. {
  184. QRect r(0, 0, rect.width(), TEXT_WIDTH);
  185. r.moveCenter(rect.center());
  186. painterEx->drawRect(r);
  187. }
  188. }
  189. }
  190. //不可选的日期或非当月日期
  191. if(date < minimumDate() || date > maximumDate() || date.month() != monthShown())
  192. {
  193. textColor = m_disableTextColor;
  194. }
  195. //当天且未选中, 加粗
  196. bool isBold = (date == QDate::currentDate() && date != selectedDate());
  197. QString strDay(QString("%1").arg(date.day(), 2, 10, QLatin1Char('0')));
  198. painterEx->setFont(FontEx(font().family(), DEFAULT_FONT_SIZE, isBold));
  199. QRect rc = rect.adjusted(-1, 0, 0, -3); // 矫正文字位置
  200. painterEx->DrawText(rc, strDay, textColor, Qt::AlignCenter);
  201. }
  202. void CalendarWidgetEx::paintEvent(QPaintEvent *)
  203. {
  204. PainterEx painter(this);
  205. //边框和背景
  206. painter.setPen(Qt::transparent);
  207. painter.setBrush(m_bgBrushColor);
  208. QRect rc(rect());
  209. painter.DrawRoundedRect(rc, WINDOW_RADIUS);
  210. QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
  211. if(vBodyLayout != nullptr) {
  212. // 画导航栏,绘制上圆角
  213. QPainterPath path;
  214. path.setFillRule(Qt::WindingFill);
  215. QRectF tmpRc(1, 0, WINDOW_WIDTH - WINDOW_RADIUS, 40);
  216. path.addRoundedRect(tmpRc, WINDOW_RADIUS, WINDOW_RADIUS);
  217. path.addRect(QRectF(tmpRc.x(), tmpRc.y() + WINDOW_RADIUS, tmpRc.width(), tmpRc.height()));
  218. // painter.fillPath(path, QColor(255, 255, 255));
  219. painter.fillPath(path, m_bgBrushColor);
  220. //分割线
  221. QWidget* pNav = vBodyLayout->itemAt(0)->widget();
  222. if (nullptr != pNav) {
  223. int h = pNav->mapTo(this, pNav->rect().bottomRight()).y() + 2;
  224. // painter.SetPenOnly(QColor(0, 0, 0, 23));
  225. painter.setPen(m_bgPenColor);
  226. painter.drawLine(QPoint(0, h), QPoint(width(), h));
  227. }
  228. }
  229. }
  230. QSize CalendarWidgetEx::minimumSizeHint() const
  231. {
  232. return QSize(WINDOW_WIDTH, WINDOW_HEIGHT);
  233. }
  234. void CalendarWidgetEx::mouseMoveEvent(QMouseEvent *event)
  235. {
  236. QCalendarWidget::mouseMoveEvent(event);
  237. QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
  238. if(vBodyLayout == nullptr) return;
  239. QTableView *pCalendarView = dynamic_cast<QTableView*>(vBodyLayout->itemAt(4)->widget());
  240. if(pCalendarView == nullptr) return;
  241. QModelIndex index = pCalendarView->indexAt(pCalendarView->mapFromGlobal(event->globalPos()));
  242. QDate dateMouseOver = dateForCell(index.row(), index.column());
  243. if(m_dateMouseOver != dateMouseOver)
  244. {
  245. m_dateMouseOver = dateMouseOver;
  246. if(m_pDateScopeModel != nullptr && !m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
  247. {
  248. m_pDateScopeModel->dtSecond = dateMouseOver;
  249. m_pDateScopeModel->Update();
  250. }
  251. update();
  252. }
  253. }
  254. //仅适用于: 不显示纵向表头(第几周), 且不显示横向表头(周几)
  255. QDate CalendarWidgetEx::dateForCell(int row, int column) const
  256. {
  257. if (row < 0 || row > 5 || column < 0 || column > 6)
  258. return QDate();
  259. const QDate refDate = referenceDate();
  260. if (!refDate.isValid())
  261. return QDate();
  262. const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
  263. if (columnForFirstOfShownMonth - 0/*m_firstColumn*/ < 1)
  264. row -= 1;
  265. const int requestedDay = 7 * (row - 0/*m_firstRow*/) + column - columnForFirstOfShownMonth - refDate.day() + 1;
  266. return refDate.addDays(requestedDay);
  267. }
  268. QDate CalendarWidgetEx::referenceDate() const
  269. {
  270. int refDay = 1;
  271. while (refDay <= 31) {
  272. QDate refDate(yearShown(), monthShown(), refDay);
  273. if (refDate.isValid())
  274. return refDate;
  275. refDay += 1;
  276. }
  277. return QDate();
  278. }
  279. int CalendarWidgetEx::columnForFirstOfMonth(const QDate &date) const
  280. {
  281. return (columnForDayOfWeek(date.dayOfWeek()) - (date.day() % 7) + 8) % 7;
  282. }
  283. int CalendarWidgetEx::columnForDayOfWeek(int day) const
  284. {
  285. if (day < 1 || day > 7)
  286. return -1;
  287. int column = day - firstDayOfWeek();
  288. if (column < 0)
  289. column += 7;
  290. return column;
  291. }
  292. void CalendarWidgetEx::initSkinColor()
  293. {
  294. switch (m_style)
  295. {
  296. case 0:
  297. m_normalTextColor = NORMAL_TEXT_BRIGHT;
  298. m_todayTextColor = TODAY_TEXT_BRIGHT;
  299. m_selectTextColor = SELECT_TEXT_BRIGHT;
  300. m_disableTextColor = DISABLE_TEXT_BRIGHT;
  301. m_splitLineColor = SPLIT_LINE_BRIGHT;
  302. m_selectBlockColor = SELECT_BRIGHT;
  303. m_hoverBlockColor = HOVER_BRIGHT;
  304. m_bgBrushColor = BG_BRUSH_BRIGHT;
  305. m_bgPenColor = BG_PEN_BRIGHT;
  306. break;
  307. case 1:
  308. m_normalTextColor = NORMAL_TEXT_DEEP;
  309. m_todayTextColor = TODAY_TEXT_DEEP;
  310. m_selectTextColor = SELECT_TEXT_DEEP;
  311. m_disableTextColor = DISABLE_TEXT_DEEP;
  312. m_splitLineColor = SPLIT_LINE_DEEP;
  313. m_selectBlockColor = SELECT_DEEP;
  314. m_hoverBlockColor = HOVER_DEEP;
  315. m_bgBrushColor = BG_BRUSH_DEEP;
  316. m_bgPenColor = BG_PEN_DEEP;
  317. break;
  318. default:
  319. break;
  320. }
  321. }
  322. void CalendarWidgetEx::OnClicked(const QDate &date)
  323. {
  324. if(m_modeSelection == Multi)
  325. {
  326. if(m_listMultiSelectDays.contains(date))
  327. {
  328. m_listMultiSelectDays.removeOne(date);
  329. }
  330. else
  331. {
  332. m_listMultiSelectDays.append(date);
  333. }
  334. }
  335. if(m_modeSelection == Scope && m_pDateScopeModel != nullptr)
  336. {
  337. if(!m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
  338. {
  339. m_pDateScopeModel->dtSecond = date;
  340. m_pDateScopeModel->bLocked = true;
  341. m_pDateScopeModel->Locked();
  342. }
  343. else
  344. {
  345. m_pDateScopeModel->dtFirst = date;
  346. m_pDateScopeModel->dtSecond = date;
  347. m_pDateScopeModel->bLocked = false;
  348. }
  349. m_pDateScopeModel->Update();
  350. }
  351. update();
  352. }
  353. void CalendarWidgetEx::leaveEvent(QEvent *)
  354. {
  355. if(m_modeSelection != Scope)
  356. {
  357. //离开日历时, 清空鼠标移入状态
  358. m_dateMouseOver = QDate();
  359. update();
  360. }
  361. }
  362. void NoFocusStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
  363. {
  364. QStyleOption *viewOption = new QStyleOption(*option);
  365. viewOption->state &= (~QStyle::State_HasFocus);
  366. //if (element == PE_FrameFocusRect) return;
  367. QProxyStyle::drawPrimitive(element, viewOption, painter, widget);
  368. delete viewOption;
  369. }