diff --git a/src/mode/katemodemenulist.h b/src/mode/katemodemenulist.h --- a/src/mode/katemodemenulist.h +++ b/src/mode/katemodemenulist.h @@ -287,6 +287,7 @@ const QIcon m_checkIcon = QIcon::fromTheme(QStringLiteral("checkbox")); QIcon m_emptyIcon; static const int m_iconSize = 16; + int m_defaultHeightItemSection; QPointer m_doc; diff --git a/src/mode/katemodemenulist.cpp b/src/mode/katemodemenulist.cpp --- a/src/mode/katemodemenulist.cpp +++ b/src/mode/katemodemenulist.cpp @@ -65,6 +65,25 @@ void KateModeMenuList::init(const SearchBarPosition searchBarPos) { + /* + * Fix font size & font style: display the font correctly when changing it from the + * KDE Plasma preferences. For example, the font type "Menu" is displayed, but "font()" + * and "fontMetrics()" return the font type "General". Therefore, this overwrites the + * "General" font. This makes it possible to correctly apply word wrapping on items, + * when changing the font or its size. + */ + QFont font = this->font(); + font.setFamily(font.family()); + font.setStyle(font.style()); + font.setStyleName(font.styleName()); + font.setBold(font.bold()); + font.setItalic(font.italic()); + font.setUnderline(font.underline()); + font.setStrikeOut(font.strikeOut()); + font.setPointSize(font.pointSize()); + setFont(font); + + // Create list and search bar m_list = KateModeMenuListData::Factory::createListView(this); m_searchBar = KateModeMenuListData::Factory::createSearchLine(this); @@ -124,7 +143,7 @@ if (overlapScrollBar()) { QHBoxLayout *layoutScrollBar = new QHBoxLayout(); layoutScrollBar->addWidget(m_scroll); - layoutScrollBar->setContentsMargins(2, 2, 2, 2); + layoutScrollBar->setContentsMargins(1, 2, 2, 2); m_layoutList->addLayout(layoutScrollBar, 0, 0, Qt::AlignRight); } @@ -139,9 +158,13 @@ // In the Windows OS, decrease menu margins. #ifdef Q_OS_WIN - layoutContainer->setContentsMargins(3, 3, 3, 3); + layoutContainer->setContentsMargins(3, 3, 2, 3); layoutContainer->setSpacing(0); - layoutSearchBar->setContentsMargins(2, 2, 2, 2); + if (searchBarPos == Bottom) { + layoutSearchBar->setContentsMargins(2, 5, 2, 2); + } else if (searchBarPos == Top) { + layoutSearchBar->setContentsMargins(2, 2, 2, 5); + } layoutSearchBar->setSpacing(0); m_layoutList->setContentsMargins(2, 2, 2, 2); m_layoutList->setSpacing(0); @@ -170,13 +193,13 @@ /* * The width of the text container in the item, in pixels. This is used to make * a custom word wrap and prevent the item's text from passing under the scroll bar. - * NOTE: 12 = the edges + * NOTE: 11 = Icon margin (8px) + Scroll bar margin (1px + 2px) */ int maxWidthText; if (overlapScrollBar()) { - maxWidthText = m_list->sizeHint().width() - m_scroll->sizeHint().width() - m_iconSize - 12; + maxWidthText = m_list->sizeHint().width() - m_scroll->sizeHint().width() - m_iconSize - 11; } else { - maxWidthText = m_list->sizeHint().width() - m_list->verticalScrollBar()->sizeHint().width() - m_iconSize - 19; + maxWidthText = m_list->sizeHint().width() - m_list->verticalScrollBar()->sizeHint().width() - m_iconSize - 12; } // Transparent color used as background in the sections. @@ -189,6 +212,7 @@ * which will remain hidden and will only be shown when necessary. */ createSectionList(QString(), transparentBrush, false); + m_defaultHeightItemSection = m_list->visualRect(m_model->index(0, 0)).height(); m_list->setRowHidden(0, true); /* @@ -261,7 +285,7 @@ if (m_list->layoutDirection() == Qt::RightToLeft) { label->setAlignment(Qt::AlignRight); } - label->setTextFormat(Qt::RichText); + label->setTextFormat(Qt::PlainText); label->setIndent(6); /* @@ -282,6 +306,26 @@ m_list->setIndexWidget(m_model->index(section->row(), 0), label); m_list->selectionModel()->select(section->index(), QItemSelectionModel::Deselect); + // Apply word wrap in sections, for long labels. + // NOTE: 3 = Scroll bar margin + int containerTextWidth; + if (overlapScrollBar()) { + containerTextWidth = m_list->sizeHint().width() - m_scroll->sizeHint().width() - 3; + } else { + containerTextWidth = m_list->sizeHint().width() - m_list->verticalScrollBar()->sizeHint().width() - 2; + } + int heightSectionMargin = m_list->visualRect(m_model->index(section->row(), 0)).height() - label->sizeHint().height(); + + if (label->sizeHint().width() > containerTextWidth) { + label->setText(setWordWrap(label->text(), containerTextWidth - label->indent(), label->fontMetrics())); + if (heightSectionMargin < 2) { + heightSectionMargin = 2; + } + section->setSizeHint(QSize(section->sizeHint().width(), label->sizeHint().height() + heightSectionMargin)); + } else if (heightSectionMargin < 2) { + section->setSizeHint(QSize(section->sizeHint().width(), label->sizeHint().height() + 2)); + } + return section; } @@ -493,7 +537,7 @@ QString KateModeMenuList::setWordWrap(const QString &text, const int maxWidth, const QFontMetrics &fontMetrics) const { // Get the length of the text, in pixels, and compare it with the container - if (fontMetrics.boundingRect(text).width() <= maxWidth) { + if (fontMetrics.horizontalAdvance(text) <= maxWidth) { return text; } @@ -506,37 +550,58 @@ QString tmpLineText = QString(); for (int i = 0; i < words.count() - 1; ++i) { - tmpLineText += words[i]; + // Elide mode in long words + if (fontMetrics.horizontalAdvance(words[i]) > maxWidth) { + if (!tmpLineText.isEmpty()) { + newText += tmpLineText + QLatin1Char('\n'); + tmpLineText.clear(); + } + newText += fontMetrics.elidedText(words[i], m_list->layoutDirection() == Qt::RightToLeft ? Qt::ElideLeft : Qt::ElideRight, maxWidth) + QLatin1Char('\n'); + continue; + } else { + tmpLineText += words[i]; + } // This prevents the last line of text from having only one word with 1 or 2 chars - if (i == words.count() - 3 && words[i + 2].length() <= 2 && fontMetrics.boundingRect(tmpLineText + QLatin1Char(' ') + words[i + 1] + QLatin1Char(' ') + words[i + 2]).width() > maxWidth) { + if (i == words.count() - 3 && words[i + 2].length() <= 2 && fontMetrics.horizontalAdvance(tmpLineText + QLatin1Char(' ') + words[i + 1] + QLatin1Char(' ') + words[i + 2]) > maxWidth) { newText += tmpLineText + QLatin1Char('\n'); tmpLineText.clear(); } // Add line break if the maxWidth is exceeded with the next word - else if (fontMetrics.boundingRect(tmpLineText + QLatin1Char(' ') + words[i + 1]).width() > maxWidth) { + else if (fontMetrics.horizontalAdvance(tmpLineText + QLatin1Char(' ') + words[i + 1]) > maxWidth) { newText += tmpLineText + QLatin1Char('\n'); tmpLineText.clear(); } else { tmpLineText.append(QLatin1Char(' ')); } } // Add line breaks in delimiters, if the last word is greater than the container - if (fontMetrics.boundingRect(words[words.count() - 1]).width() > maxWidth) { + bool bElidedLastWord = false; + if (fontMetrics.horizontalAdvance(words[words.count() - 1]) > maxWidth) { + bElidedLastWord = true; const int lastw = words.count() - 1; for (int c = words[lastw].length() - 1; c >= 0; --c) { - if (isDelimiter(words[lastw][c].unicode()) && fontMetrics.boundingRect(words[lastw].mid(0, c + 1)).width() <= maxWidth) { - words[lastw].insert(c + 1, QLatin1Char('\n')); + if (isDelimiter(words[lastw][c].unicode()) && fontMetrics.horizontalAdvance(words[lastw].mid(0, c + 1)) <= maxWidth) { + bElidedLastWord = false; + if (fontMetrics.horizontalAdvance(words[lastw].mid(c + 1)) > maxWidth) { + words[lastw] = words[lastw].mid(0, c + 1) + QLatin1Char('\n') + fontMetrics.elidedText(words[lastw].mid(c + 1), m_list->layoutDirection() == Qt::RightToLeft ? Qt::ElideLeft : Qt::ElideRight, maxWidth); + } else { + words[lastw].insert(c + 1, QLatin1Char('\n')); + } break; } } } if (!tmpLineText.isEmpty()) { newText += tmpLineText; } - newText += words[words.count() - 1]; + if (bElidedLastWord) { + newText += fontMetrics.elidedText(words[words.count() - 1], m_list->layoutDirection() == Qt::RightToLeft ? Qt::ElideLeft : Qt::ElideRight, maxWidth); + } else { + newText += words[words.count() - 1]; + } return newText; } @@ -930,22 +995,43 @@ if (m_bestResults.isEmpty()) { listView->setRowHidden(0, true); if (firstSection > 0) { - m_parentMenu->m_list->setRowHidden(firstSection - 1, true); + listView->setRowHidden(firstSection - 1, true); } - } - // Show "Best Search Matches" section, if there are items. - else { - // Show title in singular or plural, depending on the number of items - QLabel *labelSection = static_cast(listView->indexWidget(m_parentMenu->m_model->index(0, 0))); + } else { + /* + * Show "Best Search Matches" section, if there are items. + */ + + // Show title in singular or plural, depending on the number of items. + QLabel *labelSection = static_cast(listView->indexWidget(listModel->index(0, 0))); if (m_bestResults.size() == 1) { - labelSection->setText( - i18nc("Title (in singular) of the best result in an item search. Please, that the translation doesn't have more than 34 characters, since the menu where it's displayed is small and fixed.", "Best Search Match")); + labelSection->setText(i18nc("Title (in singular) of the best result in an item search. Please, that the translation doesn't have more than 34 characters, since the menu where it's displayed is small and fixed.", "Best Search Match")); } else { - labelSection->setText( - i18nc("Title (in plural) of the best results in an item search. Please, that the translation doesn't have more than 34 characters, since the menu where it's displayed is small and fixed.", "Best Search Matches")); + labelSection->setText(i18nc("Title (in plural) of the best results in an item search. Please, that the translation doesn't have more than 34 characters, since the menu where it's displayed is small and fixed.", "Best Search Matches")); } - listView->setRowHidden(0, false); + int heightSectionMargin = m_parentMenu->m_defaultHeightItemSection - labelSection->sizeHint().height(); + if (heightSectionMargin < 2) { + heightSectionMargin = 2; + } + const int listWidth = listView->sizeHint().width() - 1; + int maxWidthText; + if (overlapScrollBar()) { + maxWidthText = listWidth - m_parentMenu->m_scroll->sizeHint().width() - 1; + } else { + maxWidthText = listWidth - listView->verticalScrollBar()->sizeHint().width(); + } + // NOTE: labelSection->sizeHint().width() == labelSection->indent() + labelSection->fontMetrics().horizontalAdvance(labelSection->text()) + const bool bSectionMultiline = labelSection->sizeHint().width() > maxWidthText; + maxWidthText -= labelSection->indent(); + if (!bSectionMultiline) { + listModel->item(0, 0)->setSizeHint(QSize(listModel->item(0, 0)->sizeHint().width(), labelSection->sizeHint().height() + heightSectionMargin)); + listView->setRowHidden(0, false); + } + + /* + * Show items in "Best Search Matches" section. + */ int rowModelBestResults = 0; // New position in the model // Special Case: always show the "R Script" mode first by typing "r" in the search box @@ -973,6 +1059,15 @@ lastItem = rowModelBestResults; } + // Add word wrap in long section titles. + if (bSectionMultiline) { + if (listView->visualRect(listModel->index(lastItem, 0)).bottom() + labelSection->sizeHint().height() + heightSectionMargin > listView->geometry().height() || labelSection->sizeHint().width() > listWidth) { + labelSection->setText(m_parentMenu->setWordWrap(labelSection->text(), maxWidthText, labelSection->fontMetrics())); + } + listModel->item(0, 0)->setSizeHint(QSize(listModel->item(0, 0)->sizeHint().width(), labelSection->sizeHint().height() + heightSectionMargin)); + listView->setRowHidden(0, false); + } + m_parentMenu->m_list->setCurrentItem(1); }