mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
356 lines
10 KiB
C++
356 lines
10 KiB
C++
#include "widgetutils.h"
|
|
|
|
#include <QGuiApplication>
|
|
#include <QScreen>
|
|
#include <QWidget>
|
|
#include <QStyle>
|
|
#include <QAbstractScrollArea>
|
|
#include <QScrollBar>
|
|
#include <QDesktopWidget>
|
|
#include <QApplication>
|
|
#include <QDesktopServices>
|
|
#include <QKeyEvent>
|
|
#include <QActionGroup>
|
|
#include <QAction>
|
|
#include <QKeySequence>
|
|
#include <QScrollArea>
|
|
#include <QTimer>
|
|
#include <QShortcut>
|
|
#include <QListView>
|
|
#include <QModelIndex>
|
|
#include <QFontDatabase>
|
|
#include <QMenu>
|
|
#include <QDebug>
|
|
|
|
using namespace vnotex;
|
|
|
|
void WidgetUtils::setPropertyDynamically(QWidget *p_widget,
|
|
const char *p_prop,
|
|
const QVariant &p_val)
|
|
{
|
|
p_widget->setProperty(p_prop, p_val);
|
|
updateStyle(p_widget);
|
|
}
|
|
|
|
void WidgetUtils::updateStyle(QWidget *p_widget)
|
|
{
|
|
p_widget->style()->unpolish(p_widget);
|
|
p_widget->style()->polish(p_widget);
|
|
p_widget->update();
|
|
}
|
|
|
|
qreal WidgetUtils::calculateScaleFactor(bool p_update)
|
|
{
|
|
static qreal factor = -1;
|
|
|
|
if (factor < 0 || p_update) {
|
|
factor = QGuiApplication::primaryScreen()->devicePixelRatio();
|
|
}
|
|
|
|
return factor;
|
|
}
|
|
|
|
bool WidgetUtils::isScrollBarVisible(QAbstractScrollArea *p_widget, bool p_horizontal)
|
|
{
|
|
auto scrollBar = p_horizontal ? p_widget->horizontalScrollBar() : p_widget->verticalScrollBar();
|
|
if (scrollBar && scrollBar->isVisible() && scrollBar->minimum() != scrollBar->maximum()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QSize WidgetUtils::availableScreenSize(QWidget *p_widget)
|
|
{
|
|
auto geo = QApplication::desktop()->availableGeometry(p_widget);
|
|
return geo.size();
|
|
}
|
|
|
|
void WidgetUtils::openUrlByDesktop(const QUrl &p_url)
|
|
{
|
|
QDesktopServices::openUrl(p_url);
|
|
}
|
|
|
|
bool WidgetUtils::processKeyEventLikeVi(QWidget *p_widget,
|
|
QKeyEvent *p_event,
|
|
QWidget *p_escTargetWidget)
|
|
{
|
|
Q_ASSERT(p_widget);
|
|
|
|
bool eventHandled = false;
|
|
int key = p_event->key();
|
|
int modifiers = p_event->modifiers();
|
|
if (!p_escTargetWidget) {
|
|
p_escTargetWidget = p_widget;
|
|
}
|
|
|
|
switch (key) {
|
|
case Qt::Key_BracketLeft:
|
|
{
|
|
if (isViControlModifier(modifiers)) {
|
|
auto escEvent = new QKeyEvent(QEvent::KeyPress,
|
|
Qt::Key_Escape,
|
|
Qt::NoModifier);
|
|
QCoreApplication::postEvent(p_escTargetWidget, escEvent);
|
|
eventHandled = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::Key_J:
|
|
{
|
|
if (isViControlModifier(modifiers)) {
|
|
// The event must be allocated on the heap since the post event queue will take ownership
|
|
// of the event and delete it once it has been posted.
|
|
auto downEvent = new QKeyEvent(QEvent::KeyPress,
|
|
Qt::Key_Down,
|
|
Qt::NoModifier);
|
|
QCoreApplication::postEvent(p_widget, downEvent);
|
|
eventHandled = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Qt::Key_K:
|
|
{
|
|
if (isViControlModifier(modifiers)) {
|
|
auto upEvent = new QKeyEvent(QEvent::KeyPress,
|
|
Qt::Key_Up,
|
|
Qt::NoModifier);
|
|
QCoreApplication::postEvent(p_widget, upEvent);
|
|
eventHandled = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (eventHandled) {
|
|
p_event->accept();
|
|
}
|
|
|
|
return eventHandled;
|
|
}
|
|
|
|
bool WidgetUtils::isViControlModifier(int p_modifiers)
|
|
{
|
|
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
|
|
return p_modifiers == Qt::MetaModifier;
|
|
#else
|
|
return p_modifiers == Qt::ControlModifier;
|
|
#endif
|
|
}
|
|
|
|
void WidgetUtils::clearActionGroup(QActionGroup *p_actGroup)
|
|
{
|
|
auto actions = p_actGroup->actions();
|
|
for (auto action : actions) {
|
|
p_actGroup->removeAction(action);
|
|
}
|
|
}
|
|
|
|
void WidgetUtils::addActionShortcut(QAction *p_action,
|
|
const QString &p_shortcut,
|
|
Qt::ShortcutContext p_context)
|
|
{
|
|
QKeySequence kseq(p_shortcut);
|
|
if (kseq.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
p_action->setShortcut(kseq);
|
|
p_action->setShortcutContext(p_context);
|
|
p_action->setText(QString("%1\t%2").arg(p_action->text(), kseq.toString(QKeySequence::NativeText)));
|
|
}
|
|
|
|
void WidgetUtils::addActionShortcutText(QAction *p_action,
|
|
const QString &p_shortcut)
|
|
{
|
|
QKeySequence kseq(p_shortcut);
|
|
if (kseq.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
p_action->setText(QString("%1\t%2").arg(p_action->text(), kseq.toString(QKeySequence::NativeText)));
|
|
}
|
|
|
|
void WidgetUtils::updateSize(QWidget *p_widget)
|
|
{
|
|
p_widget->adjustSize();
|
|
p_widget->updateGeometry();
|
|
}
|
|
|
|
void WidgetUtils::resizeToHideScrollBarLater(QScrollArea *p_scroll, bool p_vertical, bool p_horizontal)
|
|
{
|
|
QTimer::singleShot(200, p_scroll, [p_scroll, p_vertical, p_horizontal]() {
|
|
WidgetUtils::resizeToHideScrollBar(p_scroll, p_vertical, p_horizontal);
|
|
});
|
|
}
|
|
|
|
void WidgetUtils::resizeToHideScrollBar(QScrollArea *p_scroll, bool p_vertical, bool p_horizontal)
|
|
{
|
|
bool changed = false;
|
|
auto parentWidget = p_scroll->parentWidget();
|
|
|
|
if (p_horizontal && WidgetUtils::isScrollBarVisible(p_scroll, true)) {
|
|
auto scrollBar = p_scroll->horizontalScrollBar();
|
|
auto delta = scrollBar->maximum() - scrollBar->minimum();
|
|
auto availableSize = WidgetUtils::availableScreenSize(p_scroll);
|
|
|
|
if (parentWidget) {
|
|
int newWidth = parentWidget->width() + delta;
|
|
if (newWidth <= availableSize.width()) {
|
|
changed = true;
|
|
p_scroll->resize(p_scroll->width() + delta, p_scroll->height());
|
|
auto geo = parentWidget->geometry();
|
|
parentWidget->setGeometry(geo.x() - delta / 2,
|
|
geo.y(),
|
|
newWidth,
|
|
geo.height());
|
|
}
|
|
} else {
|
|
int newWidth = p_scroll->width() + delta;
|
|
if (newWidth <= availableSize.width()) {
|
|
changed = true;
|
|
p_scroll->resize(newWidth, p_scroll->height());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p_vertical && WidgetUtils::isScrollBarVisible(p_scroll, false)) {
|
|
auto scrollBar = p_scroll->verticalScrollBar();
|
|
auto delta = scrollBar->maximum() - scrollBar->minimum();
|
|
auto availableSize = WidgetUtils::availableScreenSize(p_scroll);
|
|
|
|
if (parentWidget) {
|
|
int newHeight = parentWidget->height() + delta;
|
|
if (newHeight <= availableSize.height()) {
|
|
changed = true;
|
|
p_scroll->resize(p_scroll->width(), p_scroll->height() + delta);
|
|
auto geo = parentWidget->geometry();
|
|
parentWidget->setGeometry(geo.x(),
|
|
geo.y() - delta / 2,
|
|
geo.width(),
|
|
newHeight);
|
|
}
|
|
} else {
|
|
int newHeight = p_scroll->height() + delta;
|
|
if (newHeight <= availableSize.height()) {
|
|
changed = true;
|
|
p_scroll->resize(p_scroll->width(), newHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
p_scroll->updateGeometry();
|
|
}
|
|
}
|
|
|
|
QShortcut *WidgetUtils::createShortcut(const QString &p_shortcut,
|
|
QWidget *p_widget,
|
|
Qt::ShortcutContext p_context)
|
|
{
|
|
QKeySequence kseq(p_shortcut);
|
|
if (kseq.isEmpty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto shortcut = new QShortcut(kseq, p_widget, nullptr, nullptr, p_context);
|
|
return shortcut;
|
|
}
|
|
|
|
bool WidgetUtils::isMetaKey(int p_key)
|
|
{
|
|
return p_key == Qt::Key_Control
|
|
|| p_key == Qt::Key_Shift
|
|
|| p_key == Qt::Key_Meta
|
|
#if defined(Q_OS_LINUX)
|
|
// For mapping Caps as Ctrl in KDE.
|
|
|| p_key == Qt::Key_CapsLock
|
|
#endif
|
|
|| p_key == Qt::Key_Alt;
|
|
}
|
|
|
|
QVector<QModelIndex> WidgetUtils::getVisibleIndexes(const QListView *p_view)
|
|
{
|
|
QVector<QModelIndex> indexes;
|
|
|
|
auto firstItem = p_view->indexAt(QPoint(0, 0));
|
|
if (!firstItem.isValid()) {
|
|
return indexes;
|
|
}
|
|
|
|
auto lastItem = p_view->indexAt(p_view->viewport()->rect().bottomLeft());
|
|
|
|
int firstRow = firstItem.row();
|
|
int lastRow = lastItem.isValid() ? lastItem.row() : (p_view->model()->rowCount() - 1);
|
|
for (int i = firstRow; i <= lastRow; ++i) {
|
|
if (p_view->isRowHidden(i)) {
|
|
continue;
|
|
}
|
|
auto item = firstItem.siblingAtRow(i);
|
|
if (item.isValid()) {
|
|
indexes.append(item);
|
|
}
|
|
}
|
|
|
|
return indexes;
|
|
}
|
|
|
|
QString WidgetUtils::getMonospaceFont()
|
|
{
|
|
static QString font;
|
|
if (font.isNull()) {
|
|
QStringList candidates;
|
|
candidates << QStringLiteral("YaHei Consolas Hybrid")
|
|
<< QStringLiteral("Consolas")
|
|
<< QStringLiteral("Monaco")
|
|
<< QStringLiteral("Andale Mono")
|
|
<< QStringLiteral("Monospace")
|
|
<< QStringLiteral("Courier New");
|
|
auto availFamilies = QFontDatabase().families();
|
|
for (const auto &candidate : candidates) {
|
|
QString family = candidate.trimmed().toLower();
|
|
for (auto availFamily : availFamilies) {
|
|
availFamily.remove(QRegExp("\\[.*\\]"));
|
|
if (family == availFamily.trimmed().toLower()) {
|
|
font = availFamily;
|
|
return font;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback to current font.
|
|
font = QFont().family();
|
|
}
|
|
|
|
return font;
|
|
}
|
|
|
|
QAction *WidgetUtils::findActionByObjectName(const QList<QAction *> &p_actions, const QString &p_objName)
|
|
{
|
|
for (auto act : p_actions) {
|
|
if (act->objectName() == p_objName) {
|
|
return act;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Insert @p_action into @p_menu after action @p_after.
|
|
void WidgetUtils::insertActionAfter(QMenu *p_menu, QAction *p_after, QAction *p_action)
|
|
{
|
|
p_menu->insertAction(p_after, p_action);
|
|
if (p_after) {
|
|
p_menu->removeAction(p_after);
|
|
p_menu->insertAction(p_action, p_after);
|
|
}
|
|
}
|