TextOverflowWidget.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <TextOverflowWidget.h>
  9. #include <ExternalLinkDialog.h>
  10. #include <SettingsInterface.h>
  11. #include <QVBoxLayout>
  12. #include <QScrollArea>
  13. #include <QRegularExpression>
  14. #include <QDesktopServices>
  15. namespace O3DE::ProjectManager
  16. {
  17. static const QString s_overflowLink = "OverflowLink";
  18. static const int s_maxTextLength = 100;
  19. TextOverflowDialog::TextOverflowDialog(const QString& title, const QString& text, QWidget* parent)
  20. : QDialog(parent)
  21. {
  22. setWindowTitle(title);
  23. setModal(true);
  24. setObjectName("textOverflowDialog");
  25. setMinimumSize(600, 600);
  26. QVBoxLayout* vLayout = new QVBoxLayout();
  27. vLayout->setContentsMargins(5, 5, 5, 5);
  28. vLayout->setSpacing(0);
  29. vLayout->setAlignment(Qt::AlignTop);
  30. setLayout(vLayout);
  31. QScrollArea* scrollArea = new QScrollArea(this);
  32. vLayout->addWidget(scrollArea);
  33. // Set scroll bar policy
  34. scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  35. scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  36. QWidget* textArea = new QWidget();
  37. textArea->setContentsMargins(10, 10, 10, 10);
  38. scrollArea->setWidget(textArea);
  39. QVBoxLayout* scrollLayout = new QVBoxLayout;
  40. scrollLayout->setSizeConstraint(QLayout::SetFixedSize);
  41. textArea->setLayout(scrollLayout);
  42. QLabel* overflowText = new QLabel(text);
  43. overflowText->setObjectName("overflowTextDialogLabel");
  44. overflowText->setWordWrap(true);
  45. overflowText->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
  46. overflowText->setOpenExternalLinks(true);
  47. overflowText->setAlignment(Qt::AlignLeft);
  48. scrollLayout->addWidget(overflowText);
  49. }
  50. // Elides text based on character length with html link tags removed
  51. QString TextOverflowLabel::ElideLinkedText(const QString& text, int maxLength)
  52. {
  53. QRegularExpression linksRegex("</?a(|\\s+[^>]+)>");
  54. QRegularExpressionMatchIterator matches = linksRegex.globalMatch(text);
  55. int displayLength = 0;
  56. int truncateEnd = 0;
  57. int matchIndex = 0;
  58. QRegularExpressionMatch match;
  59. if (matches.hasNext())
  60. {
  61. int lastMatchLength = 0;
  62. while (matches.hasNext())
  63. {
  64. match = matches.next();
  65. int matchStartIndex = match.capturedStart();
  66. int matchLength = match.capturedLength();
  67. // If at the start of a new link tag mark this index
  68. // to truncate from here in-case the link text is too long.
  69. displayLength += matchStartIndex - truncateEnd - lastMatchLength;
  70. truncateEnd = matchStartIndex;
  71. // Exit because we've run out of displayed characters and truncate to last avaliable character
  72. if (displayLength >= maxLength)
  73. {
  74. truncateEnd -= displayLength - maxLength;
  75. break;
  76. }
  77. lastMatchLength = matchLength;
  78. ++matchIndex;
  79. }
  80. // If there is still room left after we process all matches then
  81. // offset by the last match length and the remaining display characters
  82. if (displayLength <= maxLength)
  83. {
  84. truncateEnd += lastMatchLength + maxLength - displayLength;
  85. // If we broke out early this may overestimate display length
  86. // Only trying to determine if displayLength > maxLength so it doesn't matter
  87. displayLength += text.length() - truncateEnd;
  88. }
  89. }
  90. else
  91. {
  92. displayLength = text.length();
  93. if (displayLength > maxLength)
  94. {
  95. truncateEnd = maxLength;
  96. }
  97. }
  98. // Truncate and add link to full texts
  99. if (displayLength > maxLength)
  100. {
  101. return QString("%1%2 <a href=\"%3\">Read More...</a>")
  102. .arg(
  103. text.leftRef(truncateEnd),
  104. // Append closing tag if link text got truncated %2
  105. match.isValid() && matchIndex % 2 == 1 ? match.captured() : "",
  106. s_overflowLink);
  107. }
  108. return text;
  109. }
  110. TextOverflowLabel::TextOverflowLabel(const QString& title, const QString& text, QWidget* parent)
  111. : QLabel(text, parent)
  112. , m_title(title)
  113. , m_fullText(text)
  114. {
  115. setWordWrap(true);
  116. setTextInteractionFlags(Qt::LinksAccessibleByMouse);
  117. setAlignment(Qt::AlignLeft);
  118. // Truncate text and display overflow dialog if it is too long
  119. if (text.length() > s_maxTextLength)
  120. {
  121. setText(ElideLinkedText(text, s_maxTextLength));
  122. }
  123. connect(this, &QLabel::linkActivated, this, &TextOverflowLabel::OnLinkActivated);
  124. }
  125. void TextOverflowLabel::OnLinkActivated(const QString& link)
  126. {
  127. if (link != s_overflowLink)
  128. {
  129. QDesktopServices::openUrl(QUrl(link));
  130. }
  131. else
  132. {
  133. TextOverflowDialog* overflowDialog = new TextOverflowDialog(m_title, m_fullText, this);
  134. overflowDialog->open();
  135. }
  136. }
  137. } // namespace O3DE::ProjectManager