Repository: matplotlib/matplotlib
File: lib/matplotlib/backend_bases.py (Python)

Code before:
def scroll_handler(event, canvas=None, toolbar=None):
    ax = event.inaxes
    if ax is None:
        return
    if ax.name != "rectilinear":
        # zooming is currently only supported on rectilinear axes
        return

    if toolbar is None:
        toolbar = (canvas or event.canvas).toolbar

    if toolbar is None or toolbar.mode == _Mode.NONE:
        return

    if event.key is None:  # zoom towards the mouse position
        toolbar.push_current()

        xmin, xmax = ax.get_xlim()
        ymin, ymax = ax.get_ylim()
        xmin, ymin = ax.transScale.transform((xmin, ymin))
        xmax, ymax = ax.transScale.transform((xmax, ymax))

        # mouse position in scaled (e.g., log) data coordinates
        x, y = ax.transScale.transform((event.xdata, event.ydata))

        scale_factor = 1.0 - 0.1 * event.step
        new_xmin = x - (x - xmin) * scale_factor
        new_xmax = x + (xmax - x) * scale_factor
        new_ymin = y - (y - ymin) * scale_factor
        new_ymax = y + (ymax - y) * scale_factor

        inv_scale = ax.transScale.inverted()
        new_xmin, new_ymin = inv_scale.transform((new_xmin, new_ymin))
        new_xmax, new_ymax = inv_scale.transform((new_xmax, new_ymax))

        ax.set_xlim(new_xmin, new_xmax)
        ax.set_ylim(new_ymin, new_ymax)

        ax.figure.canvas.draw_idle()


class NonGuiException(Exception):
    """Raised when trying show a figure in a non-GUI backend."""
    pass


class FigureManagerBase:
    """
    A backend-independent abstraction of a figure container and controller.

    The figure manager is used by pyplot to interact with the window in a

Review comment (suggestion):
I think the scale factor should be something like `1.1**event.step` (or `0.9**event.step`) so that scrolling one up and one down restores the original view.

Code after:
def scroll_handler(event, canvas=None, toolbar=None):
    ax = event.inaxes
    if ax is None:
        return
    if ax.name != "rectilinear":
        # zooming is currently only supported on rectilinear axes
        return

    if toolbar is None:
        toolbar = (canvas or event.canvas).toolbar

    if toolbar is None:
        # technically we do not need a toolbar, but until wheel zoom was
        # introduced, any interactive modification was only possible through
        # the toolbar tools. For now, we keep the restriction that a toolbar
        # is required for interactive navigation.
        return

    if event.key == "control":  # zoom towards the mouse position
        toolbar.push_current()

        xmin, xmax = ax.get_xlim()
        ymin, ymax = ax.get_ylim()
        (xmin, ymin), (xmax, ymax) = ax.transScale.transform(
            [(xmin, ymin), (xmax, ymax)])

        # mouse position in scaled (e.g., log) data coordinates
        x, y = ax.transScale.transform((event.xdata, event.ydata))

        scale_factor = 0.85 ** event.step
        new_xmin = x - (x - xmin) * scale_factor
        new_xmax = x + (xmax - x) * scale_factor
        new_ymin = y - (y - ymin) * scale_factor
        new_ymax = y + (ymax - y) * scale_factor

        inv_scale = ax.transScale.inverted()
        (new_xmin, new_ymin), (new_xmax, new_ymax) = inv_scale.transform(
            [(new_xmin, new_ymin), (new_xmax, new_ymax)])

        ax.set_xlim(new_xmin, new_xmax)
        ax.set_ylim(new_ymin, new_ymax)

        ax.figure.canvas.draw_idle()


class NonGuiException(Exception):
    """Raised when trying show a figure in a non-GUI backend."""
    pass


class FigureManagerBase: