def scroll(self, dy, collapse_expanded_child=True):
        """Scroll children up/down if needed.

        dy (integer)
            represent how much to move in y axis.

        collapse_expanded_child (boolean, defaults to True)
            defines whether or not the expanded child must be
            collapsed when scrolling. It is True when used
            normally, that is, when autoscrolling by hovering
            the scroll arrows, so the expanded child
            collapses.

            If we don't collapse the body of the moving
            expanded child when autoscrolling the arrows we
            would have to add extra complexity to the code
            in order to control/predict the position of the
            moving body.

            We deem such complexity unnecessary because we
            assume that when users scroll the menu they do
            so in order to reach something which isn't
            visible, otherwise there would be no need to
            scroll. Thus, getting rid of the excess of what
            is visible (the body of the expanded child)
            shouldn't cause any problem.

            There is, though, a scenario wherein we don't
            collapse the body of the expanded child (that
            is the parameter is set to False: only when
            scrolling using the mousewheel. This is so
            because it would otherwise be confusing/
            unpleasant to have the expanded body suddenly
            vanish when scrolling the menu with the mouse
            still hovering the child.

            When scrolling the menu with the mousewheel,
            the child may still collapse though, even with
            this argument being False, that is, when the
            menu scrolls, the child changes its position and
            other child may end up being hovered by the
            mouse, in which case the now unhovered child
            will naturally collapse. This is a desired
            effect.

            This parameter and its effect is not a
            requirement though, but rather an usability
            measure so the menu behaviour is more pleasing/
            less confusing.
        """
        ### check if there's need to scroll based on
        ### presence/absence of first or last child
        ### in the scroll area

        first_rect = self.children[0].rect
        last_rect = self.children[-1].rect

        ## if scrolling children into the positive y
        ## direction (children are being moved down), but
        ## the first rect is already on the scroll area,
        ## cancel the scrolling by returning early
        if dy > 0:
            if self.scroll_area.contains(first_rect):
                return

        ## if scrolling children into the negative y
        ## direction (moving children up), but the last
        ## rect is already on the scroll area, cancel the
        ## scrolling by returning early
        elif dy < 0:
            if self.scroll_area.contains(last_rect):
                return

        ### provided we didn't return early, the scrolling
        ### is legal, so move the rects using the provided
        ### delta for the y axis
        self.children.rect.move_ip(0, dy)

        ### eliminate excessive scrolling: we close any gap
        ### that may exist between first child/first arrow
        ### or last child/last arrow when they are scrolled
        ### inwards the scroll area
        self.fix_scrolling()

        ### if requested, collapse the expanded child (see
        ### the docstring of this method about this
        ### collapse_expanded_child parameter)

        if collapse_expanded_child:

            ## iterate over each child checking the
            ## existence of the 'is_expanded' attribute
            ## and executing the collapsing method of the
            ## child if such attribute is True

            for child in self.children:

                # try storing the value of the attribute
                try:
                    is_expanded = child.is_expanded

                # if it doesn't exist, just pass
                except AttributeError:
                    pass

                # if the attribute exists and its value is
                # True, collapse the child using the proper
                # method

                else:

                    if is_expanded:
                        child.collapse_self_and_children()

        ### if the children shouldn't be collapsed though,
        ### we should reposition the body of each child
        ### recursively; this is a measure to adjust the
        ### children, since they must follow the position
        ### of their scrolling parent, when the parent is
        ### being hovered by the mouse, an usability
        ### measure

        else:

            ## iterate over each child checking the
            ## existence of the 'reposition_body' method
            ## and executing it if it is found

            for child in self.children:

                # store method if it exists
                try:
                    method = child.reposition_body

                # if method doesn't exist, just pass
                except AttributeError:
                    pass

                # otherwise execute the stored method
                else:
                    method()

        ### collapse the expanded child if there is one and
        ### it doesn't collide with the scroll area (this
        ### is needed to prevent the expanded body to be
        ### still hoverable when the expanded child rect is
        ### not visible); the ommission of this measure
        ### would in specific cases cause the expanded body
        ### to be hoverable while invisible, causing
        ### confusion and preventing the user to use the
        ### menu properly

        for child in self.children:

            ## try retrieving the value of the 'is_expanded'
            ## attribute
            try:
                is_expanded = child.is_expanded

            ## if it doesn't even exist, just pass
            except AttributeError:
                pass

            ## otherwise, if it exists and is True and the
            ## expanded child don't collide with the scroll
            ## area, collapse it

            else:

                # evaluate condition
                child_touches_scroll_area = self.scroll_area.colliderect(child.rect)

                # check if conditions are met and execute
                # the collapsing method

                if is_expanded and not child_touches_scroll_area:
                    child.collapse_self_and_children()
Scroll children up/down if needed.

        dy (integer)
            represent how much to move in y axis.

        collapse_expanded_child (boolean, defaults to True)
            defines whether or not the expanded child must be
            collapsed when scrolling. It is True when used
            normally, that is, when autoscrolling by hovering
            the scroll arrows, so the expanded child
            collapses.

            If we don't collapse the body of the moving
            expanded child when autoscrolling the arrows we
            would have to add extra complexity to the code
            in order to control/predict the position of the
            moving body.

            We deem such complexity unnecessary because we
            assume that when users scroll the menu they do
            so in order to reach something which isn't
            visible, otherwise there would be no need to
            scroll. Thus, getting rid of the excess of what
            is visible (the body of the expanded child)
            shouldn't cause any problem.

            There is, though, a scenario wherein we don't
            collapse the body of the expanded child (that
            is the parameter is set to False: only when
            scrolling using the mousewheel. This is so
            because it would otherwise be confusing/
            unpleasant to have the expanded body suddenly
            vanish when scrolling the menu with the mouse
            still hovering the child.

            When scrolling the menu with the mousewheel,
            the child may still collapse though, even with
            this argument being False, that is, when the
            menu scrolls, the child changes its position and
            other child may end up being hovered by the
            mouse, in which case the now unhovered child
            will naturally collapse. This is a desired
            effect.

            This parameter and its effect is not a
            requirement though, but rather an usability
            measure so the menu behaviour is more pleasing/
            less confusing.
        
nodezator/menu/submenu/scroll.py