utilize ExtraSelection for special highlighting

1. Highlight current line;
2. Highlight selected word;
3. Highlight searched word;

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2017-01-12 21:00:06 +08:00
parent 62f2d39fbc
commit 901c477705
15 changed files with 353 additions and 51 deletions

View File

@ -8,6 +8,8 @@ current_notebook=0
tab_stop_width=4
is_expand_tab=1
highlight_cursor_line=1
highlight_selected_word=1
highlight_searched_word=1
current_background_color=System
current_render_background_color=System

View File

@ -53,6 +53,8 @@ void VConfigManager::initialize()
tabStopWidth = getConfigFromSettings("global", "tab_stop_width").toInt();
isExpandTab = getConfigFromSettings("global", "is_expand_tab").toBool();
m_highlightCursorLine = getConfigFromSettings("global", "highlight_cursor_line").toBool();
m_highlightSelectedWord = getConfigFromSettings("global", "highlight_selected_word").toBool();
m_highlightSearchedWord = getConfigFromSettings("global", "highlight_searched_word").toBool();
readPredefinedColorsFromSettings();
curBackgroundColor = getConfigFromSettings("global", "current_background_color").toString();
@ -228,23 +230,21 @@ void VConfigManager::updateMarkdownEditStyle()
void VConfigManager::updatePaletteColor()
{
QString rgb;
if (curBackgroundColor == "System") {
return;
} else {
static const QColor defaultColor = baseEditPalette.color(QPalette::Base);
QColor newColor = defaultColor;
if (curBackgroundColor != "System") {
for (int i = 0; i < predefinedColors.size(); ++i) {
if (predefinedColors[i].name == curBackgroundColor) {
rgb = predefinedColors[i].rgb;
QString rgb = predefinedColors[i].rgb;
if (!rgb.isEmpty()) {
newColor = QColor(VUtils::QRgbFromString(rgb));
}
break;
}
}
}
if (rgb.isEmpty()) {
return;
}
baseEditPalette.setColor(QPalette::Base,
QColor(VUtils::QRgbFromString(rgb)));
baseEditPalette.setColor(QPalette::Base, newColor);
// Update markdown editor palette
updateMarkdownEditStyle();

View File

@ -79,6 +79,12 @@ public:
inline bool getHighlightCursorLine() const;
inline void setHighlightCursorLine(bool p_cursorLine);
inline bool getHighlightSelectedWord() const;
inline void setHighlightSelectedWord(bool p_selectedWord);
inline bool getHighlightSearchedWord() const;
inline void setHighlightSearchedWord(bool p_searchedWord);
inline const QVector<VColor> &getPredefinedColors() const;
inline const QString &getCurBackgroundColor() const;
@ -145,6 +151,12 @@ private:
// Highlight current cursor line.
bool m_highlightCursorLine;
// Highlight selected word.
bool m_highlightSelectedWord;
// Highlight searched word.
bool m_highlightSearchedWord;
// App defined color
QVector<VColor> predefinedColors;
QString curBackgroundColor;
@ -308,6 +320,36 @@ inline void VConfigManager::setHighlightCursorLine(bool p_cursorLine)
setConfigToSettings("global", "highlight_cursor_line", m_highlightCursorLine);
}
inline bool VConfigManager::getHighlightSelectedWord() const
{
return m_highlightSelectedWord;
}
inline void VConfigManager::setHighlightSelectedWord(bool p_selectedWord)
{
if (p_selectedWord == m_highlightSelectedWord) {
return;
}
m_highlightSelectedWord = p_selectedWord;
setConfigToSettings("global", "highlight_selected_word",
m_highlightSelectedWord);
}
inline bool VConfigManager::getHighlightSearchedWord() const
{
return m_highlightSearchedWord;
}
inline void VConfigManager::setHighlightSearchedWord(bool p_searchedWord)
{
if (p_searchedWord == m_highlightSearchedWord) {
return;
}
m_highlightSearchedWord = p_searchedWord;
setConfigToSettings("global", "highlight_searched_word",
m_highlightSearchedWord);
}
inline const QVector<VColor>& VConfigManager::getPredefinedColors() const
{
return predefinedColors;

View File

@ -10,13 +10,19 @@
#include "dialog/vfindreplacedialog.h"
extern VConfigManager vconfig;
extern VNote *g_vnote;
VEdit::VEdit(VFile *p_file, QWidget *p_parent)
: QTextEdit(p_parent), m_file(p_file), m_editOps(NULL)
{
const int labelTimerInterval = 500;
const int selectedWordTimerInterval = 500;
const int labelSize = 64;
m_cursorLineColor = QColor(g_vnote->getColorFromPalette("Indigo1"));
m_selectedWordColor = QColor("Yellow");
m_searchedWordColor = QColor(g_vnote->getColorFromPalette("Green4"));
QPixmap wrapPixmap(":/resources/icons/search_wrap.svg");
m_wrapLabel = new QLabel(this);
m_wrapLabel->setPixmap(wrapPixmap.scaled(labelSize, labelSize));
@ -26,8 +32,23 @@ VEdit::VEdit(VFile *p_file, QWidget *p_parent)
m_labelTimer->setInterval(labelTimerInterval);
connect(m_labelTimer, &QTimer::timeout,
this, &VEdit::labelTimerTimeout);
m_selectedWordTimer = new QTimer(this);
m_selectedWordTimer->setSingleShot(true);
m_selectedWordTimer->setInterval(selectedWordTimerInterval);
connect(m_selectedWordTimer, &QTimer::timeout,
this, &VEdit::highlightSelectedWord);
connect(document(), &QTextDocument::modificationChanged,
(VFile *)m_file, &VFile::setModified);
m_extraSelections.resize((int)SelectionId::MaxSelection);
updateFontAndPalette();
connect(this, &VEdit::cursorPositionChanged,
this, &VEdit::highlightCurrentLine);
connect(this, &VEdit::selectionChanged,
this, &VEdit::triggerHighlightSelectedWord);
}
VEdit::~VEdit()
@ -40,6 +61,8 @@ VEdit::~VEdit()
void VEdit::beginEdit()
{
updateFontAndPalette();
setReadOnly(false);
setModified(false);
}
@ -130,6 +153,8 @@ bool VEdit::peekText(const QString &p_text, uint p_options)
return found;
}
// Use QTextEdit::find() instead of QTextDocument::find() because the later has
// bugs in searching backward.
bool VEdit::findTextHelper(const QString &p_text, uint p_options,
bool p_forward, bool &p_wrapped)
{
@ -186,6 +211,49 @@ bool VEdit::findTextHelper(const QString &p_text, uint p_options,
return found;
}
QList<QTextCursor> VEdit::findTextAll(const QString &p_text, uint p_options)
{
QList<QTextCursor> results;
if (p_text.isEmpty()) {
return results;
}
// Options
QTextDocument::FindFlags findFlags;
bool caseSensitive = false;
if (p_options & FindOption::CaseSensitive) {
findFlags |= QTextDocument::FindCaseSensitively;
caseSensitive = true;
}
if (p_options & FindOption::WholeWordOnly) {
findFlags |= QTextDocument::FindWholeWords;
}
// Use regular expression
bool useRegExp = false;
QRegExp exp;
if (p_options & FindOption::RegularExpression) {
useRegExp = true;
exp = QRegExp(p_text,
caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
}
int startPos = 0;
QTextCursor cursor;
QTextDocument *doc = document();
while (true) {
if (useRegExp) {
cursor = doc->find(exp, startPos, findFlags);
} else {
cursor = doc->find(p_text, startPos, findFlags);
}
if (cursor.isNull()) {
break;
} else {
results.append(cursor);
startPos = cursor.selectionEnd();
}
}
return results;
}
bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward)
{
bool found = false;
@ -196,8 +264,14 @@ bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward)
} else {
bool wrapped = false;
found = findTextHelper(p_text, p_options, p_forward, wrapped);
if (found && wrapped) {
showWrapLabel();
if (found) {
if (wrapped) {
showWrapLabel();
}
highlightSearchedWord(p_text, p_options);
} else {
// Simply clear previous highlight.
highlightSearchedWord("", p_options);
}
}
qDebug() << "findText" << p_text << p_options << p_forward
@ -294,3 +368,137 @@ void VEdit::labelTimerTimeout()
{
m_wrapLabel->hide();
}
void VEdit::updateFontAndPalette()
{
setFont(vconfig.getBaseEditFont());
setPalette(vconfig.getBaseEditPalette());
}
void VEdit::highlightExtraSelections()
{
int nrExtra = m_extraSelections.size();
Q_ASSERT(nrExtra == (int)SelectionId::MaxSelection);
QList<QTextEdit::ExtraSelection> extraSelects;
for (int i = 0; i < nrExtra; ++i) {
extraSelects.append(m_extraSelections[i]);
}
setExtraSelections(extraSelects);
}
void VEdit::highlightCurrentLine()
{
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::CurrentLine];
if (vconfig.getHighlightCursorLine() && !isReadOnly()) {
// Need to highlight current line.
selects.clear();
QTextEdit::ExtraSelection select;
select.format.setBackground(m_cursorLineColor);
select.format.setProperty(QTextFormat::FullWidthSelection, true);
select.cursor = textCursor();
select.cursor.clearSelection();
selects.append(select);
} else {
// Need to clear current line highlight.
if (selects.isEmpty()) {
return;
}
selects.clear();
}
highlightExtraSelections();
}
void VEdit::setReadOnly(bool p_ro)
{
QTextEdit::setReadOnly(p_ro);
highlightCurrentLine();
}
void VEdit::highlightSelectedWord()
{
QString text;
if (vconfig.getHighlightSelectedWord()) {
text = textCursor().selectedText().trimmed();
if (wordInSearchedSelection(text)) {
qDebug() << "select searched word, just skip";
text = "";
}
}
QTextCharFormat format;
format.setBackground(m_selectedWordColor);
highlightTextAll(text, FindOption::CaseSensitive, SelectionId::SelectedWord,
format);
}
bool VEdit::wordInSearchedSelection(const QString &p_text)
{
QString text = p_text.trimmed();
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
for (int i = 0; i < selects.size(); ++i) {
QString searchedWord = selects[i].cursor.selectedText();
if (text == searchedWord.trimmed()) {
return true;
}
}
return false;
}
void VEdit::triggerHighlightSelectedWord()
{
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SelectedWord];
if (!vconfig.getHighlightSelectedWord() && selects.isEmpty()) {
return;
}
m_selectedWordTimer->stop();
QString text = textCursor().selectedText().trimmed();
if (text.isEmpty()) {
// Clear previous selection right now.
highlightSelectedWord();
} else {
m_selectedWordTimer->start();
}
}
void VEdit::highlightTextAll(const QString &p_text, uint p_options,
SelectionId p_id, QTextCharFormat p_format)
{
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)p_id];
if (!p_text.isEmpty()) {
selects.clear();
QList<QTextCursor> occurs = findTextAll(p_text, p_options);
for (int i = 0; i < occurs.size(); ++i) {
QTextEdit::ExtraSelection select;
select.format = p_format;
select.cursor = occurs[i];
selects.append(select);
}
qDebug() << "highlight" << selects.size() << "occurences of" << p_text;
} else {
if (selects.isEmpty()) {
return;
}
selects.clear();
}
highlightExtraSelections();
}
void VEdit::highlightSearchedWord(const QString &p_text, uint p_options)
{
QTextCharFormat format;
format.setBackground(m_searchedWordColor);
QString text;
if (vconfig.getHighlightSearchedWord()) {
text = p_text;
}
highlightTextAll(text, p_options, SelectionId::SearchedKeyword, format);
}
void VEdit::clearSearchedWordHighlight()
{
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
selects.clear();
highlightExtraSelections();
}

View File

@ -4,6 +4,9 @@
#include <QTextEdit>
#include <QString>
#include <QPointer>
#include <QVector>
#include <QList>
#include <QColor>
#include "vconstants.h"
#include "vtoc.h"
#include "vfile.h"
@ -12,6 +15,13 @@ class VEditOperations;
class QLabel;
class QTimer;
enum class SelectionId {
CurrentLine = 0,
SelectedWord,
SearchedKeyword,
MaxSelection
};
class VEdit : public QTextEdit
{
Q_OBJECT
@ -36,19 +46,42 @@ public:
const QString &p_replaceText, bool p_findNext);
void replaceTextAll(const QString &p_text, uint p_options,
const QString &p_replaceText);
void setReadOnly(bool p_ro);
void clearSearchedWordHighlight();
private slots:
void labelTimerTimeout();
void triggerHighlightSelectedWord();
void highlightSelectedWord();
protected slots:
virtual void highlightCurrentLine();
protected:
QPointer<VFile> m_file;
VEditOperations *m_editOps;
QColor m_cursorLineColor;
virtual void updateFontAndPalette();
private:
QLabel *m_wrapLabel;
QTimer *m_labelTimer;
// highlightExtraSelections() will highlight these selections.
// Selections are indexed by SelectionId.
QVector<QList<QTextEdit::ExtraSelection> > m_extraSelections;
QTimer *m_selectedWordTimer;
QColor m_selectedWordColor;
QColor m_searchedWordColor;
void showWrapLabel();
void highlightExtraSelections();
// Find all the occurences of @p_text.
QList<QTextCursor> findTextAll(const QString &p_text, uint p_options);
void highlightTextAll(const QString &p_text, uint p_options,
SelectionId p_id, QTextCharFormat p_format);
void highlightSearchedWord(const QString &p_text, uint p_options);
bool wordInSearchedSelection(const QString &p_text);
};

View File

@ -509,10 +509,10 @@ void VEditArea::handleFindDialogClosed()
getWindow(curWindowIndex)->focusWindow();
}
// Clear all the selection in Web view.
// Clear all the search highlight.
int nrWin = splitter->count();
for (int i = 0; i < nrWin; ++i) {
getWindow(i)->clearFindSelectionInWebView();
getWindow(i)->clearSearchedWordHighlight();
}
}

View File

@ -108,7 +108,7 @@ void VEditTab::showFileReadMode()
previewByConverter();
}
setCurrentWidget(webPreviewer);
clearFindSelectionInWebView();
clearSearchedWordHighlight();
scrollPreviewToHeader(outlineIndex);
break;
default:
@ -520,11 +520,12 @@ QString VEditTab::getSelectedText() const
}
}
void VEditTab::clearFindSelectionInWebView()
void VEditTab::clearSearchedWordHighlight()
{
if (webPreviewer) {
webPreviewer->findText("");
}
m_textEditor->clearSearchedWordHighlight();
}
bool VEditTab::checkToc()

View File

@ -47,7 +47,7 @@ public:
void replaceTextAll(const QString &p_text, uint p_options,
const QString &p_replaceText);
QString getSelectedText() const;
void clearFindSelectionInWebView();
void clearSearchedWordHighlight();
signals:
void getFocused();

View File

@ -718,10 +718,10 @@ void VEditWindow::setCurrentWindow(bool p_current)
}
}
void VEditWindow::clearFindSelectionInWebView()
void VEditWindow::clearSearchedWordHighlight()
{
int nrTab = count();
for (int i = 0; i < nrTab; ++i) {
getTab(i)->clearFindSelectionInWebView();
getTab(i)->clearSearchedWordHighlight();
}
}

View File

@ -45,7 +45,7 @@ public:
bool addEditTab(QWidget *p_widget);
// Set whether it is the current window.
void setCurrentWindow(bool p_current);
void clearFindSelectionInWebView();
void clearSearchedWordHighlight();
protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

View File

@ -371,6 +371,12 @@ void VMainWindow::initEditMenu()
connect(m_replaceAllAct, SIGNAL(triggered(bool)),
m_findReplaceDialog, SLOT(replaceAll()));
QAction *searchedWordAct = new QAction(tr("Highlight Searched Pattern"), this);
searchedWordAct->setStatusTip(tr("Highlight all occurences of searched pattern"));
searchedWordAct->setCheckable(true);
connect(searchedWordAct, &QAction::triggered,
this, &VMainWindow::changeHighlightSearchedWord);
// Expand Tab into spaces.
QAction *expandTabAct = new QAction(tr("&Expand Tab"), this);
expandTabAct->setStatusTip(tr("Expand entered tab to spaces"));
@ -402,6 +408,12 @@ void VMainWindow::initEditMenu()
connect(cursorLineAct, &QAction::triggered,
this, &VMainWindow::changeHighlightCursorLine);
// Highlight selected word.
QAction *selectedWordAct = new QAction(tr("Highlight Selected Word"), this);
selectedWordAct->setStatusTip(tr("Highlight all occurences of selected word"));
selectedWordAct->setCheckable(true);
connect(selectedWordAct, &QAction::triggered,
this, &VMainWindow::changeHighlightSelectedWord);
editMenu->addAction(m_insertImageAct);
editMenu->addSeparator();
@ -414,6 +426,14 @@ void VMainWindow::initEditMenu()
findReplaceMenu->addAction(m_replaceAct);
findReplaceMenu->addAction(m_replaceFindAct);
findReplaceMenu->addAction(m_replaceAllAct);
findReplaceMenu->addSeparator();
findReplaceMenu->addAction(searchedWordAct);
if (vconfig.getHighlightSearchedWord()) {
searchedWordAct->setChecked(true);
} else {
searchedWordAct->setChecked(false);
}
editMenu->addSeparator();
m_findReplaceAct->setEnabled(false);
m_findNextAct->setEnabled(false);
@ -447,12 +467,20 @@ void VMainWindow::initEditMenu()
qWarning() << "unsupported tab stop width" << tabStopWidth << "in config";
}
initEditorBackgroundMenu(editMenu);
editMenu->addSeparator();
editMenu->addAction(cursorLineAct);
if (vconfig.getHighlightCursorLine()) {
cursorLineAct->setChecked(true);
} else {
cursorLineAct->setChecked(false);
}
editMenu->addAction(selectedWordAct);
if (vconfig.getHighlightSelectedWord()) {
selectedWordAct->setChecked(true);
} else {
selectedWordAct->setChecked(false);
}
}
void VMainWindow::initDockWindows()
@ -479,7 +507,7 @@ void VMainWindow::initAvatar()
m_avatar = new VAvatar(this);
m_avatar->setAvatarPixmap(":/resources/icons/vnote.svg");
m_avatar->setColor(vnote->getColorFromPalette("base-color"), vnote->getColorFromPalette("Indigo4"),
vnote->getColorFromPalette("teal4"));
vnote->getColorFromPalette("Teal4"));
m_avatar->hide();
}
@ -539,6 +567,16 @@ void VMainWindow::changeHighlightCursorLine(bool p_checked)
vconfig.setHighlightCursorLine(p_checked);
}
void VMainWindow::changeHighlightSelectedWord(bool p_checked)
{
vconfig.setHighlightSelectedWord(p_checked);
}
void VMainWindow::changeHighlightSearchedWord(bool p_checked)
{
vconfig.setHighlightSearchedWord(p_checked);
}
void VMainWindow::setTabStopWidth(QAction *action)
{
if (!action) {

View File

@ -47,6 +47,8 @@ private slots:
void setEditorBackgroundColor(QAction *action);
void setRenderBackgroundColor(QAction *action);
void changeHighlightCursorLine(bool p_checked);
void changeHighlightSelectedWord(bool p_checked);
void changeHighlightSearchedWord(bool p_checked);
void handleCurTabStatusChanged(const VFile *p_file, const VEditTab *p_editTab, bool p_editMode);
void onePanelView();
void twoPanelView();

View File

@ -35,10 +35,6 @@ VMdEdit::VMdEdit(VFile *p_file, QWidget *p_parent)
connect(this, &VMdEdit::cursorPositionChanged,
this, &VMdEdit::updateCurHeader);
if (vconfig.getHighlightCursorLine()) {
connect(this, &VMdEdit::cursorPositionChanged,
this, &VMdEdit::highlightCurrentLine);
}
connect(this, &VMdEdit::selectionChanged,
this, &VMdEdit::handleSelectionChanged);
@ -60,8 +56,6 @@ void VMdEdit::beginEdit()
m_editOps->updateTabSettings();
updateFontAndPalette();
setFont(vconfig.getMdEditFont());
Q_ASSERT(m_file->getContent() == toPlainTextWithoutImg());
initInitImages();
@ -491,22 +485,6 @@ int VMdEdit::removeObjectReplacementLine(QString &p_text, int p_index) const
return prevLineIdx - 1;
}
void VMdEdit::highlightCurrentLine()
{
QList<QTextEdit::ExtraSelection> extraSelects;
if (!isReadOnly()) {
QTextEdit::ExtraSelection select;
select.format.setBackground(m_cursorLineColor);
select.format.setProperty(QTextFormat::FullWidthSelection, true);
select.cursor = textCursor();
select.cursor.clearSelection();
extraSelects.append(select);
}
setExtraSelections(extraSelects);
}
void VMdEdit::handleEditStateChanged(KeyState p_state)
{
qDebug() << "edit state" << (int)p_state;

View File

@ -39,7 +39,6 @@ private slots:
void updateCurHeader();
// Update block list containing image links.
void updateImageBlocks(QSet<int> p_imageBlocks);
void highlightCurrentLine();
void handleEditStateChanged(KeyState p_state);
void handleSelectionChanged();
void handleClipboardChanged(QClipboard::Mode p_mode);
@ -48,9 +47,9 @@ protected:
void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
bool canInsertFromMimeData(const QMimeData *source) const Q_DECL_OVERRIDE;
void insertFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
void updateFontAndPalette() Q_DECL_OVERRIDE;
private:
void updateFontAndPalette();
void initInitImages();
void clearUnusedImages();
// p_text[p_index] is QChar::ObjectReplacementCharacter. Remove the line containing it.
@ -80,7 +79,6 @@ private:
QVector<QString> m_insertedImages;
QVector<QString> m_initImages;
QVector<VHeader> m_headers;
QColor m_cursorLineColor;
bool m_previewImage;
static const QString c_cursorLineColor;

View File

@ -34,11 +34,11 @@ void VNote::initPalette(QPalette palette)
m_palette.append(QPair<QString, QString>("base-color", "#BDBDBD"));
// Material Design Colors
m_palette.append(QPair<QString, QString>("teal0", "#E0F2F1"));
m_palette.append(QPair<QString, QString>("teal1", "#B2DFDB"));
m_palette.append(QPair<QString, QString>("teal2", "#80CBC4"));
m_palette.append(QPair<QString, QString>("teal3", "#4DB6AC"));
m_palette.append(QPair<QString, QString>("teal4", "#26A69A"));
m_palette.append(QPair<QString, QString>("Teal0", "#E0F2F1"));
m_palette.append(QPair<QString, QString>("Teal1", "#B2DFDB"));
m_palette.append(QPair<QString, QString>("Teal2", "#80CBC4"));
m_palette.append(QPair<QString, QString>("Teal3", "#4DB6AC"));
m_palette.append(QPair<QString, QString>("Teal4", "#26A69A"));
m_palette.append(QPair<QString, QString>("Indigo0", "#E8EAF6"));
m_palette.append(QPair<QString, QString>("Indigo1", "#C5CAE9"));