diff options
Diffstat (limited to 'cocoa')
| -rw-r--r-- | cocoa/PlatCocoa.h | 6 | ||||
| -rw-r--r-- | cocoa/PlatCocoa.mm | 140 | 
2 files changed, 146 insertions, 0 deletions
| diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h index f50c0e7f7..4487d9945 100644 --- a/cocoa/PlatCocoa.h +++ b/cocoa/PlatCocoa.h @@ -64,6 +64,8 @@ private:  	void PenColourAlpha(ColourAlpha fore); +	void SetFillStroke(FillStroke fillStroke); +  	// 24-bit RGB+A bitmap data constants  	static const int BITS_PER_COMPONENT = 8;  	static const int BITS_PER_PIXEL = BITS_PER_COMPONENT * 4; @@ -95,17 +97,21 @@ public:  	void MoveTo(int x_, int y_) override;  	void LineTo(int x_, int y_) override;  	void Polygon(Scintilla::Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override; +	void Polygon(const Scintilla::Point *pts, size_t npts, FillStroke fillStroke) override;  	void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override; +	void RectangleDraw(PRectangle rc, FillStroke fillStroke) override;  	void FillRectangle(PRectangle rc, ColourDesired back) override;  	void FillRectangle(PRectangle rc, Fill fill) override;  	void FillRectangle(PRectangle rc, Surface &surfacePattern) override;  	void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override; +	void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;  	void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,  			    ColourDesired outline, int alphaOutline, int flags) override;  	void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;  	void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;  	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 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 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(); | 
