//qcustomcalendarwidget.cpp #include "calendarwidgetex.h" #include #include #include #include #include #include #include #include #include #include #include #include "scopeselectionmodel.h" #include "PaintHelper/painthelper.h" // #include "StyleManager/lhstylemanager.h" //#include "utility/utility.h" #include "UIStyleManager.h" CalendarWidgetEx::CalendarWidgetEx(QWidget *parent) : QCalendarWidget(parent) , m_modeSelection(Normal) , m_pDateScopeModel(nullptr) , m_nLineHeight(-1) , m_hasTopSplitLine(false) { setWindowFlag(Qt::NoDropShadowWindowHint); setAttribute(Qt::WA_TranslucentBackground); setWindowFlag(Qt::FramelessWindowHint); //使用固定尺寸(无法通过resize控制日历大小, 日历整体大小由layout下的控件的fixSize决定) //layout()->setSizeConstraint(QLayout::SetFixedSize); //禁用原有的年月导航 setNavigationBarVisible(false); //禁用横向纵向表头 setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader); setHorizontalHeaderFormat(QCalendarWidget::NoHorizontalHeader); //取消聚焦虚线框 setStyle(new NoFocusStyle(this)); QVBoxLayout *vBodyLayout = qobject_cast(layout()); if(vBodyLayout == nullptr) return; m_pNav = new CalendarNav(this); m_pHeader = new CalendarHeader(this); setFirstDayOfWeek(Qt::Sunday); m_pHeader->SetFirstDayOfWeek(Qt::Sunday); vBodyLayout->insertWidget(0, m_pNav); // 导航和星期标题间距 vBodyLayout->insertSpacing(1, 10); vBodyLayout->insertWidget(2, m_pHeader); vBodyLayout->setSpacing(10); vBodyLayout->setContentsMargins(10,0,10,10); m_nLineHeight = vBodyLayout->itemAt(4)->widget()->mapTo(this, QPoint(0,0)).y(); //qDebug()<<"m_nLineHeight"<itemAt(3)->widget()->height(); //开启鼠标监测 QTableView *pCalendarView = dynamic_cast(vBodyLayout->itemAt(4)->widget()); if (Q_NULLPTR != pCalendarView) { pCalendarView->setMouseTracking(true); } //在构造函数里取消selectionChanged, clicked事件没用, 因为执行QDateTimeEdit的setCalendarWidget方法时, 会重新绑定 //所以必须等setCalendarWidget执行完后再取消事件 //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::selectionChanged, 0, 0); //calendarWidget->disconnect(calendarWidget, &QCalendarWidget::clicked, 0, 0); setMouseTracking(true); // 设置默认字体后,修复单元格变得很宽的问题 for (QWidget* f : findChildren()) { if(f->objectName() != "qt_calendar_calendarview") continue; QTableView* pView = reinterpret_cast(f); if (nullptr != pView && nullptr != pView->horizontalHeader()) { pView->horizontalHeader()->setMaximumSectionSize(WINDOW_WIDTH / 8); } } connect(this, &QCalendarWidget::clicked, this, &CalendarWidgetEx::OnClicked); // initSkinColor(); setUIStyle(); /* 设置对象名称,加载QSS */ // this->setObjectName("calendarWidget"); this->setStyleSheet("background-color:transparent;"); } void CalendarWidgetEx::SetSelectMode(SelectMode mode, ScopeSelectionModel *pDataModel) { m_listMultiSelectDays.clear(); m_modeSelection = mode; if(m_pDateScopeModel == nullptr) { m_pDateScopeModel = pDataModel; connect(m_pDateScopeModel, &ScopeSelectionModel::sig_Update, this, static_cast(&QWidget::update)); } update(); } void CalendarWidgetEx::hideNavigatioinButton(bool bPreYear, bool bPreMon, bool bNextYear, bool bNextMon) { QVBoxLayout *vBodyLayout = qobject_cast(layout()); if(vBodyLayout == nullptr) return; CalendarNav *pNav = qobject_cast(vBodyLayout->itemAt(0)->widget()); if (nullptr != pNav) { pNav->hideNextMonth(bNextMon); pNav->hideNextYear(bNextYear); pNav->hidePreMonth(bPreMon); pNav->hidePreYear(bPreYear); } } /* 设置UI样式,0亮色,1暗色 */ void CalendarWidgetEx::setUIStyle() { if(EPUIStyle.getUIStyle() == enum_UIStyle::UI_Light) { m_style = 0; } else if(EPUIStyle.getUIStyle() == enum_UIStyle::UI_Dark) { m_style = 1; } initSkinColor(); m_pNav->setQSS(m_style); m_pHeader->setQSS(m_style); update(); } void CalendarWidgetEx::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const { PainterEx *painterEx = static_cast(painter); painterEx->setRenderHint(QPainter::Antialiasing); QColor textColor; {//正常 textColor = m_normalTextColor; } #if 0 // 周六,周日特殊颜色 int nWeek = date.dayOfWeek(); if (6 == nWeek || 7 == nWeek) { textColor = QColor(255,149,0); } #endif //鼠标移入 if(date == m_dateMouseOver) { QPoint center = rect.center(); QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH); painterEx->DrawRoundedRect(rc, 2.0, m_hoverBlockColor); textColor = m_normalTextColor; } //当天 if(date == QDate::currentDate()) { painter->save(); QPoint center = rect.center(); QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH); painter->setPen(m_todayTextColor); painter->setBrush(Qt::transparent); painter->drawRoundedRect(rc, 2.0, 2.0); textColor = m_todayTextColor; painter->restore(); } //选中 if(m_modeSelection == Multi && m_listMultiSelectDays.contains(date)) { //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9,109,217)); QPoint center = rect.center(); QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH); painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor); textColor = m_selectTextColor; } if(m_modeSelection == Normal && date == selectedDate()) { //painterEx->DrawCircle(QRectF(rect).center(), 9, QColor(9, 109, 217)); QPoint center = rect.center(); QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH); painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor); textColor = m_selectTextColor; } if(m_modeSelection == Scope && m_pDateScopeModel != nullptr && m_pDateScopeModel->dtFirst.isValid() && m_pDateScopeModel->dtSecond.isValid()) { QDate scopeStart = qMin(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond); QDate scopeEnd = qMax(m_pDateScopeModel->dtFirst, m_pDateScopeModel->dtSecond); if(date == qBound(scopeStart, date, scopeEnd) && date.month() == monthShown()) { painterEx->SetBrushOnly(m_hoverBlockColor); if(date == scopeStart || date == scopeEnd) { textColor = m_selectTextColor; QPoint center = rect.center(); QRect rc(center.x() - TEXT_WIDTH / 2, center.y() - TEXT_WIDTH / 2, TEXT_WIDTH, TEXT_WIDTH); painterEx->DrawRoundedRect(rc, 2.0, m_selectBlockColor); } else { QRect r(0, 0, rect.width(), TEXT_WIDTH); r.moveCenter(rect.center()); painterEx->drawRect(r); } } } //不可选的日期或非当月日期 if(date < minimumDate() || date > maximumDate() || date.month() != monthShown()) { textColor = m_disableTextColor; } //当天且未选中, 加粗 bool isBold = (date == QDate::currentDate() && date != selectedDate()); QString strDay(QString("%1").arg(date.day(), 2, 10, QLatin1Char('0'))); painterEx->setFont(FontEx(font().family(), DEFAULT_FONT_SIZE, isBold)); QRect rc = rect.adjusted(-1, 0, 0, -3); // 矫正文字位置 painterEx->DrawText(rc, strDay, textColor, Qt::AlignCenter); } void CalendarWidgetEx::paintEvent(QPaintEvent *) { PainterEx painter(this); //边框和背景 painter.setPen(Qt::transparent); painter.setBrush(m_bgBrushColor); QRect rc(rect()); painter.DrawRoundedRect(rc, WINDOW_RADIUS); QVBoxLayout *vBodyLayout = qobject_cast(layout()); if(vBodyLayout != nullptr) { // 画导航栏,绘制上圆角 QPainterPath path; path.setFillRule(Qt::WindingFill); QRectF tmpRc(1, 0, WINDOW_WIDTH - WINDOW_RADIUS, 40); path.addRoundedRect(tmpRc, WINDOW_RADIUS, WINDOW_RADIUS); path.addRect(QRectF(tmpRc.x(), tmpRc.y() + WINDOW_RADIUS, tmpRc.width(), tmpRc.height())); // painter.fillPath(path, QColor(255, 255, 255)); painter.fillPath(path, m_bgBrushColor); //分割线 QWidget* pNav = vBodyLayout->itemAt(0)->widget(); if (nullptr != pNav) { int h = pNav->mapTo(this, pNav->rect().bottomRight()).y() + 2; // painter.SetPenOnly(QColor(0, 0, 0, 23)); painter.setPen(m_bgPenColor); painter.drawLine(QPoint(0, h), QPoint(width(), h)); } } } QSize CalendarWidgetEx::minimumSizeHint() const { return QSize(WINDOW_WIDTH, WINDOW_HEIGHT); } void CalendarWidgetEx::mouseMoveEvent(QMouseEvent *event) { QCalendarWidget::mouseMoveEvent(event); QVBoxLayout *vBodyLayout = qobject_cast(layout()); if(vBodyLayout == nullptr) return; QTableView *pCalendarView = dynamic_cast(vBodyLayout->itemAt(4)->widget()); if(pCalendarView == nullptr) return; QModelIndex index = pCalendarView->indexAt(pCalendarView->mapFromGlobal(event->globalPos())); QDate dateMouseOver = dateForCell(index.row(), index.column()); if(m_dateMouseOver != dateMouseOver) { m_dateMouseOver = dateMouseOver; if(m_pDateScopeModel != nullptr && !m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid()) { m_pDateScopeModel->dtSecond = dateMouseOver; m_pDateScopeModel->Update(); } update(); } } //仅适用于: 不显示纵向表头(第几周), 且不显示横向表头(周几) QDate CalendarWidgetEx::dateForCell(int row, int column) const { if (row < 0 || row > 5 || column < 0 || column > 6) return QDate(); const QDate refDate = referenceDate(); if (!refDate.isValid()) return QDate(); const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate); if (columnForFirstOfShownMonth - 0/*m_firstColumn*/ < 1) row -= 1; const int requestedDay = 7 * (row - 0/*m_firstRow*/) + column - columnForFirstOfShownMonth - refDate.day() + 1; return refDate.addDays(requestedDay); } QDate CalendarWidgetEx::referenceDate() const { int refDay = 1; while (refDay <= 31) { QDate refDate(yearShown(), monthShown(), refDay); if (refDate.isValid()) return refDate; refDay += 1; } return QDate(); } int CalendarWidgetEx::columnForFirstOfMonth(const QDate &date) const { return (columnForDayOfWeek(date.dayOfWeek()) - (date.day() % 7) + 8) % 7; } int CalendarWidgetEx::columnForDayOfWeek(int day) const { if (day < 1 || day > 7) return -1; int column = day - firstDayOfWeek(); if (column < 0) column += 7; return column; } void CalendarWidgetEx::initSkinColor() { switch (m_style) { case 0: m_normalTextColor = NORMAL_TEXT_BRIGHT; m_todayTextColor = TODAY_TEXT_BRIGHT; m_selectTextColor = SELECT_TEXT_BRIGHT; m_disableTextColor = DISABLE_TEXT_BRIGHT; m_splitLineColor = SPLIT_LINE_BRIGHT; m_selectBlockColor = SELECT_BRIGHT; m_hoverBlockColor = HOVER_BRIGHT; m_bgBrushColor = BG_BRUSH_BRIGHT; m_bgPenColor = BG_PEN_BRIGHT; break; case 1: m_normalTextColor = NORMAL_TEXT_DEEP; m_todayTextColor = TODAY_TEXT_DEEP; m_selectTextColor = SELECT_TEXT_DEEP; m_disableTextColor = DISABLE_TEXT_DEEP; m_splitLineColor = SPLIT_LINE_DEEP; m_selectBlockColor = SELECT_DEEP; m_hoverBlockColor = HOVER_DEEP; m_bgBrushColor = BG_BRUSH_DEEP; m_bgPenColor = BG_PEN_DEEP; break; default: break; } } void CalendarWidgetEx::OnClicked(const QDate &date) { if(m_modeSelection == Multi) { if(m_listMultiSelectDays.contains(date)) { m_listMultiSelectDays.removeOne(date); } else { m_listMultiSelectDays.append(date); } } if(m_modeSelection == Scope && m_pDateScopeModel != nullptr) { if(!m_pDateScopeModel->bLocked && m_pDateScopeModel->dtFirst.isValid()) { m_pDateScopeModel->dtSecond = date; m_pDateScopeModel->bLocked = true; m_pDateScopeModel->Locked(); } else { m_pDateScopeModel->dtFirst = date; m_pDateScopeModel->dtSecond = date; m_pDateScopeModel->bLocked = false; } m_pDateScopeModel->Update(); } update(); } void CalendarWidgetEx::leaveEvent(QEvent *) { if(m_modeSelection != Scope) { //离开日历时, 清空鼠标移入状态 m_dateMouseOver = QDate(); update(); } } void NoFocusStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { QStyleOption *viewOption = new QStyleOption(*option); viewOption->state &= (~QStyle::State_HasFocus); //if (element == PE_FrameFocusRect) return; QProxyStyle::drawPrimitive(element, viewOption, painter, widget); delete viewOption; }