diff options
Diffstat (limited to 'cocoa/PlatCocoa.mm')
-rw-r--r-- | cocoa/PlatCocoa.mm | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm index 714e4f484..618d17de1 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -51,6 +51,15 @@ extern sptr_t scintilla_send_message(void *sci, unsigned int iMessage, uptr_t wP //-------------------------------------------------------------------------------------------------- /** + * Converts a Point as used by Scintilla to a Quartz-style CGPoint. + */ +inline CGPoint CGPointFromPoint(Scintilla::Point pt) { + return CGPointMake(pt.x, pt.y); +} + +//-------------------------------------------------------------------------------------------------- + +/** * Converts a PRectangle as used by Scintilla to standard Obj-C NSRect structure . */ NSRect PRectangleToNSRect(const PRectangle &rc) { @@ -77,6 +86,18 @@ inline CGRect PRectangleToCGRect(PRectangle &rc) { return CGRectMake(rc.left, rc.top, rc.Width(), rc.Height()); } +//-------------------------------------------------------------------------------------------------- + +/** + * Converts a PRectangle as used by Scintilla to a Quartz-style rectangle. + * Result is inset by strokeWidth / 2 so stroking does not go outside the rectangle. + */ +inline CGRect CGRectFromPRectangleInset(PRectangle rc, XYPOSITION strokeWidth) { + const XYPOSITION halfStroke = strokeWidth / 2.0f; + const CGRect rect = PRectangleToCGRect(rc); + return CGRectInset(rect, halfStroke, halfStroke); +} + //----------------- FontQuartz --------------------------------------------------------------------- class FontQuartz : public Font { @@ -513,6 +534,14 @@ void SurfaceImpl::PenColourAlpha(ColourAlpha fore) { //-------------------------------------------------------------------------------------------------- +void SurfaceImpl::SetFillStroke(FillStroke fillStroke) { + FillColour(fillStroke.fill.colour); + PenColourAlpha(fillStroke.stroke.colour); + CGContextSetLineWidth(gc, fillStroke.stroke.width); +} + +//-------------------------------------------------------------------------------------------------- + CGImageRef SurfaceImpl::CreateImage() { // For now, assume that CreateImage can only be called on PixMap surfaces. if (!bitmapData) @@ -655,6 +684,27 @@ void SurfaceImpl::Polygon(Scintilla::Point *pts, size_t npts, ColourDesired fore //-------------------------------------------------------------------------------------------------- +void SurfaceImpl::Polygon(const Scintilla::Point *pts, size_t npts, FillStroke fillStroke) { + std::vector<CGPoint> points; + std::transform(pts, pts + npts, std::back_inserter(points), CGPointFromPoint); + + CGContextBeginPath(gc); + + SetFillStroke(fillStroke); + + // Draw the polygon + CGContextAddLines(gc, points.data(), npts); + + // Explicitly close the path, so it is closed for stroking AND filling (implicit close = filling only) + CGContextClosePath(gc); + CGContextDrawPath(gc, kCGPathFillStroke); + + // Restore as not all paths set + CGContextSetLineWidth(gc, 1.0f); +} + +//-------------------------------------------------------------------------------------------------- + void SurfaceImpl::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) { if (gc) { CGContextBeginPath(gc); @@ -671,6 +721,22 @@ void SurfaceImpl::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired //-------------------------------------------------------------------------------------------------- +void SurfaceImpl::RectangleDraw(PRectangle rc, FillStroke fillStroke) { + if (!gc) + return; + CGContextBeginPath(gc); + SetFillStroke(fillStroke); + + CGContextAddRect(gc, CGRectFromPRectangleInset(rc, fillStroke.stroke.width)); + + CGContextDrawPath(gc, kCGPathFillStroke); + + // Restore as not all paths set + CGContextSetLineWidth(gc, 1.0f); +} + +//-------------------------------------------------------------------------------------------------- + void SurfaceImpl::FillRectangle(PRectangle rc, ColourDesired back) { if (gc) { FillColour(back); @@ -813,6 +879,70 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesi CGContextDrawPath(gc, kCGPathFillStroke); } +void SurfaceImpl::RoundedRectangle(PRectangle rc, FillStroke fillStroke) { + // This is only called from the margin marker drawing code for SC_MARK_ROUNDRECT + // The Win32 version does + // ::RoundRect(hdc, rc.left + 1, rc.top, rc.right - 1, rc.bottom, 8, 8 ); + // which is a rectangle with rounded corners each having a radius of 4 pixels. + // It would be almost as good just cutting off the corners with lines at + // 45 degrees as is done on GTK+. + + // Create a rectangle with semicircles at the corners + const int MAX_RADIUS = 4; + const int radius = std::min(MAX_RADIUS, static_cast<int>(std::min(rc.Height()/2, rc.Width()/2))); + + // Points go clockwise, starting from just below the top left + // Corners are kept together, so we can easily create arcs to connect them + CGPoint corners[4][3] = { + { + { rc.left, rc.top + radius }, + { rc.left, rc.top }, + { rc.left + radius, rc.top }, + }, + { + { rc.right - radius - 1, rc.top }, + { rc.right - 1, rc.top }, + { rc.right - 1, rc.top + radius }, + }, + { + { rc.right - 1, rc.bottom - radius - 1 }, + { rc.right - 1, rc.bottom - 1 }, + { rc.right - radius - 1, rc.bottom - 1 }, + }, + { + { rc.left + radius, rc.bottom - 1 }, + { rc.left, rc.bottom - 1 }, + { rc.left, rc.bottom - radius - 1 }, + }, + }; + + // Align the points in the middle of the pixels + for (int i = 0; i < 4; ++ i) { + for (int j = 0; j < 3; ++ j) { + corners[i][j].x += 0.5; + corners[i][j].y += 0.5; + } + } + + SetFillStroke(fillStroke); + + // Move to the last point to begin the path + CGContextBeginPath(gc); + CGContextMoveToPoint(gc, corners[3][2].x, corners[3][2].y); + + for (int i = 0; i < 4; ++ i) { + CGContextAddLineToPoint(gc, corners[i][0].x, corners[i][0].y); + CGContextAddArcToPoint(gc, corners[i][1].x, corners[i][1].y, corners[i][2].x, corners[i][2].y, radius); + } + + // Close the path to enclose it for stroking and for filling, then draw it + CGContextClosePath(gc); + CGContextDrawPath(gc, kCGPathFillStroke); + + // Restore as not all paths set + CGContextSetLineWidth(gc, 1.0f); +} + // DrawChamferedRectangle is a helper function for AlphaRectangle that either fills or strokes a // rectangle with its corners chamfered at 45 degrees. static void DrawChamferedRectangle(CGContextRef gc, PRectangle rc, int cornerSize, CGPathDrawingMode mode) { @@ -1073,6 +1203,16 @@ void SurfaceImpl::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) CGContextDrawPath(gc, kCGPathFillStroke); } +void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke) { + const CGRect ellipseRect = CGRectFromPRectangleInset(rc, fillStroke.stroke.width / 2.0f); + SetFillStroke(fillStroke); + CGContextBeginPath(gc); + CGContextAddEllipseInRect(gc, ellipseRect); + CGContextDrawPath(gc, kCGPathFillStroke); + // Restore as not all paths set + CGContextSetLineWidth(gc, 1.0f); +} + void SurfaceImpl::CopyImageRectangle(Surface &surfaceSource, PRectangle srcRect, PRectangle dstRect) { SurfaceImpl &source = static_cast<SurfaceImpl &>(surfaceSource); CGImageRef image = source.CreateImage(); |