notepad-plus-plus-legacy/scintilla/src/Indicator.cxx

224 lines
7.9 KiB
C++
Raw Normal View History

// Scintilla source code edit control
/** @file Indicator.cxx
** Defines the style of indicators which are text decorations such as underlining.
**/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
2019-05-04 18:14:48 +00:00
#include <cmath>
#include <stdexcept>
#include <string_view>
#include <vector>
#include <map>
2019-05-04 18:14:48 +00:00
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
2019-05-04 18:14:48 +00:00
#include "IntegerRectangle.h"
#include "Indicator.h"
#include "XPM.h"
using namespace Scintilla;
static PRectangle PixelGridAlign(const PRectangle &rc) noexcept {
// Move left and right side to nearest pixel to avoid blurry visuals
return PRectangle(std::round(rc.left), std::floor(rc.top),
std::round(rc.right), std::floor(rc.bottom));
}
2019-05-04 18:14:48 +00:00
void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, DrawState drawState, int value) const {
StyleAndColour sacDraw = sacNormal;
if (Flags() & SC_INDICFLAG_VALUEFORE) {
2019-05-04 18:14:48 +00:00
sacDraw.fore = ColourDesired(value & SC_INDICVALUEMASK);
}
if (drawState == drawHover) {
sacDraw = sacHover;
}
2019-05-04 18:14:48 +00:00
const IntegerRectangle irc(rc);
surface->PenColour(sacDraw.fore);
2019-05-04 18:14:48 +00:00
const int ymid = (irc.bottom + irc.top) / 2;
if (sacDraw.style == INDIC_SQUIGGLE) {
2019-05-04 18:14:48 +00:00
const IntegerRectangle ircSquiggle(PixelGridAlign(rc));
int x = ircSquiggle.left;
const int xLast = ircSquiggle.right;
int y = 0;
2019-05-04 18:14:48 +00:00
surface->MoveTo(x, irc.top + y);
while (x < xLast) {
if ((x + 2) > xLast) {
2019-05-04 18:14:48 +00:00
y = 1;
x = xLast;
} else {
x += 2;
y = 2 - y;
}
2019-05-04 18:14:48 +00:00
surface->LineTo(x, irc.top + y);
}
} else if (sacDraw.style == INDIC_SQUIGGLEPIXMAP) {
2019-05-04 18:14:48 +00:00
const PRectangle rcSquiggle = PixelGridAlign(rc);
2019-05-04 18:14:48 +00:00
const int width = std::min(4000, static_cast<int>(rcSquiggle.Width()));
RGBAImage image(width, 3, 1.0, nullptr);
enum { alphaFull = 0xff, alphaSide = 0x2f, alphaSide2=0x5f };
for (int x = 0; x < width; x++) {
if (x%2) {
// Two halfway columns have a full pixel in middle flanked by light pixels
image.SetPixel(x, 0, sacDraw.fore, alphaSide);
image.SetPixel(x, 1, sacDraw.fore, alphaFull);
image.SetPixel(x, 2, sacDraw.fore, alphaSide);
} else {
// Extreme columns have a full pixel at bottom or top and a mid-tone pixel in centre
image.SetPixel(x, (x % 4) ? 0 : 2, sacDraw.fore, alphaFull);
image.SetPixel(x, 1, sacDraw.fore, alphaSide2);
}
}
surface->DrawRGBAImage(rcSquiggle, image.GetWidth(), image.GetHeight(), image.Pixels());
} else if (sacDraw.style == INDIC_SQUIGGLELOW) {
2019-05-04 18:14:48 +00:00
surface->MoveTo(irc.left, irc.top);
int x = irc.left + 3;
int y = 0;
while (x < rc.right) {
2019-05-04 18:14:48 +00:00
surface->LineTo(x - 1, irc.top + y);
y = 1 - y;
2019-05-04 18:14:48 +00:00
surface->LineTo(x, irc.top + y);
x += 3;
}
2019-05-04 18:14:48 +00:00
surface->LineTo(irc.right, irc.top + y); // Finish the line
} else if (sacDraw.style == INDIC_TT) {
2019-05-04 18:14:48 +00:00
surface->MoveTo(irc.left, ymid);
int x = irc.left + 5;
while (x < rc.right) {
surface->LineTo(x, ymid);
surface->MoveTo(x-3, ymid);
surface->LineTo(x-3, ymid+2);
x++;
surface->MoveTo(x, ymid);
x += 5;
}
2019-05-04 18:14:48 +00:00
surface->LineTo(irc.right, ymid); // Finish the line
if (x - 3 <= rc.right) {
surface->MoveTo(x-3, ymid);
surface->LineTo(x-3, ymid+2);
}
} else if (sacDraw.style == INDIC_DIAGONAL) {
2019-05-04 18:14:48 +00:00
int x = irc.left;
while (x < rc.right) {
2019-05-04 18:14:48 +00:00
surface->MoveTo(x, irc.top + 2);
int endX = x+3;
2019-05-04 18:14:48 +00:00
int endY = irc.top - 1;
if (endX > rc.right) {
2019-05-04 18:14:48 +00:00
endY += endX - irc.right;
endX = irc.right;
}
surface->LineTo(endX, endY);
x += 4;
}
} else if (sacDraw.style == INDIC_STRIKE) {
2019-05-04 18:14:48 +00:00
surface->MoveTo(irc.left, irc.top - 4);
surface->LineTo(irc.right, irc.top - 4);
} else if ((sacDraw.style == INDIC_HIDDEN) || (sacDraw.style == INDIC_TEXTFORE)) {
// Draw nothing
} else if (sacDraw.style == INDIC_BOX) {
2019-05-04 18:14:48 +00:00
surface->MoveTo(irc.left, ymid + 1);
surface->LineTo(irc.right, ymid + 1);
const int lineTop = static_cast<int>(rcLine.top) + 1;
surface->LineTo(irc.right, lineTop);
surface->LineTo(irc.left, lineTop);
surface->LineTo(irc.left, ymid + 1);
} else if (sacDraw.style == INDIC_ROUNDBOX ||
sacDraw.style == INDIC_STRAIGHTBOX ||
sacDraw.style == INDIC_FULLBOX) {
PRectangle rcBox = rcLine;
if (sacDraw.style != INDIC_FULLBOX)
rcBox.top = rcLine.top + 1;
rcBox.left = rc.left;
rcBox.right = rc.right;
2019-05-04 18:14:48 +00:00
surface->AlphaRectangle(rcBox, (sacDraw.style == INDIC_ROUNDBOX) ? 1 : 0,
sacDraw.fore, fillAlpha, sacDraw.fore, outlineAlpha, 0);
2019-05-04 18:14:48 +00:00
} else if (sacDraw.style == INDIC_GRADIENT ||
sacDraw.style == INDIC_GRADIENTCENTRE) {
PRectangle rcBox = rc;
rcBox.top = rcLine.top + 1;
rcBox.bottom = rcLine.bottom;
const Surface::GradientOptions options = Surface::GradientOptions::topToBottom;
const ColourAlpha start(sacNormal.fore, fillAlpha);
const ColourAlpha end(sacNormal.fore, 0);
std::vector<ColourStop> stops;
switch (sacDraw.style) {
case INDIC_GRADIENT:
stops.push_back(ColourStop(0.0, start));
stops.push_back(ColourStop(1.0, end));
break;
case INDIC_GRADIENTCENTRE:
stops.push_back(ColourStop(0.0, end));
stops.push_back(ColourStop(0.5, start));
stops.push_back(ColourStop(1.0, end));
break;
}
surface->GradientRectangle(rcBox, stops, options);
} else if (sacDraw.style == INDIC_DOTBOX) {
PRectangle rcBox = PixelGridAlign(rc);
rcBox.top = rcLine.top + 1;
rcBox.bottom = rcLine.bottom;
2019-05-04 18:14:48 +00:00
IntegerRectangle ircBox(rcBox);
// Cap width at 4000 to avoid large allocations when mistakes made
2019-05-04 18:14:48 +00:00
const int width = std::min(ircBox.Width(), 4000);
RGBAImage image(width, ircBox.Height(), 1.0, nullptr);
// Draw horizontal lines top and bottom
for (int x=0; x<width; x++) {
2019-05-04 18:14:48 +00:00
for (int y = 0; y<ircBox.Height(); y += ircBox.Height() - 1) {
image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
}
}
// Draw vertical lines left and right
2019-05-04 18:14:48 +00:00
for (int y = 1; y<ircBox.Height(); y++) {
for (int x=0; x<width; x += width-1) {
image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
}
}
surface->DrawRGBAImage(rcBox, image.GetWidth(), image.GetHeight(), image.Pixels());
} else if (sacDraw.style == INDIC_DASH) {
2019-05-04 18:14:48 +00:00
int x = irc.left;
while (x < rc.right) {
surface->MoveTo(x, ymid);
2019-05-04 18:14:48 +00:00
surface->LineTo(std::min(x + 4, irc.right), ymid);
x += 7;
}
} else if (sacDraw.style == INDIC_DOTS) {
2019-05-04 18:14:48 +00:00
int x = irc.left;
while (x < irc.right) {
const PRectangle rcDot = PRectangle::FromInts(x, ymid, x + 1, ymid + 1);
surface->FillRectangle(rcDot, sacDraw.fore);
x += 2;
}
} else if (sacDraw.style == INDIC_COMPOSITIONTHICK) {
2019-05-04 18:14:48 +00:00
const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom);
surface->FillRectangle(rcComposition, sacDraw.fore);
} else if (sacDraw.style == INDIC_COMPOSITIONTHIN) {
2019-05-04 18:14:48 +00:00
const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom-1);
surface->FillRectangle(rcComposition, sacDraw.fore);
2019-05-04 18:14:48 +00:00
} else if (sacDraw.style == INDIC_POINT || sacDraw.style == INDIC_POINTCHARACTER) {
if (rcCharacter.Width() >= 0.1) {
const XYPOSITION pixelHeight = std::floor(rc.Height() - 1.0f); // 1 pixel onto next line if multiphase
2019-05-04 18:14:48 +00:00
const XYPOSITION x = (sacDraw.style == INDIC_POINT) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2);
const XYPOSITION ix = std::round(x);
const XYPOSITION iy = std::floor(rc.top + 1.0f);
2019-05-04 18:14:48 +00:00
Point pts[] = {
Point(ix - pixelHeight, iy + pixelHeight), // Left
Point(ix + pixelHeight, iy + pixelHeight), // Right
Point(ix, iy) // Top
};
surface->Polygon(pts, std::size(pts), sacDraw.fore, sacDraw.fore);
}
} else { // Either INDIC_PLAIN or unknown
2019-05-04 18:14:48 +00:00
surface->MoveTo(irc.left, ymid);
surface->LineTo(irc.right, ymid);
}
}
void Indicator::SetFlags(int attributes_) {
attributes = attributes_;
}