예제 #1
0
 def _rebuild_shadow(self, new_image, text_render_rect):
     shadow_text_render = render_white_text_alpha_black_bg(self.font, self.text)
     apply_colour_to_surface(self.text_shadow_colour, shadow_text_render)
     for y_pos in range(-self.text_shadow_size, self.text_shadow_size + 1):
         shadow_text_rect = pygame.Rect((text_render_rect.x + self.text_shadow_offset[0],
                                         text_render_rect.y + self.text_shadow_offset[1]
                                         + y_pos),
                                        text_render_rect.size)
         basic_blit(new_image, shadow_text_render, shadow_text_rect)
     for x_pos in range(-self.text_shadow_size, self.text_shadow_size + 1):
         shadow_text_rect = pygame.Rect((text_render_rect.x + self.text_shadow_offset[0]
                                         + x_pos,
                                         text_render_rect.y + self.text_shadow_offset[1]),
                                        text_render_rect.size)
         basic_blit(new_image, shadow_text_render, shadow_text_rect)
     for x_and_y in range(-self.text_shadow_size, self.text_shadow_size + 1):
         shadow_text_rect = pygame.Rect(
             (text_render_rect.x + self.text_shadow_offset[0] + x_and_y,
              text_render_rect.y + self.text_shadow_offset[1] + x_and_y),
             text_render_rect.size)
         basic_blit(new_image, shadow_text_render, shadow_text_rect)
     for x_and_y in range(-self.text_shadow_size, self.text_shadow_size + 1):
         shadow_text_rect = pygame.Rect(
             (text_render_rect.x + self.text_shadow_offset[0] - x_and_y,
              text_render_rect.y + self.text_shadow_offset[1] + x_and_y),
             text_render_rect.size)
         basic_blit(new_image, shadow_text_render, shadow_text_rect)
    def _redraw_filled_bar(self,
                           bg_col: Union[pygame.Color, ColourGradient],
                           shape_surface: pygame.surface.Surface):
        """
        Draw a 'filled bar' onto our drawable shape, allows for things like loading bars,
        health bars etc.

        :param bg_col: the colour or gradient of the bar.
        :param shape_surface: the surface we are drawing on to.

        """
        filled_bar_width = int(self.background_rect.width *
                               self.theming['filled_bar_width_percentage'])
        bar_rect = pygame.Rect((0, 0), (filled_bar_width,
                                        self.background_rect.height))
        unfilled_bar_width = self.background_rect.width - filled_bar_width
        unfilled_bar_rect = pygame.Rect((filled_bar_width, 0),
                                        (unfilled_bar_width,
                                         self.background_rect.height))

        if isinstance(bg_col, ColourGradient):
            bg_col.apply_gradient_to_surface(shape_surface, unfilled_bar_rect)
        else:
            apply_colour_to_surface(bg_col, shape_surface, unfilled_bar_rect)
        if isinstance(self.theming['filled_bar'], ColourGradient):
            self.theming['filled_bar'].apply_gradient_to_surface(shape_surface, bar_rect)
        else:
            apply_colour_to_surface(self.theming['filled_bar'], shape_surface, bar_rect)
예제 #3
0
    def test_apply_colour_to_surface(self, _init_pygame, default_ui_manager: UIManager, default_display_surface):

        DrawableShape(containing_rect=pygame.Rect(0, 0, 100, 100),
                      theming_parameters={}, states=['normal'], manager=default_ui_manager)

        test_surface = pygame.Surface((50, 50), flags=pygame.SRCALPHA, depth=32)
        test_surface.fill(pygame.Color(255, 255, 255, 255))

        apply_colour_to_surface(pygame.Color(50, 100, 50, 255), test_surface)

        after_application_colour = test_surface.get_at((0, 0))

        # multiply blend always appears to be 1 pixel down in every channel
        assert after_application_colour == pygame.Color(50-1, 100-1, 50-1, 255-1)

        test_surface_2 = pygame.Surface((50, 50), flags=pygame.SRCALPHA, depth=32)
        test_surface_2.fill(pygame.Color(255, 255, 255, 255))

        apply_colour_to_surface(pygame.Color(150, 100, 150, 255), test_surface_2,
                                pygame.Rect(0, 0, 25, 50))

        after_application_colour = test_surface_2.get_at((0, 0))

        # multiply blend always appears to be 1 pixel down in every channel
        assert after_application_colour == pygame.Color(150 - 1, 100 - 1, 150 - 1, 255 - 1)

        after_application_colour = test_surface_2.get_at((30, 0))
        assert after_application_colour == pygame.Color(255, 255, 255, 255)
예제 #4
0
    def redraw(self):
        """
        Renders the 'chunk' text to the 'rendered_chunk' surface.

        """
        if self.style.underline or (self.is_hovered and self.link_hover_underline) or \
                (self.link_normal_underline and not self.is_hovered):
            self.font.set_underline(True)

        if len(self.chunk) > 0:
            if isinstance(self.colour, ColourGradient):
                self.rendered_chunk = render_white_text_alpha_black_bg(self.font, self.chunk)
                self.colour.apply_gradient_to_surface(self.rendered_chunk)
            else:
                if isinstance(self.bg_colour, ColourGradient) or self.bg_colour.a != 255:
                    self.rendered_chunk = render_white_text_alpha_black_bg(self.font, self.chunk)
                    apply_colour_to_surface(self.colour, self.rendered_chunk)
                else:
                    self.rendered_chunk = self.font.render(self.chunk,
                                                           True,
                                                           self.colour,
                                                           self.bg_colour).convert_alpha()
        else:
            self.rendered_chunk = pygame.surface.Surface((0, 0),
                                                         flags=pygame.SRCALPHA,
                                                         depth=32)

        self.font.set_underline(False)

        new_metrics = self.font.metrics(self.chunk)
        new_ascent = self.font.get_ascent()
        new_width = self.font.size(self.chunk)[0]
        new_height = self.font.size(self.chunk)[1]
        new_advance = sum(new_metrics[i][4] for i in range(len(self.chunk))
                          if len(new_metrics[i]) == 5)
        if (new_ascent == self.ascent and new_width == self.width and
                new_height == self.height and new_advance == self.advance):
            self.metrics_changed_after_redraw = False
        else:
            self.metrics_changed_after_redraw = True
            self.ascent = new_ascent
            self.width = new_width
            self.height = new_height
            self.advance = new_advance
            self.rect = pygame.Rect(self.position, (self.width, self.height))
예제 #5
0
 def _redraw_unselected_text(self):
     """
     Redraw text where none has been selected by a user.
     """
     self.text_surface = render_white_text_alpha_black_bg(font=self.font,
                                                          text=self.text)
     if self.is_enabled:
         if isinstance(self.text_colour, ColourGradient):
             self.text_colour.apply_gradient_to_surface(self.text_surface)
         else:
             apply_colour_to_surface(self.text_colour, self.text_surface)
     else:
         if isinstance(self.disabled_text_colour, ColourGradient):
             self.disabled_text_colour.apply_gradient_to_surface(
                 self.text_surface)
         else:
             apply_colour_to_surface(self.disabled_text_colour,
                                     self.text_surface)
예제 #6
0
    def _draw_text_with_grad_or_col(
        self, text: str, col_or_grad: Union[ColourGradient, pygame.Color]
    ) -> pygame.surface.Surface:
        """
        Draw text to a surface using either a colour or gradient.

        :param text: The text to render.
        :param col_or_grad: A colour or a colour gradient.

        :return: A surface with the text on.

        """
        text_surface = render_white_text_alpha_black_bg(font=self.font,
                                                        text=text)
        if isinstance(col_or_grad, ColourGradient):
            col_or_grad.apply_gradient_to_surface(text_surface)
        else:
            apply_colour_to_surface(col_or_grad, text_surface)
        return text_surface
    def redraw_state(self, state_str: str):
        """
        Redraws the shape's surface for a given UI state.

        :param state_str: The ID string of the state to rebuild.

        """
        text_colour_state_str = state_str + '_text'
        image_state_str = state_str + '_image'
        bg_col = self.theming[state_str + '_bg']
        border_col = self.theming[state_str + '_border']

        found_shape = None
        shape_id = None
        if 'filled_bar' not in self.theming and 'filled_bar_width_percentage' not in self.theming:
            shape_id = self.shape_cache.build_cache_id('rounded_rectangle',
                                                       self.containing_rect.size,
                                                       self.shadow_width,
                                                       self.border_width,
                                                       border_col,
                                                       bg_col,
                                                       self.corner_radius)

            found_shape = self.shape_cache.find_surface_in_cache(shape_id)
        if found_shape is not None:
            self.states[state_str].surface = found_shape.copy()
        else:
            border_corner_radius = self.corner_radius

            self.states[state_str].surface = self.base_surface.copy()

            # Try one AA call method
            aa_amount = 4
            self.border_rect = pygame.Rect((self.shadow_width * aa_amount,
                                            self.shadow_width * aa_amount),
                                           (self.click_area_shape.width * aa_amount,
                                            self.click_area_shape.height * aa_amount))

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

            dimension_scale = min(self.background_rect.width / max(self.border_rect.width, 1),
                                  self.background_rect.height / max(self.border_rect.height, 1))
            bg_corner_radius = int(border_corner_radius * dimension_scale)

            bab_surface = pygame.surface.Surface((self.containing_rect.width * aa_amount,
                                                  self.containing_rect.height * aa_amount),
                                                 flags=pygame.SRCALPHA, depth=32)
            bab_surface.fill(pygame.Color('#00000000'))
            if self.border_width > 0:
                shape_surface = self.clear_and_create_shape_surface(bab_surface,
                                                                    self.border_rect,
                                                                    0,
                                                                    border_corner_radius,
                                                                    aa_amount=aa_amount,
                                                                    clear=False)
                if isinstance(border_col, ColourGradient):
                    border_col.apply_gradient_to_surface(shape_surface)
                else:
                    apply_colour_to_surface(border_col, shape_surface)

                basic_blit(bab_surface, shape_surface, self.border_rect)

            shape_surface = self.clear_and_create_shape_surface(bab_surface,
                                                                self.background_rect,
                                                                0,
                                                                bg_corner_radius,
                                                                aa_amount=aa_amount)

            if 'filled_bar' in self.theming and 'filled_bar_width_percentage' in self.theming:
                self._redraw_filled_bar(bg_col, shape_surface)
            else:
                if isinstance(bg_col, ColourGradient):
                    bg_col.apply_gradient_to_surface(shape_surface)
                else:
                    apply_colour_to_surface(bg_col, shape_surface)

            basic_blit(bab_surface, shape_surface, self.background_rect)

            # apply AA to background
            bab_surface = pygame.transform.smoothscale(bab_surface, self.containing_rect.size)

            basic_blit(self.states[state_str].surface, bab_surface, (0, 0))

            if self.states[state_str].cached_background_id is not None:
                cached_id = self.states[state_str].cached_background_id
                self.shape_cache.remove_user_from_cache_item(cached_id)
            if (not self.has_been_resized
                    and ((self.containing_rect.width * self.containing_rect.height) < 40000)
                    and (shape_id is not None
                         and self.states[state_str].surface.get_width() <= 1024
                         and self.states[state_str].surface.get_height() <= 1024)):
                self.shape_cache.add_surface_to_cache(self.states[state_str].surface.copy(),
                                                      shape_id)
                self.states[state_str].cached_background_id = shape_id

        self.rebuild_images_and_text(image_state_str, state_str, text_colour_state_str)

        self.states[state_str].has_fresh_surface = True
        self.states[state_str].generated = True
예제 #8
0
    def rebuild(self):
        """
        Re-render the text to the label's underlying sprite image. This allows us to change what
        the displayed text is or remake it with different theming (if the theming has changed).
        """

        text_size = self.font.size(self.text)
        if text_size[1] > self.relative_rect.height or text_size[0] > self.relative_rect.width:
            width_overlap = self.relative_rect.width - text_size[0]
            height_overlap = self.relative_rect.height - text_size[1]
            warn_text = ('Label Rect is too small for text: '
                         '' + self.text + ' - size diff: ' + str((width_overlap, height_overlap)))
            warnings.warn(warn_text, UserWarning)

        new_image = pygame.surface.Surface(self.relative_rect.size,
                                           flags=pygame.SRCALPHA,
                                           depth=32)

        if isinstance(self.bg_colour, ColourGradient):
            new_image.fill(pygame.Color('#FFFFFFFF'))
            self.bg_colour.apply_gradient_to_surface(new_image)
            text_render = render_white_text_alpha_black_bg(self.font, self.text)
            if self.is_enabled:
                if isinstance(self.text_colour, ColourGradient):
                    self.text_colour.apply_gradient_to_surface(text_render)
                else:
                    apply_colour_to_surface(self.text_colour, text_render)
            else:
                if isinstance(self.disabled_text_colour, ColourGradient):
                    self.disabled_text_colour.apply_gradient_to_surface(text_render)
                else:
                    apply_colour_to_surface(self.disabled_text_colour, text_render)
        else:
            new_image.fill(self.bg_colour)
            if self.is_enabled:
                if isinstance(self.text_colour, ColourGradient):
                    text_render = render_white_text_alpha_black_bg(self.font, self.text)
                    self.text_colour.apply_gradient_to_surface(text_render)
                else:
                    if self.bg_colour.a != 255 or self.text_shadow:
                        text_render = render_white_text_alpha_black_bg(self.font, self.text)
                        apply_colour_to_surface(self.text_colour, text_render)
                    else:
                        text_render = self.font.render(self.text, True,
                                                       self.text_colour, self.bg_colour)
                        text_render = text_render.convert_alpha()
            else:
                if isinstance(self.disabled_text_colour, ColourGradient):
                    text_render = render_white_text_alpha_black_bg(self.font, self.text)
                    self.disabled_text_colour.apply_gradient_to_surface(text_render)
                else:
                    if self.bg_colour.a != 255 or self.text_shadow:
                        text_render = render_white_text_alpha_black_bg(self.font, self.text)
                        apply_colour_to_surface(self.disabled_text_colour, text_render)
                    else:
                        text_render = self.font.render(self.text, True,
                                                       self.disabled_text_colour, self.bg_colour)
                        text_render = text_render.convert_alpha()
        text_render_rect = text_render.get_rect(centerx=int(self.rect.width / 2),
                                                centery=int(self.rect.height / 2))

        if self.text_shadow:
            self._rebuild_shadow(new_image, text_render_rect)

        basic_blit(new_image, text_render, text_render_rect)

        self.set_image(new_image)
    def redraw_state(self, state_str: str):
        """
        Redraws the shape's surface for a given UI state.

        :param state_str: The ID string of the state to rebuild.

        """
        border_colour_state_str = state_str + '_border'
        bg_colour_state_str = state_str + '_bg'
        text_colour_state_str = state_str + '_text'
        image_state_str = state_str + '_image'

        found_shape = None
        shape_id = None
        if 'filled_bar' not in self.theming and 'filled_bar_width_percentage' not in self.theming:
            shape_id = self.shape_cache.build_cache_id(
                'ellipse', self.containing_rect.size, self.shadow_width,
                self.border_width, self.theming[border_colour_state_str],
                self.theming[bg_colour_state_str])

            found_shape = self.shape_cache.find_surface_in_cache(shape_id)
        if found_shape is not None:
            self.states[state_str].surface = found_shape.copy()
        else:
            self.states[state_str].surface = self.base_surface.copy()

            # Try one AA call method
            aa_amount = 4
            self.border_rect = pygame.Rect(
                (self.shadow_width * aa_amount, self.shadow_width * aa_amount),
                (self.click_area_shape.width * aa_amount,
                 self.click_area_shape.height * aa_amount))

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

            bab_surface = pygame.surface.Surface(
                (self.containing_rect.width * aa_amount,
                 self.containing_rect.height * aa_amount),
                flags=pygame.SRCALPHA,
                depth=32)
            bab_surface.fill(pygame.Color('#00000000'))
            if self.border_width > 0:
                if isinstance(self.theming[border_colour_state_str],
                              ColourGradient):
                    shape_surface = self.clear_and_create_shape_surface(
                        bab_surface,
                        self.border_rect,
                        0,
                        aa_amount=aa_amount,
                        clear=False)
                    self.theming[
                        border_colour_state_str].apply_gradient_to_surface(
                            shape_surface)
                else:
                    shape_surface = self.clear_and_create_shape_surface(
                        bab_surface,
                        self.border_rect,
                        0,
                        aa_amount=aa_amount,
                        clear=False)
                    apply_colour_to_surface(
                        self.theming[border_colour_state_str], shape_surface)
                basic_blit(bab_surface, shape_surface, self.border_rect)
            if isinstance(self.theming[bg_colour_state_str], ColourGradient):
                shape_surface = self.clear_and_create_shape_surface(
                    bab_surface, self.background_rect, 1, aa_amount=aa_amount)
                self.theming[bg_colour_state_str].apply_gradient_to_surface(
                    shape_surface)
            else:
                shape_surface = self.clear_and_create_shape_surface(
                    bab_surface, self.background_rect, 1, aa_amount=aa_amount)
                apply_colour_to_surface(self.theming[bg_colour_state_str],
                                        shape_surface)

            basic_blit(bab_surface, shape_surface, self.background_rect)
            # apply AA to background
            bab_surface = pygame.transform.smoothscale(
                bab_surface, self.containing_rect.size)

            # cut a hole in shadow, then blit background into it
            sub_surface = pygame.surface.Surface(
                ((self.containing_rect.width -
                  (2 * self.shadow_width)) * aa_amount,
                 (self.containing_rect.height -
                  (2 * self.shadow_width)) * aa_amount),
                flags=pygame.SRCALPHA,
                depth=32)
            sub_surface.fill(pygame.Color('#00000000'))
            pygame.draw.ellipse(sub_surface, pygame.Color("#FFFFFFFF"),
                                sub_surface.get_rect())
            small_sub = pygame.transform.smoothscale(
                sub_surface,
                (self.containing_rect.width - (2 * self.shadow_width),
                 self.containing_rect.height - (2 * self.shadow_width)))
            self.states[state_str].surface.blit(
                small_sub,
                pygame.Rect((self.shadow_width, self.shadow_width),
                            sub_surface.get_size()),
                special_flags=pygame.BLEND_RGBA_SUB)
            basic_blit(self.states[state_str].surface, bab_surface, (0, 0))

            if (shape_id is not None
                    and self.states[state_str].surface.get_width() <= 1024
                    and self.states[state_str].surface.get_height() <= 1024):
                self.shape_cache.add_surface_to_cache(
                    self.states[state_str].surface.copy(), shape_id)

        self.rebuild_images_and_text(image_state_str, state_str,
                                     text_colour_state_str)

        self.states[state_str].has_fresh_surface = True
        self.states[state_str].generated = True
예제 #10
0
    def rebuild_images_and_text(self, image_state_str: str, state_str: str,
                                text_colour_state_str: str):
        """
        Rebuilds any text or image used by a specific state in the drawable shape. Effectively
        this means adding them on top of whatever is already in the state's surface. As such it
        should generally be called last in the process of building up a finished drawable shape
        state.

        :param image_state_str: image ID of the state we are going to be adding images and text to.
        :param state_str: normal ID of the state we are going to be adding images and text to.
        :param text_colour_state_str: text ID of the state we are going to be adding images and
                                      text to.

        """
        # Draw any themed images
        if image_state_str in self.theming and self.theming[
                image_state_str] is not None:
            image_rect = self.theming[image_state_str].get_rect()
            image_rect.center = (int(self.containing_rect.width / 2),
                                 int(self.containing_rect.height / 2))
            basic_blit(self.states[state_str].surface,
                       self.theming[image_state_str], image_rect)
        # Draw any text
        if 'text' in self.theming and 'font' in self.theming and self.theming[
                'text'] is not None:
            if len(self.theming['text']
                   ) > 0 and text_colour_state_str in self.theming:
                text_surface = render_white_text_alpha_black_bg(
                    font=self.theming['font'], text=self.theming['text'])
                if isinstance(self.theming[text_colour_state_str],
                              ColourGradient):
                    self.theming[
                        text_colour_state_str].apply_gradient_to_surface(
                            text_surface)
                else:
                    apply_colour_to_surface(
                        self.theming[text_colour_state_str], text_surface)
            else:
                text_surface = None

            if 'text_shadow' in self.theming:
                text_shadow = render_white_text_alpha_black_bg(
                    font=self.theming['font'], text=self.theming['text'])
                apply_colour_to_surface(self.theming['text_shadow'],
                                        text_shadow)

                basic_blit(
                    self.states[state_str].surface, text_shadow,
                    (self.aligned_text_rect.x, self.aligned_text_rect.y + 1))
                basic_blit(
                    self.states[state_str].surface, text_shadow,
                    (self.aligned_text_rect.x, self.aligned_text_rect.y - 1))
                basic_blit(
                    self.states[state_str].surface, text_shadow,
                    (self.aligned_text_rect.x + 1, self.aligned_text_rect.y))
                basic_blit(
                    self.states[state_str].surface, text_shadow,
                    (self.aligned_text_rect.x - 1, self.aligned_text_rect.y))

            if text_surface is not None and self.aligned_text_rect is not None:
                basic_blit(self.states[state_str].surface, text_surface,
                           self.aligned_text_rect)
예제 #11
0
    def __init__(self,
                 font_size: int,
                 font_name: str,
                 chunk: str,
                 style: CharStyle,
                 colour: Union[pygame.Color, ColourGradient],
                 bg_colour: Union[pygame.Color, ColourGradient],
                 is_link: bool,
                 link_href: str,
                 link_style: CharStyle,
                 position: Tuple[int, int],
                 font_dictionary: UIFontDictionary):

        self.style = style
        self.chunk = chunk
        self.font_size = font_size
        self.font_name = font_name
        self.is_link = is_link
        self.link_href = link_href
        self.link_style = link_style

        self.font = font_dictionary.find_font(font_size, font_name,
                                              self.style.bold, self.style.italic)

        if self.is_link:
            self.normal_colour = self.link_style['link_text']
            self.hover_colour = self.link_style['link_hover']
            self.selected_colour = self.link_style['link_selected']
            self.link_normal_underline = self.link_style['link_normal_underline']
            self.link_hover_underline = self.link_style['link_hover_underline']
        else:
            self.normal_colour = colour
            self.hover_colour = None
            self.selected_colour = None
            self.link_normal_underline = False
            self.link_hover_underline = False

        self.colour = self.normal_colour
        self.bg_colour = bg_colour
        self.position = position

        self.is_hovered = False
        self.is_selected = False

        if self.style.underline or (self.is_hovered and self.link_hover_underline) or \
                (self.link_normal_underline and not self.is_hovered):
            self.font.set_underline(True)

        if len(self.chunk) > 0:
            if not isinstance(self.colour, ColourGradient):
                if isinstance(self.bg_colour, ColourGradient) or self.bg_colour.a != 255:
                    self.rendered_chunk = render_white_text_alpha_black_bg(self.font, self.chunk)
                    apply_colour_to_surface(self.colour, self.rendered_chunk)
                else:
                    self.rendered_chunk = self.font.render(self.chunk,
                                                           True,
                                                           self.colour,
                                                           self.bg_colour).convert_alpha()
            else:
                self.rendered_chunk = render_white_text_alpha_black_bg(self.font, self.chunk)
                self.colour.apply_gradient_to_surface(self.rendered_chunk)
        else:
            self.rendered_chunk = pygame.surface.Surface((0, 0),
                                                         flags=pygame.SRCALPHA,
                                                         depth=32)
        metrics = self.font.metrics(self.chunk)
        self.ascent = self.font.get_ascent()
        self.width = self.font.size(self.chunk)[0]
        self.height = self.font.size(self.chunk)[1]
        self.advance = 0
        for i in range(len(self.chunk)):
            if len(metrics[i]) == 5:
                self.advance += metrics[i][4]

        self.rect = pygame.Rect(self.position, (self.width, self.height))
        self.metrics_changed_after_redraw = False

        self.unset_underline_style()