diff --git a/frontend/control/list-labels/show-more-label.cpp b/frontend/control/list-labels/show-more-label.cpp index 8c33de7..49c4b40 100644 --- a/frontend/control/list-labels/show-more-label.cpp +++ b/frontend/control/list-labels/show-more-label.cpp @@ -56,6 +56,7 @@ void ShowMoreLabel::initUi() { m_layout->setAlignment(Qt::AlignRight); m_layout->addWidget(m_textLabel); m_textLabel->setPalette(pal); + m_textLabel->setCursor(QCursor(Qt::PointingHandCursor)); // m_layout->addWidget(m_loadingIconLabel); } @@ -64,9 +65,11 @@ bool ShowMoreLabel::eventFilter(QObject *watched, QEvent *event) { if(event->type() == QEvent::MouseButtonPress) { if(! m_timer->isActive()) { if(!m_isOpen) { + m_textLabel->setText(tr("Retract")); m_isOpen = true; Q_EMIT this->showMoreClicked(); } else { + m_textLabel->setText(tr("Show More...")); m_isOpen = false; Q_EMIT this->retractClicked(); } diff --git a/frontend/control/stack-pages/home-page-section.cpp b/frontend/control/stack-pages/home-page-section.cpp index 1f1c1e7..9389310 100644 --- a/frontend/control/stack-pages/home-page-section.cpp +++ b/frontend/control/stack-pages/home-page-section.cpp @@ -229,6 +229,12 @@ void ItemWidget::initUi(HomePageItemShape shape, const QString& name, const QIco } } +/** + * @brief ItemWidget::eventFilter 处理悬停、离开、点击的窗格样式和点击时发出的信号 + * @param watched + * @param event + * @return + */ bool ItemWidget::eventFilter(QObject *watched, QEvent *event) { if(watched == this) { if(event->type() == QEvent::MouseButtonPress) { diff --git a/frontend/control/stack-pages/home-page.cpp b/frontend/control/stack-pages/home-page.cpp index 218e3ce..ae57e8a 100644 --- a/frontend/control/stack-pages/home-page.cpp +++ b/frontend/control/stack-pages/home-page.cpp @@ -66,6 +66,7 @@ void HomePage::appendSection(HomePageSection *section) //以下为homepage各版块的信息获取的回调 +//NEW_TODO //获取快速打开应用的列表 QVector get_quickly_cb() { @@ -114,18 +115,25 @@ QVector get_commonly_cb() void HomePage::registerSections() { //快速打开 - HomePageSection *quickly_section = new HomePageSection(tr("Open Quickly"), HomePageItemShape::Square, m_widget); - quickly_section->setItems(get_quickly_cb()); - if (quickly_section->length()) - this->appendSection(quickly_section); + createSection(tr("Open Quickly"), HomePageItemShape::Square, get_quickly_cb()); //最近打开 - HomePageSection *recently_section = new HomePageSection(tr("Recently Opened"), HomePageItemShape::Bar, m_widget); - recently_section->setItems(get_recently_cb()); - if (recently_section->length()) - this->appendSection(recently_section); + createSection(tr("Recently Opened"), HomePageItemShape::Bar, get_recently_cb()); //常用应用 - HomePageSection *commonly_section = new HomePageSection(tr("Commonly Used"), HomePageItemShape::Square, m_widget); - commonly_section->setItems(get_commonly_cb()); - if (commonly_section->length()) - this->appendSection(commonly_section); + createSection(tr("Commonly Used"), HomePageItemShape::Square, get_commonly_cb()); +} + +void HomePage::createSection(const QString §ion_name, const HomePageItemShape &shape, QVector items) +{ + HomePageSection *section = new HomePageSection(section_name, shape, m_widget); + section->setItems(items); + if (section->length()) + this->appendSection(section); + connect(section, &HomePageSection::requestAction, this, [ = ](const QString &key, const QString &action, const QString &pluginId) { + SearchPluginIface *plugin = SearchPluginManager::getInstance()->getPlugin(pluginId); + if (plugin) { + plugin->openAction(action, key); + } else { + qWarning()<<"Get plugin failed!"; + } + }); } diff --git a/frontend/control/stack-pages/home-page.h b/frontend/control/stack-pages/home-page.h index 22d8ff5..af20f3c 100644 --- a/frontend/control/stack-pages/home-page.h +++ b/frontend/control/stack-pages/home-page.h @@ -24,6 +24,7 @@ #include #include "home-page-section.h" #include "file-utils.h" +#include "pluginmanage/search-plugin-manager.h" namespace Zeeker { class HomePage : public QScrollArea @@ -40,6 +41,7 @@ private: QVBoxLayout * m_mainLyt = nullptr; void registerSections(); + void createSection(const QString &, const HomePageItemShape &, QVector); Q_SIGNALS: }; diff --git a/frontend/control/stack-pages/search-page-section.cpp b/frontend/control/stack-pages/search-page-section.cpp index ca0cb83..29e34c2 100644 --- a/frontend/control/stack-pages/search-page-section.cpp +++ b/frontend/control/stack-pages/search-page-section.cpp @@ -33,6 +33,9 @@ using namespace Zeeker; #define NAME_LABEL_WIDTH 280 #define ICON_SIZE QSize(96, 96) #define LINE_STYLE "QFrame{background: rgba(0,0,0,0.2);}" +#define ACTION_NORMAL_COLOR QColor(55, 144, 250, 255) +#define ACTION_HOVER_COLOR QColor(64, 169, 251, 255) +#define ACTION_PRESS_COLOR QColor(41, 108, 217, 255) ResultArea::ResultArea(QWidget *parent) : QScrollArea(parent) { @@ -110,6 +113,7 @@ void DetailArea::initUi() DetailWidget::DetailWidget(QWidget *parent) : QWidget(parent) { initUi(); + clear(); } QString escapeHtml(const QString & str) { @@ -131,21 +135,22 @@ void DetailWidget::setWidgetInfo(const QString &plugin_name, const SearchPluginI m_nameFrame->show(); m_line_1->show(); if (info.description.length() > 0) { - //NEW_TODO + //NEW_TODO 样式待优化 clearLayout(m_descFrameLyt); Q_FOREACH (SearchPluginIface::DescriptionInfo desc, info.description) { QLabel * descLabel = new QLabel(m_descFrame); + descLabel->setTextFormat(Qt::PlainText); + descLabel->setWordWrap(true); QString show_desc = desc.key + ": " + desc.value; descLabel->setText(show_desc); m_descFrameLyt->addWidget(descLabel); } + m_descFrame->show(); m_line_2->show(); } clearLayout(m_actionFrameLyt); - Q_FOREACH (auto action, info.actionMap) { - //NEW_TODO - QLabel * actionLabel = new QLabel(m_actionFrame); - actionLabel->setText(action); + Q_FOREACH (auto action, info.actionList) { + ActionLabel * actionLabel = new ActionLabel(action, info.key, plugin_name, m_actionFrame); m_actionFrameLyt->addWidget(actionLabel); } m_actionFrame->show(); @@ -179,6 +184,7 @@ void DetailWidget::initUi() m_nameLabel = new QLabel(m_nameFrame); m_nameLabel->setMaximumWidth(NAME_LABEL_WIDTH); m_pluginLabel = new QLabel(m_nameFrame); + m_pluginLabel->setEnabled(false); m_nameFrameLyt->addWidget(m_nameLabel); m_nameFrameLyt->addStretch(); m_nameFrameLyt->addWidget(m_pluginLabel); @@ -239,3 +245,48 @@ void DetailWidget::clearLayout(QLayout *layout) } child = NULL; } + +ActionLabel::ActionLabel(const QString &action, const QString &key, const QString &plugin, QWidget *parent) : QLabel(parent) +{ + m_action = action; + m_key = key; + m_plugin = plugin; + this->initUi(); + this->installEventFilter(this); +} + +void ActionLabel::initUi() +{ + this->setText(m_action); + QPalette pal = palette(); + pal.setColor(QPalette::WindowText, ACTION_NORMAL_COLOR); + pal.setColor(QPalette::Light, ACTION_HOVER_COLOR); + pal.setColor(QPalette::Dark, ACTION_PRESS_COLOR); + this->setPalette(pal); + this->setForegroundRole(QPalette::WindowText); + this->setCursor(QCursor(Qt::PointingHandCursor)); +} + +bool ActionLabel::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == this) { + if(event->type() == QEvent::MouseButtonPress) { + this->setForegroundRole(QPalette::Dark); + return true; + } else if(event->type() == QEvent::MouseButtonRelease) { + SearchPluginIface *plugin = SearchPluginManager::getInstance()->getPlugin(m_plugin); + if (plugin) + plugin->openAction(m_action, m_key); + else + qWarning()<<"Get plugin failed!"; + this->setForegroundRole(QPalette::Light); + return true; + } else if(event->type() == QEvent::Enter) { + this->setForegroundRole(QPalette::Light); + return true; + } else if(event->type() == QEvent::Leave) { + this->setForegroundRole(QPalette::WindowText); + return true; + } + } +} diff --git a/frontend/control/stack-pages/search-page-section.h b/frontend/control/stack-pages/search-page-section.h index 342ab1b..ec3717e 100644 --- a/frontend/control/stack-pages/search-page-section.h +++ b/frontend/control/stack-pages/search-page-section.h @@ -91,6 +91,23 @@ private: Q_SIGNALS: void setWidgetInfo(const QString&, const SearchPluginIface::ResultInfo&); }; + +class ActionLabel : public QLabel +{ + Q_OBJECT +public: + ActionLabel(const QString &action, const QString &key, const QString &plugin, QWidget *parent = nullptr); + ~ActionLabel() = default; +private: + void initUi(); + QString m_action; + QString m_key; + QString m_plugin; + +protected: + bool eventFilter(QObject *, QEvent *); +}; + } #endif // SEARCHPAGESECTION_H diff --git a/frontend/model/search-result-manager.cpp b/frontend/model/search-result-manager.cpp index fbcbc9a..09679ed 100644 --- a/frontend/model/search-result-manager.cpp +++ b/frontend/model/search-result-manager.cpp @@ -32,7 +32,11 @@ SearchResultManager::SearchResultManager(const QString& plugin_id, QObject *pare void SearchResultManager::startSearch(const QString &keyword) { //NEW_TODO 加锁?停止线程?重新搜索? - stopSearch(); +// stopSearch(); + qDebug()<start by others"; + if(! m_get_result_thread->isRunning()) { + m_get_result_thread->start(); + } m_result_queue->clear(); SearchPluginIface *plugin = SearchPluginManager::getInstance()->getPlugin(m_plugin_id); // plugin->KeywordSearch(keyword, m_result_queue); @@ -46,34 +50,31 @@ void SearchResultManager::startSearch(const QString &keyword) desc_1.key = "描述"; desc_1.value = "控制面板搜索插件"; desc.append(desc_1); - QMap actions; - actions.insert("ukcc-search","打开"); + QStringList actions; + actions.append("打开"); test_info.description = desc; - test_info.actionMap = actions; + test_info.actionList = actions; m_result_queue->append(test_info); } else { - test_info.icon = QIcon::fromTheme("ukui-control-center"); - test_info.name = "文件"; + test_info.icon = QIcon::fromTheme("unknown"); + test_info.name = "文件12345abcde.txt"; QVector desc; SearchPluginIface::DescriptionInfo desc_1; SearchPluginIface::DescriptionInfo desc_2; desc_1.key = "描述"; desc_1.value = "一个文件"; desc_2.key = "路径"; - desc_2.value = "一个路径"; + desc_2.value = "一个路径/a/b/c/d/e/fffffff/文件12345abcde.txt"; desc.append(desc_1); desc.append(desc_2); - QMap actions; - actions.insert("file1","打开"); - actions.insert("file2","复制路径"); + QStringList actions; + actions.append("打开"); + actions.append("复制路径"); test_info.description = desc; - test_info.actionMap = actions; + test_info.actionList = actions; m_result_queue->append(test_info); } /********************测试用数据********************/ - - qWarning()<start by others"; - m_get_result_thread->start(); } /** @@ -82,9 +83,9 @@ void SearchResultManager::startSearch(const QString &keyword) void SearchResultManager::stopSearch() { if(m_get_result_thread->isRunning()) { - qWarning()<stopped by others"; - m_get_result_thread->requestInterruption(); - m_get_result_thread->quit(); + qDebug()<stopped by others"; + m_get_result_thread->stop(); +// m_get_result_thread->quit(); } } @@ -98,6 +99,12 @@ ReceiveResultThread::ReceiveResultThread(QQueue * m_result_queue = result_queue; } +void ReceiveResultThread::stop() +{ + this->requestInterruption(); + this->quit(); +} + //NEW_TODO 还未对队列加锁 void ReceiveResultThread::run() { diff --git a/frontend/model/search-result-manager.h b/frontend/model/search-result-manager.h index 1941173..cf3efaa 100644 --- a/frontend/model/search-result-manager.h +++ b/frontend/model/search-result-manager.h @@ -34,13 +34,13 @@ class ReceiveResultThread : public QThread { public: ReceiveResultThread(QQueue * result_queue, QObject * parent = nullptr); ~ReceiveResultThread() = default; + void stop(); protected: void run() override; private: QQueue * m_result_queue; - Q_SIGNALS: void gotResultInfo(const SearchPluginIface::ResultInfo&); diff --git a/frontend/model/search-result-model.cpp b/frontend/model/search-result-model.cpp index 922515f..0440cff 100644 --- a/frontend/model/search-result-model.cpp +++ b/frontend/model/search-result-model.cpp @@ -79,14 +79,23 @@ QVariant SearchResultModel::data(const QModelIndex &index, int role) const void SearchResultModel::appendInfo(const SearchPluginIface::ResultInfo &info) { this->beginResetModel(); - qWarning()<<"Got a result. name ="<style() : QApplication::style(); + + optionV4.text = QString(); + style->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter); //绘制非文本区域内容 + if(index.model()->data(index, Qt::DisplayRole).toString().isEmpty()) return; + + QTextDocument doc; + doc.setHtml(getHtmlText(painter, option, index)); //提取富文本 + QAbstractTextDocumentLayout::PaintContext ctx; + + if(optionV4.state & QStyle::State_Selected) + ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText)); + + QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &optionV4); + textRect.adjust(0, -5, 0, 0); + painter->save(); + painter->translate(textRect.topLeft()); + painter->setClipRect(textRect.translated(-textRect.topLeft())); + doc.documentLayout()->draw(painter, ctx); //绘制文本区域内容 + painter->restore(); +} + +QString ResultViewDelegate::getHtmlText(QPainter *painter, const QStyleOptionViewItem &itemOption, const QModelIndex &index) const +{ + int indexFindLeft = 0; + QString indexString = index.model()->data(index, Qt::DisplayRole).toString(); + QFont ft(painter->font().family(), GlobalSettings::getInstance()->getValue(FONT_SIZE_KEY).toInt()); + QFontMetrics fm(ft); + QString indexColString = fm.elidedText(indexString, Qt::ElideRight, itemOption.rect.width() - 30); //当字体超过Item的长度时显示为省略号 + QString htmlString; + if((indexColString.toUpper()).contains((m_regFindKeyWords.toUpper()))) { + indexFindLeft = indexColString.toUpper().indexOf(m_regFindKeyWords.toUpper()); //得到查找字体在当前整个Item字体中的位置 + htmlString = escapeHtml(indexColString.left(indexFindLeft)) + "" + escapeHtml(indexColString.mid(indexFindLeft, m_regFindKeyWords.length())) + "" + escapeHtml(indexColString.right(indexColString.length() - indexFindLeft - m_regFindKeyWords.length())); + } else { + bool boldOpenned = false; + for(int i = 0; i < indexColString.length(); i++) { + if((m_regFindKeyWords.toUpper()).contains(QString(indexColString.at(i)).toUpper())) { + if(! boldOpenned) { + boldOpenned = true; + htmlString.append(QString("")); + } + htmlString.append(escapeHtml(QString(indexColString.at(i)))); + } else { + if(boldOpenned) { + boldOpenned = false; + htmlString.append(QString("")); + } + htmlString.append(escapeHtml(QString(indexColString.at(i)))); + + } + } + } +// qDebug()<"<", ">"); + return temp; +} diff --git a/frontend/view/result-view-delegate.h b/frontend/view/result-view-delegate.h new file mode 100644 index 0000000..7e885a4 --- /dev/null +++ b/frontend/view/result-view-delegate.h @@ -0,0 +1,46 @@ +/* + * + * Copyright (C) 2020, KylinSoft Co., Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: zhangjiaping + * + */ +#ifndef RESULTVIEWDELEGATE_H +#define RESULTVIEWDELEGATE_H + +#include +#include +#include +#include +#include +#include "global-settings.h" + +namespace Zeeker { +class ResultViewDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + explicit ResultViewDelegate(QObject *parent = nullptr); + ~ResultViewDelegate() = default; + void setSearchKeyword(const QString &); +private: + QString m_regFindKeyWords = 0; + void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override; + QString getHtmlText(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const; + QString escapeHtml(const QString&) const; +}; +} + +#endif // RESULTVIEWDELEGATE_H diff --git a/frontend/view/result-view.cpp b/frontend/view/result-view.cpp index 5fe8022..331e9ce 100644 --- a/frontend/view/result-view.cpp +++ b/frontend/view/result-view.cpp @@ -22,6 +22,24 @@ void ResultWidget::setEnabled(const bool &enabled) m_enabled = enabled; } +/** + * @brief ResultWidget::expandListSlot 展开列表的槽函数 + */ +void ResultWidget::expandListSlot() +{ +//NEW_TODO + qWarning()<<"List will be expanded!"; +} + +/** + * @brief ResultWidget::reduceListSlot 收起列表的槽函数 + */ +void ResultWidget::reduceListSlot() +{ +//NEW_TODO + qWarning()<<"List will be reduced!"; +} + void ResultWidget::initUi() { m_mainLyt = new QVBoxLayout(this); @@ -35,9 +53,10 @@ void ResultWidget::initUi() m_resultView = new ResultView(m_plugin_id, this); - //NEW_TODO + //NEW_TODO 当列表条目大于n?时显示 m_showMoreLabel = new ShowMoreLabel(this); m_showMoreLabel->setFixedHeight(UNFOLD_LABEL_HEIGHT); +// m_showMoreLabel->hide(); m_mainLyt->addWidget(m_titleLabel); m_mainLyt->addWidget(m_resultView); @@ -48,9 +67,17 @@ void ResultWidget::initUi() void ResultWidget::initConnections() { connect(this, &ResultWidget::startSearch, m_resultView, &ResultView::startSearch); + connect(this, &ResultWidget::startSearch, this, [ = ]() { + m_showMoreLabel->resetLabel(); + }); connect(this, &ResultWidget::stopSearch, m_resultView, &ResultView::stopSearch); + connect(this, &ResultWidget::stopSearch, this, [ = ]() { + m_showMoreLabel->resetLabel(); + }); connect(m_resultView, &ResultView::currentRowChanged, this, &ResultWidget::currentRowChanged); connect(this, &ResultWidget::clearSelectedRow, m_resultView, &ResultView::clearSelectedRow); + connect(m_showMoreLabel, &ShowMoreLabel::showMoreClicked, this, &ResultWidget::expandListSlot); + connect(m_showMoreLabel, &ShowMoreLabel::retractClicked, this, &ResultWidget::reduceListSlot); } ResultView::ResultView(const QString &plugin_id, QWidget *parent) : QTreeView(parent) @@ -66,6 +93,8 @@ ResultView::ResultView(const QString &plugin_id, QWidget *parent) : QTreeView(pa this->setModel(m_model); initConnections(); m_plugin_id = plugin_id; + m_style_delegate = new ResultViewDelegate(this); + this->setItemDelegate(m_style_delegate); } bool ResultView::isSelected() @@ -82,23 +111,55 @@ void ResultView::clearSelectedRow() } } +/** + * @brief ResultView::onRowDoubleClickedSlot 处理列表中的双击打开事件 + * @param index 点击的条目 + */ +void ResultView::onRowDoubleClickedSlot(const QModelIndex &index) +{ + const SearchPluginIface::ResultInfo &info = m_model->getInfo(index); + SearchPluginIface *plugin = SearchPluginManager::getInstance()->getPlugin(m_plugin_id); + try { + if (plugin) { + if (!info.actionList.isEmpty()) { + plugin->openAction(info.actionList.at(0), info.key); + } else { + throw -2; + } + } else { + throw -1; + } + } catch(int e) { + qWarning()<<"Open failed, reason="<currentRowChanged(m_plugin_id, m_model->getInfo(this->currentIndex())); + m_is_selected = false; + if(!selected.isEmpty()) { + QRegion region = visualRegionForSelection(selected); + QRect rect = region.boundingRect(); +// Q_EMIT this->currentSelectPos(mapToParent(rect.topLeft())); + } +} + void ResultView::initConnections() { - connect(this, &ResultView::startSearch, m_model, &SearchResultModel::startSearch); +// connect(this, &ResultView::startSearch, m_model, &SearchResultModel::startSearch); + connect(this, &ResultView::startSearch, [ = ](const QString &keyword) { + m_style_delegate->setSearchKeyword(keyword); + m_model->startSearch(keyword); + }); connect(this, &ResultView::stopSearch, m_model, &SearchResultModel::stopSearch); - connect(this->selectionModel(), &QItemSelectionModel::selectionChanged, this, [ = ](const QItemSelection & selected, const QItemSelection & deselected) { - //NEW_TODO 处理选中事件 - m_is_selected = true; - Q_EMIT this->currentRowChanged(m_plugin_id, m_model->getInfo(this->currentIndex())); - m_is_selected = false; - if(!selected.isEmpty()) { - QRegion region = visualRegionForSelection(selected); - QRect rect = region.boundingRect(); -// Q_EMIT this->currentSelectPos(mapToParent(rect.topLeft())); - } - }); - - connect(this, &ResultView::activated, this, [ = ](const QModelIndex & index) { - //NEW_TODO 处理双击打开事件 - }); + connect(this->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ResultView::onRowSelectedSlot); + connect(this, &ResultView::activated, this, &ResultView::onRowDoubleClickedSlot); } diff --git a/frontend/view/result-view.h b/frontend/view/result-view.h index 6fac408..bf61801 100644 --- a/frontend/view/result-view.h +++ b/frontend/view/result-view.h @@ -6,6 +6,7 @@ #include "search-result-model.h" #include "show-more-label.h" #include "title-label.h" +#include "result-view-delegate.h" namespace Zeeker { @@ -19,12 +20,15 @@ public: public Q_SLOTS: void clearSelectedRow(); + void onRowDoubleClickedSlot(const QModelIndex &); + void onRowSelectedSlot(const QItemSelection &, const QItemSelection &); private: void initConnections(); SearchResultModel * m_model = nullptr; QString m_plugin_id; bool m_is_selected = false; + ResultViewDelegate * m_style_delegate = nullptr; Q_SIGNALS: void startSearch(const QString &); @@ -41,6 +45,11 @@ public: ~ResultWidget() = default; QString pluginId(); void setEnabled(const bool&); + +public Q_SLOTS: + void expandListSlot(); + void reduceListSlot(); + private: QString m_plugin_id; bool m_enabled = true; diff --git a/frontend/view/view.pri b/frontend/view/view.pri index 5ee6282..7e6d820 100644 --- a/frontend/view/view.pri +++ b/frontend/view/view.pri @@ -1,7 +1,9 @@ INCLUDEPATH += $$PWD HEADERS += \ + $$PWD/result-view-delegate.h \ $$PWD/result-view.h \ SOURCES += \ + $$PWD/result-view-delegate.cpp \ $$PWD/result-view.cpp \