widgetutils.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #include "widgetutils.h"
  2. #include <QGuiApplication>
  3. #include <QScreen>
  4. #include <QWidget>
  5. #include <QStyle>
  6. #include <QAbstractScrollArea>
  7. #include <QScrollBar>
  8. #include <QDesktopWidget>
  9. #include <QApplication>
  10. #include <QDesktopServices>
  11. #include <QKeyEvent>
  12. #include <QActionGroup>
  13. #include <QAction>
  14. #include <QKeySequence>
  15. #include <QScrollArea>
  16. #include <QTimer>
  17. #include <QShortcut>
  18. #include <QListView>
  19. #include <QModelIndex>
  20. #include <QFontDatabase>
  21. #include <QMenu>
  22. #include <QDebug>
  23. #include <QLineEdit>
  24. #include <QLayout>
  25. #include <core/global.h>
  26. using namespace vnotex;
  27. void WidgetUtils::setPropertyDynamically(QWidget *p_widget,
  28. const char *p_prop,
  29. const QVariant &p_val)
  30. {
  31. p_widget->setProperty(p_prop, p_val);
  32. updateStyle(p_widget);
  33. }
  34. void WidgetUtils::updateStyle(QWidget *p_widget)
  35. {
  36. p_widget->style()->unpolish(p_widget);
  37. p_widget->style()->polish(p_widget);
  38. p_widget->update();
  39. }
  40. qreal WidgetUtils::calculateScaleFactor(bool p_update)
  41. {
  42. static qreal factor = -1;
  43. if (factor < 0 || p_update) {
  44. factor = QGuiApplication::primaryScreen()->devicePixelRatio();
  45. }
  46. return factor;
  47. }
  48. bool WidgetUtils::isScrollBarVisible(QAbstractScrollArea *p_widget, bool p_horizontal)
  49. {
  50. auto scrollBar = p_horizontal ? p_widget->horizontalScrollBar() : p_widget->verticalScrollBar();
  51. if (scrollBar && scrollBar->isVisible() && scrollBar->minimum() != scrollBar->maximum()) {
  52. return true;
  53. }
  54. return false;
  55. }
  56. QSize WidgetUtils::availableScreenSize(QWidget *p_widget)
  57. {
  58. auto geo = QApplication::desktop()->availableGeometry(p_widget);
  59. return geo.size();
  60. }
  61. void WidgetUtils::openUrlByDesktop(const QUrl &p_url)
  62. {
  63. QDesktopServices::openUrl(p_url);
  64. }
  65. bool WidgetUtils::processKeyEventLikeVi(QWidget *p_widget,
  66. QKeyEvent *p_event,
  67. QWidget *p_escTargetWidget)
  68. {
  69. Q_ASSERT(p_widget);
  70. bool eventHandled = false;
  71. int key = p_event->key();
  72. int modifiers = p_event->modifiers();
  73. if (!p_escTargetWidget) {
  74. p_escTargetWidget = p_widget;
  75. }
  76. switch (key) {
  77. case Qt::Key_BracketLeft:
  78. {
  79. if (isViControlModifier(modifiers)) {
  80. auto escEvent = new QKeyEvent(QEvent::KeyPress,
  81. Qt::Key_Escape,
  82. Qt::NoModifier);
  83. QCoreApplication::postEvent(p_escTargetWidget, escEvent);
  84. eventHandled = true;
  85. }
  86. break;
  87. }
  88. case Qt::Key_J:
  89. {
  90. if (isViControlModifier(modifiers)) {
  91. // The event must be allocated on the heap since the post event queue will take ownership
  92. // of the event and delete it once it has been posted.
  93. auto downEvent = new QKeyEvent(QEvent::KeyPress,
  94. Qt::Key_Down,
  95. Qt::NoModifier);
  96. QCoreApplication::postEvent(p_widget, downEvent);
  97. eventHandled = true;
  98. }
  99. break;
  100. }
  101. case Qt::Key_K:
  102. {
  103. if (isViControlModifier(modifiers)) {
  104. auto upEvent = new QKeyEvent(QEvent::KeyPress,
  105. Qt::Key_Up,
  106. Qt::NoModifier);
  107. QCoreApplication::postEvent(p_widget, upEvent);
  108. eventHandled = true;
  109. }
  110. break;
  111. }
  112. default:
  113. break;
  114. }
  115. if (eventHandled) {
  116. p_event->accept();
  117. }
  118. return eventHandled;
  119. }
  120. bool WidgetUtils::isViControlModifier(int p_modifiers)
  121. {
  122. #if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
  123. return p_modifiers == Qt::MetaModifier;
  124. #else
  125. return p_modifiers == Qt::ControlModifier;
  126. #endif
  127. }
  128. void WidgetUtils::clearActionGroup(QActionGroup *p_actGroup)
  129. {
  130. auto actions = p_actGroup->actions();
  131. for (auto action : actions) {
  132. p_actGroup->removeAction(action);
  133. }
  134. }
  135. void WidgetUtils::addActionShortcut(QAction *p_action,
  136. const QString &p_shortcut,
  137. Qt::ShortcutContext p_context)
  138. {
  139. QKeySequence kseq(p_shortcut);
  140. if (kseq.isEmpty()) {
  141. return;
  142. }
  143. p_action->setShortcut(kseq);
  144. p_action->setShortcutContext(p_context);
  145. p_action->setText(QString("%1\t%2").arg(p_action->text(), kseq.toString(QKeySequence::NativeText)));
  146. }
  147. void WidgetUtils::addActionShortcutText(QAction *p_action,
  148. const QString &p_shortcut)
  149. {
  150. QKeySequence kseq(p_shortcut);
  151. if (kseq.isEmpty()) {
  152. return;
  153. }
  154. p_action->setText(QString("%1\t%2").arg(p_action->text(), kseq.toString(QKeySequence::NativeText)));
  155. }
  156. void WidgetUtils::updateSize(QWidget *p_widget)
  157. {
  158. p_widget->adjustSize();
  159. p_widget->updateGeometry();
  160. }
  161. void WidgetUtils::resizeToHideScrollBarLater(QScrollArea *p_scroll, bool p_vertical, bool p_horizontal)
  162. {
  163. QTimer::singleShot(200, p_scroll, [p_scroll, p_vertical, p_horizontal]() {
  164. WidgetUtils::resizeToHideScrollBar(p_scroll, p_vertical, p_horizontal);
  165. });
  166. }
  167. void WidgetUtils::resizeToHideScrollBar(QScrollArea *p_scroll, bool p_vertical, bool p_horizontal)
  168. {
  169. bool changed = false;
  170. auto parentWidget = p_scroll->parentWidget();
  171. if (p_horizontal && WidgetUtils::isScrollBarVisible(p_scroll, true)) {
  172. auto scrollBar = p_scroll->horizontalScrollBar();
  173. auto delta = scrollBar->maximum() - scrollBar->minimum();
  174. auto availableSize = WidgetUtils::availableScreenSize(p_scroll);
  175. if (parentWidget) {
  176. int newWidth = parentWidget->width() + delta;
  177. if (newWidth <= availableSize.width()) {
  178. changed = true;
  179. p_scroll->resize(p_scroll->width() + delta, p_scroll->height());
  180. auto geo = parentWidget->geometry();
  181. parentWidget->setGeometry(geo.x() - delta / 2,
  182. geo.y(),
  183. newWidth,
  184. geo.height());
  185. }
  186. } else {
  187. int newWidth = p_scroll->width() + delta;
  188. if (newWidth <= availableSize.width()) {
  189. changed = true;
  190. p_scroll->resize(newWidth, p_scroll->height());
  191. }
  192. }
  193. }
  194. if (p_vertical && WidgetUtils::isScrollBarVisible(p_scroll, false)) {
  195. auto scrollBar = p_scroll->verticalScrollBar();
  196. auto delta = scrollBar->maximum() - scrollBar->minimum();
  197. auto availableSize = WidgetUtils::availableScreenSize(p_scroll);
  198. if (parentWidget) {
  199. int newHeight = parentWidget->height() + delta;
  200. if (newHeight <= availableSize.height()) {
  201. changed = true;
  202. p_scroll->resize(p_scroll->width(), p_scroll->height() + delta);
  203. auto geo = parentWidget->geometry();
  204. parentWidget->setGeometry(geo.x(),
  205. geo.y() - delta / 2,
  206. geo.width(),
  207. newHeight);
  208. }
  209. } else {
  210. int newHeight = p_scroll->height() + delta;
  211. if (newHeight <= availableSize.height()) {
  212. changed = true;
  213. p_scroll->resize(p_scroll->width(), newHeight);
  214. }
  215. }
  216. }
  217. if (changed) {
  218. p_scroll->updateGeometry();
  219. }
  220. }
  221. QShortcut *WidgetUtils::createShortcut(const QString &p_shortcut,
  222. QWidget *p_widget,
  223. Qt::ShortcutContext p_context)
  224. {
  225. QKeySequence kseq(p_shortcut);
  226. if (kseq.isEmpty()) {
  227. return nullptr;
  228. }
  229. auto shortcut = new QShortcut(kseq, p_widget, nullptr, nullptr, p_context);
  230. return shortcut;
  231. }
  232. bool WidgetUtils::isMetaKey(int p_key)
  233. {
  234. return p_key == Qt::Key_Control
  235. || p_key == Qt::Key_Shift
  236. || p_key == Qt::Key_Meta
  237. #if defined(Q_OS_LINUX)
  238. // For mapping Caps as Ctrl in KDE.
  239. || p_key == Qt::Key_CapsLock
  240. #endif
  241. || p_key == Qt::Key_Alt;
  242. }
  243. QVector<QModelIndex> WidgetUtils::getVisibleIndexes(const QListView *p_view)
  244. {
  245. QVector<QModelIndex> indexes;
  246. auto firstItem = p_view->indexAt(QPoint(0, 0));
  247. if (!firstItem.isValid()) {
  248. return indexes;
  249. }
  250. auto lastItem = p_view->indexAt(p_view->viewport()->rect().bottomLeft());
  251. int firstRow = firstItem.row();
  252. int lastRow = lastItem.isValid() ? lastItem.row() : (p_view->model()->rowCount() - 1);
  253. for (int i = firstRow; i <= lastRow; ++i) {
  254. if (p_view->isRowHidden(i)) {
  255. continue;
  256. }
  257. auto item = firstItem.siblingAtRow(i);
  258. if (item.isValid()) {
  259. indexes.append(item);
  260. }
  261. }
  262. return indexes;
  263. }
  264. QString WidgetUtils::getMonospaceFont()
  265. {
  266. static QString font;
  267. if (font.isNull()) {
  268. QStringList candidates;
  269. candidates << QStringLiteral("YaHei Consolas Hybrid")
  270. << QStringLiteral("Consolas")
  271. << QStringLiteral("Monaco")
  272. << QStringLiteral("Andale Mono")
  273. << QStringLiteral("Monospace")
  274. << QStringLiteral("Courier New");
  275. auto availFamilies = QFontDatabase().families();
  276. for (const auto &candidate : candidates) {
  277. QString family = candidate.trimmed().toLower();
  278. for (auto availFamily : availFamilies) {
  279. availFamily.remove(QRegExp("\\[.*\\]"));
  280. if (family == availFamily.trimmed().toLower()) {
  281. font = availFamily;
  282. return font;
  283. }
  284. }
  285. }
  286. // Fallback to current font.
  287. font = QFont().family();
  288. }
  289. return font;
  290. }
  291. QAction *WidgetUtils::findActionByObjectName(const QList<QAction *> &p_actions, const QString &p_objName)
  292. {
  293. for (auto act : p_actions) {
  294. if (act->objectName() == p_objName) {
  295. return act;
  296. }
  297. }
  298. return nullptr;
  299. }
  300. // Insert @p_action into @p_menu after action @p_after.
  301. void WidgetUtils::insertActionAfter(QMenu *p_menu, QAction *p_after, QAction *p_action)
  302. {
  303. p_menu->insertAction(p_after, p_action);
  304. if (p_after) {
  305. p_menu->removeAction(p_after);
  306. p_menu->insertAction(p_action, p_after);
  307. }
  308. }
  309. void WidgetUtils::selectBaseName(QLineEdit *p_lineEdit)
  310. {
  311. auto text = p_lineEdit->text();
  312. int dotIndex = text.lastIndexOf(QLatin1Char('.'));
  313. p_lineEdit->setSelection(0, (dotIndex == -1) ? text.size() : dotIndex);
  314. }
  315. void WidgetUtils::setContentsMargins(QLayout *p_layout)
  316. {
  317. p_layout->setContentsMargins(CONTENTS_MARGIN, CONTENTS_MARGIN, CONTENTS_MARGIN, CONTENTS_MARGIN);
  318. }