From 0f816814b30427914fa034ec6cabf6315984467f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20S=C3=B6derberg?= Date: Fri, 7 Mar 2025 10:46:30 +0100 Subject: [PATCH 1/8] Feat: add CSS keyword support (#20) Add: CSS keyword constants to Travertino and update core style component #17 Update: testbed to handle CSS keywords and improve coverage #24 Add: CSS font size keywords and tests for iOS #5 #10 (#18) Add: windows CSS keyword support and tests #6 #11 (#22) Add: Cocoa CSS keywords support and tests #3 #8 (#23) Add: Gtk css keywords support and tests #4 #9 (#27) Fix: remove xxxl keywords #29 Docs: add css keywords to style reference (#33) Add: Android support for CSS font keywords and tests #2 #7 (#28) ---------- Co-authored-by: Phoebe Schwartz <144394710+peschwartz@users.noreply.github.com> Co-authored-by: KlaraLindemalm <118901489+KlaraLindemalm@users.noreply.github.com> Co-authored-by: Jacmol <104684303+Jacmol@users.noreply.github.com> Co-authored-by: carltestar --- android/src/toga_android/fonts.py | 26 ++++++++- android/tests_backend/fonts.py | 28 +++++++-- changes/1814.misc.rst | 1 + cocoa/src/toga_cocoa/fonts.py | 22 +++++++ cocoa/tests_backend/fonts.py | 16 +++++ core/src/toga/fonts.py | 8 ++- core/src/toga/style/pack.py | 17 +++++- core/tests/style/pack/test_css.py | 25 ++++++++ core/tests/test_fonts.py | 81 ++++++++++++++++++++++++++ docs/reference/style/pack.rst | 11 +++- gtk/src/toga_gtk/fonts.py | 14 ++++- gtk/src/toga_gtk/libs/styles.py | 10 +++- gtk/tests_backend/fonts.py | 13 +++++ iOS/src/toga_iOS/fonts.py | 23 ++++++++ iOS/tests_backend/fonts.py | 18 ++++++ testbed/tests/test_fonts.py | 11 +++- travertino/src/travertino/constants.py | 19 +++++- travertino/src/travertino/fonts.py | 11 +++- travertino/tests/test_fonts.py | 63 ++++++++++++++++++++ winforms/src/toga_winforms/fonts.py | 20 ++++++- winforms/tests_backend/fonts.py | 16 ++++- 21 files changed, 432 insertions(+), 21 deletions(-) create mode 100644 changes/1814.misc.rst diff --git a/android/src/toga_android/fonts.py b/android/src/toga_android/fonts.py index f3b7018564..d9d1617011 100644 --- a/android/src/toga_android/fonts.py +++ b/android/src/toga_android/fonts.py @@ -4,6 +4,12 @@ from android.graphics import Typeface from android.util import TypedValue from org.beeware.android import MainActivity +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) from toga.fonts import ( _REGISTERED_FONT_CACHE, @@ -96,6 +102,7 @@ def typeface(self, *, default=Typeface.DEFAULT): def size(self, *, default=None): """Return the font size in physical pixels.""" context = MainActivity.singletonThis + base_size = 14 if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE: if default is None: typed_array = context.obtainStyledAttributes( @@ -104,12 +111,27 @@ def size(self, *, default=None): default = typed_array.getDimension(0, 0) typed_array.recycle() return default - + elif ( + isinstance(self.interface.size, str) + and self.interface.size in ABSOLUTE_FONT_SIZES + ): + default = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) + return default + elif ( + isinstance(self.interface.size, str) + and self.interface.size in RELATIVE_FONT_SIZES + ): + parent_size = getattr(self.interface, "_parent_size", default) + default = parent_size * RELATIVE_FONT_SIZE_SCALE.get( + self.interface.size, 1.0 + ) + return default else: # Using SP means we follow the standard proportion between CSS pixels and # points by default, but respect the system text scaling setting. - return TypedValue.applyDimension( + default = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, self.interface.size * (96 / 72), context.getResources().getDisplayMetrics(), ) + return default diff --git a/android/tests_backend/fonts.py b/android/tests_backend/fonts.py index 6a0ddddf02..c1ac5317ac 100644 --- a/android/tests_backend/fonts.py +++ b/android/tests_backend/fonts.py @@ -6,6 +6,11 @@ from fontTools.ttLib import TTFont from java import jint from java.lang import Integer, Long +from travertino.constants import ( + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) from toga.fonts import ( BOLD, @@ -76,15 +81,30 @@ def assert_font_options(self, weight=NORMAL, style=NORMAL, variant=NORMAL): assert NORMAL == variant def assert_font_size(self, expected): + base_size = 14 if expected == SYSTEM_DEFAULT_FONT_SIZE: - expected = self.default_font_size * (72 / 96) - assert round(self.text_size) == round( - TypedValue.applyDimension( + expected = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + self.default_font_size, + self.native.getResources().getDisplayMetrics(), + ) + elif isinstance(expected, str): + if expected in RELATIVE_FONT_SIZES: + parent_size = getattr(self, "_parent_size", base_size) + expected = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + parent_size * RELATIVE_FONT_SIZE_SCALE.get(expected, 1.0), + self.native.getResources().getDisplayMetrics(), + ) + else: + expected = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + else: + expected = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, expected * (96 / 72), self.native.getResources().getDisplayMetrics(), ) - ) + assert round(self.text_size) == round(expected) def assert_font_family(self, expected): if not SYSTEM_FONTS: diff --git a/changes/1814.misc.rst b/changes/1814.misc.rst new file mode 100644 index 0000000000..c4c06aab1d --- /dev/null +++ b/changes/1814.misc.rst @@ -0,0 +1 @@ +Added support for CSS font size keywords in Android, Cocoa, GTK, iOS, and Windows. diff --git a/cocoa/src/toga_cocoa/fonts.py b/cocoa/src/toga_cocoa/fonts.py index d6fa949b47..24a66bbe19 100644 --- a/cocoa/src/toga_cocoa/fonts.py +++ b/cocoa/src/toga_cocoa/fonts.py @@ -1,6 +1,12 @@ from pathlib import Path from fontTools.ttLib import TTFont +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) from toga.fonts import ( _REGISTERED_FONT_CACHE, @@ -93,6 +99,22 @@ def __init__(self, interface): if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE: font_size = NSFont.systemFontSize + elif ( + isinstance(self.interface.size, str) + and self.interface.size in ABSOLUTE_FONT_SIZES + ): + base_size = NSFont.systemFontSize + font_size = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) + elif ( + isinstance(self.interface.size, str) + and self.interface.size in RELATIVE_FONT_SIZES + ): + parent_size = getattr( + self.interface, "_parent_size", NSFont.systemFontSize + ) + font_size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( + self.interface.size, 1.0 + ) else: # A "point" in Apple APIs is equivalent to a CSS pixel, but the Toga # public API works in CSS points, which are slightly larger diff --git a/cocoa/tests_backend/fonts.py b/cocoa/tests_backend/fonts.py index ec98b782a2..ffeaf85815 100644 --- a/cocoa/tests_backend/fonts.py +++ b/cocoa/tests_backend/fonts.py @@ -1,3 +1,9 @@ +from travertino.constants import ( + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) + from toga.fonts import ( BOLD, CURSIVE, @@ -48,6 +54,16 @@ def assert_font_options(self, weight=NORMAL, style=NORMAL, variant=NORMAL): def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: assert self.font.pointSize == 13 + elif isinstance(expected, str): + base_size = 13 + if expected in RELATIVE_FONT_SIZES: + parent_size = getattr(self, "_parent_size", base_size) + expected_size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( + expected, 1.0 + ) + else: + expected_size = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + assert abs(self.font.pointSize - expected_size) < 0.01 else: assert self.font.pointSize == expected * 96 / 72 diff --git a/core/src/toga/fonts.py b/core/src/toga/fonts.py index f2a7016d49..9122705744 100644 --- a/core/src/toga/fonts.py +++ b/core/src/toga/fonts.py @@ -5,6 +5,7 @@ # Use the Travertino font definitions as-is from travertino import constants from travertino.constants import ( + ABSOLUTE_FONT_SIZES, BOLD, CURSIVE, FANTASY, @@ -13,6 +14,7 @@ MONOSPACE, NORMAL, OBLIQUE, + RELATIVE_FONT_SIZES, SANS_SERIF, SERIF, SMALL_CAPS, @@ -62,7 +64,11 @@ def __str__(self) -> str: size = ( "default size" if self.size == SYSTEM_DEFAULT_FONT_SIZE - else f"{self.size}pt" + else ( + f"{self.size}" + if self.size in ABSOLUTE_FONT_SIZES or self.size in RELATIVE_FONT_SIZES + else f"{self.size}pt" + ) ) weight = f" {self.weight}" if self.weight != NORMAL else "" variant = f" {self.variant}" if self.variant != NORMAL else "" diff --git a/core/src/toga/style/pack.py b/core/src/toga/style/pack.py index 8f28fc75ee..ad969dd6de 100644 --- a/core/src/toga/style/pack.py +++ b/core/src/toga/style/pack.py @@ -7,6 +7,7 @@ from travertino.colors import rgb, hsl from travertino.constants import ( # noqa: F401 + ABSOLUTE_FONT_SIZES, BOLD, BOTTOM, CENTER, @@ -23,6 +24,7 @@ NONE, NORMAL, OBLIQUE, + RELATIVE_FONT_SIZES, RIGHT, ROW, RTL, @@ -107,7 +109,12 @@ class IntrinsicSize(BaseIntrinsicSize): font_style: str = validated_property(*FONT_STYLES, initial=NORMAL) font_variant: str = validated_property(*FONT_VARIANTS, initial=NORMAL) font_weight: str = validated_property(*FONT_WEIGHTS, initial=NORMAL) - font_size: int = validated_property(integer=True, initial=SYSTEM_DEFAULT_FONT_SIZE) + font_size: int | str = validated_property( + *ABSOLUTE_FONT_SIZES, + *RELATIVE_FONT_SIZES, + integer=True, + initial=SYSTEM_DEFAULT_FONT_SIZE, + ) @classmethod def _debug(cls, *args: str) -> None: # pragma: no cover @@ -930,7 +937,13 @@ def __css__(self) -> str: else: css.append(f"font-family: {self.font_family};") if self.font_size != SYSTEM_DEFAULT_FONT_SIZE: - css.append(f"font-size: {self.font_size}pt;") + if isinstance(self.font_size, str) and ( + self.font_size in ABSOLUTE_FONT_SIZES + or self.font_size in RELATIVE_FONT_SIZES + ): + css.append(f"font-size: {self.font_size};") + else: + css.append(f"font-size: {self.font_size}pt;") if self.font_weight != NORMAL: css.append(f"font-weight: {self.font_weight};") if self.font_style != NORMAL: diff --git a/core/tests/style/pack/test_css.py b/core/tests/style/pack/test_css.py index bef58b0392..116e9c1be8 100644 --- a/core/tests/style/pack/test_css.py +++ b/core/tests/style/pack/test_css.py @@ -449,6 +449,31 @@ "flex-direction: row; flex: 0.0 0 auto; font-size: 42pt;", id="font-size", ), + pytest.param( + Pack(font_size="smaller"), + "flex-direction: row; flex: 0.0 0 auto; font-size: smaller;", + id="font-size-smaller", + ), + pytest.param( + Pack(font_size="small"), + "flex-direction: row; flex: 0.0 0 auto; font-size: small;", + id="font-size-small", + ), + pytest.param( + Pack(font_size="xx-small"), + "flex-direction: row; flex: 0.0 0 auto; font-size: xx-small;", + id="font-size-xx-small", + ), + pytest.param( + Pack(font_size="x-large"), + "flex-direction: row; flex: 0.0 0 auto; font-size: x-large;", + id="font-size-x-large", + ), + pytest.param( + Pack(font_size="large"), + "flex-direction: row; flex: 0.0 0 auto; font-size: large;", + id="font-size-large", + ), pytest.param( Pack(font_size=SYSTEM_DEFAULT_FONT_SIZE), "flex-direction: row; flex: 0.0 0 auto;", diff --git a/core/tests/test_fonts.py b/core/tests/test_fonts.py index d59f212b62..62730780f0 100644 --- a/core/tests/test_fonts.py +++ b/core/tests/test_fonts.py @@ -95,6 +95,87 @@ def app(): NORMAL, "system default size", ), + # Custom font, small size + ( + "Custom Font", + "small", + NORMAL, + NORMAL, + NORMAL, + "Custom Font small", + ), + # System font, medium size + ( + SYSTEM, + "medium", + NORMAL, + NORMAL, + NORMAL, + "system medium", + ), + # System font, large size + ( + SYSTEM, + "large", + NORMAL, + NORMAL, + NORMAL, + "system large", + ), + # System font, x-small size + ( + SYSTEM, + "x-small", + NORMAL, + NORMAL, + NORMAL, + "system x-small", + ), + # System font, xx-small size + ( + SYSTEM, + "xx-small", + NORMAL, + NORMAL, + NORMAL, + "system xx-small", + ), + # System font, xx-large size + ( + SYSTEM, + "xx-large", + NORMAL, + NORMAL, + NORMAL, + "system xx-large", + ), + # System font, x-large size + ( + SYSTEM, + "x-large", + NORMAL, + NORMAL, + NORMAL, + "system x-large", + ), + # Custom font, smaller size + ( + "Custom Font", + "smaller", + NORMAL, + NORMAL, + NORMAL, + "Custom Font smaller", + ), + # Custom font, larger size + ( + "Custom Font", + "larger", + NORMAL, + NORMAL, + NORMAL, + "Custom Font larger", + ), ], ) def test_builtin_font(family, size, weight, style, variant, as_str): diff --git a/docs/reference/style/pack.rst b/docs/reference/style/pack.rst index 62af4b44ec..0645ebfacb 100644 --- a/docs/reference/style/pack.rst +++ b/docs/reference/style/pack.rst @@ -287,11 +287,18 @@ The weight of the font to be used. ``font_size`` ------------- -**Values:** ```` +**Values:** + - ```` (in :ref:`CSS points `) + - Absolute keywords: ``xx-small`` | ``x-small`` | ``small`` | ``medium`` | ``large`` | ``x-large`` | ``xx-large`` + - Relative keywords: ``larger`` | ``smaller`` **Initial value:** System default -The size of the font to be used, in :ref:`CSS points `. +The size of the font to be used. Can be specified in three ways: + +* An integer value in :ref:`CSS points ` +* An absolute size keyword, which sets the size relative to the system's base font size +* A relative size keyword, which adjusts the size relative to the parent element's font size The relationship between Pack and CSS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gtk/src/toga_gtk/fonts.py b/gtk/src/toga_gtk/fonts.py index b9ab1b1922..98732f6fe4 100644 --- a/gtk/src/toga_gtk/fonts.py +++ b/gtk/src/toga_gtk/fonts.py @@ -1,6 +1,11 @@ from pathlib import Path from warnings import warn +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + RELATIVE_FONT_SIZES, +) + from toga.fonts import ( _REGISTERED_FONT_CACHE, BOLD, @@ -78,8 +83,13 @@ def __init__(self, interface): font.set_family(family) - # If this is a non-default font size, set the font size - if self.interface.size != SYSTEM_DEFAULT_FONT_SIZE: + # Default font as well as values in absolute and relative font + # size are handled by Pango. Otherwise set font size manually. + if ( + self.interface.size != SYSTEM_DEFAULT_FONT_SIZE + and self.interface.size not in ABSOLUTE_FONT_SIZES + and self.interface.size not in RELATIVE_FONT_SIZES + ): font.set_size(self.interface.size * Pango.SCALE) # Set font style diff --git a/gtk/src/toga_gtk/libs/styles.py b/gtk/src/toga_gtk/libs/styles.py index 583bd523d6..eb8195c4de 100644 --- a/gtk/src/toga_gtk/libs/styles.py +++ b/gtk/src/toga_gtk/libs/styles.py @@ -1,3 +1,8 @@ +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + RELATIVE_FONT_SIZES, +) + from toga.colors import TRANSPARENT from toga.fonts import SYSTEM_DEFAULT_FONT_SIZE @@ -61,7 +66,10 @@ def get_font_css(value): "font-family": f"{value.family!r}", } - if value.size != SYSTEM_DEFAULT_FONT_SIZE: + # If value is an absolute or relative keyword, use those to set size instead + if value.size in ABSOLUTE_FONT_SIZES or value.size in RELATIVE_FONT_SIZES: + style["font-size"] = f"{value.size}" + elif value.size != SYSTEM_DEFAULT_FONT_SIZE: style["font-size"] = f"{value.size}pt" return style diff --git a/gtk/tests_backend/fonts.py b/gtk/tests_backend/fonts.py index d09da437f6..a27a436e5f 100644 --- a/gtk/tests_backend/fonts.py +++ b/gtk/tests_backend/fonts.py @@ -1,3 +1,10 @@ +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) + from toga.fonts import ( BOLD, ITALIC, @@ -29,6 +36,12 @@ def assert_font_size(self, expected): assert expected == SYSTEM_DEFAULT_FONT_SIZE elif expected == SYSTEM_DEFAULT_FONT_SIZE: assert 8 < int(self.font.get_size() / Pango.SCALE) < 18 + elif expected in ABSOLUTE_FONT_SIZES: + scale = FONT_SIZE_SCALE.get(expected, 1.0) + assert 8 * scale < int(self.font.get_size() / Pango.SCALE) < 18 * scale + elif expected in RELATIVE_FONT_SIZES: + scale = RELATIVE_FONT_SIZE_SCALE.get(expected, 1.0) + assert 8 * scale < int(self.font.get_size() / Pango.SCALE) < 18 * scale else: assert int(self.font.get_size() / Pango.SCALE) == expected diff --git a/iOS/src/toga_iOS/fonts.py b/iOS/src/toga_iOS/fonts.py index fa9585b898..b09a41cb0b 100644 --- a/iOS/src/toga_iOS/fonts.py +++ b/iOS/src/toga_iOS/fonts.py @@ -1,6 +1,12 @@ from pathlib import Path from fontTools.ttLib import TTFont +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) from toga.fonts import ( _REGISTERED_FONT_CACHE, @@ -92,6 +98,23 @@ def __init__(self, interface): if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE: size = UIFont.labelFontSize + elif ( + isinstance(self.interface.size, str) + and self.interface.size in ABSOLUTE_FONT_SIZES + ): + base_size = UIFont.labelFontSize + size = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) + elif ( + isinstance(self.interface.size, str) + and self.interface.size in RELATIVE_FONT_SIZES + ): + # Get the parent's font size, or use MEDIUM as fallback + parent_size = getattr( + self.interface, "_parent_size", UIFont.labelFontSize + ) + size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( + self.interface.size, 1.0 + ) else: # A "point" in Apple APIs is equivalent to a CSS pixel, but the Toga # public API works in CSS points, which are slightly larger diff --git a/iOS/tests_backend/fonts.py b/iOS/tests_backend/fonts.py index b3b38fb1fe..d85cb6120d 100644 --- a/iOS/tests_backend/fonts.py +++ b/iOS/tests_backend/fonts.py @@ -1,3 +1,9 @@ +from travertino.constants import ( + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) + from toga.fonts import ( BOLD, CURSIVE, @@ -51,6 +57,18 @@ def assert_font_options(self, weight=NORMAL, style=NORMAL, variant=NORMAL): def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: assert self.font.pointSize == 17 + elif isinstance(expected, str): + base_size = 17 + if expected in RELATIVE_FONT_SIZES: + # For relative sizes, we need to know the parent size + # In tests, assume MEDIUM as the parent size if not specified + parent_size = getattr(self, "_parent_size", base_size) + expected_size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( + expected, 1.0 + ) + else: + expected_size = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + assert abs(self.font.pointSize - expected_size) < 0.01 else: assert self.font.pointSize == expected * 96 / 72 diff --git a/testbed/tests/test_fonts.py b/testbed/tests/test_fonts.py index 2cba23b5df..3db9480236 100644 --- a/testbed/tests/test_fonts.py +++ b/testbed/tests/test_fonts.py @@ -1,6 +1,10 @@ from importlib import import_module import pytest +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + RELATIVE_FONT_SIZES, +) import toga from toga.fonts import ( @@ -53,7 +57,12 @@ async def test_use_system_font_fallback( async def test_font_options(widget: toga.Label, font_probe): """Every combination of weight, style and variant can be used on a font.""" for font_family in SYSTEM_DEFAULT_FONTS: - for font_size in [20, SYSTEM_DEFAULT_FONT_SIZE]: + for font_size in [ + 20, + SYSTEM_DEFAULT_FONT_SIZE, + *ABSOLUTE_FONT_SIZES, + *RELATIVE_FONT_SIZES, + ]: for font_weight in FONT_WEIGHTS: for font_style in FONT_STYLES: for font_variant in FONT_VARIANTS: diff --git a/travertino/src/travertino/constants.py b/travertino/src/travertino/constants.py index 5460c39279..c41ba2a5c0 100644 --- a/travertino/src/travertino/constants.py +++ b/travertino/src/travertino/constants.py @@ -90,7 +90,6 @@ LARGE = "large" X_LARGE = "x-large" XX_LARGE = "xx-large" -XXX_LARGE = "xxx-large" ABSOLUTE_FONT_SIZES = { XX_SMALL, @@ -100,7 +99,6 @@ LARGE, X_LARGE, XX_LARGE, - XXX_LARGE, } LARGER = "larger" @@ -108,6 +106,23 @@ RELATIVE_FONT_SIZES = {LARGER, SMALLER} +FONT_SIZE_SCALE_FACTOR = 1.2 + +FONT_SIZE_SCALE = { + XX_SMALL: 0.512, # ~(1/FONT_SIZE_SCALE_FACTOR)^3 - 60% smaller + X_SMALL: 0.64, # ~(1/FONT_SIZE_SCALE_FACTOR)^2 - 40% smaller + SMALL: 0.8, # ~(1/FONT_SIZE_SCALE_FACTOR) - 20% smaller + MEDIUM: 1.0, # baseline + LARGE: 1.2, # ~FONT_SIZE_SCALE_FACTOR - 20% larger + X_LARGE: 1.44, # ~FONT_SIZE_SCALE_FACTOR^2 - 40% larger + XX_LARGE: 1.728, # ~FONT_SIZE_SCALE_FACTOR^3 - 60% larger +} + +RELATIVE_FONT_SIZE_SCALE = { + LARGER: FONT_SIZE_SCALE_FACTOR, # 20% larger + SMALLER: 1 / FONT_SIZE_SCALE_FACTOR, # 20% smaller +} + ###################################################################### # Colors ###################################################################### diff --git a/travertino/src/travertino/fonts.py b/travertino/src/travertino/fonts.py index 25c08af8fe..3876184e76 100644 --- a/travertino/src/travertino/fonts.py +++ b/travertino/src/travertino/fonts.py @@ -1,4 +1,5 @@ from .constants import ( + ABSOLUTE_FONT_SIZES, BOLD, FONT_STYLES, FONT_VARIANTS, @@ -6,6 +7,7 @@ ITALIC, NORMAL, OBLIQUE, + RELATIVE_FONT_SIZES, SMALL_CAPS, SYSTEM_DEFAULT_FONT_SIZE, ) @@ -26,6 +28,11 @@ def __init__(self, family, size, style=NORMAL, variant=NORMAL, weight=NORMAL): try: if size.strip().endswith("pt"): self.size = int(size[:-2]) + elif ( + size.strip() in ABSOLUTE_FONT_SIZES + or size.strip() in RELATIVE_FONT_SIZES + ): + self.size = size.strip() else: raise ValueError(f"Invalid font size {size!r}") except Exception: @@ -47,7 +54,9 @@ def __repr__(self): ( "system default size" if self.size == SYSTEM_DEFAULT_FONT_SIZE - else f"{self.size}pt" + else ( + f"{self.size}" if isinstance(self.size, str) else f"{self.size}pt" + ) ), self.family, ) diff --git a/travertino/tests/test_fonts.py b/travertino/tests/test_fonts.py index 8729b51524..95e843b02c 100644 --- a/travertino/tests/test_fonts.py +++ b/travertino/tests/test_fonts.py @@ -1,12 +1,23 @@ import pytest from travertino.constants import ( + ABSOLUTE_FONT_SIZES, BOLD, ITALIC, + LARGE, + LARGER, + MEDIUM, NORMAL, OBLIQUE, + RELATIVE_FONT_SIZES, + SMALL, SMALL_CAPS, + SMALLER, SYSTEM_DEFAULT_FONT_SIZE, + X_LARGE, + X_SMALL, + XX_LARGE, + XX_SMALL, ) from travertino.fonts import Font @@ -79,10 +90,62 @@ def test_simple_construction(size): assert_font(Font("Comic Sans", size), "Comic Sans", 12, NORMAL, NORMAL, NORMAL) +@pytest.mark.parametrize( + "size", + [ + XX_SMALL, + X_SMALL, + SMALL, + MEDIUM, + LARGE, + X_LARGE, + XX_LARGE, + LARGER, + SMALLER, + ], +) +def test_css_font_size_keywords(size): + font = Font("Comic Sans", size) + assert_font(font, "Comic Sans", size, NORMAL, NORMAL, NORMAL) + assert isinstance(font.size, str) + assert font.size in ABSOLUTE_FONT_SIZES or font.size in RELATIVE_FONT_SIZES + + +@pytest.mark.parametrize( + "size, expected_repr", + [ + (XX_SMALL, ""), + (X_SMALL, ""), + (SMALL, ""), + (MEDIUM, ""), + (LARGE, ""), + (X_LARGE, ""), + (XX_LARGE, ""), + (LARGER, ""), + (SMALLER, ""), + ], +) +def test_css_font_size_repr(size, expected_repr): + font = Font("Comic Sans", size) + assert repr(font) == expected_repr + + def test_invalid_construction(): with pytest.raises(ValueError): Font("Comic Sans", "12 quatloos") + with pytest.raises(ValueError): + Font("Comic Sans", "invalid-size") + + with pytest.raises(ValueError): + Font("Comic Sans", "") + + try: + Font("Comic Sans", None) + assert False, "Should have raised TypeError" + except TypeError: + pass + @pytest.mark.parametrize( "family", diff --git a/winforms/src/toga_winforms/fonts.py b/winforms/src/toga_winforms/fonts.py index 96985872e3..7bc882d346 100644 --- a/winforms/src/toga_winforms/fonts.py +++ b/winforms/src/toga_winforms/fonts.py @@ -8,6 +8,13 @@ from System.Drawing.Text import PrivateFontCollection from System.IO import FileNotFoundException from System.Runtime.InteropServices import ExternalException +from travertino.constants import ( + ABSOLUTE_FONT_SIZES, + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, + SYSTEM_DEFAULT_FONT_SIZE, +) from toga.fonts import ( _REGISTERED_FONT_CACHE, @@ -18,7 +25,6 @@ SANS_SERIF, SERIF, SYSTEM, - SYSTEM_DEFAULT_FONT_SIZE, ) _FONT_CACHE = {} @@ -93,6 +99,18 @@ def __init__(self, interface): # Convert font size to Winforms format if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE: font_size = DEFAULT_FONT.Size + elif ( + isinstance(self.interface.size, str) + and self.interface.size in ABSOLUTE_FONT_SIZES + ): + font_size = DEFAULT_FONT.Size + font_size *= FONT_SIZE_SCALE.get(self.interface.size, 1.0) + elif ( + isinstance(self.interface.size, str) + and self.interface.size in RELATIVE_FONT_SIZES + ): + font_size = getattr(self.interface, "_parent_size", DEFAULT_FONT.Size) + font_size *= RELATIVE_FONT_SIZE_SCALE.get(self.interface.size, 1.0) else: font_size = self.interface.size diff --git a/winforms/tests_backend/fonts.py b/winforms/tests_backend/fonts.py index 93bacf364c..0b725a9fea 100644 --- a/winforms/tests_backend/fonts.py +++ b/winforms/tests_backend/fonts.py @@ -1,4 +1,9 @@ from System.Drawing import FontFamily, SystemFonts +from travertino.constants import ( + FONT_SIZE_SCALE, + RELATIVE_FONT_SIZE_SCALE, + RELATIVE_FONT_SIZES, +) from toga.fonts import ( BOLD, @@ -45,8 +50,15 @@ def font_size(self): def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: - expected = 9 - assert self.font_size == expected + expected = 9.0 + elif isinstance(expected, str): + base_size = 9.0 + if expected in RELATIVE_FONT_SIZES: + parent_size = getattr(self, "_parent_size", base_size) + expected = parent_size * RELATIVE_FONT_SIZE_SCALE.get(expected, 1.0) + else: + expected = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + assert abs(self.font.SizeInPoints - expected) < 0.1 def assert_font_family(self, expected): assert str(self.font.Name) == { From 59351121dadef6aaa98b472a2971d81abb4f04de Mon Sep 17 00:00:00 2001 From: Phoebe Schwartz <144394710+peschwartz@users.noreply.github.com> Date: Fri, 7 Mar 2025 11:34:40 +0100 Subject: [PATCH 2/8] Fix: GTK testbed font size tests #37 (#40) fix relative size tests to include parent size if one exists --- gtk/tests_backend/fonts.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gtk/tests_backend/fonts.py b/gtk/tests_backend/fonts.py index a27a436e5f..8a04bf9a1e 100644 --- a/gtk/tests_backend/fonts.py +++ b/gtk/tests_backend/fonts.py @@ -40,8 +40,14 @@ def assert_font_size(self, expected): scale = FONT_SIZE_SCALE.get(expected, 1.0) assert 8 * scale < int(self.font.get_size() / Pango.SCALE) < 18 * scale elif expected in RELATIVE_FONT_SIZES: + parent_size = getattr( + self, "_parent_size", self.font.get_size() / Pango.SCALE + ) scale = RELATIVE_FONT_SIZE_SCALE.get(expected, 1.0) - assert 8 * scale < int(self.font.get_size() / Pango.SCALE) < 18 * scale + expected = parent_size * scale + assert ( + abs(expected - int(self.font.get_size() / Pango.SCALE)) <= 5 + ) # Same as checking 8 to 18 else: assert int(self.font.get_size() / Pango.SCALE) == expected From 849e245844af1ccdd40fe003cbb99e995e7779f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20S=C3=B6derberg?= Date: Tue, 11 Mar 2025 10:35:01 +0100 Subject: [PATCH 3/8] fix: correct absolute sizes, add xxl #3242 --- travertino/src/travertino/constants.py | 17 ++++++++++------- travertino/tests/test_fonts.py | 3 +++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/travertino/src/travertino/constants.py b/travertino/src/travertino/constants.py index c41ba2a5c0..b7640d6700 100644 --- a/travertino/src/travertino/constants.py +++ b/travertino/src/travertino/constants.py @@ -90,6 +90,7 @@ LARGE = "large" X_LARGE = "x-large" XX_LARGE = "xx-large" +XXX_LARGE = "xxx-large" ABSOLUTE_FONT_SIZES = { XX_SMALL, @@ -99,6 +100,7 @@ LARGE, X_LARGE, XX_LARGE, + XXX_LARGE, } LARGER = "larger" @@ -109,13 +111,14 @@ FONT_SIZE_SCALE_FACTOR = 1.2 FONT_SIZE_SCALE = { - XX_SMALL: 0.512, # ~(1/FONT_SIZE_SCALE_FACTOR)^3 - 60% smaller - X_SMALL: 0.64, # ~(1/FONT_SIZE_SCALE_FACTOR)^2 - 40% smaller - SMALL: 0.8, # ~(1/FONT_SIZE_SCALE_FACTOR) - 20% smaller - MEDIUM: 1.0, # baseline - LARGE: 1.2, # ~FONT_SIZE_SCALE_FACTOR - 20% larger - X_LARGE: 1.44, # ~FONT_SIZE_SCALE_FACTOR^2 - 40% larger - XX_LARGE: 1.728, # ~FONT_SIZE_SCALE_FACTOR^3 - 60% larger + XX_SMALL: 0.6, + X_SMALL: 0.75, + SMALL: 0.89, + MEDIUM: 1.0, + LARGE: 1.2, + X_LARGE: 1.5, + XX_LARGE: 2.0, + XXX_LARGE: 3.0, } RELATIVE_FONT_SIZE_SCALE = { diff --git a/travertino/tests/test_fonts.py b/travertino/tests/test_fonts.py index 95e843b02c..666eeba82c 100644 --- a/travertino/tests/test_fonts.py +++ b/travertino/tests/test_fonts.py @@ -18,6 +18,7 @@ X_SMALL, XX_LARGE, XX_SMALL, + XXX_LARGE, ) from travertino.fonts import Font @@ -100,6 +101,7 @@ def test_simple_construction(size): LARGE, X_LARGE, XX_LARGE, + XXX_LARGE, LARGER, SMALLER, ], @@ -121,6 +123,7 @@ def test_css_font_size_keywords(size): (LARGE, ""), (X_LARGE, ""), (XX_LARGE, ""), + (XXX_LARGE, ""), (LARGER, ""), (SMALLER, ""), ], From 35d8379b9990c45610d7d0d52497f46fb63844e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20S=C3=B6derberg?= Date: Tue, 11 Mar 2025 11:03:13 +0100 Subject: [PATCH 4/8] docs: re-added xxl css keyword --- docs/reference/style/pack.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/style/pack.rst b/docs/reference/style/pack.rst index 0645ebfacb..db6ce83cf4 100644 --- a/docs/reference/style/pack.rst +++ b/docs/reference/style/pack.rst @@ -289,7 +289,7 @@ The weight of the font to be used. **Values:** - ```` (in :ref:`CSS points `) - - Absolute keywords: ``xx-small`` | ``x-small`` | ``small`` | ``medium`` | ``large`` | ``x-large`` | ``xx-large`` + - Absolute keywords: ``xx-small`` | ``x-small`` | ``small`` | ``medium`` | ``large`` | ``x-large`` | ``xx-large`` | ``xxx-large`` - Relative keywords: ``larger`` | ``smaller`` **Initial value:** System default From e29909de2218a47cd9b7aacd46780cbb4b04d406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20S=C3=B6derberg?= Date: Tue, 18 Mar 2025 16:06:54 +0100 Subject: [PATCH 5/8] fix: remove all relative_font_size instances (#41) fix: remove all relative_font_size instances and update documentation --------- Co-authored-by: phoebe --- android/src/toga_android/fonts.py | 11 ----------- android/tests_backend/fonts.py | 13 +------------ changes/1814.misc.rst | 2 +- cocoa/src/toga_cocoa/fonts.py | 13 +------------ cocoa/tests_backend/fonts.py | 11 +---------- core/src/toga/fonts.py | 5 +---- core/src/toga/style/pack.py | 8 +++----- core/tests/style/pack/test_css.py | 5 ----- core/tests/test_fonts.py | 18 ------------------ docs/reference/style/pack.rst | 4 +--- gtk/src/toga_gtk/fonts.py | 2 -- gtk/src/toga_gtk/libs/styles.py | 3 +-- gtk/tests_backend/fonts.py | 11 ----------- iOS/src/toga_iOS/fonts.py | 14 +------------- iOS/tests_backend/fonts.py | 13 +------------ testbed/tests/test_fonts.py | 2 -- travertino/src/travertino/constants.py | 13 ------------- travertino/src/travertino/fonts.py | 6 +----- travertino/tests/test_fonts.py | 12 +----------- winforms/src/toga_winforms/fonts.py | 8 -------- winforms/tests_backend/fonts.py | 9 +-------- 21 files changed, 15 insertions(+), 168 deletions(-) diff --git a/android/src/toga_android/fonts.py b/android/src/toga_android/fonts.py index d9d1617011..64a96062d6 100644 --- a/android/src/toga_android/fonts.py +++ b/android/src/toga_android/fonts.py @@ -7,8 +7,6 @@ from travertino.constants import ( ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -117,15 +115,6 @@ def size(self, *, default=None): ): default = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) return default - elif ( - isinstance(self.interface.size, str) - and self.interface.size in RELATIVE_FONT_SIZES - ): - parent_size = getattr(self.interface, "_parent_size", default) - default = parent_size * RELATIVE_FONT_SIZE_SCALE.get( - self.interface.size, 1.0 - ) - return default else: # Using SP means we follow the standard proportion between CSS pixels and # points by default, but respect the system text scaling setting. diff --git a/android/tests_backend/fonts.py b/android/tests_backend/fonts.py index c1ac5317ac..b5d54f9cf8 100644 --- a/android/tests_backend/fonts.py +++ b/android/tests_backend/fonts.py @@ -8,8 +8,6 @@ from java.lang import Integer, Long from travertino.constants import ( FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -81,7 +79,6 @@ def assert_font_options(self, weight=NORMAL, style=NORMAL, variant=NORMAL): assert NORMAL == variant def assert_font_size(self, expected): - base_size = 14 if expected == SYSTEM_DEFAULT_FONT_SIZE: expected = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, @@ -89,15 +86,7 @@ def assert_font_size(self, expected): self.native.getResources().getDisplayMetrics(), ) elif isinstance(expected, str): - if expected in RELATIVE_FONT_SIZES: - parent_size = getattr(self, "_parent_size", base_size) - expected = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_SP, - parent_size * RELATIVE_FONT_SIZE_SCALE.get(expected, 1.0), - self.native.getResources().getDisplayMetrics(), - ) - else: - expected = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + expected = self.default_font_size * FONT_SIZE_SCALE.get(expected, 1.0) else: expected = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, diff --git a/changes/1814.misc.rst b/changes/1814.misc.rst index c4c06aab1d..62c8fcfe12 100644 --- a/changes/1814.misc.rst +++ b/changes/1814.misc.rst @@ -1 +1 @@ -Added support for CSS font size keywords in Android, Cocoa, GTK, iOS, and Windows. +Added support for absolute CSS font size keywords in Android, Cocoa, GTK, iOS, and Windows. diff --git a/cocoa/src/toga_cocoa/fonts.py b/cocoa/src/toga_cocoa/fonts.py index 24a66bbe19..bd8c82e29e 100644 --- a/cocoa/src/toga_cocoa/fonts.py +++ b/cocoa/src/toga_cocoa/fonts.py @@ -4,8 +4,6 @@ from travertino.constants import ( ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -105,16 +103,7 @@ def __init__(self, interface): ): base_size = NSFont.systemFontSize font_size = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) - elif ( - isinstance(self.interface.size, str) - and self.interface.size in RELATIVE_FONT_SIZES - ): - parent_size = getattr( - self.interface, "_parent_size", NSFont.systemFontSize - ) - font_size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( - self.interface.size, 1.0 - ) + else: # A "point" in Apple APIs is equivalent to a CSS pixel, but the Toga # public API works in CSS points, which are slightly larger diff --git a/cocoa/tests_backend/fonts.py b/cocoa/tests_backend/fonts.py index ffeaf85815..84032d24da 100644 --- a/cocoa/tests_backend/fonts.py +++ b/cocoa/tests_backend/fonts.py @@ -1,7 +1,5 @@ from travertino.constants import ( FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -55,14 +53,7 @@ def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: assert self.font.pointSize == 13 elif isinstance(expected, str): - base_size = 13 - if expected in RELATIVE_FONT_SIZES: - parent_size = getattr(self, "_parent_size", base_size) - expected_size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( - expected, 1.0 - ) - else: - expected_size = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + expected_size = 13 * FONT_SIZE_SCALE.get(expected, 1.0) assert abs(self.font.pointSize - expected_size) < 0.01 else: assert self.font.pointSize == expected * 96 / 72 diff --git a/core/src/toga/fonts.py b/core/src/toga/fonts.py index 9122705744..187e0947e8 100644 --- a/core/src/toga/fonts.py +++ b/core/src/toga/fonts.py @@ -14,7 +14,6 @@ MONOSPACE, NORMAL, OBLIQUE, - RELATIVE_FONT_SIZES, SANS_SERIF, SERIF, SMALL_CAPS, @@ -65,9 +64,7 @@ def __str__(self) -> str: "default size" if self.size == SYSTEM_DEFAULT_FONT_SIZE else ( - f"{self.size}" - if self.size in ABSOLUTE_FONT_SIZES or self.size in RELATIVE_FONT_SIZES - else f"{self.size}pt" + f"{self.size}" if self.size in ABSOLUTE_FONT_SIZES else f"{self.size}pt" ) ) weight = f" {self.weight}" if self.weight != NORMAL else "" diff --git a/core/src/toga/style/pack.py b/core/src/toga/style/pack.py index ad969dd6de..e76a3312ea 100644 --- a/core/src/toga/style/pack.py +++ b/core/src/toga/style/pack.py @@ -24,7 +24,6 @@ NONE, NORMAL, OBLIQUE, - RELATIVE_FONT_SIZES, RIGHT, ROW, RTL, @@ -111,7 +110,6 @@ class IntrinsicSize(BaseIntrinsicSize): font_weight: str = validated_property(*FONT_WEIGHTS, initial=NORMAL) font_size: int | str = validated_property( *ABSOLUTE_FONT_SIZES, - *RELATIVE_FONT_SIZES, integer=True, initial=SYSTEM_DEFAULT_FONT_SIZE, ) @@ -937,9 +935,9 @@ def __css__(self) -> str: else: css.append(f"font-family: {self.font_family};") if self.font_size != SYSTEM_DEFAULT_FONT_SIZE: - if isinstance(self.font_size, str) and ( - self.font_size in ABSOLUTE_FONT_SIZES - or self.font_size in RELATIVE_FONT_SIZES + if ( + isinstance(self.font_size, str) + and self.font_size in ABSOLUTE_FONT_SIZES ): css.append(f"font-size: {self.font_size};") else: diff --git a/core/tests/style/pack/test_css.py b/core/tests/style/pack/test_css.py index 116e9c1be8..f1fa7e0789 100644 --- a/core/tests/style/pack/test_css.py +++ b/core/tests/style/pack/test_css.py @@ -449,11 +449,6 @@ "flex-direction: row; flex: 0.0 0 auto; font-size: 42pt;", id="font-size", ), - pytest.param( - Pack(font_size="smaller"), - "flex-direction: row; flex: 0.0 0 auto; font-size: smaller;", - id="font-size-smaller", - ), pytest.param( Pack(font_size="small"), "flex-direction: row; flex: 0.0 0 auto; font-size: small;", diff --git a/core/tests/test_fonts.py b/core/tests/test_fonts.py index 62730780f0..da20e84e26 100644 --- a/core/tests/test_fonts.py +++ b/core/tests/test_fonts.py @@ -158,24 +158,6 @@ def app(): NORMAL, "system x-large", ), - # Custom font, smaller size - ( - "Custom Font", - "smaller", - NORMAL, - NORMAL, - NORMAL, - "Custom Font smaller", - ), - # Custom font, larger size - ( - "Custom Font", - "larger", - NORMAL, - NORMAL, - NORMAL, - "Custom Font larger", - ), ], ) def test_builtin_font(family, size, weight, style, variant, as_str): diff --git a/docs/reference/style/pack.rst b/docs/reference/style/pack.rst index db6ce83cf4..a584adf053 100644 --- a/docs/reference/style/pack.rst +++ b/docs/reference/style/pack.rst @@ -289,8 +289,7 @@ The weight of the font to be used. **Values:** - ```` (in :ref:`CSS points `) - - Absolute keywords: ``xx-small`` | ``x-small`` | ``small`` | ``medium`` | ``large`` | ``x-large`` | ``xx-large`` | ``xxx-large`` - - Relative keywords: ``larger`` | ``smaller`` + - Absolute keywords: ``xx-small`` | ``x-small`` | ``small`` | ``medium`` | ``large`` | ``x-large`` | ``xx-large`` **Initial value:** System default @@ -298,7 +297,6 @@ The size of the font to be used. Can be specified in three ways: * An integer value in :ref:`CSS points ` * An absolute size keyword, which sets the size relative to the system's base font size -* A relative size keyword, which adjusts the size relative to the parent element's font size The relationship between Pack and CSS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gtk/src/toga_gtk/fonts.py b/gtk/src/toga_gtk/fonts.py index 98732f6fe4..99f25ebd23 100644 --- a/gtk/src/toga_gtk/fonts.py +++ b/gtk/src/toga_gtk/fonts.py @@ -3,7 +3,6 @@ from travertino.constants import ( ABSOLUTE_FONT_SIZES, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -88,7 +87,6 @@ def __init__(self, interface): if ( self.interface.size != SYSTEM_DEFAULT_FONT_SIZE and self.interface.size not in ABSOLUTE_FONT_SIZES - and self.interface.size not in RELATIVE_FONT_SIZES ): font.set_size(self.interface.size * Pango.SCALE) diff --git a/gtk/src/toga_gtk/libs/styles.py b/gtk/src/toga_gtk/libs/styles.py index eb8195c4de..8a0c7a8985 100644 --- a/gtk/src/toga_gtk/libs/styles.py +++ b/gtk/src/toga_gtk/libs/styles.py @@ -1,6 +1,5 @@ from travertino.constants import ( ABSOLUTE_FONT_SIZES, - RELATIVE_FONT_SIZES, ) from toga.colors import TRANSPARENT @@ -67,7 +66,7 @@ def get_font_css(value): } # If value is an absolute or relative keyword, use those to set size instead - if value.size in ABSOLUTE_FONT_SIZES or value.size in RELATIVE_FONT_SIZES: + if value.size in ABSOLUTE_FONT_SIZES: style["font-size"] = f"{value.size}" elif value.size != SYSTEM_DEFAULT_FONT_SIZE: style["font-size"] = f"{value.size}pt" diff --git a/gtk/tests_backend/fonts.py b/gtk/tests_backend/fonts.py index 8a04bf9a1e..5b50705e73 100644 --- a/gtk/tests_backend/fonts.py +++ b/gtk/tests_backend/fonts.py @@ -1,8 +1,6 @@ from travertino.constants import ( ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -39,15 +37,6 @@ def assert_font_size(self, expected): elif expected in ABSOLUTE_FONT_SIZES: scale = FONT_SIZE_SCALE.get(expected, 1.0) assert 8 * scale < int(self.font.get_size() / Pango.SCALE) < 18 * scale - elif expected in RELATIVE_FONT_SIZES: - parent_size = getattr( - self, "_parent_size", self.font.get_size() / Pango.SCALE - ) - scale = RELATIVE_FONT_SIZE_SCALE.get(expected, 1.0) - expected = parent_size * scale - assert ( - abs(expected - int(self.font.get_size() / Pango.SCALE)) <= 5 - ) # Same as checking 8 to 18 else: assert int(self.font.get_size() / Pango.SCALE) == expected diff --git a/iOS/src/toga_iOS/fonts.py b/iOS/src/toga_iOS/fonts.py index b09a41cb0b..6b8ac095df 100644 --- a/iOS/src/toga_iOS/fonts.py +++ b/iOS/src/toga_iOS/fonts.py @@ -4,8 +4,6 @@ from travertino.constants import ( ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -102,17 +100,7 @@ def __init__(self, interface): isinstance(self.interface.size, str) and self.interface.size in ABSOLUTE_FONT_SIZES ): - base_size = UIFont.labelFontSize - size = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) - elif ( - isinstance(self.interface.size, str) - and self.interface.size in RELATIVE_FONT_SIZES - ): - # Get the parent's font size, or use MEDIUM as fallback - parent_size = getattr( - self.interface, "_parent_size", UIFont.labelFontSize - ) - size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( + size = UIFont.labelFontSize * FONT_SIZE_SCALE.get( self.interface.size, 1.0 ) else: diff --git a/iOS/tests_backend/fonts.py b/iOS/tests_backend/fonts.py index d85cb6120d..99674eab24 100644 --- a/iOS/tests_backend/fonts.py +++ b/iOS/tests_backend/fonts.py @@ -1,7 +1,5 @@ from travertino.constants import ( FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -58,16 +56,7 @@ def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: assert self.font.pointSize == 17 elif isinstance(expected, str): - base_size = 17 - if expected in RELATIVE_FONT_SIZES: - # For relative sizes, we need to know the parent size - # In tests, assume MEDIUM as the parent size if not specified - parent_size = getattr(self, "_parent_size", base_size) - expected_size = parent_size * RELATIVE_FONT_SIZE_SCALE.get( - expected, 1.0 - ) - else: - expected_size = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + expected_size = 17 * FONT_SIZE_SCALE.get(expected, 1.0) assert abs(self.font.pointSize - expected_size) < 0.01 else: assert self.font.pointSize == expected * 96 / 72 diff --git a/testbed/tests/test_fonts.py b/testbed/tests/test_fonts.py index 3db9480236..fc8e4324fe 100644 --- a/testbed/tests/test_fonts.py +++ b/testbed/tests/test_fonts.py @@ -3,7 +3,6 @@ import pytest from travertino.constants import ( ABSOLUTE_FONT_SIZES, - RELATIVE_FONT_SIZES, ) import toga @@ -61,7 +60,6 @@ async def test_font_options(widget: toga.Label, font_probe): 20, SYSTEM_DEFAULT_FONT_SIZE, *ABSOLUTE_FONT_SIZES, - *RELATIVE_FONT_SIZES, ]: for font_weight in FONT_WEIGHTS: for font_style in FONT_STYLES: diff --git a/travertino/src/travertino/constants.py b/travertino/src/travertino/constants.py index b7640d6700..e93251a2a9 100644 --- a/travertino/src/travertino/constants.py +++ b/travertino/src/travertino/constants.py @@ -90,7 +90,6 @@ LARGE = "large" X_LARGE = "x-large" XX_LARGE = "xx-large" -XXX_LARGE = "xxx-large" ABSOLUTE_FONT_SIZES = { XX_SMALL, @@ -100,14 +99,8 @@ LARGE, X_LARGE, XX_LARGE, - XXX_LARGE, } -LARGER = "larger" -SMALLER = "smaller" - -RELATIVE_FONT_SIZES = {LARGER, SMALLER} - FONT_SIZE_SCALE_FACTOR = 1.2 FONT_SIZE_SCALE = { @@ -118,12 +111,6 @@ LARGE: 1.2, X_LARGE: 1.5, XX_LARGE: 2.0, - XXX_LARGE: 3.0, -} - -RELATIVE_FONT_SIZE_SCALE = { - LARGER: FONT_SIZE_SCALE_FACTOR, # 20% larger - SMALLER: 1 / FONT_SIZE_SCALE_FACTOR, # 20% smaller } ###################################################################### diff --git a/travertino/src/travertino/fonts.py b/travertino/src/travertino/fonts.py index 3876184e76..a7cbaefba9 100644 --- a/travertino/src/travertino/fonts.py +++ b/travertino/src/travertino/fonts.py @@ -7,7 +7,6 @@ ITALIC, NORMAL, OBLIQUE, - RELATIVE_FONT_SIZES, SMALL_CAPS, SYSTEM_DEFAULT_FONT_SIZE, ) @@ -28,10 +27,7 @@ def __init__(self, family, size, style=NORMAL, variant=NORMAL, weight=NORMAL): try: if size.strip().endswith("pt"): self.size = int(size[:-2]) - elif ( - size.strip() in ABSOLUTE_FONT_SIZES - or size.strip() in RELATIVE_FONT_SIZES - ): + elif size.strip() in ABSOLUTE_FONT_SIZES: self.size = size.strip() else: raise ValueError(f"Invalid font size {size!r}") diff --git a/travertino/tests/test_fonts.py b/travertino/tests/test_fonts.py index 666eeba82c..25bd7e772f 100644 --- a/travertino/tests/test_fonts.py +++ b/travertino/tests/test_fonts.py @@ -5,20 +5,16 @@ BOLD, ITALIC, LARGE, - LARGER, MEDIUM, NORMAL, OBLIQUE, - RELATIVE_FONT_SIZES, SMALL, SMALL_CAPS, - SMALLER, SYSTEM_DEFAULT_FONT_SIZE, X_LARGE, X_SMALL, XX_LARGE, XX_SMALL, - XXX_LARGE, ) from travertino.fonts import Font @@ -101,16 +97,13 @@ def test_simple_construction(size): LARGE, X_LARGE, XX_LARGE, - XXX_LARGE, - LARGER, - SMALLER, ], ) def test_css_font_size_keywords(size): font = Font("Comic Sans", size) assert_font(font, "Comic Sans", size, NORMAL, NORMAL, NORMAL) assert isinstance(font.size, str) - assert font.size in ABSOLUTE_FONT_SIZES or font.size in RELATIVE_FONT_SIZES + assert font.size in ABSOLUTE_FONT_SIZES @pytest.mark.parametrize( @@ -123,9 +116,6 @@ def test_css_font_size_keywords(size): (LARGE, ""), (X_LARGE, ""), (XX_LARGE, ""), - (XXX_LARGE, ""), - (LARGER, ""), - (SMALLER, ""), ], ) def test_css_font_size_repr(size, expected_repr): diff --git a/winforms/src/toga_winforms/fonts.py b/winforms/src/toga_winforms/fonts.py index 7bc882d346..74b201ae6d 100644 --- a/winforms/src/toga_winforms/fonts.py +++ b/winforms/src/toga_winforms/fonts.py @@ -11,8 +11,6 @@ from travertino.constants import ( ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, SYSTEM_DEFAULT_FONT_SIZE, ) @@ -105,12 +103,6 @@ def __init__(self, interface): ): font_size = DEFAULT_FONT.Size font_size *= FONT_SIZE_SCALE.get(self.interface.size, 1.0) - elif ( - isinstance(self.interface.size, str) - and self.interface.size in RELATIVE_FONT_SIZES - ): - font_size = getattr(self.interface, "_parent_size", DEFAULT_FONT.Size) - font_size *= RELATIVE_FONT_SIZE_SCALE.get(self.interface.size, 1.0) else: font_size = self.interface.size diff --git a/winforms/tests_backend/fonts.py b/winforms/tests_backend/fonts.py index 0b725a9fea..61a55d4931 100644 --- a/winforms/tests_backend/fonts.py +++ b/winforms/tests_backend/fonts.py @@ -1,8 +1,6 @@ from System.Drawing import FontFamily, SystemFonts from travertino.constants import ( FONT_SIZE_SCALE, - RELATIVE_FONT_SIZE_SCALE, - RELATIVE_FONT_SIZES, ) from toga.fonts import ( @@ -52,12 +50,7 @@ def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: expected = 9.0 elif isinstance(expected, str): - base_size = 9.0 - if expected in RELATIVE_FONT_SIZES: - parent_size = getattr(self, "_parent_size", base_size) - expected = parent_size * RELATIVE_FONT_SIZE_SCALE.get(expected, 1.0) - else: - expected = base_size * FONT_SIZE_SCALE.get(expected, 1.0) + expected = 9.0 * FONT_SIZE_SCALE.get(expected, 1.0) assert abs(self.font.SizeInPoints - expected) < 0.1 def assert_font_family(self, expected): From f5bdde2f48b01c2953fd12ffd3a997153eac0e1c Mon Sep 17 00:00:00 2001 From: Phoebe Schwartz <144394710+peschwartz@users.noreply.github.com> Date: Mon, 7 Apr 2025 19:44:19 +0200 Subject: [PATCH 6/8] New changes for fix 1814 (#42) Updated code from review. --- android/src/toga_android/fonts.py | 8 ++------ android/tests_backend/fonts.py | 2 +- cocoa/src/toga_cocoa/fonts.py | 9 ++------- cocoa/tests_backend/fonts.py | 2 +- core/src/toga/fonts.py | 5 +---- core/src/toga/style/pack.py | 5 +---- docs/reference/style/pack.rst | 2 +- gtk/tests_backend/fonts.py | 2 +- iOS/src/toga_iOS/fonts.py | 10 ++-------- iOS/tests_backend/fonts.py | 2 +- travertino/src/travertino/constants.py | 2 -- travertino/tests/test_fonts.py | 5 +---- winforms/src/toga_winforms/fonts.py | 8 ++------ winforms/tests_backend/fonts.py | 2 +- 14 files changed, 17 insertions(+), 47 deletions(-) diff --git a/android/src/toga_android/fonts.py b/android/src/toga_android/fonts.py index 64a96062d6..8e07658d16 100644 --- a/android/src/toga_android/fonts.py +++ b/android/src/toga_android/fonts.py @@ -5,7 +5,6 @@ from android.util import TypedValue from org.beeware.android import MainActivity from travertino.constants import ( - ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, ) @@ -109,11 +108,8 @@ def size(self, *, default=None): default = typed_array.getDimension(0, 0) typed_array.recycle() return default - elif ( - isinstance(self.interface.size, str) - and self.interface.size in ABSOLUTE_FONT_SIZES - ): - default = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) + elif isinstance(self.interface.size, str): + default = base_size * FONT_SIZE_SCALE[self.interface.size] return default else: # Using SP means we follow the standard proportion between CSS pixels and diff --git a/android/tests_backend/fonts.py b/android/tests_backend/fonts.py index b5d54f9cf8..a21d318d63 100644 --- a/android/tests_backend/fonts.py +++ b/android/tests_backend/fonts.py @@ -86,7 +86,7 @@ def assert_font_size(self, expected): self.native.getResources().getDisplayMetrics(), ) elif isinstance(expected, str): - expected = self.default_font_size * FONT_SIZE_SCALE.get(expected, 1.0) + expected = self.default_font_size * FONT_SIZE_SCALE[expected] else: expected = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, diff --git a/cocoa/src/toga_cocoa/fonts.py b/cocoa/src/toga_cocoa/fonts.py index bd8c82e29e..6f24e9732b 100644 --- a/cocoa/src/toga_cocoa/fonts.py +++ b/cocoa/src/toga_cocoa/fonts.py @@ -2,7 +2,6 @@ from fontTools.ttLib import TTFont from travertino.constants import ( - ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, ) @@ -97,13 +96,9 @@ def __init__(self, interface): if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE: font_size = NSFont.systemFontSize - elif ( - isinstance(self.interface.size, str) - and self.interface.size in ABSOLUTE_FONT_SIZES - ): + elif isinstance(self.interface.size, str): base_size = NSFont.systemFontSize - font_size = base_size * FONT_SIZE_SCALE.get(self.interface.size, 1.0) - + font_size = base_size * FONT_SIZE_SCALE[self.interface.size] else: # A "point" in Apple APIs is equivalent to a CSS pixel, but the Toga # public API works in CSS points, which are slightly larger diff --git a/cocoa/tests_backend/fonts.py b/cocoa/tests_backend/fonts.py index 84032d24da..3c5127630a 100644 --- a/cocoa/tests_backend/fonts.py +++ b/cocoa/tests_backend/fonts.py @@ -53,7 +53,7 @@ def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: assert self.font.pointSize == 13 elif isinstance(expected, str): - expected_size = 13 * FONT_SIZE_SCALE.get(expected, 1.0) + expected_size = 13 * FONT_SIZE_SCALE[expected] assert abs(self.font.pointSize - expected_size) < 0.01 else: assert self.font.pointSize == expected * 96 / 72 diff --git a/core/src/toga/fonts.py b/core/src/toga/fonts.py index 187e0947e8..ae1a9a560d 100644 --- a/core/src/toga/fonts.py +++ b/core/src/toga/fonts.py @@ -5,7 +5,6 @@ # Use the Travertino font definitions as-is from travertino import constants from travertino.constants import ( - ABSOLUTE_FONT_SIZES, BOLD, CURSIVE, FANTASY, @@ -63,9 +62,7 @@ def __str__(self) -> str: size = ( "default size" if self.size == SYSTEM_DEFAULT_FONT_SIZE - else ( - f"{self.size}" if self.size in ABSOLUTE_FONT_SIZES else f"{self.size}pt" - ) + else f"{self.size}" if isinstance(self.size, str) else f"{self.size}pt" ) weight = f" {self.weight}" if self.weight != NORMAL else "" variant = f" {self.variant}" if self.variant != NORMAL else "" diff --git a/core/src/toga/style/pack.py b/core/src/toga/style/pack.py index e76a3312ea..4f459a754d 100644 --- a/core/src/toga/style/pack.py +++ b/core/src/toga/style/pack.py @@ -935,10 +935,7 @@ def __css__(self) -> str: else: css.append(f"font-family: {self.font_family};") if self.font_size != SYSTEM_DEFAULT_FONT_SIZE: - if ( - isinstance(self.font_size, str) - and self.font_size in ABSOLUTE_FONT_SIZES - ): + if isinstance(self.font_size, str): css.append(f"font-size: {self.font_size};") else: css.append(f"font-size: {self.font_size}pt;") diff --git a/docs/reference/style/pack.rst b/docs/reference/style/pack.rst index a584adf053..5866a54911 100644 --- a/docs/reference/style/pack.rst +++ b/docs/reference/style/pack.rst @@ -293,7 +293,7 @@ The weight of the font to be used. **Initial value:** System default -The size of the font to be used. Can be specified in three ways: +The size of the font to be used. Can be specified in the following ways: * An integer value in :ref:`CSS points ` * An absolute size keyword, which sets the size relative to the system's base font size diff --git a/gtk/tests_backend/fonts.py b/gtk/tests_backend/fonts.py index 5b50705e73..3489206a94 100644 --- a/gtk/tests_backend/fonts.py +++ b/gtk/tests_backend/fonts.py @@ -35,7 +35,7 @@ def assert_font_size(self, expected): elif expected == SYSTEM_DEFAULT_FONT_SIZE: assert 8 < int(self.font.get_size() / Pango.SCALE) < 18 elif expected in ABSOLUTE_FONT_SIZES: - scale = FONT_SIZE_SCALE.get(expected, 1.0) + scale = FONT_SIZE_SCALE[expected] assert 8 * scale < int(self.font.get_size() / Pango.SCALE) < 18 * scale else: assert int(self.font.get_size() / Pango.SCALE) == expected diff --git a/iOS/src/toga_iOS/fonts.py b/iOS/src/toga_iOS/fonts.py index 6b8ac095df..91d20bb42b 100644 --- a/iOS/src/toga_iOS/fonts.py +++ b/iOS/src/toga_iOS/fonts.py @@ -2,7 +2,6 @@ from fontTools.ttLib import TTFont from travertino.constants import ( - ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, ) @@ -96,13 +95,8 @@ def __init__(self, interface): if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE: size = UIFont.labelFontSize - elif ( - isinstance(self.interface.size, str) - and self.interface.size in ABSOLUTE_FONT_SIZES - ): - size = UIFont.labelFontSize * FONT_SIZE_SCALE.get( - self.interface.size, 1.0 - ) + elif isinstance(self.interface.size, str): + size = UIFont.labelFontSize * FONT_SIZE_SCALE[self.interface.size] else: # A "point" in Apple APIs is equivalent to a CSS pixel, but the Toga # public API works in CSS points, which are slightly larger diff --git a/iOS/tests_backend/fonts.py b/iOS/tests_backend/fonts.py index 99674eab24..68afd55068 100644 --- a/iOS/tests_backend/fonts.py +++ b/iOS/tests_backend/fonts.py @@ -56,7 +56,7 @@ def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: assert self.font.pointSize == 17 elif isinstance(expected, str): - expected_size = 17 * FONT_SIZE_SCALE.get(expected, 1.0) + expected_size = 17 * FONT_SIZE_SCALE[expected] assert abs(self.font.pointSize - expected_size) < 0.01 else: assert self.font.pointSize == expected * 96 / 72 diff --git a/travertino/src/travertino/constants.py b/travertino/src/travertino/constants.py index e93251a2a9..7ee116bc03 100644 --- a/travertino/src/travertino/constants.py +++ b/travertino/src/travertino/constants.py @@ -101,8 +101,6 @@ XX_LARGE, } -FONT_SIZE_SCALE_FACTOR = 1.2 - FONT_SIZE_SCALE = { XX_SMALL: 0.6, X_SMALL: 0.75, diff --git a/travertino/tests/test_fonts.py b/travertino/tests/test_fonts.py index 25bd7e772f..45048b762a 100644 --- a/travertino/tests/test_fonts.py +++ b/travertino/tests/test_fonts.py @@ -133,11 +133,8 @@ def test_invalid_construction(): with pytest.raises(ValueError): Font("Comic Sans", "") - try: + with pytest.raises(TypeError): Font("Comic Sans", None) - assert False, "Should have raised TypeError" - except TypeError: - pass @pytest.mark.parametrize( diff --git a/winforms/src/toga_winforms/fonts.py b/winforms/src/toga_winforms/fonts.py index 74b201ae6d..984c4d4867 100644 --- a/winforms/src/toga_winforms/fonts.py +++ b/winforms/src/toga_winforms/fonts.py @@ -9,7 +9,6 @@ from System.IO import FileNotFoundException from System.Runtime.InteropServices import ExternalException from travertino.constants import ( - ABSOLUTE_FONT_SIZES, FONT_SIZE_SCALE, SYSTEM_DEFAULT_FONT_SIZE, ) @@ -97,12 +96,9 @@ def __init__(self, interface): # Convert font size to Winforms format if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE: font_size = DEFAULT_FONT.Size - elif ( - isinstance(self.interface.size, str) - and self.interface.size in ABSOLUTE_FONT_SIZES - ): + elif isinstance(self.interface.size, str): font_size = DEFAULT_FONT.Size - font_size *= FONT_SIZE_SCALE.get(self.interface.size, 1.0) + font_size *= FONT_SIZE_SCALE[self.interface.size] else: font_size = self.interface.size diff --git a/winforms/tests_backend/fonts.py b/winforms/tests_backend/fonts.py index 61a55d4931..77d9abf7b8 100644 --- a/winforms/tests_backend/fonts.py +++ b/winforms/tests_backend/fonts.py @@ -50,7 +50,7 @@ def assert_font_size(self, expected): if expected == SYSTEM_DEFAULT_FONT_SIZE: expected = 9.0 elif isinstance(expected, str): - expected = 9.0 * FONT_SIZE_SCALE.get(expected, 1.0) + expected = 9.0 * FONT_SIZE_SCALE[expected] assert abs(self.font.SizeInPoints - expected) < 0.1 def assert_font_family(self, expected): From 81b2609027f1d11b668436ab3054c95f96f76949 Mon Sep 17 00:00:00 2001 From: phoebe Date: Wed, 2 Jul 2025 10:51:57 -0600 Subject: [PATCH 7/8] remove duplicate SYSTEM_DEFAULT_IMPORTS --- winforms/src/toga_winforms/fonts.py | 1 - 1 file changed, 1 deletion(-) diff --git a/winforms/src/toga_winforms/fonts.py b/winforms/src/toga_winforms/fonts.py index 01c877b3af..b32b8ae4f3 100644 --- a/winforms/src/toga_winforms/fonts.py +++ b/winforms/src/toga_winforms/fonts.py @@ -10,7 +10,6 @@ from System.Runtime.InteropServices import ExternalException from travertino.constants import ( FONT_SIZE_SCALE, - SYSTEM_DEFAULT_FONT_SIZE, ) from toga.fonts import ( From 07b4706d71af85918c00242e36551f2ed770dc14 Mon Sep 17 00:00:00 2001 From: phoebe Date: Wed, 2 Jul 2025 11:01:03 -0600 Subject: [PATCH 8/8] update formattings --- core/src/toga/fonts.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/toga/fonts.py b/core/src/toga/fonts.py index ca8060d3d2..b9df4b70b4 100644 --- a/core/src/toga/fonts.py +++ b/core/src/toga/fonts.py @@ -72,7 +72,9 @@ def __str__(self) -> str: size = ( "default size" if self.size == SYSTEM_DEFAULT_FONT_SIZE - else f"{self.size}" if isinstance(self.size, str) else f"{self.size}pt" + else f"{self.size}" + if isinstance(self.size, str) + else f"{self.size}pt" ) weight = f" {self.weight}" if self.weight != NORMAL else "" variant = f" {self.variant}" if self.variant != NORMAL else ""