diff options
author | Jesús Eduardo <heckyel@hyperbola.info> | 2017-09-11 17:47:17 -0500 |
---|---|---|
committer | Jesús Eduardo <heckyel@hyperbola.info> | 2017-09-11 17:47:17 -0500 |
commit | 14738704ede6dfa6ac79f362a9c1f7f40f470cdc (patch) | |
tree | 31c83bdd188ae7b64d7169974d6f066ccfe95367 /mvc/widgets/osx/layoutmanager.py | |
parent | eb1896583afbbb622cadcde1a24e17173f61904f (diff) | |
download | librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.tar.lz librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.tar.xz librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.zip |
rename mvc at lvc
Diffstat (limited to 'mvc/widgets/osx/layoutmanager.py')
-rw-r--r-- | mvc/widgets/osx/layoutmanager.py | 445 |
1 files changed, 0 insertions, 445 deletions
diff --git a/mvc/widgets/osx/layoutmanager.py b/mvc/widgets/osx/layoutmanager.py deleted file mode 100644 index de4301b..0000000 --- a/mvc/widgets/osx/layoutmanager.py +++ /dev/null @@ -1,445 +0,0 @@ -# @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. - -"""textlayout.py -- Contains the LayoutManager class. It handles laying text, -buttons, getting font metrics and other tasks that are required to size -things. -""" -import logging -import math - -from AppKit import * -from Foundation import * -from objc import YES, NO, nil - -import drawing - -INFINITE = 1000000 # size of an "infinite" dimension - -class MiroLayoutManager(NSLayoutManager): - """Overide NSLayoutManager to draw better underlines.""" - - def drawUnderlineForGlyphRange_underlineType_baselineOffset_lineFragmentRect_lineFragmentGlyphRange_containerOrigin_(self, glyph_range, typ, offset, line_rect, line_glyph_range, container_origin): - container, _ = self.textContainerForGlyphAtIndex_effectiveRange_(glyph_range.location, None) - rect = self.boundingRectForGlyphRange_inTextContainer_(glyph_range, container) - x = container_origin.x + rect.origin.x - y = (container_origin.y + rect.origin.y + rect.size.height - offset) - underline_height, offset = self.calc_underline_extents(glyph_range) - y = math.ceil(y + offset) + underline_height / 2.0 - path = NSBezierPath.bezierPath() - path.setLineWidth_(underline_height) - path.moveToPoint_(NSPoint(x, y)) - path.relativeLineToPoint_(NSPoint(rect.size.width, 0)) - path.stroke() - - def calc_underline_extents(self, line_glyph_range): - index = self.characterIndexForGlyphAtIndex_(line_glyph_range.location) - font, _ = self.textStorage().attribute_atIndex_effectiveRange_(NSFontAttributeName, index, None) - # we use a couple of magic numbers that seems to work okay. I (BDK) - # got it from some old mozilla code. - height = font.ascender() - font.descender() - height = max(1.0, round(0.05 * height)) - offset = max(1.0, round(0.1 * height)) - return height, offset - -class TextBoxPool(object): - """Handles a pool of TextBox objects. We monitor the TextBox objects and - when those objects die, we reclaim them for the pool. - - Creating TextBoxes is fairly expensive and NSLayoutManager do a lot of - caching, so it's useful to keep them around rather than destroying them. - """ - - def __init__(self): - self.used_text_boxes = [] - self.available_text_boxes = [] - - def get(self): - """Get a NSLayoutManager, either from the pool or by creating a new - one. - """ - try: - rv = self.available_text_boxes.pop() - except IndexError: - rv = TextBox() - self.used_text_boxes.append(rv) - return rv - - def reclaim_textboxes(self): - """Move used TextBoxes back to the available pool. This should be - called after the code using text boxes is done using all of them. - """ - self.available_text_boxes.extend(self.used_text_boxes) - self.used_text_boxes[:] = [] - -text_box_pool = TextBoxPool() - -class Font(object): - line_height_sizer = NSLayoutManager.alloc().init() - - def __init__(self, nsfont): - self.nsfont = nsfont - - def ascent(self): - return self.nsfont.ascender() - - def descent(self): - return -self.nsfont.descender() - - def line_height(self): - return Font.line_height_sizer.defaultLineHeightForFont_(self.nsfont) - -class FontPool(object): - def __init__(self): - self._cached_fonts = {} - - def get(self, scale_factor, bold, italic, family): - cache_key = (scale_factor, bold, italic, family) - try: - return self._cached_fonts[cache_key] - except KeyError: - font = self._create(scale_factor, bold, italic, family) - self._cached_fonts[cache_key] = font - return font - - def _create(self, scale_factor, bold, italic, family): - size = round(scale_factor * NSFont.systemFontSize()) - nsfont = None - if family is not None: - if bold: - nsfont = NSFont.fontWithName_size_(family + " Bold", size) - else: - nsfont = NSFont.fontWithName_size_(family, size) - if nsfont is None: - logging.error('FontPool: family %s scale %s bold %s ' - 'italic %s not found', - family, scale_factor, bold, italic) - # Att his point either we have requested a custom font that failed - # to load or the system font was requested. - if nsfont is None: - if bold: - nsfont = NSFont.boldSystemFontOfSize_(size) - else: - nsfont = NSFont.systemFontOfSize_(size) - return Font(nsfont) - -class LayoutManager(object): - font_pool = FontPool() - default_font = font_pool.get(1.0, False, False, None) - - def __init__(self): - self.current_font = self.default_font - self.set_text_color((0, 0, 0)) - self.set_text_shadow(None) - - def font(self, scale_factor, bold=False, italic=False, family=None): - return self.font_pool.get(scale_factor, bold, italic, family) - - def set_font(self, scale_factor, bold=False, italic=False, family=None): - self.current_font = self.font(scale_factor, bold, italic, family) - - def set_text_color(self, color): - self.text_color = color - - def set_text_shadow(self, shadow): - self.shadow = shadow - - def textbox(self, text, underline=False): - text_box = text_box_pool.get() - color = NSColor.colorWithDeviceRed_green_blue_alpha_(self.text_color[0], self.text_color[1], self.text_color[2], 1.0) - text_box.reset(text, self.current_font, color, self.shadow, underline) - return text_box - - def button(self, text, pressed=False, disabled=False, style='normal'): - if style == 'webby': - return StyledButton(text, self.current_font, pressed, disabled) - else: - return NativeButton(text, self.current_font, pressed, disabled) - - def reset(self): - text_box_pool.reclaim_textboxes() - self.current_font = self.default_font - self.text_color = (0, 0, 0) - self.shadow = None - -class TextBox(object): - def __init__(self): - self.layout_manager = MiroLayoutManager.alloc().init() - container = NSTextContainer.alloc().init() - container.setLineFragmentPadding_(0) - self.layout_manager.addTextContainer_(container) - self.layout_manager.setUsesFontLeading_(NO) - self.text_storage = NSTextStorage.alloc().init() - self.text_storage.addLayoutManager_(self.layout_manager) - self.text_container = self.layout_manager.textContainers()[0] - - def reset(self, text, font, color, shadow, underline): - """Reset the text box so it's ready to be used by a new owner.""" - self.text_storage.deleteCharactersInRange_(NSRange(0, - self.text_storage.length())) - self.text_container.setContainerSize_(NSSize(INFINITE, INFINITE)) - self.paragraph_style = NSMutableParagraphStyle.alloc().init() - self.font = font - self.color = color - self.shadow = shadow - self.width = None - self.set_text(text, underline=underline) - - def make_attr_string(self, text, color, font, underline): - attributes = NSMutableDictionary.alloc().init() - if color is not None: - nscolor = NSColor.colorWithDeviceRed_green_blue_alpha_(color[0], color[1], color[2], 1.0) - attributes.setObject_forKey_(nscolor, NSForegroundColorAttributeName) - else: - attributes.setObject_forKey_(self.color, NSForegroundColorAttributeName) - if font is not None: - attributes.setObject_forKey_(font.nsfont, NSFontAttributeName) - else: - attributes.setObject_forKey_(self.font.nsfont, NSFontAttributeName) - if underline: - attributes.setObject_forKey_(NSUnderlineStyleSingle, NSUnderlineStyleAttributeName) - attributes.setObject_forKey_(self.paragraph_style.copy(), NSParagraphStyleAttributeName) - if text is None: - text = "" - return NSAttributedString.alloc().initWithString_attributes_(text, attributes) - - def set_text(self, text, color=None, font=None, underline=False): - string = self.make_attr_string(text, color, font, underline) - self.text_storage.setAttributedString_(string) - - def append_text(self, text, color=None, font=None, underline=False): - string = self.make_attr_string(text, color, font, underline) - self.text_storage.appendAttributedString_(string) - - def set_width(self, width): - if width is not None: - self.text_container.setContainerSize_(NSSize(width, INFINITE)) - else: - self.text_container.setContainerSize_(NSSize(INFINITE, INFINITE)) - self.width = width - - def update_paragraph_style(self): - attr = NSParagraphStyleAttributeName - value = self.paragraph_style.copy() - rnge = NSMakeRange(0, self.text_storage.length()) - self.text_storage.addAttribute_value_range_(attr, value, rnge) - - def set_wrap_style(self, wrap): - if wrap == 'word': - self.paragraph_style.setLineBreakMode_(NSLineBreakByWordWrapping) - elif wrap == 'char': - self.paragraph_style.setLineBreakMode_(NSLineBreakByCharWrapping) - elif wrap == 'truncated-char': - self.paragraph_style.setLineBreakMode_(NSLineBreakByTruncatingTail) - else: - raise ValueError("Unknown wrap value: %s" % wrap) - self.update_paragraph_style() - - def set_alignment(self, align): - if align == 'left': - self.paragraph_style.setAlignment_(NSLeftTextAlignment) - elif align == 'right': - self.paragraph_style.setAlignment_(NSRightTextAlignment) - elif align == 'center': - self.paragraph_style.setAlignment_(NSCenterTextAlignment) - else: - raise ValueError("Unknown align value: %s" % align) - self.update_paragraph_style() - - def get_size(self): - # The next line is there just to force cocoa to layout the text - self.layout_manager.glyphRangeForTextContainer_(self.text_container) - rect = self.layout_manager.usedRectForTextContainer_(self.text_container) - return rect.size.width, rect.size.height - - def char_at(self, x, y): - width, height = self.get_size() - if 0 <= x < width and 0 <= y < height: - index, _ = self.layout_manager.glyphIndexForPoint_inTextContainer_fractionOfDistanceThroughGlyph_(NSPoint(x, y), self.text_container, None) - return index - else: - return None - - def draw(self, context, x, y, width, height): - if self.shadow is not None: - context.save() - context.set_shadow(self.shadow.color, self.shadow.opacity, self.shadow.offset, self.shadow.blur_radius) - self.width = width - self.text_container.setContainerSize_(NSSize(width, height)) - glyph_range = self.layout_manager.glyphRangeForTextContainer_(self.text_container) - self.layout_manager.drawGlyphsForGlyphRange_atPoint_(glyph_range, NSPoint(x, y)) - if self.shadow is not None: - context.restore() - context.path.removeAllPoints() - -class NativeButton(object): - - def __init__(self, text, font, pressed, disabled=False): - self.min_width = 0 - self.cell = NSButtonCell.alloc().init() - self.cell.setBezelStyle_(NSRoundRectBezelStyle) - self.cell.setButtonType_(NSMomentaryPushInButton) - self.cell.setFont_(font.nsfont) - self.cell.setEnabled_(not disabled) - self.cell.setTitle_(text) - if pressed: - self.cell.setState_(NSOnState) - else: - self.cell.setState_(NSOffState) - self.cell.setImagePosition_(NSImageLeft) - - def set_icon(self, icon): - image = icon.image.copy() - image.setFlipped_(NO) - self.cell.setImage_(image) - - def get_size(self): - size = self.cell.cellSize() - return size.width, size.height - - def draw(self, context, x, y, width, height): - rect = NSMakeRect(x, y, width, height) - NSGraphicsContext.currentContext().saveGraphicsState() - self.cell.drawWithFrame_inView_(rect, context.view) - NSGraphicsContext.currentContext().restoreGraphicsState() - context.path.removeAllPoints() - -class StyledButton(object): - PAD_HORIZONTAL = 11 - BIG_PAD_VERTICAL = 4 - SMALL_PAD_VERTICAL = 2 - TOP_COLOR = (1, 1, 1) - BOTTOM_COLOR = (0.86, 0.86, 0.86) - LINE_COLOR_TOP = (0.71, 0.71, 0.71) - LINE_COLOR_BOTTOM = (0.45, 0.45, 0.45) - TEXT_COLOR = (0.19, 0.19, 0.19) - DISABLED_COLOR = (0.86, 0.86, 0.86) - DISABLED_TEXT_COLOR = (0.43, 0.43, 0.43) - ICON_PAD = 8 - - def __init__(self, text, font, pressed, disabled=False): - self.pressed = pressed - self.disabled = disabled - attributes = NSMutableDictionary.alloc().init() - attributes.setObject_forKey_(font.nsfont, NSFontAttributeName) - if self.disabled: - color = self.DISABLED_TEXT_COLOR - else: - color = self.TEXT_COLOR - nscolor = NSColor.colorWithDeviceRed_green_blue_alpha_(color[0], color[1], color[2], 1.0) - attributes.setObject_forKey_(nscolor, NSForegroundColorAttributeName) - self.title = NSAttributedString.alloc().initWithString_attributes_(text, attributes) - self.image = None - - def set_icon(self, icon): - self.image = icon.image.copy() - self.image.setFlipped_(YES) - - def get_size(self): - width, height = self.get_text_size() - if self.image is not None: - width += self.image.size().width + self.ICON_PAD - height = max(height, self.image.size().height) - height += self.BIG_PAD_VERTICAL * 2 - else: - height += self.SMALL_PAD_VERTICAL * 2 - if height % 2 == 1: - # make height even so that the radius of our circle is whole - height += 1 - width += self.PAD_HORIZONTAL * 2 - return width, height - - def get_text_size(self): - size = self.title.size() - return size.width, size.height - - def draw(self, context, x, y, width, height): - self._draw_button(context, x, y, width, height) - self._draw_title(context, x, y) - context.path.removeAllPoints() - - def _draw_button(self, context, x, y, width, height): - radius = height / 2 - self._draw_path(context, x, y, width, height, radius) - if self.disabled: - end_color = self.DISABLED_COLOR - start_color = self.DISABLED_COLOR - elif self.pressed: - end_color = self.TOP_COLOR - start_color = self.BOTTOM_COLOR - else: - context.set_line_width(1) - start_color = self.TOP_COLOR - end_color = self.BOTTOM_COLOR - gradient = drawing.Gradient(x, y, x, y+height) - gradient.set_start_color(start_color) - gradient.set_end_color(end_color) - context.gradient_fill(gradient) - self._draw_border(context, x, y, width, height, radius) - - def _draw_path(self, context, x, y, width, height, radius): - inner_width = width - radius * 2 - context.move_to(x + radius, y) - context.rel_line_to(inner_width, 0) - context.arc(x + width - radius, y+radius, radius, -math.pi/2, math.pi/2) - context.rel_line_to(-inner_width, 0) - context.arc(x + radius, y+radius, radius, math.pi/2, -math.pi/2) - - def _draw_path_reverse(self, context, x, y, width, height, radius): - inner_width = width - radius * 2 - context.move_to(x + radius, y) - context.arc_negative(x + radius, y+radius, radius, -math.pi/2, math.pi/2) - context.rel_line_to(inner_width, 0) - context.arc_negative(x + width - radius, y+radius, radius, math.pi/2, -math.pi/2) - context.rel_line_to(-inner_width, 0) - - def _draw_border(self, context, x, y, width, height, radius): - self._draw_path(context, x, y, width, height, radius) - self._draw_path_reverse(context, x+1, y+1, width-2, height-2, radius-1) - gradient = drawing.Gradient(x, y, x, y+height) - gradient.set_start_color(self.LINE_COLOR_TOP) - gradient.set_end_color(self.LINE_COLOR_BOTTOM) - context.save() - context.clip() - context.rectangle(x, y, width, height) - context.gradient_fill(gradient) - context.restore() - - def _draw_title(self, context, x, y): - c_width, c_height = self.get_size() - t_width, t_height = self.get_text_size() - x = x + self.PAD_HORIZONTAL - y = y + (c_height - t_height) / 2 - if self.image is not None: - self.image.drawAtPoint_fromRect_operation_fraction_( - NSPoint(x, y+3), NSZeroRect, NSCompositeSourceOver, 1.0) - x += self.image.size().width + self.ICON_PAD - else: - y += 0.5 - self.title.drawAtPoint_(NSPoint(x, y)) |