-
Notifications
You must be signed in to change notification settings - Fork 28.9k
Implement system fonts system channel listener #38930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about other objects/widgets that use TextPainter to measure text (example: CupertinoDataPicker or Material's TimePicker)? Do we not have to notify them as well so they can rebuild?
@@ -148,6 +149,21 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture | |||
renderView.scheduleInitialFrame(); | |||
} | |||
|
|||
Set<SystemFontsClient> _systemFontsObserver; | |||
|
|||
/// Handler function that is called when the operating system notifies the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is about painting text, PaintingBinding is probably a better place for this.
@@ -148,6 +149,21 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture | |||
renderView.scheduleInitialFrame(); | |||
} | |||
|
|||
Set<SystemFontsClient> _systemFontsObserver; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There probably needs to be a public way to register listeners. Not all listeners are going to be RenderObjects (e.g. all the the non-RenderObject that use TextPainter, which are for example Widgets using CustomPaint like the TimePicker).
@override | ||
void attach(covariant Object owner) { | ||
super.attach(owner); | ||
RendererBinding.instance._systemFontsObserver.add(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just pass in markNeedsLayout directly here? That way, interested RenderObjects would just have to implement the mixin and are good to go. (It also avoids an extra function call when this gets called).
@@ -556,6 +556,9 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB | |||
case 'memoryPressure': | |||
handleMemoryPressure(); | |||
break; | |||
case 'fontsChange': | |||
handleSystemFontsChange(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is odd that the message bubbles up all the way to the widget layer and is then redirected back to the rendering layer. What if you've implemented your own Widget system based on Flutter's rendering layer? You still want the font thing to work.
|
||
@override | ||
void bringIntoView(TextPosition position) { } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: end with blank line.
Ready for re review @goderbauer |
@@ -246,7 +246,7 @@ abstract class WidgetsBindingObserver { | |||
} | |||
|
|||
/// The glue between the widgets layer and the Flutter engine. | |||
mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { | |||
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to include this to get passed doc parsing.
/// Client for listening to system fonts change. | ||
/// | ||
/// Using this mixin will cause it automatically subscribe to system channel. | ||
mixin SystemFontsClient on RenderObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe call this "RelayoutWhenSystemFontsChangeMixin" to better describe what it does?
@@ -415,3 +415,20 @@ class RenderingFlutterBinding extends BindingBase with GestureBinding, ServicesB | |||
renderView.child = root; | |||
} | |||
} | |||
|
|||
/// Client for listening to system fonts change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Mixin for [RenderObject] that will call [markNeedsLayout] whenever the system fonts change".
Can you also add an explanation why/when a developer should mix this in?
@override | ||
void attach(covariant Object owner) { | ||
super.attach(owner); | ||
PaintingBinding.instance?.systemFonts?.addListener(markNeedsLayout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would instance or systemFonts be null? Shouldn't they always be initialized when you get to this place?
@override | ||
void detach() { | ||
super.detach(); | ||
PaintingBinding.instance?.systemFonts?.removeListener(markNeedsLayout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
/// Client for listening to system fonts change. | ||
/// | ||
/// Using this mixin will cause it automatically subscribe to system channel. | ||
mixin SystemFontsClient on RenderObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably find a better location then rendering/binding.dart
for this.
} | ||
} | ||
|
||
class _SystemFonts extends Listenable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe _SystemFontsListener
@@ -100,6 +100,18 @@ class _DatePickerLayoutDelegate extends MultiChildLayoutDelegate { | |||
} | |||
} | |||
|
|||
/// Retrieves cached widths of columns in the input state. | |||
@visibleForTesting | |||
Map<int, double> debugGetCupertinoDatePickerStateCache(State<CupertinoDatePicker> state) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awkward code to add to the framework. Can we find another way to test this?
} | ||
|
||
void _handleSystemFontsChange () { | ||
didChangeDependencies(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calling didChangeDependecies seems awkward (especially because that one calls super). Maybe factor out what you need from didChangeDependencies into a seperate method that we can call fro both places?
} | ||
|
||
void _handleSystemFontsChange() { | ||
didChangeDependencies(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
@@ -47,6 +49,11 @@ mixin ServicesBinding on BindingBase { | |||
return const _DefaultBinaryMessenger._(); | |||
} | |||
|
|||
/// Handler for flutter/system message channel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handler called for messages received on the [SystemChannels.system] call.
Other bindings may override this to respond to incoming system message.
14dc007
to
e0856ae
Compare
} | ||
|
||
void _handleSystemFontsChange () { | ||
estimatedColumnWidths.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put this statement into the setState closure? (That's the state getting changed here, right?)
void _handleSystemFontsChange () { | ||
estimatedColumnWidths.clear(); | ||
setState(() { | ||
// didChangeDependencies should update the width if necessary. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment seems outdated?
} | ||
|
||
void _handleSystemFontsChange() { | ||
_refreshEstimatedColumnWidths(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put this inside the setState since that's causing the state to change?
void _handleSystemFontsChange() { | ||
_refreshEstimatedColumnWidths(); | ||
setState(() { | ||
// didChangeDependencies should update the width if necessary. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment seems outdated?
} | ||
|
||
void _handleSystemFontsChange () { | ||
estimatedColumnWidths.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment why we have to clear this on system font changes (e.g. because it was determined by measuring the width of text which may have changed with the new fonts).
/// system fonts change | ||
/// | ||
/// System fonts can change when OS install or remove a font. Use this mixin if | ||
/// the [RenderObject] uses [TextPainter] or text related painter to correctly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's a text related painter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using ui.Paragraph directly without going through text painter. I should update the doc comment
@@ -13,6 +13,7 @@ import 'package:flutter/services.dart'; | |||
|
|||
import 'package:vector_math/vector_math_64.dart'; | |||
|
|||
import 'binding.dart'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this import be removed?
@@ -47,6 +49,13 @@ mixin ServicesBinding on BindingBase { | |||
return const _DefaultBinaryMessenger._(); | |||
} | |||
|
|||
/// Handler called for messages received on the [SystemChannels.system] call. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
call -> message channel.
@@ -47,6 +49,13 @@ mixin ServicesBinding on BindingBase { | |||
return const _DefaultBinaryMessenger._(); | |||
} | |||
|
|||
/// Handler called for messages received on the [SystemChannels.system] call. | |||
/// | |||
/// Other bindings may override this to respond to incoming system message. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
message -> messages.
case 'memoryPressure': | ||
handleMemoryPressure(); | ||
break; | ||
if (type == 'memoryPressure') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the change from a switch?
3071459
to
f8d5bb9
Compare
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. ℹ️ Googlers: Go here for more info. |
1a86f0e
to
2655235
Compare
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
@goderbauer this is ready to review. |
@override | ||
void systemFontsDidChange() { | ||
super.systemFontsDidChange(); | ||
_textPainter.markNeedsLayout(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure how do we test _textPainter has been marked needs layout. this also apply to all the other widget.
The current test only verify renderobject is dirty after font change. unless I added a bunch of visibleforTesting for all the widgets that have textpainter
void _handleSystemFontsChange () { | ||
setState(() { | ||
// System fonts change might cause the text layout width to change. | ||
// Clears cached widths to make sure we do not have updated values. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we do not have updated values? Isn't clearing the cache causing the values to get recalculated/updated?
Maybe:
"Clear cached width to ensure that they get recalculated with the new system fonts."
void _handleSystemFontsChange() { | ||
setState(() { | ||
// System fonts change might cause the text layout width to change. | ||
// Clears cached widths to make sure we do not have updated values. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is not really clearing a cache? It's proactively recalculating the widths?
(You could probably just remove the second line.)
@@ -79,6 +79,49 @@ mixin PaintingBinding on BindingBase, ServicesBinding { | |||
super.evict(asset); | |||
imageCache.clear(); | |||
} | |||
|
|||
/// Listenable for OSes system fonts change event. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Listenable for OSes system fonts change event. | |
/// Listenable that notifies when the fonts available on the system have changed. |
final String type = message['type']; | ||
switch (type) { | ||
case 'fontsChange': | ||
final _SystemFontsNotifier notifier = systemFonts; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this slightly awkward cast you could have a correctly typed private _systemFonts variable and a public systemFonts getter for it.
@@ -161,6 +161,13 @@ class TextPainter { | |||
ui.Paragraph _paragraph; | |||
bool _needsLayout = true; | |||
|
|||
/// Marks this text painter's layout information as dirty and removes cached | |||
/// information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should include information about when somebody may want to call this.
/// text when it happens. | ||
mixin RelayoutWhenSystemFontsChangeMixin on RenderObject { | ||
|
||
/// A callback that is called when system fonts has changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Called when system fonts have changed.
|
||
/// A callback that is called when system fonts has changed. | ||
/// | ||
/// Subclass should override this method to clear any extra cache. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subclasses should override this method to clear any cached values that depend on font-related metrics.
mixin RelayoutWhenSystemFontsChangeMixin on RenderObject { | ||
|
||
/// A callback that is called when system fonts has changed. | ||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default, [mark needsLayout] is called on the [RenderObject] implementing this mixin.
@override | ||
void systemFontsDidChange() { | ||
super.systemFontsDidChange(); | ||
_labelPainter.markNeedsLayout(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will _updateLabelPainter not automatically cause this to happen?
@override | ||
void systemFontsDidChange() { | ||
super.systemFontsDidChange(); | ||
_startLabelPainter.markNeedsLayout(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will _updateLabelPainters not cause this to happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, _updateLabelPainters will set the same property of existing textpainter, which will all get shortcircuited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
||
/// A callback that is called when system fonts have changed. | ||
/// | ||
/// By default, [mark needsLayout] is called on the [RenderObject] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: no space after mark. (sorry...)
974dfa6
to
7b41a5f
Compare
614fefd
to
75e1926
Compare
* Implement system fonts system channel listener
Description
This pr add a system channel listener to repaint RenderParagraph and RenderEditable upon receiving system fonts change notification. We will have engine side change coming soon
flutter/engine#12276
Related Issues
#38556
Tests
I added the following tests:
RenderParagraph relayout upon system fonts changes
RenderEditable relayout upon system fonts changes
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze --flutter-repo
) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?