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 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 TextScreen(Screen): # bg_color = (0, 0, 0) # fg_color = (255, 255, 255) # border = 20 heading_font = FontProperty('heading_font') button_font = FontProperty('button_font') def __init__(self, shell, filename, **kwds): text = get_text(filename) text_pages = text.split("\nPAGE\n") pages = [] page_size = (0, 0) for text_page in text_pages: lines = text_page.strip().split("\n") page = Page(self, lines[0], lines[1:]) pages.append(page) page_size = maximum(page_size, page.size) self.pages = pages bf = self.button_font b1 = Button("Prev Page", font=bf, action=self.prev_page) b2 = Button("Menu", font=bf, action=self.go_back) b3 = Button("Next Page", font=bf, action=self.next_page) b = self.margin page_rect = Rect((b, b), page_size) gap = (0, 18) b1.topleft = add(page_rect.bottomleft, gap) b2.midtop = add(page_rect.midbottom, gap) b3.topright = add(page_rect.bottomright, gap) Screen.__init__(self, shell, **kwds) self.size = add(b3.bottomright, (b, b)) self.add(b1) self.add(b2) self.add(b3) self.prev_button = b1 self.next_button = b3 self.set_current_page(0) def draw(self, surface): b = self.margin self.pages[self.current_page].draw(surface, self.fg_color, (b, b)) def at_first_page(self): return self.current_page == 0 def at_last_page(self): return self.current_page == len(self.pages) - 1 def set_current_page(self, n): self.current_page = n self.prev_button.enabled = not self.at_first_page() self.next_button.enabled = not self.at_last_page() def prev_page(self): if not self.at_first_page(): self.set_current_page(self.current_page - 1) def next_page(self): if not self.at_last_page(): self.set_current_page(self.current_page + 1) def go_back(self): self.parent.show_menu()
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 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