calendarwidgetex.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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. /* 设置对象名称,加载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. void CalendarWidgetEx::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
  101. {
  102. PainterEx *painterEx = static_cast<PainterEx*>(painter);
  103. painterEx->setRenderHint(QPainter::Antialiasing);
  104. QColor textColor;
  105. {//正常
  106. textColor = m_normalTextColor;
  107. }
  108. #if 0
  109. // 周六,周日特殊颜色
  110. int nWeek = date.dayOfWeek();
  111. if (6 == nWeek || 7 == nWeek) {
  112. textColor = QColor(255,149,0);
  113. }
  114. #endif
  115. //鼠标移入
  116. if(date == m_dateMouseOver)
  117. {
  118. QPoint center = rect.center();
  119. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  120. painterEx->DrawRoundedRect(rc, 2.0, m_hoverBlockColor);
  121. textColor = m_normalTextColor;
  122. }
  123. //当天
  124. if(date == QDate::currentDate())
  125. {
  126. painter->save();
  127. QPoint center = rect.center();
  128. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  129. painter->setPen(m_todayTextColor);
  130. painter->setBrush(Qt::transparent);
  131. painter->drawRoundedRect(rc, 2.0, 2.0);
  132. textColor = m_todayTextColor;
  133. painter->restore();
  134. }
  135. //选中
  136. if(m_modeSelection == Multi && m_listMultiSelectDays.contains(date))
  137. {
  138. //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9,109,217));
  139. QPoint center = rect.center();
  140. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  141. painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
  142. textColor = m_selectTextColor;
  143. }
  144. if(m_modeSelection == Normal && date == selectedDate())
  145. {
  146. //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9, 109, 217));
  147. QPoint center = rect.center();
  148. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  149. painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
  150. textColor = m_selectTextColor;
  151. }
  152. if(m_modeSelection == Scope && m_pDateScopeModel != nullptr && m_pDateScopeModel->dtFirst.isValid() && m_pDateScopeModel->dtSecond.isValid())
  153. {
  154. QDate scopeStart = qMin(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
  155. QDate scopeEnd = qMax(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond);
  156. if(date == qBound(scopeStart, date, scopeEnd) && date.month() == monthShown())
  157. {
  158. painterEx->SetBrushOnly(m_hoverBlockColor);
  159. if(date == scopeStart || date == scopeEnd)
  160. {
  161. textColor = m_selectTextColor;
  162. QPoint center = rect.center();
  163. QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH);
  164. painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor);
  165. }
  166. else
  167. {
  168. QRect r(0, 0, rect.width(), TEXT_WIDTH);
  169. r.moveCenter(rect.center());
  170. painterEx->drawRect(r);
  171. }
  172. }
  173. }
  174. //不可选的日期或非当月日期
  175. if(date < minimumDate() || date > maximumDate() || date.month() != monthShown())
  176. {
  177. textColor = m_disableTextColor;
  178. }
  179. //当天且未选中, 加粗
  180. bool isBold = (date == QDate::currentDate() && date != selectedDate());
  181. QString strDay(QString("%1").arg(date.day(), 2, 10, QLatin1Char('0')));
  182. painterEx->setFont(FontEx(font().family(), DEFAULT_FONT_SIZE, isBold));
  183. QRect rc = rect.adjusted(-1, 0, 0, -3); // 矫正文字位置
  184. painterEx->DrawText(rc, strDay, textColor, Qt::AlignCenter);
  185. }
  186. void CalendarWidgetEx::paintEvent(QPaintEvent *)
  187. {
  188. PainterEx painter(this);
  189. //边框和背景
  190. painter.setPen(Qt::transparent);
  191. painter.setBrush(m_bgBrushColor);
  192. QRect rc(rect());
  193. painter.DrawRoundedRect(rc, WINDOW_RADIUS);
  194. QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
  195. if(vBodyLayout != nullptr) {
  196. // 画导航栏,绘制上圆角
  197. QPainterPath path;
  198. path.setFillRule(Qt::WindingFill);
  199. QRectF tmpRc(1, 0, WINDOW_WIDTH - WINDOW_RADIUS, 40);
  200. path.addRoundedRect(tmpRc, WINDOW_RADIUS, WINDOW_RADIUS);
  201. path.addRect(QRectF(tmpRc.x(), tmpRc.y() + WINDOW_RADIUS, tmpRc.width(), tmpRc.height()));
  202. // painter.fillPath(path, QColor(255, 255, 255));
  203. painter.fillPath(path, m_bgBrushColor);
  204. //分割线
  205. QWidget* pNav = vBodyLayout->itemAt(0)->widget();
  206. if (nullptr != pNav) {
  207. int h = pNav->mapTo(this, pNav->rect().bottomRight()).y();
  208. // painter.SetPenOnly(QColor(0, 0, 0, 23));
  209. painter.setPen(m_bgPenColor);
  210. painter.drawLine(QPoint(0, h), QPoint(width(), h));
  211. }
  212. }
  213. }
  214. QSize CalendarWidgetEx::minimumSizeHint() const
  215. {
  216. return QSize(WINDOW_WIDTH, WINDOW_HEIGHT);
  217. }
  218. void CalendarWidgetEx::mouseMoveEvent(QMouseEvent *event)
  219. {
  220. QCalendarWidget::mouseMoveEvent(event);
  221. QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
  222. if(vBodyLayout == nullptr) return;
  223. QTableView *pCalendarView = dynamic_cast<QTableView*>(vBodyLayout->itemAt(4)->widget());
  224. if(pCalendarView == nullptr) return;
  225. QModelIndex index = pCalendarView->indexAt(pCalendarView->mapFromGlobal(event->globalPos()));
  226. QDate dateMouseOver = dateForCell(index.row(), index.column());
  227. if(m_dateMouseOver != dateMouseOver)
  228. {
  229. m_dateMouseOver = dateMouseOver;
  230. if(m_pDateScopeModel != nullptr && !m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
  231. {
  232. m_pDateScopeModel->dtSecond = dateMouseOver;
  233. m_pDateScopeModel->Update();
  234. }
  235. update();
  236. }
  237. }
  238. //仅适用于: 不显示纵向表头(第几周), 且不显示横向表头(周几)
  239. QDate CalendarWidgetEx::dateForCell(int row, int column) const
  240. {
  241. if (row < 0 || row > 5 || column < 0 || column > 6)
  242. return QDate();
  243. const QDate refDate = referenceDate();
  244. if (!refDate.isValid())
  245. return QDate();
  246. const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
  247. if (columnForFirstOfShownMonth - 0/*m_firstColumn*/ < 1)
  248. row -= 1;
  249. const int requestedDay = 7 * (row - 0/*m_firstRow*/) + column - columnForFirstOfShownMonth - refDate.day() + 1;
  250. return refDate.addDays(requestedDay);
  251. }
  252. QDate CalendarWidgetEx::referenceDate() const
  253. {
  254. int refDay = 1;
  255. while (refDay <= 31) {
  256. QDate refDate(yearShown(), monthShown(), refDay);
  257. if (refDate.isValid())
  258. return refDate;
  259. refDay += 1;
  260. }
  261. return QDate();
  262. }
  263. int CalendarWidgetEx::columnForFirstOfMonth(const QDate &date) const
  264. {
  265. return (columnForDayOfWeek(date.dayOfWeek()) - (date.day() % 7) + 8) % 7;
  266. }
  267. int CalendarWidgetEx::columnForDayOfWeek(int day) const
  268. {
  269. if (day < 1 || day > 7)
  270. return -1;
  271. int column = day - firstDayOfWeek();
  272. if (column < 0)
  273. column += 7;
  274. return column;
  275. }
  276. void CalendarWidgetEx::initSkinColor()
  277. {
  278. switch (LHStyleManager::Instance()->GetCurSkinStyle())
  279. {
  280. case eWhiteStyle:
  281. m_normalTextColor = NORMAL_TEXT_BRIGHT;
  282. m_todayTextColor = TODAY_TEXT_BRIGHT;
  283. m_selectTextColor = SELECT_TEXT_BRIGHT;
  284. m_disableTextColor = DISABLE_TEXT_BRIGHT;
  285. m_splitLineColor = SPLIT_LINE_BRIGHT;
  286. m_selectBlockColor = SELECT_BRIGHT;
  287. m_hoverBlockColor = HOVER_BRIGHT;
  288. m_bgBrushColor = BG_BRUSH_BRIGHT;
  289. m_bgPenColor = BG_PEN_BRIGHT;
  290. break;
  291. case eBlackStyle:
  292. m_normalTextColor = NORMAL_TEXT_DEEP;
  293. m_todayTextColor = TODAY_TEXT_DEEP;
  294. m_selectTextColor = SELECT_TEXT_DEEP;
  295. m_disableTextColor = DISABLE_TEXT_DEEP;
  296. m_splitLineColor = SPLIT_LINE_DEEP;
  297. m_selectBlockColor = SELECT_DEEP;
  298. m_hoverBlockColor = HOVER_DEEP;
  299. m_bgBrushColor = BG_BRUSH_DEEP;
  300. m_bgPenColor = BG_PEN_DEEP;
  301. break;
  302. default:
  303. break;
  304. }
  305. }
  306. void CalendarWidgetEx::OnClicked(const QDate &date)
  307. {
  308. if(m_modeSelection == Multi)
  309. {
  310. if(m_listMultiSelectDays.contains(date))
  311. {
  312. m_listMultiSelectDays.removeOne(date);
  313. }
  314. else
  315. {
  316. m_listMultiSelectDays.append(date);
  317. }
  318. }
  319. if(m_modeSelection == Scope && m_pDateScopeModel != nullptr)
  320. {
  321. if(!m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid())
  322. {
  323. m_pDateScopeModel->dtSecond = date;
  324. m_pDateScopeModel->bLocked = true;
  325. m_pDateScopeModel->Locked();
  326. }
  327. else
  328. {
  329. m_pDateScopeModel->dtFirst = date;
  330. m_pDateScopeModel->dtSecond = date;
  331. m_pDateScopeModel->bLocked = false;
  332. }
  333. m_pDateScopeModel->Update();
  334. }
  335. update();
  336. }
  337. void CalendarWidgetEx::leaveEvent(QEvent *)
  338. {
  339. if(m_modeSelection != Scope)
  340. {
  341. //离开日历时, 清空鼠标移入状态
  342. m_dateMouseOver = QDate();
  343. update();
  344. }
  345. }
  346. void NoFocusStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
  347. {
  348. QStyleOption *viewOption = new QStyleOption(*option);
  349. viewOption->state &= (~QStyle::State_HasFocus);
  350. //if (element == PE_FrameFocusRect) return;
  351. QProxyStyle::drawPrimitive(element, viewOption, painter, widget);
  352. delete viewOption;
  353. }