diff --git a/src/core/global.h b/src/core/global.h index 967fb3a5..29b8e4ee 100644 --- a/src/core/global.h +++ b/src/core/global.h @@ -133,6 +133,8 @@ namespace vnotex int m_length = -1; }; + Q_DECLARE_METATYPE(Segment); + } // ns vnotex Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions); diff --git a/src/core/historymgr.cpp b/src/core/historymgr.cpp index 7ead6db0..875061bf 100644 --- a/src/core/historymgr.cpp +++ b/src/core/historymgr.cpp @@ -8,6 +8,7 @@ #include "vnotex.h" #include "notebookmgr.h" #include +#include using namespace vnotex; @@ -59,9 +60,11 @@ void HistoryMgr::loadHistory() const auto ¬ebooks = VNoteX::getInst().getNotebookMgr().getNotebooks(); for (const auto &nb : notebooks) { const auto &history = nb->getHistory(); + const auto &backend = nb->getBackend(); for (const auto &item : history) { auto fullItem = QSharedPointer::create(); fullItem->m_item = item; + fullItem->m_item.m_path = backend->getFullPath(item.m_path); fullItem->m_notebookName = nb->getName(); m_history.push_back(fullItem); } diff --git a/src/core/iconfig.h b/src/core/iconfig.h index a8cee1ff..d10701fa 100644 --- a/src/core/iconfig.h +++ b/src/core/iconfig.h @@ -96,8 +96,8 @@ namespace vnotex auto arr = read(p_default, p_user, p_key).toArray(); QStringList res; res.reserve(arr.size()); - for (const auto &ele : arr) { - res.push_back(ele.toString()); + for (int i = 0; i < arr.size(); ++i) { + res.push_back(arr[i].toString()); } return res; } @@ -108,8 +108,8 @@ namespace vnotex auto arr = p_obj.value(p_key).toArray(); QStringList res; res.reserve(arr.size()); - for (const auto &ele : arr) { - res.push_back(ele.toString()); + for (int i = 0; i < arr.size(); ++i) { + res.push_back(arr[i].toString()); } return res; } diff --git a/src/core/location.h b/src/core/location.h index 1ccba00c..31b40e79 100644 --- a/src/core/location.h +++ b/src/core/location.h @@ -39,7 +39,7 @@ namespace vnotex { Line() = default; - Line(int p_lineNumber, const QString &p_text, const QVector &p_segments) + Line(int p_lineNumber, const QString &p_text, const QList &p_segments) : m_lineNumber(p_lineNumber), m_text(p_text), m_segments(p_segments) @@ -51,10 +51,10 @@ namespace vnotex QString m_text; - QVector m_segments; + QList m_segments; }; - void addLine(int p_lineNumber, const QString &p_text, const QVector &p_segments) + void addLine(int p_lineNumber, const QString &p_text, const QList &p_segments) { m_lines.push_back(Line(p_lineNumber, p_text, p_segments)); } diff --git a/src/core/notebook/bundlenotebook.cpp b/src/core/notebook/bundlenotebook.cpp index 81bbb32e..83b447d3 100644 --- a/src/core/notebook/bundlenotebook.cpp +++ b/src/core/notebook/bundlenotebook.cpp @@ -70,7 +70,7 @@ void BundleNotebook::addHistory(const HistoryItem &p_item) { HistoryItem item(p_item); item.m_path = getBackend()->getRelativePath(item.m_path); - HistoryMgr::insertHistoryItem(m_history, p_item); + HistoryMgr::insertHistoryItem(m_history, item); updateNotebookConfig(); } diff --git a/src/data/extra/themes/moonlight/palette.json b/src/data/extra/themes/moonlight/palette.json index b4889768..26994e32 100644 --- a/src/data/extra/themes/moonlight/palette.json +++ b/src/data/extra/themes/moonlight/palette.json @@ -236,10 +236,6 @@ "locationlist" : { "node_icon" : { "fg" : "@base#icon#fg" - }, - "text_highlight" : { - "fg" : "@base#master#fg", - "bg" : "@base#master#bg" } }, "viewsplit" : { @@ -621,6 +617,16 @@ "fg" : "@palette#bg2_9", "border" : "@palette#bg2_9" } + }, + "styleditemdelegate" : { + "separator" : { + "fg" : "@base#normal#fg", + "bg" : "@widgets#separator#bg" + }, + "highlight" : { + "fg" : "@base#master#fg", + "bg" : "@base#master#bg" + } } } } diff --git a/src/data/extra/themes/native/palette.json b/src/data/extra/themes/native/palette.json index 796d683f..a3f545de 100644 --- a/src/data/extra/themes/native/palette.json +++ b/src/data/extra/themes/native/palette.json @@ -99,10 +99,6 @@ "locationlist" : { "node_icon" : { "fg" : "@base#icon#fg" - }, - "text_highlight" : { - "fg" : "@base#master#fg", - "bg" : "@base#master#bg" } }, "viewsplit" : { @@ -134,6 +130,16 @@ "fg" : "@base#master#bg", "border" : "@base#master#bg" } + }, + "styleditemdelegate" : { + "separator" : { + "fg" : "@base#normal#fg", + "bg" : "@base#normal#border" + }, + "highlight" : { + "fg" : "@base#master#fg", + "bg" : "@base#master#bg" + } } } } diff --git a/src/data/extra/themes/pure/palette.json b/src/data/extra/themes/pure/palette.json index ef909c64..5b370a7f 100644 --- a/src/data/extra/themes/pure/palette.json +++ b/src/data/extra/themes/pure/palette.json @@ -232,10 +232,6 @@ "locationlist" : { "node_icon" : { "fg" : "@base#icon#fg" - }, - "text_highlight" : { - "fg" : "@base#master#fg", - "bg" : "@base#master#bg" } }, "viewsplit" : { @@ -617,6 +613,16 @@ "fg" : "@base#master#bg", "border" : "@base#master#bg" } + }, + "styleditemdelegate" : { + "separator" : { + "fg" : "@base#normal#fg", + "bg" : "@widgets#separator#bg" + }, + "highlight" : { + "fg" : "@base#master#fg", + "bg" : "@base#master#bg" + } } } } diff --git a/src/search/filesearchengine.cpp b/src/search/filesearchengine.cpp index 9df01888..3ff46a25 100644 --- a/src/search/filesearchengine.cpp +++ b/src/search/filesearchengine.cpp @@ -97,7 +97,7 @@ void FileSearchEngineWorker::searchFile(const QString &p_filePath, const QString const auto lineText = ins.readLine(); bool matched = false; - QVector segments; + QList segments; if (!shouldStartBatchMode) { matched = m_token.matched(lineText, &segments); } else { @@ -172,6 +172,9 @@ void FileSearchEngine::search(const QSharedPointer &p_option, const int step = totalSize / numThread; int remain = totalSize % numThread; int start = 0; + + qDebug() << "start async file search" << totalSize << numThread; + for (int i = 0; i < numThread && start < totalSize; ++i) { int len = step; if (remain) { diff --git a/src/search/searcher.cpp b/src/search/searcher.cpp index 3539ef4f..0189fc97 100644 --- a/src/search/searcher.cpp +++ b/src/search/searcher.cpp @@ -294,7 +294,7 @@ bool Searcher::searchContent(const File *p_file) if (idx > pos) { QString lineText = content.mid(pos, idx - pos); bool matched = false; - QVector segments; + QList segments; if (!shouldStartBatchMode) { matched = m_token.matched(lineText, &segments); } else { @@ -480,17 +480,21 @@ bool Searcher::firstPhaseSearch(Notebook *p_notebook, QVector &p_secondPhaseItems) { Q_ASSERT(!p_secondPhaseItems.isEmpty()); + + emit logRequested(tr("Start second-phase search: %n files(s)", "", p_secondPhaseItems.size())); qDebug() << "secondPhaseSearch" << p_secondPhaseItems.size(); createSearchEngine(); - m_engine->search(m_option, m_token, p_secondPhaseItems); connect(m_engine.data(), &ISearchEngine::finished, this, &Searcher::finished); connect(m_engine.data(), &ISearchEngine::logRequested, this, &Searcher::logRequested); connect(m_engine.data(), &ISearchEngine::resultItemsAdded, this, &Searcher::resultItemsAdded); + + m_engine->search(m_option, m_token, p_secondPhaseItems); + return true; } diff --git a/src/search/searchresultitem.cpp b/src/search/searchresultitem.cpp index fbb0dfdc..1e8c918c 100644 --- a/src/search/searchresultitem.cpp +++ b/src/search/searchresultitem.cpp @@ -6,7 +6,7 @@ QSharedPointer SearchResultItem::createBufferItem(const QStrin const QString &p_displayPath, int p_lineNumber, const QString &p_text, - const QVector &p_segments) + const QList &p_segments) { auto item = createBufferItem(p_targetPath, p_displayPath); item->m_location.addLine(p_lineNumber, p_text, p_segments); @@ -27,7 +27,7 @@ QSharedPointer SearchResultItem::createFileItem(const QString const QString &p_displayPath, int p_lineNumber, const QString &p_text, - const QVector &p_segments) + const QList &p_segments) { auto item = createFileItem(p_targetPath, p_displayPath); item->m_location.addLine(p_lineNumber, p_text, p_segments); @@ -64,7 +64,7 @@ QSharedPointer SearchResultItem::createNotebookItem(const QStr return item; } -void SearchResultItem::addLine(int p_lineNumber, const QString &p_text, const QVector &p_segments) +void SearchResultItem::addLine(int p_lineNumber, const QString &p_text, const QList &p_segments) { m_location.addLine(p_lineNumber, p_text, p_segments); } diff --git a/src/search/searchresultitem.h b/src/search/searchresultitem.h index abe60e1f..f9a13ece 100644 --- a/src/search/searchresultitem.h +++ b/src/search/searchresultitem.h @@ -17,13 +17,13 @@ namespace vnotex return p_dbg; } - void addLine(int p_lineNumber, const QString &p_text, const QVector &p_segments); + void addLine(int p_lineNumber, const QString &p_text, const QList &p_segments); static QSharedPointer createBufferItem(const QString &p_targetPath, const QString &p_displayPath, int p_lineNumber, const QString &p_text, - const QVector &p_segments); + const QList &p_segments); static QSharedPointer createBufferItem(const QString &p_targetPath, const QString &p_displayPath); @@ -32,7 +32,7 @@ namespace vnotex const QString &p_displayPath, int p_lineNumber, const QString &p_text, - const QVector &p_segments); + const QList &p_segments); static QSharedPointer createFileItem(const QString &p_targetPath, const QString &p_displayPath); diff --git a/src/search/searchtoken.cpp b/src/search/searchtoken.cpp index d3900946..8d5133d8 100644 --- a/src/search/searchtoken.cpp +++ b/src/search/searchtoken.cpp @@ -31,7 +31,7 @@ void SearchToken::append(const QRegularExpression &p_regExp) m_regularExpressions.append(p_regExp); } -bool SearchToken::matched(const QString &p_text, QVector *p_segments) const +bool SearchToken::matched(const QString &p_text, QList *p_segments) const { const int consSize = constraintSize(); if (consSize == 0) { @@ -90,7 +90,7 @@ void SearchToken::startBatchMode() m_matchedConstraintsCountInBatchMode = 0; } -bool SearchToken::matchedInBatchMode(const QString &p_text, QVector *p_segments) +bool SearchToken::matchedInBatchMode(const QString &p_text, QList *p_segments) { bool isMatched = false; const int consSize = m_matchedConstraintsInBatchMode.size(); diff --git a/src/search/searchtoken.h b/src/search/searchtoken.h index c276067e..a999df50 100644 --- a/src/search/searchtoken.h +++ b/src/search/searchtoken.h @@ -35,7 +35,7 @@ namespace vnotex void append(const QRegularExpression &p_regExp); // Whether @p_text is matched. - bool matched(const QString &p_text, QVector *p_segments = nullptr) const; + bool matched(const QString &p_text, QList *p_segments = nullptr) const; int constraintSize() const; @@ -48,7 +48,7 @@ namespace vnotex // Match one string in batch mode. // Return true if @p_text is matched. - bool matchedInBatchMode(const QString &p_text, QVector *p_segments = nullptr); + bool matchedInBatchMode(const QString &p_text, QList *p_segments = nullptr); bool readyToEndBatchMode() const; diff --git a/src/widgets/dialogs/selectdialog.cpp b/src/widgets/dialogs/selectdialog.cpp index 73abebca..9bb3878f 100644 --- a/src/widgets/dialogs/selectdialog.cpp +++ b/src/widgets/dialogs/selectdialog.cpp @@ -118,21 +118,6 @@ void SelectDialog::keyPressEvent(QKeyEvent *p_event) return; } - // On Mac OS X, it is `Command+O` to activate an item, instead of Return. -#if defined(Q_OS_MACOS) || defined(Q_OS_MAC) - { - const int key = p_event->key(); - if (key == Qt::Key_Return || key == Qt::Key_Enter) { - p_event->accept(); - if (auto item = m_list->currentItem()) { - selectionChosen(item); - } - - return; - } - } -#endif - QDialog::keyPressEvent(p_event); } diff --git a/src/widgets/editors/statuswidget.cpp b/src/widgets/editors/statuswidget.cpp index 5846a5a7..d1e508b6 100644 --- a/src/widgets/editors/statuswidget.cpp +++ b/src/widgets/editors/statuswidget.cpp @@ -2,25 +2,26 @@ #include #include -#include +#include +#include using namespace vnotex; StatusWidget::StatusWidget(QWidget *p_parent) : QWidget(p_parent) { - auto layout = new QHBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); + m_mainLayout = new QStackedLayout(this); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + m_mainLayout->setSpacing(0); m_messageLabel = new QLabel(this); m_messageLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - layout->addWidget(m_messageLabel); + m_mainLayout->addWidget(m_messageLabel); m_messageTimer = new QTimer(this); m_messageTimer->setSingleShot(true); connect(m_messageTimer, &QTimer::timeout, - m_messageLabel, &QLabel::clear); + this, &StatusWidget::clearMessage); } StatusWidget::~StatusWidget() @@ -32,13 +33,42 @@ StatusWidget::~StatusWidget() void StatusWidget::showMessage(const QString &p_msg, int p_milliseconds) { + if (p_msg.isEmpty()) { + clearMessage(); + return; + } + m_messageLabel->setText(p_msg); - m_messageTimer->start(p_milliseconds); + m_mainLayout->setCurrentWidget(m_messageLabel); + + if (p_milliseconds > 0) { + m_messageTimer->start(p_milliseconds); + } } void StatusWidget::setEditorStatusWidget(const QSharedPointer &p_editorWidget) { Q_ASSERT(!m_editorWidget); m_editorWidget = p_editorWidget; - layout()->addWidget(m_editorWidget.data()); + m_mainLayout->addWidget(m_editorWidget.data()); + m_mainLayout->setCurrentWidget(m_editorWidget.data()); +} + +void StatusWidget::resizeEvent(QResizeEvent *p_event) +{ + QWidget::resizeEvent(p_event); + + int maxWidth = width() - 10; + if (maxWidth <= 0) { + maxWidth = width(); + } + m_messageLabel->setMaximumWidth(maxWidth); +} + +void StatusWidget::clearMessage() +{ + m_messageLabel->clear(); + if (m_editorWidget) { + m_mainLayout->setCurrentWidget(m_editorWidget.data()); + } } diff --git a/src/widgets/editors/statuswidget.h b/src/widgets/editors/statuswidget.h index 47cc5d3e..1e93e4f0 100644 --- a/src/widgets/editors/statuswidget.h +++ b/src/widgets/editors/statuswidget.h @@ -6,6 +6,7 @@ class QLabel; class QTimer; +class QStackedLayout; namespace vnotex { @@ -22,7 +23,14 @@ namespace vnotex void setEditorStatusWidget(const QSharedPointer &p_editorWidget); + protected: + void resizeEvent(QResizeEvent *p_event) Q_DECL_OVERRIDE; + private: + void clearMessage(); + + QStackedLayout *m_mainLayout = nullptr; + QLabel *m_messageLabel = nullptr; QTimer *m_messageTimer = nullptr; diff --git a/src/widgets/historypanel.cpp b/src/widgets/historypanel.cpp index 9dd35b9f..27e12b8c 100644 --- a/src/widgets/historypanel.cpp +++ b/src/widgets/historypanel.cpp @@ -14,7 +14,6 @@ #include #include - #include "titlebar.h" #include "listwidget.h" #include "mainwindow.h" @@ -40,7 +39,7 @@ void HistoryPanel::setupUI() mainLayout->addWidget(m_titleBar); } - m_historyList = new ListWidget(this); + m_historyList = new ListWidget(true, this); m_historyList->setContextMenuPolicy(Qt::CustomContextMenu); m_historyList->setSelectionMode(QAbstractItemView::ExtendedSelection); connect(m_historyList, &QListWidget::customContextMenuRequested, diff --git a/src/widgets/itemproxystyle.cpp b/src/widgets/itemproxystyle.cpp new file mode 100644 index 00000000..664e961f --- /dev/null +++ b/src/widgets/itemproxystyle.cpp @@ -0,0 +1,257 @@ +#include "itemproxystyle.h" + +#include +#include +#include +#include +#include + +#include "styleditemdelegate.h" + +using namespace vnotex; + +ItemProxyStyle::ItemProxyStyle(QStyle *p_style) + : QProxyStyle(p_style) +{ +} + +void ItemProxyStyle::drawControl(QStyle::ControlElement p_element, + const QStyleOption *p_option, + QPainter *p_painter, + const QWidget *p_widget) const +{ + if (p_element == QStyle::CE_ItemViewItem) { + if (drawItemViewItem(p_option, p_painter, p_widget)) { + return; + } + } + + QProxyStyle::drawControl(p_element, p_option, p_painter, p_widget); +} + +bool ItemProxyStyle::drawItemViewItem(const QStyleOption *p_option, QPainter *p_painter, const QWidget *p_widget) const +{ + const QStyleOptionViewItem *vopt = qstyleoption_cast(p_option); + if (!vopt) { + return false; + } + + const auto value = vopt->index.data(HighlightsRole); + if (!value.canConvert>()) { + return false; + } + + auto segments = value.value>(); + if (segments.isEmpty()) { + return false; + } + + // Copied from qtbase/src/widgets/styles/qcommonstyle.cpp. + + p_painter->save(); + p_painter->setClipRect(vopt->rect); + QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, p_widget); + QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, p_widget); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, p_widget); + + // Draw the background. + proxy()->drawPrimitive(PE_PanelItemViewItem, vopt, p_painter, p_widget); + + // Draw the check mark. + if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) { + QStyleOptionViewItem option(*vopt); + option.rect = checkRect; + option.state = option.state & ~QStyle::State_HasFocus; + switch (vopt->checkState) { + case Qt::Unchecked: + option.state |= QStyle::State_Off; + break; + case Qt::PartiallyChecked: + option.state |= QStyle::State_NoChange; + break; + case Qt::Checked: + option.state |= QStyle::State_On; + break; + } + proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, p_painter, p_widget); + } + + // Draw the icon. + QIcon::Mode mode = QIcon::Normal; + if (!(vopt->state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if (vopt->state & QStyle::State_Selected) { + mode = QIcon::Selected; + } + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(p_painter, iconRect, vopt->decorationAlignment, mode, state); + + // Draw the text. + if (!vopt->text.isEmpty()) { + QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active)) { + cg = QPalette::Inactive; + } + if (vopt->state & QStyle::State_Selected) { + p_painter->setPen(vopt->palette.color(cg, QPalette::HighlightedText)); + } else { + p_painter->setPen(vopt->palette.color(cg, QPalette::Text)); + } + if (vopt->state & QStyle::State_Editing) { + p_painter->setPen(vopt->palette.color(cg, QPalette::Text)); + p_painter->drawRect(textRect.adjusted(0, 0, -1, -1)); + } + + viewItemDrawText(p_painter, vopt, textRect); + } + + // Draw the focus rect. + if (vopt->state & QStyle::State_HasFocus) { + QStyleOptionFocusRect o; + o.QStyleOption::operator=(*vopt); + o.rect = proxy()->subElementRect(SE_ItemViewItemFocusRect, vopt, p_widget); + o.state |= QStyle::State_KeyboardFocusChange; + o.state |= QStyle::State_Item; + QPalette::ColorGroup cg = (vopt->state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; + o.backgroundColor = vopt->palette.color(cg, (vopt->state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); + proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, p_painter, p_widget); + } + + p_painter->restore(); + + return true; +} + +static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth, int maxHeight = -1, int *lastVisibleLine = nullptr) +{ + if (lastVisibleLine) + *lastVisibleLine = -1; + qreal height = 0; + qreal widthUsed = 0; + textLayout.beginLayout(); + int i = 0; + while (true) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(lineWidth); + line.setPosition(QPointF(0, height)); + height += line.height(); + widthUsed = qMax(widthUsed, line.naturalTextWidth()); + // we assume that the height of the next line is the same as the current one + if (maxHeight > 0 && lastVisibleLine && height + line.height() > maxHeight) { + const QTextLine nextLine = textLayout.createLine(); + *lastVisibleLine = nextLine.isValid() ? i : -1; + break; + } + ++i; + } + textLayout.endLayout(); + return QSizeF(widthUsed, height); +} + +void ItemProxyStyle::viewItemDrawText(QPainter *p_painter, const QStyleOptionViewItem *p_option, const QRect &p_rect) const +{ + // Copied from qtbase/src/widgets/styles/qcommonstyle.cpp. + + const QWidget *widget = p_option->widget; + const int textMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1; + // Remove width padding. + QRect textRect = p_rect.adjusted(textMargin, 0, -textMargin, 0); + const bool wrapText = p_option->features & QStyleOptionViewItem::WrapText; + QTextOption textOption; + textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap); + textOption.setTextDirection(p_option->direction); + textOption.setAlignment(QStyle::visualAlignment(p_option->direction, p_option->displayAlignment)); + QPointF paintPosition; + const QString newText = calculateElidedText(p_option->text, + textOption, + p_option->font, + textRect, + p_option->displayAlignment, + p_option->textElideMode, + 0, + true, + &paintPosition); + QTextLayout textLayout(newText, p_option->font); + textLayout.setTextOption(textOption); + viewItemTextLayout(textLayout, textRect.width()); + textLayout.draw(p_painter, paintPosition); +} + +QString ItemProxyStyle::calculateElidedText(const QString &text, const QTextOption &textOption, + const QFont &font, const QRect &textRect, const Qt::Alignment valign, + Qt::TextElideMode textElideMode, int flags, + bool lastVisibleLineShouldBeElided, QPointF *paintStartPosition) const +{ + // Copied from qtbase/src/widgets/styles/qcommonstyle.cpp. + + QTextLayout textLayout(text, font); + textLayout.setTextOption(textOption); + // In AlignVCenter mode when more than one line is displayed and the height only allows + // some of the lines it makes no sense to display those. From a users perspective it makes + // more sense to see the start of the text instead something inbetween. + const bool vAlignmentOptimization = paintStartPosition && valign.testFlag(Qt::AlignVCenter); + int lastVisibleLine = -1; + viewItemTextLayout(textLayout, textRect.width(), vAlignmentOptimization ? textRect.height() : -1, &lastVisibleLine); + const QRectF boundingRect = textLayout.boundingRect(); + // don't care about LTR/RTL here, only need the height + const QRect layoutRect = QStyle::alignedRect(Qt::LayoutDirectionAuto, valign, + boundingRect.size().toSize(), textRect); + if (paintStartPosition) + *paintStartPosition = QPointF(textRect.x(), layoutRect.top()); + QString ret; + qreal height = 0; + const int lineCount = textLayout.lineCount(); + for (int i = 0; i < lineCount; ++i) { + const QTextLine line = textLayout.lineAt(i); + height += line.height(); + // above visible rect + if (height + layoutRect.top() <= textRect.top()) { + if (paintStartPosition) + paintStartPosition->ry() += line.height(); + continue; + } + const int start = line.textStart(); + const int length = line.textLength(); + const bool drawElided = line.naturalTextWidth() > textRect.width(); + bool elideLastVisibleLine = lastVisibleLine == i; + if (!drawElided && i + 1 < lineCount && lastVisibleLineShouldBeElided) { + const QTextLine nextLine = textLayout.lineAt(i + 1); + const int nextHeight = height + nextLine.height() / 2; + // elide when less than the next half line is visible + if (nextHeight + layoutRect.top() > textRect.height() + textRect.top()) + elideLastVisibleLine = true; + } + QString text = textLayout.text().mid(start, length); + if (drawElided || elideLastVisibleLine) { + Q_ASSERT(false); + if (elideLastVisibleLine) { + if (text.endsWith(QChar::LineSeparator)) + text.chop(1); + text += QChar(0x2026); + } + /* TODO: QStackTextEngine is a private class. + const QStackTextEngine engine(text, font); + ret += engine.elidedText(textElideMode, textRect.width(), flags); + */ + Q_UNUSED(flags); + Q_UNUSED(textElideMode); + ret += text; + // no newline for the last line (last visible or real) + // sometimes drawElided is true but no eliding is done so the text ends + // with QChar::LineSeparator - don't add another one. This happened with + // arabic text in the testcase for QTBUG-72805 + if (i < lineCount - 1 && + !ret.endsWith(QChar::LineSeparator)) + ret += QChar::LineSeparator; + } else { + ret += text; + } + // below visible text, can stop + if ((height + layoutRect.top() >= textRect.bottom()) || + (lastVisibleLine >= 0 && lastVisibleLine == i)) + break; + } + return ret; +} diff --git a/src/widgets/itemproxystyle.h b/src/widgets/itemproxystyle.h new file mode 100644 index 00000000..49b1881c --- /dev/null +++ b/src/widgets/itemproxystyle.h @@ -0,0 +1,35 @@ +#ifndef ITEMPROXYSTYLE_H +#define ITEMPROXYSTYLE_H + +#include + +class QStyleOptionViewItem; +class QTextOption; + +namespace vnotex +{ + // Draw item with text segments highlighted. + class ItemProxyStyle : public QProxyStyle + { + Q_OBJECT + public: + explicit ItemProxyStyle(QStyle *p_style = nullptr); + + void drawControl(QStyle::ControlElement p_element, + const QStyleOption *p_option, + QPainter *p_painter, + const QWidget *p_widget = nullptr) const Q_DECL_OVERRIDE; + + private: + bool drawItemViewItem(const QStyleOption *p_option, QPainter *p_painter, const QWidget *p_widget) const; + + void viewItemDrawText(QPainter *p_painter, const QStyleOptionViewItem *p_option, const QRect &p_rect) const; + + QString calculateElidedText(const QString &text, const QTextOption &textOption, + const QFont &font, const QRect &textRect, const Qt::Alignment valign, + Qt::TextElideMode textElideMode, int flags, + bool lastVisibleLineShouldBeElided, QPointF *paintStartPosition) const; + }; +} + +#endif // ITEMPROXYSTYLE_H diff --git a/src/widgets/lineedit.cpp b/src/widgets/lineedit.cpp index 780957ec..b62fd5d1 100644 --- a/src/widgets/lineedit.cpp +++ b/src/widgets/lineedit.cpp @@ -115,9 +115,23 @@ void LineEdit::setInputMethodEnabled(bool p_enabled) if (m_inputMethodEnabled != p_enabled) { m_inputMethodEnabled = p_enabled; - QInputMethod *im = QGuiApplication::inputMethod(); - im->reset(); - // Ask input method to query current state, which will call inputMethodQuery(). - im->update(Qt::ImEnabled); + updateInputMethod(); } } + +void LineEdit::showEvent(QShowEvent *p_event) +{ + QLineEdit::showEvent(p_event); + + if (!m_inputMethodEnabled) { + updateInputMethod(); + } +} + +void LineEdit::updateInputMethod() const +{ + QInputMethod *im = QGuiApplication::inputMethod(); + im->reset(); + // Ask input method to query current state, which will call inputMethodQuery(). + im->update(Qt::ImEnabled); +} diff --git a/src/widgets/lineedit.h b/src/widgets/lineedit.h index e98ed647..bdeb2bb8 100644 --- a/src/widgets/lineedit.h +++ b/src/widgets/lineedit.h @@ -23,7 +23,11 @@ namespace vnotex protected: void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; + void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE; + private: + void updateInputMethod() const; + // Whether enable input method. bool m_inputMethodEnabled = true; }; diff --git a/src/widgets/listwidget.cpp b/src/widgets/listwidget.cpp index 4f768006..04620deb 100644 --- a/src/widgets/listwidget.cpp +++ b/src/widgets/listwidget.cpp @@ -1,13 +1,47 @@ #include "listwidget.h" #include + +#include +#include #include +#include "styleditemdelegate.h" using namespace vnotex; +QBrush ListWidget::s_separatorForeground; + +QBrush ListWidget::s_separatorBackground; + ListWidget::ListWidget(QWidget *p_parent) : QListWidget(p_parent) { + initialize(); +} + +ListWidget::ListWidget(bool p_enhancedStyle, QWidget *p_parent) + : QListWidget(p_parent) +{ + initialize(); + + if (p_enhancedStyle) { + auto delegate = new StyledItemDelegate(QSharedPointer::create(this), + StyledItemDelegate::None, + this); + setItemDelegate(delegate); + } +} + +void ListWidget::initialize() +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + + const auto &themeMgr = VNoteX::getInst().getThemeMgr(); + s_separatorForeground = QColor(themeMgr.paletteColor(QStringLiteral("widgets#styleditemdelegate#separator#fg"))); + s_separatorBackground = QColor(themeMgr.paletteColor(QStringLiteral("widgets#styleditemdelegate#separator#bg"))); + } } void ListWidget::keyPressEvent(QKeyEvent *p_event) @@ -16,6 +50,16 @@ void ListWidget::keyPressEvent(QKeyEvent *p_event) return; } + // On Mac OS X, it is `Command+O` to activate an item, instead of Return. +#if defined(Q_OS_MACOS) || defined(Q_OS_MAC) + if (p_event->key() == Qt::Key_Return) { + if (auto item = currentItem()) { + emit itemActivated(item); + } + return; + } +#endif + QListWidget::keyPressEvent(p_event); } @@ -44,12 +88,14 @@ QVector ListWidget::getVisibleItems(const QListWidget *p_widg QListWidgetItem *ListWidget::createSeparatorItem(const QString &p_text) { - QListWidgetItem *item = new QListWidgetItem(p_text, nullptr, c_separatorType); + QListWidgetItem *item = new QListWidgetItem(p_text, nullptr, ItemTypeSeparator); + item->setData(Qt::ForegroundRole, s_separatorForeground); + item->setData(Qt::BackgroundRole, s_separatorBackground); item->setFlags(Qt::NoItemFlags); return item; } bool ListWidget::isSeparatorItem(const QListWidgetItem *p_item) { - return p_item->type() == c_separatorType; + return p_item->type() == ItemTypeSeparator; } diff --git a/src/widgets/listwidget.h b/src/widgets/listwidget.h index 0a143794..3dfd698f 100644 --- a/src/widgets/listwidget.h +++ b/src/widgets/listwidget.h @@ -12,6 +12,8 @@ namespace vnotex public: explicit ListWidget(QWidget *p_parent = nullptr); + ListWidget(bool p_enhancedStyle, QWidget *p_parent = nullptr); + static QVector getVisibleItems(const QListWidget *p_widget); static QListWidgetItem *createSeparatorItem(const QString &p_text); @@ -22,7 +24,16 @@ namespace vnotex void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; private: - static const int c_separatorType = 2000; + enum + { + ItemTypeSeparator = 2000 + }; + + void initialize(); + + static QBrush s_separatorForeground; + + static QBrush s_separatorBackground; }; } diff --git a/src/widgets/locationlist.cpp b/src/widgets/locationlist.cpp index f7ad04cc..16a9f143 100644 --- a/src/widgets/locationlist.cpp +++ b/src/widgets/locationlist.cpp @@ -8,6 +8,7 @@ #include "treewidget.h" #include "widgetsfactory.h" #include "titlebar.h" +#include "styleditemdelegate.h" #include #include @@ -24,20 +25,10 @@ QIcon LocationList::s_folderIcon; QIcon LocationList::s_notebookIcon; -QString LocationList::s_textHighlightForeground; - -QString LocationList::s_textHighlightBackground; - LocationList::LocationList(QWidget *p_parent) : QFrame(p_parent) { setupUI(); - - if (s_textHighlightForeground.isEmpty()) { - const auto &themeMgr = VNoteX::getInst().getThemeMgr(); - s_textHighlightForeground = themeMgr.paletteColor(QStringLiteral("widgets#locationlist#text_highlight#fg")); - s_textHighlightBackground = themeMgr.paletteColor(QStringLiteral("widgets#locationlist#text_highlight#bg")); - } } void LocationList::setupUI() @@ -50,11 +41,10 @@ void LocationList::setupUI() mainLayout->addWidget(m_titleBar); } - m_tree = new TreeWidget(TreeWidget::Flag::None, this); + m_tree = new TreeWidget(TreeWidget::Flag::EnhancedStyle, this); // When updated, pay attention to the Columns enum. m_tree->setHeaderLabels(QStringList() << tr("Path") << tr("Line") << tr("Text")); TreeWidget::showHorizontalScrollbar(m_tree); - m_tree->header()->setSectionResizeMode(QHeaderView::Interactive); connect(m_tree, &QTreeWidget::itemActivated, this, [this](QTreeWidgetItem *p_item, int p_col) { Q_UNUSED(p_col); @@ -135,36 +125,15 @@ void LocationList::setItemLocationLineAndText(QTreeWidgetItem *p_item, const Com p_item->setText(Columns::LineColumn, QString::number(p_line.m_lineNumber + 1)); } - if (p_line.m_segments.isEmpty()) { - p_item->setText(Columns::TextColumn, p_line.m_text); + // Truncate the text. + if (p_line.m_text.size() > 500) { + p_item->setText(Columns::TextColumn, p_line.m_text.left(500)); } else { - auto segments = p_line.m_segments; - std::sort(segments.begin(), segments.end()); + p_item->setText(Columns::TextColumn, p_line.m_text); + } - // Use \n as a marker for < and use \r for >. - QString text(p_line.m_text); - int lastOffset = text.size(); - for (int i = segments.size() - 1; i >= 0; --i) { - Q_ASSERT(segments[i].m_length > 0); - if (segments[i].m_offset + segments[i].m_length > lastOffset) { - // Interset. - continue; - } - - lastOffset = segments[i].m_offset; - text.insert(segments[i].m_offset + segments[i].m_length, QStringLiteral("\n/span\r")); - text.insert(segments[i].m_offset, - QString("\nspan style='color:%1;background-color:%2'\r").arg(s_textHighlightForeground, s_textHighlightBackground)); - } - - text = text.toHtmlEscaped(); - text.replace(QLatin1Char('\n'), QLatin1Char('<')); - text.replace(QLatin1Char('\r'), QLatin1Char('>')); - - auto label = new QLabel(m_tree); - label->setTextFormat(Qt::RichText); - label->setText(text); - m_tree->setItemWidget(p_item, Columns::TextColumn, label); + if (!p_line.m_segments.isEmpty()) { + p_item->setData(Columns::TextColumn, HighlightsRole, QVariant::fromValue(p_line.m_segments)); } } @@ -172,9 +141,8 @@ void LocationList::addLocation(const ComplexLocation &p_location) { auto item = new QTreeWidgetItem(m_tree); item->setText(Columns::PathColumn, p_location.m_displayPath); - item->setData(Columns::PathColumn, Qt::UserRole, p_location.m_path); - item->setIcon(Columns::PathColumn, getItemIcon(p_location.m_type)); + item->setData(Columns::PathColumn, Qt::UserRole, p_location.m_path); if (p_location.m_lines.size() == 1) { setItemLocationLineAndText(item, p_location.m_lines[0]); diff --git a/src/widgets/locationlist.h b/src/widgets/locationlist.h index bc2a9e17..b987bf98 100644 --- a/src/widgets/locationlist.h +++ b/src/widgets/locationlist.h @@ -68,10 +68,6 @@ namespace vnotex static QIcon s_folderIcon; static QIcon s_notebookIcon; - - static QString s_textHighlightForeground; - - static QString s_textHighlightBackground; }; } diff --git a/src/widgets/markdownviewwindow.cpp b/src/widgets/markdownviewwindow.cpp index be1afe79..6c022330 100644 --- a/src/widgets/markdownviewwindow.cpp +++ b/src/widgets/markdownviewwindow.cpp @@ -250,7 +250,6 @@ void MarkdownViewWindow::setupToolBar() auto toolBar = createToolBar(this); const auto &editorConfig = ConfigMgr::getInst().getEditorConfig(); - const auto &markdownEditorConfig = editorConfig.getMarkdownEditorConfig(); const int iconSize = editorConfig.getToolBarIconSize(); toolBar->setIconSize(QSize(iconSize, iconSize)); diff --git a/src/widgets/notebooknodeexplorer.cpp b/src/widgets/notebooknodeexplorer.cpp index 40da1d0c..16aa043c 100644 --- a/src/widgets/notebooknodeexplorer.cpp +++ b/src/widgets/notebooknodeexplorer.cpp @@ -1,9 +1,7 @@ #include "notebooknodeexplorer.h" -#include #include #include -#include #include #include #include diff --git a/src/widgets/searchpanel.cpp b/src/widgets/searchpanel.cpp index d8d3e3b2..dadc613d 100644 --- a/src/widgets/searchpanel.cpp +++ b/src/widgets/searchpanel.cpp @@ -313,6 +313,8 @@ void SearchPanel::startSearch() void SearchPanel::handleSearchFinished(SearchState p_state) { + qDebug() << "handleSearchFinished" << (int)p_state; + Q_ASSERT(m_searchOngoing); Q_ASSERT(p_state != SearchState::Idle); diff --git a/src/widgets/simplesegmenthighlighter.cpp b/src/widgets/simplesegmenthighlighter.cpp new file mode 100644 index 00000000..56810e6c --- /dev/null +++ b/src/widgets/simplesegmenthighlighter.cpp @@ -0,0 +1,35 @@ +#include "simplesegmenthighlighter.h" + +#include + +using namespace vnotex; + +SimpleSegmentHighlighter::SimpleSegmentHighlighter(QTextDocument *p_parent) + : QSyntaxHighlighter(p_parent) +{ +} + +void SimpleSegmentHighlighter::highlightBlock(const QString &p_text) +{ + if (m_segments.isEmpty() || !m_highlightFormat.isValid()) { + return; + } + + const int len = p_text.size(); + for (const auto &seg : m_segments) { + if (seg.m_offset >= 0 && seg.m_offset < len) { + setFormat(seg.m_offset, qMin(seg.m_length, len - seg.m_offset), m_highlightFormat); + } + } +} + +void SimpleSegmentHighlighter::setSegments(const QList &p_segments) +{ + m_segments = p_segments; +} + +void SimpleSegmentHighlighter::setHighlightFormat(const QBrush &p_foreground, const QBrush &p_background) +{ + m_highlightFormat.setForeground(p_foreground); + m_highlightFormat.setBackground(p_background); +} diff --git a/src/widgets/simplesegmenthighlighter.h b/src/widgets/simplesegmenthighlighter.h new file mode 100644 index 00000000..068b8442 --- /dev/null +++ b/src/widgets/simplesegmenthighlighter.h @@ -0,0 +1,31 @@ +#ifndef SIMPLESEGMENTHIGHLIGHTER_H +#define SIMPLESEGMENTHIGHLIGHTER_H + +#include +#include + +#include + +namespace vnotex +{ + class SimpleSegmentHighlighter : public QSyntaxHighlighter + { + Q_OBJECT + public: + explicit SimpleSegmentHighlighter(QTextDocument *p_parent); + + void setSegments(const QList &p_segments); + + void setHighlightFormat(const QBrush &p_foreground, const QBrush &p_background); + + protected: + void highlightBlock(const QString &p_text) Q_DECL_OVERRIDE; + + private: + QTextCharFormat m_highlightFormat; + + QList m_segments; + }; +} + +#endif // SIMPLESEGMENTHIGHLIGHTER_H diff --git a/src/widgets/styleditemdelegate.cpp b/src/widgets/styleditemdelegate.cpp new file mode 100644 index 00000000..98197432 --- /dev/null +++ b/src/widgets/styleditemdelegate.cpp @@ -0,0 +1,158 @@ +#include "styleditemdelegate.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "listwidget.h" +#include "treewidget.h" +#include "simplesegmenthighlighter.h" + +using namespace vnotex; + +StyledItemDelegateListWidget::StyledItemDelegateListWidget(const ListWidget *p_listWidget) + : m_listWidget(p_listWidget) +{ +} + + +StyledItemDelegateTreeWidget::StyledItemDelegateTreeWidget(const TreeWidget *p_treeWidget) + : m_treeWidget(p_treeWidget) +{ +} + + +QBrush StyledItemDelegate::s_highlightForeground; + +QBrush StyledItemDelegate::s_highlightBackground; + +StyledItemDelegate::StyledItemDelegate(const QSharedPointer &p_interface, + DelegateFlags p_flags, + QObject *p_parent) + : QStyledItemDelegate(p_parent), + m_interface(p_interface), + m_flags(p_flags) +{ + initialize(); + + if (m_flags & DelegateFlag::Highlights) { + m_document = new QTextDocument(this); + m_highlighter = new SimpleSegmentHighlighter(m_document); + m_highlighter->setHighlightFormat(s_highlightForeground, s_highlightBackground); + } +} + +void StyledItemDelegate::initialize() +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + + const auto &themeMgr = VNoteX::getInst().getThemeMgr(); + s_highlightForeground = QColor(themeMgr.paletteColor(QStringLiteral("widgets#styleditemdelegate#highlight#fg"))); + s_highlightBackground = QColor(themeMgr.paletteColor(QStringLiteral("widgets#styleditemdelegate#highlight#bg"))); + } +} + +void StyledItemDelegate::paint(QPainter *p_painter, + const QStyleOptionViewItem &p_option, + const QModelIndex &p_index) const +{ + // [Qt's BUG] Qt does not draw the background from Qt::BackgroundRole. Do it manually. + auto bgBrushVal = p_index.data(Qt::BackgroundRole); + if (bgBrushVal.canConvert()) { + auto brush = qvariant_cast(bgBrushVal); + if (brush.style() != Qt::NoBrush) { + p_painter->fillRect(p_option.rect, brush); + } + } + + if (m_flags & DelegateFlag::Highlights) { + const auto value = p_index.data(HighlightsRole); + if (value.canConvert>()) { + auto segments = value.value>(); + if (!segments.isEmpty()) { + paintWithHighlights(p_painter, p_option, p_index, segments); + return; + } + } + } + + QStyledItemDelegate::paint(p_painter, p_option, p_index); +} + +static void drawContents(const QStyleOptionViewItem &p_option, + QTextDocument *p_doc, + QPainter *p_painter, + const QRectF &p_rect) +{ + // From qtbase/src/gui/text/qtextdocument.cpp. + + p_painter->save(); + + QAbstractTextDocumentLayout::PaintContext ctx; + if (p_rect.isValid()) { + p_painter->setClipRect(p_rect); + ctx.clip = p_rect; + } + + // Update palette. + ctx.palette.setBrush(QPalette::Text, p_option.palette.brush(QPalette::Text)); + + p_doc->documentLayout()->draw(p_painter, ctx); + + p_painter->restore(); +} + +void StyledItemDelegate::paintWithHighlights(QPainter *p_painter, + const QStyleOptionViewItem &p_option, + const QModelIndex &p_index, + const QList &p_segments) const +{ + QStyleOptionViewItem opt(p_option); + initStyleOption(&opt, p_index); + + m_highlighter->setSegments(p_segments); + m_document->clear(); + m_document->setDefaultFont(opt.font); + m_document->setPlainText(opt.text); + + p_painter->save(); + + // Draw the item without text. + opt.text = ""; + auto style = opt.widget ? opt.widget->style() : QApplication::style(); + style->drawControl(QStyle::CE_ItemViewItem, &opt, p_painter, opt.widget); + + // Draw the text via QTextDocument. + p_painter->translate(opt.rect.left(), opt.rect.top()); + const QRect clip(0, 0, opt.rect.width(), opt.rect.height()); + drawContents(opt, m_document, p_painter, clip); + + p_painter->restore(); +} + +QSize StyledItemDelegate::sizeHint(const QStyleOptionViewItem &p_option, const QModelIndex &p_index) const +{ + if (m_flags & DelegateFlag::Highlights) { + const auto value = p_index.data(HighlightsRole); + if (value.canConvert>()) { + auto segments = value.value>(); + if (!segments.isEmpty()) { + QStyleOptionViewItem opt(p_option); + initStyleOption(&opt, p_index); + + m_document->setPlainText(opt.text); + return QSize(m_document->idealWidth(), m_document->size().height()); + + } + } + } + + return QStyledItemDelegate::sizeHint(p_option, p_index); +} diff --git a/src/widgets/styleditemdelegate.h b/src/widgets/styleditemdelegate.h new file mode 100644 index 00000000..4806c52c --- /dev/null +++ b/src/widgets/styleditemdelegate.h @@ -0,0 +1,98 @@ +#ifndef STYLEDITEMDELEGATE_H +#define STYLEDITEMDELEGATE_H + +#include +#include +#include +#include + +#include + +class QTextDocument; + +namespace vnotex +{ + class ListWidget; + class TreeWidget; + class SimpleSegmentHighlighter; + + enum + { + HighlightsRole = 0x0101 + }; + + + class StyledItemDelegateInterface + { + public: + virtual ~StyledItemDelegateInterface() = default; + }; + + + class StyledItemDelegateListWidget : public StyledItemDelegateInterface + { + public: + explicit StyledItemDelegateListWidget(const ListWidget *p_listWidget); + + private: + const ListWidget *m_listWidget = nullptr; + }; + + + class StyledItemDelegateTreeWidget : public StyledItemDelegateInterface + { + public: + explicit StyledItemDelegateTreeWidget(const TreeWidget *p_treeWidget); + + private: + const TreeWidget *m_treeWidget = nullptr; + }; + + + // Template is not supported with QObject. + class StyledItemDelegate : public QStyledItemDelegate + { + Q_OBJECT + public: + enum DelegateFlag + { + None = 0, + Highlights = 0x1 + }; + Q_DECLARE_FLAGS(DelegateFlags, DelegateFlag); + + StyledItemDelegate(const QSharedPointer &p_interface, + DelegateFlags p_flags = DelegateFlag::None, + QObject *p_parent = nullptr); + + void paint(QPainter *p_painter, + const QStyleOptionViewItem &p_option, + const QModelIndex &p_index) const Q_DECL_OVERRIDE; + + QSize sizeHint(const QStyleOptionViewItem &p_option, const QModelIndex &p_index) const Q_DECL_OVERRIDE; + + private: + void initialize(); + + void paintWithHighlights(QPainter *p_painter, + const QStyleOptionViewItem &p_option, + const QModelIndex &p_index, + const QList &p_segments) const; + + QSharedPointer m_interface; + + DelegateFlags m_flags = DelegateFlag::None; + + QTextDocument *m_document = nullptr; + + SimpleSegmentHighlighter *m_highlighter = nullptr; + + static QBrush s_highlightForeground; + + static QBrush s_highlightBackground; + }; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::StyledItemDelegate::DelegateFlags) + +#endif // STYLEDITEMDELEGATE_H diff --git a/src/widgets/treewidget.cpp b/src/widgets/treewidget.cpp index e49051b7..1b82f253 100644 --- a/src/widgets/treewidget.cpp +++ b/src/widgets/treewidget.cpp @@ -6,6 +6,7 @@ #include #include +#include "styleditemdelegate.h" using namespace vnotex; @@ -18,6 +19,11 @@ TreeWidget::TreeWidget(TreeWidget::Flags p_flags, QWidget *p_parent) : QTreeWidget(p_parent), m_flags(p_flags) { + if (m_flags & Flag::EnhancedStyle) { + auto interface = QSharedPointer::create(this); + auto delegate = new StyledItemDelegate(interface, StyledItemDelegate::Highlights, this); + setItemDelegate(delegate); + } } void TreeWidget::mousePressEvent(QMouseEvent *p_event) @@ -92,22 +98,15 @@ void TreeWidget::keyPressEvent(QKeyEvent *p_event) return; } - switch (p_event->key()) { - case Qt::Key_Return: - Q_FALLTHROUGH(); - case Qt::Key_Enter: - { - auto item = currentItem(); - if (item && item->childCount() > 0) { - item->setExpanded(!item->isExpanded()); + // On Mac OS X, it is `Command+O` to activate an item, instead of Return. +#if defined(Q_OS_MACOS) || defined(Q_OS_MAC) + if (p_event->key() == Qt::Key_Return) { + if (auto item = currentItem()) { + emit itemActivated(item, currentColumn()); } - - break; - } - - default: - break; + return; } +#endif QTreeWidget::keyPressEvent(p_event); } diff --git a/src/widgets/treewidget.h b/src/widgets/treewidget.h index 9d41b5eb..c3f710e9 100644 --- a/src/widgets/treewidget.h +++ b/src/widgets/treewidget.h @@ -13,7 +13,8 @@ namespace vnotex enum Flag { None = 0, - ClickSpaceToClearSelection = 0x1 + ClickSpaceToClearSelection = 0x1, + EnhancedStyle = 0x2 }; Q_DECLARE_FLAGS(Flags, Flag) diff --git a/src/widgets/viewwindow.h b/src/widgets/viewwindow.h index 717b6404..9be9c64f 100644 --- a/src/widgets/viewwindow.h +++ b/src/widgets/viewwindow.h @@ -170,6 +170,13 @@ namespace vnotex virtual void handleFindAndReplaceWidgetOpened(); + protected: + bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE; + + void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE; + + void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; + protected: void setCentralWidget(QWidget *p_widget); @@ -181,12 +188,6 @@ namespace vnotex void setStatusWidget(const QSharedPointer &p_widget); - bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE; - - void wheelEvent(QWheelEvent *p_event) Q_DECL_OVERRIDE; - - void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; - // Provide some common actions of tool bar for ViewWindow. QAction *addAction(QToolBar *p_toolBar, ViewWindowToolBarHelper::Action p_action); diff --git a/src/widgets/widgets.pri b/src/widgets/widgets.pri index 73443900..2d27c7d8 100644 --- a/src/widgets/widgets.pri +++ b/src/widgets/widgets.pri @@ -50,6 +50,7 @@ SOURCES += \ $$PWD/floatingwidget.cpp \ $$PWD/fullscreentoggleaction.cpp \ $$PWD/historypanel.cpp \ + $$PWD/itemproxystyle.cpp \ $$PWD/lineedit.cpp \ $$PWD/lineeditdelegate.cpp \ $$PWD/lineeditwithsnippet.cpp \ @@ -67,7 +68,9 @@ SOURCES += \ $$PWD/quickselector.cpp \ $$PWD/searchinfoprovider.cpp \ $$PWD/searchpanel.cpp \ + $$PWD/simplesegmenthighlighter.cpp \ $$PWD/snippetpanel.cpp \ + $$PWD/styleditemdelegate.cpp \ $$PWD/systemtrayhelper.cpp \ $$PWD/textviewwindow.cpp \ $$PWD/toolbarhelper.cpp \ @@ -157,6 +160,7 @@ HEADERS += \ $$PWD/floatingwidget.h \ $$PWD/fullscreentoggleaction.h \ $$PWD/historypanel.h \ + $$PWD/itemproxystyle.h \ $$PWD/lineedit.h \ $$PWD/lineeditdelegate.h \ $$PWD/lineeditwithsnippet.h \ @@ -175,7 +179,9 @@ HEADERS += \ $$PWD/quickselector.h \ $$PWD/searchinfoprovider.h \ $$PWD/searchpanel.h \ + $$PWD/simplesegmenthighlighter.h \ $$PWD/snippetpanel.h \ + $$PWD/styleditemdelegate.h \ $$PWD/systemtrayhelper.h \ $$PWD/textviewwindow.h \ $$PWD/textviewwindowhelper.h \