aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cocoa/PlatCocoa.h1
-rw-r--r--cocoa/PlatCocoa.mm55
-rwxr-xr-xgtk/PlatGTK.cxx63
-rw-r--r--qt/ScintillaEditBase/PlatQt.cpp58
-rw-r--r--qt/ScintillaEditBase/PlatQt.h1
-rw-r--r--src/Platform.h9
-rw-r--r--win32/PlatWin.cxx103
7 files changed, 288 insertions, 2 deletions
diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h
index 36022286c..c14ca1928 100644
--- a/cocoa/PlatCocoa.h
+++ b/cocoa/PlatCocoa.h
@@ -113,6 +113,7 @@ public:
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
void Ellipse(PRectangle rc, FillStroke fillStroke) override;
+ void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;
void Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm
index 55a6ee620..7bec2d73d 100644
--- a/cocoa/PlatCocoa.mm
+++ b/cocoa/PlatCocoa.mm
@@ -1231,6 +1231,61 @@ void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke) {
CGContextSetLineWidth(gc, 1.0f);
}
+void SurfaceImpl::Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) {
+ const CGFloat midLine = rc.Centre().y;
+ const CGFloat piOn2 = acos(0.0);
+ const XYPOSITION halfStroke = fillStroke.stroke.width / 2.0f;
+ const float radius = rc.Height() / 2.0f - halfStroke;
+ PRectangle rcInner = rc;
+ rcInner.left += radius;
+ rcInner.right -= radius;
+
+ SetFillStroke(fillStroke);
+ CGContextBeginPath(gc);
+
+ const Ends leftSide = static_cast<Ends>(static_cast<int>(ends) & 0xf);
+ const Ends rightSide = static_cast<Ends>(static_cast<int>(ends) & 0xf0);
+ switch (leftSide) {
+ case Ends::leftFlat:
+ CGContextMoveToPoint(gc, rc.left + halfStroke, rc.top + halfStroke);
+ CGContextAddLineToPoint(gc, rc.left + halfStroke, rc.bottom - halfStroke);
+ break;
+ case Ends::leftAngle:
+ CGContextMoveToPoint(gc, rcInner.left + halfStroke, rc.top + halfStroke);
+ CGContextAddLineToPoint(gc, rc.left + halfStroke, rc.Centre().y);
+ CGContextAddLineToPoint(gc, rcInner.left + halfStroke, rc.bottom - halfStroke);
+ break;
+ case Ends::semiCircles:
+ default:
+ CGContextMoveToPoint(gc, rcInner.left + halfStroke, rc.top + halfStroke);
+ CGContextAddArc(gc, rcInner.left + halfStroke, midLine, radius, -piOn2, piOn2, 1);
+ break;
+ }
+
+ switch (rightSide) {
+ case Ends::rightFlat:
+ CGContextAddLineToPoint(gc, rc.right - halfStroke, rc.bottom - halfStroke);
+ CGContextAddLineToPoint(gc, rc.right - halfStroke, rc.top + halfStroke);
+ break;
+ case Ends::rightAngle:
+ CGContextAddLineToPoint(gc, rcInner.right - halfStroke, rc.bottom - halfStroke);
+ CGContextAddLineToPoint(gc, rc.right - halfStroke, rc.Centre().y);
+ CGContextAddLineToPoint(gc, rcInner.right - halfStroke, rc.top + halfStroke);
+ break;
+ case Ends::semiCircles:
+ default:
+ CGContextAddLineToPoint(gc, rcInner.right - halfStroke, rc.bottom - halfStroke);
+ CGContextAddArc(gc, rcInner.right - halfStroke, midLine, radius, piOn2, -piOn2, 1);
+ break;
+ }
+
+ // Close the path to enclose it for stroking and for filling, then draw it
+ CGContextClosePath(gc);
+ CGContextDrawPath(gc, kCGPathFillStroke);
+
+ CGContextSetLineWidth(gc, 1.0f);
+}
+
void SurfaceImpl::CopyImageRectangle(Surface &surfaceSource, PRectangle srcRect, PRectangle dstRect) {
SurfaceImpl &source = static_cast<SurfaceImpl &>(surfaceSource);
CGImageRef image = source.CreateImage();
diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx
index 0b87b8743..aa0d69477 100755
--- a/gtk/PlatGTK.cxx
+++ b/gtk/PlatGTK.cxx
@@ -47,6 +47,8 @@ namespace {
constexpr double kPi = 3.14159265358979323846;
+constexpr double degrees = kPi / 180.0;
+
// The Pango version guard for pango_units_from_double and pango_units_to_double
// is more complex than simply implementing these here.
@@ -178,6 +180,7 @@ public:
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
void Ellipse(PRectangle rc, FillStroke fillStroke) override;
+ void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
@@ -612,8 +615,6 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {
}
static void PathRoundRectangle(cairo_t *context, double left, double top, double width, double height, double radius) noexcept {
- constexpr double degrees = kPi / 180.0;
-
cairo_new_sub_path(context);
cairo_arc(context, left + width - radius, top + radius, radius, -90 * degrees, 0 * degrees);
cairo_arc(context, left + width - radius, top + height - radius, radius, 0 * degrees, 90 * degrees);
@@ -749,6 +750,64 @@ void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke) {
cairo_stroke(context);
}
+void SurfaceImpl::Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) {
+ const XYPOSITION midLine = rc.Centre().y;
+ const XYPOSITION halfStroke = fillStroke.stroke.width / 2.0f;
+ const XYPOSITION radius = rc.Height() / 2.0f - halfStroke;
+ PRectangle rcInner = rc;
+ rcInner.left += radius;
+ rcInner.right -= radius;
+
+ cairo_new_sub_path(context);
+
+ const Ends leftSide = static_cast<Ends>(static_cast<int>(ends) & 0xf);
+ const Ends rightSide = static_cast<Ends>(static_cast<int>(ends) & 0xf0);
+ switch (leftSide) {
+ case Ends::leftFlat:
+ cairo_move_to(context, rc.left + halfStroke, rc.top + halfStroke);
+ cairo_line_to(context, rc.left + halfStroke, rc.bottom - halfStroke);
+ break;
+ case Ends::leftAngle:
+ cairo_move_to(context, rcInner.left + halfStroke, rc.top + halfStroke);
+ cairo_line_to(context, rc.left + halfStroke, rc.Centre().y);
+ cairo_line_to(context, rcInner.left + halfStroke, rc.bottom - halfStroke);
+ break;
+ case Ends::semiCircles:
+ default:
+ cairo_move_to(context, rcInner.left + halfStroke, rc.top + halfStroke);
+ cairo_arc_negative(context, rcInner.left + halfStroke, midLine, radius,
+ 270 * degrees, 90 * degrees);
+ break;
+ }
+
+ switch (rightSide) {
+ case Ends::rightFlat:
+ cairo_line_to(context, rc.right - halfStroke, rc.bottom - halfStroke);
+ cairo_line_to(context, rc.right - halfStroke, rc.top + halfStroke);
+ break;
+ case Ends::rightAngle:
+ cairo_line_to(context, rcInner.right - halfStroke, rc.bottom - halfStroke);
+ cairo_line_to(context, rc.right - halfStroke, rc.Centre().y);
+ cairo_line_to(context, rcInner.right - halfStroke, rc.top + halfStroke);
+ break;
+ case Ends::semiCircles:
+ default:
+ cairo_line_to(context, rcInner.right - halfStroke, rc.bottom - halfStroke);
+ cairo_arc_negative(context, rcInner.right - halfStroke, midLine, radius,
+ 90 * degrees, 270 * degrees);
+ break;
+ }
+
+ // Close the path to enclose it for stroking and for filling, then draw it
+ cairo_close_path(context);
+ PenColourAlpha(fillStroke.fill.colour);
+ cairo_fill_preserve(context);
+
+ PenColourAlpha(fillStroke.stroke.colour);
+ cairo_set_line_width(context, fillStroke.stroke.width);
+ cairo_stroke(context);
+}
+
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
SurfaceImpl &surfi = static_cast<SurfaceImpl &>(surfaceSource);
const bool canDraw = surfi.psurf != nullptr;
diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp
index fa4c062d8..7e2fd3e98 100644
--- a/qt/ScintillaEditBase/PlatQt.cpp
+++ b/qt/ScintillaEditBase/PlatQt.cpp
@@ -524,6 +524,64 @@ void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke)
GetPainter()->drawEllipse(rect);
}
+void SurfaceImpl::Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) {
+ const XYPOSITION halfStroke = fillStroke.stroke.width / 2.0f;
+ const XYPOSITION radius = rc.Height() / 2.0f - halfStroke;
+ PRectangle rcInner = rc;
+ rcInner.left += radius;
+ rcInner.right -= radius;
+ const XYPOSITION arcHeight = rc.Height() - fillStroke.stroke.width;
+
+ PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour);
+
+ QPainterPath path;
+
+ const Ends leftSide = static_cast<Ends>(static_cast<int>(ends) & 0xf);
+ const Ends rightSide = static_cast<Ends>(static_cast<int>(ends) & 0xf0);
+ switch (leftSide) {
+ case Ends::leftFlat:
+ path.moveTo(rc.left + halfStroke, rc.top + halfStroke);
+ path.lineTo(rc.left + halfStroke, rc.bottom - halfStroke);
+ break;
+ case Ends::leftAngle:
+ path.moveTo(rcInner.left + halfStroke, rc.top + halfStroke);
+ path.lineTo(rc.left + halfStroke, rc.Centre().y);
+ path.lineTo(rcInner.left + halfStroke, rc.bottom - halfStroke);
+ break;
+ case Ends::semiCircles:
+ default:
+ path.moveTo(rcInner.left + halfStroke, rc.top + halfStroke);
+ QRectF rectangleArc(rc.left + halfStroke, rc.top + halfStroke,
+ arcHeight, arcHeight);
+ path.arcTo(rectangleArc, 90, 180);
+ break;
+ }
+
+ switch (rightSide) {
+ case Ends::rightFlat:
+ path.lineTo(rc.right - halfStroke, rc.bottom - halfStroke);
+ path.lineTo(rc.right - halfStroke, rc.top + halfStroke);
+ break;
+ case Ends::rightAngle:
+ path.lineTo(rcInner.right - halfStroke, rc.bottom - halfStroke);
+ path.lineTo(rc.right - halfStroke, rc.Centre().y);
+ path.lineTo(rcInner.right - halfStroke, rc.top + halfStroke);
+ break;
+ case Ends::semiCircles:
+ default:
+ path.lineTo(rcInner.right - halfStroke, rc.bottom - halfStroke);
+ QRectF rectangleArc(rc.right - arcHeight - halfStroke, rc.top + halfStroke,
+ arcHeight, arcHeight);
+ path.arcTo(rectangleArc, 270, 180);
+ break;
+ }
+
+ // Close the path to enclose it for stroking and for filling, then draw it
+ path.closeSubpath();
+ GetPainter()->drawPath(path);
+}
+
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource)
{
SurfaceImpl *source = dynamic_cast<SurfaceImpl *>(&surfaceSource);
diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h
index 2ca31da2d..a95e1bd88 100644
--- a/qt/ScintillaEditBase/PlatQt.h
+++ b/qt/ScintillaEditBase/PlatQt.h
@@ -126,6 +126,7 @@ public:
void Ellipse(PRectangle rc, ColourDesired fore,
ColourDesired back) override;
void Ellipse(PRectangle rc, FillStroke fillStroke) override;
+ void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
diff --git a/src/Platform.h b/src/Platform.h
index 4c7b6523d..7f61967c3 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -179,6 +179,14 @@ public:
virtual void Init(SurfaceID sid, WindowID wid)=0;
virtual void InitPixMap(int width, int height, Surface *surface_, WindowID wid)=0;
+ enum class Ends {
+ semiCircles = 0x0,
+ leftFlat = 0x1,
+ leftAngle = 0x2,
+ rightFlat = 0x10,
+ rightAngle = 0x20,
+ };
+
virtual void Release() noexcept=0;
virtual int Supports(int feature) noexcept=0;
virtual bool Initialised()=0;
@@ -206,6 +214,7 @@ public:
virtual void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) = 0;
virtual void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)=0;
virtual void Ellipse(PRectangle rc, FillStroke fillStroke)=0;
+ virtual void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends)=0;
virtual void Copy(PRectangle rc, Point from, Surface &surfaceSource)=0;
virtual std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) = 0;
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx
index eac743ef3..673df5c2b 100644
--- a/win32/PlatWin.cxx
+++ b/win32/PlatWin.cxx
@@ -505,6 +505,7 @@ public:
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
void Ellipse(PRectangle rc, FillStroke fillStroke) override;
+ void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
@@ -1083,6 +1084,11 @@ void SurfaceGDI::Ellipse(PRectangle rc, FillStroke fillStroke) {
::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
}
+void SurfaceGDI::Stadium(PRectangle rc, FillStroke fillStroke, [[maybe_unused]] Ends ends) {
+ // TODO: Implement properly - the rectangle is just a placeholder
+ RectangleDraw(rc, fillStroke);
+}
+
void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
::BitBlt(hdc,
static_cast<int>(rc.left), static_cast<int>(rc.top),
@@ -1436,6 +1442,7 @@ public:
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
void Ellipse(PRectangle rc, FillStroke fillStroke) override;
+ void Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
@@ -1999,6 +2006,102 @@ void SurfaceD2D::Ellipse(PRectangle rc, FillStroke fillStroke) {
pRenderTarget->DrawEllipse(ellipseOutline, pBrush, fillStroke.stroke.width);
}
+void SurfaceD2D::Stadium(PRectangle rc, FillStroke fillStroke, Ends ends) {
+ if (!pRenderTarget)
+ return;
+ if (rc.Width() < rc.Height()) {
+ // Can't draw nice ends so just draw a rectangle
+ RectangleDraw(rc, fillStroke);
+ return;
+ }
+ const float radius = rc.Height() / 2.0f;
+ const float radiusFill = radius - fillStroke.stroke.width;
+ const XYPOSITION halfStroke = fillStroke.stroke.width / 2.0f;
+ if (ends == Surface::Ends::semiCircles) {
+ const D2D1_RECT_F rect = { rc.left, rc.top, rc.right, rc.bottom };
+ D2D1_ROUNDED_RECT roundedRectFill = { RectangleInset(rect, fillStroke.stroke.width),
+ radiusFill, radiusFill };
+ D2DPenColourAlpha(fillStroke.fill.colour);
+ pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush);
+
+ D2D1_ROUNDED_RECT roundedRect = { RectangleInset(rect, halfStroke),
+ radius, radius };
+ D2DPenColourAlpha(fillStroke.stroke.colour);
+ pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush, fillStroke.stroke.width);
+ } else {
+ const Ends leftSide = static_cast<Ends>(static_cast<int>(ends) & 0xf);
+ const Ends rightSide = static_cast<Ends>(static_cast<int>(ends) & 0xf0);
+ PRectangle rcInner = rc;
+ rcInner.left += radius;
+ rcInner.right -= radius;
+ ID2D1PathGeometry *pathGeometry = nullptr;
+ const HRESULT hrGeometry = pD2DFactory->CreatePathGeometry(&pathGeometry);
+ if (FAILED(hrGeometry) || !pathGeometry)
+ return;
+ ID2D1GeometrySink *pSink = nullptr;
+ const HRESULT hrSink = pathGeometry->Open(&pSink);
+ if (SUCCEEDED(hrSink) && pSink) {
+ switch (leftSide) {
+ case Ends::leftFlat:
+ pSink->BeginFigure(D2D1::Point2F(rc.left + halfStroke, rc.top + halfStroke), D2D1_FIGURE_BEGIN_FILLED);
+ pSink->AddLine(D2D1::Point2F(rc.left + halfStroke, rc.bottom - halfStroke));
+ break;
+ case Ends::leftAngle:
+ pSink->BeginFigure(D2D1::Point2F(rcInner.left + halfStroke, rc.top + halfStroke), D2D1_FIGURE_BEGIN_FILLED);
+ pSink->AddLine(D2D1::Point2F(rc.left + halfStroke, rc.Centre().y));
+ pSink->AddLine(D2D1::Point2F(rcInner.left + halfStroke, rc.bottom - halfStroke));
+ break;
+ case Ends::semiCircles:
+ default: {
+ pSink->BeginFigure(D2D1::Point2F(rcInner.left + halfStroke, rc.top + halfStroke), D2D1_FIGURE_BEGIN_FILLED);
+ D2D1_ARC_SEGMENT segment{};
+ segment.point = D2D1::Point2F(rcInner.left + halfStroke, rc.bottom - halfStroke);
+ segment.size = D2D1::SizeF(radiusFill, radiusFill);
+ segment.rotationAngle = 0.0f;
+ segment.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;
+ segment.arcSize = D2D1_ARC_SIZE_SMALL;
+ pSink->AddArc(segment);
+ }
+ break;
+ }
+
+ switch (rightSide) {
+ case Ends::rightFlat:
+ pSink->AddLine(D2D1::Point2F(rc.right - halfStroke, rc.bottom - halfStroke));
+ pSink->AddLine(D2D1::Point2F(rc.right - halfStroke, rc.top + halfStroke));
+ break;
+ case Ends::rightAngle:
+ pSink->AddLine(D2D1::Point2F(rcInner.right - halfStroke, rc.bottom - halfStroke));
+ pSink->AddLine(D2D1::Point2F(rc.right - halfStroke, rc.Centre().y));
+ pSink->AddLine(D2D1::Point2F(rcInner.right - halfStroke, rc.top + halfStroke));
+ break;
+ case Ends::semiCircles:
+ default: {
+ pSink->AddLine(D2D1::Point2F(rcInner.right - halfStroke, rc.bottom - halfStroke));
+ D2D1_ARC_SEGMENT segment{};
+ segment.point = D2D1::Point2F(rcInner.right - halfStroke, rc.top + halfStroke);
+ segment.size = D2D1::SizeF(radiusFill, radiusFill);
+ segment.rotationAngle = 0.0f;
+ segment.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;
+ segment.arcSize = D2D1_ARC_SIZE_SMALL;
+ pSink->AddArc(segment);
+ }
+ break;
+ }
+
+ pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+
+ pSink->Close();
+ }
+ ReleaseUnknown(pSink);
+ D2DPenColourAlpha(fillStroke.fill.colour);
+ pRenderTarget->FillGeometry(pathGeometry, pBrush);
+ D2DPenColourAlpha(fillStroke.stroke.colour);
+ pRenderTarget->DrawGeometry(pathGeometry, pBrush, fillStroke.stroke.width);
+ ReleaseUnknown(pathGeometry);
+ }
+}
+
void SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
SurfaceD2D &surfOther = dynamic_cast<SurfaceD2D &>(surfaceSource);
ID2D1Bitmap *pBitmap = nullptr;