Search code examples
pythonpygamescrollable

Make a rect object scrollable


I am drawing a chess move log onto a rect object - however after a certain point the text goes off of the bottom and gets cut off. I was wondering if its possible to make the rect surface scroll able so I can see the whole move log.

Here is the code for drawing the move log :

This is what I mean

As you can see after the 21st move - the log goes off the bottom of the move log area

def drawMoveLog(screen, gs, font): #draws move log
    moveLogArea = p.Rect(BOARD_WIDTH, 0, MOVE_LOG_PANEL_WIDTH, MOVE_LOG_PANEL_HEIGHT)
    p.draw.rect(screen , p.Color("gray"), moveLogArea)
    moveLog = gs.moveLog
    moveText = []
    for i in range(0, len(moveLog), 2): #go through move log 2 at a time
        moveString = "|| " + str(i//2 + 1) + ". " + str(moveLog[i]) + " "# to keep move 2 and 2 the same
        if i + 1 < len(moveLog): # before i continue want to make sure black moved
            moveString += str(moveLog[i + 1]) + " "
        moveText.append(moveString)

    movesPerRow = 1
    padding = 5
    lineSpacing = 4
    textY = padding
    #make 3 moves go in 1 line
    for i in range(0, len(moveText), movesPerRow):
        text = ""
        for j in range (movesPerRow):
            if i + j < len(moveText):
                text += moveText[i+j]
        textObject = font.render(text, True, p.Color('Black'))
        textLocation = moveLogArea.move(padding, textY)
        screen.blit(textObject, textLocation)
        textY += textObject.get_height() + lineSpacing


Solution

  • Create a function that renders the text in a transparent pygame.Surface high enough to contain the full text. To create a transparent Surface object you have to set the flag pygame.SRCALPHA:

    def createMoveLog(gs, font): #draws move log
        moveLog = gs.moveLog
        moveText = []
        for i in range(0, len(moveLog), 2): #go through move log 2 at a time
            moveString = "|| " + str(i//2 + 1) + ". " + str(moveLog[i]) + " "# to keep move 2 and 2 the same
            if i + 1 < len(moveLog): # before i continue want to make sure black moved
                moveString += str(moveLog[i + 1]) + " "
            moveText.append(moveString)
    
        movesPerRow = 1
        padding = 5
        lineSpacing = 4
        no_of_lines = (len(moveText)+movesPerRow-1) // movesPerRow
        line_height = font.get_height() + lineSpacing 
        text_size = (MOVE_LOG_PANEL_WIDTH, no_of_lines * line_height + 2*padding)
        text_surface = p.Surface(text_size, p.SRCALPHA)
    
        textY = padding
        #make 3 moves go in 1 line
        for i in range(0, len(moveText), movesPerRow):
            text = ""
            for j in range (movesPerRow):
                if i + j < len(moveText):
                    text += moveText[i+j]
            textObject = font.render(text, True, p.Color('Black'))
            text_surface.blit(textObject, (0, textY))
            textY += textObject.get_height() + lineSpacing
    
        return text_surface
    

    Render the full text and use pygame.Surface.subsurface to create a new surface that references a rectangular area with the height MOVE_LOG_PANEL_HEIGHT.
    The scroll argument is a value in the range [0.0, 1.0] and is used to scroll the text linearly. When the scrolling is 0.0, the top of the text is displayed, and when the scrolling is 1.0, the bottom of the text is displayed:

    def drawMoveLog(screen, gs, font, scroll): #draws move log
        moveLogArea = p.Rect(BOARD_WIDTH, 0, MOVE_LOG_PANEL_WIDTH, MOVE_LOG_PANEL_HEIGHT)
        p.draw.rect(screen , p.Color("gray"), moveLogArea)
    
        text_surface = createMoveLog(gs, font)
        dy = text_surface.get_height() - MOVE_LOG_PANEL_HEIGHT
        if dy > 0:
            text_offset = int(dy * scroll) 
            test_rect = text_surface.get_rect()
            sub_rect = p.Rect(0, text_offset, MOVE_LOG_PANEL_WIDTH, MOVE_LOG_PANEL_HEIGHT)
            sub_text_surface = text_surface.subsurface(sub_rect)
            screen.blit(sub_text_surface, moveLogArea)
    
        else:
            screen.blit(text_surface, moveLogArea)
    

    Compare the rendering of

    moveLog = ["d3", "Nh6", "e3", "Rg8", "f3", "b6", "g3", "a5", "h3", "Ra6", "c3", "g6", "b3", "a4"]
    

    with scroll = 0.0 and scroll = 1.0.