class ButtonBase(Control): align = 'c' action = None rightClickAction = None default_choice_color = ThemeProperty('default_choice_color') default_choice_bg_color = ThemeProperty('default_choice_bg_color') def mouse_down(self, event): button = event.button if self.enabled and button == 1: self._highlighted = True def mouse_drag(self, event): state = event in self if event.buttons[0] == 1 and state != self._highlighted: self._highlighted = state self.invalidate() def mouse_up(self, event): button = event.button if event in self and button == 1: if self is event.clicked_widget or ( event.clicked_widget and self in event.clicked_widget.all_parents()): self._highlighted = False if self.enabled: self.call_handler('action') if event in self and button == 3 and self.enabled: if self is event.clicked_widget or ( event.clicked_widget and self in event.clicked_widget.all_parents()): self.call_handler('rightClickAction') self.get_root().fix_sticky_ctrl()
class ButtonBase(Control): align = 'c' action = None default_choice_color = ThemeProperty('default_choice_color') default_choice_bg_color = ThemeProperty('default_choice_bg_color') def mouse_down(self, event): if self.enabled: self._highlighted = True def mouse_drag(self, event): state = event in self if state != self._highlighted: self._highlighted = state self.invalidate() def mouse_up(self, event): if event in self: if self is event.clicked_widget or ( event.clicked_widget and self in event.clicked_widget.all_parents()): self._highlighted = False if self.enabled: self.call_handler('action')
class CheckWidget(Widget): default_size = (16, 16) margin = 4 border_width = 1 check_mark_tweak = 2 if __builtins__.get("mcenf_tab_to_next"): tab_stop = True highlight_color = ThemeProperty('highlight_color') smooth = ThemeProperty('smooth') def __init__(self, **kwds): Widget.__init__(self, Rect((0, 0), self.default_size), **kwds) def draw(self, surf): if self.highlighted: r = self.get_margin_rect() fg = self.fg_color d = self.check_mark_tweak p1 = (r.left, r.centery - d) p2 = (r.centerx - d, r.bottom) p3 = (r.right, r.top - d) if self.smooth: draw.aalines(surf, fg, False, [p1, p2, p3]) else: draw.lines(surf, fg, False, [p1, p2, p3])
class Multichoice(PaletteView, Control): highlight_color = ThemeProperty('highlight_color') cell_margin = ThemeProperty('cell_margin') #highlight_style = 'frame' align = 'c' tab_stop = True def __init__(self, cell_size, values, **kwds): PaletteView.__init__(self, cell_size, 1, len(values), **kwds) self.values = values def num_items(self): return len(self.values) def item_is_selected(self, n): return self.get_value() == self.values[n] def click_item(self, n, e): if self.tab_stop: self.focus() self.set_value(self.values[n]) def draw(self, surf): if self.has_focus(): surf.fill(self.highlight_color) PaletteView.draw(self, surf) def key_down(self, e): k = e.key if k == K_LEFT: self.change_value(-1) elif k == K_RIGHT: self.change_value(1) else: PaletteView.key_down(self, e) def change_value(self, d): values = self.values if values: n = len(values) value = self.get_value() try: i = values.index(value) except ValueError: if d < 0: i = 0 else: i = n - 1 else: i = max(0, min(n - 1, i + d)) self.set_value(values[i])
class Image(Widget): # image Image to display highlight_color = ThemeProperty('highlight_color') image = overridable_property('image') highlighted = False def __init__(self, image=None, rect=None, **kwds): Widget.__init__(self, rect, **kwds) if image: if isinstance(image, basestring): image = resource.get_image(image) w, h = image.get_size() d = 2 * self.margin self.size = w + d, h + d self._image = image def get_image(self): return self._image def set_image(self, x): self._image = x def draw(self, surf): frame = surf.get_rect() if self.highlighted: surf.fill(self.highlight_color) image = self.image r = image.get_rect() r.center = frame.center surf.blit(image, r)
class ProgressBar(Control, Widget): bc = ThemeProperty('sel_color') center = 0 border_width = 1 def __init__(self, width=100, center=0, **kwds): Widget.__init__(self, Rect((0, 0), (width, 16)), **kwds) if (center > width): center = width / 2 self.center = center def draw(self, surf): frame = surf.get_rect() print self.value rect = Rect((self.center, 0), (self.value, frame.height)) draw.rect(surf, self.bc, rect, 0)
class TabPanel(Widget): # pages [Widget] # current_page Widget tab_font = FontProperty('tab_font') tab_height = ThemeProperty('tab_height') tab_border_width = ThemeProperty('tab_border_width') tab_spacing = ThemeProperty('tab_spacing') tab_margin = ThemeProperty('tab_margin') tab_fg_color = ThemeProperty('tab_fg_color') default_tab_bg_color = ThemeProperty('default_tab_bg_color') tab_area_bg_color = ThemeProperty('tab_area_bg_color') tab_dimming = ThemeProperty('tab_dimming') tab_titles = None #use_page_bg_color_for_tabs = ThemeProperty('use_page_bg_color_for_tabs') def __init__(self, pages=None, **kwds): Widget.__init__(self, **kwds) self.pages = [] self.current_page = None if pages: w = h = 0 for title, page in pages: w = max(w, page.width) h = max(h, page.height) self._add_page(title, page) self.size = (w, h) self.show_page(pages[0][1]) def content_size(self): return self.width, self.height - self.tab_height def content_rect(self): return Rect((0, self.tab_height), self.content_size()) def page_height(self): return self.height - self.tab_height def add_page(self, title, page): self._add_page(title, page) if not self.current_page: self.show_page(page) def _add_page(self, title, page): page.tab_title = _(title) page.anchor = 'ltrb' self.pages.append(page) def remove_page(self, page): try: i = self.pages.index(page) del self.pages[i] except IndexError: pass if page is self.current_page: self.show_page(None) def show_page(self, page): if self.current_page: self.remove(self.current_page) self.current_page = page if page: th = self.tab_height page.rect = Rect(0, th, self.width, self.height - th) self.add(page) page.focus() def draw(self, surf): self.draw_tab_area_bg(surf) self.draw_tabs(surf) def draw_tab_area_bg(self, surf): bg = self.tab_area_bg_color if bg: surf.fill(bg, (0, 0, self.width, self.tab_height)) def draw_tabs(self, surf): font = self.tab_font fg = self.tab_fg_color b = self.tab_border_width if b: surf.fill(fg, (0, self.tab_height - b, self.width, b)) for i, title, page, selected, rect in self.iter_tabs(): x0 = rect.left w = rect.width h = rect.height r = rect if not selected: r = Rect(r) r.bottom -= b self.draw_tab_bg(surf, page, selected, r) if b: surf.fill(fg, (x0, 0, b, h)) surf.fill(fg, (x0 + b, 0, w - 2 * b, b)) surf.fill(fg, (x0 + w - b, 0, b, h)) buf = font.render(title, True, page.fg_color or fg) r = buf.get_rect() r.center = (x0 + w // 2, h // 2) surf.blit(buf, r) def iter_tabs(self): pages = self.pages current_page = self.current_page n = len(pages) b = self.tab_border_width s = self.tab_spacing h = self.tab_height m = self.tab_margin width = self.width - 2 * m + s - b x0 = m for i, page in enumerate(pages): x1 = m + (i + 1) * width // n # self.tab_boundary(i + 1) selected = page is current_page yield i, page.tab_title, page, selected, Rect( x0, 0, x1 - x0 - s + b, h) x0 = x1 def draw_tab_bg(self, surf, page, selected, rect): bg = self.tab_bg_color_for_page(page) if not selected: bg = brighten(bg, self.tab_dimming) surf.fill(bg, rect) def tab_bg_color_for_page(self, page): return getattr(page, 'tab_bg_color', None) \ or page.bg_color \ or self.default_tab_bg_color def mouse_down(self, e): x, y = e.local if y < self.tab_height: i = self.tab_number_containing_x(x) if i is not None: self.show_page(self.pages[i]) def tab_number_containing_x(self, x): n = len(self.pages) m = self.tab_margin width = self.width - 2 * m + self.tab_spacing - self.tab_border_width i = (x - m) * n // width if 0 <= i < n: return i def gl_draw_self(self, root, offset): self.gl_draw(root, offset) def gl_draw(self, root, offset): pages = self.pages if len(pages) > 1: tlcorner = (offset[0] + self.bottomleft[0], offset[1] + self.bottomleft[1]) pageTabContents = [] current_page = self.current_page n = len(pages) b = self.tab_border_width s = self.tab_spacing h = self.tab_height m = self.tab_margin tabWidth = (self.size[0] - (s * n) - (2 * m)) / n width = self.width - 2 * m + s - b x0 = m + tlcorner[0] font = self.tab_font fg = self.tab_fg_color surface = Surface(self.size, SRCALPHA) glEnable(GL_BLEND) for i, page in enumerate(pages): x1 = x0 + tabWidth selected = page is current_page if selected: glColor(1.0, 1.0, 1.0, 0.5) else: glColor(0.5, 0.5, 0.5, 0.5) glRectf(x0, tlcorner[1] - (m + b), x1, tlcorner[1] - (h)) buf = font.render(self.pages[i].tab_title, True, self.fg_color or fg) r = buf.get_rect() offs = ((tabWidth - r.size[0]) / 2) + m + ((s + tabWidth) * i) surface.blit(buf, (offs, m)) x0 = x1 + s data = image.tostring(surface, 'RGBA', 1) rect = self.rect.move(offset) w, h = root.size glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(0, w, 0, h) glMatrixMode(GL_MODELVIEW) glLoadIdentity() glRasterPos2i(rect.left, h - rect.bottom) glPushAttrib(GL_COLOR_BUFFER_BIT) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glDrawPixels(self.width, self.height, GL_RGBA, GL_UNSIGNED_BYTE, fromstring(data, dtype='uint8')) glPopAttrib() glFlush() glDisable(GL_BLEND)
class Tree(Column): """...""" rows = [] row_margin = 2 column_margin = 2 bullet_size = ThemeProperty('bullet_size') bullet_color_active = ThemeProperty('bullet_color_active') bullet_color_inactive = ThemeProperty('bullet_color_inactive') def __init__(self, *args, **kwargs): self.menu = [("Add", "add_item"), ("Delete", "delete_item"), ("New child", "add_child"), ("Rename", "rename_item"), ("", ""), ("Cut", "cut_item"), ("Copy", "copy_item"), ("Paste", "paste_item"), ("Paste as child", "paste_child"), ] if not hasattr(self, 'map_types_item'): global map_types_item self.map_types_item = setup_map_types_item() self.selected_item_index = None self.selected_item = None self.clicked_item = None self.copyBuffer = kwargs.pop('copyBuffer', None) self._parent = kwargs.pop('_parent', None) self.styles = kwargs.pop('styles', {}) self.compound_types = [dict,] + kwargs.pop('compound_types', []) self.item_types = self.compound_types + kwargs.pop('item_types', [a[0] for a in self.map_types_item.values()] or [int, float, unicode, bool]) for t in self.item_types: if 'create_%s'%t.__name__ in globals().keys(): setattr(self, 'create_%s'%t.__name__, globals()['create_%s'%t.__name__]) self.show_fields = kwargs.pop('show_fields', False) self.deployed = [] self.data = data = kwargs.pop("data", {}) self.draw_zebra = draw_zebra = kwargs.pop('draw_zebra', True) # self.inner_width = kwargs.pop('inner_width', 'auto') self.inner_width = kwargs.pop('inner_width', 500) self.__num_rows = len(data.keys()) self.build_layout() # row_height = self.font.size(' ')[1] row_height = self.font.get_linesize() self.treeRow = treeRow = TreeRow((self.inner_width, row_height), 10, draw_zebra=draw_zebra) Column.__init__(self, [treeRow,], **kwargs) def dispatch_key(self, name, evt): if not hasattr(evt, 'key'): return if name == "key_down": keyname = self.root.getKey(evt) if keyname == "Up" and self.selected_item_index > 0: if self.selected_item_index == None: self.selected_item_index = -1 self.selected_item_index = max(self.selected_item_index - 1, 0) elif keyname == "Down" and self.selected_item_index < len(self.rows) - 1: if self.selected_item_index == None: self.selected_item_index = -1 self.selected_item_index += 1 elif keyname == 'Page down': if self.selected_item_index == None: self.selected_item_index = -1 self.selected_item_index = min(len(self.rows) - 1, self.selected_item_index + self.treeRow.num_rows()) elif keyname == 'Page up': if self.selected_item_index == None: self.selected_item_index = -1 self.selected_item_index = max(0, self.selected_item_index - self.treeRow.num_rows()) if self.treeRow.cell_to_item_no(0, 0) != None and (self.treeRow.cell_to_item_no(0, 0) + self.treeRow.num_rows() -1 > self.selected_item_index or self.treeRow.cell_to_item_no(0, 0) + self.treeRow.num_rows() -1 < self.selected_item_index): self.treeRow.scroll_to_item(self.selected_item_index) if keyname == 'Return' and self.selected_item_index != None: self.select_item(self.selected_item_index) if self.selected_item[7] in self.compound_types: self.deploy(self.selected_item[6]) def cut_item(self): self.copyBuffer = ([] + self.selected_item, 1) self.delete_item() def copy_item(self): self.copyBuffer = ([] + self.selected_item, 0) def paste_item(self): parent = self.get_item_parent(self.selected_item) name = self.copyBuffer[0][3] old_name = u"%s"%self.copyBuffer[0][3] if self.copyBuffer[1] == 0: name = input_text_buttons("Choose a name", 300, self.copyBuffer[0][3]) else: old_name = "" if name and type(name) in (str, unicode) and name != old_name: new_item = copy.deepcopy(self.copyBuffer[0][9]) if hasattr(new_item, 'name'): new_item.name = name self.add_item_to(parent, (name, new_item)) def paste_child(self): name = self.copyBuffer[0][3] old_name = u"%s"%self.copyBuffer[0][3] names = [] children = self.get_item_children(self.selected_item) if children: names = [a[3] for a in children] if name in names: name = input_text_buttons("Choose a name", 300, self.copyBuffer[0][3]) else: old_name = "" if name and type(name) in (str, unicode) and name != old_name: new_item = copy.deepcopy(self.copyBuffer[0][9]) if hasattr(new_item, 'name'): new_item.name = name self.add_item_to(self.selected_item, (name, new_item)) @staticmethod def add_item_to_dict(parent, name, item): parent[name] = item def add_item_to(self, parent, (name, item)): if parent is None: tp = 'dict' parent = self.data else: tp = parent[7].__name__ parent = parent[9] if not name: i = 0 name = 'Item %03d'%i while name in self.data.keys(): i += 1 name = 'Item %03d'%i meth = getattr(self, 'add_item_to_%s'%tp, None) if meth: meth(parent, name, item) self.build_layout() else: alert(_("No function implemented to add items to %s type.")%type(parent).__name__, doNotTranslate=True)
class Widget(gvent.GventReceiver): logger = logging.getLogger("GyUI.Widget") font = FontProperty('font') fg_color = ThemeProperty('fg_color') bg_color = ThemeProperty('bg_color') bg_image = ThemeProperty('bg_image') scale_bg = ThemeProperty('scale_bg') border_width = ThemeProperty('border_width') border_color = ThemeProperty('border_color') sel_color = ThemeProperty('sel_color') margin = ThemeProperty('margin') menu_bar = overridable_property('menu_bar') is_gl_container = overridable_property('is_gl_container') # rect properties accesses left = rect_property('left') right = rect_property('right') top = rect_property('top') bottom = rect_property('bottom') width = rect_property('width') height = rect_property('height') size = rect_property('size') topleft = rect_property('topleft') topright = rect_property('topright') bottomleft = rect_property('bottomleft') bottomright = rect_property('bottomright') midleft = rect_property('midleft') midright = rect_property('midright') midtop = rect_property('midtop') midbottom = rect_property('midbottom') center = rect_property('center') centerx = rect_property('centerx') centery = rect_property('centery') # local_rect properties accesses local_left = local_rect_property('left') local_right = local_rect_property('right') local_top = local_rect_property('top') local_bottom = local_rect_property('bottom') local_width = local_rect_property('width') local_height = local_rect_property('height') local_size = local_rect_property('size') local_topleft = local_rect_property('topleft') local_topright = local_rect_property('topright') local_bottomleft = local_rect_property('bottomleft') local_bottomright = local_rect_property('bottomright') local_midleft = local_rect_property('midleft') local_midright = local_rect_property('midright') local_midtop = local_rect_property('midtop') local_midbottom = local_rect_property('midbottom') local_center = local_rect_property('center') local_centerx = local_rect_property('centerx') local_centery = local_rect_property('centery') def __init__(self, rect=None): ''' Constructor for Widget ''' gvent.GventReceiver.__init__(self) self.dirty = 1 self.parent = None if rect is None: self._rect = pygame.Rect((0, 0), (100, 100)) elif rect: self._rect = pygame.Rect(rect) # self._rect = rect self._local_rect = pygame.Rect((0, 0), (100, 100)) def get_rect(self): ''' Getter for rect property ''' return self._rect def set_rect(self, rect): ''' Setter for rect property ''' old_rect = self._rect self._rect = pygame.Rect(rect) self._rect_global_to_local() self._moved(old_rect.topleft) self._resized(old_rect.size) rect = property(get_rect, set_rect) def get_local_rect(self): ''' Getter for local_rect property ''' return self._local_rect def set_local_rect(self, rect): ''' Setter for local_rect property ''' old_rect = self._local_rect self._local_rect = pygame.Rect(rect) self._rect_local_to_global() self._moved(old_rect.topleft) self._resized(old_rect.size) local_rect = property(get_local_rect, set_local_rect) def _rect_global_to_local(self): ''' Set local rect from global rect ''' if self.parent: move_by = (self.parent.left * -1, self.parent.top * -1) self._local_rect = self._rect.move(move_by) else: self._local_rect = self._rect print self, "new local_rect:", self._local_rect def _rect_local_to_global(self): ''' Set global rect from local rect ''' if self.parent: self._rect = self._local_rect.move(self.parent.topleft) else: self._rect = self._local_rect def _resized(self, old_size): ''' Inform widget that it's size has changed ''' if self._rect.size != tuple(old_size): # new size is different pass def _moved(self, old_pos): if self._rect.topleft != tuple(old_pos): # new pos is actually different self.set_dirty() def _parent_moved(self, old_pos): # recalculate local rect with new parent position self._rect_local_to_global() def _set_parent(self, parent): self.parent = parent # self._parent_resized(None) self._parent_moved(None) def get_widget_at(self, pos): ''' Get widged at that (global) position This is mainly useful as recursion end from Bin widgets ''' return self def _draw(self, surface): ''' Execute actual draw of this very widget ''' pygame.draw.rect(surface, (0xff, 0, 0), self.rect) def draw(self, surface): ''' ''' self._draw(surface) def set_dirty(self): ''' Set this widget to be redrawn next draw ''' if not self.dirty: self.dirty = 1 def set_dirty_all(self): self.set_dirty()
class Label(Widget): text = overridable_property('text') align = overridable_property('align') hover_color = ThemeProperty('hover_color') highlight_color = ThemeProperty('highlight_color') disabled_color = ThemeProperty('disabled_color') highlight_bg_color = ThemeProperty('highlight_bg_color') hover_bg_color = ThemeProperty('hover_bg_color') enabled_bg_color = ThemeProperty('enabled_bg_color') disabled_bg_color = ThemeProperty('disabled_bg_color') enabled = True highlighted = False _align = 'l' def __init__(self, text, width=None, base_text=None, **kwds): #-# Translation live update preparation # base_text: to be used each time a widget takes a formated string # defaults to 'text'. Widget.__init__(self, **kwds) #-# Translation live update preparation self.fixed_width = width self.base_text = base_text or text self.previous_translation = _(text, doNotTranslate=kwds.get( 'doNotTranslate', False)) #-# self._text = _(text, doNotTranslate=kwds.get('doNotTranslate', False)) #-# self.calc_size() #-# #-# Translation live update preparation def calc_size(self): lines = self._text.split("\n") tw, th = 0, 0 for i in range(len(lines)): line = lines[i] if i == len(lines) - 1: w, h = self.font.size(line) else: w, h = self.font.size(line)[0], self.font.get_linesize() tw = max(tw, w) th += h if self.fixed_width is not None: tw = self.fixed_width else: tw = max(1, tw) d = 2 * self.margin self.size = (tw + d, th + d) def get_update_translation(self): return Widget.update_translation(self) def set_update_ui(self, v): self.text = self.base_text self.set_text(self.base_text) self.calc_size() Widget.set_update_ui(self, v) #-# def __repr__(self): return "Label {0}, child of {1}".format(self.text, self.parent) def get_text(self): return self._text def set_text(self, x, doNotTranslate=False): self._text = _(x, doNotTranslate=doNotTranslate or self.doNotTranslate) self.calc_size() def get_align(self): return self._align def set_align(self, x): self._align = x def draw(self, surface): if not self.enabled: fg = self.disabled_color bg = self.disabled_bg_color else: fg = self.fg_color bg = self.enabled_bg_color if self.is_default: fg = self.default_choice_color or fg bg = self.default_choice_bg_color or bg if self.is_hover: fg = self.hover_color or fg bg = self.hover_bg_color or bg if self.highlighted: fg = self.highlight_color or fg bg = self.highlight_bg_color or bg self.draw_with(surface, fg, bg) is_default = False def draw_with(self, surface, fg, bg=None): if bg: r = surface.get_rect() b = self.border_width if b: e = -2 * b r.inflate_ip(e, e) surface.fill(bg, r) m = self.margin align = self.align width = surface.get_width() y = m lines = self.text.split("\n") font = self.font dy = font.get_linesize() for line in lines: if len(line): size = font.size(line) if size[0] == 0: continue image = font.render(line, True, fg) r = image.get_rect() r.top = y if align == 'l': r.left = m elif align == 'r': r.right = width - m else: r.centerx = width // 2 surface.blit(image, r) y += dy
class PaletteView(GridView): # nrows int No. of displayed rows # ncols int No. of displayed columns # # Abstract methods: # # num_items() --> no. of items # draw_item(surface, item_no, rect) # click_item(item_no, event) # item_is_selected(item_no) --> bool sel_width = ThemeProperty('sel_width') zebra_color = ThemeProperty('zebra_color') scroll_button_size = ThemeProperty('scroll_button_size') scroll_button_color = ThemeProperty('scroll_button_color') highlight_style = ThemeProperty('highlight_style') # 'frame' or 'fill' or 'reverse' or None def __init__(self, cell_size, nrows, ncols, scrolling=False, **kwds): GridView.__init__(self, cell_size, nrows, ncols, **kwds) self.scrolling = scrolling if scrolling: d = self.scroll_button_size #l = self.width #b = self.height self.width += d #self.scroll_up_rect = Rect(l, 0, d, d).inflate(-4, -4) #self.scroll_down_rect = Rect(l, b - d, d, d).inflate(-4, -4) self.scroll = 0 self.dragging_hover = False self.scroll_rel = 0 def scroll_up_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.top = m r.right = self.width - m r.inflate_ip(-4, -4) return r def scroll_down_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.bottom = self.height - m r.right = self.width - m r.inflate_ip(-4, -4) return r def draw(self, surface): GridView.draw(self, surface) u = False d = False if self.can_scroll_up(): u = True self.draw_scroll_up_button(surface) if self.can_scroll_down(): d = True self.draw_scroll_down_button(surface) if u or d: self.draw_scrollbar(surface) def draw_scroll_up_button(self, surface): r = self.scroll_up_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.bottomleft, r.midtop, r.bottomright]) def draw_scroll_down_button(self, surface): r = self.scroll_down_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.topleft, r.midbottom, r.topright]) def draw_cell(self, surface, row, col, rect): i = self.cell_to_item_no(row, col) if i is not None: highlight = self.item_is_selected(i) self.draw_item_and_highlight(surface, i, rect, highlight) def draw_item_and_highlight(self, surface, i, rect, highlight): if not i % 2 and "Header" not in str(type(self)): surface.fill(self.zebra_color, rect) if highlight: self.draw_prehighlight(surface, i, rect) if highlight and self.highlight_style == 'reverse': fg = self.inherited('bg_color') or self.sel_color else: fg = self.fg_color self.draw_item_with(surface, i, rect, fg) if highlight: self.draw_posthighlight(surface, i, rect) def draw_item_with(self, surface, i, rect, fg): old_fg = self.fg_color self.fg_color = fg try: self.draw_item(surface, i, rect) finally: self.fg_color = old_fg def draw_prehighlight(self, surface, i, rect): if self.highlight_style == 'reverse': color = self.fg_color else: color = self.sel_color self.draw_prehighlight_with(surface, i, rect, color) def draw_prehighlight_with(self, surface, i, rect, color): style = self.highlight_style if style == 'frame': frame_rect(surface, color, rect, self.sel_width) elif style == 'fill' or style == 'reverse': surface.fill(color, rect) def draw_posthighlight(self, surface, i, rect): pass def mouse_down(self, event): if event.button == 1: if self.scrolling: p = event.local if self.scrollbar_rect().collidepoint(p): self.dragging_hover = True return elif self.scroll_up_rect().collidepoint(p): self.scroll_up() return elif self.scroll_down_rect().collidepoint(p): self.scroll_down() return if event.button == 4: self.scroll_up() if event.button == 5: self.scroll_down() GridView.mouse_down(self, event) def mouse_drag(self, event): if self.dragging_hover: self.scroll_rel += event.rel[1] sub = self.scroll_up_rect().bottom t = self.scroll_down_rect().top d = t - sub # Get the total row number (n). n = self.num_items() / getattr(getattr(self, 'parent', None), 'num_cols', lambda: 1)() # Get the displayed row number (v) s = float(d) / n if abs(self.scroll_rel) >= s: if self.scroll_rel > 0: self.scroll_down(delta=int(abs(self.scroll_rel) / s)) else: self.scroll_up(delta=int(abs(self.scroll_rel) / s)) self.scroll_rel = 0 def mouse_up(self, event): if event.button == 1: if self.dragging_hover: self.dragging_hover = False self.scroll_rel = 0 def scroll_up(self, delta=1): if self.can_scroll_up(): self.scroll -= delta def scroll_down(self, delta=1): if self.can_scroll_down(): self.scroll += delta def scroll_to_item(self, n): i = max(0, min(n, self.num_items() - 1)) p = self.items_per_page() self.scroll = p * (i // p) def can_scroll_up(self): return self.scrolling and self.scroll > 0 def can_scroll_down(self): return self.scrolling and self.scroll + self.items_per_page() < self.num_items() def items_per_page(self): return self.num_rows() * self.num_cols() def click_cell(self, row, col, event): i = self.cell_to_item_no(row, col) if i is not None: self.click_item(i, event) def cell_to_item_no(self, row, col): i = self.scroll + row * self.num_cols() + col if 0 <= i < self.num_items(): return i else: return None def num_rows(self): ch = self.cell_size[1] if ch: return self.height // ch else: return 0 def num_cols(self): width = self.width if self.scrolling: width -= self.scroll_button_size cw = self.cell_size[0] if cw: return width // cw else: return 0 def item_is_selected(self, n): return False def click_item(self, n, e): pass def scrollbar_rect(self): # Get the distance between the scroll buttons (d) sub = self.scroll_up_rect().bottom t = self.scroll_down_rect().top d = t - sub # Get the total row number (n). n = self.num_items() / getattr(getattr(self, 'parent', None), 'num_cols', lambda: 1)() # Get the displayed row number (v) v = self.num_rows() if n: s = float(d) / n else: s = 0 h = s * v if type(h) == float: if h - int(h) > 0: h += 1 top = max(sub, sub + (s * self.scroll) + self.scroll_rel) r = Rect(0, top, self.scroll_button_size, h) m = self.margin r.right = self.width - m r.bottom = min(r.bottom, t) r.inflate_ip(-4, -4) if r.h < 1: r.h = int(h) return r def draw_scrollbar(self, surface): r = self.scrollbar_rect() c = map(lambda x: min(255, max(0, x + 10)), self.scroll_button_color) draw.rect(surface, c, r)
class Menu(Dialog): disabled_color = ThemeProperty('disabled_color') click_outside_response = -1 scroll_button_size = ThemeProperty('scroll_button_size') scroll_button_color = ThemeProperty('scroll_button_color') scroll = 0 def __init__(self, title, items, scrolling=False, scroll_items=30, scroll_page=5, **kwds): self.title = _(title) self.items = items self._items = [MenuItem(*item) for item in items] self.scrolling = scrolling and len(self._items) > scroll_items self.scroll_items = scroll_items self.scroll_page = scroll_page Dialog.__init__(self, **kwds) h = self.font.get_linesize() if self.scrolling: self.height = h * self.scroll_items + h else: self.height = h * len(self._items) + h def present(self, client, pos): client = client or get_root() self.topleft = client.local_to_global(pos) focus = get_focus() font = self.font h = font.get_linesize() items = self._items margin = self.margin if self.scrolling: height = h * self.scroll_items + h else: height = h * len(items) + h w1 = w2 = 0 for item in items: item.enabled = self.command_is_enabled(item, focus) w1 = max(w1, font.size(item.text)[0]) w2 = max(w2, font.size(item.keyname)[0]) width = w1 + 2 * margin self._key_margin = width if w2 > 0: width += w2 + margin if self.scrolling: width += self.scroll_button_size self.size = (width, height) self._hilited = None root = get_root() self.rect.clamp_ip(root.rect) return Dialog.present(self, centered=False) def command_is_enabled(self, item, focus): cmd = item.command if cmd: enabler_name = cmd + '_enabled' handler = focus while handler: enabler = getattr(handler, enabler_name, None) if enabler: return enabler() handler = handler.next_handler() return True def scroll_up_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.top = m r.right = self.width - m r.inflate_ip(-4, -4) return r def scroll_down_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.bottom = self.height - m r.right = self.width - m r.inflate_ip(-4, -4) return r def draw(self, surf): font = self.font h = font.get_linesize() sep = surf.get_rect() sep.height = 1 if self.scrolling: sep.width -= self.margin + self.scroll_button_size colors = [self.disabled_color, self.fg_color] bg = self.bg_color xt = self.margin xk = self._key_margin y = h // 2 hilited = self._hilited if self.scrolling: items = self._items[self.scroll:self.scroll + self.scroll_items] else: items = self._items for item in items: text = item.text if not text: sep.top = y + h // 2 surf.fill(colors[0], sep) else: if item is hilited: rect = surf.get_rect() rect.top = y rect.height = h if self.scrolling: rect.width -= xt + self.scroll_button_size surf.fill(colors[1], rect) color = bg else: color = colors[item.enabled] buf = font.render(item.text, True, color) surf.blit(buf, (xt, y)) keyname = item.keyname if keyname: buf = font.render(keyname, True, color) surf.blit(buf, (xk, y)) y += h if self.scrolling: if self.can_scroll_up(): self.draw_scroll_up_button(surf) if self.can_scroll_down(): self.draw_scroll_down_button(surf) def draw_scroll_up_button(self, surface): r = self.scroll_up_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.bottomleft, r.midtop, r.bottomright]) def draw_scroll_down_button(self, surface): r = self.scroll_down_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.topleft, r.midbottom, r.topright]) def mouse_move(self, e): self.mouse_drag(e) def mouse_drag(self, e): item = self.find_enabled_item(e) if item is not self._hilited: self._hilited = item self.invalidate() def mouse_up(self, e): if 1 <= e.button <= 3: item = self.find_enabled_item(e) if item: self.dismiss(self._items.index(item)) def find_enabled_item(self, e): x, y = e.local if 0 <= x < (self.width - self.margin - self.scroll_button_size if self.scrolling else self.width): h = self.font.get_linesize() i = (y - h // 2) // h + self.scroll items = self._items if 0 <= i < len(items): item = items[i] if item.enabled: return item def mouse_down(self, event): if event.button == 1: if self.scrolling: p = event.local if self.scroll_up_rect().collidepoint(p): self.scroll_up() return elif self.scroll_down_rect().collidepoint(p): self.scroll_down() return if event.button == 4: self.scroll_up() if event.button == 5: self.scroll_down() Dialog.mouse_down(self, event) def scroll_up(self): if self.can_scroll_up(): self.scroll = max(self.scroll - self.scroll_page, 0) def scroll_down(self): if self.can_scroll_down(): self.scroll = min(self.scroll + self.scroll_page, len(self._items) - self.scroll_items) def can_scroll_up(self): return self.scrolling and self.scroll > 0 def can_scroll_down(self): return (self.scrolling and self.scroll + self.scroll_items < len(self._items)) def find_item_for_key(self, e): for item in self._items: if item.keycode == e.key \ and item.shift == e.shift and item.alt == e.alt: focus = get_focus() if self.command_is_enabled(item, focus): return self._items.index(item) else: return -1 return -1 def get_command(self, i): if i >= 0: item = self._items[i] cmd = item.command if cmd: return cmd + '_cmd' def invoke_item(self, i): cmd = self.get_command(i) if cmd: get_focus().handle_command(cmd)
class Menu(Dialog): disabled_color = ThemeProperty('disabled_color') click_outside_response = -1 scroll_button_size = ThemeProperty('scroll_button_size') scroll_button_color = ThemeProperty('scroll_button_color') scroll = 0 def __init__(self, title, items, scrolling=False, scroll_items=30, scroll_page=5, handler=None, **kwds): self.handler = handler self.title = _(title, doNotTranslate=kwds.get('doNotTranslate', False)) self.items = items self._items = [ MenuItem(*item, doNotTranslate=kwds.get('doNotTranslate', False)) for item in items ] self.scrolling = scrolling and len(self._items) > scroll_items self.scroll_items = scroll_items self.scroll_page = scroll_page if __builtins__.get("mcenf_tab_to_next"): self._selected_item_index = 0 self._hilited = self._items[self._selected_item_index] Dialog.__init__(self, **kwds) self.root = self.get_root() self.calc_size() #&# def set_items(self, items): self.items = items self._items = [ MenuItem(*item, doNotTranslate=self.doNotTranslate) for item in items ] def set_scroll_items(self, scroll_items): if scroll_items < len(self.items): self.scroll_items = scroll_items else: self.scroll_items = len(self.items) self.calc_size() #&# def calc_size(self): h = self.font.get_linesize() if self.scrolling: self.height = h * self.scroll_items + h else: self.height = h * len(self._items) + h def set_update_ui(self, v): if v: self._items = [ MenuItem(*item, doNotTranslate=self.doNotTranslate) for item in self.items ] Dialog.set_update_ui(self, v) def present(self, client, pos): client = client or self.root self.topleft = client.local_to_global(pos) focus = self.handler or get_focus() font = self.font h = font.get_linesize() items = self._items margin = self.margin if self.scrolling: height = h * self.scroll_items + h else: height = h * len(items) + h w1 = w2 = 0 for item in items: item.enabled = self.command_is_enabled(item, focus) w1 = max(w1, font.size(item.text)[0]) w2 = max(w2, font.size(item.keyname)[0]) width = w1 + 2 * margin self._key_margin = width if w2 > 0: width += w2 + margin if self.scrolling: width += self.scroll_button_size self.size = (width, height) if not __builtins__.get("mcenf_tab_to_next"): self._hilited = None self.rect.clamp_ip(self.root.rect) return Dialog.present(self, centered=False) @staticmethod def command_is_enabled(item, focus): cmd = item.command if cmd: enabler_name = cmd + '_enabled' handler = focus while handler: enabler = getattr(handler, enabler_name, None) if enabler: return enabler() handler = handler.next_handler() return True def scroll_up_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.top = m r.right = self.width - m r.inflate_ip(-4, -4) return r def scroll_down_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.bottom = self.height - m r.right = self.width - m r.inflate_ip(-4, -4) return r def draw(self, surf): font = self.font h = font.get_linesize() sep = surf.get_rect() sep.height = 1 if self.scrolling: sep.width -= self.margin + self.scroll_button_size colors = [self.disabled_color, self.fg_color] bg = self.bg_color xt = self.margin xk = self._key_margin y = h // 2 hilited = self._hilited if self.scrolling: items = self._items[self.scroll:self.scroll + self.scroll_items] else: items = self._items for item in items: text = item.text if not text: sep.top = y + h // 2 surf.fill(colors[0], sep) else: if item is hilited: rect = surf.get_rect() rect.top = y rect.height = h if self.scrolling: rect.width -= xt + self.scroll_button_size surf.fill(colors[1], rect) color = bg else: color = colors[item.enabled] buf = font.render(item.text, True, color) surf.blit(buf, (xt, y)) keyname = item.keyname if keyname: buf = font.render(keyname, True, color) surf.blit(buf, (xk, y)) y += h if self.scrolling: if self.can_scroll_up(): self.draw_scroll_up_button(surf) if self.can_scroll_down(): self.draw_scroll_down_button(surf) def draw_scroll_up_button(self, surface): r = self.scroll_up_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.bottomleft, r.midtop, r.bottomright]) def draw_scroll_down_button(self, surface): r = self.scroll_down_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.topleft, r.midbottom, r.topright]) def mouse_move(self, e): self.mouse_drag(e) def mouse_drag(self, e): item = self.find_enabled_item(e) if item is not self._hilited: self._hilited = item self.invalidate() if __builtins__.get("mcenf_tab_to_next"): if item: self._selected_item_index = self._items.index(item) def mouse_up(self, e): if 1 <= e.button <= 3: item = self.find_enabled_item(e) if item: self.dismiss(self._items.index(item)) def find_enabled_item(self, e): x, y = e.local if 0 <= x < (self.width - self.margin - self.scroll_button_size if self.scrolling else self.width): h = self.font.get_linesize() i = (y - h // 2) // h + self.scroll items = self._items if 0 <= i < len(items): item = items[i] if item.enabled: return item def mouse_down(self, event): if event.button == 1: if self.scrolling: p = event.local if self.scroll_up_rect().collidepoint(p): self.scroll_up() return elif self.scroll_down_rect().collidepoint(p): self.scroll_down() return if event.button == 4: self.scroll_up() if event.button == 5: self.scroll_down() Dialog.mouse_down(self, event) def scroll_up(self): if self.can_scroll_up(): self.scroll = max(self.scroll - self.scroll_page, 0) def scroll_down(self): if self.can_scroll_down(): self.scroll = min(self.scroll + self.scroll_page, len(self._items) - self.scroll_items) def can_scroll_up(self): return self.scrolling and self.scroll > 0 def can_scroll_down(self): return (self.scrolling and self.scroll + self.scroll_items < len(self._items)) def find_item_for_key(self, e): for item in self._items: if item.keycode == e.key \ and item.shift == e.shift and item.alt == e.alt: focus = get_focus() if self.command_is_enabled(item, focus): return self._items.index(item) else: return -1 return -1 def get_command(self, i): if i >= 0: item = self._items[i] cmd = item.command if cmd: return cmd + '_cmd' def invoke_item(self, i): cmd = self.get_command(i) if cmd: get_focus().handle_command(cmd) if __builtins__.get("mcenf_tab_to_next"): def key_down(self, event): """Handles key presses to select and activate menu items or dismiss it. :param event: object: The event to be processed.""" key = self.root.getKey(event) last_index = len(self._items) - 1 # Just define a dummy method when scrolling is not necessary. def _x(*args, **kwargs): """...""" pass def page_up_down(*args, **kwargs): """Triggers scroll alignment to see the selected item on page up/down event.""" self.scroll = min(self._selected_item_index, last_index - self.scroll_items + 1) view_meth = _x if key == "Up": if self._selected_item_index == 0: self._selected_item_index = last_index self.scroll = last_index - self.scroll_items + 1 else: self._selected_item_index -= 1 view_meth = self.scroll_up elif key == "Down": if self._selected_item_index == last_index: self._selected_item_index = 0 self.scroll = 0 else: self._selected_item_index += 1 view_meth = self.scroll_down elif key == "Page up": self._selected_item_index = max( 0, self._selected_item_index - self.scroll_items) view_meth = page_up_down elif key == "Page down": self._selected_item_index = min( last_index, self._selected_item_index + self.scroll_items) view_meth = page_up_down elif key in ("Return", "Enter", "Space"): self.dismiss(self._selected_item_index) elif key == "Escape": self.dismiss(False) else: Dialog.key_down(self, event) self._hilited = self._items[self._selected_item_index] # Ensure the selected item is visible by scrollign accordingly if self._hilited not in self._items[self.scroll:self.scroll + self.scroll_items]: view_meth()
class PaletteView(GridView): # nrows int No. of displayed rows # ncols int No. of displayed columns # # Abstract methods: # # num_items() --> no. of items # draw_item(surface, item_no, rect) # click_item(item_no, event) # item_is_selected(item_no) --> bool sel_width = ThemeProperty('sel_width') zebra_color = ThemeProperty('zebra_color') scroll_button_size = ThemeProperty('scroll_button_size') scroll_button_color = ThemeProperty('scroll_button_color') highlight_style = ThemeProperty('highlight_style') # 'frame' or 'fill' or 'reverse' or None def __init__(self, cell_size, nrows, ncols, scrolling=False, **kwds): GridView.__init__(self, cell_size, nrows, ncols, **kwds) self.scrolling = scrolling if scrolling: d = self.scroll_button_size #l = self.width #b = self.height self.width += d #self.scroll_up_rect = Rect(l, 0, d, d).inflate(-4, -4) #self.scroll_down_rect = Rect(l, b - d, d, d).inflate(-4, -4) self.scroll = 0 def scroll_up_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.top = m r.right = self.width - m r.inflate_ip(-4, -4) return r def scroll_down_rect(self): d = self.scroll_button_size r = Rect(0, 0, d, d) m = self.margin r.bottom = self.height - m r.right = self.width - m r.inflate_ip(-4, -4) return r def draw(self, surface): GridView.draw(self, surface) if self.can_scroll_up(): self.draw_scroll_up_button(surface) if self.can_scroll_down(): self.draw_scroll_down_button(surface) def draw_scroll_up_button(self, surface): r = self.scroll_up_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.bottomleft, r.midtop, r.bottomright]) def draw_scroll_down_button(self, surface): r = self.scroll_down_rect() c = self.scroll_button_color draw.polygon(surface, c, [r.topleft, r.midbottom, r.topright]) def draw_cell(self, surface, row, col, rect): i = self.cell_to_item_no(row, col) if i is not None: highlight = self.item_is_selected(i) self.draw_item_and_highlight(surface, i, rect, highlight) def draw_item_and_highlight(self, surface, i, rect, highlight): if i % 2: surface.fill(self.zebra_color, rect) if highlight: self.draw_prehighlight(surface, i, rect) if highlight and self.highlight_style == 'reverse': fg = self.inherited('bg_color') or self.sel_color else: fg = self.fg_color self.draw_item_with(surface, i, rect, fg) if highlight: self.draw_posthighlight(surface, i, rect) def draw_item_with(self, surface, i, rect, fg): old_fg = self.fg_color self.fg_color = fg try: self.draw_item(surface, i, rect) finally: self.fg_color = old_fg def draw_prehighlight(self, surface, i, rect): if self.highlight_style == 'reverse': color = self.fg_color else: color = self.sel_color self.draw_prehighlight_with(surface, i, rect, color) def draw_prehighlight_with(self, surface, i, rect, color): style = self.highlight_style if style == 'frame': frame_rect(surface, color, rect, self.sel_width) elif style == 'fill' or style == 'reverse': surface.fill(color, rect) def draw_posthighlight(self, surface, i, rect): pass def mouse_down(self, event): if event.button == 1: if self.scrolling: p = event.local if self.scroll_up_rect().collidepoint(p): self.scroll_up() return elif self.scroll_down_rect().collidepoint(p): self.scroll_down() return if event.button == 4: self.scroll_up() if event.button == 5: self.scroll_down() GridView.mouse_down(self, event) def scroll_up(self): if self.can_scroll_up(): self.scroll -= self.items_per_page() / 2 def scroll_down(self): if self.can_scroll_down(): self.scroll += self.items_per_page() / 2 def scroll_to_item(self, n): i = max(0, min(n, self.num_items() - 1)) p = self.items_per_page() self.scroll = p * (i // p) def can_scroll_up(self): return self.scrolling and self.scroll > 0 def can_scroll_down(self): return self.scrolling and self.scroll + self.items_per_page( ) < self.num_items() def items_per_page(self): return self.num_rows() * self.num_cols() def click_cell(self, row, col, event): i = self.cell_to_item_no(row, col) if i is not None: self.click_item(i, event) def cell_to_item_no(self, row, col): i = self.scroll + row * self.num_cols() + col if 0 <= i < self.num_items(): return i else: return None def num_rows(self): ch = self.cell_size[1] if ch: return self.height // ch else: return 0 def num_cols(self): width = self.width if self.scrolling: width -= self.scroll_button_size cw = self.cell_size[0] if cw: return width // cw else: return 0 def item_is_selected(self, n): return False def click_item(self, n, e): pass
class Label(Widget): text = overridable_property('text') align = overridable_property('align') hover_color = ThemeProperty('hover_color') highlight_color = ThemeProperty('highlight_color') disabled_color = ThemeProperty('disabled_color') highlight_bg_color = ThemeProperty('highlight_bg_color') hover_bg_color = ThemeProperty('hover_bg_color') enabled_bg_color = ThemeProperty('enabled_bg_color') disabled_bg_color = ThemeProperty('disabled_bg_color') enabled = True highlighted = False _align = 'l' def __init__(self, text, width=None, **kwds): Widget.__init__(self, **kwds) font = self.font text = _(text, doNotTranslate=kwds.get('doNotTranslate', False)) lines = text.split("\n") tw, th = 0, 0 for line in lines: w, h = font.size(line) tw = max(tw, w) th += h if width is not None: tw = width else: tw = max(1, tw) d = 2 * self.margin self.size = (tw + d, th + d) self._text = text def __repr__(self): return "Label {0}, child of {1}".format(self.text, self.parent) def get_text(self): return self._text def set_text(self, x): self._text = _(x) def get_align(self): return self._align def set_align(self, x): self._align = x def draw(self, surface): if not self.enabled: fg = self.disabled_color bg = self.disabled_bg_color else: fg = self.fg_color bg = self.enabled_bg_color if self.is_default: fg = self.default_choice_color or fg bg = self.default_choice_bg_color or bg if self.is_hover: fg = self.hover_color or fg bg = self.hover_bg_color or bg if self.highlighted: fg = self.highlight_color or fg bg = self.highlight_bg_color or bg self.draw_with(surface, fg, bg) is_default = False def draw_with(self, surface, fg, bg=None): if bg: r = surface.get_rect() b = self.border_width if b: e = -2 * b r.inflate_ip(e, e) surface.fill(bg, r) m = self.margin align = self.align width = surface.get_width() y = m lines = self.text.split("\n") font = self.font dy = font.get_linesize() for line in lines: if len(line): size = font.size(line) if size[0] == 0: continue image = font.render(line, True, fg) r = image.get_rect() r.top = y if align == 'l': r.left = m elif align == 'r': r.right = width - m else: r.centerx = width // 2 surface.blit(image, r) y += dy
class Label(Widget): text = overridable_property('text') align = overridable_property('align') highlight_color = ThemeProperty('highlight_color') disabled_color = ThemeProperty('disabled_color') highlight_bg_color = ThemeProperty('highlight_bg_color') enabled_bg_color = ThemeProperty('enabled_bg_color') disabled_bg_color = ThemeProperty('disabled_bg_color') enabled = True highlighted = False _align = 'l' def __init__(self, text, width = None, **kwds): Widget.__init__(self, **kwds) font = self.font self.set_text(text) return lines = text.split("\n") tw, th = 0, 0 for line in lines: w, h = font.size(line) tw = max(tw, w) th += h if width is not None: tw = width else: tw = max(1, tw) d = 2 * self.margin self.size = (tw + d, th + d) self._text = text def get_text(self): return self._text def _render_image(self): ''' Render image to be blited on draw ''' line_height = self.font.get_linesize() lines = self._text.split("\n") fg = self.fg_color bg = self.bg_color line_images = [] line_width = 0 for line in lines: print bg img = self.font.render(line, True, fg) line_width = max(line_width, img.get_width()) line_images.append(img) space = self.border_width + self.margin self.width = 2*space + line_width self.height = 2*space + len(line_images)*line_height self._image = pygame.Surface(self.size) if bg: self._image.fill() dest = pygame.Rect((0,0),(self.width,line_height)) for img in line_images: dest.width = img.get_width() if self.align=='c': dest.centerx = self.width/2 elif self.align=='r': dest.right = self.width # else: # dest.left = 0 # blit it self._image.blit(img, dest) dest.top += dest.height def set_text(self, text): self._text = text self._render_image() def get_align(self): return self._align def set_align(self, x): self._align = x self._render_image() def draw(self, surface): surface.blit(self._image, self.rect) def draw_with(self, surface, fg, bg = None): if bg: r = surface.get_rect() b = self.border_width if b: e = - 2 * b r.inflate_ip(e, e) surface.fill(bg, r) m = self.margin align = self.align width = surface.get_width() y = m lines = self.text.split("\n") font = self.font dy = font.get_linesize() for line in lines: image = font.render(line, True, fg) r = image.get_rect() r.top = y if align == 'l': r.left = m elif align == 'r': r.right = width - m else: r.centerx = width // 2 surface.blit(image, r) y += dy
class Label(Widget): text = overridable_property('text') align = overridable_property('align') highlight_color = ThemeProperty('highlight_color') disabled_color = ThemeProperty('disabled_color') highlight_bg_color = ThemeProperty('highlight_bg_color') enabled_bg_color = ThemeProperty('enabled_bg_color') disabled_bg_color = ThemeProperty('disabled_bg_color') enabled = True highlighted = False _align = 'l' def __init__(self, text, width = None, **kwds): Widget.__init__(self, **kwds) font = self.font lines = text.split("\n") tw, th = 0, 0 for line in lines: w, h = font.size(line) tw = max(tw, w) th += h if width is not None: tw = width else: tw = max(1, tw) d = 2 * self.margin self.size = (tw + d, th + d) self._text = text def get_text(self): return self._text def set_text(self, x): self._text = x def get_align(self): return self._align def set_align(self, x): self._align = x def draw(self, surface): if not self.enabled: fg = self.disabled_color bg = self.disabled_bg_color elif self.highlighted: fg = self.highlight_color bg = self.highlight_bg_color else: fg = self.fg_color bg = self.enabled_bg_color self.draw_with(surface, fg, bg) def draw_with(self, surface, fg, bg = None): if bg: r = surface.get_rect() b = self.border_width if b: e = - 2 * b r.inflate_ip(e, e) surface.fill(bg, r) m = self.margin align = self.align width = surface.get_width() y = m lines = self.text.split("\n") font = self.font dy = font.get_linesize() for line in lines: image = font.render(line, True, fg) r = image.get_rect() r.top = y if align == 'l': r.left = m elif align == 'r': r.right = width - m else: r.centerx = width // 2 surface.blit(image, r) y += dy
class Menu(Dialog): disabled_color = ThemeProperty('disabled_color') click_outside_response = -1 def __init__(self, title, items, **kwds): self.title = title self.items = items self._items = [MenuItem(*item) for item in items] Dialog.__init__(self, **kwds) def present(self, client, pos): client = client or get_root() self.topleft = client.local_to_global(pos) focus = get_focus() font = self.font h = font.get_linesize() items = self._items margin = self.margin height = h * len(items) + h w1 = w2 = 0 for item in items: item.enabled = self.command_is_enabled(item, focus) w1 = max(w1, font.size(item.text)[0]) w2 = max(w2, font.size(item.keyname)[0]) width = w1 + 2 * margin self._key_margin = width if w2 > 0: width += w2 + margin self.size = (width, height) self._hilited = None return Dialog.present(self, centered=False) def command_is_enabled(self, item, focus): cmd = item.command if cmd: enabler_name = cmd + '_enabled' handler = focus while handler: enabler = getattr(handler, enabler_name, None) if enabler: return enabler() handler = handler.next_handler() return True def draw(self, surf): font = self.font h = font.get_linesize() sep = surf.get_rect() sep.height = 1 colors = [self.disabled_color, self.fg_color] bg = self.bg_color xt = self.margin xk = self._key_margin y = h // 2 hilited = self._hilited for item in self._items: text = item.text if not text: sep.top = y + h // 2 surf.fill(colors[0], sep) else: if item is hilited: rect = surf.get_rect() rect.top = y rect.height = h surf.fill(colors[1], rect) color = bg else: color = colors[item.enabled] buf = font.render(item.text, True, color) surf.blit(buf, (xt, y)) keyname = item.keyname if keyname: buf = font.render(keyname, True, color) surf.blit(buf, (xk, y)) y += h def mouse_move(self, e): self.mouse_drag(e) def mouse_drag(self, e): item = self.find_enabled_item(e) if item is not self._hilited: self._hilited = item self.invalidate() def mouse_up(self, e): item = self.find_enabled_item(e) if item: self.dismiss(self._items.index(item)) def find_enabled_item(self, e): x, y = e.local if 0 <= x < self.width: h = self.font.get_linesize() i = (y - h // 2) // h items = self._items if 0 <= i < len(items): item = items[i] if item.enabled: return item def find_item_for_key(self, e): for item in self._items: if item.keycode == e.key \ and item.shift == e.shift and item.alt == e.alt: focus = get_focus() if self.command_is_enabled(item, focus): return self._items.index(item) else: return -1 return -1 def get_command(self, i): if i >= 0: item = self._items[i] cmd = item.command if cmd: return cmd + '_cmd' def invoke_item(self, i): cmd = self.get_command(i) if cmd: get_focus().handle_command(cmd)
class Tree(Column): """...""" rows = [] row_margin = 2 column_margin = 2 bullet_size = ThemeProperty('bullet_size') bullet_color_active = ThemeProperty('bullet_color_active') bullet_color_inactive = ThemeProperty('bullet_color_inactive') def __init__(self, *args, **kwargs): self.menu = [("Add", "add_item"), ("Delete", "delete_item"), ("New child", "add_child"), ("Rename", "rename_item"), ] self.selected_item_index = None self.selected_item = None self._parent = kwargs.pop('_parent', None) self.styles = kwargs.pop('styles', {}) self.compound_types = [dict,] + kwargs.pop('compound_types', []) self.show_fields = kwargs.pop('show_fields', False) self.deployed = [] self.data = data = kwargs.pop("data", {}) self.draw_zebra = draw_zebra = kwargs.pop('draw_zebra', True) # self.inner_width = kwargs.pop('inner_width', 'auto') self.inner_width = kwargs.pop('inner_width', 500) self.__num_rows = len(data.keys()) self.build_layout() row_height = self.font.size(' ')[1] self.treeRow = treeRow = TreeRow((self.inner_width, row_height), 10, draw_zebra=draw_zebra) Column.__init__(self, [treeRow,], **kwargs) def add_item(self): print "add_item" def add_child(self): print "add_child" def delete_item(self): print "delete_item" def rename_item(self): print "rename_item" def show_menu(self, pos): if self.menu and self.selected_item_index: m = Menu("Menu", self.menu) i = m.present(self, pos) if i > -1: meth = getattr(self, self.menu[i][1], None) if meth: meth() def build_layout(self): data = self.data parent = 0 children = [] keys = data.keys() keys.sort() items = [[0, a, data[a], parent, children, keys.index(a) + 1] for a in keys] rows = [] w = 50 aId = len(items) + 1 while items: lvl, k, v, p, c, id = items.pop(0) _c = False fields = [] if type(v) in self.compound_types: meth = getattr(self, 'parse_%s'%v.__class__.__name__, None) if meth is not None: v = meth(k, v) ks = v.keys() ks.sort() ks.reverse() for a in ks: b = v[a] if id in self.deployed: itm = [lvl + 1, a, b, id, [], aId] items.insert(0, itm) c.append(aId) _c = True aId += 1 else: if type(v) in (list, tuple): fields = v elif type(v) not in self.compound_types or hasattr(self._parent, 'build_%s'%k.lower()): fields = [v,] head = Surface((self.bullet_size * (lvl + 1) + self.font.size(k)[0], self.bullet_size), SRCALPHA) if _c: meth = getattr(self, 'draw_%s_bullet'%{False: 'closed', True: 'opened'}[id in self.deployed]) else: meth = getattr(self, 'draw_%s_bullet'%v.__class__.__name__, None) if not meth: meth = self.draw_deadend_bullet bg, fg, shape, text = self.styles.get(type(v), ({True: self.bullet_color_active, False: self.bullet_color_inactive}[_c], self.fg_color, 'square', ''), ) meth(head, bg, fg, shape, text, k, lvl) rows.append([head, fields, [w] * len(fields), k, p, c, id, type(v), lvl]) self.rows = rows def deploy(self, id): if id in self.deployed: self.deployed.remove(id) else: self.deployed.append(id) self.build_layout() def click_item(self, n, pos): """...""" row = self.rows[n] r = self.get_bullet_rect(row[0], row[8]) x = pos[0] if self.margin + r.left - self.treeRow.hscroll <= x <= self.margin + self.treeRow.margin + r.right - self.treeRow.hscroll: id = row[6] self.deploy(id) else: self.select_item(n) def select_item(self, n): self.selected_item_index = n self.selected_item = self.rows[n] def get_bullet_rect(self, surf, lvl): r = Rect(0, 0, self.bullet_size, self.bullet_size) r.left = self.bullet_size * lvl r.inflate_ip(-4, -4) return r def draw_item_text(self, surf, r, text): buf = self.font.render(unicode(text), True, self.fg_color) blit_in_rect(surf, buf, Rect(r.right, r.top, surf.get_width() - r.right, r.height), 'c') def draw_deadend_bullet(self, surf, bg, fg, shape, text, item_text, lvl): r = self.get_bullet_rect(surf, lvl) draw.polygon(surf, bg, [r.midtop, r.midright, r.midbottom, r.midleft]) self.draw_item_text(surf, r, item_text) def draw_closed_bullet(self, surf, bg, fg, shape, text, item_text, lvl): r = self.get_bullet_rect(surf, lvl) draw.polygon(surf, bg, [r.topleft, r.midright, r.bottomleft]) self.draw_item_text(surf, r, item_text) def draw_opened_bullet(self, surf, bg, fg, shape, text, item_text, lvl): r = self.get_bullet_rect(surf, lvl) draw.polygon(surf, bg, [r.topleft, r.midbottom, r.topright]) self.draw_item_text(surf, r, item_text) def draw_tree_cell(self, surf, i, data, cell_rect, column): """...""" if type(data) in (str, unicode): self.draw_text_cell(surf, i, data, cell_rect, 'l', self.font) else: self.draw_image_cell(surf, i, data, cell_rect, column) @staticmethod def draw_image_cell(surf, i, data, cell_rect, column): """...""" blit_in_rect(surf, data, cell_rect, 'l') def draw_text_cell(self, surf, i, data, cell_rect, align, font): buf = font.render(unicode(data), True, self.fg_color) blit_in_rect(surf, buf, cell_rect, align) def num_rows(self): return len(self.rows) def row_data(self, row): return self.rows[row] def column_info(self, row_data): m = self.column_margin d = 2 * m x = 0 for i in range(0,2): if i < 1: width = self.width data = row_data[i] yield i, x + m, width - d, None, data x += width if self.show_fields: for i in range(len(row_data[2])): width = 50 * (i + 1) data = row_data[2][i] if type(data) != (str, unicode): data = repr(data) yield i, x + m, width - d, None, data x += width
class Widget(object): # rect Rect bounds in parent's coordinates # parent Widget containing widget # subwidgets [Widget] contained widgets # focus_switch Widget subwidget to receive key events # fg_color color or None to inherit from parent # bg_color color to fill background, or None # visible boolean # border_width int width of border to draw around widget, or None # border_color color or None to use widget foreground color # tab_stop boolean stop on this widget when tabbing # anchor string of 'ltrb' font = FontProperty('font') fg_color = ThemeProperty('fg_color') bg_color = ThemeProperty('bg_color') bg_image = ThemeProperty('bg_image') scale_bg = ThemeProperty('scale_bg') border_width = ThemeProperty('border_width') border_color = ThemeProperty('border_color') sel_color = ThemeProperty('sel_color') margin = ThemeProperty('margin') menu_bar = overridable_property('menu_bar') is_gl_container = overridable_property('is_gl_container') tab_stop = False enter_response = None cancel_response = None anchor = 'ltwh' debug_resize = False _menubar = None _visible = True _is_gl_container = False redraw_every_event = True tooltip = None tooltipText = None doNotTranslate = False def __init__(self, rect=None, **kwds): if rect and not isinstance(rect, Rect): raise TypeError("Widget rect not a pygame.Rect") self._rect = Rect(rect or (0, 0, 100, 100)) self.parent = None self.subwidgets = [] self.focus_switch = None self.is_modal = False self.set(**kwds) self.root = self.get_root() def set(self, **kwds): for name, value in kwds.iteritems(): if not hasattr(self, name): raise TypeError("Unexpected keyword argument '%s'" % name) setattr(self, name, value) def get_rect(self): return self._rect def set_rect(self, x): old_size = self._rect.size self._rect = Rect(x) self._resized(old_size) # def get_anchor(self): # if self.hstretch: # chars ='lr' # elif self.hmove: # chars = 'r' # else: # chars = 'l' # if self.vstretch: # chars += 'tb' # elif self.vmove: # chars += 'b' # else: # chars += 't' # return chars # # def set_anchor(self, chars): # self.hmove = 'r' in chars and not 'l' in chars # self.vmove = 'b' in chars and not 't' in chars # self.hstretch = 'r' in chars and 'l' in chars # self.vstretch = 'b' in chars and 't' in chars # # anchor = property(get_anchor, set_anchor) resizing_axes = {'h': 'lr', 'v': 'tb'} resizing_values = {'': [0], 'm': [1], 's': [0, 1]} def set_resizing(self, axis, value): chars = self.resizing_axes[axis] anchor = self.anchor for c in chars: anchor = anchor.replace(c, '') for i in self.resizing_values[value]: anchor += chars[i] self.anchor = anchor + value def _resized(self, (old_width, old_height)): new_width, new_height = self._rect.size dw = new_width - old_width dh = new_height - old_height if dw or dh: self.resized(dw, dh)
class TabPanel(Widget): # pages [Widget] # current_page Widget tab_font = FontProperty('tab_font') tab_height = ThemeProperty('tab_height') tab_border_width = ThemeProperty('tab_border_width') tab_spacing = ThemeProperty('tab_spacing') tab_margin = ThemeProperty('tab_margin') tab_fg_color = ThemeProperty('tab_fg_color') default_tab_bg_color = ThemeProperty('default_tab_bg_color') tab_area_bg_color = ThemeProperty('tab_area_bg_color') tab_dimming = ThemeProperty('tab_dimming') #use_page_bg_color_for_tabs = ThemeProperty('use_page_bg_color_for_tabs') def __init__(self, pages=None, **kwds): Widget.__init__(self, **kwds) self.pages = [] self.current_page = None if pages: w = h = 0 for title, page in pages: w = max(w, page.width) h = max(h, page.height) self._add_page(title, page) self.size = (w, h) self.show_page(pages[0][1]) def content_size(self): return (self.width, self.height - self.tab_height) def content_rect(self): return Rect((0, self.tab_height), self.content_size()) def page_height(self): return self.height - self.tab_height def add_page(self, title, page): self._add_page(title, page) if not self.current_page: self.show_page(page) def _add_page(self, title, page): page.tab_title = title page.anchor = 'ltrb' self.pages.append(page) def remove_page(self, page): try: i = self.pages.index(page) del self.pages[i] except IndexError: pass if page is self.current_page: self.show_page(None) def show_page(self, page): if self.current_page: self.remove(self.current_page) self.current_page = page if page: th = self.tab_height page.rect = Rect(0, th, self.width, self.height - th) self.add(page) page.focus() def draw(self, surf): self.draw_tab_area_bg(surf) self.draw_tabs(surf) def draw_tab_area_bg(self, surf): bg = self.tab_area_bg_color if bg: surf.fill(bg, (0, 0, self.width, self.tab_height)) def draw_tabs(self, surf): font = self.tab_font fg = self.tab_fg_color b = self.tab_border_width if b: surf.fill(fg, (0, self.tab_height - b, self.width, b)) for i, title, page, selected, rect in self.iter_tabs(): x0 = rect.left w = rect.width h = rect.height r = rect if not selected: r = Rect(r) r.bottom -= b self.draw_tab_bg(surf, page, selected, r) if b: surf.fill(fg, (x0, 0, b, h)) surf.fill(fg, (x0 + b, 0, w - 2 * b, b)) surf.fill(fg, (x0 + w - b, 0, b, h)) buf = font.render(title, True, page.fg_color or fg) r = buf.get_rect() r.center = (x0 + w // 2, h // 2) surf.blit(buf, r) def iter_tabs(self): pages = self.pages current_page = self.current_page n = len(pages) b = self.tab_border_width s = self.tab_spacing h = self.tab_height m = self.tab_margin width = self.width - 2 * m + s - b x0 = m for i, page in enumerate(pages): x1 = m + (i + 1) * width // n #self.tab_boundary(i + 1) selected = page is current_page yield i, page.tab_title, page, selected, Rect( x0, 0, x1 - x0 - s + b, h) x0 = x1 def draw_tab_bg(self, surf, page, selected, rect): bg = self.tab_bg_color_for_page(page) if not selected: bg = brighten(bg, self.tab_dimming) surf.fill(bg, rect) def tab_bg_color_for_page(self, page): return getattr(page, 'tab_bg_color', None) \ or page.bg_color \ or self.default_tab_bg_color def mouse_down(self, e): x, y = e.local if y < self.tab_height: i = self.tab_number_containing_x(x) if i is not None: self.show_page(self.pages[i]) def tab_number_containing_x(self, x): n = len(self.pages) m = self.tab_margin width = self.width - 2 * m + self.tab_spacing - self.tab_border_width i = (x - m) * n // width if 0 <= i < n: return i