aboutsummaryrefslogtreecommitdiffstats
path: root/mvc/widgets/osx/drawing.py
diff options
context:
space:
mode:
Diffstat (limited to 'mvc/widgets/osx/drawing.py')
-rw-r--r--mvc/widgets/osx/drawing.py289
1 files changed, 289 insertions, 0 deletions
diff --git a/mvc/widgets/osx/drawing.py b/mvc/widgets/osx/drawing.py
new file mode 100644
index 0000000..aaad1e9
--- /dev/null
+++ b/mvc/widgets/osx/drawing.py
@@ -0,0 +1,289 @@
+# @Base: Miro - an RSS based video player application
+# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+# Participatory Culture Foundation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the OpenSSL
+# library.
+#
+# You must obey the GNU General Public License in all respects for all of
+# the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the file(s),
+# but you are not obligated to do so. If you do not wish to do so, delete
+# this exception statement from your version. If you delete this exception
+# statement from all source files in the program, then also delete it here.
+
+"""miro.plat.frontend.widgets.drawing -- Draw on Views."""
+
+import math
+
+from Foundation import *
+from AppKit import *
+#from Quartz import *
+from objc import YES, NO, nil
+
+
+class ImageSurface:
+ """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
+ def __init__(self, image):
+ """Create a new ImageSurface."""
+ self.image = image.nsimage.copy()
+ self.width = image.width
+ self.height = image.height
+
+ def get_size(self):
+ return self.width, self.height
+
+ def draw(self, context, x, y, width, height, fraction=1.0):
+ if self.width == 0 or self.height == 0:
+ return
+ current_context = NSGraphicsContext.currentContext()
+ current_context.setShouldAntialias_(YES)
+ current_context.setImageInterpolation_(NSImageInterpolationHigh)
+ current_context.saveGraphicsState()
+ flip_context(y + height)
+ dest_rect = NSMakeRect(x, 0, width, height)
+ if self.width >= width and self.height >= height:
+ # drawing to area smaller than our image
+ source_rect = NSMakeRect(0, 0, width, height)
+ self.image.drawInRect_fromRect_operation_fraction_(
+ dest_rect, source_rect, NSCompositeSourceOver, fraction)
+ else:
+ # drawing to area larger than our image. Need to tile it.
+ NSColor.colorWithPatternImage_(self.image).set()
+ current_context.setPatternPhase_(
+ self._calc_pattern_phase(context, x, y))
+ NSBezierPath.fillRect_(dest_rect)
+ current_context.restoreGraphicsState()
+
+ def draw_rect(self, context, dest_x, dest_y, source_x, source_y, width,
+ height, fraction=1.0):
+ if width == 0 or height == 0:
+ return
+ current_context = NSGraphicsContext.currentContext()
+ current_context.setShouldAntialias_(YES)
+ current_context.setImageInterpolation_(NSImageInterpolationHigh)
+ current_context.saveGraphicsState()
+ flip_context(dest_y + height)
+ dest_y = 0
+ dest_rect = NSMakeRect(dest_x, dest_y, width, height)
+ source_rect = NSMakeRect(source_x, self.height-source_y-height,
+ width, height)
+ self.image.drawInRect_fromRect_operation_fraction_(
+ dest_rect, source_rect, NSCompositeSourceOver, fraction)
+ current_context.restoreGraphicsState()
+
+ def _calc_pattern_phase(self, context, x, y):
+ """Calculate the pattern phase to draw tiled images.
+
+ When we draw with a pattern, we want the image in the pattern to start
+ at the top-left of where we're drawing to. This function does the
+ dirty work necessary.
+
+ :returns: NSPoint to send to setPatternPhase_
+ """
+ # convert to view coords
+ view_point = NSPoint(context.origin.x + x, context.origin.y + y)
+ # convert to window coords, which is setPatternPhase_ uses
+ return context.view.convertPoint_toView_(view_point, nil)
+
+def convert_cocoa_color(color):
+ rgb = color.colorUsingColorSpaceName_(NSDeviceRGBColorSpace)
+ return (rgb.redComponent(), rgb.greenComponent(), rgb.blueComponent())
+
+def convert_widget_color(color, alpha=1.0):
+ return NSColor.colorWithDeviceRed_green_blue_alpha_(color[0], color[1],
+ color[2], alpha)
+def flip_context(height):
+ """Make the current context's coordinates flipped.
+
+ This is useful for drawing images, since they use the normal cocoa
+ coordinates and we use flipped versions.
+
+ :param height: height of the current area we are drawing to.
+ """
+ xform = NSAffineTransform.transform()
+ xform.translateXBy_yBy_(0, height)
+ xform.scaleXBy_yBy_(1.0, -1.0)
+ xform.concat()
+
+class DrawingStyle(object):
+ """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
+ def __init__(self, bg_color=None, text_color=None):
+ self.use_custom_style = True
+ if text_color is None:
+ self.text_color = self.default_text_color
+ else:
+ self.text_color = convert_cocoa_color(text_color)
+ if bg_color is None:
+ self.bg_color = self.default_bg_color
+ else:
+ self.bg_color = convert_cocoa_color(bg_color)
+
+ default_text_color = convert_cocoa_color(NSColor.textColor())
+ default_bg_color = convert_cocoa_color(NSColor.textBackgroundColor())
+
+class DrawingContext:
+ """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
+ def __init__(self, view, drawing_area, rect):
+ self.view = view
+ self.path = NSBezierPath.bezierPath()
+ self.color = NSColor.blackColor()
+ self.width = drawing_area.size.width
+ self.height = drawing_area.size.height
+ self.origin = drawing_area.origin
+ if drawing_area.origin != NSZeroPoint:
+ xform = NSAffineTransform.transform()
+ xform.translateXBy_yBy_(drawing_area.origin.x,
+ drawing_area.origin.y)
+ xform.concat()
+
+ def move_to(self, x, y):
+ self.path.moveToPoint_(NSPoint(x, y))
+
+ def rel_move_to(self, dx, dy):
+ self.path.relativeMoveToPoint_(NSPoint(dx, dy))
+
+ def line_to(self, x, y):
+ self.path.lineToPoint_(NSPoint(x, y))
+
+ def rel_line_to(self, dx, dy):
+ self.path.relativeLineToPoint_(NSPoint(dx, dy))
+
+ def curve_to(self, x1, y1, x2, y2, x3, y3):
+ self.path.curveToPoint_controlPoint1_controlPoint2_(
+ NSPoint(x3, y3), NSPoint(x1, y1), NSPoint(x2, y2))
+
+ def rel_curve_to(self, dx1, dy1, dx2, dy2, dx3, dy3):
+ self.path.relativeCurveToPoint_controlPoint1_controlPoint2_(
+ NSPoint(dx3, dy3), NSPoint(dx1, dy1), NSPoint(dx2, dy2))
+
+ def arc(self, x, y, radius, angle1, angle2):
+ angle1 = (angle1 * 360) / (2 * math.pi)
+ angle2 = (angle2 * 360) / (2 * math.pi)
+ center = NSPoint(x, y)
+ self.path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(center, radius, angle1, angle2)
+
+ def arc_negative(self, x, y, radius, angle1, angle2):
+ angle1 = (angle1 * 360) / (2 * math.pi)
+ angle2 = (angle2 * 360) / (2 * math.pi)
+ center = NSPoint(x, y)
+ self.path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_clockwise_(center, radius, angle1, angle2, YES)
+
+ def rectangle(self, x, y, width, height):
+ rect = NSMakeRect(x, y, width, height)
+ self.path.appendBezierPathWithRect_(rect)
+
+ def set_color(self, color, alpha=1.0):
+ self.color = convert_widget_color(color, alpha)
+ self.color.set()
+
+ def set_shadow(self, color, opacity, offset, blur_radius):
+ shadow = NSShadow.alloc().init()
+ # shadow offset is always in the cocoa coordinates, so we need to
+ # reverse the y part
+ shadow.setShadowOffset_(NSPoint(offset[0], -offset[1]))
+ shadow.setShadowBlurRadius_(blur_radius)
+ shadow.setShadowColor_(convert_widget_color(color, opacity))
+ shadow.set()
+
+ def set_line_width(self, width):
+ self.path.setLineWidth_(width)
+
+ def stroke(self):
+ self.path.stroke()
+ self.path.removeAllPoints()
+
+ def stroke_preserve(self):
+ self.path.stroke()
+
+ def fill(self):
+ self.path.fill()
+ self.path.removeAllPoints()
+
+ def fill_preserve(self):
+ self.path.fill()
+
+ def clip(self):
+ self.path.addClip()
+ self.path.removeAllPoints()
+
+ def save(self):
+ NSGraphicsContext.currentContext().saveGraphicsState()
+
+ def restore(self):
+ NSGraphicsContext.currentContext().restoreGraphicsState()
+
+ def gradient_fill(self, gradient):
+ self.gradient_fill_preserve(gradient)
+ self.path.removeAllPoints()
+
+ def gradient_fill_preserve(self, gradient):
+ context = NSGraphicsContext.currentContext()
+ context.saveGraphicsState()
+ self.path.addClip()
+ gradient.draw()
+ context.restoreGraphicsState()
+
+class Gradient(object):
+ """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
+ def __init__(self, x1, y1, x2, y2):
+ self.x1, self.y1, self.x2, self.y2 = x1, y1, x2, y2
+ self.start_color = None
+ self.end_color = None
+
+ def set_start_color(self, (red, green, blue)):
+ self.start_color = (red, green, blue)
+
+ def set_end_color(self, (red, green, blue)):
+ self.end_color = (red, green, blue)
+
+ def draw(self):
+ start_color = convert_widget_color(self.start_color)
+ end_color = convert_widget_color(self.end_color)
+ nsgradient = NSGradient.alloc().initWithStartingColor_endingColor_(start_color, end_color)
+ start_point = NSPoint(self.x1, self.y1)
+ end_point = NSPoint(self.x2, self.y2)
+ nsgradient.drawFromPoint_toPoint_options_(start_point, end_point, 0)
+
+class DrawingMixin(object):
+ def calc_size_request(self):
+ return self.size_request(self.view.layout_manager)
+
+ # squish width / squish height only make sense on GTK
+ def set_squish_width(self, setting):
+ pass
+
+ def set_squish_height(self, setting):
+ pass
+
+ # Default implementations for methods that subclasses override.
+
+ def is_opaque(self):
+ return False
+
+ def size_request(self, layout_manager):
+ return 0, 0
+
+ def draw(self, context, layout_manager):
+ pass
+
+ def viewport_repositioned(self):
+ # since this is a Mixin class, we want to make sure that our other
+ # classes see the viewport_repositioned() call.
+ super(DrawingMixin, self).viewport_repositioned()
+ self.queue_redraw()