Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion ultraplot/axes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2986,6 +2986,8 @@ def _update_title(self, loc, title=None, **kwargs):
kw["text"] = title[self.number - 1]
else:
raise ValueError(f"Invalid title {title!r}. Must be string(s).")
if any(key in kwargs for key in ("size", "fontsize")):
self._title_dict[loc]._ultraplot_manual_size = True
kw.update(kwargs)
self._title_dict[loc].update(kw)

Expand Down Expand Up @@ -3042,7 +3044,9 @@ def _update_title_position(self, renderer):
# Offset title away from a-b-c label
atext, ttext = aobj.get_text(), tobj.get_text()
awidth = twidth = 0
pad = (abcpad / 72) / self._get_size_inches()[0]
width_inches = self._get_size_inches()[0]
pad = (abcpad / 72) / width_inches
abc_pad = (self._abc_pad / 72) / width_inches
ha = aobj.get_ha()

# Get dimensions of non-empty elements
Expand All @@ -3059,6 +3063,36 @@ def _update_title_position(self, renderer):
.width
)

# Shrink the title font if both texts share a location and would overflow
if (
atext
and ttext
and self._abc_loc == self._title_loc
and twidth > 0
and not getattr(tobj, "_ultraplot_manual_size", False)
):
scale = 1
base_x = tobj.get_position()[0]
if ha == "left":
available = 1 - (base_x + awidth + pad)
if available < twidth and available > 0:
scale = available / twidth
elif ha == "right":
available = base_x + abc_pad - pad - awidth
if available < twidth and available > 0:
scale = available / twidth
elif ha == "center":
# Conservative fit for centered titles sharing the abc location
left_room = base_x - 0.5 * (awidth + pad)
right_room = 1 - (base_x + 0.5 * (awidth + pad))
max_room = min(left_room, right_room)
if max_room < twidth / 2 and max_room > 0:
scale = (2 * max_room) / twidth

if scale < 1:
tobj.set_fontsize(tobj.get_fontsize() * scale)
twidth *= scale

# Calculate offsets based on alignment and content
aoffset = toffset = 0
if atext and ttext:
Expand Down
30 changes: 30 additions & 0 deletions ultraplot/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""
import numpy as np
import pytest

import ultraplot as uplt
from ultraplot.internals.warnings import UltraPlotWarning

Expand Down Expand Up @@ -130,6 +131,35 @@ def test_cartesian_format_all_units_types():
ax.format(**kwargs)


def test_title_shrinks_when_abc_overlaps():
"""
Ensure long titles shrink when they would overlap the abc label.
"""
fig, ax = uplt.subplots(figsize=(2, 2))
ax.format(abc=True, title="X" * 200, titleloc="left", abcloc="left")
title_obj = ax._title_dict["left"]
original_size = title_obj.get_fontsize()
fig.canvas.draw()
assert title_obj.get_fontsize() < original_size


def test_title_manual_size_ignores_auto_shrink():
"""
Ensure explicit title sizes bypass auto-scaling.
"""
fig, ax = uplt.subplots(figsize=(2, 2))
ax.format(
abc=True,
title="X" * 200,
titleloc="left",
abcloc="left",
title_kw={"size": 20},
)
title_obj = ax._title_dict["left"]
fig.canvas.draw()
assert title_obj.get_fontsize() == 20


def test_axis_access():
# attempt to access the ax object 2d and linearly
fig, ax = uplt.subplots(ncols=2, nrows=2)
Expand Down
Loading