def load_graphics(self):
        theme = self.theme[self.get_path()]

        self._button = theme['image'].generate(theme['gui_color'],
                                               **self.get_batch('background'))

        self._label = Label(self.label,
                            font_name=theme['font'],
                            font_size=theme['font_size'],
                            color=theme['text_color'],
                            **self.get_batch('foreground'))
    def load_graphics(self):
        theme = self.theme[self.get_path()]

        self._button = theme['image'].generate(theme['gui_color'], **self.get_batch('background'))

        self._label = Label(self.label,
                            font_name=theme['font'],
                            font_size=theme['font_size'],
                            color=theme['text_color'],
                            **self.get_batch('foreground'))
class Button(TwoStateController, Viewer):
    def __init__(self, label="", is_pressed=False, on_press=None):
        TwoStateController.__init__(self, is_pressed=is_pressed, on_press=on_press)
        Viewer.__init__(self)

        self.label = label

        # graphics
        self._label = None
        self._button = None

    def change_state(self):
        self._is_pressed = not self._is_pressed
        self.reload()
        self.reset_size()
        self._on_press(self._is_pressed)

    def hit_test(self, x, y):
        return self.is_inside(x, y)

    def on_mouse_press(self, x, y, button, modifiers):
        self.change_state()

    def get_path(self):
        path = ['button']
        if self.is_pressed:
            path.append('down')
        else:
            path.append('up')
        return path

    def load_graphics(self):
        theme = self.theme[self.get_path()]

        self._button = theme['image'].generate(theme['gui_color'], **self.get_batch('background'))

        self._label = Label(self.label,
                            font_name=theme['font'],
                            font_size=theme['font_size'],
                            color=theme['text_color'],
                            **self.get_batch('foreground'))

    def unload_graphics(self):
        self._button.unload()
        self._label.unload()

    def compute_size(self):
        # Treat the height of the label as ascent + descent
        font = self._label.document.get_font()
        height = font.ascent - font.descent

        return self._button.get_needed_size(self._label.content_width, height)

    def layout(self):
        self._button.update(self.x, self.y, self.width, self.height)

        # centers the label on the middle of the button
        x, y, width, height = self._button.get_content_region()

        font = self._label.document.get_font()
        self._label.x = x + width/2 - self._label.content_width/2
        self._label.y = y + height/2 - font.ascent/2 - font.descent
        self._label.update()

    def delete(self):
        TwoStateController.delete(self)
        Viewer.delete(self)
class Button(TwoStateController, Viewer):
    def __init__(self, label="", is_pressed=False, on_press=None):
        TwoStateController.__init__(self,
                                    is_pressed=is_pressed,
                                    on_press=on_press)
        Viewer.__init__(self)

        self.label = label

        # graphics
        self._label = None
        self._button = None

    def change_state(self):
        self._is_pressed = not self._is_pressed
        self.reload()
        self.reset_size()
        self._on_press(self._is_pressed)

    def hit_test(self, x, y):
        return self.is_inside(x, y)

    def on_mouse_press(self, x, y, button, modifiers):
        self.change_state()

    def get_path(self):
        path = ['button']
        if self.is_pressed:
            path.append('down')
        else:
            path.append('up')
        return path

    def load_graphics(self):
        theme = self.theme[self.get_path()]

        self._button = theme['image'].generate(theme['gui_color'],
                                               **self.get_batch('background'))

        self._label = Label(self.label,
                            font_name=theme['font'],
                            font_size=theme['font_size'],
                            color=theme['text_color'],
                            **self.get_batch('foreground'))

    def unload_graphics(self):
        self._button.unload()
        self._label.unload()

    def compute_size(self):
        # Treat the height of the label as ascent + descent
        font = self._label.document.get_font()
        height = font.ascent - font.descent

        return self._button.get_needed_size(self._label.content_width, height)

    def layout(self):
        self._button.update(self.x, self.y, self.width, self.height)

        # centers the label on the middle of the button
        x, y, width, height = self._button.get_content_region()

        font = self._label.document.get_font()
        self._label.x = x + width / 2 - self._label.content_width / 2
        self._label.y = y + height / 2 - font.ascent / 2 - font.descent
        self._label.update()

    def delete(self):
        TwoStateController.delete(self)
        Viewer.delete(self)