gtkpygtkgtkscrolledwindow

Auto scroll a gtkscrolledwindow


I have a form interface with a large number of horizontal drop down and text boxes that are inside a GTKscrolledWindow. The form has to be aligned horizontally because there are an unlimited number of forms in the window (think tabel of forms).

Is it (possible/how do I) auto-scroll the GTKScrolledWindow to the right when the operator tabs to the next form control which is off to the right of the screen.?

Related question would be how do I detect if the focused element (button etc) is visible in it's parent scrolledwindow.

Thanks.


Solution

  • Here's what I did, based on Michy's answer.

    To make a scrolled window auto-scroll, run in the constructor:

    FocusScroll(scrolledwindow).
    
        class FocusScroll(object):
        """
        Get a gtk.ScrolledWindow which contains a gtk.Viewport.
        Attach event handlers which will scroll it to show the focused widget.
        """
        def __init__(self, scrolledwindow):
            self.scrolledwindow = scrolledwindow
            self.viewport = scrolledwindow.get_child()
            assert isinstance(self.viewport, gtk.Viewport)
            self.main_widget = self.viewport.get_child()
            self.vadj = scrolledwindow.get_vadjustment()
            self.window = self.get_window(scrolledwindow)
    
            self.viewport.connect('set-focus-child', self.on_viewport_set_focus_child)
    
        def get_window(self, widget):
            if isinstance(widget, gtk.Window):
                return widget
            else:
                return self.get_window(widget.get_parent())
    
        def is_child(self, widget, container):
            """
            Go recursively over all children of container, to check if widget is
            a child of it.
            """
            for child in container.get_children():
                if child is widget:
                    return True
                elif isinstance(child, gtk.Container):
                    if self.is_child(widget, child):
                        return True
            else:
                return False
    
        def on_viewport_set_focus_child(self, _viewport, _child):
            idle_add(self.scroll_slide_viewport)
    
        def scroll_slide_viewport(self):
            """Scroll the viewport if needed to see the current focused widget"""
            widget = self.window.get_focus()
            if not self.is_child(widget, self.main_widget):
                return
    
            _wleft, wtop = widget.translate_coordinates(self.main_widget, 0, 0)
            wbottom = wtop + widget.get_allocation().height
    
            top = self.vadj.value
            bottom = top + self.vadj.page_size
    
            if wtop < top:
                self.vadj.value = wtop
            elif wbottom > bottom:
                self.vadj.value = wbottom - self.vadj.page_size