mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add locale parameter to EditableText (#18222)
This commit is contained in:
parent
326caa5d22
commit
691cbee613
@ -1 +1 @@
|
||||
760320e3fc8ccd12deb4066bd4033a98d078359f
|
||||
568342373b14fab81fd665d397c8c00db3d46fca
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle, Locale;
|
||||
import 'dart:ui' as ui show Paragraph, ParagraphBuilder, ParagraphConstraints, ParagraphStyle;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
@ -46,7 +46,7 @@ class TextPainter {
|
||||
double textScaleFactor = 1.0,
|
||||
int maxLines,
|
||||
String ellipsis,
|
||||
ui.Locale locale,
|
||||
Locale locale,
|
||||
}) : assert(text == null || text.debugAssertIsValid()),
|
||||
assert(textAlign != null),
|
||||
assert(textScaleFactor != null),
|
||||
@ -168,9 +168,9 @@ class TextPainter {
|
||||
}
|
||||
|
||||
/// The locale used to select region-specific glyphs.
|
||||
ui.Locale get locale => _locale;
|
||||
ui.Locale _locale;
|
||||
set locale(ui.Locale value) {
|
||||
Locale get locale => _locale;
|
||||
Locale _locale;
|
||||
set locale(Locale value) {
|
||||
if (_locale == value)
|
||||
return;
|
||||
_locale = value;
|
||||
|
||||
@ -299,6 +299,13 @@ class TextStyle extends Diagnosticable {
|
||||
final double height;
|
||||
|
||||
/// The locale used to select region-specific glyphs.
|
||||
///
|
||||
/// This property is rarely set. Typically the locale used to select
|
||||
/// region-specific glyphs is defined by the text widget's [BuildContext]
|
||||
/// using `Localizations.localeOf(context)`. For example [RichText] defines
|
||||
/// its locale this way. However, a rich text widget's [TextSpan]s could specify
|
||||
/// text styles with different explicit locales in order to select different
|
||||
/// region-specifc glyphs for each text span.
|
||||
final Locale locale;
|
||||
|
||||
/// The paint drawn as a background for the text.
|
||||
|
||||
@ -133,6 +133,7 @@ class RenderEditable extends RenderBox {
|
||||
this.onCaretChanged,
|
||||
this.ignorePointer = false,
|
||||
bool obscureText = false,
|
||||
Locale locale,
|
||||
}) : assert(textAlign != null),
|
||||
assert(textDirection != null, 'RenderEditable created without a textDirection.'),
|
||||
assert(maxLines == null || maxLines > 0),
|
||||
@ -145,6 +146,7 @@ class RenderEditable extends RenderBox {
|
||||
textAlign: textAlign,
|
||||
textDirection: textDirection,
|
||||
textScaleFactor: textScaleFactor,
|
||||
locale: locale,
|
||||
),
|
||||
_cursorColor = cursorColor,
|
||||
_showCursor = showCursor ?? new ValueNotifier<bool>(false),
|
||||
@ -249,6 +251,24 @@ class RenderEditable extends RenderBox {
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
/// Used by this renderer's internal [TextPainter] to select a locale-specific
|
||||
/// font.
|
||||
///
|
||||
/// In some cases the same Unicode character may be rendered differently depending
|
||||
/// on the locale. For example the '骨' character is rendered differently in
|
||||
/// the Chinese and Japanese locales. In these cases the [locale] may be used
|
||||
/// to select a locale-specific font.
|
||||
///
|
||||
/// If this value is null, a system-dependent algorithm is used to select
|
||||
/// the font.
|
||||
Locale get locale => _textPainter.locale;
|
||||
set locale(Locale value) {
|
||||
if (_textPainter.locale == value)
|
||||
return;
|
||||
_textPainter.locale = value;
|
||||
markNeedsTextLayout();
|
||||
}
|
||||
|
||||
/// The color to use when painting the cursor.
|
||||
Color get cursorColor => _cursorColor;
|
||||
Color _cursorColor;
|
||||
@ -749,6 +769,7 @@ class RenderEditable extends RenderBox {
|
||||
properties.add(new IntProperty('maxLines', maxLines));
|
||||
properties.add(new DiagnosticsProperty<Color>('selectionColor', selectionColor));
|
||||
properties.add(new DoubleProperty('textScaleFactor', textScaleFactor));
|
||||
properties.add(new DiagnosticsProperty<Locale>('locale', locale, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<TextSelection>('selection', selection));
|
||||
properties.add(new DiagnosticsProperty<ViewportOffset>('offset', offset));
|
||||
}
|
||||
|
||||
@ -458,6 +458,7 @@ class RenderParagraph extends RenderBox {
|
||||
properties.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
|
||||
properties.add(new EnumProperty<TextOverflow>('overflow', overflow));
|
||||
properties.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: 1.0));
|
||||
properties.add(new DiagnosticsProperty<Locale>('locale', locale, defaultValue: null));
|
||||
properties.add(new IntProperty('maxLines', maxLines, ifNull: 'unlimited'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import 'basic.dart';
|
||||
import 'focus_manager.dart';
|
||||
import 'focus_scope.dart';
|
||||
import 'framework.dart';
|
||||
import 'localizations.dart';
|
||||
import 'media_query.dart';
|
||||
import 'scroll_controller.dart';
|
||||
import 'scroll_physics.dart';
|
||||
@ -160,6 +161,7 @@ class EditableText extends StatefulWidget {
|
||||
@required this.cursorColor,
|
||||
this.textAlign = TextAlign.start,
|
||||
this.textDirection,
|
||||
this.locale,
|
||||
this.textScaleFactor,
|
||||
this.maxLines = 1,
|
||||
this.autofocus = false,
|
||||
@ -229,6 +231,15 @@ class EditableText extends StatefulWidget {
|
||||
/// Defaults to the ambient [Directionality], if any.
|
||||
final TextDirection textDirection;
|
||||
|
||||
/// Used to select a font when the same Unicode character can
|
||||
/// be rendered differently, depending on the locale.
|
||||
///
|
||||
/// It's rarely necessary to set this property. By default its value
|
||||
/// is inherited from the enclosing app with `Localizations.localeOf(context)`.
|
||||
///
|
||||
/// See [RenderEditable.locale] for more information.
|
||||
final Locale locale;
|
||||
|
||||
/// The number of font pixels for each logical pixel.
|
||||
///
|
||||
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
||||
@ -299,6 +310,7 @@ class EditableText extends StatefulWidget {
|
||||
style?.debugFillProperties(properties);
|
||||
properties.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
|
||||
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<Locale>('locale', locale, defaultValue: null));
|
||||
properties.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
|
||||
properties.add(new IntProperty('maxLines', maxLines, defaultValue: 1));
|
||||
properties.add(new DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
|
||||
@ -687,6 +699,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
||||
textScaleFactor: widget.textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
|
||||
textAlign: widget.textAlign,
|
||||
textDirection: _textDirection,
|
||||
locale: widget.locale,
|
||||
obscureText: widget.obscureText,
|
||||
autocorrect: widget.autocorrect,
|
||||
offset: offset,
|
||||
@ -747,6 +760,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
this.textScaleFactor,
|
||||
this.textAlign,
|
||||
@required this.textDirection,
|
||||
this.locale,
|
||||
this.obscureText,
|
||||
this.autocorrect,
|
||||
this.offset,
|
||||
@ -767,6 +781,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
final double textScaleFactor;
|
||||
final TextAlign textAlign;
|
||||
final TextDirection textDirection;
|
||||
final Locale locale;
|
||||
final bool obscureText;
|
||||
final bool autocorrect;
|
||||
final ViewportOffset offset;
|
||||
@ -786,6 +801,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
textScaleFactor: textScaleFactor,
|
||||
textAlign: textAlign,
|
||||
textDirection: textDirection,
|
||||
locale: locale ?? Localizations.localeOf(context, nullOk: true),
|
||||
selection: value.selection,
|
||||
offset: offset,
|
||||
onSelectionChanged: onSelectionChanged,
|
||||
@ -807,6 +823,7 @@ class _Editable extends LeafRenderObjectWidget {
|
||||
..textScaleFactor = textScaleFactor
|
||||
..textAlign = textAlign
|
||||
..textDirection = textDirection
|
||||
..locale = locale ?? Localizations.localeOf(context, nullOk: true)
|
||||
..selection = value.selection
|
||||
..offset = offset
|
||||
..onSelectionChanged = onSelectionChanged
|
||||
|
||||
@ -206,6 +206,7 @@ class Text extends StatelessWidget {
|
||||
this.style,
|
||||
this.textAlign,
|
||||
this.textDirection,
|
||||
this.locale,
|
||||
this.softWrap,
|
||||
this.overflow,
|
||||
this.textScaleFactor,
|
||||
@ -220,6 +221,7 @@ class Text extends StatelessWidget {
|
||||
this.style,
|
||||
this.textAlign,
|
||||
this.textDirection,
|
||||
this.locale,
|
||||
this.softWrap,
|
||||
this.overflow,
|
||||
this.textScaleFactor,
|
||||
@ -263,6 +265,15 @@ class Text extends StatelessWidget {
|
||||
/// Defaults to the ambient [Directionality], if any.
|
||||
final TextDirection textDirection;
|
||||
|
||||
/// Used to select a font when the same Unicode character can
|
||||
/// be rendered differently, depending on the locale.
|
||||
///
|
||||
/// It's rarely necessary to set this property. By default its value
|
||||
/// is inherited from the enclosing app with `Localizations.localeOf(context)`.
|
||||
///
|
||||
/// See [RenderParagraph.locale] for more information.
|
||||
final Locale locale;
|
||||
|
||||
/// Whether the text should break at soft line breaks.
|
||||
///
|
||||
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
||||
@ -303,6 +314,7 @@ class Text extends StatelessWidget {
|
||||
return new RichText(
|
||||
textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
|
||||
textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
|
||||
locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null
|
||||
softWrap: softWrap ?? defaultTextStyle.softWrap,
|
||||
overflow: overflow ?? defaultTextStyle.overflow,
|
||||
textScaleFactor: textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
|
||||
@ -325,6 +337,7 @@ class Text extends StatelessWidget {
|
||||
style?.debugFillProperties(properties);
|
||||
properties.add(new EnumProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
|
||||
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<Locale>('locale', locale, defaultValue: null));
|
||||
properties.add(new FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true));
|
||||
properties.add(new EnumProperty<TextOverflow>('overflow', overflow, defaultValue: null));
|
||||
properties.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
|
||||
|
||||
@ -451,7 +451,7 @@ class _TextStyleProxy implements TextStyle {
|
||||
@override FontStyle get fontStyle => _delegate.fontStyle;
|
||||
@override FontWeight get fontWeight => _delegate.fontWeight;
|
||||
@override double get height => _delegate.height;
|
||||
@override ui.Locale get locale => _delegate.locale;
|
||||
@override Locale get locale => _delegate.locale;
|
||||
@override ui.Paint get background => _delegate.background;
|
||||
@override bool get inherit => _delegate.inherit;
|
||||
@override double get letterSpacing => _delegate.letterSpacing;
|
||||
@ -479,7 +479,7 @@ class _TextStyleProxy implements TextStyle {
|
||||
}
|
||||
|
||||
@override
|
||||
TextStyle copyWith({Color color, String fontFamily, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, ui.Locale locale, ui.Paint background, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel}) {
|
||||
TextStyle copyWith({Color color, String fontFamily, double fontSize, FontWeight fontWeight, FontStyle fontStyle, double letterSpacing, double wordSpacing, TextBaseline textBaseline, double height, Locale locale, ui.Paint background, TextDecoration decoration, Color decorationColor, TextDecorationStyle decorationStyle, String debugLabel}) {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
@ -489,7 +489,7 @@ class _TextStyleProxy implements TextStyle {
|
||||
}
|
||||
|
||||
@override
|
||||
ui.ParagraphStyle getParagraphStyle({TextAlign textAlign, TextDirection textDirection, double textScaleFactor = 1.0, String ellipsis, int maxLines, ui.Locale locale}) {
|
||||
ui.ParagraphStyle getParagraphStyle({TextAlign textAlign, TextDirection textDirection, double textScaleFactor = 1.0, String ellipsis, int maxLines, Locale locale}) {
|
||||
throw new UnimplementedError();
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ void main() {
|
||||
),
|
||||
textAlign: TextAlign.start,
|
||||
textDirection: TextDirection.ltr,
|
||||
locale: const Locale('ja', 'JP'),
|
||||
offset: new ViewportOffset.zero(),
|
||||
);
|
||||
expect(editable.getMinIntrinsicWidth(double.infinity), 50.0);
|
||||
@ -33,6 +34,7 @@ void main() {
|
||||
' │ maxLines: 1\n'
|
||||
' │ selectionColor: null\n'
|
||||
' │ textScaleFactor: 1.0\n'
|
||||
' │ locale: ja_JP\n'
|
||||
' │ selection: null\n'
|
||||
' │ offset: _FixedViewportOffset#00000(offset: 0.0)\n'
|
||||
' ╘═╦══ text ═══\n'
|
||||
@ -46,4 +48,4 @@ void main() {
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
159
packages/flutter/test/rendering/localized_fonts_test.dart
Normal file
159
packages/flutter/test/rendering/localized_fonts_test.dart
Normal file
@ -0,0 +1,159 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
// TODO(hansmuller): when https://github.com/flutter/flutter/issues/17700
|
||||
// is fixed, these tests should be updated to use a real font (not Ahem).
|
||||
|
||||
void main() {
|
||||
testWidgets(
|
||||
'RichText TextSpan styles with different locales',
|
||||
(WidgetTester tester) async {
|
||||
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
supportedLocales: const <Locale>[
|
||||
const Locale('en', 'US'),
|
||||
const Locale('ja'),
|
||||
const Locale('zh'),
|
||||
],
|
||||
home: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
const String character = '骨';
|
||||
final TextStyle style = Theme.of(context).textTheme.display3;
|
||||
return new Scaffold(
|
||||
body: new Container(
|
||||
padding: const EdgeInsets.all(48.0),
|
||||
alignment: Alignment.center,
|
||||
child: new RepaintBoundary(
|
||||
// Expected result can be seen here:
|
||||
// https://user-images.githubusercontent.com/1377460/40503473-faad6f34-5f42-11e8-972b-d83b727c9d0e.png
|
||||
child: new RichText(
|
||||
text: new TextSpan(
|
||||
children: <TextSpan>[
|
||||
new TextSpan(text: character, style: style.copyWith(locale: const Locale('ja'))),
|
||||
new TextSpan(text: character, style: style.copyWith(locale: const Locale('zh'))),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
find.byType(RichText),
|
||||
matchesGoldenFile('localized_fonts.rich_text.styled_text_span.png'),
|
||||
skip: !Platform.isLinux,
|
||||
);
|
||||
},
|
||||
skip: !Platform.isLinux,
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Text with locale-specific glyphs, ambient locale',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
supportedLocales: const <Locale>[
|
||||
const Locale('en', 'US'),
|
||||
const Locale('ja'),
|
||||
const Locale('zh'),
|
||||
],
|
||||
home: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
const String character = '骨';
|
||||
final TextStyle style = Theme.of(context).textTheme.display3;
|
||||
return new Scaffold(
|
||||
body: new Container(
|
||||
padding: const EdgeInsets.all(48.0),
|
||||
alignment: Alignment.center,
|
||||
child: new RepaintBoundary(
|
||||
// Expected result can be seen here:
|
||||
// https://user-images.githubusercontent.com/1377460/40503473-faad6f34-5f42-11e8-972b-d83b727c9d0e.png
|
||||
child: new Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
new Localizations.override(
|
||||
context: context,
|
||||
locale: const Locale('ja'),
|
||||
child: new Text(character, style: style),
|
||||
),
|
||||
new Localizations.override(
|
||||
context: context,
|
||||
locale: const Locale('zh'),
|
||||
child: new Text(character, style: style),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
find.byType(Row),
|
||||
matchesGoldenFile('localized_fonts.text_ambient_locale.chars.png'),
|
||||
skip: !Platform.isLinux,
|
||||
);
|
||||
},
|
||||
skip: !Platform.isLinux,
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Text with locale-specific glyphs, explicit locale',
|
||||
(WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
supportedLocales: const <Locale>[
|
||||
const Locale('en', 'US'),
|
||||
const Locale('ja'),
|
||||
const Locale('zh'),
|
||||
],
|
||||
home: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
const String character = '骨';
|
||||
final TextStyle style = Theme.of(context).textTheme.display3;
|
||||
return new Scaffold(
|
||||
body: new Container(
|
||||
padding: const EdgeInsets.all(48.0),
|
||||
alignment: Alignment.center,
|
||||
child: new RepaintBoundary(
|
||||
// Expected result can be seen here:
|
||||
// https://user-images.githubusercontent.com/1377460/40503473-faad6f34-5f42-11e8-972b-d83b727c9d0e.png
|
||||
child: new Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
new Text(character, style: style, locale: const Locale('ja')),
|
||||
new Text(character, style: style, locale: const Locale('zh')),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
find.byType(Row),
|
||||
matchesGoldenFile('localized_fonts.text_explicit_locale.chars.png'),
|
||||
skip: !Platform.isLinux,
|
||||
);
|
||||
},
|
||||
skip: !Platform.isLinux,
|
||||
);
|
||||
|
||||
}
|
||||
@ -288,6 +288,7 @@ void main() {
|
||||
final RenderParagraph paragraph = new RenderParagraph(
|
||||
const TextSpan(text: _kText),
|
||||
textDirection: TextDirection.ltr,
|
||||
locale: const Locale('ja', 'JP'),
|
||||
);
|
||||
expect(paragraph, hasAGoodToStringDeep);
|
||||
expect(
|
||||
@ -301,6 +302,7 @@ void main() {
|
||||
' │ textDirection: ltr\n'
|
||||
' │ softWrap: wrapping at box width\n'
|
||||
' │ overflow: clip\n'
|
||||
' │ locale: ja_JP\n'
|
||||
' │ maxLines: unlimited\n'
|
||||
' ╘═╦══ text ═══\n'
|
||||
' ║ TextSpan:\n'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user