Search code examples
pythontextpygamedraw

how to shrink a textbox in pygame when you delete text?


When the user enters or deletes text I want the rectangle to shrink and grow depending on how much text is entered. This process works just fine except for one small error. When you delete text an empty rectangle forms in place of the letter that was deleted. How do I fix this?

from pygame import *

init()
screen = display.set_mode((800, 600))

name_font = font.Font(None, 32)
name_text = ''

class Rectangle:

    def __init__(self, x, y):
        self.active = False
        self.x = x
        self.y = y
        self.text_surface = name_font.render(name_text, True, (255, 255, 255))
        self.rect_width = max(140, 10 + self.text_surface.get_width())
        self.input_rect = Rect(x, y, self.rect_width, 32)
        self.input_rect.w = self.text_surface.get_width() + 10
        self.click_value = 10


    def naming(self, events):
        global rect_1
        global name_text

        for e in events:
            if e.type == MOUSEBUTTONDOWN:
                if self.input_rect.x + self.click_value >= mx >= self.input_rect.x - 10 and self.input_rect.y + 26 >= my >= self.input_rect.y - 10:
                    self.active = True
                else:
                    self.active = False

            if e.type == KEYDOWN:
                if self.active:
                    if e.key == K_BACKSPACE:
                        name_text = name_text[:-1]
                        screen.fill((0, 0, 0))
                    else:
                        name_text += e.unicode
                        self.click_value += 7

                    self.text_surface = name_font.render(name_text, True, (255, 255, 255))

    def draw(self, screen):
        draw.rect(screen, (0, 0, 0), self.input_rect, 0)
        draw.rect(screen, (255, 255, 255), self.input_rect, 2)
        self.input_rect.w = self.text_surface.get_width() + 10
        screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))


rect_1 = Rectangle(200, 200)

while True:
    mx, my = mouse.get_pos()
    events = event.get()
    for e in events:
        if e.type == QUIT:
            quit()

    rect_1.naming(events)
    rect_1.draw(screen)
    display.update()
    time.delay(1)

Solution

  • You have to compute the width of the input rectangle before you draw it:

    class Rectangle:
        # [...]
    
        def draw(self, screen):
            self.input_rect.w = self.text_surface.get_width() + 10
            draw.rect(screen, (0, 0, 0), self.input_rect, 0)
            draw.rect(screen, (255, 255, 255), self.input_rect, 2)
            screen.blit(self.text_surface, (self.input_rect.x + 5, self.input_rect.y + 5))
    

    However, the wrong order doesn't matter if you were to clear the screen every frame. The bug would hardly be noticeable as it would only be visible for a single frame. Clear the screen in every frame and not in case of K_BACKSPACE:

    while True:
        mx, my = mouse.get_pos()
        events = event.get()
        for e in events:
            if e.type == QUIT:
                quit()
    
        rect_1.naming(events)
    
        screen.fill((0, 0, 0)) # <---
        rect_1.draw(screen)
        display.update()
        time.delay(1)