mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 05:49:53 +08:00
LocationList: fix the recently introduced regression when highlighting segments of text
This commit is contained in:
parent
5ba425ae95
commit
9cf015a676
@ -133,6 +133,8 @@ namespace vnotex
|
||||
int m_length = -1;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Segment);
|
||||
|
||||
} // ns vnotex
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "vnotex.h"
|
||||
#include "notebookmgr.h"
|
||||
#include <notebook/notebook.h>
|
||||
#include <notebookbackend/inotebookbackend.h>
|
||||
|
||||
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<HistoryItemFull>::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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace vnotex
|
||||
{
|
||||
Line() = default;
|
||||
|
||||
Line(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
|
||||
Line(int p_lineNumber, const QString &p_text, const QList<Segment> &p_segments)
|
||||
: m_lineNumber(p_lineNumber),
|
||||
m_text(p_text),
|
||||
m_segments(p_segments)
|
||||
@ -51,10 +51,10 @@ namespace vnotex
|
||||
|
||||
QString m_text;
|
||||
|
||||
QVector<Segment> m_segments;
|
||||
QList<Segment> m_segments;
|
||||
};
|
||||
|
||||
void addLine(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
|
||||
void addLine(int p_lineNumber, const QString &p_text, const QList<Segment> &p_segments)
|
||||
{
|
||||
m_lines.push_back(Line(p_lineNumber, p_text, p_segments));
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ void FileSearchEngineWorker::searchFile(const QString &p_filePath, const QString
|
||||
|
||||
const auto lineText = ins.readLine();
|
||||
bool matched = false;
|
||||
QVector<Segment> segments;
|
||||
QList<Segment> segments;
|
||||
if (!shouldStartBatchMode) {
|
||||
matched = m_token.matched(lineText, &segments);
|
||||
} else {
|
||||
@ -172,6 +172,9 @@ void FileSearchEngine::search(const QSharedPointer<SearchOption> &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) {
|
||||
|
@ -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<Segment> segments;
|
||||
QList<Segment> segments;
|
||||
if (!shouldStartBatchMode) {
|
||||
matched = m_token.matched(lineText, &segments);
|
||||
} else {
|
||||
@ -480,17 +480,21 @@ bool Searcher::firstPhaseSearch(Notebook *p_notebook, QVector<SearchSecondPhaseI
|
||||
bool Searcher::secondPhaseSearch(const QVector<SearchSecondPhaseItem> &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;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ QSharedPointer<SearchResultItem> SearchResultItem::createBufferItem(const QStrin
|
||||
const QString &p_displayPath,
|
||||
int p_lineNumber,
|
||||
const QString &p_text,
|
||||
const QVector<Segment> &p_segments)
|
||||
const QList<Segment> &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> SearchResultItem::createFileItem(const QString
|
||||
const QString &p_displayPath,
|
||||
int p_lineNumber,
|
||||
const QString &p_text,
|
||||
const QVector<Segment> &p_segments)
|
||||
const QList<Segment> &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> SearchResultItem::createNotebookItem(const QStr
|
||||
return item;
|
||||
}
|
||||
|
||||
void SearchResultItem::addLine(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments)
|
||||
void SearchResultItem::addLine(int p_lineNumber, const QString &p_text, const QList<Segment> &p_segments)
|
||||
{
|
||||
m_location.addLine(p_lineNumber, p_text, p_segments);
|
||||
}
|
||||
|
@ -17,13 +17,13 @@ namespace vnotex
|
||||
return p_dbg;
|
||||
}
|
||||
|
||||
void addLine(int p_lineNumber, const QString &p_text, const QVector<Segment> &p_segments);
|
||||
void addLine(int p_lineNumber, const QString &p_text, const QList<Segment> &p_segments);
|
||||
|
||||
static QSharedPointer<SearchResultItem> createBufferItem(const QString &p_targetPath,
|
||||
const QString &p_displayPath,
|
||||
int p_lineNumber,
|
||||
const QString &p_text,
|
||||
const QVector<Segment> &p_segments);
|
||||
const QList<Segment> &p_segments);
|
||||
|
||||
static QSharedPointer<SearchResultItem> 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<Segment> &p_segments);
|
||||
const QList<Segment> &p_segments);
|
||||
|
||||
static QSharedPointer<SearchResultItem> createFileItem(const QString &p_targetPath,
|
||||
const QString &p_displayPath);
|
||||
|
@ -31,7 +31,7 @@ void SearchToken::append(const QRegularExpression &p_regExp)
|
||||
m_regularExpressions.append(p_regExp);
|
||||
}
|
||||
|
||||
bool SearchToken::matched(const QString &p_text, QVector<Segment> *p_segments) const
|
||||
bool SearchToken::matched(const QString &p_text, QList<Segment> *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<Segment> *p_segments)
|
||||
bool SearchToken::matchedInBatchMode(const QString &p_text, QList<Segment> *p_segments)
|
||||
{
|
||||
bool isMatched = false;
|
||||
const int consSize = m_matchedConstraintsInBatchMode.size();
|
||||
|
@ -35,7 +35,7 @@ namespace vnotex
|
||||
void append(const QRegularExpression &p_regExp);
|
||||
|
||||
// Whether @p_text is matched.
|
||||
bool matched(const QString &p_text, QVector<Segment> *p_segments = nullptr) const;
|
||||
bool matched(const QString &p_text, QList<Segment> *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<Segment> *p_segments = nullptr);
|
||||
bool matchedInBatchMode(const QString &p_text, QList<Segment> *p_segments = nullptr);
|
||||
|
||||
bool readyToEndBatchMode() const;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -2,25 +2,26 @@
|
||||
|
||||
#include <QTimer>
|
||||
#include <QLabel>
|
||||
#include <QHBoxLayout>
|
||||
#include <QStackedLayout>
|
||||
#include <QDebug>
|
||||
|
||||
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<QWidget> &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());
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
class QLabel;
|
||||
class QTimer;
|
||||
class QStackedLayout;
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
@ -22,7 +23,14 @@ namespace vnotex
|
||||
|
||||
void setEditorStatusWidget(const QSharedPointer<QWidget> &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;
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <core/notebookmgr.h>
|
||||
#include <core/fileopenparameters.h>
|
||||
|
||||
|
||||
#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,
|
||||
|
257
src/widgets/itemproxystyle.cpp
Normal file
257
src/widgets/itemproxystyle.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
#include "itemproxystyle.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include <QTextOption>
|
||||
#include <QTextLayout>
|
||||
|
||||
#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<const QStyleOptionViewItem *>(p_option);
|
||||
if (!vopt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto value = vopt->index.data(HighlightsRole);
|
||||
if (!value.canConvert<QList<Segment>>()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto segments = value.value<QList<Segment>>();
|
||||
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;
|
||||
}
|
35
src/widgets/itemproxystyle.h
Normal file
35
src/widgets/itemproxystyle.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef ITEMPROXYSTYLE_H
|
||||
#define ITEMPROXYSTYLE_H
|
||||
|
||||
#include <QProxyStyle>
|
||||
|
||||
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
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -1,13 +1,47 @@
|
||||
#include "listwidget.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include <core/vnotex.h>
|
||||
#include <core/thememgr.h>
|
||||
#include <utils/widgetutils.h>
|
||||
#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<StyledItemDelegateListWidget>::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<QListWidgetItem *> 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;
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ namespace vnotex
|
||||
public:
|
||||
explicit ListWidget(QWidget *p_parent = nullptr);
|
||||
|
||||
ListWidget(bool p_enhancedStyle, QWidget *p_parent = nullptr);
|
||||
|
||||
static QVector<QListWidgetItem *> 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "treewidget.h"
|
||||
#include "widgetsfactory.h"
|
||||
#include "titlebar.h"
|
||||
#include "styleditemdelegate.h"
|
||||
|
||||
#include <core/vnotex.h>
|
||||
#include <core/thememgr.h>
|
||||
@ -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]);
|
||||
|
@ -68,10 +68,6 @@ namespace vnotex
|
||||
static QIcon s_folderIcon;
|
||||
|
||||
static QIcon s_notebookIcon;
|
||||
|
||||
static QString s_textHighlightForeground;
|
||||
|
||||
static QString s_textHighlightBackground;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -1,9 +1,7 @@
|
||||
#include "notebooknodeexplorer.h"
|
||||
|
||||
#include <QTreeWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QSplitter>
|
||||
#include <QTreeWidget>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QSet>
|
||||
|
@ -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);
|
||||
|
||||
|
35
src/widgets/simplesegmenthighlighter.cpp
Normal file
35
src/widgets/simplesegmenthighlighter.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include "simplesegmenthighlighter.h"
|
||||
|
||||
#include <QTextDocument>
|
||||
|
||||
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<Segment> &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);
|
||||
}
|
31
src/widgets/simplesegmenthighlighter.h
Normal file
31
src/widgets/simplesegmenthighlighter.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef SIMPLESEGMENTHIGHLIGHTER_H
|
||||
#define SIMPLESEGMENTHIGHLIGHTER_H
|
||||
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QList>
|
||||
|
||||
#include <core/global.h>
|
||||
|
||||
namespace vnotex
|
||||
{
|
||||
class SimpleSegmentHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SimpleSegmentHighlighter(QTextDocument *p_parent);
|
||||
|
||||
void setSegments(const QList<Segment> &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<Segment> m_segments;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SIMPLESEGMENTHIGHLIGHTER_H
|
158
src/widgets/styleditemdelegate.cpp
Normal file
158
src/widgets/styleditemdelegate.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
#include "styleditemdelegate.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QListWidgetItem>
|
||||
#include <QTextDocument>
|
||||
#include <QApplication>
|
||||
#include <QStyle>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
|
||||
#include <core/vnotex.h>
|
||||
#include <core/thememgr.h>
|
||||
#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<StyledItemDelegateInterface> &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<QBrush>()) {
|
||||
auto brush = qvariant_cast<QBrush>(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<QList<Segment>>()) {
|
||||
auto segments = value.value<QList<Segment>>();
|
||||
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<Segment> &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<QList<Segment>>()) {
|
||||
auto segments = value.value<QList<Segment>>();
|
||||
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);
|
||||
}
|
98
src/widgets/styleditemdelegate.h
Normal file
98
src/widgets/styleditemdelegate.h
Normal file
@ -0,0 +1,98 @@
|
||||
#ifndef STYLEDITEMDELEGATE_H
|
||||
#define STYLEDITEMDELEGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QSharedPointer>
|
||||
#include <QBrush>
|
||||
#include <QList>
|
||||
|
||||
#include <core/global.h>
|
||||
|
||||
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<StyledItemDelegateInterface> &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<Segment> &p_segments) const;
|
||||
|
||||
QSharedPointer<StyledItemDelegateInterface> 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
|
@ -6,6 +6,7 @@
|
||||
#include <QDropEvent>
|
||||
|
||||
#include <utils/widgetutils.h>
|
||||
#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<StyledItemDelegateTreeWidget>::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);
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ namespace vnotex
|
||||
enum Flag
|
||||
{
|
||||
None = 0,
|
||||
ClickSpaceToClearSelection = 0x1
|
||||
ClickSpaceToClearSelection = 0x1,
|
||||
EnhancedStyle = 0x2
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
|
@ -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<StatusWidget> &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);
|
||||
|
||||
|
@ -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 \
|
||||
|
Loading…
x
Reference in New Issue
Block a user