feat(ios): new a11y properties for managing font scale (#10260)

This commit is contained in:
Dimitris-Rafail Katsampas
2023-04-06 02:20:15 +03:00
committed by GitHub
parent 2f9e5c0b84
commit 7aaa1d899d
11 changed files with 196 additions and 34 deletions

View File

@ -0,0 +1,70 @@
import * as TKUnit from '../tk-unit';
import * as helper from '../ui-helper';
import { isIOS, Label, StackLayout } from '@nativescript/core';
export function test_iosAccessibilityAdjustsFontSize_property() {
if (isIOS) {
const deviceFontScaleMock = 4.0;
const page = helper.getCurrentPage();
const testView = new Label();
const layout = new StackLayout();
layout.addChild(testView);
page.content = layout;
layout.style.iosAccessibilityAdjustsFontSize = false;
layout.style.fontScaleInternal = deviceFontScaleMock;
const nativeFontSize = testView.nativeTextViewProtected.font.pointSize;
layout.style.iosAccessibilityAdjustsFontSize = true;
const nativeFontSizeWithAdjust = testView.nativeTextViewProtected.font.pointSize;
TKUnit.assertEqual(nativeFontSize, testView.style.fontInternal.fontSize, 'View font size was scaled even though iosAccessibilityAdjustsFontSize is disabled');
TKUnit.assertEqual(nativeFontSizeWithAdjust, testView.style.fontInternal.fontSize * deviceFontScaleMock, 'View font size was not scaled even though iosAccessibilityAdjustsFontSize is enabled');
}
}
export function test_iosAccessibilityMinFontScale_property() {
if (isIOS) {
const deviceFontScaleMock = 1.0;
const page = helper.getCurrentPage();
const testView = new Label();
const layout = new StackLayout();
layout.addChild(testView);
page.content = layout;
layout.style.iosAccessibilityAdjustsFontSize = true;
layout.style.fontScaleInternal = deviceFontScaleMock;
testView.style.iosAccessibilityMinFontScale = 2.0;
const nativeFontSize = testView.nativeTextViewProtected.font.pointSize;
const expectedNativeFontSize = testView.style.fontInternal.fontSize * testView.style.iosAccessibilityMinFontScale;
TKUnit.assertEqual(nativeFontSize, expectedNativeFontSize, 'View font size scaling does not respect iosAccessibilityMinFontScale');
}
}
export function test_iosAccessibilityMaxFontScale_property() {
if (isIOS) {
const deviceFontScaleMock = 4.0;
const page = helper.getCurrentPage();
const testView = new Label();
const layout = new StackLayout();
layout.addChild(testView);
page.content = layout;
layout.style.iosAccessibilityAdjustsFontSize = true;
layout.style.fontScaleInternal = deviceFontScaleMock;
testView.style.iosAccessibilityMaxFontScale = 2.0;
const nativeFontSize = testView.nativeTextViewProtected.font.pointSize;
const expectedNativeFontSize = testView.style.fontInternal.fontSize * testView.style.iosAccessibilityMaxFontScale;
TKUnit.assertEqual(nativeFontSize, expectedNativeFontSize, 'View font size scaling does not respect iosAccessibilityMaxFontScale');
}
}

View File

@ -54,6 +54,9 @@ if (!__CI__) {
allTests['PROFILING'] = profilingTests;
}
import * as a11yPropertiesTests from './accessibility/accessibility-properties-tests';
allTests['A11Y-PROPERTIES'] = a11yPropertiesTests;
import * as appSettingsTests from './application-settings/application-settings-tests';
allTests['APPLICATION-SETTINGS'] = appSettingsTests;

View File

@ -582,13 +582,17 @@ export function test_setting_font_properties_sets_native_font() {
export function test_native_font_size_with_a11y_font_scale() {
if (isIOS) {
const page = helper.getCurrentPage();
const testView = new Label();
const deviceFontScaleMock = 4.0;
page.content = testView;
const page = helper.getCurrentPage();
const testView = new Label();
const layout = new StackLayout();
layout.addChild(testView);
testView.style._fontScale = deviceFontScaleMock;
page.content = layout;
layout.style.iosAccessibilityAdjustsFontSize = true;
layout.style.fontScaleInternal = deviceFontScaleMock;
const nativeFontSize = testView.nativeTextViewProtected.font.pointSize;
const expectedNativeFontSize = testView.style.fontInternal.fontSize * deviceFontScaleMock;

View File

@ -52,10 +52,10 @@ function applyFontScaleToRootViews(): void {
const fontScale = getCurrentFontScale();
rootView.style._fontScale = fontScale;
rootView.style.fontScaleInternal = fontScale;
const rootModalViews = <Array<View>>rootView._getRootModalViews();
rootModalViews.forEach((rootModalView) => (rootModalView.style._fontScale = fontScale));
rootModalViews.forEach((rootModalView) => (rootModalView.style.fontScaleInternal = fontScale));
}
export function initAccessibilityCssHelper(): void {

View File

@ -31,6 +31,30 @@ export const accessibilityEnabledProperty = new CssProperty<Style, boolean>({
});
accessibilityEnabledProperty.register(Style);
export const iosAccessibilityAdjustsFontSizeProperty = new InheritedCssProperty<Style, boolean>({
defaultValue: false,
name: 'iosAccessibilityAdjustsFontSize',
cssName: 'ios-a11y-adjusts-font-size',
valueConverter: booleanConverter,
});
iosAccessibilityAdjustsFontSizeProperty.register(Style);
export const iosAccessibilityMinFontScaleProperty = new InheritedCssProperty<Style, number>({
defaultValue: 0,
name: 'iosAccessibilityMinFontScale',
cssName: 'ios-a11y-min-font-scale',
valueConverter: parseFloat,
});
iosAccessibilityMinFontScaleProperty.register(Style);
export const iosAccessibilityMaxFontScaleProperty = new InheritedCssProperty<Style, number>({
defaultValue: 0,
name: 'iosAccessibilityMaxFontScale',
cssName: 'ios-a11y-max-font-scale',
valueConverter: parseFloat,
});
iosAccessibilityMaxFontScaleProperty.register(Style);
export const accessibilityHiddenProperty = new (global.isIOS ? InheritedCssProperty : CssProperty)({
name: 'accessibilityHidden',
cssName: 'a11y-hidden',

View File

@ -282,6 +282,21 @@ export abstract class View extends ViewCommon {
*/
accessibilityMediaSession: boolean;
/**
* Defines whether accessibility font scale should affect font size.
*/
iosAccessibilityAdjustsFontSize: boolean;
/**
* Gets or sets the minimum accessibility font scale.
*/
iosAccessibilityMinFontScale: number;
/**
* Gets or sets the maximum accessibility font scale.
*/
iosAccessibilityMaxFontScale: number;
/**
* Internal use only. This is used to limit the number of updates to android.view.View.setContentDescription()
*/

View File

@ -405,7 +405,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
modalRootViewCssClasses.forEach((c) => this.cssClasses.add(c));
parent._modal = this;
this.style._fontScale = getCurrentFontScale();
this.style.fontScaleInternal = getCurrentFontScale();
this._modalParent = parent;
this._modalContext = options.context;
this._closeModalCallback = (...originalArgs) => {
@ -859,6 +859,27 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
this.style.accessibilityMediaSession = value;
}
get iosAccessibilityAdjustsFontSize(): boolean {
return this.style.iosAccessibilityAdjustsFontSize;
}
set iosAccessibilityAdjustsFontSize(value: boolean) {
this.style.iosAccessibilityAdjustsFontSize = value;
}
get iosAccessibilityMinFontScale(): number {
return this.style.iosAccessibilityMinFontScale;
}
set iosAccessibilityMinFontScale(value: number) {
this.style.iosAccessibilityMinFontScale = value;
}
get iosAccessibilityMaxFontScale(): number {
return this.style.iosAccessibilityMaxFontScale;
}
set iosAccessibilityMaxFontScale(value: number) {
this.style.iosAccessibilityMaxFontScale = value;
}
get automationText(): string {
return this.accessibilityIdentifier;
}

View File

@ -2,8 +2,8 @@ import { TransformFunctionsInfo } from '../animation';
import { CoreTypes } from '../../core-types';
import { Color } from '../../color';
import { CssProperty, CssAnimationProperty, ShorthandProperty, InheritedCssProperty } from '../core/properties';
import { Style } from '../styling/style';
import { Font, FontStyle, FontWeight } from './font';
import { Style } from './style';
import { Font, FontStyleType, FontWeightType } from './font';
import { Background } from './background';
export namespace Length {
@ -95,11 +95,12 @@ export const verticalAlignmentProperty: CssProperty<Style, CoreTypes.VerticalAli
export const fontSizeProperty: InheritedCssProperty<Style, number>;
export const fontFamilyProperty: InheritedCssProperty<Style, string>;
export const fontStyleProperty: InheritedCssProperty<Style, FontStyle>;
export const fontWeightProperty: InheritedCssProperty<Style, FontWeight>;
export const fontStyleProperty: InheritedCssProperty<Style, FontStyleType>;
export const fontWeightProperty: InheritedCssProperty<Style, FontWeightType>;
export const backgroundInternalProperty: CssProperty<Style, Background>;
export const fontInternalProperty: InheritedCssProperty<Style, Font>;
export const fontScaleInternalProperty: InheritedCssProperty<Style, number>;
export const androidElevationProperty: CssProperty<Style, number>;
export const androidDynamicElevationOffsetProperty: CssProperty<Style, number>;

View File

@ -1,12 +1,12 @@
// Types
import { unsetValue, CssProperty, CssAnimationProperty, ShorthandProperty, InheritedCssProperty } from '../core/properties';
import { Style } from '../styling/style';
import { Style } from './style';
import { Transformation, TransformationValue, TransformFunctionsInfo } from '../animation';
import { Color } from '../../color';
import { Font, parseFont, FontStyle, FontStyleType, FontWeight, FontWeightType, FontVariationSettings, FontVariationSettingsType } from '../../ui/styling/font';
import { Font, parseFont, FontStyle, FontStyleType, FontWeight, FontWeightType, FontVariationSettings, FontVariationSettingsType } from './font';
import { Background } from './background';
import { layout, hasDuplicates } from '../../utils';
import { Background } from '../../ui/styling/background';
import { radiansToDegrees } from '../../utils/number-utils';
@ -1326,13 +1326,13 @@ export const fontFamilyProperty = new InheritedCssProperty<Style, string>({
});
fontFamilyProperty.register(Style);
export const fontScaleProperty = new InheritedCssProperty<Style, number>({
name: '_fontScale',
cssName: '_fontScale',
export const fontScaleInternalProperty = new InheritedCssProperty<Style, number>({
name: 'fontScaleInternal',
cssName: '_fontScaleInternal',
defaultValue: 1.0,
valueConverter: (v) => parseFloat(v),
});
fontScaleProperty.register(Style);
fontScaleInternalProperty.register(Style);
export const fontSizeProperty = new InheritedCssProperty<Style, number>({
name: 'fontSize',

View File

@ -108,7 +108,7 @@ export class Style extends Observable implements StyleDefinition {
/**
* This property ensures inheritance of a11y scale among views.
*/
public _fontScale: number;
public fontScaleInternal: number;
public backgroundInternal: Background;
public rotate: number;
@ -233,6 +233,9 @@ export class Style extends Observable implements StyleDefinition {
public accessibilityLanguage: string;
public accessibilityMediaSession: boolean;
public accessibilityStep: number;
public iosAccessibilityAdjustsFontSize: boolean;
public iosAccessibilityMinFontScale: number;
public iosAccessibilityMaxFontScale: number;
public PropertyBag: {
new (): { [property: string]: string };

View File

@ -4,11 +4,12 @@ import { CSSShadow } from '../styling/css-shadow';
// Requires
import { Font } from '../styling/font';
import { iosAccessibilityAdjustsFontSizeProperty, iosAccessibilityMaxFontScaleProperty, iosAccessibilityMinFontScaleProperty } from '../../accessibility/accessibility-properties';
import { TextBaseCommon, textProperty, formattedTextProperty, textAlignmentProperty, textDecorationProperty, textTransformProperty, textShadowProperty, letterSpacingProperty, lineHeightProperty, maxLinesProperty, resetSymbol } from './text-base-common';
import { Color } from '../../color';
import { FormattedString } from './formatted-string';
import { Span } from './span';
import { colorProperty, fontInternalProperty, fontScaleProperty, Length } from '../styling/style-properties';
import { colorProperty, fontInternalProperty, fontScaleInternalProperty, Length } from '../styling/style-properties';
import { isString, isNullOrUndefined } from '../../utils/types';
import { iOSNativeHelper } from '../../utils';
import { Trace } from '../../trace';
@ -187,29 +188,49 @@ export class TextBase extends TextBaseCommon {
if (!(value instanceof Font) || !this.formattedText) {
let nativeView = this.nativeTextViewProtected;
nativeView = nativeView instanceof UIButton ? nativeView.titleLabel : nativeView;
if (value instanceof Font) {
// Apply a11y font scale if not set
if (value.fontScale !== this.style._fontScale) {
value.fontScale = this.style._fontScale;
}
nativeView.font = value.getUIFont(nativeView.font);
} else {
nativeView.font = value;
}
nativeView.font = value instanceof Font ? value.getUIFont(nativeView.font) : value;
}
}
[fontScaleProperty.setNative](value: number) {
[fontScaleInternalProperty.setNative](value: number) {
const nativeView = this.nativeTextViewProtected instanceof UIButton ? this.nativeTextViewProtected.titleLabel : this.nativeTextViewProtected;
const currentFont = this.style.fontInternal || Font.default.withFontSize(nativeView.font.pointSize);
if (currentFont.fontScale !== value) {
const newFont = currentFont.withFontScale(value);
let finalValue;
if (this.iosAccessibilityAdjustsFontSize) {
finalValue = value;
if (this.iosAccessibilityMinFontScale && this.iosAccessibilityMinFontScale > value) {
finalValue = this.iosAccessibilityMinFontScale;
}
if (this.iosAccessibilityMaxFontScale && this.iosAccessibilityMaxFontScale < value) {
finalValue = this.iosAccessibilityMaxFontScale;
}
} else {
finalValue = 1.0;
}
const newFont = currentFont.withFontScale(finalValue);
this.style.fontInternal = newFont;
// Request layout on font scale as it's not done automatically
if (currentFont.fontScale !== finalValue) {
this.requestLayout();
}
}
[iosAccessibilityAdjustsFontSizeProperty.setNative](value: boolean) {
this[fontScaleInternalProperty.setNative](this.style.fontScaleInternal);
}
[iosAccessibilityMinFontScaleProperty.setNative](value: number) {
this[fontScaleInternalProperty.setNative](this.style.fontScaleInternal);
}
[iosAccessibilityMaxFontScaleProperty.setNative](value: number) {
this[fontScaleInternalProperty.setNative](this.style.fontScaleInternal);
}
[textAlignmentProperty.setNative](value: CoreTypes.TextAlignmentType) {
const nativeView = <UITextField | UITextView | UILabel>this.nativeTextViewProtected;
switch (value) {