mirror of
https://github.com/notepad-plus-plus/notepad-plus-plus.git
synced 2025-09-12 20:48:25 +02:00
update to Scinitlla Release 5.3.5 (https://www.scintilla.org/scintilla535.zip) Released 31 May 2023. On Win32, implement IME context sensitivity with IMR_DOCUMENTFEED. Feature #1310. On Win32 remove dependence on MSIMG32.DLL by replacing AlphaBlend by GdiAlphaBlend. Bug #1923. On Qt, stop movement of IME candidate box. On Qt, report correct caret position within paragraph for IME retrieve surrounding text. On Qt for Cocoa, fix crash in entry of multi-character strings with IME. and Lexilla Release 5.2.5 (https://www.scintilla.org/lexilla525.zip) Released 31 May 2023. Add CharacterSetArray constructor without setBase initial argument for common case where this is setNone and the initialSet argument completely defines the characters. This shortens and clarifies use of CharacterSetArray. Bash: implement highlighting inside quoted elements and here-docs. Controlled with properties lexer.bash.styling.inside.string, lexer.bash.styling.inside.backticks, lexer.bash.styling.inside.parameter, and lexer.bash.styling.inside.heredoc. Issue #154, Issue #153, Feature #1033. Bash: add property lexer.bash.command.substitution to choose how to style command substitutions. 0 → SCE_SH_BACKTICKS; 1 → surrounding "$(" and ")" as operators and contents styled as bash code; 2 → use distinct styles (base style + 64) for contents. Choice (2) is a provisional feature and details may change before it is finalized. Issue #153. Bash: fix nesting of parameters (SCE_SH_PARAM) like ${var/$sub/"${rep}}"}. Issue #154. Bash: fix single character special parameters like $? by limiting style. Issue #154. Bash: treat "$$" as special parameter and end scalars before "$". Issue #154. Bash: treat "<<" in arithmetic contexts as left bitwise shift operator instead of here-doc. Issue #137. Batch: style SCE_BAT_AFTER_LABEL used for rest of line after label which is not executed. Issue #148. F#: Lex interpolated verbatim strings as verbatim. Issue #156. VB: allow multiline strings when lexer.vb.strings.multiline set. Issue #151. Close #13729
592 lines
20 KiB
C++
592 lines
20 KiB
C++
// Scintilla source code edit control
|
|
/** @file LineMarker.cxx
|
|
** Defines the look of a line marker in the margin.
|
|
**/
|
|
// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
|
|
// The License.txt file describes the conditions under which this software may be distributed.
|
|
|
|
#include <cstring>
|
|
#include <cmath>
|
|
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <memory>
|
|
|
|
#include "ScintillaTypes.h"
|
|
|
|
#include "Debugging.h"
|
|
#include "Geometry.h"
|
|
|
|
#include "Platform.h"
|
|
|
|
#include "XPM.h"
|
|
#include "LineMarker.h"
|
|
#include "UniConversion.h"
|
|
|
|
using namespace Scintilla;
|
|
using namespace Scintilla::Internal;
|
|
|
|
LineMarker::LineMarker(const LineMarker &other) {
|
|
// Defined to avoid pxpm and image being blindly copied, not as a complete copy constructor.
|
|
markType = other.markType;
|
|
fore = other.fore;
|
|
back = other.back;
|
|
backSelected = other.backSelected;
|
|
strokeWidth = other.strokeWidth;
|
|
layer = other.layer;
|
|
alpha = other.alpha;
|
|
if (other.pxpm)
|
|
pxpm = std::make_unique<XPM>(*other.pxpm);
|
|
else
|
|
pxpm = nullptr;
|
|
if (other.image)
|
|
image = std::make_unique<RGBAImage>(*other.image);
|
|
else
|
|
image = nullptr;
|
|
customDraw = other.customDraw;
|
|
}
|
|
|
|
LineMarker &LineMarker::operator=(const LineMarker &other) {
|
|
// Defined to avoid pxpm and image being blindly copied, not as a complete assignment operator.
|
|
if (this != &other) {
|
|
markType = other.markType;
|
|
fore = other.fore;
|
|
back = other.back;
|
|
backSelected = other.backSelected;
|
|
strokeWidth = other.strokeWidth;
|
|
layer = other.layer;
|
|
alpha = other.alpha;
|
|
if (other.pxpm)
|
|
pxpm = std::make_unique<XPM>(*other.pxpm);
|
|
else
|
|
pxpm = nullptr;
|
|
if (other.image)
|
|
image = std::make_unique<RGBAImage>(*other.image);
|
|
else
|
|
image = nullptr;
|
|
customDraw = other.customDraw;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ColourRGBA LineMarker::BackWithAlpha() const noexcept {
|
|
return ColourRGBA(back, static_cast<int>(alpha));
|
|
}
|
|
|
|
void LineMarker::SetXPM(const char *textForm) {
|
|
pxpm = std::make_unique<XPM>(textForm);
|
|
markType = MarkerSymbol::Pixmap;
|
|
}
|
|
|
|
void LineMarker::SetXPM(const char *const *linesForm) {
|
|
pxpm = std::make_unique<XPM>(linesForm);
|
|
markType = MarkerSymbol::Pixmap;
|
|
}
|
|
|
|
void LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) {
|
|
image = std::make_unique<RGBAImage>(static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y), scale, pixelsRGBAImage);
|
|
markType = MarkerSymbol::RgbaImage;
|
|
}
|
|
|
|
namespace {
|
|
|
|
enum class Expansion { Minus, Plus };
|
|
enum class Shape { Square, Circle };
|
|
|
|
void DrawSymbol(Surface *surface, Shape shape, Expansion expansion, PRectangle rcSymbol, XYPOSITION widthStroke,
|
|
ColourRGBA colourFill, ColourRGBA colourFrame, ColourRGBA colourFrameRight, ColourRGBA colourExpansion) {
|
|
|
|
const FillStroke fillStroke(colourFill, colourFrame, widthStroke);
|
|
const PRectangle rcSymbolLeft = Side(rcSymbol, Edge::left, (rcSymbol.Width() + widthStroke) / 2.0f);
|
|
surface->SetClip(rcSymbolLeft);
|
|
if (shape == Shape::Square) {
|
|
// Hollowed square
|
|
surface->RectangleDraw(rcSymbol, fillStroke);
|
|
} else {
|
|
surface->Ellipse(rcSymbol, fillStroke);
|
|
}
|
|
surface->PopClip();
|
|
|
|
const FillStroke fillStrokeRight(colourFill, colourFrameRight, widthStroke);
|
|
const PRectangle rcSymbolRight = Side(rcSymbol, Edge::right, (rcSymbol.Width() - widthStroke) / 2.0f);
|
|
surface->SetClip(rcSymbolRight);
|
|
if (shape == Shape::Square) {
|
|
surface->RectangleDraw(rcSymbol, fillStrokeRight);
|
|
} else {
|
|
surface->Ellipse(rcSymbol, fillStrokeRight);
|
|
}
|
|
surface->PopClip();
|
|
|
|
const PRectangle rcPlusMinus = rcSymbol.Inset(widthStroke + 1.0f);
|
|
const XYPOSITION armWidth = (rcPlusMinus.Width() - widthStroke) / 2.0f;
|
|
const XYPOSITION top = rcPlusMinus.top + armWidth;
|
|
const PRectangle rcH = PRectangle(
|
|
rcPlusMinus.left, top,
|
|
rcPlusMinus.right, top + widthStroke);
|
|
surface->FillRectangle(rcH, colourExpansion);
|
|
if (expansion == Expansion::Plus) {
|
|
const XYPOSITION left = rcPlusMinus.left + armWidth;
|
|
const PRectangle rcV = PRectangle(
|
|
left, rcPlusMinus.top,
|
|
left + widthStroke, rcPlusMinus.bottom);
|
|
surface->FillRectangle(rcV, colourExpansion);
|
|
}
|
|
}
|
|
|
|
void DrawTail(Surface *surface, XYPOSITION leftLine, XYPOSITION rightTail, XYPOSITION centreY, XYPOSITION widthSymbolStroke, ColourRGBA fill) {
|
|
const XYPOSITION slopeLength = 2.0f + widthSymbolStroke;
|
|
const XYPOSITION strokeTop = centreY + slopeLength;
|
|
const XYPOSITION halfWidth = widthSymbolStroke / 2.0f;
|
|
const XYPOSITION strokeMiddle = strokeTop + halfWidth;
|
|
const Point lines[] = {
|
|
// Stick
|
|
Point(rightTail, strokeMiddle),
|
|
Point(leftLine + halfWidth + slopeLength, strokeMiddle),
|
|
// Slope
|
|
Point(leftLine + widthSymbolStroke / 2.0f, centreY + halfWidth),
|
|
};
|
|
surface->PolyLine(lines, std::size(lines), Stroke(fill, widthSymbolStroke));
|
|
}
|
|
|
|
}
|
|
|
|
void LineMarker::DrawFoldingMark(Surface *surface, const PRectangle &rcWhole, FoldPart part) const {
|
|
// Assume: edges of rcWhole are integers.
|
|
// Code can only really handle integer strokeWidth.
|
|
|
|
ColourRGBA colourHead = back;
|
|
ColourRGBA colourBody = back;
|
|
ColourRGBA colourTail = back;
|
|
|
|
switch (part) {
|
|
case FoldPart::head:
|
|
case FoldPart::headWithTail:
|
|
colourHead = backSelected;
|
|
colourTail = backSelected;
|
|
break;
|
|
case FoldPart::body:
|
|
colourHead = backSelected;
|
|
colourBody = backSelected;
|
|
break;
|
|
case FoldPart::tail:
|
|
colourBody = backSelected;
|
|
colourTail = backSelected;
|
|
break;
|
|
default:
|
|
// LineMarker::undefined
|
|
break;
|
|
}
|
|
|
|
const int pixelDivisions = surface->PixelDivisions();
|
|
|
|
// Folding symbols should have equal height and width to be either a circle or square.
|
|
// So find the minimum of width and height.
|
|
const XYPOSITION minDimension = std::floor(std::min(rcWhole.Width(), rcWhole.Height() - 2)) - 1;
|
|
|
|
// If strokeWidth would take up too much of area reduce to reasonable width.
|
|
const XYPOSITION widthStroke = PixelAlignFloor(std::min(strokeWidth, minDimension / 5.0f), pixelDivisions);
|
|
|
|
// To centre +/-, odd strokeWidth -> odd symbol width, even -> even
|
|
const XYPOSITION widthSymbol =
|
|
((std::lround(minDimension * pixelDivisions) % 2) == (std::lround(widthStroke * pixelDivisions) % 2)) ?
|
|
minDimension : minDimension - 1.0f / pixelDivisions;
|
|
|
|
const Point centre = PixelAlign(rcWhole.Centre(), pixelDivisions);
|
|
|
|
// Folder symbols and lines follow some rules to join up, fit the pixel grid,
|
|
// and avoid over-painting.
|
|
|
|
const XYPOSITION halfSymbol = std::round(widthSymbol / 2);
|
|
const Point topLeft(centre.x - halfSymbol, centre.y - halfSymbol);
|
|
const PRectangle rcSymbol(
|
|
topLeft.x, topLeft.y,
|
|
topLeft.x + widthSymbol, topLeft.y + widthSymbol);
|
|
const XYPOSITION leftLine = rcSymbol.Centre().x - widthStroke / 2.0f;
|
|
const XYPOSITION rightLine = leftLine + widthStroke;
|
|
|
|
// This is the vertical line through the whole area which is subdivided
|
|
// when there is a symbol on the line or the colour changes for highlighting.
|
|
const PRectangle rcVLine(leftLine, rcWhole.top, rightLine, rcWhole.bottom);
|
|
|
|
// Portions of rcVLine above and below the symbol.
|
|
const PRectangle rcAboveSymbol = Clamp(rcVLine, Edge::bottom, rcSymbol.top);
|
|
const PRectangle rcBelowSymbol = Clamp(rcVLine, Edge::top, rcSymbol.bottom);
|
|
|
|
// Projection to right.
|
|
const PRectangle rcStick(
|
|
rcVLine.right, centre.y + 1.0f - widthStroke,
|
|
rcWhole.right - 1, centre.y + 1.0f);
|
|
|
|
switch (markType) {
|
|
|
|
case MarkerSymbol::VLine:
|
|
surface->FillRectangle(rcVLine, colourBody);
|
|
break;
|
|
|
|
case MarkerSymbol::LCorner:
|
|
surface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y + 1.0f), colourTail);
|
|
surface->FillRectangle(rcStick, colourTail);
|
|
break;
|
|
|
|
case MarkerSymbol::TCorner:
|
|
surface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y + 1.0f), colourBody);
|
|
surface->FillRectangle(Clamp(rcVLine, Edge::top, centre.y + 1.0f), colourHead);
|
|
surface->FillRectangle(rcStick, colourTail);
|
|
break;
|
|
|
|
// CORNERCURVE cases divide slightly lower than CORNER to accommodate the curve
|
|
case MarkerSymbol::LCornerCurve:
|
|
surface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y), colourTail);
|
|
DrawTail(surface, leftLine, rcWhole.right - 1.0f, centre.y - widthStroke,
|
|
widthStroke, colourTail);
|
|
break;
|
|
|
|
case MarkerSymbol::TCornerCurve:
|
|
surface->FillRectangle(Clamp(rcVLine, Edge::bottom, centre.y), colourBody);
|
|
surface->FillRectangle(Clamp(rcVLine, Edge::top, centre.y), colourHead);
|
|
DrawTail(surface, leftLine, rcWhole.right - 1.0f, centre.y - widthStroke,
|
|
widthStroke, colourTail);
|
|
break;
|
|
|
|
case MarkerSymbol::BoxPlus:
|
|
DrawSymbol(surface, Shape::Square, Expansion::Plus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourHead, colourTail);
|
|
break;
|
|
|
|
case MarkerSymbol::BoxPlusConnected: {
|
|
const ColourRGBA colourBelow = (part == FoldPart::headWithTail) ? colourTail : colourBody;
|
|
surface->FillRectangle(rcBelowSymbol, colourBelow);
|
|
surface->FillRectangle(rcAboveSymbol, colourBody);
|
|
|
|
const ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;
|
|
DrawSymbol(surface, Shape::Square, Expansion::Plus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourRight, colourTail);
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::BoxMinus:
|
|
surface->FillRectangle(rcBelowSymbol, colourHead);
|
|
DrawSymbol(surface, Shape::Square, Expansion::Minus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourHead, colourTail);
|
|
break;
|
|
|
|
case MarkerSymbol::BoxMinusConnected: {
|
|
surface->FillRectangle(rcBelowSymbol, colourHead);
|
|
surface->FillRectangle(rcAboveSymbol, colourBody);
|
|
|
|
const ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;
|
|
DrawSymbol(surface, Shape::Square, Expansion::Minus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourRight, colourTail);
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::CirclePlus:
|
|
DrawSymbol(surface, Shape::Circle, Expansion::Plus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourHead, colourTail);
|
|
break;
|
|
|
|
case MarkerSymbol::CirclePlusConnected: {
|
|
const ColourRGBA colourBelow = (part == FoldPart::headWithTail) ? colourTail : colourBody;
|
|
surface->FillRectangle(rcBelowSymbol, colourBelow);
|
|
surface->FillRectangle(rcAboveSymbol, colourBody);
|
|
|
|
const ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;
|
|
DrawSymbol(surface, Shape::Circle, Expansion::Plus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourRight, colourTail);
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::CircleMinus:
|
|
surface->FillRectangle(rcBelowSymbol, colourHead);
|
|
DrawSymbol(surface, Shape::Circle, Expansion::Minus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourHead, colourTail);
|
|
break;
|
|
|
|
case MarkerSymbol::CircleMinusConnected: {
|
|
surface->FillRectangle(rcBelowSymbol, colourHead);
|
|
surface->FillRectangle(rcAboveSymbol, colourBody);
|
|
const ColourRGBA colourRight = (part == FoldPart::body) ? colourTail : colourHead;
|
|
DrawSymbol(surface, Shape::Circle, Expansion::Minus, rcSymbol, widthStroke,
|
|
fore, colourHead, colourRight, colourTail);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
void LineMarker::AlignedPolygon(Surface *surface, const Point *pts, size_t npts) const {
|
|
const XYPOSITION move = strokeWidth / 2.0;
|
|
std::vector<Point> points;
|
|
std::transform(pts, pts + npts, std::back_inserter(points), [=](Point pt) noexcept ->Point {
|
|
return Point(pt.x + move, pt.y + move);
|
|
});
|
|
surface->Polygon(points.data(), std::size(points), FillStroke(back, fore, strokeWidth));
|
|
}
|
|
|
|
void LineMarker::Draw(Surface *surface, const PRectangle &rcWhole, const Font *fontForCharacter, FoldPart part, MarginType marginStyle) const {
|
|
// This is to satisfy the changed API - eventually the stroke width will be exposed to clients
|
|
|
|
if (customDraw) {
|
|
customDraw(surface, rcWhole, fontForCharacter, static_cast<int>(part), marginStyle, this);
|
|
return;
|
|
}
|
|
|
|
if ((markType == MarkerSymbol::Pixmap) && (pxpm)) {
|
|
pxpm->Draw(surface, rcWhole);
|
|
return;
|
|
}
|
|
if ((markType == MarkerSymbol::RgbaImage) && (image)) {
|
|
// Make rectangle just large enough to fit image centred on centre of rcWhole
|
|
PRectangle rcImage;
|
|
rcImage.top = ((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2;
|
|
rcImage.bottom = rcImage.top + image->GetScaledHeight();
|
|
rcImage.left = ((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2;
|
|
rcImage.right = rcImage.left + image->GetScaledWidth();
|
|
surface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels());
|
|
return;
|
|
}
|
|
|
|
if ((markType >= MarkerSymbol::VLine) && markType <= (MarkerSymbol::CircleMinusConnected)) {
|
|
DrawFoldingMark(surface, rcWhole, part);
|
|
return;
|
|
}
|
|
|
|
// Restrict most shapes a bit
|
|
const PRectangle rc(rcWhole.left, rcWhole.top + 1, rcWhole.right, rcWhole.bottom - 1);
|
|
// Ensure does not go beyond edge
|
|
const XYPOSITION minDim = std::min(rcWhole.Width(), rcWhole.Height() - 2) - 1;
|
|
|
|
const Point centre = rcWhole.Centre();
|
|
XYPOSITION centreX = std::floor(centre.x);
|
|
const XYPOSITION centreY = std::floor(centre.y);
|
|
const XYPOSITION dimOn2 = std::floor(minDim / 2);
|
|
const XYPOSITION dimOn4 = std::floor(minDim / 4);
|
|
const XYPOSITION armSize = dimOn2 - 2;
|
|
if (marginStyle == MarginType::Number || marginStyle == MarginType::Text || marginStyle == MarginType::RText) {
|
|
// On textual margins move marker to the left to try to avoid overlapping the text
|
|
centreX = rcWhole.left + dimOn2 + 1;
|
|
}
|
|
|
|
switch (markType) {
|
|
case MarkerSymbol::RoundRect: {
|
|
PRectangle rcRounded = rc;
|
|
rcRounded.left = rc.left + 1;
|
|
rcRounded.right = rc.right - 1;
|
|
surface->RoundedRectangle(rcRounded, FillStroke(back, fore, strokeWidth));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::Circle: {
|
|
const PRectangle rcCircle = PRectangle(
|
|
centreX - dimOn2,
|
|
centreY - dimOn2,
|
|
centreX + dimOn2,
|
|
centreY + dimOn2);
|
|
surface->Ellipse(rcCircle, FillStroke(back, fore, strokeWidth));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::Arrow: {
|
|
const Point pts[] = {
|
|
Point(centreX - dimOn4, centreY - dimOn2),
|
|
Point(centreX - dimOn4, centreY + dimOn2),
|
|
Point(centreX + dimOn2 - dimOn4, centreY),
|
|
};
|
|
AlignedPolygon(surface, pts, std::size(pts));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::ArrowDown: {
|
|
const Point pts[] = {
|
|
Point(centreX - dimOn2, centreY - dimOn4),
|
|
Point(centreX + dimOn2, centreY - dimOn4),
|
|
Point(centreX, centreY + dimOn2 - dimOn4),
|
|
};
|
|
AlignedPolygon(surface, pts, std::size(pts));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::Plus: {
|
|
const Point pts[] = {
|
|
Point(centreX - armSize, centreY - 1),
|
|
Point(centreX - 1, centreY - 1),
|
|
Point(centreX - 1, centreY - armSize),
|
|
Point(centreX + 1, centreY - armSize),
|
|
Point(centreX + 1, centreY - 1),
|
|
Point(centreX + armSize, centreY - 1),
|
|
Point(centreX + armSize, centreY + 1),
|
|
Point(centreX + 1, centreY + 1),
|
|
Point(centreX + 1, centreY + armSize),
|
|
Point(centreX - 1, centreY + armSize),
|
|
Point(centreX - 1, centreY + 1),
|
|
Point(centreX - armSize, centreY + 1),
|
|
};
|
|
AlignedPolygon(surface, pts, std::size(pts));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::Minus: {
|
|
const Point pts[] = {
|
|
Point(centreX - armSize, centreY - 1),
|
|
Point(centreX + armSize, centreY - 1),
|
|
Point(centreX + armSize, centreY + 1),
|
|
Point(centreX - armSize, centreY + 1),
|
|
};
|
|
AlignedPolygon(surface, pts, std::size(pts));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::SmallRect: {
|
|
PRectangle rcSmall;
|
|
rcSmall.left = rc.left + 1;
|
|
rcSmall.top = rc.top + 2;
|
|
rcSmall.right = rc.right - 1;
|
|
rcSmall.bottom = rc.bottom - 2;
|
|
surface->RectangleDraw(rcSmall, FillStroke(back, fore, strokeWidth));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::Empty:
|
|
case MarkerSymbol::Background:
|
|
case MarkerSymbol::Underline:
|
|
case MarkerSymbol::Available:
|
|
// An invisible marker so don't draw anything
|
|
break;
|
|
|
|
case MarkerSymbol::DotDotDot: {
|
|
XYPOSITION right = static_cast<XYPOSITION>(centreX - 6);
|
|
for (int b = 0; b < 3; b++) {
|
|
const PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom - 2);
|
|
surface->FillRectangle(rcBlob, fore);
|
|
right += 5.0f;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::Arrows: {
|
|
XYPOSITION right = centreX - 4.0f + strokeWidth / 2.0f;
|
|
const XYPOSITION midY = centreY + strokeWidth / 2.0f;
|
|
const XYPOSITION armLength = std::round(dimOn2 - strokeWidth);
|
|
for (int b = 0; b < 3; b++) {
|
|
const Point pts[] = {
|
|
Point(right - armLength, midY - armLength),
|
|
Point(right, midY),
|
|
Point(right - armLength, midY + armLength)
|
|
};
|
|
surface->PolyLine(pts, std::size(pts), Stroke(fore, strokeWidth));
|
|
right += strokeWidth + 3.0f;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::ShortArrow: {
|
|
const Point pts[] = {
|
|
Point(centreX, centreY + dimOn2),
|
|
Point(centreX + dimOn2, centreY),
|
|
Point(centreX, centreY - dimOn2),
|
|
Point(centreX, centreY - dimOn4),
|
|
Point(centreX - dimOn4, centreY - dimOn4),
|
|
Point(centreX - dimOn4, centreY + dimOn4),
|
|
Point(centreX, centreY + dimOn4),
|
|
Point(centreX, centreY + dimOn2),
|
|
};
|
|
AlignedPolygon(surface, pts, std::size(pts));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::FullRect:
|
|
surface->FillRectangle(rcWhole, back);
|
|
break;
|
|
|
|
case MarkerSymbol::LeftRect: {
|
|
PRectangle rcLeft = rcWhole;
|
|
rcLeft.right = rcLeft.left + 4;
|
|
surface->FillRectangle(rcLeft, back);
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::Bar: {
|
|
PRectangle rcBar = rcWhole;
|
|
const XYPOSITION widthBar = std::floor(rcWhole.Width() / 3.0);
|
|
rcBar.left = centreX - std::floor(widthBar / 2.0);
|
|
rcBar.right = rcBar.left + widthBar;
|
|
surface->SetClip(rcWhole);
|
|
switch (part) {
|
|
case LineMarker::FoldPart::headWithTail:
|
|
surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));
|
|
break;
|
|
case LineMarker::FoldPart::head:
|
|
rcBar.bottom += 5;
|
|
surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));
|
|
break;
|
|
case LineMarker::FoldPart::tail:
|
|
rcBar.top -= 5;
|
|
surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));
|
|
break;
|
|
case LineMarker::FoldPart::body:
|
|
rcBar.top -= 5;
|
|
rcBar.bottom += 5;
|
|
surface->RectangleDraw(rcBar, FillStroke(back, fore, strokeWidth));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
surface->PopClip();
|
|
}
|
|
break;
|
|
|
|
|
|
case MarkerSymbol::Bookmark: {
|
|
const XYPOSITION halfHeight = std::floor(minDim / 3);
|
|
const Point pts[] = {
|
|
Point(rcWhole.left, centreY - halfHeight),
|
|
Point(rcWhole.right - strokeWidth - 2, centreY - halfHeight),
|
|
Point(rcWhole.right - strokeWidth - 2 - halfHeight, centreY),
|
|
Point(rcWhole.right - strokeWidth - 2, centreY + halfHeight),
|
|
Point(rcWhole.left, centreY + halfHeight),
|
|
};
|
|
AlignedPolygon(surface, pts, std::size(pts));
|
|
}
|
|
break;
|
|
|
|
case MarkerSymbol::VerticalBookmark: {
|
|
const XYPOSITION halfWidth = std::floor(minDim / 3);
|
|
const Point pts[] = {
|
|
Point(centreX - halfWidth, centreY - dimOn2),
|
|
Point(centreX + halfWidth, centreY - dimOn2),
|
|
Point(centreX + halfWidth, centreY + dimOn2),
|
|
Point(centreX, centreY + dimOn2 - halfWidth),
|
|
Point(centreX - halfWidth, centreY + dimOn2),
|
|
};
|
|
AlignedPolygon(surface, pts, std::size(pts));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (markType >= MarkerSymbol::Character) {
|
|
char character[UTF8MaxBytes + 1] {};
|
|
const int uch = static_cast<int>(markType) - static_cast<int>(MarkerSymbol::Character);
|
|
UTF8FromUTF32Character(uch, character);
|
|
const XYPOSITION width = surface->WidthTextUTF8(fontForCharacter, character);
|
|
PRectangle rcText = rc;
|
|
rcText.left += (rc.Width() - width) / 2;
|
|
rcText.right = rcText.left + width;
|
|
surface->DrawTextNoClipUTF8(rcText, fontForCharacter, rcText.bottom - 2,
|
|
character, fore, back);
|
|
} else {
|
|
// treat as MarkerSymbol::FullRect
|
|
surface->FillRectangle(rcWhole, back);
|
|
}
|
|
break;
|
|
}
|
|
}
|