|
@@ -0,0 +1,373 @@
|
|
|
+//qcustomcalendarwidget.cpp
|
|
|
+#include "calendarwidgetex.h"
|
|
|
+#include <QPainter>
|
|
|
+#include <QProxyStyle>
|
|
|
+#include <QTableView>
|
|
|
+#include <QHeaderView>
|
|
|
+#include <QLayout>
|
|
|
+#include <QPushButton>
|
|
|
+#include <QLabel>
|
|
|
+#include <QDebug>
|
|
|
+#include <QLayoutItem>
|
|
|
+#include <QKeyEvent>
|
|
|
+#include <QPainterPath>
|
|
|
+
|
|
|
+#include "scopeselectionmodel.h"
|
|
|
+#include "calendarheader.h"
|
|
|
+#include "calendarnav.h"
|
|
|
+#include "PaintHelper/painthelper.h"
|
|
|
+#include "StyleManager/lhstylemanager.h"
|
|
|
+//#include "utility/utility.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<QVBoxLayout *>(layout());
|
|
|
+ if(vBodyLayout == nullptr) return;
|
|
|
+
|
|
|
+ CalendarNav *pNav = new CalendarNav(this);
|
|
|
+ CalendarHeader *pHeader = new CalendarHeader(this);
|
|
|
+ setFirstDayOfWeek(Qt::Sunday);
|
|
|
+ pHeader->SetFirstDayOfWeek(Qt::Sunday);
|
|
|
+
|
|
|
+ vBodyLayout->insertWidget(0, pNav);
|
|
|
+ // 导航和星期标题间距
|
|
|
+ vBodyLayout->insertSpacing(1, 10);
|
|
|
+ vBodyLayout->insertWidget(2, 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"<<m_nLineHeight<<vBodyLayout->itemAt(3)->widget()->height();
|
|
|
+
|
|
|
+ //开启鼠标监测
|
|
|
+ QTableView *pCalendarView = dynamic_cast<QTableView*>(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<QWidget*>()) {
|
|
|
+ if(f->objectName() != "qt_calendar_calendarview") continue;
|
|
|
+ QTableView* pView = reinterpret_cast<QTableView*>(f);
|
|
|
+ if (nullptr != pView && nullptr != pView->horizontalHeader()) {
|
|
|
+ pView->horizontalHeader()->setMaximumSectionSize(WINDOW_WIDTH / 8);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ connect(this, &QCalendarWidget::clicked, this, &CalendarWidgetEx::OnClicked);
|
|
|
+ initSkinColor();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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<void(QWidget::*)()>(&QWidget::update));
|
|
|
+ }
|
|
|
+ update();
|
|
|
+}
|
|
|
+
|
|
|
+void CalendarWidgetEx::hideNavigatioinButton(bool bPreYear, bool bPreMon, bool bNextYear, bool bNextMon)
|
|
|
+{
|
|
|
+ QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(layout());
|
|
|
+ if(vBodyLayout == nullptr) return;
|
|
|
+ CalendarNav *pNav = qobject_cast<CalendarNav*>(vBodyLayout->itemAt(0)->widget());
|
|
|
+ if (nullptr != pNav) {
|
|
|
+ pNav->hideNextMonth(bNextMon);
|
|
|
+ pNav->hideNextYear(bNextYear);
|
|
|
+ pNav->hidePreMonth(bPreMon);
|
|
|
+ pNav->hidePreYear(bPreYear);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CalendarWidgetEx::paintCell(QPainter *painter, const QRect &rect, const QDate &date) const
|
|
|
+{
|
|
|
+ PainterEx *painterEx = static_cast<PainterEx*>(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(QColor(255, 255, 255));
|
|
|
+ QRect rc(rect());
|
|
|
+ painter.DrawRoundedRect(rc, WINDOW_RADIUS);
|
|
|
+
|
|
|
+ QVBoxLayout *vBodyLayout = qobject_cast<QVBoxLayout *>(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));
|
|
|
+ //分割线
|
|
|
+ QWidget* pNav = vBodyLayout->itemAt(0)->widget();
|
|
|
+ if (nullptr != pNav) {
|
|
|
+ int h = pNav->mapTo(this, pNav->rect().bottomRight()).y();
|
|
|
+ painter.SetPenOnly(QColor(0, 0, 0, 23));
|
|
|
+ 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<QVBoxLayout *>(layout());
|
|
|
+ if(vBodyLayout == nullptr) return;
|
|
|
+ QTableView *pCalendarView = dynamic_cast<QTableView*>(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 (LHStyleManager::Instance()->GetCurSkinStyle()) {
|
|
|
+ case eBrightStyle:
|
|
|
+ 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;
|
|
|
+ break;
|
|
|
+ case eDarkStyle:
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+
|