-
Notifications
You must be signed in to change notification settings - Fork 24.6k
[iOS] increasing font scale (content size catgory) breaks text #45857
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
Comments
|
|
I'd like to look into this issue |
After some looking into this, it seems to be caused by the What I've tried, and seems to be working for text, is checking whether I've also seen some weird behavior around TextInputs, but I'd like to know your opinion before looking further into it. cc @cortinico, @cipolleschi |
This sounds like a good approach @j-piasecki. I was talking with @sammy-SC about that and he agreed that we should implement it properly.
Which are the other strange behaviors you observed? |
That's right
TextInput uses attributed string from the state for measurement in some cases, and the state is updated during layout. During the first pass, the already stored attributed string is used which also stores the I've also observed the layout being wrong after changing the scale when dirtying the tree on This video shows both things: Screen.Recording.2024-08-07.at.15.23.43.mp4Dirtying the tree on every commit fixes the layout after text input updates, so the root cause may be similar. I need to look more into it though. |
I figured out the wrong text layout after pressing the button and it's not related to text input. Yoga caches layout in the nodes, the button press results in I'm not clear on the connection between the JS part and Native code. Do you have any tips for going forward with this? I doubt that I will be able to get much from reading |
I suppose that setting
But @sammy-SC is the real expert here, and he just went on PTO today! 😱 Setting dirty for every commit is not a solution as we will lose some optimizations we introduced this year. |
I opened a draft PR with the initial implementation - #45978. Let me know if it's going in the right direction. |
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Pull Request resolved: facebook#45978 Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Pull Request resolved: facebook#45978 Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Pull Request resolved: facebook#45978 Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Pull Request resolved: facebook#45978 Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Pull Request resolved: facebook#45978 Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement, I've added a `fontSizeMultiplier_` field in the node that keeps track of the font scale it was last laid out with. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. - When the `fontSizeMultiplier` is changed, the entire `ShadowTree` is cloned. I'm not sure if there's a reliable way of determining whether the node is mutable or not. - Changing font scale and navigating back to the app on Android has the potential to cause a `SIGSEGV` but it also happens without changes in this PR. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Reviewed By: NickGerleman Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Reviewed By: NickGerleman Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Reviewed By: NickGerleman Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Reviewed By: NickGerleman Differential Revision: D71727907 Pulled By: j-piasecki
Summary: Fixes facebook#45857 The general idea behind this PR is the same for both platforms: dirty all nodes with `MeasurableYogaNode` trait when the layout is constrained with a new `fontSizeMultiplier`. There were a few caveats: - `ParagraphShadowNode` marks its layout as clean in the constructor in most cases. To prevent that from using a stale measurement I'm using the font scale multiplier stored in `content_` property of the node. That value is then compared with the scale used to create the attributed string kept in the node's state. If those differ, the layout is not cleared. - On Android, font scale wasn't passed down to the `SurfaceHandler` - On Android, text measurement relies on cached `DisplayMetrics` which were not updated when the system font scale changed. - `AndroidTextInputShadowNode` wasn't using `fontSizeMultiplier` at all. I needed to add it in all places where an `AttributedString` is constructed. ## Changelog: [GENERAL] [FIXED] - Fixed text not updating correctly after changing font scale in settings Pull Request resolved: facebook#45978 Test Plan: So far tested on the following code: ```jsx function App() { const [counter,setCounter] = useState(0); const [text,setText] = useState('TextInput'); const [flag,setFlag] = useState(true); return ( <SafeAreaView style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', }} > <Text style={{fontSize: 24}}>RN 24 Label Testing {flag ? 'A' : 'B'}</Text> <TextInput value={text} onChangeText={setText} style={{fontSize: 24, borderWidth: 1}} placeholder="Placeholder" /> <Pressable onPress={() => setCounter(prevState => prevState + 1)} style={{backgroundColor: counter % 2 === 0 ? 'red' : 'blue', width: 200, height: 50}} /> <Pressable onPress={() => setFlag(!flag)} style={{backgroundColor: 'green', width: 200, height: 50}} /> </SafeAreaView> ); } ``` Reviewed By: NickGerleman Differential Revision: D71727907 Pulled By: j-piasecki fbshipit-source-id: 240fb5fa4967a9182bce7e885798b233d1e25aea
Description
Hello team,
Everything started from an issue reported in the Unistyles repo: issue. After some debugging, I was able to isolate the issue to React Native core. It’s not easily visible, as with the new architecture, the app is not hot reloading when the user changes the font scale (this was also reported here).
It has nothing to do with Unistyles, but it was spotted by @yzhe554 because Unistyles auto re-renders the view if any native change happens, like a font scale change.
I created a repro repo with no external dependencies.
Steps to reproduce
<Text>
that depends onfontSize
contentSizeCategory
(fontScale)React Native Version
0.75-rc.6
Affected Platforms
Runtime - iOS
Areas
Fabric - The New Renderer
Output of
npx react-native info
Stacktrace or Logs
Reproducer
https://github.com/jpudysz/react-native-new-arch-repro
Screenshots and Videos
Result for paper:
Screen.Recording.2024-08-01.at.09.17.20.mov
Result for fabric:
Screen.Recording.2024-08-01.at.09.21.30.mov
As you can see with new architecture text breaks at the end of the line.
The text was updated successfully, but these errors were encountered: