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

Code before:
            The parent Axes for the widget.
        labels : list of str
            The labels of the check buttons.
        actives : list of bool, optional
            The initial check states of the buttons. The list must have the
            same length as *labels*. If not given, all buttons are unchecked.
        layout : None or "vertical" or "horizontal" or (int, int), default: None
            The layout of the check buttons. Options are:

            - ``None``: Use legacy vertical layout (default, keeps backward
              compatibility with fixed button and label positions).
            - ``"vertical"``: Arrange buttons in a single column with
              dynamic positioning based on text widths.
            - ``"horizontal"``: Arrange buttons in a single row with
              dynamic positioning based on text widths.
            - ``(rows, cols)`` tuple: Arrange buttons in a grid with the
              specified number of rows and columns. Buttons are placed
              left-to-right, top-to-bottom with dynamic positioning.

            The layout options "vertical", "horizontal" and ``(rows, cols)``
            temporarily manipulate the figure and redraw to determine
            exact text sizes. This is usually ok, but may cause side-effects
            and has a slight performance impact.

            .. admonition:: Provisional
                The the new layout options are provisional. Their algorithmic
                behavior, including if and when a figure redraw happens, as well
                as the the exact positions of buttons and labels may still change
                without prior warning.

            .. versionadded:: 3.11
        useblit : bool, default: True
            Use blitting for faster drawing if supported by the backend.
            See the tutorial :ref:`blitting` for details.

            .. versionadded:: 3.7

        label_props : dict of lists, optional
            Dictionary of `.Text` properties to be used for the labels. Each
            dictionary value should be a list of at least a single element. If
            the list is of length M, its values are cycled such that the Nth
            label gets the (N mod M) property.

            .. versionadded:: 3.7
        frame_props : dict, optional
            Dictionary of scatter `.Collection` properties to be used for the
            check button frame. Defaults (label font size / 2)**2 size, black
            edgecolor, no facecolor, and 1.0 linewidth.

            .. versionadded:: 3.7
        check_props : dict, optional

Review comment (suggestion):
```suggestion
                The new layout options are provisional. Their algorithmic
```

Code after:
        if self._useblit:
            self.connect_event('draw_event', self._clear)

        self._observers = cbook.CallbackRegistry(signals=["clicked"])

    def _init_layout(self, layout, labels, label_props):

        label_props = _expand_text_props(label_props)

        if layout is None:
            # legacy hard-coded vertical layout
            self._buttons_xs = [0.15] * len(labels)
            self._buttons_ys = np.linspace(1, 0, len(labels)+2)[1:-1]
            self.labels = [
                self.ax.text(0.25, y, label, transform=self.ax.transAxes,
                        horizontalalignment="left", verticalalignment="center",
                        **props)
                for y, label, props in zip(self._buttons_ys, labels, label_props)]
            return

        # New layout algorithm with text measurement
        # Parse layout parameter
        n_labels = len(labels)
        match layout:
            case "vertical":
                n_rows, n_cols = n_labels, 1
            case "horizontal":
                n_rows, n_cols = 1, n_labels
            case (int() as n_rows, int() as n_cols):
                if n_rows * n_cols < n_labels:
                    raise ValueError(
                        f"layout {layout} has {n_rows * n_cols} positions but "
                        f"{n_labels} labels were provided"
                    )
            case _:
                raise ValueError(
                    "layout must be None, 'vertical', 'horizontal', or a (rows, cols) "
                    f"tuple; got {layout!r}")

        # Define spacing in points for DPI-independent sizing
        fig = self.ax.get_figure(root=False)
        axes_width_display = 72 * self.ax.bbox.transformed(
            fig.dpi_scale_trans.inverted()
        ).width
        left_margin_display = 11  # points
        button_text_offset_display = 5.5  # points
        col_spacing_display = 11  # points

        # Convert to axes coordinates
        left_margin = left_margin_display / axes_width_display
        button_text_offset = button_text_offset_display / axes_width_display