VTE cleanup related to GTK 3.24 requirement
[geany-mirror.git] / scintilla / src / Indicator.cxx
blob5c15caeb6dbfef7223578823286dce4a2ecfa00f
1 // Scintilla source code edit control
2 /** @file Indicator.cxx
3 ** Defines the style of indicators which are text decorations such as underlining.
4 **/
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <cmath>
10 #include <stdexcept>
11 #include <string_view>
12 #include <vector>
13 #include <map>
14 #include <optional>
15 #include <algorithm>
16 #include <memory>
18 #include "ScintillaTypes.h"
20 #include "Debugging.h"
21 #include "Geometry.h"
22 #include "Platform.h"
24 #include "Indicator.h"
25 #include "XPM.h"
27 using namespace Scintilla;
28 using namespace Scintilla::Internal;
30 void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, State state, int value) const {
31 StyleAndColour sacDraw = sacNormal;
32 if (FlagSet(Flags(), IndicFlag::ValueFore)) {
33 sacDraw.fore = ColourRGBA::FromRGB(value & static_cast<int>(IndicValue::Mask));
35 if (state == State::hover) {
36 sacDraw = sacHover;
39 const int pixelDivisions = surface->PixelDivisions();
41 const XYPOSITION halfWidth = strokeWidth / 2.0f;
43 const PRectangle rcAligned(PixelAlignOutside(rc, pixelDivisions));
44 PRectangle rcFullHeightAligned = PixelAlignOutside(rcLine, pixelDivisions);
45 rcFullHeightAligned.left = rcAligned.left;
46 rcFullHeightAligned.right = rcAligned.right;
48 const XYPOSITION ymid = PixelAlign(rc.Centre().y, pixelDivisions);
50 // This is a reasonable clip for indicators beneath text like underlines
51 PRectangle rcClip = rcAligned;
52 rcClip.bottom = rcFullHeightAligned.bottom;
54 switch (sacDraw.style) {
55 case IndicatorStyle::Squiggle: {
56 surface->SetClip(rcClip);
57 XYPOSITION x = rcAligned.left + halfWidth;
58 const XYPOSITION top = rcAligned.top + halfWidth;
59 const XYPOSITION xLast = rcAligned.right + halfWidth;
60 XYPOSITION y = 0;
61 std::vector<Point> pts;
62 const XYPOSITION pitch = 1 + strokeWidth;
63 pts.emplace_back(x, top + y);
64 while (x < xLast) {
65 x += pitch;
66 y = pitch - y;
67 pts.emplace_back(x, top + y);
69 surface->PolyLine(pts.data(), std::size(pts), Stroke(sacDraw.fore, strokeWidth));
70 surface->PopClip();
72 break;
74 case IndicatorStyle::SquigglePixmap: {
75 const PRectangle rcSquiggle = PixelAlign(rc, 1);
77 const int width = std::min(4000, static_cast<int>(rcSquiggle.Width()));
78 RGBAImage image(width, 3, 1.0, nullptr);
79 constexpr unsigned int alphaFull = 0xff;
80 constexpr unsigned int alphaSide = 0x2f;
81 constexpr unsigned int alphaSide2 = 0x5f;
82 for (int x = 0; x < width; x++) {
83 if (x%2) {
84 // Two halfway columns have a full pixel in middle flanked by light pixels
85 image.SetPixel(x, 0, ColourRGBA(sacDraw.fore, alphaSide));
86 image.SetPixel(x, 1, ColourRGBA(sacDraw.fore, alphaFull));
87 image.SetPixel(x, 2, ColourRGBA(sacDraw.fore, alphaSide));
88 } else {
89 // Extreme columns have a full pixel at bottom or top and a mid-tone pixel in centre
90 image.SetPixel(x, (x % 4) ? 0 : 2, ColourRGBA(sacDraw.fore, alphaFull));
91 image.SetPixel(x, 1, ColourRGBA(sacDraw.fore, alphaSide2));
94 surface->DrawRGBAImage(rcSquiggle, image.GetWidth(), image.GetHeight(), image.Pixels());
96 break;
98 case IndicatorStyle::SquiggleLow: {
99 std::vector<Point> pts;
100 const XYPOSITION top = rcAligned.top + halfWidth;
101 int y = 0;
102 XYPOSITION x = std::round(rcAligned.left) + halfWidth;
103 pts.emplace_back(x, top + y);
104 const XYPOSITION pitch = 2 + strokeWidth;
105 x += pitch;
106 while (x < rcAligned.right) {
107 pts.emplace_back(x - 1, top + y);
108 y = 1 - y;
109 pts.emplace_back(x, top + y);
110 x += pitch;
112 pts.emplace_back(rcAligned.right, top + y);
113 surface->PolyLine(pts.data(), std::size(pts), Stroke(sacDraw.fore, strokeWidth));
115 break;
117 case IndicatorStyle::TT: {
118 surface->SetClip(rcClip);
119 const XYPOSITION yLine = ymid;
120 XYPOSITION x = rcAligned.left + 5.0f;
121 const XYPOSITION pitch = 4 + strokeWidth;
122 while (x < rc.right + pitch) {
123 const PRectangle line(x-pitch, yLine, x, yLine + strokeWidth);
124 surface->FillRectangle(line, sacDraw.fore);
125 const PRectangle tail(x - 2 - strokeWidth, yLine + strokeWidth, x - 2, yLine + strokeWidth * 2);
126 surface->FillRectangle(tail, sacDraw.fore);
127 x++;
128 x += pitch;
130 surface->PopClip();
132 break;
134 case IndicatorStyle::Diagonal: {
135 surface->SetClip(rcClip);
136 XYPOSITION x = rcAligned.left + halfWidth;
137 const XYPOSITION top = rcAligned.top + halfWidth;
138 const XYPOSITION pitch = 3 + strokeWidth;
139 while (x < rc.right) {
140 const XYPOSITION endX = x+3;
141 const XYPOSITION endY = top - 1;
142 surface->LineDraw(Point(x, top + 2), Point(endX, endY), Stroke(sacDraw.fore, strokeWidth));
143 x += pitch;
145 surface->PopClip();
147 break;
149 case IndicatorStyle::Strike: {
150 const XYPOSITION yStrike = std::round(rcLine.Centre().y);
151 const PRectangle rcStrike(
152 rcAligned.left, yStrike, rcAligned.right, yStrike + strokeWidth);
153 surface->FillRectangle(rcStrike, sacDraw.fore);
155 break;
157 case IndicatorStyle::Hidden:
158 case IndicatorStyle::TextFore:
159 // Draw nothing
160 break;
162 case IndicatorStyle::Box: {
163 PRectangle rcBox = rcFullHeightAligned;
164 rcBox.top = rcBox.top + 1.0f;
165 rcBox.bottom = ymid + 1.0f;
166 surface->RectangleFrame(rcBox, Stroke(ColourRGBA(sacDraw.fore, outlineAlpha), strokeWidth));
168 break;
170 case IndicatorStyle::RoundBox:
171 case IndicatorStyle::StraightBox:
172 case IndicatorStyle::FullBox: {
173 PRectangle rcBox = rcFullHeightAligned;
174 if (sacDraw.style != IndicatorStyle::FullBox)
175 rcBox.top = rcBox.top + 1;
176 surface->AlphaRectangle(rcBox, (sacDraw.style == IndicatorStyle::RoundBox) ? 1.0f : 0.0f,
177 FillStroke(ColourRGBA(sacDraw.fore, fillAlpha), ColourRGBA(sacDraw.fore, outlineAlpha), strokeWidth));
179 break;
181 case IndicatorStyle::Gradient:
182 case IndicatorStyle::GradientCentre: {
183 PRectangle rcBox = rcFullHeightAligned;
184 rcBox.top = rcBox.top + 1;
185 const Surface::GradientOptions options = Surface::GradientOptions::topToBottom;
186 const ColourRGBA start(sacDraw.fore, fillAlpha);
187 const ColourRGBA end(sacDraw.fore, 0);
188 std::vector<ColourStop> stops;
189 switch (sacDraw.style) {
190 case IndicatorStyle::Gradient:
191 stops.push_back(ColourStop(0.0, start));
192 stops.push_back(ColourStop(1.0, end));
193 break;
194 case IndicatorStyle::GradientCentre:
195 stops.push_back(ColourStop(0.0, end));
196 stops.push_back(ColourStop(0.5, start));
197 stops.push_back(ColourStop(1.0, end));
198 break;
199 default:
200 break;
202 surface->GradientRectangle(rcBox, stops, options);
204 break;
206 case IndicatorStyle::DotBox: {
207 PRectangle rcBox = rcFullHeightAligned;
208 rcBox.top = rcBox.top + 1;
209 // Cap width at 4000 to avoid large allocations when mistakes made
210 const int width = std::min(static_cast<int>(rcBox.Width()), 4000);
211 const int height = static_cast<int>(rcBox.Height());
212 RGBAImage image(width, height, 1.0, nullptr);
213 // Draw horizontal lines top and bottom
214 for (int x=0; x<width; x++) {
215 for (int y = 0; y< height; y += height - 1) {
216 image.SetPixel(x, y, ColourRGBA(sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha));
219 // Draw vertical lines left and right
220 for (int y = 1; y<height; y++) {
221 for (int x=0; x<width; x += width-1) {
222 image.SetPixel(x, y, ColourRGBA(sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha));
225 surface->DrawRGBAImage(rcBox, image.GetWidth(), image.GetHeight(), image.Pixels());
227 break;
229 case IndicatorStyle::Dash: {
230 XYPOSITION x = std::floor(rc.left);
231 const XYPOSITION widthDash = 3 + std::round(strokeWidth);
232 while (x < rc.right) {
233 const PRectangle rcDash = PRectangle(x, ymid,
234 x + widthDash, ymid + std::round(strokeWidth));
235 surface->FillRectangle(rcDash, sacDraw.fore);
236 x += 3 + widthDash;
239 break;
241 case IndicatorStyle::Dots: {
242 const XYPOSITION widthDot = std::round(strokeWidth);
243 XYPOSITION x = std::floor(rc.left);
244 while (x < rc.right) {
245 const PRectangle rcDot = PRectangle(x, ymid,
246 x + widthDot, ymid + widthDot);
247 surface->FillRectangle(rcDot, sacDraw.fore);
248 x += widthDot * 2;
251 break;
253 case IndicatorStyle::CompositionThick: {
254 const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom);
255 surface->FillRectangle(rcComposition, ColourRGBA(sacDraw.fore, outlineAlpha));
257 break;
259 case IndicatorStyle::CompositionThin: {
260 const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom-1);
261 surface->FillRectangle(rcComposition, sacDraw.fore);
263 break;
265 case IndicatorStyle::Point:
266 case IndicatorStyle::PointCharacter:
267 if (rcCharacter.Width() >= 0.1) {
268 const XYPOSITION pixelHeight = std::floor(rc.Height()); // 1 pixel onto next line if multiphase
269 const XYPOSITION x = (sacDraw.style == IndicatorStyle::Point) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2);
270 // 0.5f is to hit midpoint of pixels:
271 const XYPOSITION ix = std::round(x) + 0.5f;
272 const XYPOSITION iy = std::ceil(rc.bottom) + 0.5f;
273 const Point pts[] = {
274 Point(ix - pixelHeight, iy), // Left
275 Point(ix + pixelHeight, iy), // Right
276 Point(ix, iy - pixelHeight) // Top
278 surface->Polygon(pts, std::size(pts), FillStroke(sacDraw.fore));
280 break;
282 case IndicatorStyle::PointTop:
283 if (rcCharacter.Width() >= 0.1) {
284 const XYPOSITION pixelHeight = std::floor(rc.Height()); // 1 pixel onto previous line if multiphase
285 const XYPOSITION x = rcCharacter.left;
286 // 0.5f is to hit midpoint of pixels:
287 const XYPOSITION ix = std::round(x) + 0.5f;
288 const XYPOSITION iy = std::floor(rcLine.top) - 0.5f;
289 const Point pts[] = {
290 Point(ix - pixelHeight, iy), // Left
291 Point(ix + pixelHeight, iy), // Right
292 Point(ix, iy + pixelHeight) // Bottom
294 surface->Polygon(pts, std::size(pts), FillStroke(sacDraw.fore));
296 break;
298 default:
299 // Either IndicatorStyle::Plain or unknown
300 surface->FillRectangle(PRectangle(rcAligned.left, ymid,
301 rcAligned.right, ymid + std::round(strokeWidth)), sacDraw.fore);
305 void Indicator::SetFlags(IndicFlag attributes_) noexcept {
306 attributes = attributes_;