diff --git a/src/mode/katemodemenulist.h b/src/mode/katemodemenulist.h --- a/src/mode/katemodemenulist.h +++ b/src/mode/katemodemenulist.h @@ -46,7 +46,7 @@ namespace KTextEditor { class DocumentPrivate; } -namespace KateModeMenuListData { class ListView; class ListItem; class SearchLine; } +namespace KateModeMenuListData { class ListView; class ListItem; class SearchLine; class Factory; } /** * Class of menu to select the @@ -104,14 +104,22 @@ { init(searchBarPos); } - KateModeMenuList(QWidget *parent, const SearchBarPosition searchBarPos = Bottom) : QMenu(parent) + KateModeMenuList(const QString &title, const SearchBarPosition searchBarPos, QWidget *parent) : QMenu(title, parent) { init(searchBarPos); } - KateModeMenuList(const QString &title, QWidget *parent, const SearchBarPosition searchBarPos = Bottom) : QMenu(title, parent) + KateModeMenuList(const QString &title, QWidget *parent) : QMenu(title, parent) + { + init(Bottom); + } + KateModeMenuList(const SearchBarPosition searchBarPos, QWidget *parent) : QMenu(parent) { init(searchBarPos); } + KateModeMenuList(QWidget *parent) : QMenu(parent) + { + init(Bottom); + } ~KateModeMenuList() { } /** @@ -219,12 +227,13 @@ * Create a new section in the list of items and add it to the model. * It corresponds to a separator line and a title. * @param sectionName Section title. + * @param background Background color is generally transparent. * @param bSeparator True if a separation line will also be created before the section title. * @param modelPosition Position in the model where to insert the new section. If the value is * less than zero, the section is added to the end of the list/model. * @return A pointer to the item created with the section title. */ - KateModeMenuListData::ListItem* createSectionList(const QString §ionName, bool bSeparator = true, int modelPosition = -1); + KateModeMenuListData::ListItem* createSectionList(const QString §ionName, const QBrush &background, bool bSeparator = true, int modelPosition = -1); /** * Load message when the list is empty in the search. @@ -255,6 +264,7 @@ * however, this isn't a problem, since the list widget is never inactive. */ const QIcon m_checkIcon = QIcon::fromTheme(QStringLiteral("checkbox")); + QIcon m_emptyIcon; static const int m_iconSize = 16; QPointer m_doc; @@ -286,6 +296,8 @@ } public: + ~ListView() { } + /** * Define the size of the widget list. * @p height and @p width are values in pixels. @@ -314,7 +326,7 @@ private: KateModeMenuList *m_parentMenu = nullptr; - friend KateModeMenuList; + friend Factory; }; @@ -329,10 +341,19 @@ const KateFileType *m_type = nullptr; const QString *m_searchName = nullptr; + bool m_bHasNewSearchName; - friend KateModeMenuList; + friend Factory; public: + ~ListItem() + { + // Delete m_searchName only if a new name was generated for the search. + if (m_searchName && m_bHasNewSearchName) { + delete m_searchName; + } + } + /** * Associate this item with a KateFileType object. */ @@ -379,13 +400,15 @@ { Q_OBJECT + public: + ~SearchLine() { } + private: SearchLine(KateModeMenuList *menu) : QLineEdit(menu) { m_parentMenu = menu; init(); } - ~SearchLine() { }; void init(); @@ -414,7 +437,7 @@ QList> m_bestResults; KateModeMenuList *m_parentMenu = nullptr; - friend KateModeMenuList; + friend Factory; protected: /** @@ -431,6 +454,26 @@ void _k_queueSearch(const QString &s); void _k_activateSearch(); }; + + + class Factory + { + private: + friend KateModeMenuList; + Factory() { }; + static ListView* createListView(KateModeMenuList *parentMenu) + { + return new ListView (parentMenu); + } + static ListItem* createListItem() + { + return new ListItem(); + } + static SearchLine* createSearchLine(KateModeMenuList *parentMenu) + { + return new SearchLine(parentMenu); + } + }; } #endif // KATEMODEMENULIST_H diff --git a/src/mode/katemodemenulist.cpp b/src/mode/katemodemenulist.cpp --- a/src/mode/katemodemenulist.cpp +++ b/src/mode/katemodemenulist.cpp @@ -69,16 +69,21 @@ void KateModeMenuList::init(const SearchBarPosition searchBarPos) { - m_list = new KateModeMenuListData::ListView(this); - m_searchBar = new KateModeMenuListData::SearchLine(this); + m_list = KateModeMenuListData::Factory::createListView(this); + m_searchBar = KateModeMenuListData::Factory::createSearchLine(this); + + // Empty icon for items. + QPixmap emptyIconPixmap(m_iconSize, m_iconSize); + emptyIconPixmap.fill(Qt::transparent); + m_emptyIcon = QIcon(emptyIconPixmap); /* * Load list widget, scroll bar and items. */ if (overlapScrollBar()) { + // The vertical scroll bar will be added in another layout m_scroll = new QScrollBar(Qt::Vertical, this); m_list->setVerticalScrollBar(m_scroll); - // The vertical scroll bar will be added in another layout m_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { @@ -103,9 +108,16 @@ /* * Set layouts and widgets. + * container (QWidget) + * └── layoutContainer (QVBoxLayout) + * ├── m_layoutList (QGridLayout) + * │ ├── m_list (ListView) + * │ ├── layoutScrollBar (QHBoxLayout) --> m_scroll (QScrollBar) + * │ └── m_emptyListMsg (QLabel) + * └── layoutSearchBar (QHBoxLayout) --> m_searchBar (SearchLine) */ - QWidget *container = new QWidget(); - QVBoxLayout *layoutContainer = new QVBoxLayout(); + QWidget *container = new QWidget(this); + QVBoxLayout *layoutContainer = new QVBoxLayout(container); m_layoutList = new QGridLayout(); QHBoxLayout *layoutSearchBar = new QHBoxLayout(); @@ -121,14 +133,14 @@ } layoutSearchBar->addWidget(m_searchBar); - if (searchBarPos == Top) { layoutContainer->addLayout(layoutSearchBar); } layoutContainer->addLayout(m_layoutList); if (searchBarPos == Bottom) { layoutContainer->addLayout(layoutSearchBar); } + // In the Windows OS, decrease menu margins. #ifdef Q_OS_WIN layoutContainer->setContentsMargins(3, 3, 3, 3); @@ -145,8 +157,10 @@ widAct->setDefaultWidget(container); addAction(widAct); - // Detect selected item with one click. - // This also applies to double-clicks. + /* + * Detect selected item with one click. + * This also applies to double-clicks. + */ connect(m_list, &KateModeMenuListData::ListView::clicked, this, &KateModeMenuList::selectHighlighting); } @@ -171,65 +185,60 @@ maxWidthText = m_list->sizeHint().width() - m_list->verticalScrollBar()->sizeHint().width() - m_iconSize - 19; } - // The first item on the list is the "Best Search Matches" section, - // which will remain hidden and will only be shown when necessary. - createSectionList(QString(), false); - m_list->setRowHidden(0, true); + // Transparent color used as background in the sections. + QPixmap transparentPixmap = QPixmap(m_iconSize / 2, m_iconSize / 2); + transparentPixmap.fill(Qt::transparent); + QBrush transparentBrush(transparentPixmap); - // Set empty icon - QPixmap emptyIconPixmap(m_iconSize, m_iconSize); - emptyIconPixmap.fill(Qt::transparent); - const QIcon emptyIcon(emptyIconPixmap); + /* + * The first item on the list is the "Best Search Matches" section, + * which will remain hidden and will only be shown when necessary. + */ + createSectionList(QString(), transparentBrush, false); + m_list->setRowHidden(0, true); /* * Get list of modes from KateModeManager::list(). * We assume that the modes are arranged according to sections, alphabetically; * and the attribute "translatedSection" isn't empty if "section" has a value. */ for (auto *hl : KTextEditor::EditorPrivate::self()->modeManager()->list()) { - /* - * Detects a new section. - */ + // Detects a new section. if ( !hl->translatedSection.isEmpty() && (prevHlSection == nullptr || hl->translatedSection != *prevHlSection) ) { - createSectionList(hl->sectionTranslated()); + createSectionList(hl->sectionTranslated(), transparentBrush); } prevHlSection = hl->translatedSection.isNull() ? nullptr : &hl->translatedSection; - /* - * Create item in the list with the language name. - */ - KateModeMenuListData::ListItem *item = new KateModeMenuListData::ListItem(); + // Create item in the list with the language name. + KateModeMenuListData::ListItem *item = KateModeMenuListData::Factory::createListItem(); item->setText(setWordWrap( hl->nameTranslated(), maxWidthText, m_list->fontMetrics() )); item->setMode(hl); // NOTE: Search names generated in: KateModeMenuListData::SearchLine::updateSearch() - item->setIcon(emptyIcon); + item->setIcon(m_emptyIcon); item->setEditable(false); // Add item m_model->appendRow(item); } } -KateModeMenuListData::ListItem* KateModeMenuList::createSectionList(const QString §ionName, bool bSeparator, int modelPosition) +KateModeMenuListData::ListItem* KateModeMenuList::createSectionList(const QString §ionName, const QBrush &background, bool bSeparator, int modelPosition) { - QPixmap transparent = QPixmap(m_iconSize / 2, m_iconSize / 2); - transparent.fill(Qt::transparent); - /* * Add a separator to the list. */ if (bSeparator) { - KateModeMenuListData::ListItem *separator = new KateModeMenuListData::ListItem(); + KateModeMenuListData::ListItem *separator = KateModeMenuListData::Factory::createListItem(); separator->setFlags(Qt::NoItemFlags); separator->setEnabled(false); separator->setEditable(false); separator->setSelectable(false); separator->setSizeHint(QSize(separator->sizeHint().width() - 2, 4)); - separator->setBackground(QBrush(transparent)); + separator->setBackground(background); - QFrame *line = new QFrame(); + QFrame *line = new QFrame(m_list); line->setFrameStyle(QFrame::HLine); // In the Windows OS, decrease opacity of the section separator line. @@ -243,38 +252,42 @@ m_model->insertRow(modelPosition, separator); } m_list->setIndexWidget( m_model->index(separator->row(), 0), line ); + m_list->selectionModel()->select(separator->index(), QItemSelectionModel::Deselect); } /* * Add the section name to the list. */ - KateModeMenuListData::ListItem *section = new KateModeMenuListData::ListItem(); + KateModeMenuListData::ListItem *section = KateModeMenuListData::Factory::createListItem(); section->setFlags(Qt::NoItemFlags); section->setEnabled(false); section->setEditable(false); section->setSelectable(false); - QLabel *label = new QLabel(sectionName); + QLabel *label = new QLabel(sectionName, m_list); if (m_list->layoutDirection() == Qt::RightToLeft) { label->setAlignment(Qt::AlignRight); } label->setTextFormat(Qt::RichText); label->setIndent(6); - // NOTE: Names of sections in bold. The font color - // should change according to Kate's color theme. + /* + * NOTE: Names of sections in bold. The font color + * should change according to Kate's color theme. + */ QFont font = label->font(); font.setWeight(QFont::Bold); label->setFont(font); - section->setBackground(QBrush(transparent)); + section->setBackground(background); if (modelPosition < 0) { m_model->appendRow(section); } else { m_model->insertRow(modelPosition + 1, section); } m_list->setIndexWidget( m_model->index(section->row(), 0), label ); + m_list->selectionModel()->select(section->index(), QItemSelectionModel::Deselect); return section; } @@ -370,9 +383,7 @@ { // Change the previously selected item to empty icon if (m_selectedItem) { - QPixmap emptyIcon(m_iconSize, m_iconSize); - emptyIcon.fill(Qt::transparent); - m_selectedItem->setIcon(QIcon(emptyIcon)); + m_selectedItem->setIcon(m_emptyIcon); } // Update the selected item @@ -460,7 +471,7 @@ void KateModeMenuList::loadEmptyMsg() { - m_emptyListMsg = new QLabel(i18nc("A search yielded no results", "No items matching your search")); + m_emptyListMsg = new QLabel(i18nc("A search yielded no results", "No items matching your search"), this); m_emptyListMsg->setMargin(15); m_emptyListMsg->setWordWrap(true); @@ -561,11 +572,12 @@ searchName.remove(0, 1); } m_searchName = new QString(searchName); - return true; + m_bHasNewSearchName = true; } else { m_searchName = itemName; + m_bHasNewSearchName = false; } - return false; + return m_bHasNewSearchName; } @@ -642,8 +654,10 @@ { m_queuedSearches = 0; m_bSearchStateAutoScroll = (text().trimmed().isEmpty()) ? false : true; - // NOTE: This calls "SearchLine::_k_queueSearch()" with an empty string. - // The search clearing should be done without delays. + /* + * NOTE: This calls "SearchLine::_k_queueSearch()" with an empty string. + * The search clearing should be done without delays. + */ QLineEdit::clear(); } @@ -998,9 +1012,11 @@ void KateModeMenuListData::SearchLine::setSearchResult(const int rowItem, bool &bEmptySection, int &lastSection, int &firstSection, int &lastItem) { if (lastItem == -1) { - // Detect the first result of the search and "select" it. - // This allows you to scroll through the list using - // the Up/Down keys after entering a search. + /* + * Detect the first result of the search and "select" it. + * This allows you to scroll through the list using + * the Up/Down keys after entering a search. + */ m_parentMenu->m_list->setCurrentItem(rowItem); // Position of the first section visible.