Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
bd1a2a9
Updated documentation for Canvas.
freakboy3742 Jul 9, 2023
dc57388
Update docstrings, core API, and canvas examples.
freakboy3742 Jul 11, 2023
ab6f9f6
Complete documentation audit and add changenotes.
freakboy3742 Jul 12, 2023
20b3556
Core Canvas widget tests ported.
freakboy3742 Jul 12, 2023
c87c837
Initial canvas probe implementations for Cocoa and iOS.
freakboy3742 Jul 14, 2023
c8aa596
Initial GTK Cairo fixes plus probe.
freakboy3742 Jul 14, 2023
83a2b72
Merge branch 'gtk-font-tests' into audit-canvas
freakboy3742 Jul 14, 2023
440f3be
Make the Cocoa sRGB conversion conditional on an ICC profile existing.
freakboy3742 Jul 15, 2023
2a4fb29
Add a __len__ operation to context, and expanded canvas tutorial.
freakboy3742 Jul 15, 2023
3ae60c1
Allow for a default font on write_text, and correct docs on text origin.
freakboy3742 Jul 15, 2023
1b09a23
Cocoa Canvas at 100% coverage.
freakboy3742 Jul 15, 2023
43d765a
iOS canvas at 100% coverage.
freakboy3742 Jul 15, 2023
c9453d4
Mark Dummy interface as not required, and update Winforms/Android int…
freakboy3742 Jul 15, 2023
91648c9
GTK Canvas at 100% coverage.
freakboy3742 Jul 15, 2023
4e88fb2
Correct GTK multiline text handling.
freakboy3742 Jul 15, 2023
dd03691
Correct transparency handling on iOS.
freakboy3742 Jul 15, 2023
5dc4c2c
Exclude Font from dummy interface checks.
freakboy3742 Jul 15, 2023
de81c39
Disable multiline text test on GTK.
freakboy3742 Jul 15, 2023
d15ed48
Enable capture of test failure screenshots.
freakboy3742 Jul 16, 2023
39c501a
Rework reference variant handling to allow for more complex variant d…
freakboy3742 Jul 16, 2023
4c5e816
Merge branch 'gtk-font-tests' into audit-canvas
freakboy3742 Jul 17, 2023
d033c1e
Merge branch 'gtk-font-tests' into audit-canvas
freakboy3742 Jul 26, 2023
55ba727
Merge branch 'gtk-font-tests' into audit-canvas
freakboy3742 Jul 26, 2023
9fc0aef
Scale up canvas reference images to 200x200.
freakboy3742 Jul 27, 2023
3df8899
Set final test error thresholds.
freakboy3742 Jul 28, 2023
9c8271f
Merge branch 'gtk-font-tests' into audit-canvas
freakboy3742 Aug 4, 2023
66a613b
Merge branch 'gtk-font-tests' into audit-canvas
freakboy3742 Aug 25, 2023
b8370a1
Merge remote-tracking branch 'origin/main' into audit-canvas
mhsmith Sep 14, 2023
67834ee
Make Widget.add, insert, remove and clear throw an exception if calle…
mhsmith Sep 17, 2023
5fccc5c
Documentation cleanups
mhsmith Sep 17, 2023
f793e53
Remove redundant can_have_children methods
mhsmith Sep 17, 2023
bba6824
Fix grammar
mhsmith Sep 17, 2023
142ec17
WinForms Canvas working in examples/canvas and tutorial4
mhsmith Sep 17, 2023
399b3e1
All WinForms Canvas tests passing except transparency, text and input
mhsmith Sep 20, 2023
ee334a1
WinForms Canvas input tests passing
mhsmith Sep 21, 2023
8e336af
Canvas docs clarifications
mhsmith Sep 21, 2023
233adb2
Remove redundant DrawingOperation documentation
mhsmith Sep 24, 2023
561828a
Fix WinForms Canvas test_transparency
mhsmith Sep 25, 2023
fa57ae9
Add method hyperlinks to DrawingObject subclass list
mhsmith Sep 25, 2023
146190a
All WinForms Canvas tests passing at both 100% and 125% scale, except…
mhsmith Sep 25, 2023
78eaf7f
Correct transparency handling on Cocoa canvas.
freakboy3742 Sep 25, 2023
fd7bb64
tutorial4 working on Android, except for text
mhsmith Sep 26, 2023
ae4f0ed
Canvas testbed passing on Android, except for text
mhsmith Sep 26, 2023
1a8d1cc
Add text baseline setting, update tests, and fix on WinForms
mhsmith Sep 27, 2023
1a7f57d
Update core Canvas tests
mhsmith Sep 27, 2023
c1611e6
Merge remote-tracking branch 'remotes/origin/main' into audit-canvas
mhsmith Sep 28, 2023
dbb70c6
Correct WinForms text issues
mhsmith Sep 28, 2023
16d622d
Merge branch 'canvas-tmp' into audit-canvas
mhsmith Sep 28, 2023
d9c61fd
Implement write_text baseline on Cocoa and iOS, and correct font sizes
mhsmith Sep 28, 2023
da05a42
Make MIDDLE and BOTTOM refer to the entire text block, and add roundi…
mhsmith Sep 30, 2023
9139042
Update MIDDLE and BOTTOM on WinForms, and update tests to verify it
mhsmith Sep 30, 2023
a975cab
Make Cocoa draw text lines separately
mhsmith Sep 30, 2023
9fe6c4a
Make WinForms default font follow the system theme
mhsmith Sep 30, 2023
e5c02a9
Cocoa: align ALPHABETIC baseline with a logical pixel
mhsmith Sep 30, 2023
cadc219
Cocoa: switch from drawAtPoint to drawWithRect
mhsmith Oct 1, 2023
425e6d4
Add definitions of CSS pixels and points
mhsmith Oct 1, 2023
86a2012
Copy Cocoa text implementation to iOS, and add tests for multi-size b…
mhsmith Oct 2, 2023
7cf6ce1
Android: implement Canvas text rendering
mhsmith Oct 2, 2023
1e0f349
Update WinForms text reference image
mhsmith Oct 2, 2023
8d486b4
Minor tweaks to sizing descriptions
freakboy3742 Oct 2, 2023
488403d
Rewrite GTK Canvas text rendering to use Pango
mhsmith Oct 4, 2023
d87dd61
More tweaks to sizing descriptions
mhsmith Oct 5, 2023
e97d1b4
GTK: refresh font map after loading a font
mhsmith Oct 5, 2023
6cf78a9
Android Canvas at 100% coverage
mhsmith Oct 5, 2023
d04ff97
WinForms Canvas at 100% coverage
mhsmith Oct 5, 2023
c6a18ed
Implement HTML behavior when drawing on an empty path (Android, Cocoa…
mhsmith Oct 5, 2023
d7f4e2e
Base Cocoa and iOS line height on font metrics
mhsmith Oct 5, 2023
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
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ jobs:

- backend: macOS
runs-on: macos-12
app-user-data-path: /Users/runner/Library/Application Support/org.beeware.toga.testbed

# We use a fixed Ubuntu version rather than `-latest` because at some point,
# `-latest` will be updated, but it will be a soft changeover, which would cause
Expand All @@ -225,13 +226,15 @@ jobs:

briefcase-run-prefix: 'DISPLAY=:99'
setup-python: false # Use the system Python packages.
app-user-data-path: /home/runner/.local/share/testbed

- backend: windows
runs-on: windows-latest

- backend: iOS
runs-on: macos-12
briefcase-run-args: ' -d "iPhone SE (3rd generation)"'
# app data path is determined at runtime, as the path contains the Simulator and app ID.

- backend: android
runs-on: macos-12
Expand Down Expand Up @@ -264,3 +267,19 @@ jobs:
with:
name: testbed-failure-logs-${{ matrix.backend }}
path: testbed/logs/*
- name: Copy app generated user data
if: failure()
run: |
mkdir testbed/app_data
if [ "${{ matrix.backend }}" = "iOS" ]; then
APP_DATA_PATH="$(xcrun simctl get_app_container booted org.beeware.toga.testbed data)/Documents"
else
APP_DATA_PATH="${{ matrix.app-user-data-path }}"
fi
cp -r "$APP_DATA_PATH" testbed/app_data/testbed-app_data-${{ matrix.backend }}
- name: Upload app data
uses: actions/[email protected]
if: failure()
with:
name: testbed-failure-app-data-${{ matrix.backend }}
path: testbed/app_data/*
151 changes: 79 additions & 72 deletions android/src/toga_android/fonts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from pathlib import Path

from org.beeware.android import MainActivity

from android import R
from android.graphics import Typeface
from android.util import TypedValue
from toga.fonts import (
Expand All @@ -25,84 +28,88 @@ class Font:
def __init__(self, interface):
self.interface = interface

def apply(self, tv, default_size, default_typeface):
"""Apply the font to the given native widget.

:param tv: A native instance of TextView, or one of its subclasses.
:param default_size: The default font size of this widget, in pixels.
:param default_typeface: The default Typeface of this widget.
"""
if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE:
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, default_size)
else:
# The default size for most widgets is 14sp, so mapping 1 Toga "point" to 1sp
# will give relative sizes that are consistent with desktop platforms. Using
# SP means font sizes will all change proportionately if the user adjusts the
# text size in the system settings.
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, self.interface.size)
def typeface(self, *, default=Typeface.DEFAULT):
cache_key = (self.interface, default)
if typeface := _FONT_CACHE.get(cache_key):
return typeface

cache_key = (self.interface, default_typeface)
font_key = self.interface._registered_font_key(
self.interface.family,
weight=self.interface.weight,
style=self.interface.style,
variant=self.interface.variant,
)
try:
typeface = _FONT_CACHE[cache_key]
font_path = _REGISTERED_FONT_CACHE[font_key]
except KeyError:
typeface = None
font_key = self.interface._registered_font_key(
self.interface.family,
weight=self.interface.weight,
style=self.interface.style,
variant=self.interface.variant,
)
try:
font_path = _REGISTERED_FONT_CACHE[font_key]
except KeyError:
# Not a pre-registered font
if self.interface.family not in SYSTEM_DEFAULT_FONTS:
print(
f"Unknown font '{self.interface}'; "
"using system font as a fallback"
)
# Not a pre-registered font
if self.interface.family not in SYSTEM_DEFAULT_FONTS:
print(
f"Unknown font '{self.interface}'; "
"using system font as a fallback"
)
else:
if Path(font_path).is_file():
typeface = Typeface.createFromFile(font_path)
if typeface is Typeface.DEFAULT:
raise ValueError(f"Unable to load font file {font_path}")
else:
if Path(font_path).is_file():
typeface = Typeface.createFromFile(font_path)
if typeface is Typeface.DEFAULT:
raise ValueError(f"Unable to load font file {font_path}")
else:
raise ValueError(f"Font file {font_path} could not be found")
raise ValueError(f"Font file {font_path} could not be found")

if typeface is None:
if self.interface.family is SYSTEM:
# The default button font is not marked as bold, but it has a weight
# of "medium" (500), which is in between "normal" (400), and "bold"
# (600 or 700). To preserve this, we use the widget's original
# typeface as a starting point rather than Typeface.DEFAULT.
typeface = default_typeface
elif self.interface.family is MESSAGE:
typeface = Typeface.DEFAULT
elif self.interface.family is SERIF:
typeface = Typeface.SERIF
elif self.interface.family is SANS_SERIF:
typeface = Typeface.SANS_SERIF
elif self.interface.family is MONOSPACE:
typeface = Typeface.MONOSPACE
elif self.interface.family is CURSIVE:
typeface = Typeface.create("cursive", Typeface.NORMAL)
elif self.interface.family is FANTASY:
# Android appears to not have a fantasy font available by default,
# but if it ever does, we'll start using it. Android seems to choose
# a serif font when asked for a fantasy font.
typeface = Typeface.create("fantasy", Typeface.NORMAL)
else:
typeface = Typeface.create(self.interface.family, Typeface.NORMAL)
if typeface is None:
if self.interface.family is SYSTEM:
# The default button font is not marked as bold, but it has a weight
# of "medium" (500), which is in between "normal" (400), and "bold"
# (600 or 700). To preserve this, we use the widget's original
# typeface as a starting point rather than Typeface.DEFAULT.
typeface = default
elif self.interface.family is MESSAGE:
typeface = Typeface.DEFAULT
elif self.interface.family is SERIF:
typeface = Typeface.SERIF
elif self.interface.family is SANS_SERIF:
typeface = Typeface.SANS_SERIF
elif self.interface.family is MONOSPACE:
typeface = Typeface.MONOSPACE
elif self.interface.family is CURSIVE:
typeface = Typeface.create("cursive", Typeface.NORMAL)
elif self.interface.family is FANTASY:
# Android appears to not have a fantasy font available by default,
# but if it ever does, we'll start using it. Android seems to choose
# a serif font when asked for a fantasy font.
typeface = Typeface.create("fantasy", Typeface.NORMAL)
else:
typeface = Typeface.create(self.interface.family, Typeface.NORMAL)

native_style = typeface.getStyle()
if self.interface.weight == BOLD:
native_style |= Typeface.BOLD
if self.interface.style in {ITALIC, OBLIQUE}:
native_style |= Typeface.ITALIC

native_style = typeface.getStyle()
if self.interface.weight == BOLD:
native_style |= Typeface.BOLD
if self.interface.style in {ITALIC, OBLIQUE}:
native_style |= Typeface.ITALIC
if native_style != typeface.getStyle():
typeface = Typeface.create(typeface, native_style)

if native_style != typeface.getStyle():
typeface = Typeface.create(typeface, native_style)
_FONT_CACHE[cache_key] = typeface
return typeface

_FONT_CACHE[cache_key] = typeface
def size(self, *, default=None):
"""Return the font size in physical pixels."""
context = MainActivity.singletonThis
if self.interface.size == SYSTEM_DEFAULT_FONT_SIZE:
if default is None:
typed_array = context.obtainStyledAttributes(
R.style.TextAppearance_Small, [R.attr.textSize]
)
default = typed_array.getDimension(0, 0)
typed_array.recycle()
return default

tv.setTypeface(typeface)
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(
TypedValue.COMPLEX_UNIT_SP,
self.interface.size * (96 / 72),
context.getResources().getDisplayMetrics(),
)
8 changes: 5 additions & 3 deletions android/src/toga_android/widgets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ class Scalable:
def init_scale(self, context):
# The baseline DPI is 160:
# https://developer.android.com/training/multiscreen/screendensities
self.scale = context.getResources().getDisplayMetrics().densityDpi / 160
self.dpi_scale = context.getResources().getDisplayMetrics().densityDpi / 160

# Convert CSS pixels to native pixels
def scale_in(self, value, rounding=SCALE_DEFAULT_ROUNDING):
return self.scale_round(value * self.scale, rounding)
return self.scale_round(value * self.dpi_scale, rounding)

# Convert native pixels to CSS pixels
def scale_out(self, value, rounding=SCALE_DEFAULT_ROUNDING):
if isinstance(value, at_least):
return at_least(self.scale_out(value.value, rounding))
else:
return self.scale_round(value / self.scale, rounding)
return self.scale_round(value / self.dpi_scale, rounding)

def scale_round(self, value, rounding):
if rounding is None:
return value
return int(Decimal(value).to_integral(rounding))


Expand Down
Loading