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

Code before:
        if layout == "vertical":
            n_rows, n_cols = n_labels, 1
        elif layout == "horizontal":
            n_rows, n_cols = 1, n_labels
        else:
            raise ValueError(bad_layout_raise_msg)
    elif isinstance(layout, tuple) and len(layout) == 2:
        n_rows, n_cols = layout
        if not (isinstance(n_rows, int) and isinstance(n_cols, int)):
            raise TypeError(
                f"layout tuple must contain two integers; got {layout!r}"
            )
        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"
            )
    else:
        raise ValueError(bad_layout_raise_msg)

    # Define spacing in display units (pixels) for consistency
    # across different axes sizes
    axes_width_display = ax.bbox.width
    left_margin_display = 15  # pixels
    button_text_offset_display = 6.5  # pixels
    col_spacing_display = 15  # pixels

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

    # Create temporary text objects to measure widths
    temp_texts = []
    for label, props in zip(labels, label_props):
        temp_texts.append(ax.text(
            0,
            0,
            label,
            transform=ax.transAxes,
            **props,
        ))
    # Force a draw to get accurate text measurements
    ax.figure.canvas.draw()

    # Calculate max text width per column (in axes coordinates)
    col_widths = []
    for col_idx in range(n_cols):
        col_texts = []
        for row_idx in range(n_rows):
            label_idx = row_idx * n_cols + col_idx

Review comment (suggestion):
Pixels are unsafe for mixed-DPI setups; points are probably better if you scale by `figure.dpi_scale_trans`.

Code after:
            # Avoid spurious warnings if keymaps are getting deprecated.
            with _api.suppress_matplotlib_deprecation_warning():
                stack.enter_context(mpl.rc_context(
                    {k: [] for k in mpl.rcParams if k.startswith("keymap.")}))

    def stop_typing(self):
        if self.capturekeystrokes:
            self._on_stop_typing()
            self._on_stop_typing = None
            notifysubmit = True
        else:
            notifysubmit = False
        self.capturekeystrokes = False
        self.cursor.set_visible(False)
        self.ax.get_figure(root=True).canvas.draw()
        if notifysubmit and self.eventson:
            # Because process() might throw an error in the user's code, only
            # call it once we've already done our cleanup.
            self._observers.process('submit', self.text)

    @_call_with_reparented_event
    def _click(self, event):
        if self.ignore(event):
            return
        if not self.ax.contains(event)[0]:
            self.stop_typing()
            return
        if not self.eventson:
            return
        if event.canvas.mouse_grabber != self.ax:
            event.canvas.grab_mouse(self.ax)
        if not self.capturekeystrokes:
            self.begin_typing()
        self.cursor_index = self.text_disp._char_index_at(event.x)
        self._rendercursor()

    @_call_with_reparented_event
    def _resize(self, event):
        self.stop_typing()

    @_call_with_reparented_event
    def _motion(self, event):
        if self.ignore(event):
            return
        c = self.hovercolor if self.ax.contains(event)[0] else self.color
        if not colors.same_color(c, self.ax.get_facecolor()):
            self.ax.set_facecolor(c)
            if self.drawon:
                self.ax.get_figure(root=True).canvas.draw()

    def on_text_change(self, func):