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

        dx, dy (integers)
            represent how much to move in x and y axes,
            respectively.

        collapse_expanded_child (boolean, defaults to True)
            defines whether or not the active menu 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 (that is, the active menu).

            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.
        """
        ### retrieve the last and first child rects

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

        ### check need to scroll; look into the appropriate
        ### axis depending on the whether the menu manager
        ### is a menubar

        if self.is_menubar:

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

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

        else:

            ## 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
        ### deltas for each axis
        self.children.rect.move_ip(dx, 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 (in
        ### this case the active menu; see docstring about
        ### the collapse_expanded_child parameter)
        if collapse_expanded_child:
            self.collapse_active_menu()

        ### if the expanded child shouldn't be collapsed
        ### though, we should reposition the body of each
        ### child recursively; this is a measure adopted so
        ### the children follow the position of their
        ### scrolling parent, when the mouse is hovering
        ### the parent, an usability measure
        else:
            self.reposition_children_bodies()

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

        if self.active_menu and not self.scroll_area.colliderect(self.active_menu.rect):

            self.collapse_active_menu()
Scroll children if needed.

        dx, dy (integers)
            represent how much to move in x and y axes,
            respectively.

        collapse_expanded_child (boolean, defaults to True)
            defines whether or not the active menu 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 (that is, the active menu).

            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/scroll.py