using System; using System.Collections.Generic; using System.Linq; using Unity.UIWidgets.async2; using Unity.UIWidgets.foundation; using Unity.UIWidgets.rendering; using Unity.UIWidgets.ui; namespace Unity.UIWidgets.widgets { class _Pending { public _Pending( LocalizationsDelegate del, Future futureValue) { this.del = del; this.futureValue = futureValue; } public readonly LocalizationsDelegate del; public readonly Future futureValue; internal static Future> _loadAll( Locale locale, IEnumerable allDelegates) { Dictionary output = new Dictionary(); List<_Pending> pendingList = null; HashSet types = new HashSet(); List delegates = new List(); foreach (LocalizationsDelegate del in allDelegates) { if (!types.Contains(del.type) && del.isSupported(locale)) { types.Add(del.type); delegates.Add(del); } } foreach (LocalizationsDelegate del in delegates) { Future inputValue = del.load(locale).to(); object completedValue = null; Future futureValue = inputValue.then_(value => { completedValue = value; return FutureOr.value(completedValue); }).to(); if (completedValue != null) { Type type = del.type; D.assert(!output.ContainsKey(type)); output[type] = completedValue; } else { pendingList = pendingList ?? new List<_Pending>(); pendingList.Add(new _Pending(del, futureValue)); } } if (pendingList == null) { return Future.value(output).to>(); } return Future.wait(pendingList.Select(p => p.futureValue)) .then(values => { var list = (List)values; D.assert(list.Count == pendingList.Count); for (int i = 0; i < list.Count; i += 1) { Type type = pendingList[i].del.type; D.assert(!output.ContainsKey(type)); output[type] = list[i]; } return output; }).to>(); } } public abstract class LocalizationsDelegate { protected LocalizationsDelegate() { } public abstract bool isSupported(Locale locale); public abstract Future load(Locale locale); public abstract bool shouldReload(LocalizationsDelegate old); public abstract Type type { get; } public override string ToString() { return $"{GetType()}[{type}]"; } } public abstract class LocalizationsDelegate : LocalizationsDelegate { //public abstract Future load(Locale locale); public override Type type { get { return typeof(T); } } } public abstract class WidgetsLocalizations { public TextDirection textDirection { get; } static WidgetsLocalizations of(BuildContext context) { return Localizations.of(context, typeof(WidgetsLocalizations)); } } class _WidgetsLocalizationsDelegate : LocalizationsDelegate { public _WidgetsLocalizationsDelegate() { } public override bool isSupported(Locale locale) { return true; } public override Future load(Locale locale) { return DefaultWidgetsLocalizations.load(locale); } public override bool shouldReload(LocalizationsDelegate old) { return false; } public override string ToString() { return "DefaultWidgetsLocalizations.delegate(en_US)"; } } public class DefaultWidgetsLocalizations : WidgetsLocalizations { public DefaultWidgetsLocalizations() { } public static Future load(Locale locale) { return new SynchronousFuture(new DefaultWidgetsLocalizations()); } public static readonly LocalizationsDelegate del = new _WidgetsLocalizationsDelegate(); } class _LocalizationsScope : InheritedWidget { public _LocalizationsScope( Key key = null, Locale locale = null, _LocalizationsState localizationsState = null, Dictionary typeToResources = null, Widget child = null ) : base(key: key, child: child) { D.assert(locale != null); D.assert(localizationsState != null); D.assert(typeToResources != null); this.locale = locale; this.localizationsState = localizationsState; this.typeToResources = typeToResources; } public readonly Locale locale; public readonly _LocalizationsState localizationsState; public readonly Dictionary typeToResources; public override bool updateShouldNotify(InheritedWidget old) { return typeToResources != ((_LocalizationsScope) old).typeToResources; } } public class Localizations : StatefulWidget { public Localizations( Key key = null, Locale locale = null, List delegates = null, Widget child = null ) : base(key: key) { D.assert(locale != null); D.assert(delegates != null); D.assert(delegates.Any(del => del is LocalizationsDelegate)); this.locale = locale; this.delegates = delegates; this.child = child; } public static Localizations overrides( Key key = null, BuildContext context = null, Locale locale = null, List delegates = null, Widget child = null ) { List mergedDelegates = _delegatesOf(context); if (delegates != null) { mergedDelegates.InsertRange(0, delegates); } return new Localizations( key: key, locale: locale ?? localeOf(context), delegates: mergedDelegates, child: child ); } public readonly Locale locale; public readonly List delegates; public readonly Widget child; public static Locale localeOf(BuildContext context, bool nullOk = false) { D.assert(context != null); _LocalizationsScope scope = (_LocalizationsScope) context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>(); if (nullOk && scope == null) { return null; } D.assert((bool) (scope != null), () => "a Localizations ancestor was not found"); return scope.localizationsState.locale; } public static List _delegatesOf(BuildContext context) { D.assert(context != null); _LocalizationsScope scope = (_LocalizationsScope) context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>(); D.assert(scope != null, () => "a Localizations ancestor was not found"); return new List(scope.localizationsState.widget.delegates); } public static T of(BuildContext context, Type type) { D.assert(context != null); D.assert(type != null); _LocalizationsScope scope = (_LocalizationsScope) context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>(); if (scope != null && scope.localizationsState != null) { return scope.localizationsState.resourcesFor(type); } return default; } public override State createState() { return new _LocalizationsState(); } public override void debugFillProperties(DiagnosticPropertiesBuilder properties) { base.debugFillProperties(properties); properties.add(new DiagnosticsProperty("locale", locale)); properties.add(new EnumerableProperty("delegates", delegates)); } } class _LocalizationsState : State { readonly GlobalKey _localizedResourcesScopeKey = GlobalKey.key(); Dictionary _typeToResources = new Dictionary(); public Locale locale { get { return _locale; } } Locale _locale; public override void initState() { base.initState(); load(widget.locale); } bool _anyDelegatesShouldReload(Localizations old) { if (widget.delegates.Count != old.delegates.Count) { return true; } List delegates = widget.delegates.ToList(); List oldDelegates = old.delegates.ToList(); for (int i = 0; i < delegates.Count; i += 1) { LocalizationsDelegate del = delegates[i]; LocalizationsDelegate oldDelegate = oldDelegates[i]; if (del.GetType() != oldDelegate.GetType() || del.shouldReload(oldDelegate)) { return true; } } return false; } public override void didUpdateWidget(StatefulWidget oldWidget) { Localizations old = (Localizations) oldWidget; base.didUpdateWidget(old); if (widget.locale != old.locale || (widget.delegates == null && old.delegates != null) || (widget.delegates != null && old.delegates == null) || (widget.delegates != null && _anyDelegatesShouldReload(old))) { load(widget.locale); } } void load(Locale locale) { var delegates = widget.delegates; if (delegates == null || delegates.isEmpty()) { _locale = locale; return; } Dictionary typeToResources = null; Future> typeToResourcesFuture = _Pending._loadAll(locale, delegates) .then(value => { return FutureOr.value(typeToResources = (Dictionary)value); }).to>(); if (typeToResources != null) { _typeToResources = typeToResources; _locale = locale; } else { typeToResourcesFuture.then(value => { if (mounted) { setState(() => { _typeToResources = (Dictionary) value; _locale = locale; }); } }); } } public T resourcesFor(Type type) { D.assert(type != null); T resources = (T) _typeToResources.getOrDefault(type); return resources; } TextDirection _textDirection { get { WidgetsLocalizations resources = (WidgetsLocalizations)_typeToResources.getOrDefault(typeof(WidgetsLocalizations)) ; D.assert(resources != null); return resources.textDirection; } } public override Widget build(BuildContext context) { if (_locale == null) { return new Container(); } return new _LocalizationsScope( key: _localizedResourcesScopeKey, locale: _locale, localizationsState: this, typeToResources: _typeToResources, child: new Directionality( textDirection: _textDirection, child: widget.child ) ); } } }