Skip to content
Advertisement

Converting scroll bar coordinates to mouse coordinates

I’m trying to make a scroll bar and at the moment, the scroll works by changing the coordinates when blitting (as opposed to changing the actual rect coordinates). This means that rect collisions for buttons do not work when they are moved. I am attempting to combat this by calculating the percentage that the scroll bar has scrolled, converting that to some multiplier or screen coordinate, and then getting the mouse position.

Some notes: Self.bar is the actual slider handle (the small thing you use to scroll) Self.rect is the entire slider, and its height is equal to screen height Self.total_h is the total height that the scroll bar needs to scroll, for example if it needed to scroll to 2x the screen height then total_h would equal screen_height * 2.

Some code I have tried so far:

# Calculate the distance between the top of the handle and the top of the overall bar and divide by the handle height 
# (shortened from ((self.bar.rect.top - self.rect.top) / self.rect.h) * (self.rect.h / self.bar.rect.h) which makes more intuitive sense.
self.scroll_percent = ((self.bar.rect.top - self.rect.top) / self.bar.rect.h)
# These all do not work:
# pos_y = pg.mouse.get_pos()[1] * self.scroll_percent
# pos_y = pg.mouse.get_pos()[1] * (self.total_h / self.scroll_percent)
# pos_y = (self.total_h / self.scroll_percent) * pg.mouse.get_pos()[1]
# etc

The logic just doesn’t make sense to me, and I’ve got no idea how to do this. To clarify, my goal is to allow the user to scroll the screen using a scroll bar, and depending on the scroll bar’s position, we change the mouse pos accordingly.

Advertisement

Answer

I don’t really understand why you bother with some percentage ? If I understood correctly you are only scrolling up and down so the only thing you need to know is the y offset, which is 0 when the scroll bar is at the top and then it is just the y value at which you are blitting your surface. So simply remove the y offset to your mouse y when you check for collision. Maybe I missed something ?

If I understood corretly, here is an simple example of what to do : (I didn’t recreate the scroll bar since you said you’ve got this part working. I just made the surface go up automatically. I’m sure you will figure out a way to integrate this solution to your own code)

# General import
import pygame as pg
import sys

# Init
pg.init()

# Display
screen = pg.display.set_mode((500, 500))
FPS = 30
clock = pg.time.Clock()

# Surface declaration
drawing_surface = pg.Surface((screen.get_width(), screen.get_height() * 2))
drawing_surface.fill((255,0,0))
drawing_surface_y = 0

# Button
test_btn = pg.Rect(20, 400, 100, 40)

# Main functions
def update():
    global drawing_surface_y
    drawing_surface_y -= 1

def draw():
    # Clear the screen
    screen.fill((0,0,0))

    # Render the button
    pg.draw.rect(drawing_surface, (0,0,255), test_btn)

    # Blit the drawing surface
    screen.blit(drawing_surface, (0, drawing_surface_y))

def handle_input():
    for evt in pg.event.get():
        if evt.type == pg.QUIT:
            exit()
        if evt.type == pg.MOUSEBUTTONDOWN:
            if evt.button == 1:
                on_click()

def on_click():
    mx, my = pg.mouse.get_pos()
    if test_btn.collidepoint(mx, my - drawing_surface_y):
        print("Test button has been clicked")

def exit():
    pg.quit()
    sys.exit()

# Other functions

# Main loop
if __name__ == "__main__":
    while True:
        handle_input()
        update()
        draw()
        pg.display.update()
        clock.tick(FPS)

Test this code and let me know if it answers your question !

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement