Page 1 of 1

Maintaining Focus in Scrolling Viewport

Posted: Fri Apr 26, 2024 1:49 pm
by snotwurm
Hi all! I'm doing some contract work on a published VN, and we're trying to improve the feel of the controller support. The game displays all the dialogue choice options in a scrolling viewport, and we are attempting to set the viewport scroll directly based on when one of the choices is focused.

Our current approach is to create an action that fires when the textbutton is hovered.

In choices screen here:

Code: Select all

screen choice(items):
    style_prefix "choice"
    default yadj = ui.adjustment()
    default hoveredrect = (1, 2, 3, 100)
    default hoveredy = 100

    key "K_UP" action ScrollViewport(yadj, 'up', items)
    key "K_DOWN" action ScrollViewport(yadj, 'down', items)
    key "pad_lefty_pos" action ScrollViewport(yadj, 'down', items)
    key "pad_lefty_neg" action ScrollViewport(yadj, 'up', items)
    key "pad_righty_pos" action ScrollViewport(yadj, 'down', items)
    key "pad_righty_neg" action ScrollViewport(yadj, 'up', items)
    key "pad_dpup_press" action ScrollViewport(yadj, 'up', items)
    key "pad_dpdown_press" action ScrollViewport(yadj, 'down', items)
    add "gui/choices_backdrop.png"
    viewport yadjustment yadj:
        draggable True
        mousewheel True
        scrollbars "vertical"
        xalign 0.0
        xsize 430
        ysize 650
        xpos 1480
        ypos 25
        vbox:
            for idx, i in enumerate(items):
                if idx == 0:
                    textbutton i.caption id "choice_" + str(idx) action i.action default_focus True hovered OnChoiceFocus(idx, yadj)
                        #hovered SetVariable("captured", renpy.focus_coordinates())
                else:
                    textbutton i.caption id "choice_" + str(idx) action i.action hovered OnChoiceFocus(idx, yadj)
                        #hovered SetVariable("captured", renpy.focus_coordinates())
And scrolling viewport code here:

Code: Select all

######## Viewport Scrolling ##############
init python:
    class OnChoiceFocus(Action):
        def __init__(self, index, yadj):
            self.index = index
            self.yadj = yadj

        def __call__(self):
            widget = renpy.get_widget("choice", "choice_" + str(self.index))
            dispProps = renpy.get_displayable_properties("choice_" + str(self.index))
            renpy.log(dispProps)
            # self.yadj.change(dispProps["offset"])
            # GetFocusRect("current_choice")

    class ScrollViewport(Action):

        def __init__(self, yadj, scroll_dir, items, step=(gui.interface_text_size)*3):
        #def __init__(self, yadj, scroll_dir, step):
            self.yadj = yadj
            self.scroll_dir = scroll_dir
            self.step = step
            self.items = items

        def __call__(self):
            #yadj = renpy.focus_coordinates()[3]
            # Don't confuse dir with the other use case for "up" which
            # refers to the state of the key (pressed/released)
            # print renpy.get_focus_rect()

            if self.scroll_dir == "up":
                renpy.display.behavior.queue_event('focus_up', up=False)
            else:
                renpy.display.behavior.queue_event('focus_down', up=False)

            return
In the code above when a textbutton is hovered, we're trying to get the offset of that textbutton relative to the viewport, and scroll the viewport to that position, to ensure the choice is always visible.

We're having trouble getting the offset of the textbutton. Is there a way to get the offset of the returned widget (which is `Text`, rather than `Button`) to then use in the adjustment for the viewport?