Esempio n. 1
0
    def test_enable(self, _init_pygame: None, default_ui_manager: UIManager,
                    _display_surface_return_none: None):
        container = UIScrollingContainer(pygame.Rect(100, 100, 200, 200),
                                         manager=default_ui_manager)
        button_1 = UIButton(relative_rect=pygame.Rect(10, 10, 150, 30),
                            text="Test Button",
                            tool_tip_text="This is a test of the button's tool tip functionality.",
                            manager=default_ui_manager,
                            container=container)

        button_2 = UIButton(relative_rect=pygame.Rect(10, 50, 150, 30),
                            text="Test Button 2",
                            manager=default_ui_manager,
                            container=container)

        container.disable()
        container.enable()

        assert container.is_enabled is True
        assert button_1.is_enabled is True
        assert button_2.is_enabled is True

        # process a mouse button down event
        button_1.process_event(
            pygame.event.Event(pygame.MOUSEBUTTONDOWN, {'button': 1, 'pos': button_1.rect.center}))

        # process a mouse button up event
        button_1.process_event(
            pygame.event.Event(pygame.MOUSEBUTTONUP, {'button': 1, 'pos': button_1.rect.center}))

        button_1.update(0.01)

        assert button_1.check_pressed() is True
Esempio n. 2
0
    def test_check_pressed(self, _init_pygame: None, default_ui_manager: UIManager,
                           _display_surface_return_none: None):
        button = UIButton(relative_rect=pygame.Rect(10, 10, 150, 30),
                          text="Test Button",
                          tool_tip_text="This is a test of the button's tool tip functionality.",
                          manager=default_ui_manager)

        # process a mouse button down event
        button.process_event(pygame.event.Event(pygame.MOUSEBUTTONDOWN, {'button': 1, 'pos': (50, 25)}))

        # process a mouse button up event
        button.process_event(pygame.event.Event(pygame.MOUSEBUTTONUP, {'button': 1, 'pos': (50, 25)}))

        button.update(0.01)

        assert button.check_pressed() is True
Esempio n. 3
0
    def test_disable(self, _init_pygame: None, default_ui_manager: UIManager,
                     _display_surface_return_none: None):
        panel = UIPanel(relative_rect=pygame.Rect(0, 0, 150, 400),
                        starting_layer_height=5,
                        manager=default_ui_manager)
        button_1 = UIButton(
            relative_rect=pygame.Rect(10, 10, 150, 30),
            text="Test Button",
            tool_tip_text=
            "This is a test of the button's tool tip functionality.",
            manager=default_ui_manager,
            container=panel)

        button_2 = UIButton(relative_rect=pygame.Rect(10, 50, 150, 30),
                            text="Test Button 2",
                            manager=default_ui_manager,
                            container=panel)

        panel.disable()

        assert panel.is_enabled is False
        assert button_1.is_enabled is False
        assert button_2.is_enabled is False

        # process a mouse button down event
        button_1.process_event(
            pygame.event.Event(pygame.MOUSEBUTTONDOWN, {
                'button': 1,
                'pos': button_1.rect.center
            }))

        # process a mouse button up event
        button_1.process_event(
            pygame.event.Event(pygame.MOUSEBUTTONUP, {
                'button': 1,
                'pos': button_1.rect.center
            }))

        button_1.update(0.01)

        assert button_1.check_pressed() is False
Esempio n. 4
0
class UIClosedDropDownState:
    """
    The closed state of the drop down just displays the currently chosen option and a button that will switch the menu
    to the expanded state.
    """

    def __init__(self, drop_down_menu_ui, selected_option, base_position_rect,
                 open_button_width, expand_direction, manager, container, element_ids, object_ids):
        self.drop_down_menu_ui = drop_down_menu_ui
        self.selected_option_button = None
        self.open_button = None
        self.selected_option = selected_option
        self.base_position_rect = base_position_rect
        self.expand_direction = expand_direction
        self.ui_manager = manager
        self.ui_container = container
        self.element_ids = element_ids
        self.object_ids = object_ids

        self.shape_type = None
        self.drawable_shape = None

        self.open_button_width = open_button_width

        self.should_transition = False
        self.target_state = 'expanded'

    def rebuild(self):
        theming_parameters = {'normal_bg': self.drop_down_menu_ui.background_colour,
                              'normal_border': self.drop_down_menu_ui.border_colour,
                              'border_width': self.drop_down_menu_ui.border_width,
                              'shadow_width': self.drop_down_menu_ui.shadow_width,
                              'shape_corner_radius': self.drop_down_menu_ui.shape_corner_radius}

        if self.drop_down_menu_ui.shape_type == 'rectangle':
            self.drawable_shape = RectDrawableShape(self.drop_down_menu_ui.rect, theming_parameters,
                                                    ['normal'], self.ui_manager)
        elif self.drop_down_menu_ui.shape_type == 'rounded_rectangle':
            self.drawable_shape = RoundedRectangleShape(self.drop_down_menu_ui.rect, theming_parameters,
                                                        ['normal'], self.ui_manager)

        self.drop_down_menu_ui.image = self.drawable_shape.get_surface('normal')

        # extra
        if self.open_button is not None:
            expand_button_symbol = '▼'
            if self.expand_direction is not None:
                if self.expand_direction == 'up':
                    expand_button_symbol = '▲'
                elif self.expand_direction == 'down':
                    expand_button_symbol = '▼'
            self.open_button.set_text(expand_button_symbol)

    def start(self):
        """
        Called each time we enter the closed state. It creates the necessary elements, the selected option and the
        open button.
        """
        self.rebuild()

        self.should_transition = False
        self.selected_option_button = UIButton(pygame.Rect((self.base_position_rect.x,
                                                            self.base_position_rect.y),
                                                           (self.base_position_rect.width - self.open_button_width,
                                                            self.base_position_rect.height)),
                                               self.selected_option,
                                               self.ui_manager,
                                               self.ui_container,
                                               starting_height=2,
                                               parent_element=self.drop_down_menu_ui,
                                               object_id='#selected_option')
        open_button_x = self.base_position_rect.x + self.base_position_rect.width - self.open_button_width

        expand_button_symbol = '▼'
        if self.expand_direction is not None:
            if self.expand_direction == 'up':
                expand_button_symbol = '▲'
            elif self.expand_direction == 'down':
                expand_button_symbol = '▼'

        self.open_button = UIButton(pygame.Rect((open_button_x,
                                                 self.base_position_rect.y),
                                                (self.open_button_width, self.base_position_rect.height)),
                                    expand_button_symbol,
                                    self.ui_manager,
                                    self.ui_container,
                                    starting_height=2,
                                    parent_element=self.drop_down_menu_ui,
                                    object_id='#expand_button')

    def finish(self):
        """
        Called when we leave the closed state. Kills the open button and the selected option button.
        """
        self.selected_option_button.kill()
        self.open_button.kill()

    def update(self):
        if self.open_button.check_pressed():
            self.should_transition = True
Esempio n. 5
0
class UIExpandedDropDownState:
    """
    The expanded state of the drop down  displays the currently chosen option, all the available options and a button
    to close the menu and return to the closed state.

    Picking an option will also close the menu.
    """

    def __init__(self, drop_down_menu_ui, options_list, selected_option, base_position_rect,
                 close_button_width, expand_direction, manager, container, element_ids, object_ids):
        self.drop_down_menu_ui = drop_down_menu_ui
        self.should_transition = False
        self.options_list = options_list
        self.selected_option = selected_option
        self.base_position_rect = base_position_rect
        self.selected_option_rect = None
        self.expand_direction = expand_direction
        self.ui_manager = manager
        self.ui_container = container
        self.element_ids = element_ids
        self.object_ids = object_ids
        self.rect_height_offset = 0

        self.close_button_width = close_button_width

        self.selected_option_button = None
        self.close_button = None
        self.drawable_shape = None
        self.menu_buttons = []

        self.should_transition = False
        self.target_state = 'closed'

    def rebuild(self):
        # shape for expanded drop down is a little trick because it is two rectangles, one on top of the other
        # forming an 'L' shape (or an inverted L if dropping down)

        if self.expand_direction == 'down':
            overall_background_rect = pygame.Rect(self.drop_down_menu_ui.rect.topleft,
                                                  (self.drop_down_menu_ui.rect.width + 50,
                                                   self.base_position_rect.height * (1 + len(self.options_list)) +
                                                   2 * self.drop_down_menu_ui.shadow_width +
                                                   2 * self.drop_down_menu_ui.border_width))

            options_background_rect = pygame.Rect(self.drop_down_menu_ui.rect.topleft,
                                                  (self.base_position_rect.width - self.close_button_width +
                                                   2 * self.drop_down_menu_ui.shadow_width +
                                                   2 * self.drop_down_menu_ui.border_width,
                                                   self.base_position_rect.height * (1 + len(self.options_list)) +
                                                   2 * self.drop_down_menu_ui.shadow_width +
                                                   2 * self.drop_down_menu_ui.border_width))
            self.rect_height_offset = 0
            self.selected_option_rect = pygame.Rect((0, 0),
                                                    self.drop_down_menu_ui.rect.size)
        else:
            # need to adjust the position of the rect so it appears in the right position
            self.rect_height_offset = self.base_position_rect.height * len(self.options_list)
            self.drop_down_menu_ui.rect.y = self.drop_down_menu_ui.rect.y - self.rect_height_offset
            self.drop_down_menu_ui.relative_rect.y = self.drop_down_menu_ui.relative_rect.y - self.rect_height_offset

            self.selected_option_rect = pygame.Rect((0, self.rect_height_offset),
                                                    self.drop_down_menu_ui.rect.size)

            overall_background_rect = pygame.Rect(self.drop_down_menu_ui.rect.topleft,
                                                  (self.drop_down_menu_ui.rect.width + 50,
                                                   self.base_position_rect.height * (1 + len(self.options_list)) +
                                                   2 * self.drop_down_menu_ui.shadow_width +
                                                   2 * self.drop_down_menu_ui.border_width))

            options_background_rect = pygame.Rect(self.drop_down_menu_ui.rect.topleft,
                                                  (self.base_position_rect.width - self.close_button_width +
                                                   2 * self.drop_down_menu_ui.shadow_width +
                                                   2 * self.drop_down_menu_ui.border_width,
                                                   self.base_position_rect.height * (1 + len(self.options_list)) +
                                                   2 * self.drop_down_menu_ui.shadow_width +
                                                   2 * self.drop_down_menu_ui.border_width))

        self.drop_down_menu_ui.image = pygame.Surface(overall_background_rect.size, flags=pygame.SRCALPHA)
        self.drop_down_menu_ui.image.fill(pygame.Color('#00000000'))

        theming_parameters = {'normal_bg': self.drop_down_menu_ui.background_colour,
                              'normal_border': self.drop_down_menu_ui.border_colour,
                              'border_width': self.drop_down_menu_ui.border_width,
                              'shadow_width': self.drop_down_menu_ui.shadow_width,
                              'shape_corner_radius': self.drop_down_menu_ui.shape_corner_radius}

        if self.drop_down_menu_ui.shape_type == 'rectangle':
            drawable_shape = RectDrawableShape(self.selected_option_rect, theming_parameters,
                                               ['normal'], self.ui_manager)

            self.drop_down_menu_ui.image.blit(drawable_shape.get_surface('normal'), self.selected_option_rect.topleft)
            self.drop_down_menu_ui.image.fill(pygame.Color('#00000000'),
                                              pygame.Rect((0, 0),
                                                          (options_background_rect.width -
                                                           self.drop_down_menu_ui.shadow_width -
                                                           self.drop_down_menu_ui.border_width,
                                                           options_background_rect.height)))

            options_drawable_shape = RectDrawableShape(options_background_rect, theming_parameters,
                                                       ['normal'], self.ui_manager)
            self.drop_down_menu_ui.image.blit(options_drawable_shape.get_surface('normal'), (0, 0))
        elif self.drop_down_menu_ui.shape_type == 'rounded_rectangle':
            drawable_shape = RoundedRectangleShape(self.selected_option_rect, theming_parameters,
                                                   ['normal'], self.ui_manager)

            self.drop_down_menu_ui.image.blit(drawable_shape.get_surface('normal'), self.selected_option_rect.topleft)
            self.drop_down_menu_ui.image.fill(pygame.Color('#00000000'),
                                              pygame.Rect((0, 0),
                                                          (options_background_rect.width -
                                                           self.drop_down_menu_ui.shadow_width -
                                                           self.drop_down_menu_ui.border_width,
                                                           options_background_rect.height)))

            options_drawable_shape = RoundedRectangleShape(options_background_rect, theming_parameters,
                                                           ['normal'], self.ui_manager)
            self.drop_down_menu_ui.image.blit(options_drawable_shape.get_surface('normal'), (0, 0))

        # extra
        if self.close_button is not None:
            expand_button_symbol = '▼'
            if self.expand_direction is not None:
                if self.expand_direction == 'up':
                    expand_button_symbol = '▲'
                elif self.expand_direction == 'down':
                    expand_button_symbol = '▼'
            self.close_button.set_text(expand_button_symbol)

    def start(self):
        """
        Called each time we enter the expanded state. It creates the necessary elements, the selected option, all the
        other available options and the close button.
        """
        self.should_transition = False

        option_y_pos = self.base_position_rect.y
        self.selected_option_button = UIButton(pygame.Rect(self.base_position_rect.topleft,
                                                           (self.base_position_rect.width - self.close_button_width,
                                                            self.base_position_rect.height)),
                                               self.selected_option,
                                               self.ui_manager,
                                               self.ui_container,
                                               starting_height=2,
                                               parent_element=self.drop_down_menu_ui,
                                               object_id='#selected_option')

        expand_button_symbol = '▼'
        select_button_dist_to_move = self.selected_option_button.rect.height
        option_button_dist_to_move = self.base_position_rect.height

        if self.expand_direction is not None:
            if self.expand_direction == 'up':
                expand_button_symbol = '▲'
                select_button_dist_to_move = -self.selected_option_button.rect.height
                option_button_dist_to_move = -self.base_position_rect.height
            elif self.expand_direction == 'down':
                expand_button_symbol = '▼'
                select_button_dist_to_move = self.selected_option_button.rect.height
                option_button_dist_to_move = self.base_position_rect.height

        close_button_x = self.base_position_rect.x + self.base_position_rect.width - self.close_button_width

        self.close_button = UIButton(pygame.Rect((close_button_x, self.base_position_rect.y),
                                                 (self.close_button_width, self.base_position_rect.height)),
                                     expand_button_symbol,
                                     self.ui_manager,
                                     self.ui_container,
                                     starting_height=2,
                                     parent_element=self.drop_down_menu_ui,
                                     object_id='#expand_button')

        option_y_pos += select_button_dist_to_move
        for option in self.options_list:
            new_button = UIButton(pygame.Rect((self.base_position_rect.x, option_y_pos),
                                              (self.base_position_rect.width - self.close_button_width,
                                               self.base_position_rect.height)),
                                  option,
                                  self.ui_manager,
                                  self.ui_container,
                                  starting_height=3,  # height allows options to overlap other UI elements
                                  parent_element=self.drop_down_menu_ui,
                                  object_id='#option')
            option_y_pos += option_button_dist_to_move
            self.menu_buttons.append(new_button)

        self.rebuild()

    def finish(self):
        """
        cleans everything up upon exiting the expanded menu state.
        """
        for button in self.menu_buttons:
            button.kill()

        self.menu_buttons.clear()

        self.selected_option_button.kill()
        self.close_button.kill()

        self.drop_down_menu_ui.rect.y += self.rect_height_offset
        self.drop_down_menu_ui.relative_rect.y += self.rect_height_offset

    def update(self):
        if self.close_button is not None and self.close_button.check_pressed():
            self.should_transition = True

        for button in self.menu_buttons:
            if button.check_pressed():
                self.drop_down_menu_ui.selected_option = button.text
                self.should_transition = True

                drop_down_changed_event = pygame.event.Event(pygame.USEREVENT,
                                                             {'user_type': 'ui_drop_down_menu_changed',
                                                              'text': button.text,
                                                              'ui_element': self.drop_down_menu_ui,
                                                              'ui_object_id': self.object_ids[-1]})
                pygame.event.post(drop_down_changed_event)
class EverythingWindow(UIWindow):
    def __init__(self, rect, ui_manager):

        element_ids = ['everything_window']

        super().__init__(rect, ui_manager, element_ids=element_ids)

        # create shadow
        shadow_padding = (2, 2)

        self.image = self.ui_manager.get_shadow(self.rect.size)
        self.image.fill(self.ui_manager.get_theme().get_colour(self.object_ids, self.element_ids, 'dark_bg'),
                        pygame.Rect(shadow_padding,
                                    (self.rect.width - shadow_padding[0] * 2,
                                     self.rect.height - shadow_padding[1] * 2)
                                    ))

        self.get_container().relative_rect.width = self.rect.width - shadow_padding[0] * 2
        self.get_container().relative_rect.height = self.rect.height - shadow_padding[1] * 2
        self.get_container().relative_rect.x = self.get_container().relative_rect.x + shadow_padding[0]
        self.get_container().relative_rect.y = self.get_container().relative_rect.y + shadow_padding[1]
        self.get_container().update_containing_rect_position()

        self.close_window_button = UIButton(relative_rect=pygame.Rect((self.get_container().rect.width-20, 0),
                                                                      (20, 20)),
                                            text='╳',
                                            manager=ui_manager,
                                            container=self.get_container(),
                                            parent_element=self
                                            )
        self.menu_bar = UIButton(relative_rect=pygame.Rect((0, 0),
                                                           (self.get_container().rect.width-20, 20)),
                                 text='Everything Container',
                                 manager=ui_manager,
                                 container=self.get_container(),
                                 parent_element=self,
                                 object_id='#message_window_title_bar'
                                 )
        self.menu_bar.set_hold_range((100, 100))

        self.grabbed_window = False
        self.starting_grab_difference = (0, 0)

        self.test_slider = UIHorizontalSlider(pygame.Rect((int(self.rect.width / 2),
                                                           int(self.rect.height * 0.70)),
                                                          (240, 25)),
                                              50.0,
                                              (0.0, 100.0),
                                              self.ui_manager,
                                              container=self.get_container(),
                                              parent_element=self)

        self.slider_label = UILabel(pygame.Rect((int(self.rect.width / 2) + 250,
                                                 int(self.rect.height * 0.70)),
                                                (27, 25)),
                                    str(int(self.test_slider.get_current_value())),
                                    self.ui_manager,
                                    container=self.get_container(),
                                    parent_element=self)

        self.test_text_entry = UITextEntryLine(pygame.Rect((int(self.rect.width / 2),
                                                            int(self.rect.height * 0.50)),
                                                           (200, -1)),
                                               self.ui_manager,
                                               container=self.get_container(),
                                               parent_element=self)
        self.test_text_entry.set_forbidden_characters('numbers')

        current_resolution_string = 'Item 1'
        self.test_drop_down_menu = UIDropDownMenu(['Item 1',
                                                   'Item 2',
                                                   'Item 3',
                                                   'Item 4',
                                                   'Item 5',
                                                   'Item 6'],
                                                  current_resolution_string,
                                                  pygame.Rect((int(self.rect.width / 2),
                                                               int(self.rect.height * 0.3)),
                                                              (200, 25)),
                                                  self.ui_manager,
                                                  container=self.get_container(),
                                                  parent_element=self)

        self.health_bar = UIScreenSpaceHealthBar(pygame.Rect((int(self.rect.width / 9),
                                                              int(self.rect.height * 0.7)),
                                                             (200, 20)),
                                                 self.ui_manager,
                                                 container=self.get_container(),
                                                 parent_element=self)

        loaded_test_image = pygame.image.load('data/images/splat.png').convert_alpha()

        self.test_image = UIImage(pygame.Rect((int(self.rect.width / 9),
                                               int(self.rect.height * 0.3)),
                                              loaded_test_image.get_rect().size),
                                  loaded_test_image, self.ui_manager,
                                  container=self.get_container(),
                                  parent_element=self)
        self.is_selected = False

    def select(self):
        self.is_selected = True

    def unselect(self):
        self.is_selected = False

    def process_event(self, event):
        processed_event = False
        if self.is_selected:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_p:
                    self.test_slider.set_current_value(50)

        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                mouse_x, mouse_y = event.pos
                if self.rect.collidepoint(mouse_x, mouse_y):
                    processed_event = True
                    self.window_stack.move_window_to_front(self)

        return processed_event

    def update(self, time_delta):
        if self.alive():
            if self.test_slider.has_moved_recently:
                self.slider_label.set_text(str(int(self.test_slider.get_current_value())))

            if self.menu_bar.held:
                mouse_x, mouse_y = pygame.mouse.get_pos()
                if not self.grabbed_window:
                    self.window_stack.move_window_to_front(self)
                    self.grabbed_window = True
                    self.starting_grab_difference = (mouse_x - self.rect.x,
                                                     mouse_y - self.rect.y)

                current_grab_difference = (mouse_x - self.rect.x,
                                           mouse_y - self.rect.y)

                adjustment_required = (current_grab_difference[0] - self.starting_grab_difference[0],
                                       current_grab_difference[1] - self.starting_grab_difference[1])

                self.rect.x += adjustment_required[0]
                self.rect.y += adjustment_required[1]
                self.get_container().relative_rect.x += adjustment_required[0]
                self.get_container().relative_rect.y += adjustment_required[1]
                self.get_container().update_containing_rect_position()

            else:
                self.grabbed_window = False

            if self.close_window_button.check_pressed():
                self.kill()

        super().update(time_delta)
class UIMessageWindow(UIWindow):
    """
    A simple popup window for delivering text-only messages to users.

    :param message_window_rect: The size and position of the window, includes the menu bar across the top.
    :param message_title: The title of the message window.
    :param html_message: The message itself. Can make use of HTML (a subset of) to style the text.
    :param manager: The UIManager that manages this UIElement.
    :param object_id: A custom defined ID for fine tuning of theming.
    """
    def __init__(self,
                 message_window_rect: pygame.Rect,
                 message_title: str,
                 html_message: str,
                 manager: ui_manager.UIManager,
                 object_id: Union[str, None] = None):

        new_element_ids, new_object_ids = self.create_valid_ids(
            parent_element=None,
            object_id=object_id,
            element_id='message_window')
        super().__init__(message_window_rect, manager, new_element_ids,
                         new_object_ids)

        self.done_button_vertical_start = 30
        self.done_button_vertical_space = 40
        self.menu_bar_height = 20
        self.close_button_width = 20
        self.grabbed_window = False
        self.starting_grab_difference = (0, 0)

        self.shadow_width = None  # type: Union[None, int]
        self.border_width = None  # type: Union[None, int]
        self.background_colour = None
        self.border_colour = None

        self.border_rect = None
        self.background_rect = None
        self.text_block_rect = None  # type: Union[None, pygame.Rect]

        self.menu_bar = None
        self.close_window_button = None
        self.dismiss_button = None
        self.text_block = None

        self.drawable_shape = None
        self.shape_type = 'rectangle'
        self.shape_corner_radius = None

        self.rebuild_from_changed_theme_data()

        self.menu_bar = UIButton(relative_rect=pygame.Rect(
            (0, 0), ((self.rect.width - (self.shadow_width * 2)) -
                     self.close_button_width, self.menu_bar_height)),
                                 text=message_title,
                                 manager=manager,
                                 container=self.get_container(),
                                 parent_element=self,
                                 object_id='#message_window_title_bar')
        self.menu_bar.set_hold_range((100, 100))

        self.close_window_button = UIButton(relative_rect=pygame.Rect(
            ((self.rect.width - self.shadow_width * 2) -
             self.close_button_width, 0),
            (self.close_button_width, self.menu_bar_height)),
                                            text='╳',
                                            manager=manager,
                                            container=self.get_container(),
                                            parent_element=self,
                                            object_id='#close_button')

        self.dismiss_button = UIButton(
            relative_rect=pygame.Rect(
                (int(self.rect.width / 2) + 45,
                 (self.border_rect.height - self.done_button_vertical_start)),
                (70, 20)),
            text="Dismiss",
            manager=manager,
            container=self.get_container(),
            tool_tip_text="<font face=fira_code color=normal_text size=2>"
            "Click to get rid of this message.</font>",
            parent_element=self,
            object_id='#dismiss_button')

        self.text_block = UITextBox(html_message,
                                    self.text_block_rect,
                                    manager=manager,
                                    container=self.get_container(),
                                    parent_element=self)

    def rebuild(self):
        """

        """
        border_rect_width = self.rect.width - (self.shadow_width * 2)
        border_rect_height = self.rect.height - (self.shadow_width * 2)
        self.border_rect = pygame.Rect((self.shadow_width, self.shadow_width),
                                       (border_rect_width, border_rect_height))

        background_rect_width = border_rect_width - (self.border_width * 2)
        background_rect_height = border_rect_height - (self.border_width * 2)
        self.background_rect = pygame.Rect(
            (self.shadow_width + self.border_width,
             self.shadow_width + self.border_width),
            (background_rect_width, background_rect_height))

        self.text_block_rect = pygame.Rect(
            (self.border_width, self.menu_bar_height),
            (self.border_rect.width - self.border_width,
             (self.border_rect.height - self.menu_bar_height -
              self.done_button_vertical_space)))

        theming_parameters = {
            'normal_bg': self.background_colour,
            'normal_border': self.border_colour,
            'border_width': self.border_width,
            'shadow_width': self.shadow_width,
            'shape_corner_radius': self.shape_corner_radius
        }

        if self.shape_type == 'rectangle':
            self.drawable_shape = RectDrawableShape(self.rect,
                                                    theming_parameters,
                                                    ['normal'],
                                                    self.ui_manager)
        elif self.shape_type == 'rounded_rectangle':
            self.drawable_shape = RoundedRectangleShape(
                self.rect, theming_parameters, ['normal'], self.ui_manager)

        self.image = self.drawable_shape.get_surface('normal')

        self.get_container(
        ).relative_rect.width = self.rect.width - self.shadow_width * 2
        self.get_container(
        ).relative_rect.height = self.rect.height - self.shadow_width * 2
        self.get_container(
        ).relative_rect.x = self.relative_rect.x + self.shadow_width
        self.get_container(
        ).relative_rect.y = self.relative_rect.y + self.shadow_width
        self.get_container().update_containing_rect_position()

        if self.menu_bar is not None:
            self.menu_bar.set_dimensions(
                ((self.rect.width - (self.shadow_width * 2)) -
                 self.close_button_width, self.menu_bar_height))
        if self.close_window_button is not None:
            self.close_window_button.set_relative_position(
                ((self.rect.width - self.shadow_width * 2) -
                 self.close_button_width, 0))
        if self.dismiss_button is not None:
            self.dismiss_button.set_relative_position(
                ((self.rect.width / 2) + 45,
                 (self.border_rect.height - self.done_button_vertical_start)))
        if self.text_block is not None:
            self.text_block.set_relative_position(self.text_block_rect.topleft)
            self.text_block.set_dimensions(self.text_block_rect.size)

    def update(self, time_delta: float):
        """
        Called every update loop of our UI manager. Handles moving and closing the window.

        :param time_delta: The time in seconds between calls to this function.
        """
        if self.alive():

            if self.dismiss_button.check_pressed():
                self.kill()

            if self.menu_bar.held:
                mouse_x, mouse_y = self.ui_manager.get_mouse_position()
                if not self.grabbed_window:
                    self.window_stack.move_window_to_front(self)
                    self.grabbed_window = True
                    self.starting_grab_difference = (mouse_x - self.rect.x,
                                                     mouse_y - self.rect.y)

                current_grab_difference = (mouse_x - self.rect.x,
                                           mouse_y - self.rect.y)

                adjustment_required = (current_grab_difference[0] -
                                       self.starting_grab_difference[0],
                                       current_grab_difference[1] -
                                       self.starting_grab_difference[1])

                self.rect.x += adjustment_required[0]
                self.rect.y += adjustment_required[1]
                self.relative_rect.x = self.rect.x - self.ui_container.rect.x
                self.relative_rect.y = self.rect.y - self.ui_container.rect.y
                self.get_container().relative_rect.x += adjustment_required[0]
                self.get_container().relative_rect.y += adjustment_required[1]
                self.get_container().update_containing_rect_position()

            else:
                self.grabbed_window = False

            if self.close_window_button.check_pressed():
                self.kill()

        super().update(time_delta)

    def rebuild_from_changed_theme_data(self):
        """
        Called by the UIManager to check the theming data and rebuild whatever needs rebuilding for this element when
        the theme data has changed.
        """
        has_any_changed = False

        shape_type = 'rectangle'
        shape_type_string = self.ui_theme.get_misc_data(
            self.object_ids, self.element_ids, 'shape')
        if shape_type_string is not None:
            if shape_type_string in ['rectangle', 'rounded_rectangle']:
                shape_type = shape_type_string
        if shape_type != self.shape_type:
            self.shape_type = shape_type
            has_any_changed = True

        corner_radius = 2
        shape_corner_radius_string = self.ui_theme.get_misc_data(
            self.object_ids, self.element_ids, 'shape_corner_radius')
        if shape_corner_radius_string is not None:
            try:
                corner_radius = int(shape_corner_radius_string)
            except ValueError:
                corner_radius = 2
        if corner_radius != self.shape_corner_radius:
            self.shape_corner_radius = corner_radius
            has_any_changed = True

        border_width = 1
        border_width_string = self.ui_theme.get_misc_data(
            self.object_ids, self.element_ids, 'border_width')
        if border_width_string is not None:
            try:
                border_width = int(border_width_string)
            except ValueError:
                border_width = 1

        if border_width != self.border_width:
            self.border_width = border_width
            has_any_changed = True

        shadow_width = 2
        shadow_width_string = self.ui_theme.get_misc_data(
            self.object_ids, self.element_ids, 'shadow_width')
        if shadow_width_string is not None:
            try:
                shadow_width = int(shadow_width_string)
            except ValueError:
                shadow_width = 2
        if shadow_width != self.shadow_width:
            self.shadow_width = shadow_width
            has_any_changed = True

        background_colour = self.ui_theme.get_colour_or_gradient(
            self.object_ids, self.element_ids, 'dark_bg')
        if background_colour != self.background_colour:
            self.background_colour = background_colour
            has_any_changed = True

        border_colour = self.ui_theme.get_colour_or_gradient(
            self.object_ids, self.element_ids, 'normal_border')
        if border_colour != self.border_colour:
            self.border_colour = border_colour
            has_any_changed = True

        if has_any_changed:
            self.rebuild()
Esempio n. 8
0
class UIMessageWindow(UIWindow):
    """
    A simple popup window for delivering text-only messages to users.

    :param message_window_rect: The size and position of the window, includes the menu bar across the top.
    :param message_title: The title of the message window.
    :param html_message: The message itself. Can make use of HTML (a subset of) to style the text.
    :param manager: The UIManager that manages this UIElement.
    :param object_id: A custom defined ID for fine tuning of theming.
    """
    def __init__(self,
                 message_window_rect: pygame.Rect,
                 message_title: str,
                 html_message: str,
                 manager: ui_manager.UIManager,
                 object_id: Union[str, None] = None):

        new_element_ids, new_object_ids = self.create_valid_ids(
            parent_element=None,
            object_id=object_id,
            element_id='message_window')
        super().__init__(message_window_rect, manager, new_element_ids,
                         new_object_ids)

        self.bg_colour = self.ui_manager.get_theme().get_colour(
            self.object_ids, self.element_ids, 'dark_bg')
        self.border_colour = self.ui_manager.get_theme().get_colour(
            self.object_ids, self.element_ids, 'normal_border')

        self.border_width = 1
        border_width_string = self.ui_theme.get_misc_data(
            self.object_ids, self.element_ids, 'border_width')
        if border_width_string is not None:
            self.border_width = int(border_width_string)

        self.shadow_width = 1
        shadow_width_string = self.ui_theme.get_misc_data(
            self.object_ids, self.element_ids, 'shadow_width')
        if shadow_width_string is not None:
            self.shadow_width = int(shadow_width_string)

        border_rect_width = self.rect.width - (self.shadow_width * 2)
        border_rect_height = self.rect.height - (self.shadow_width * 2)
        self.border_rect = pygame.Rect((self.shadow_width, self.shadow_width),
                                       (border_rect_width, border_rect_height))

        background_rect_width = border_rect_width - (self.border_width * 2)
        background_rect_height = border_rect_height - (self.border_width * 2)
        self.background_rect = pygame.Rect(
            (self.shadow_width + self.border_width,
             self.shadow_width + self.border_width),
            (background_rect_width, background_rect_height))

        if self.shadow_width > 0:
            self.image = self.ui_manager.get_shadow(self.rect.size,
                                                    self.shadow_width)
        else:
            self.image = pygame.Surface(self.rect.size, flags=pygame.SRCALPHA)

        self.image.fill(self.border_colour, self.border_rect)
        self.image.fill(self.bg_colour, self.background_rect)

        self.get_container(
        ).relative_rect.width = self.rect.width - self.shadow_width * 2
        self.get_container(
        ).relative_rect.height = self.rect.height - self.shadow_width * 2
        self.get_container().relative_rect.x = self.get_container(
        ).relative_rect.x + self.shadow_width
        self.get_container().relative_rect.y = self.get_container(
        ).relative_rect.y + self.shadow_width
        self.get_container().update_containing_rect_position()

        menu_bar_height = 20
        close_button_width = 20
        self.menu_bar = UIButton(relative_rect=pygame.Rect(
            (0, 0),
            ((self.rect.width -
              (self.shadow_width * 2)) - close_button_width, menu_bar_height)),
                                 text=message_title,
                                 manager=manager,
                                 container=self.get_container(),
                                 parent_element=self,
                                 object_id='#message_window_title_bar')
        self.menu_bar.set_hold_range((100, 100))

        self.grabbed_window = False
        self.starting_grab_difference = (0, 0)

        self.close_window_button = UIButton(relative_rect=pygame.Rect(
            ((self.rect.width - self.shadow_width * 2) - close_button_width,
             0), (close_button_width, menu_bar_height)),
                                            text='╳',
                                            manager=manager,
                                            container=self.get_container(),
                                            parent_element=self,
                                            object_id='#close_button')
        done_button_vertical_start = 30
        done_button_vertical_space = 40
        self.dismiss_button = UIButton(
            relative_rect=pygame.Rect(
                ((self.rect.width / 2) + 45,
                 (border_rect_height - done_button_vertical_start)), (70, 20)),
            text="Dismiss",
            manager=manager,
            container=self.get_container(),
            tool_tip_text="<font face=fira_code color=normal_text size=2>"
            "Click to get rid of this message.</font>",
            parent_element=self,
            object_id='#dismiss_button')

        text_block_rect = pygame.Rect(
            (self.border_width, menu_bar_height),
            (self.border_rect.width - self.border_width,
             (border_rect_height - menu_bar_height -
              done_button_vertical_space)))
        self.text_block = UITextBox(html_message,
                                    text_block_rect,
                                    manager=manager,
                                    container=self.get_container(),
                                    parent_element=self)

    def update(self, time_delta: float):
        """
        Called every update loop of our UI manager. Handles moving and closing the window.

        :param time_delta: The time in seconds between calls to this function.
        """
        if self.alive():

            if self.dismiss_button.check_pressed():
                self.kill()

            if self.menu_bar.held:
                mouse_x, mouse_y = pygame.mouse.get_pos()
                if not self.grabbed_window:
                    self.window_stack.move_window_to_front(self)
                    self.grabbed_window = True
                    self.starting_grab_difference = (mouse_x - self.rect.x,
                                                     mouse_y - self.rect.y)

                current_grab_difference = (mouse_x - self.rect.x,
                                           mouse_y - self.rect.y)

                adjustment_required = (current_grab_difference[0] -
                                       self.starting_grab_difference[0],
                                       current_grab_difference[1] -
                                       self.starting_grab_difference[1])

                self.rect.x += adjustment_required[0]
                self.rect.y += adjustment_required[1]
                self.get_container().relative_rect.x += adjustment_required[0]
                self.get_container().relative_rect.y += adjustment_required[1]
                self.get_container().update_containing_rect_position()

            else:
                self.grabbed_window = False

            if self.close_window_button.check_pressed():
                self.kill()

        super().update(time_delta)
class PongWindow(UIWindow):
    def __init__(self, ui_manager):
        super().__init__(pygame.Rect((25, 25), (320, 240)), ui_manager, ['pong_window'])

        self.bg_colour = self.ui_manager.get_theme().get_colour(self.object_ids, self.element_ids, 'dark_bg')

        # create shadow
        shadow_padding = (2, 2)
        background_surface = pygame.Surface((self.rect.width - shadow_padding[0] * 2,
                                             self.rect.height - shadow_padding[1] * 2))
        background_surface.fill(self.bg_colour)
        self.image = self.ui_manager.get_shadow(self.rect.size)
        self.image.blit(background_surface, shadow_padding)

        self.get_container().relative_rect.width = self.rect.width - shadow_padding[0] * 2
        self.get_container().relative_rect.height = self.rect.height - shadow_padding[1] * 2
        self.get_container().relative_rect.x = self.get_container().relative_rect.x + shadow_padding[0]
        self.get_container().relative_rect.y = self.get_container().relative_rect.y + shadow_padding[1]
        self.get_container().update_containing_rect_position()

        self.menu_bar = UIButton(relative_rect=pygame.Rect((0, 0),
                                                           ((self.rect.width - shadow_padding[0] * 2) - 20, 20)),
                                 text='Super Awesome Pong!',
                                 manager=ui_manager,
                                 container=self.get_container(),
                                 parent_element=self
                                 )
        self.menu_bar.set_hold_range((100, 100))

        self.grabbed_window = False
        self.starting_grab_difference = (0, 0)

        self.close_window_button = UIButton(relative_rect=pygame.Rect(((self.rect.width - shadow_padding[0] * 2) - 20,
                                                                       0),
                                                                      (20, 20)),
                                            text='╳',
                                            manager=ui_manager,
                                            container=self.get_container(),
                                            parent_element=self
                                            )

        game_surface_size = (self.get_container().rect.width - 4, self.get_container().rect.height - 24)
        self.game_surface_element = UIImage(pygame.Rect((2, 22),
                                                        game_surface_size),
                                            pygame.Surface(game_surface_size).convert(),
                                            manager=ui_manager,
                                            container=self.get_container(),
                                            parent_element=self)

        self.pong_game = PongGame(game_surface_size)

    def process_event(self, event):
        self.pong_game.process_event(event)

    def update(self, time_delta):
        if self.alive():

            if self.menu_bar.held:
                mouse_x, mouse_y = pygame.mouse.get_pos()
                if not self.grabbed_window:
                    self.window_stack.move_window_to_front(self)
                    self.grabbed_window = True
                    self.starting_grab_difference = (mouse_x - self.rect.x,
                                                     mouse_y - self.rect.y)

                current_grab_difference = (mouse_x - self.rect.x,
                                           mouse_y - self.rect.y)

                adjustment_required = (current_grab_difference[0] - self.starting_grab_difference[0],
                                       current_grab_difference[1] - self.starting_grab_difference[1])

                self.rect.x += adjustment_required[0]
                self.rect.y += adjustment_required[1]
                self.get_container().relative_rect.x += adjustment_required[0]
                self.get_container().relative_rect.y += adjustment_required[1]
                self.get_container().update_containing_rect_position()

            else:
                self.grabbed_window = False

            if not self.grabbed_window:
                self.pong_game.update(time_delta)

            if self.close_window_button.check_pressed():
                self.kill()

        super().update(time_delta)

        self.pong_game.draw(self.game_surface_element.image)
Esempio n. 10
0
class UIClosedDropDownState:
    """
    The closed state of the drop down just displays the currently chosen option and a button that will switch the menu
    to the expanded state.
    """
    def __init__(self, drop_down_menu_ui, selected_option, base_position_rect,
                 open_button_width, manager, container, element_ids,
                 object_ids):
        self.drop_down_menu_ui = drop_down_menu_ui
        self.selected_option_button = None
        self.open_button = None
        self.selected_option = selected_option
        self.base_position_rect = base_position_rect
        self.ui_manager = manager
        self.ui_container = container
        self.element_ids = element_ids
        self.object_ids = object_ids

        self.open_button_width = open_button_width

        self.should_transition = False
        self.target_state = 'expanded'

    def start(self):
        """
        Called each time we enter the closed state. It creates the necessary elements, the selected option and the
        open button.
        """

        # First handle the background
        if self.drop_down_menu_ui.shadow_width > 0:
            self.drop_down_menu_ui.image = self.ui_manager.get_shadow(
                self.drop_down_menu_ui.rect.size)
        else:
            self.drop_down_menu_ui.image = pygame.Surface(
                self.drop_down_menu_ui.rect.size, flags=pygame.SRCALPHA)

        border_rect = pygame.Rect((self.drop_down_menu_ui.shadow_width,
                                   self.drop_down_menu_ui.shadow_width),
                                  (self.drop_down_menu_ui.rect.width -
                                   (2 * self.drop_down_menu_ui.shadow_width),
                                   self.drop_down_menu_ui.rect.height -
                                   (2 * self.drop_down_menu_ui.shadow_width)))
        if self.drop_down_menu_ui.border_width > 0:
            self.drop_down_menu_ui.image.fill(
                self.drop_down_menu_ui.border_colour, border_rect)

        relative_background_rect = pygame.Rect(
            (self.drop_down_menu_ui.border_width +
             self.drop_down_menu_ui.shadow_width,
             self.drop_down_menu_ui.border_width +
             self.drop_down_menu_ui.shadow_width),
            (border_rect.width - (2 * self.drop_down_menu_ui.border_width),
             border_rect.height - (2 * self.drop_down_menu_ui.border_width)))

        self.drop_down_menu_ui.image.fill(
            self.drop_down_menu_ui.background_colour, relative_background_rect)

        self.should_transition = False
        self.selected_option_button = UIButton(
            pygame.Rect(
                (self.base_position_rect.x, self.base_position_rect.y),
                (self.base_position_rect.width - self.open_button_width,
                 self.base_position_rect.height)),
            self.selected_option,
            self.ui_manager,
            self.ui_container,
            starting_height=2,
            parent_element=self.drop_down_menu_ui,
            object_id='#selected_option')
        open_button_x = self.base_position_rect.x + self.base_position_rect.width - self.open_button_width

        expand_direction = self.ui_manager.get_theme().get_misc_data(
            self.object_ids, self.element_ids, 'expand_direction')
        expand_button_symbol = '▼'
        if expand_direction is not None:
            if expand_direction == 'up':
                expand_button_symbol = '▲'
            elif expand_direction == 'down':
                expand_button_symbol = '▼'

        self.open_button = UIButton(pygame.Rect(
            (open_button_x, self.base_position_rect.y),
            (self.open_button_width, self.base_position_rect.height)),
                                    expand_button_symbol,
                                    self.ui_manager,
                                    self.ui_container,
                                    starting_height=2,
                                    parent_element=self.drop_down_menu_ui,
                                    object_id='#expand_button')

    def finish(self):
        """
        Called when we leave the closed state. Kills the open button and the selected option button.
        """
        self.selected_option_button.kill()
        self.open_button.kill()

    def update(self):
        if self.open_button.check_pressed():
            self.should_transition = True
Esempio n. 11
0
class UIExpandedDropDownState:
    """
    The expanded state of the drop down  displays the currently chosen option, all the available options and a button
    to close the menu and return to the closed state.

    Picking an option will also close the menu.
    """
    def __init__(self, drop_down_menu_ui, options_list, selected_option,
                 base_position_rect, close_button_width, manager, container,
                 element_ids, object_ids):
        self.drop_down_menu_ui = drop_down_menu_ui
        self.should_transition = False
        self.options_list = options_list
        self.selected_option = selected_option
        self.base_position_rect = base_position_rect
        self.ui_manager = manager
        self.ui_container = container
        self.element_ids = element_ids
        self.object_ids = object_ids

        self.close_button_width = close_button_width

        self.selected_option_button = None
        self.close_button = None
        self.menu_buttons = []

        self.should_transition = False
        self.target_state = 'closed'

    def start(self):
        """
        Called each time we enter the expanded state. It creates the necessary elements, the selected option, all the
        other available options and the close button.
        """
        self.should_transition = False

        option_y_pos = self.base_position_rect.y
        self.selected_option_button = UIButton(
            pygame.Rect(
                self.base_position_rect.topleft,
                (self.base_position_rect.width - self.close_button_width,
                 self.base_position_rect.height)),
            self.selected_option,
            self.ui_manager,
            self.ui_container,
            starting_height=2,
            parent_element=self.drop_down_menu_ui,
            object_id='#selected_option')

        expand_direction = self.ui_manager.get_theme().get_misc_data(
            self.object_ids, self.element_ids, 'expand_direction')
        expand_button_symbol = '▼'
        select_button_dist_to_move = self.selected_option_button.rect.height
        option_button_dist_to_move = self.base_position_rect.height

        if expand_direction is not None:
            if expand_direction == 'up':
                expand_button_symbol = '▲'
                select_button_dist_to_move = -self.selected_option_button.rect.height
                option_button_dist_to_move = -self.base_position_rect.height
            elif expand_direction == 'down':
                expand_button_symbol = '▼'
                select_button_dist_to_move = self.selected_option_button.rect.height
                option_button_dist_to_move = self.base_position_rect.height

        close_button_x = self.base_position_rect.x + self.base_position_rect.width - self.close_button_width

        self.close_button = UIButton(pygame.Rect(
            (close_button_x, self.base_position_rect.y),
            (self.close_button_width, self.base_position_rect.height)),
                                     expand_button_symbol,
                                     self.ui_manager,
                                     self.ui_container,
                                     starting_height=2,
                                     parent_element=self.drop_down_menu_ui,
                                     object_id='#expand_button')

        option_y_pos += select_button_dist_to_move
        for option in self.options_list:
            new_button = UIButton(
                pygame.Rect(
                    (self.base_position_rect.x, option_y_pos),
                    (self.base_position_rect.width - self.close_button_width,
                     self.base_position_rect.height)),
                option,
                self.ui_manager,
                self.ui_container,
                starting_height=
                3,  # height allows options to overlap other UI elements
                parent_element=self.drop_down_menu_ui,
                object_id='#option')
            option_y_pos += option_button_dist_to_move
            self.menu_buttons.append(new_button)

        overall_background_rect = pygame.Rect(
            self.drop_down_menu_ui.rect.topleft,
            (self.drop_down_menu_ui.rect.width + 50,
             self.base_position_rect.height * (1 + len(self.options_list)) +
             2 * self.drop_down_menu_ui.shadow_width +
             2 * self.drop_down_menu_ui.border_width))

        options_background_rect = pygame.Rect(
            self.drop_down_menu_ui.rect.topleft,
            (self.base_position_rect.width - self.close_button_width +
             2 * self.drop_down_menu_ui.shadow_width +
             2 * self.drop_down_menu_ui.border_width,
             self.base_position_rect.height * (1 + len(self.options_list)) +
             2 * self.drop_down_menu_ui.shadow_width +
             2 * self.drop_down_menu_ui.border_width))
        self.drop_down_menu_ui.image = pygame.Surface(
            overall_background_rect.size, flags=pygame.SRCALPHA)
        self.drop_down_menu_ui.image.fill(pygame.Color('#00000000'))

        if self.drop_down_menu_ui.shadow_width > 0:
            self.drop_down_menu_ui.image.blit(
                self.ui_manager.get_shadow(self.drop_down_menu_ui.rect.size),
                (0, 0))

        border_rect = pygame.Rect((self.drop_down_menu_ui.shadow_width,
                                   self.drop_down_menu_ui.shadow_width),
                                  (self.drop_down_menu_ui.rect.width -
                                   (2 * self.drop_down_menu_ui.shadow_width),
                                   self.drop_down_menu_ui.rect.height -
                                   (2 * self.drop_down_menu_ui.shadow_width)))
        if self.drop_down_menu_ui.border_width > 0:
            self.drop_down_menu_ui.image.fill(
                self.drop_down_menu_ui.border_colour, border_rect)

        relative_background_rect = pygame.Rect(
            (self.drop_down_menu_ui.border_width +
             self.drop_down_menu_ui.shadow_width,
             self.drop_down_menu_ui.border_width +
             self.drop_down_menu_ui.shadow_width),
            (border_rect.width - (2 * self.drop_down_menu_ui.border_width),
             border_rect.height - (2 * self.drop_down_menu_ui.border_width)))

        self.drop_down_menu_ui.image.fill(
            self.drop_down_menu_ui.background_colour, relative_background_rect)

        if self.drop_down_menu_ui.shadow_width > 0:
            self.drop_down_menu_ui.image.fill(
                pygame.Color('#00000000'),
                pygame.Rect((0, 0), (options_background_rect.width -
                                     self.drop_down_menu_ui.shadow_width -
                                     self.drop_down_menu_ui.border_width,
                                     options_background_rect.height)))
            self.drop_down_menu_ui.image.blit(
                self.ui_manager.get_shadow(options_background_rect.size),
                (0, 0))

        options_border_rect = pygame.Rect(
            (self.drop_down_menu_ui.shadow_width,
             self.drop_down_menu_ui.shadow_width),
            (options_background_rect.width -
             (2 * self.drop_down_menu_ui.shadow_width),
             options_background_rect.height -
             (2 * self.drop_down_menu_ui.shadow_width)))

        if self.drop_down_menu_ui.border_width > 0:
            self.drop_down_menu_ui.image.fill(
                self.drop_down_menu_ui.border_colour, options_border_rect)

        options_background_rect = pygame.Rect(
            (self.drop_down_menu_ui.border_width +
             self.drop_down_menu_ui.shadow_width,
             self.drop_down_menu_ui.border_width +
             self.drop_down_menu_ui.shadow_width),
            (options_border_rect.width -
             (2 * self.drop_down_menu_ui.border_width),
             options_border_rect.height -
             (2 * self.drop_down_menu_ui.border_width)))

        self.drop_down_menu_ui.image.fill(
            self.drop_down_menu_ui.background_colour, options_background_rect)

    def finish(self):
        """
        cleans everything up upon exiting the expanded menu state.
        """
        for button in self.menu_buttons:
            button.kill()

        self.menu_buttons.clear()

        self.selected_option_button.kill()
        self.close_button.kill()

    def update(self):
        if self.close_button is not None and self.close_button.check_pressed():
            self.should_transition = True

        for button in self.menu_buttons:
            if button.check_pressed():
                self.drop_down_menu_ui.selected_option = button.text
                self.should_transition = True

                drop_down_changed_event = pygame.event.Event(
                    pygame.USEREVENT, {
                        'user_type': 'ui_drop_down_menu_changed',
                        'text': button.text,
                        'ui_element': self.drop_down_menu_ui,
                        'ui_object_id': self.object_ids[-1]
                    })
                pygame.event.post(drop_down_changed_event)