Unverified Commit 97b3742f authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Automatically flip material icons in RTL. (#12876)

I'm not really sure how to test this without a reference test, since
eventually we want to move this to a font feature and thus the obvious
test (looking for the Transform widget) isn't really valid.
parent b6bd628b
https://storage.googleapis.com/flutter_infra/flutter/fonts/13ac995daa9dda0a6ba0a45f1fccc541e616a74c/fonts.zip
https://storage.googleapis.com/flutter_infra/flutter/fonts/7a25ab00acc9a9e4a498641dcbd5a923a1249239/fonts.zip
......@@ -28,6 +28,82 @@ const Map<String, String> kIdentifierRewrites = const <String, String>{
'class': 'class_',
};
final Set<String> kMirroredIcons = new Set<String>.from(<String>[
// This list is obtained from:
// http://google.github.io/material-design-icons/#icons-in-rtl
'arrow_back',
'arrow_back_ios',
'arrow_forward',
'arrow_forward_ios',
'arrow_left',
'arrow_right',
'assignment',
'assignment_return',
'backspace',
'battery_unknown',
'call_made',
'call_merge',
'call_missed',
'call_missed_outgoing',
'call_received',
'call_split',
'chevron_left',
'chevron_right',
'chrome_reader_mode',
'device_unknown',
'dvr',
'event_note',
'featured_play_list',
'featured_video',
'first_page',
'flight_land',
'flight_takeoff',
'format_indent_decrease',
'format_indent_increase',
'format_list_bulleted',
'forward',
'functions',
'help',
'help_outline',
'input',
'keyboard_backspace',
'keyboard_tab',
'label',
'label_important',
'label_outline',
'last_page',
'launch',
'list',
'live_help',
'mobile_screen_share',
'multiline_chart',
'navigate_before',
'navigate_next',
'next_week',
'note',
'open_in_new',
'playlist_add',
'queue_music',
'redo',
'reply',
'reply_all',
'screen_share',
'send',
'short_text',
'show_chart',
'sort',
'star_half',
'subject',
'trending_flat',
'toc',
'trending_down',
'trending_up',
'undo',
'view_list',
'view_quilt',
'wrap_text',
]);
void main(List<String> args) {
// If we're run from the `tools` dir, set the cwd to the repo root.
if (path.basename(Directory.current.path) == 'tools')
......@@ -94,9 +170,10 @@ String getIconDeclaration(String line) {
final String codepoint = tokens[1];
final String identifier = kIdentifierRewrites[name] ?? name;
final String description = name.replaceAll('_', ' ');
final String rtl = kMirroredIcons.contains(name) ? ', matchTextDirection: true' : '';
return '''
/// <p><i class="material-icons md-36">$name</i> &#x2014; material icon named "$description".</p>
static const IconData $identifier = const IconData(0x$codepoint, fontFamily: 'MaterialIcons');
static const IconData $identifier = const IconData(0x$codepoint, fontFamily: 'MaterialIcons'$rtl);
''';
}
......@@ -599,31 +599,8 @@ class _MonthPickerState extends State<MonthPicker> {
});
}
Icon _getPreviousMonthIcon(TextDirection textDirection) {
assert(textDirection != null);
switch (textDirection) {
case TextDirection.rtl:
return const Icon(Icons.chevron_right);
case TextDirection.ltr:
return const Icon(Icons.chevron_left);
}
return null;
}
Icon _getNextMonthIcon(TextDirection textDirection) {
assert(textDirection != null);
switch (textDirection) {
case TextDirection.rtl:
return const Icon(Icons.chevron_left);
case TextDirection.ltr:
return const Icon(Icons.chevron_right);
}
return null;
}
@override
Widget build(BuildContext context) {
final TextDirection textDirection = Directionality.of(context);
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
return new SizedBox(
width: _kMonthPickerPortraitWidth,
......@@ -642,7 +619,7 @@ class _MonthPickerState extends State<MonthPicker> {
top: 0.0,
start: 8.0,
child: new IconButton(
icon: _getPreviousMonthIcon(textDirection),
icon: const Icon(Icons.chevron_left),
tooltip: localizations.previousMonthTooltip,
onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth,
),
......@@ -651,7 +628,7 @@ class _MonthPickerState extends State<MonthPicker> {
top: 0.0,
end: 8.0,
child: new IconButton(
icon: _getNextMonthIcon(textDirection),
icon: const Icon(Icons.chevron_right),
tooltip: localizations.nextMonthTooltip,
onPressed: _isDisplayingLastMonth ? null : _handleNextMonth,
),
......
......@@ -11,7 +11,6 @@ import 'icon_data.dart';
import 'icon_theme.dart';
import 'icon_theme_data.dart';
/// A graphical icon widget drawn with a glyph from a font described in
/// an [IconData] such as material's predefined [IconData]s in [Icons].
///
......@@ -37,6 +36,7 @@ class Icon extends StatelessWidget {
this.size,
this.color,
this.semanticLabel,
this.textDirection,
}) : super(key: key);
/// The icon to display. The available icons are described in [Icons].
......@@ -91,14 +91,29 @@ class Icon extends StatelessWidget {
///
/// See also:
///
/// * [Semantics.label] which is set with [semanticLabel] in the underlying
/// * [Semantics.label], which is set to [semanticLabel] in the underlying
/// [Semantics] widget.
final String semanticLabel;
/// The text direction to use for rendering the icon.
///
/// If this is null, the ambient [Directionality] is used instead.
///
/// Some icons follow the reading direction. For example, "back" buttons point
/// left in left-to-right environments and right in right-to-left
/// environments. Such icons have their [IconData.matchTextDirection] field
/// set to true, and the [Icon] widget uses the [textDirection] to determine
/// the orientation in which to draw the icon.
///
/// This property has no effect if the [icon]'s [IconData.matchTextDirection]
/// field is false, but for consistency a text direction value must always be
/// specified, either directly using this property or using [Directionality].
final TextDirection textDirection;
@override
Widget build(BuildContext context) {
assert(debugCheckHasDirectionality(context));
final TextDirection textDirection = Directionality.of(context);
assert(this.textDirection != null || debugCheckHasDirectionality(context));
final TextDirection textDirection = this.textDirection ?? Directionality.of(context);
final IconThemeData iconTheme = IconTheme.of(context);
......@@ -116,6 +131,35 @@ class Icon extends StatelessWidget {
if (iconOpacity != 1.0)
iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity);
Widget iconWidget = new RichText(
textDirection: textDirection, // Since we already fetched it for the assert...
text: new TextSpan(
text: new String.fromCharCode(icon.codePoint),
style: new TextStyle(
inherit: false,
color: iconColor,
fontSize: iconSize,
fontFamily: icon.fontFamily,
package: icon.fontPackage,
),
),
);
if (icon.matchTextDirection) {
switch (textDirection) {
case TextDirection.rtl:
iconWidget = new Transform(
transform: new Matrix4.identity()..scale(-1.0),
alignment: Alignment.center,
transformHitTests: false,
child: iconWidget,
);
break;
case TextDirection.ltr:
break;
}
}
return new Semantics(
label: semanticLabel,
child: new ExcludeSemantics(
......@@ -123,19 +167,7 @@ class Icon extends StatelessWidget {
width: iconSize,
height: iconSize,
child: new Center(
child: new RichText(
textDirection: textDirection, // Since we already fetched it for the assert...
text: new TextSpan(
text: new String.fromCharCode(icon.codePoint),
style: new TextStyle(
inherit: false,
color: iconColor,
fontSize: iconSize,
fontFamily: icon.fontFamily,
package: icon.fontPackage,
),
),
),
child: iconWidget,
),
),
),
......
......@@ -21,6 +21,7 @@ class IconData {
this.codePoint, {
this.fontFamily,
this.fontPackage,
this.matchTextDirection: false,
});
/// The Unicode code point at which this icon is stored in the icon font.
......@@ -39,6 +40,13 @@ class IconData {
/// * [TextStyle], which describes how to use fonts from other packages.
final String fontPackage;
/// Whether this icon should be automatically mirrored in right-to-left
/// environments.
///
/// The [Icon] widget respects this value by mirroring the icon when the
/// [Directionality] is [TextDirection.rtl].
final bool matchTextDirection;
@override
bool operator ==(dynamic other) {
if (runtimeType != other.runtimeType)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment