calendarwidgetex.cpp 13 KB

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