dde-dock/frame/window/components/datetimedisplayer.cpp
2023-10-26 11:39:57 +08:00

463 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (C) 2022 ~ 2023 Deepin Technology Co., Ltd.
// SPDX-FileCopyrightText: 2018 - 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "datetimedisplayer.h"
#include "tipswidget.h"
#include "dockpopupwindow.h"
#include "utils.h"
#include "dbusutil.h"
#include <DFontSizeManager>
#include <DDBusSender>
#include <DGuiApplicationHelper>
#include <DConfig>
#include <QHBoxLayout>
#include <QPainter>
#include <QFont>
#include <QMenu>
#include <QPainterPath>
#include <QMouseEvent>
#include <QFontMetrics>
DWIDGET_USE_NAMESPACE
DGUI_USE_NAMESPACE
#define DATETIMESIZE 40
#define ITEMSPACE 8
static QMap<int, QString> dateFormat{{ 0,"yyyy/M/d" }, { 1,"yyyy-M-d" }, { 2,"yyyy.M.d" }, { 3,"yyyy/MM/dd" },
{ 4,"yyyy-MM-dd" }, { 5,"yyyy.MM.dd" }, { 6,"yy/M/d" }, { 7,"yy-M-d" }, { 8,"yy.M.d" }};
static QMap<int, QString> timeFormat{{0, "h:mm"}, {1, "hh:mm"}};
const QString localeName_key = "localeName";
const QString shortDateFormat_key = "shortDateFormat";
const QString shortTimeFormat_key = "shortTimeFormat";
DateTimeDisplayer::DateTimeDisplayer(bool showMultiRow, QWidget *parent)
: QWidget (parent)
, m_timedateInter(new Timedate("org.deepin.dde.Timedate1", "/org/deepin/dde/Timedate1", QDBusConnection::sessionBus(), this))
, m_position(Dock::Position::Bottom)
, m_dateFont(QFont())
, m_timeFont(QFont())
, m_tipsWidget(new Dock::TipsWidget(this))
, m_menu(new QMenu(this))
, m_tipsTimer(new QTimer(this))
, m_currentSize(0)
, m_oneRow(false)
, m_showMultiRow(showMultiRow)
, m_config(DTK_CORE_NAMESPACE::DConfig::createGeneric("org.deepin.region-format", QString(), this))
{
m_tipPopupWindow.reset(new DockPopupWindow);
// 日期格式变化的时候,需要重绘
connect(m_timedateInter, &Timedate::ShortDateFormatChanged, this, &DateTimeDisplayer::onDateTimeFormatChanged);
// 时间格式变化的时候,需要重绘
connect(m_timedateInter, &Timedate::ShortTimeFormatChanged, this, &DateTimeDisplayer::onDateTimeFormatChanged);
// 是否使用24小时制发生变化的时候也需要重绘
connect(m_timedateInter, &Timedate::Use24HourFormatChanged, this, &DateTimeDisplayer::onDateTimeFormatChanged);
// 连接日期时间修改信号,更新日期时间插件的布局
connect(m_timedateInter, &Timedate::TimeUpdate, this, static_cast<void (QWidget::*)()>(&DateTimeDisplayer::update));
// 连接定时器和时间显示的tips信号,一秒钟触发一次,显示时间
connect(m_tipsTimer, &QTimer::timeout, this, &DateTimeDisplayer::onTimeChanged);
QMetaObject::invokeMethod(this, "onDateTimeFormatChanged");
m_tipsTimer->setInterval(1000);
m_tipsTimer->start();
updatePolicy();
createMenuItem();
if (Utils::IS_WAYLAND_DISPLAY)
m_tipPopupWindow->setWindowFlags(m_tipPopupWindow->windowFlags() | Qt::FramelessWindowHint);
m_tipPopupWindow->hide();
m_locale = QLocale::system();
if (!m_config->isValid())
return;
if (!m_config->isDefaultValue(localeName_key)) {
m_locale = QLocale(m_config->value(localeName_key).toString());
}
if (!m_config->isDefaultValue(shortDateFormat_key)) {
m_shortDateFormatStr = m_config->value(shortDateFormat_key).toString();
}
if (!m_config->isDefaultValue(shortTimeFormat_key)) {
m_shortTimeFormatStr = m_config->value(shortTimeFormat_key).toString();
}
connect(m_config, &DTK_CORE_NAMESPACE::DConfig::valueChanged, this, [this] (const QString &key) {
if (key == shortDateFormat_key) {
m_shortDateFormatStr = m_config->value(key).toString();
} else if (key == shortTimeFormat_key) {
m_shortTimeFormatStr = m_config->value(key).toString();
} else if (key == localeName_key) {
m_locale = QLocale(m_config->value(key).toString());
}
update();
});
}
DateTimeDisplayer::~DateTimeDisplayer()
{
}
void DateTimeDisplayer::setPositon(Dock::Position position)
{
if (m_position == position)
return;
m_position = position;
updatePolicy();
update();
}
void DateTimeDisplayer::setOneRow(bool oneRow)
{
m_oneRow = oneRow;
update();
}
void DateTimeDisplayer::updatePolicy()
{
switch(m_position) {
case Dock::Position::Top:
case Dock::Position::Bottom:
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
break;
case Dock::Position::Left:
case Dock::Position::Right:
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
break;
}
m_tipPopupWindow->setPosition(m_position);
m_tipPopupWindow->setContent(m_tipsWidget);
}
QSize DateTimeDisplayer::suitableSize() const
{
return suitableSize(m_position);
}
QSize DateTimeDisplayer::suitableSize(const Dock::Position &position) const
{
DateTimeInfo info = dateTimeInfo(position);
if (position == Dock::Position::Left || position == Dock::Position::Right)
return QSize(width(), info.m_timeRect.height() + info.m_dateRect.height());
// 如果在上下显示
if (m_showMultiRow) {
// 如果显示多行的情况,一般是在高效模式下显示,因此,返回最大的尺寸
return QSize(qMax(info.m_timeRect.width(), info.m_dateRect.width()), height());
}
return QSize(info.m_timeRect.width() + info.m_dateRect.width() + 16, height());
}
void DateTimeDisplayer::mousePressEvent(QMouseEvent *event)
{
if ((event->button() != Qt::RightButton))
return QWidget::mousePressEvent(event);
m_menu->exec(QCursor::pos());
}
void DateTimeDisplayer::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event);
DDBusSender().service("org.deepin.dde.Widgets1")
.path("/org/deepin/dde/Widgets1")
.interface("org.deepin.dde.Widgets1")
.method("Toggle").call();
}
QString DateTimeDisplayer::getTimeString(const Dock::Position &position) const
{
QString tFormat = QString("hh:mm");
if (timeFormat.contains(m_shortDateFormat))
tFormat = timeFormat[m_shortDateFormat];
if (!m_shortTimeFormatStr.isEmpty())
tFormat = m_shortTimeFormatStr;
if (!m_use24HourFormat) {
if (position == Dock::Top || position == Dock::Bottom)
tFormat = tFormat.append(" AP");
else
tFormat = tFormat.append("\nAP");
}
return m_locale.toString(QDateTime::currentDateTime(), tFormat);
}
QString DateTimeDisplayer::getDateString() const
{
return getDateString(m_position);
}
QString DateTimeDisplayer::getDateString(const Dock::Position &position) const
{
QString shortDateFormat = "yyyy-MM-dd";
if (dateFormat.contains(m_shortDateFormat))
shortDateFormat = dateFormat.value(m_shortDateFormat);
if (!m_shortDateFormatStr.isEmpty())
shortDateFormat = m_shortDateFormatStr;
// 如果是左右方向,则不显示年份
if (position == Dock::Position::Left || position == Dock::Position::Right) {
static QStringList yearStrList{"yyyy/", "/yyyy", "yyyy-", "-yyyy", "yyyy.", ".yyyy",
"yy/", "/yy", "yy-", "-yy", "yy.", ".yy"};
for (int i = 0; i < yearStrList.size() ; i++) {
const QString &yearStr = yearStrList[i];
if (shortDateFormat.contains(yearStr)) {
shortDateFormat = shortDateFormat.remove(yearStr);
break;
}
}
}
return m_locale.toString(QDateTime::currentDateTime(), shortDateFormat);
}
DateTimeDisplayer::DateTimeInfo DateTimeDisplayer::dateTimeInfo(const Dock::Position &position) const
{
DateTimeInfo info;
info.m_timeRect = rect();
info.m_dateRect = rect();
info.m_time = getTimeString(position);
info.m_date = getDateString(position);
// 如果是左右方向
if (position == Dock::Position::Left || position == Dock::Position::Right) {
int textWidth = rect().width();
int timeHeight = QFontMetrics(m_timeFont).boundingRect(info.m_time).height() * (info.m_time.count('\n') + 1);
int dateHeight = QFontMetrics(m_dateFont).boundingRect(info.m_date).height();
info.m_timeRect = QRect(0, 0, textWidth, timeHeight);
info.m_dateRect = QRect(0, timeHeight, textWidth, dateHeight);
return info;
}
int timeWidth = QFontMetrics(m_timeFont).boundingRect(info.m_time).width() + 2;
int dateWidth = QFontMetrics(m_dateFont).boundingRect(info.m_date).width() + 2;
int rHeight = height();
// 如果是上下方向
if (m_showMultiRow) {
// 日期时间多行显示一般是高效模式下向下和向上偏移2个像素
info.m_timeRect = QRect(0, 2, timeWidth, rHeight / 2);
info.m_dateRect = QRect(0, rHeight / 2 - 2, dateWidth, rHeight / 2);
} else {
// 3:时间和日期3部分间隔
if (rect().width() > (ITEMSPACE * 3 + timeWidth + dateWidth)) {
info.m_timeRect = QRect(ITEMSPACE, 0, timeWidth, rHeight);
int dateX = info.m_timeRect.right() + (rect().width() -(ITEMSPACE * 2 + timeWidth + dateWidth));
info.m_dateRect = QRect(dateX, 0, dateWidth, rHeight);
} else {
// 宽度不满足间隔为ITEMSPACE的需要自己计算间隔。
int itemSpace = (rect().width() - timeWidth - dateWidth) / 3;
info.m_timeRect = QRect(itemSpace, 0, timeWidth, rHeight);
int dateX = info.m_timeRect.right() + itemSpace;
info.m_dateRect = QRect(dateX, 0, dateWidth, rHeight);
}
}
return info;
}
void DateTimeDisplayer::onTimeChanged()
{
const QDateTime currentDateTime = QDateTime::currentDateTime();
if (m_use24HourFormat)
m_tipsWidget->setText(QLocale().toString(currentDateTime.date()) + currentDateTime.toString(" HH:mm:ss"));
else
m_tipsWidget->setText(QLocale().toString(currentDateTime.date()) + currentDateTime.toString(" hh:mm:ss AP"));
// 如果时间和日期有一个不等,则实时刷新界面
if (m_lastDateString != getDateString() || m_lastTimeString != getTimeString())
update();
}
void DateTimeDisplayer::onDateTimeFormatChanged()
{
m_shortDateFormat = m_timedateInter->shortDateFormat();
m_use24HourFormat = m_timedateInter->use24HourFormat();
// 此处需要强制重绘因为在重绘过程中才会改变m_currentSize信息方便在后面判断是否需要调整尺寸
repaint();
}
void DateTimeDisplayer::paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
DateTimeInfo info = dateTimeInfo(m_position);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
int timeAlignFlag = Qt::AlignCenter;
int dateAlignFlag = Qt::AlignCenter;
if (m_showMultiRow) {
timeAlignFlag = Qt::AlignHCenter | Qt::AlignBottom;
dateAlignFlag = Qt::AlignHCenter | Qt::AlignTop;
}
painter.setFont(m_timeFont);
painter.setPen(QPen(palette().brightText(), 2));
painter.drawText(textRect(info.m_timeRect), timeAlignFlag, info.m_time);
painter.setFont(m_dateFont);
painter.setPen(QPen(palette().brightText(), 1));
painter.drawText(textRect(info.m_dateRect), dateAlignFlag, info.m_date);
updateLastData(info);
}
QPoint DateTimeDisplayer::tipsPoint() const
{
QPoint pointInTopWidget = parentWidget()->mapTo(window(), pos());
switch (m_position) {
case Dock::Position::Left: {
pointInTopWidget.setX(window()->x() + window()->width());
pointInTopWidget.setY(pointInTopWidget.y() + height() / 2);
break;
}
case Dock::Position::Top: {
pointInTopWidget.setY(y() + window()->y() + window()->height());
pointInTopWidget.setX(pointInTopWidget.x() + width() / 2);
break;
}
case Dock::Position::Right: {
pointInTopWidget.setY(pointInTopWidget.y() + height() / 2);
pointInTopWidget.setX(pointInTopWidget.x() - width() / 2);
break;
}
case Dock::Position::Bottom: {
pointInTopWidget.setY(-POPUP_PADDING);
pointInTopWidget.setX(pointInTopWidget.x() + width() / 2);
break;
}
}
return window()->mapToGlobal(pointInTopWidget);
}
void DateTimeDisplayer::updateFont() const
{
auto info = getTimeString(m_position);
// "xx:xx\nAP" 获取到前 xx:xx 部分
info = info.left(info.indexOf('\n'));
if (m_position == Dock::Position::Left || m_position == Dock::Position::Right) {
auto f = QFont();
bool caled = false;
f.setPixelSize(100);
// 左右时根据获取可以全部显示文本的最小的宽度, 且最大只到40
while(width() > 0 && f.pixelSize() > 2 &&
(QFontMetrics(f).boundingRect(info).width() > qMin(DATETIMESIZE, width()) - 4)) {
f.setPixelSize(f.pixelSize() - 1);
caled = true;
}
// 经过正确的计算后才能更新字体大小
if (caled) {
m_timeFont.setPixelSize(f.pixelSize());
m_dateFont.setPixelSize(f.pixelSize() - 2);
}
return;
}
if ((Dock::Position::Top == m_position || Dock::Position::Bottom == m_position )) {
// 单行时保持高度的一半双行时尽量和高度一致但最大只到12。
auto s = height() / (m_oneRow ? 2 : 1) - 2;
m_timeFont.setPixelSize(std::min(s, 12));
// 双行时日期比时间字体小两个像素。
m_dateFont.setPixelSize(std::min(s, 12) - (m_oneRow ? 0 : 2));
}
}
void DateTimeDisplayer::createMenuItem()
{
QAction *timeFormatAction = new QAction(this);
timeFormatAction->setText(m_use24HourFormat ? tr("12-hour time"): tr("24-hour time"));
connect(timeFormatAction, &QAction::triggered, this, [ = ] {
bool use24hourformat = !m_use24HourFormat;
// 此时调用 dbus 更新时间格式但是本地 m_use24HourFormat 未更新,所以需要使用新变量,设置新格式
m_timedateInter->setUse24HourFormat(use24hourformat);
timeFormatAction->setText(use24hourformat ? tr("12-hour time") : tr("24-hour time"));
});
m_menu->addAction(timeFormatAction);
if (!QFile::exists(ICBC_CONF_FILE)) {
QAction *timeSettingAction = new QAction(tr("Time settings"), this);
connect(timeSettingAction, &QAction::triggered, this, [ = ] {
DDBusSender()
.service(controllCenterService)
.path(controllCenterPath)
.interface(controllCenterInterface)
.method(QString("ShowPage"))
.arg(QString("datetime"))
.call();
});
m_menu->addAction(timeSettingAction);
}
}
QRect DateTimeDisplayer::textRect(const QRect &sourceRect) const
{
// 如果是上下,则不做任何变化
if (!m_showMultiRow && (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom))
return sourceRect;
QRect resultRect = sourceRect;
QSize size = suitableSize();
// 如果是左右或者上下多行显示,设置宽度
resultRect.setWidth(size.width());
return resultRect;
}
void DateTimeDisplayer::enterEvent(QEvent *event)
{
Q_UNUSED(event);
Q_EMIT requestDrawBackground(rect());
update();
m_tipPopupWindow->show(tipsPoint());
}
void DateTimeDisplayer::leaveEvent(QEvent *event)
{
Q_UNUSED(event);
Q_EMIT requestDrawBackground(QRect());
update();
m_tipPopupWindow->hide();
}
QString DateTimeDisplayer::getTimeString() const
{
return getTimeString(m_position);
}
void DateTimeDisplayer::updateLastData(const DateTimeInfo &info)
{
int lastSize = m_currentSize;
m_lastDateString = info.m_date;
m_lastTimeString = info.m_time;
QSize dateTimeSize = suitableSize();
if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom)
m_currentSize = dateTimeSize.width();
else
m_currentSize = dateTimeSize.height();
// 如果日期时间的格式发生了变化,需要通知外部来调整日期时间的尺寸
if (lastSize != m_currentSize)
Q_EMIT requestUpdate();
}
bool DateTimeDisplayer::event(QEvent *event)
{
if (event->type() == QEvent::Resize) {
updateFont();
}
return QWidget::event(event);
}