class FoldingBox(SingleContainer): """Collapsible container""" def __init__(self, title, **kwargs): '''Create a folding box Keyword arguments: name -- unique widget identifier content -- child container collapsed -- if true, container folded initially ''' SingleContainer.__init__(self, **kwargs) self.shapes['topbar'] = Rectangle() self.elements['title'] = BasicLabel(title, font_size=8, color=(0, 0, 0, 255), anchor_x='left', anchor_y='center') self.content = kwargs.get('content', None) self._last_h = 15 self._collapsed = False self._top_h = 0 self.collapsed = kwargs.get('collapsed', False) def _get_title(self): return self.elements['title'].text def _set_title(self, title): self.elements['title'].text = title title = property(_get_title, _set_title) def _get_collapsed(self): return self._collapsed def _set_collapsed(self, collapsed): if collapsed != self._collapsed: self._collapsed = collapsed self._h, self._last_h = self._last_h, self._h for c in self.children: c.visible = not collapsed if self.theme: self.shapes['topbar'].patch = self.theme['folding_box'][( 'image_closed' if collapsed else 'image')] self.find_root().update_layout() collapsed = property(_get_collapsed, _set_collapsed) def update_theme(self, theme): SingleContainer.update_theme(self, theme) if theme: patch = theme['folding_box'][('image_closed' if self._collapsed else 'image')] self.shapes['topbar'].patch = patch self.elements['title'].font_name = theme['font'] self.elements['title'].font_size = theme['font_size_small'] self.elements['title'].color = theme['font_color'] if self._collapsed: self._h = patch.padding_top else: self._last_h = patch.padding_top self._top_h = patch.padding_top def update_elements(self): if self.theme: patch = self.theme['folding_box'][('image_closed' if self._collapsed else 'image')] self.shapes['topbar'].update( self._gx + patch.padding_left, self._gy + self.h - patch.padding_top, self.w - patch.padding_left - patch.padding_right, 1) self.elements['title'].x = self._gx + patch.padding_left self.elements[ 'title'].y = self._gy + self.h - patch.padding_top / 2 + 1 self.topbar = Rect(0, self.h - patch.padding_top, self.w, patch.padding_top) SingleContainer.update_elements(self) def determine_size(self): self._content.determine_size() size = self._content._pref_size if self.collapsed: self._pref_size = Size(size[0], self._top_h) else: self._pref_size = Size(size[0], size[1] + self._top_h) def reset_size(self, size): Widget.reset_size(self, size) if not self.collapsed: self._content.reset_size(Size(size.w, size.h - self._top_h)) def on_mouse_press(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and \ self.topbar.hit_test(x - self._gx, y - self._gy): self.collapsed = not self.collapsed return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED def clip_rect(self): return Rect(self._gx, self._gy, self.w, self.h - 15)
class Dialogue(SingleContainer): """Moveable window, optionally resizeable""" def __init__(self, title, **kwargs): '''Create a dialogue Keyword arguments: name -- unique widget identifier content -- child container ''' SingleContainer.__init__(self, **kwargs) self.resizeable = kwargs.get('resizeable', False) self.shapes['background'] = Rectangle() self.shapes['title_bar'] = Rectangle() self.elements['title'] = BasicLabel(title, anchor_x='center') self.topbar = Rect(0, 0, 0, 15) self._in_drag = False self.content = kwargs.get('content', None) def _get_title(self): return self.elements['title'].text def _set_title(self, title): self.elements['title'].text = title title = property(_get_title, _set_title) def update_theme(self, theme): SingleContainer.update_theme(self, theme) if theme: background_patch = theme['window']['image_background'] title_patch = theme['window']['image_title_bar'] self.shapes['background'].patch = background_patch self.shapes['title_bar'].patch = title_patch self.elements['title'].font_name = theme['font'] self.elements['title'].font_size = theme['font_size_small'] self.elements['title'].color = theme['font_color'] font = self.elements['title'].document.get_font() height = font.ascent - font.descent self._pref_size = Size( title_patch.padding_x + self.elements['title'].content_width, background_patch.padding_y + title_patch.padding_y + height) def update_elements(self): if self._dirty and self.theme: background = self.theme['window']['image_background'] title_bar = self.theme['window']['image_title_bar'] font = self.elements['title'].document.get_font() height = font.ascent - font.descent h = title_bar.padding_bottom + height + title_bar.padding_top self.shapes['background'].update(self._gx, self._gy, self.w, self.h) self.shapes['title_bar'].update_in( self._gx - background.padding_left, self._gy + self.h, self.w + background.padding_left + background.padding_right, h) self.elements['title'].x = self._gx + self.w / 2 self.topbar = Rect( -background.padding_left, self.h, self.w + background.padding_left + background.padding_right, h) self.elements[ 'title'].y = self._gy + self.h + title_bar.padding_bottom - font.descent SingleContainer.update_elements(self) def reset_size(self, size): self._y += self._h - size.h SingleContainer.reset_size(self, size) def bounds(self): return Rect(self._gx, self._gy, self.w, self.h + self.topbar.h) def on_mouse_press(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and \ self.topbar.hit_test(x - self.x, y - self.y): self._in_drag = True self._offset_x = x - self.x self._offset_y = y - self.y return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if button == pyglet.window.mouse.LEFT and self._in_drag: self._x = x - self._offset_x self._y = y - self._offset_y self.find_root().update_layout() return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_drag(self, x, y, dx, dy, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_release(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and self._in_drag: self._in_drag = False return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_release(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED
class Button(Widget): """Clickable button""" def __init__(self, text, **kwargs): '''Create a button control Keyword arguments: action -- callback to be invoked when the button is clicked ''' Widget.__init__(self, **kwargs) self.elements['label'] = BasicLabel(text, font_size=8, color=(0, 0, 0, 255), anchor_x='left', anchor_y='bottom') self.shapes['frame'] = Rectangle() self.active_region = Rect(0, 0, 0, 0) self.action = kwargs.get('action', None) self._down = False def _get_text(self): return self.elements['label'].text def _set_text(self, text): self.elements['label'].text = text text = property(_get_text, _set_text) def update_theme(self, theme): Widget.update_theme(self, theme) if theme: patch = theme['button'][('image_down' if self._down else 'image_up')] label = self.elements['label'] label.font_name = self.theme['font'] label.font_size = self.theme['font_size'] label.color = theme['font_color'] font = label.document.get_font() height = font.ascent - font.descent self._pref_size = Size( patch.padding_left + label.content_width + patch.padding_right, patch.padding_bottom + height + patch.padding_top) label.x = patch.padding_left + label.content_width / 2 label.y = patch.padding_bottom + height / 2 - font.descent self.shapes['frame'].patch = patch def update_elements(self): if self._dirty and self.theme: patch = self.theme['button'][('image_down' if self._down else 'image_up')] label = self.elements['label'] font = label.document.get_font() height = font.ascent - font.descent left = 0 if self.halign == 'center': left = self.w / 2 - self._pref_size[0] / 2 elif self.halign == 'right': left = self.w - self._pref_size[0] bottom = 0 if self.valign == 'center': bottom = self.h / 2 - self._pref_size[1] / 2 elif self.valign == 'top': bottom = self.h - self._pref_size[1] label.x = self._gx + left + patch.padding_left label.y = self._gy + bottom + patch.padding_bottom self.shapes['frame'].update( self._gx + left + patch.padding_left, self._gy + bottom + patch.padding_bottom, label.content_width, height) self.active_region = Rect(self._gx + left + patch.padding_left, self._gy + bottom + patch.padding_bottom, label.content_width, height) Widget.update_elements(self) def on_mouse_press(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and self.active_region.hit_test( x, y): self.shapes['frame'].patch = self.theme['button']['image_down'] self._down = True return pyglet.event.EVENT_HANDLED Widget.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if button == pyglet.window.mouse.LEFT and self._down: if self.active_region.hit_test(x, y): self.shapes['frame'].patch = self.theme['button']['image_down'] else: self.shapes['frame'].patch = self.theme['button']['image_up'] return pyglet.event.EVENT_HANDLED Widget.on_mouse_drag(self, x, y, dx, dy, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_release(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and self._down: self.shapes['frame'].patch = self.theme['button']['image_up'] self._down = False if self.active_region.hit_test(x, y): if self.action: self.action(self) return pyglet.event.EVENT_HANDLED Widget.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED
class Dialogue(SingleContainer): """Moveable window, optionally resizeable""" def __init__(self, title, **kwargs): '''Create a dialogue Keyword arguments: name -- unique widget identifier content -- child container ''' SingleContainer.__init__(self, **kwargs) self.resizeable = kwargs.get('resizeable', False) self.shapes['background'] = Rectangle() self.shapes['title_bar'] = Rectangle() self.elements['title'] = BasicLabel(title, anchor_x='center') self.topbar = Rect(0, 0, 0, 15) self._in_drag = False self.content = kwargs.get('content', None) def _get_title(self): return self.elements['title'].text def _set_title(self, title): self.elements['title'].text = title title = property(_get_title, _set_title) def update_theme(self, theme): SingleContainer.update_theme(self, theme) if theme: background_patch = theme['window']['image_background'] title_patch = theme['window']['image_title_bar'] self.shapes['background'].patch = background_patch self.shapes['title_bar'].patch = title_patch self.elements['title'].font_name = theme['font'] self.elements['title'].font_size = theme['font_size_small'] self.elements['title'].color = theme['font_color'] font = self.elements['title'].document.get_font() height = font.ascent - font.descent self._pref_size = Size(title_patch.padding_x + self.elements['title'].content_width, background_patch.padding_y + title_patch.padding_y + height) def update_elements(self): if self._dirty and self.theme: background = self.theme['window']['image_background'] title_bar = self.theme['window']['image_title_bar'] font = self.elements['title'].document.get_font() height = font.ascent - font.descent h = title_bar.padding_bottom + height + title_bar.padding_top self.shapes['background'].update(self._gx, self._gy, self.w, self.h) self.shapes['title_bar'].update_in(self._gx - background.padding_left, self._gy + self.h, self.w + background.padding_left + background.padding_right, h) self.elements['title'].x = self._gx + self.w/2 self.topbar = Rect(-background.padding_left, self.h, self.w + background.padding_left + background.padding_right, h) self.elements['title'].y = self._gy + self.h + title_bar.padding_bottom - font.descent SingleContainer.update_elements(self) def reset_size(self, size): self._y += self._h - size.h SingleContainer.reset_size(self, size) def bounds(self): return Rect(self._gx, self._gy, self.w, self.h + self.topbar.h) def on_mouse_press(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and \ self.topbar.hit_test(x - self.x, y - self.y): self._in_drag = True self._offset_x = x - self.x self._offset_y = y - self.y return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if button == pyglet.window.mouse.LEFT and self._in_drag: self._x = x - self._offset_x self._y = y - self._offset_y self.find_root().update_layout() return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_drag(self, x, y, dx, dy, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_release(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and self._in_drag: self._in_drag = False return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_release(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED
class Button(Widget): """Clickable button""" def __init__(self, text, **kwargs): '''Create a button control Keyword arguments: action -- callback to be invoked when the button is clicked ''' Widget.__init__(self, **kwargs) self.elements['label'] = BasicLabel(text, font_size=8, color=(0,0,0,255), anchor_x='left', anchor_y='bottom') self.shapes['frame'] = Rectangle() self.active_region = Rect(0, 0, 0, 0) self.action = kwargs.get('action', None) self._down = False def _get_text(self): return self.elements['label'].text def _set_text(self, text): self.elements['label'].text = text text = property(_get_text, _set_text) def update_theme(self, theme): Widget.update_theme(self, theme) if theme: patch = theme['button'][('image_down' if self._down else 'image_up')] label = self.elements['label'] label.font_name = self.theme['font'] label.font_size = self.theme['font_size'] label.color = theme['font_color'] font = label.document.get_font() height = font.ascent - font.descent self._pref_size = Size(patch.padding_left + label.content_width + patch.padding_right, patch.padding_bottom + height + patch.padding_top) label.x = patch.padding_left + label.content_width/2 label.y = patch.padding_bottom + height/2 - font.descent self.shapes['frame'].patch = patch def update_elements(self): if self._dirty and self.theme: patch = self.theme['button'][('image_down' if self._down else 'image_up')] label = self.elements['label'] font = label.document.get_font() height = font.ascent - font.descent left = 0 if self.halign == 'center': left = self.w/2 - self._pref_size[0]/2 elif self.halign == 'right': left = self.w - self._pref_size[0] bottom = 0 if self.valign == 'center': bottom = self.h/2 - self._pref_size[1]/2 elif self.valign == 'top': bottom = self.h - self._pref_size[1] label.x = self._gx + left + patch.padding_left label.y = self._gy + bottom + patch.padding_bottom self.shapes['frame'].update(self._gx + left + patch.padding_left, self._gy + bottom + patch.padding_bottom, label.content_width, height) self.active_region = Rect(self._gx + left + patch.padding_left, self._gy + bottom + patch.padding_bottom, label.content_width, height) Widget.update_elements(self) def on_mouse_press(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and self.active_region.hit_test(x, y): self.shapes['frame'].patch = self.theme['button']['image_down'] self._down = True return pyglet.event.EVENT_HANDLED Widget.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_drag(self, x, y, dx, dy, button, modifiers): if button == pyglet.window.mouse.LEFT and self._down: if self.active_region.hit_test(x, y): self.shapes['frame'].patch = self.theme['button']['image_down'] else: self.shapes['frame'].patch = self.theme['button']['image_up'] return pyglet.event.EVENT_HANDLED Widget.on_mouse_drag(self, x, y, dx, dy, button, modifiers) return pyglet.event.EVENT_UNHANDLED def on_mouse_release(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and self._down: self.shapes['frame'].patch = self.theme['button']['image_up'] self._down = False if self.active_region.hit_test(x, y): if self.action: self.action(self) return pyglet.event.EVENT_HANDLED Widget.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED
class FoldingBox(SingleContainer): """Collapsible container""" def __init__(self, title, **kwargs): '''Create a folding box Keyword arguments: name -- unique widget identifier content -- child container collapsed -- if true, container folded initially ''' SingleContainer.__init__(self, **kwargs) self.shapes['topbar'] = Rectangle() self.elements['title'] = BasicLabel(title, font_size=8, color=(0,0,0,255), anchor_x='left', anchor_y='center') self.content = kwargs.get('content', None) self._last_h = 15 self._collapsed = False self._top_h = 0 self.collapsed = kwargs.get('collapsed', False) def _get_title(self): return self.elements['title'].text def _set_title(self, title): self.elements['title'].text = title title = property(_get_title, _set_title) def _get_collapsed(self): return self._collapsed def _set_collapsed(self, collapsed): if collapsed != self._collapsed: self._collapsed = collapsed self._h, self._last_h = self._last_h, self._h for c in self.children: c.visible = not collapsed if self.theme: self.shapes['topbar'].patch = self.theme['folding_box'][('image_closed' if collapsed else 'image')] self.find_root().update_layout() collapsed = property(_get_collapsed, _set_collapsed) def update_theme(self, theme): SingleContainer.update_theme(self, theme) if theme: patch = theme['folding_box'][('image_closed' if self._collapsed else 'image')] self.shapes['topbar'].patch = patch self.elements['title'].font_name = theme['font'] self.elements['title'].font_size = theme['font_size_small'] self.elements['title'].color = theme['font_color'] if self._collapsed: self._h = patch.padding_top else: self._last_h = patch.padding_top self._top_h = patch.padding_top def update_elements(self): if self.theme: patch = self.theme['folding_box'][('image_closed' if self._collapsed else 'image')] self.shapes['topbar'].update(self._gx + patch.padding_left, self._gy + self.h - patch.padding_top, self.w - patch.padding_left - patch.padding_right, 1) self.elements['title'].x = self._gx + patch.padding_left self.elements['title'].y = self._gy + self.h - patch.padding_top/2 + 1 self.topbar = Rect(0, self.h-patch.padding_top, self.w, patch.padding_top) SingleContainer.update_elements(self) def determine_size(self): self._content.determine_size() size = self._content._pref_size if self.collapsed: self._pref_size = Size(size[0], self._top_h) else: self._pref_size = Size(size[0], size[1] + self._top_h) def reset_size(self, size): Widget.reset_size(self, size) if not self.collapsed: self._content.reset_size(Size(size.w, size.h - self._top_h)) def on_mouse_press(self, x, y, button, modifiers): if button == pyglet.window.mouse.LEFT and \ self.topbar.hit_test(x - self._gx, y - self._gy): self.collapsed = not self.collapsed return pyglet.event.EVENT_HANDLED SingleContainer.on_mouse_press(self, x, y, button, modifiers) return pyglet.event.EVENT_UNHANDLED def clip_rect(self): return Rect(self._gx, self._gy, self.w, self.h-15)