Skip to content

Commit

Permalink
fix: improve highlight drawing (#534, #585)
Browse files Browse the repository at this point in the history
  • Loading branch information
variar committed Nov 20, 2024
1 parent f6a4217 commit 22fc3ca
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 88 deletions.
133 changes: 131 additions & 2 deletions src/ui/include/highlightedmatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
#ifndef KLOGG_HIGHLIGHTEDMATCH_H
#define KLOGG_HIGHLIGHTEDMATCH_H

#include "containers.h"
#include "linetypes.h"
#include <QColor>
#include <algorithm>

// Represents a match result for QuickFind or highlighter
class HighlightedMatch {
public:
public:
// Construct a match (must be initialised)
HighlightedMatch( LineColumn start_column, LineLength size, QColor foreColor, QColor backColor )
: startColumn_{ start_column }
Expand All @@ -40,6 +42,11 @@ class HighlightedMatch {
return startColumn_;
}

LineColumn endColumn() const
{
return size_ > 0_length ? startColumn_ + size_ - 1_length : startColumn_;
}

LineLength size() const
{
return size_;
Expand All @@ -55,12 +62,134 @@ class HighlightedMatch {
return backColor_;
}

private:
private:
LineColumn startColumn_;
LineLength size_;

QColor foreColor_;
QColor backColor_;
};

class HighlightedMatchRanges {

public:
HighlightedMatchRanges() = default;
explicit HighlightedMatchRanges( klogg::vector<HighlightedMatch> matches )
: matches_( std::move( matches ) )
{
}

klogg::vector<HighlightedMatch> matches() const
{
return matches_;
}

void clear()
{
matches_.clear();
}

bool empty() const
{
return matches_.empty();
}

const HighlightedMatch& front() const
{
return matches_.front();
}

const HighlightedMatch& back() const
{
return matches_.back();
}

void clamp( LineColumn firstVisibleColumn, LineColumn lastVisibleColumn )
{
for ( HighlightedMatch& m : matches_ ) {
if ( m.endColumn() < firstVisibleColumn || m.startColumn() > lastVisibleColumn ) {
m = HighlightedMatch{ m.startColumn(), 0_length, m.foreColor(), m.backColor() };
continue;
}

if ( m.startColumn() < firstVisibleColumn ) {
m = HighlightedMatch{ firstVisibleColumn,
m.endColumn() - firstVisibleColumn + 1_length, m.foreColor(),
m.backColor() };
}

if ( m.endColumn() > lastVisibleColumn ) {
m = HighlightedMatch{ m.startColumn(),
lastVisibleColumn - m.startColumn() + 1_length, m.foreColor(),
m.backColor() };
}
}
matches_.erase(
std::remove_if( matches_.begin(), matches_.end(),
[]( const HighlightedMatch& m ) { return m.size() == 0_length; } ),
matches_.end() );
}

void addMatches( const klogg::vector<HighlightedMatch>& patternMatches )
{
for ( HighlightedMatch m : patternMatches ) {
addMatch( m );
}
}

void addMatch( HighlightedMatch newMatch )
{
for ( auto matchIt = matches_.begin(); matchIt != matches_.end(); ++matchIt ) {
HighlightedMatch& m = *matchIt;
const LineColumn oldMatchL = m.startColumn();
const LineColumn oldMatchR = m.endColumn();
const LineColumn newMatchL = newMatch.startColumn();
const LineColumn newMatchR = newMatch.endColumn();

if ( oldMatchR < newMatchL ) {
continue;
}
else if ( newMatchR < oldMatchL ) {
break;
}
else if ( newMatchL <= oldMatchL && newMatchR >= oldMatchR ) {
m = HighlightedMatch{ m.startColumn(), 0_length, m.foreColor(), m.backColor() };
}
else if ( oldMatchL <= newMatchL && oldMatchR >= newMatchR ) {
m = HighlightedMatch{ m.startColumn(), newMatchL - oldMatchL, m.foreColor(),
m.backColor() };

HighlightedMatch tailMatch{ newMatchR + 1_length, oldMatchR - newMatchR,
m.foreColor(), m.backColor() };
matches_.insert( std::next( matchIt ), tailMatch );
break;
}
else if ( oldMatchL < newMatchL && oldMatchR < newMatchR ) {
m = HighlightedMatch{ m.startColumn(), newMatchL - oldMatchL, m.foreColor(),
m.backColor() };
}
else if ( oldMatchL <= newMatchR && oldMatchR > newMatchR ) {
m = HighlightedMatch{ newMatchR + 1_length, oldMatchR - newMatchR, m.foreColor(),
m.backColor() };
break;
}
}

matches_.erase(
std::remove_if( matches_.begin(), matches_.end(),
[]( const HighlightedMatch& m ) { return m.size() == 0_length; } ),
matches_.end() );

auto insertPos
= std::lower_bound( matches_.begin(), matches_.end(), newMatch,
[]( const HighlightedMatch& lhs, const HighlightedMatch& rhs ) {
return lhs.startColumn() < rhs.startColumn();
} );
matches_.insert( insertPos, newMatch );
}

private:
klogg::vector<HighlightedMatch> matches_;
};

#endif // KLOGG_HIGHLIGHTEDMATCH_H
2 changes: 1 addition & 1 deletion src/ui/include/highlighterset.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class HighlighterSet {
// Returns weither the passed line match a filter of the set,
// if so, it returns the fore/back colors the line should use.
HighlighterMatchType matchLine( const QString& line,
klogg::vector<HighlightedMatch>& matches ) const;
HighlightedMatchRanges& matches ) const;

bool isEmpty() const;

Expand Down
120 changes: 56 additions & 64 deletions src/ui/src/abstractlogview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@

#include "abstractlogview.h"
#include "containers.h"
#include "highlightedmatch.h"
#include "linetypes.h"

#include "active_screen.h"
Expand Down Expand Up @@ -275,6 +276,16 @@ class LineDrawer {
// LOG_INFO << "added Chunk of " << length;
}

LineColumn endColumn() const
{
return chunks_.empty() ? 0_lcol : chunks_.back().end();
}

bool empty() const
{
return chunks_.empty();
}

// Draw the current line of text using the given painter,
// in the passed block (in pixels)
// The line must be cut to fit on the screen.
Expand Down Expand Up @@ -2336,13 +2347,14 @@ void AbstractLogView::drawTextArea( QPaintDevice* paintDevice )
// Position in pixel of the base line of the line to print
int yPos = 0;
wrappedLinesInfo_.clear();
klogg::vector<std::pair<QColor, QColor>> highlightColors;
for ( auto currentLine = 0_lcount; currentLine < nbLines; ++currentLine ) {
const auto lineNumber = firstLine_ + currentLine;
QString logLine = logLines[ currentLine.get() ];

const int xPos = contentStartPosX + ContentMarginWidth;

klogg::vector<HighlightedMatch> highlighterMatches;
HighlightedMatchRanges highlighterMatches;

if ( selection_.isLineSelected( lineNumber ) && !selection_.isSingleLine() ) {
// Reverse the selected line
Expand All @@ -2369,16 +2381,14 @@ void AbstractLogView::drawTextArea( QPaintDevice* paintDevice )
if ( patternHighlight ) {
klogg::vector<HighlightedMatch> patternMatches;
patternHighlight->matchLine( logLine, patternMatches );
highlighterMatches.insert( highlighterMatches.end(), patternMatches.begin(),
patternMatches.end() );
highlighterMatches.addMatches( patternMatches );
}

highlighterMatches.reserve( additionalHighlighters.size() );
// highlighterMatches.reserve( additionalHighlighters.size() );
for ( const auto& highlighter : additionalHighlighters ) {
klogg::vector<HighlightedMatch> patternMatches;
highlighter.matchLine( logLine, patternMatches );
highlighterMatches.insert( highlighterMatches.end(), patternMatches.begin(),
patternMatches.end() );
highlighterMatches.addMatches( patternMatches );
}
}
}
Expand Down Expand Up @@ -2408,27 +2418,27 @@ void AbstractLogView::drawTextArea( QPaintDevice* paintDevice )
match.foreColor(), match.backColor() };
};

klogg::vector<HighlightedMatch> allHighlights;
allHighlights.reserve( highlighterMatches.size() );
std::transform( highlighterMatches.cbegin(), highlighterMatches.cend(),
std::back_inserter( allHighlights ), untabifyHighlight );
klogg::vector<HighlightedMatch> sortedHighlights = highlighterMatches.matches();
std::transform( sortedHighlights.begin(), sortedHighlights.end(), sortedHighlights.begin(),
untabifyHighlight );

HighlightedMatchRanges allHighlights{ std::move( sortedHighlights ) };

// string to print, cut to fit the length and position of the view
const QString& expandedLine = untabify(std::move(logLine));
const QString& expandedLine = untabify( std::move( logLine ) );

// Has the line got elements to be highlighted
klogg::vector<HighlightedMatch> quickFindMatches;
quickFindPattern_->matchLine( expandedLine, quickFindMatches );
allHighlights.insert( allHighlights.end(),
std::make_move_iterator( quickFindMatches.begin() ),
std::make_move_iterator( quickFindMatches.end() ) );
allHighlights.addMatches( quickFindMatches );

// Is there something selected in the line?
const auto selectionPortion = selection_.getPortionForLine( lineNumber );
if ( selectionPortion.isValid() ) {
allHighlights.emplace_back( selectionPortion.startColumn(), selectionPortion.size(),
palette.color( QPalette::HighlightedText ),
palette.color( QPalette::Highlight ) );
allHighlights.addMatch( HighlightedMatch{ selectionPortion.startColumn(),
selectionPortion.size(),
palette.color( QPalette::HighlightedText ),
palette.color( QPalette::Highlight ) } );
}

const auto wrappedLineLength
Expand All @@ -2442,64 +2452,46 @@ void AbstractLogView::drawTextArea( QPaintDevice* paintDevice )
backColor );

LineDrawer lineDrawer( backColor );
const auto firstVisibleColumn = std::clamp( useTextWrap_ ? 0_lcol : firstCol_, 0_lcol,
LineColumn{ klogg::isize( expandedLine ) } );
const auto lastVisibleColumn
= useTextWrap_ ? LineColumn{ klogg::isize( expandedLine ) } : firstCol_ + nbVisibleCols;
allHighlights.clamp( firstVisibleColumn, lastVisibleColumn );

if ( !allHighlights.empty() && !expandedLine.isEmpty() ) {
auto highlightColors = klogg::vector<std::pair<QColor, QColor>>(
static_cast<size_t>( expandedLine.size() ),
std::make_pair( foreColor, backColor ) );
// first part without highlight
if ( allHighlights.front().startColumn() > firstVisibleColumn ) {
lineDrawer.addChunk( firstVisibleColumn,
allHighlights.front().startColumn() - 1_length, foreColor,
backColor );
}

for ( const auto& match : allHighlights ) {
auto matchEnd = match.startColumn() + match.size();
for ( const auto& match : allHighlights.matches() ) {
const auto matchStart = match.startColumn();

// a part between two highlight regions
if ( !lineDrawer.empty() && matchStart - lineDrawer.endColumn() > 1_length ) {
lineDrawer.addChunk( lineDrawer.endColumn() + 1_length, matchStart - 1_length,
foreColor, backColor );
}

const auto matchEnd = match.endColumn();
auto matchLengthInString = match.size();
if ( matchEnd >= LineColumn{ expandedLine.size() } ) {
matchLengthInString
= LineLength{ klogg::isize( expandedLine ) - match.startColumn().get() };
}
if ( matchLengthInString > 0_length ) {
std::fill_n( highlightColors.begin() + match.startColumn().get(),
matchLengthInString.get(),
std::make_pair( match.foreColor(), match.backColor() ) );
lineDrawer.addChunk( match.startColumn(), matchEnd, match.foreColor(),
match.backColor() );
}
}

klogg::vector<LineColumn> columnIndexes( highlightColors.size() );
std::iota( columnIndexes.begin(), columnIndexes.end(), 0_lcol );

auto columnIndexIt = columnIndexes.begin();

const auto firstVisibleColumn
= std::clamp( useTextWrap_ ? 0_lcol : firstCol_, 0_lcol,
LineColumn{ klogg::isize( expandedLine ) } );
std::advance( columnIndexIt, firstVisibleColumn.get() );
while ( columnIndexIt != columnIndexes.end() ) {
auto highlightDiffColumnIt = std::adjacent_find(
columnIndexIt, columnIndexes.end(),
[ &highlightColors ]( LineColumn lhsColumn, LineColumn rhsColumn ) {
return highlightColors[ lhsColumn.get<size_t>() ]
!= highlightColors[ rhsColumn.get<size_t>() ];
} );

if ( highlightDiffColumnIt != columnIndexes.end() ) {
auto highlightChunkStart = *columnIndexIt;
auto highlightChunkEnd = *highlightDiffColumnIt;
lineDrawer.addChunk(
highlightChunkStart, highlightChunkEnd,
highlightColors[ highlightChunkStart.get<size_t>() ].first,
highlightColors[ highlightChunkStart.get<size_t>() ].second );

columnIndexIt = highlightDiffColumnIt + 1;
}
else {
break;
}
}
if ( columnIndexIt != columnIndexes.end() && !columnIndexes.empty() ) {
const auto lastHighlightChunkStart = *columnIndexIt;
const auto lastHighlightChunkEnd = *columnIndexes.rbegin();
if ( lastHighlightChunkEnd >= lastHighlightChunkStart ) {
lineDrawer.addChunk( lastHighlightChunkStart, lastHighlightChunkEnd,
highlightColors.back().first,
highlightColors.back().second );
}
// last part without highlight
const auto lastHighlightColumn = allHighlights.back().endColumn();
if ( lastHighlightColumn < lastVisibleColumn ) {
lineDrawer.addChunk( lastHighlightColumn + 1_length, lastVisibleColumn, foreColor,
backColor );
}
}
else {
Expand Down
Loading

0 comments on commit 22fc3ca

Please sign in to comment.