比较提交

...
此合并请求有变更与目标分支冲突。
/com.unity.uiwidgets/Runtime/widgets/single_child_scroll_view.cs
/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample
/Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample
/Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery
/Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery
/Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/cupertino
/com.unity.uiwidgets/Runtime/material
/com.unity.uiwidgets/Runtime/material
/Samples/UIWidgetsSamples_2019_4/Assets/Editor
/Samples/UIWidgetsSamples_2019_4/Assets/Editor

1 次代码提交

作者 SHA1 备注 提交日期
guanghuispark d0b6e5fc Merge branch 'master' into zgh/devtools 3 年前
共有 568 个文件被更改,包括 13751 次插入8 次删除
  1. 5
      Samples/UIWidgetsSamples_2019_4/Packages/manifest.json
  2. 3
      com.unity.uiwidgets/Runtime/animation/animation_controller.cs
  3. 2
      com.unity.uiwidgets/Runtime/foundation/key.cs
  4. 4
      com.unity.uiwidgets/Runtime/widgets/single_child_scroll_view.cs
  5. 2
      com.unity.uiwidgets/Runtime/semantics/semantics.cs.meta
  6. 8
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample.meta
  7. 3
      Samples/UIWidgetsSamples_2019_4/Assets/Editor.meta
  8. 8
      com.unity.uiwidgets/Runtime/cupertino.meta
  9. 8
      com.unity.uiwidgets/Runtime/redux.meta
  10. 8
      com.unity.uiwidgets/Runtime/Plugins.meta
  11. 9
      com.unity.uiwidgets.devtools/package.json
  12. 23
      com.unity.uiwidgets.devtools/Editor/enum_utils.cs
  13. 11
      com.unity.uiwidgets.devtools/Editor/Unity.UIWidgets.DevTools.Editor.asmdef
  14. 23
      com.unity.uiwidgets.devtools/Editor/analytics/provider.cs
  15. 53
      com.unity.uiwidgets.devtools/Editor/analytics/stub_provider.cs
  16. 548
      com.unity.uiwidgets.devtools/Editor/app.cs
  17. 3
      com.unity.uiwidgets.devtools/Editor/config_specific.meta
  18. 18
      com.unity.uiwidgets.devtools/Editor/config_specific/ide_theme/ide_theme.cs
  19. 52
      com.unity.uiwidgets.devtools/Editor/globals.cs
  20. 3
      com.unity.uiwidgets.devtools/Editor/globals.cs.meta
  21. 260
      com.unity.uiwidgets.devtools/Editor/inspector/diagnostics.cs
  22. 1001
      com.unity.uiwidgets.devtools/Editor/inspector/diagnostics_node.cs
  23. 152
      com.unity.uiwidgets.devtools/Editor/inspector/flutter_widget.cs
  24. 974
      com.unity.uiwidgets.devtools/Editor/inspector/inspector_controller.cs
  25. 1001
      com.unity.uiwidgets.devtools/Editor/inspector/inspector_data_models.cs
  26. 408
      com.unity.uiwidgets.devtools/Editor/inspector/inspector_screen.cs
  27. 144
      com.unity.uiwidgets.devtools/Editor/inspector/inspector_screen_details_tab.cs
  28. 1001
      com.unity.uiwidgets.devtools/Editor/inspector/inspector_service.cs
  29. 43
      com.unity.uiwidgets.devtools/Editor/inspector/inspector_text_style.cs
  30. 873
      com.unity.uiwidgets.devtools/Editor/inspector/inspector_tree.cs
  31. 453
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/box/box.cs
  32. 807
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/flex/flex.cs
  33. 266
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/flex/utils.cs
  34. 93
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/layout_explorer.cs
  35. 374
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/arrow.cs
  36. 39
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/dimension.cs
  37. 170
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/free_space.cs
  38. 71
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/overflow_indicator_painter.cs
  39. 218
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/theme.cs
  40. 193
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/widget_constraints.cs
  41. 345
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/layout_explorer_widget.cs
  42. 569
      com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/utils.cs
  43. 21
      com.unity.uiwidgets.devtools/Editor/listenable.cs
  44. 20
      com.unity.uiwidgets.devtools/Editor/main.cs
  45. 290
      com.unity.uiwidgets.devtools/Editor/screen.cs
  46. 59
      com.unity.uiwidgets.devtools/Editor/theme.cs
  47. 311
      com.unity.uiwidgets.devtools/Editor/ui/icons.cs
  48. 59
      com.unity.uiwidgets.devtools/Editor/utils.cs
  49. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialButtonSample.cs.meta
  50. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialInkWellSample.cs.meta
  51. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialNavigationBarSample.cs.meta
  52. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialSliderSample.cs.meta
  53. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialTabBarSample.cs.meta
  54. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialThemeSample.cs.meta
  55. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/TableSample.cs.meta
  56. 11
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/ReorderableListSample.cs.meta
  57. 47
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/BottomAppBarSample.cs
  58. 107
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/DividerAndButton.cs
  59. 204
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialAppBarSample.cs
  60. 73
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialButtonSample.cs
  61. 56
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialInkWellSample.cs
  62. 86
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialNavigationBarSample.cs
  63. 61
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialSliderSample.cs
  64. 130
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialTabBarSample.cs
  65. 82
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialThemeSample.cs
  66. 79
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/ReorderableListSample.cs
  67. 53
      Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/TableSample.cs
  68. 3
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery.meta
  69. 14
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/UIWidgetsGallery.asmdef
  70. 27
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/GalleryMain.cs
  71. 3
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine.meta
  72. 3
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/material.meta
  73. 19
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs
  74. 3
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs.meta
  75. 276
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_alert_demo.cs
  76. 89
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_buttons_demo.cs
  77. 307
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs
  78. 67
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_slider_demo.cs
  79. 873
      Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_navigation_demo.cs

5
Samples/UIWidgetsSamples_2019_4/Packages/manifest.json


"com.unity.collab-proxy": "1.2.16",
"com.unity.ide.rider": "1.1.4",
"com.unity.ide.vscode": "1.2.1",
"com.unity.uiwidgets": "file:../../../com.unity.uiwidgets",
"com.unity.test-framework": "1.1.14",
"com.unity.test-framework": "1.1.16",
"com.unity.uiwidgets": "file:../../../com.unity.uiwidgets",
"com.unity.uiwidgets.devtools": "file:../../../com.unity.uiwidgets.devtools",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",
"com.unity.modules.animation": "1.0.0",

3
com.unity.uiwidgets/Runtime/animation/animation_controller.cs


public override float value {
get { return _value; }
float _value;
public float _value;
public void setValue(float newValue) {
stop();

2
com.unity.uiwidgets/Runtime/foundation/key.cs


#pragma warning disable 0660
#pragma warning disable 0661
public abstract class Key {
protected Key() {
public Key() {
}
public static Key key(string value) {

4
com.unity.uiwidgets/Runtime/widgets/single_child_scroll_view.cs


public class SingleChildScrollView : StatelessWidget {
public SingleChildScrollView(
Key key = null,
Axis scrollDirection = Axis.vertical,
Axis? scrollDirection = null,
bool reverse = false,
EdgeInsetsGeometry padding = null,
bool? primary = null,

() =>
"Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. " +
"You cannot both set primary to true and pass an explicit controller.");
this.scrollDirection = scrollDirection;
this.scrollDirection = scrollDirection?? Axis.vertical;
this.reverse = reverse;
this.padding = padding;
this.primary = primary ?? controller == null && scrollDirection == Axis.vertical;

2
com.unity.uiwidgets/Runtime/semantics/semantics.cs.meta


fileFormatVersion: 2
guid: 2f6601e4d6a788a458d2f9e1d06fa1ea
guid: 5b2b6b1be992fe64887b60d5c32c5508
MonoImporter:
externalObjects: {}
serializedVersion: 2

8
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample.meta


fileFormatVersion: 2
guid: c0f377c78881e9243879a01b93524d63
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

3
Samples/UIWidgetsSamples_2019_4/Assets/Editor.meta


fileFormatVersion: 2
guid: fbf4348f25a14fb7a48fd287e0d01b88
timeCreated: 1614220178

8
com.unity.uiwidgets/Runtime/cupertino.meta


fileFormatVersion: 2
guid: 5c3d34af75804254da0566129e0d4ac0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
com.unity.uiwidgets/Runtime/redux.meta


fileFormatVersion: 2
guid: a074d5db1a47743f19fc0f9ac709e173
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
com.unity.uiwidgets/Runtime/Plugins.meta


fileFormatVersion: 2
guid: 13f5f7be9f33d144d89d2195786677c7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

9
com.unity.uiwidgets.devtools/package.json


{
"name": "com.unity.uiwidgets.devtools",
"displayName":"DevTools",
"version": "1.5.4-preview.1",
"unity": "2019.4",
"description": "DevTools is an indispensable tool for development in UIWidgets",
"dependencies": {
}
}

23
com.unity.uiwidgets.devtools/Editor/enum_utils.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
namespace Unity.UIWidgets.DevTools
{
public class EnumUtils<T> {
public EnumUtils(List<T> enumValues) {
foreach (var val in enumValues) {
var enumDescription = DiagnosticUtils.describeEnum(val);
_lookupTable[enumDescription] = val;
_reverseLookupTable[val] = enumDescription;
}
}
Dictionary<string, T> _lookupTable = new Dictionary<string, T>();
Dictionary<T, string> _reverseLookupTable = new Dictionary<T, string>();
T enumEntry(string enumName) => _lookupTable[enumName];
string name(T enumEntry) => _reverseLookupTable[enumEntry];
}
}

11
com.unity.uiwidgets.devtools/Editor/Unity.UIWidgets.DevTools.Editor.asmdef


{
"name": "Unity.UIWidgets.DevTools.Editor",
"references": [
"Unity.UIWidgets",
"Unity.UIWidgets.Editor"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}

23
com.unity.uiwidgets.devtools/Editor/analytics/provider.cs


namespace Unity.UIWidgets.DevTools.analytics
{
public abstract class AnalyticsProvider {
private bool isGtagsEnabled
{
get;
}
private bool shouldPrompt
{
get;
}
private bool isEnabled
{
get;
}
public abstract void setUpAnalytics();
public abstract void setAllowAnalytics();
public abstract void setDontAllowAnalytics();
}
}

53
com.unity.uiwidgets.devtools/Editor/analytics/stub_provider.cs


using Unity.UIWidgets.async2;
namespace Unity.UIWidgets.DevTools.analytics
{
public class stub_provider
{
public static AnalyticsProvider analyticsProvider
{
get
{
return _provider;
}
}
public static AnalyticsProvider _provider = new _StubProvider();
}
public class _StubProvider : AnalyticsProvider {
public new bool isEnabled
{
get
{
return false;
}
}
public new bool shouldPrompt
{
get
{
return false;
}
}
public new bool isGtagsEnabled
{
get
{
return false;
}
}
public override void setAllowAnalytics() {}
public override void setDontAllowAnalytics() {}
public override void setUpAnalytics() {}
}
}

548
com.unity.uiwidgets.devtools/Editor/app.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.DevTools;
using Unity.UIWidgets.DevTools.analytics;
using Unity.UIWidgets.DevTools.config_specific.ide_theme;
using Unity.UIWidgets.DevTools.inspector;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Screen = Unity.UIWidgets.DevTools.Screen;
namespace Unity.UIWidgets.DevTools
{
public class appUtils
{
public static readonly bool showVmDeveloperMode = false;
/// Whether this DevTools build is external.
public bool isExternalBuild = true;
public static List<DevToolsScreen<object>> defaultScreens {
get
{
return new List<DevToolsScreen<object>>
{
new DevToolsScreen<object>(
new InspectorScreen(),
createController: () => new InspectorSettingsController()
)
};
}
}
}
public class DevToolsApp : StatefulWidget {
public DevToolsApp(
List<DevToolsScreen<object>> screens,
IdeTheme ideTheme,
AnalyticsProvider analyticsProvider
)
{
this.screens = screens;
this.ideTheme = ideTheme;
this.analyticsProvider = analyticsProvider;
}
public readonly List<DevToolsScreen<object>> screens;
public readonly IdeTheme ideTheme;
public readonly AnalyticsProvider analyticsProvider;
public override State createState()
{
return new DevToolsAppState();
}
}
// TODO(https://github.com/flutter/devtools/issues/1146): Introduce tests that
// navigate the full app.
public class DevToolsAppState : State<DevToolsApp> {
List<Screen> _screens
{
get
{
List<Screen> screensList = new List<Screen>();
foreach (var s in widget.screens)
{
screensList.Add(s.screen);
}
return screensList;
}
}
IdeTheme ideTheme
{
get
{
return widget.ideTheme;
}
}
bool isDarkThemeEnabled
{
get
{
return _isDarkThemeEnabled;
}
}
bool _isDarkThemeEnabled;
bool vmDeveloperModeEnabled
{
get
{
return _vmDeveloperModeEnabled;
}
}
bool _vmDeveloperModeEnabled;
public override void initState() {
base.initState();
// ga.setupDimensions();
//
// serviceManager.isolateManager.onSelectedIsolateChanged.listen((_) => {
// setState(() => {
// _clearCachedRoutes();
// });
// });
//
// _isDarkThemeEnabled = preferences.darkModeTheme.value;
// preferences.darkModeTheme.addListener(() => {
// setState(() => {
// _isDarkThemeEnabled = preferences.darkModeTheme.value;
// });
// });
//
// _vmDeveloperModeEnabled = preferences.vmDeveloperModeEnabled.value;
// preferences.vmDeveloperModeEnabled.addListener(() => {
// setState(() => {
// _vmDeveloperModeEnabled = preferences.vmDeveloperModeEnabled.value;
// });
// });
}
public void didUpdateWidget(DevToolsApp oldWidget) {
base.didUpdateWidget(oldWidget);
//_clearCachedRoutes();
}
// Gets the page for a given page/path and args.
Page _getPage(BuildContext context2, string page, Dictionary<string, string> args) {
// Provide the appropriate page route.
if (pages.ContainsKey(page)) {
Widget widget = pages[page](
context2,
page,
args
);
D.assert(() => {
widget = new _AlternateCheckedModeBanner(
builder: (context) => pages[page](
context,
page,
args
)
);
return true;
});
return MaterialPage(child: widget);
}
// Return a page not found.
return MaterialPage(
child: DevToolsScaffold.withChild(
key: Key.key("not-found"),
child: CenteredMessage("'$page' not found."),
ideTheme: ideTheme,
analyticsProvider: widget.analyticsProvider
)
);
}
Widget _buildTabbedPage(
BuildContext context,
string page,
Dictionary<string, string> _params
) {
var vmServiceUri = _params["uri"];
// Always return the landing screen if there's no VM service URI.
if (vmServiceUri?.isEmpty() ?? true) {
return DevToolsScaffold.withChild(
key: Key.key("landing"),
child: LandingScreenBody(),
ideTheme: ideTheme,
analyticsProvider: widget.analyticsProvider,
actions: new List<Widget>{
new OpenSettingsAction(),
new OpenAboutAction()
}
);
}
// TODO(dantup): We should be able simplify this a little, removing params['page']
// and only supporting /inspector (etc.) instead of also &page=inspector if
// all IDEs switch over to those URLs.
if (page?.isEmpty() ?? true) {
page = _params["page"];
}
var embed = _params["embed"] == "true";
var hide = _params["hide"]?.Split(',');
return Initializer(
url: vmServiceUri,
allowConnectionScreenOnDisconnect: !embed,
builder: (_) => {
return new ValueListenableBuilder<bool>(
valueListenable: preferences.vmDeveloperModeEnabled,
builder: (_, __, ___) => {
var tabs = _visibleScreens()
.where((p) => embed && page != null ? p.screenId == page : true)
.where((p) => !hide.Contains(p.screenId))
.toList();
if (tabs.isEmpty) {
return DevToolsScaffold.withChild(
child: CenteredMessage(
$"The \"{page}\" screen is not available for this application."),
ideTheme: ideTheme,
analyticsProvider: widget.analyticsProvider
);
}
List<Widget> widgets = new List<Widget>();
if (serviceManager.connectedApp.isFlutterAppNow)
{
widgets.Add(HotReloadButton());
widgets.Add(HotRestartButton());
}
widgets.Add(new OpenSettingsAction());
widgets.Add(new OpenAboutAction());
return _providedControllers(
child: DevToolsScaffold(
embed: embed,
ideTheme: ideTheme,
page: page,
tabs: tabs,
analyticsProvider: widget.analyticsProvider,
actions: widgets
)
);
}
);
}
);
}
// The pages that the app exposes.
Dictionary<string, UrlParametersBuilder> pages {
get
{
if (_routes == null)
{
return null;
}
return _routes ?? {
homePageId: _buildTabbedPage,
foreach (Screen screen in widget.screens)
screen.screen.screenId: _buildTabbedPage,
snapshotPageId: (_, __, args) => {
final snapshotArgs = SnapshotArguments.fromArgs(args);
return DevToolsScaffold.withChild(
key: new UniqueKey(),
analyticsProvider: widget.analyticsProvider,
child: _providedControllers(
offline: true,
child: SnapshotScreenBody(snapshotArgs, _screens),
),
ideTheme: ideTheme,
);
},
appSizePageId: (_, __, ___) =>{
return DevToolsScaffold.withChild(
key: Key.key("appsize"),
analyticsProvider: widget.analyticsProvider,
child: _providedControllers(
child: AppSizeBody()
),
ideTheme: ideTheme,
actions: [
OpenSettingsAction(),
OpenAboutAction(),
],
);
},
};
}
}
Dictionary<string, UrlParametersBuilder> _routes;
void _clearCachedRoutes() {
_routes = null;
}
List<Screen> _visibleScreens() => _screens.where(shouldShowScreen).toList();
Widget _providedControllers({@required Widget child, bool offline = false}) {
var _providers = widget.screens
.where((s) =>
s.createController != null && (offline ? s.supportsOffline : true))
.map((s) => s.controllerProvider)
.toList();
return MultiProvider(
providers: _providers,
child: child,
);
}
public override Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),//themeFor(isDarkTheme: isDarkThemeEnabled, ideTheme: ideTheme),
builder: (context2, child) => null//Notifications(child: child),
// routerDelegate: DevToolsRouterDelegate(_getPage),
// routeInformationParser: DevToolsRouteInformationParser()
);
}
}
public class DevToolsScreen<C> {
public delegate C CreateController();
public DevToolsScreen(
Screen screen,
CreateController createController = null,
bool supportsOffline = false
)
{
this.screen = screen;
this.createController = createController;
this.supportsOffline = supportsOffline;
}
public readonly Screen screen;
public readonly CreateController createController;
public readonly bool supportsOffline;
Provider<C> controllerProvider {
get
{
D.assert(createController != null);
return Provider<C>(create: (_) => createController());
}
}
}
public delegate Widget UrlParametersBuilder(
BuildContext buildContext,
string s,
Dictionary<string, string> dictionary
);
public class _AlternateCheckedModeBanner : StatelessWidget {
public _AlternateCheckedModeBanner(Key key = null, WidgetBuilder builder = null) : base(key: key)
{
this.builder = builder;
}
public readonly WidgetBuilder builder;
public override Widget build(BuildContext context) {
return new Banner(
message: "DEBUG",
textDirection: TextDirection.ltr,
location: BannerLocation.topStart,
child: new Builder(
builder: builder
)
);
}
}
/*public class OpenAboutAction : StatelessWidget {
public override Widget build(BuildContext context) {
return DevToolsTooltip(
tooltip: "About DevTools",
child: new InkWell(
onTap: () => {
unawaited(showDialog(
context: context,
builder: (context2) => new DevToolsAboutDialog()
));
},
child: new Container(
width: DevToolsScaffold.actionWidgetSize,
height: DevToolsScaffold.actionWidgetSize,
alignment: Alignment.center,
child: new Icon(
Icons.help_outline,
size: actionsIconSize
)
)
)
);
}
}
public class OpenSettingsAction : StatelessWidget {
public override Widget build(BuildContext context) {
return DevToolsTooltip(
tooltip: "Settings",
child: new InkWell(
onTap: () => {
unawaited(showDialog(
context: context,
builder: (context2) => new SettingsDialog()
));
},
child: new Container(
width: DevToolsScaffold.actionWidgetSize,
height: DevToolsScaffold.actionWidgetSize,
alignment: Alignment.center,
child: new Icon(
Icons.settings,
size: actionsIconSize
)
)
)
);
}
}
public class DevToolsAboutDialog : StatelessWidget {
public override Widget build(BuildContext context) {
var theme = Theme.of(context);
List<Widget> widgets = new List<Widget>();
widgets.Add(new SizedBox(height: defaultSpacing));
List<Widget> temp = dialogSubHeader(theme, "Feedback");
foreach (var widget in temp)
{
widgets.Add(widget);
}
widgets.Add(new Wrap(
children: new List<Widget>{
new Text("Encountered an issue? Let us know at "),
_createFeedbackLink(context),
new Text(".")
}
));
return DevToolsDialog(
title: dialogTitleText(theme, "About DevTools"),
content: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: widgets
),
actions: [
DialogCloseButton(),
],
);
}
Widget _aboutDevTools(BuildContext context) {
return const SelectableText('DevTools version ${devtools.version}');
}
Widget _createFeedbackLink(BuildContext context) {
const urlPath = 'github.com/flutter/devtools/issues';
final colorScheme = Theme.of(context).colorScheme;
return InkWell(
onTap: () async {
ga.select(devToolsMain, feedback);
const reportIssuesUrl = 'https://$urlPath';
await launchUrl(reportIssuesUrl, context);
},
child: Text(urlPath, style: linkTextStyle(colorScheme)),
);
}
}
TODO(kenz): merge the checkbox functionality here with [NotifierCheckbox]
class SettingsDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DevToolsDialog(
title: dialogTitleText(Theme.of(context), 'Settings'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildOption(
label: const Text('Use a dark theme'),
listenable: preferences.darkModeTheme,
toggle: preferences.toggleDarkModeTheme,
),
if (isExternalBuild && isDevToolsServerAvailable)
_buildOption(
label: const Text('Enable analytics'),
listenable: ga.gaEnabledNotifier,
toggle: ga.setAnalyticsEnabled,
),
_buildOption(
label: const Text('Enable VM developer mode'),
listenable: preferences.vmDeveloperModeEnabled,
toggle: preferences.toggleVmDeveloperMode,
),
],
),
actions: [
DialogCloseButton(),
],
);
}
Widget _buildOption({
Text label,
ValueListenable<bool> listenable,
Function(bool) toggle,
}) {
return InkWell(
onTap: () => toggle(!listenable.value),
child: Row(
children: [
ValueListenableBuilder<bool>(
valueListenable: listenable,
builder: (context, value, _) {
return Checkbox(
value: value,
onChanged: toggle,
);
},
),
label,
],
),
);
}*/
}

3
com.unity.uiwidgets.devtools/Editor/config_specific.meta


fileFormatVersion: 2
guid: ec20b4818fa849a88ade34bed3bc361d
timeCreated: 1615531810

18
com.unity.uiwidgets.devtools/Editor/config_specific/ide_theme/ide_theme.cs


using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.DevTools.config_specific.ide_theme
{
public class IdeTheme {
public IdeTheme(Color backgroundColor = null, Color foregroundColor = null, float? fontSize = null)
{
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
this.fontSize = fontSize;
}
public readonly Color backgroundColor;
public readonly Color foregroundColor;
public readonly float? fontSize;
}
}

52
com.unity.uiwidgets.devtools/Editor/globals.cs


using System;
using System.Collections.Generic;
namespace Unity.UIWidgets.DevTools
{
public class globals
{
public static bool offlineMode = false;
// TODO(kenz): store this data in an inherited widget.
Dictionary<string, object> offlineDataJson = new Dictionary<string, object>();
public static readonly Dictionary<Type, object> _globals = new Dictionary<Type, object>();
public static ServiceConnectionManager serviceManager
{
get
{
return _globals[ServiceConnectionManager];
}
}
// MessageBus get messageBus => globals[MessageBus];
//
// FrameworkController get frameworkController => globals[FrameworkController];
//
// Storage get storage => globals[Storage];
//
// SurveyService get surveyService => globals[SurveyService];
//
public static PreferencesController preferences
{
get
{
return _globals[PreferencesController];
}
}
// string generateDevToolsTitle() {
// if (!serviceManager.hasConnection) return " ";
// return serviceManager.connectedApp.isFlutterAppNow
// ? "Flutter DevTools"
// : "Dart DevTools";
// }
//
// void setGlobal(Type clazz, dynamic instance) {
// globals[clazz] = instance;
// }
}
}

3
com.unity.uiwidgets.devtools/Editor/globals.cs.meta


fileFormatVersion: 2
guid: 2cd9c39bad944654afbb4b9928bf19b9
timeCreated: 1615544477

260
com.unity.uiwidgets.devtools/Editor/inspector/diagnostics.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.DevTools.inspector;
using Unity.UIWidgets.DevTools.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.DevTools.inspector
{
public class diagnosticsUtils
{
public static ColorIconMaker _colorIconMaker = new ColorIconMaker();
public static CustomIconMaker _customIconMaker = new CustomIconMaker();
public static CustomIcon defaultIcon = _customIconMaker.fromInfo("Default");
public static readonly bool _showRenderObjectPropertiesAsLinks = false;
}
public class DiagnosticsNodeDescription : StatelessWidget {
public DiagnosticsNodeDescription(
RemoteDiagnosticsNode diagnostic,
bool isSelected = false,
string errorText = null
)
{
this.diagnostic = diagnostic;
this.isSelected = isSelected;
this.errorText = errorText;
}
public readonly RemoteDiagnosticsNode diagnostic;
public readonly bool isSelected;
public readonly string errorText;
Widget _paddedIcon(Widget icon) {
return new Padding(
padding: EdgeInsets.only(right: iconPadding),
child: icon
);
}
IEnumerable<TextSpan> _buildDescriptionTextSpans(
String description,
TextStyle textStyle,
ColorScheme colorScheme
) {
if (diagnostic.isDiagnosticableValue) {
var match = treeNodePrimaryDescriptionPattern.firstMatch(description);
if (match != null) {
yield return new TextSpan(text: match.group(1), style: textStyle);
if (match.group(2).isNotEmpty()) {
yield return new TextSpan(
text: match.group(2),
style: inspector_text_styles.unimportant(colorScheme)
);
}
//return new List<TextSpan>();
}
} else if (diagnostic.type == "ErrorDescription") {
var match = assertionThrownBuildingError.firstMatch(description);
if (match != null) {
yield return new TextSpan(text: match.group(1), style: textStyle);
yield return new TextSpan(text: match.group(3), style: textStyle);
// return;
}
}
if (description?.isNotEmpty() == true) {
yield return new TextSpan(text: description, style: textStyle);
}
}
Widget buildDescription(
string description,
TextStyle textStyle,
ColorScheme colorScheme,
bool isProperty = false
) {
return new RichText(
overflow: TextOverflow.ellipsis,
text: new TextSpan(
children: _buildDescriptionTextSpans(
description,
textStyle,
colorScheme
).ToList()
)
);
}
public override Widget build(BuildContext context) {
if (diagnostic == null) {
return new SizedBox();
}
var colorScheme = Theme.of(context).colorScheme;
Widget icon = diagnostic.icon;
var children = new List<Widget>();
if (icon != null) {
children.Add(_paddedIcon(icon));
}
string name = diagnostic.name;
TextStyle textStyle = DefaultTextStyle.of(context)
.style
.merge(InspectorControllerUtils.textStyleForLevel(diagnostic.level, colorScheme));
if (diagnostic.isProperty) {
// Display of inline properties.
string propertyType = diagnostic.propertyType;
Dictionary<string, object> properties = diagnostic.valuePropertiesJson;
if (name?.isNotEmpty() == true && diagnostic.showName) {
children.Add(new Text($"{name}{diagnostic.separator} ", style: textStyle));
}
if (diagnostic.isCreatedByLocalProject) {
textStyle =
textStyle.merge(inspector_text_styles.regularBold(colorScheme));
}
string description = diagnostic.description;
if (propertyType != null && properties != null) {
switch (propertyType) {
case "Color":
{
int alpha = JsonUtils.getIntMember(properties, "alpha");
int red = JsonUtils.getIntMember(properties, "red");
int green = JsonUtils.getIntMember(properties, "green");
int blue = JsonUtils.getIntMember(properties, "blue");
string radix(int chan) => Convert.ToString(chan,16).PadLeft(2, '0');
if (alpha == 255) {
description = $"#{radix(red)}{radix(green)}{radix(blue)}";
} else {
description =
$"#{radix(alpha)}{radix(red)}{radix(green)}{radix(blue)}";
}
Color color = Color.fromARGB(alpha, red, green, blue);
children.Add(_paddedIcon(diagnosticsUtils._colorIconMaker.getCustomIcon(color)));
break;
}
case "IconData":
{
int codePoint =
JsonUtils.getIntMember(properties, "codePoint");
if (codePoint > 0) {
icon = FlutterMaterialIcons.getIconForCodePoint(
codePoint,
colorScheme
);
if (icon != null) {
children.Add(_paddedIcon(icon));
}
}
break;
}
}
}
if (diagnosticsUtils._showRenderObjectPropertiesAsLinks &&
propertyType == "RenderObject") {
textStyle = textStyle.merge(inspector_text_styles.link(colorScheme));
}
// TODO(jacobr): custom display for units, iterables, and padding.
children.Add(new Flexible(
child: buildDescription(
description,
textStyle,
colorScheme,
isProperty: true
)
));
if (diagnostic.level == DiagnosticLevel.fine &&
diagnostic.hasDefaultValue) {
children.Add(new Text(" "));
children.Add(_paddedIcon(diagnosticsUtils.defaultIcon));
}
} else {
// Non property, regular node case.
if (name?.isNotEmpty() == true && diagnostic.showName && name != "child") {
if (name.StartsWith("child ")) {
children.Add(new Text(
name,
style: inspector_text_styles.unimportant(colorScheme)
));
} else {
children.Add(new Text(name, style: textStyle));
}
if (diagnostic.showSeparator) {
children.Add(new Text(
diagnostic.separator,
style: inspector_text_styles.unimportant(colorScheme)
));
if (diagnostic.separator != " " &&
diagnostic.description.isNotEmpty()) {
children.Add(new Text(
" ",
style: inspector_text_styles.unimportant(colorScheme)
));
}
}
}
if (!diagnostic.isSummaryTree && diagnostic.isCreatedByLocalProject) {
textStyle =
textStyle.merge(inspector_text_styles.regularBold(colorScheme));
}
var diagnosticDescription = buildDescription(
diagnostic.description,
textStyle,
colorScheme,
isProperty: false
);
if (errorText != null) {
// TODO(dantup): Find if there's a way to achieve this without
// the nested row.
diagnosticDescription = new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: new List<Widget>{
diagnosticDescription,
_buildErrorText(colorScheme),
}
);
}
children.Add(new Expanded(child: diagnosticDescription));
}
return new Row(mainAxisSize: MainAxisSize.min, children: children);
}
Flexible _buildErrorText(ColorScheme colorScheme) {
return new Flexible(
child: new RichText(
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
text: new TextSpan(
text: errorText,
style: isSelected
? inspector_text_styles.regular
: inspector_text_styles.error(colorScheme)
)
)
);
}
}
}

1001
com.unity.uiwidgets.devtools/Editor/inspector/diagnostics_node.cs
文件差异内容过多而无法显示
查看文件

152
com.unity.uiwidgets.devtools/Editor/inspector/flutter_widget.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.DevTools.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools.inspector
{
public class Category {
public Category(string label, Image icon)
{
this.label = label;
this.icon = icon;
}
public static Category accessibility = new Category(
"Accessibility",
IconsUtils.createImageIcon("icons/inspector/balloonInformation.png")
);
public static Category animationAndMotion = new Category(
"Animation and Motion",
IconsUtils.createImageIcon("icons/inspector/resume.png")
);
public static Category assetsImagesAndIcons = new Category(
"Assets, Images, and Icons",
IconsUtils.createImageIcon("icons/inspector/any_type.png")
);
public static Category asyncCategory = new Category(
"Async",
IconsUtils.createImageIcon("icons/inspector/threads.png")
);
public static readonly Category basics = new Category(
"Basics",
null // TODO(jacobr): add an icon.
);
public static readonly Category cupertino = new Category(
"Cupertino (iOS-style widgets)",
null // TODO(jacobr): add an icon.
);
public static Category input = new Category(
"Input",
IconsUtils.createImageIcon("icons/inspector/renderer.png")
);
public static Category paintingAndEffects = new Category(
"Painting and effects",
IconsUtils.createImageIcon("icons/inspector/colors.png")
);
public static Category scrolling = new Category(
"Scrolling",
IconsUtils.createImageIcon("icons/inspector/scrollbar.png")
);
public static Category stack = new Category(
"Stack",
IconsUtils.createImageIcon("icons/inspector/value.png")
);
public static Category styling = new Category(
"Styling",
IconsUtils.createImageIcon("icons/inspector/atrule.png")
);
public static Category text = new Category(
"Text",
IconsUtils.createImageIcon("icons/inspector/textArea.png")
);
public static List<Category> values = new List<Category> {
accessibility,
animationAndMotion,
assetsImagesAndIcons,
asyncCategory,
basics,
cupertino,
input,
paintingAndEffects,
scrolling,
stack,
styling,
text,
};
public readonly string label;
public readonly Image icon;
static Dictionary<string, Category> _categories;
public static Category forLabel(string label) {
if (_categories == null) {
_categories = new Dictionary<string, Category>();
foreach (var category in values) {
_categories[category.label] = category;
}
}
return _categories[label];
}
}
public class FlutterWidget {
public FlutterWidget(Dictionary<string, object> json)
{
this.json = json;
icon = initIcon(json);
}
public readonly Dictionary<string, object> json;
public static Image icon;
//[!!!] may has error
static Image initIcon(Dictionary<string, object> json)
{
List<object> categories = new List<object>();
categories.Add(json.getOrDefault("categories"));
if (categories != null) {
// TODO(pq): consider priority over first match.
foreach (string label in categories) {
Category category = Category.forLabel(label);
if (category != null) {
icon = category.icon;
if (icon != null) return icon;
}
}
}
return null;
}
string name
{
get
{
return JsonUtils.getStringMember(json, "name");
}
}
List<string> categories
{
get
{
return JsonUtils.getValues(json, "categories");
}
}
List<string> subCategories
{
get
{
return JsonUtils.getValues(json, "subcategories");
}
}
}
}

974
com.unity.uiwidgets.devtools/Editor/inspector/inspector_controller.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.DevTools.inspector;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.DevTools.inspector
{
public class InspectorControllerUtils
{
public const string inspectorRefQueryParam = "inspectorRef";
public static TextStyle textStyleForLevel(DiagnosticLevel level, ColorScheme colorScheme) {
switch (level) {
case DiagnosticLevel.hidden:
return inspector_text_styles.unimportant(colorScheme);
case DiagnosticLevel.warning:
return inspector_text_styles.warning(colorScheme);
case DiagnosticLevel.error:
return inspector_text_styles.error(colorScheme);
case DiagnosticLevel.debug:
case DiagnosticLevel.info:
case DiagnosticLevel.fine:
default:
return inspector_text_styles.regular;
}
}
}
public class InspectorSettingsController {
public readonly ValueNotifier<bool> showOnlyUserDefined = new ValueNotifier<bool>(false);
public readonly ValueNotifier<bool> expandSelectedBuildMethod = new ValueNotifier<bool>(true);
}
/// This class is based on the InspectorPanel class from the Flutter IntelliJ
/// plugin with some refactors to make it more of a true controller than a view.
public class InspectorController : DisposableController, AutoDisposeControllerMixin, InspectorServiceClient {
public InspectorController(
InspectorService inspectorService,
InspectorTreeController inspectorTree,
InspectorTreeController detailsTree,
FlutterTreeType treeType,
InspectorController parent,
bool isSummaryTree = true,
VoidCallback onExpandCollapseSupported = null,
VoidCallback onLayoutExplorerSupported = null
)
{
_treeGroups = new InspectorObjectGroupManager(inspectorService, "tree");
_selectionGroups =
new InspectorObjectGroupManager(inspectorService, "selection");
_refreshRateLimiter = RateLimiter(refreshFramesPerSecond, refresh);
assert(inspectorTree != null);
inspectorTree.config = InspectorTreeConfig(
summaryTree: isSummaryTree,
treeType: treeType,
onNodeAdded: _onNodeAdded,
onHover: highlightShowNode,
onSelectionChange: selectionChanged,
onExpand: _onExpand,
onClientActiveChange: _onClientChange,
);
if (isSummaryTree) {
details = inspector.InspectorController(
inspectorService: inspectorService,
inspectorTree: detailsTree,
treeType: treeType,
parent: this,
isSummaryTree: false,
);
} else {
details = null;
}
flutterIsolateSubscription = serviceManager.isolateManager
.getSelectedIsolate((IsolateRef flutterIsolate) {
// TODO(jacobr): listen for real isolate stopped events.
// Only send an isolate stopped event if there was a previous isolate or
// the isolate has actually changed.
if (_activeIsolate != null && _activeIsolate != flutterIsolate) {
onIsolateStopped();
}
_activeIsolate = flutterIsolate;
});
_checkForExpandCollapseSupport();
_checkForLayoutExplorerSupport();
// This logic only needs to be run once so run it in the outermost
// controller.
if (parent == null) {
// If select mode is available, enable the on device inspector as it
// won't interfere with users.
addAutoDisposeListener(_supportsToggleSelectWidgetMode, () => {
if (_supportsToggleSelectWidgetMode.value) {
serviceManager.serviceExtensionManager.setServiceExtensionState(
extensions.enableOnDeviceInspector.extension,
true,
true
);
}
});
}
}
ValueListenable<bool> get _supportsToggleSelectWidgetMode =>
serviceManager.serviceExtensionManager
.hasServiceExtension(extensions.toggleSelectWidgetMode.extension);
void _onClientChange(bool added) {
_clientCount += added ? 1 : -1;
assert(_clientCount >= 0);
if (_clientCount == 1) {
setVisibleToUser(true);
setActivate(true);
} else if (_clientCount == 0) {
setVisibleToUser(false);
}
}
int _clientCount = 0;
/// Maximum frame rate to refresh the inspector at to avoid taxing the
/// physical device with too many requests to recompute properties and trees.
///
/// A value up to around 30 frames per second could be reasonable for
/// debugging highly interactive cases particularly when the user is on a
/// simulator or high powered native device. The frame rate is set low
/// for now mainly to minimize risk.
public static readonly float refreshFramesPerSecond = 5.0f;
public readonly bool isSummaryTree;
public readonly VoidCallback onExpandCollapseSupported;
public readonly VoidCallback onLayoutExplorerSupported;
/// Parent InspectorController if this is a details subtree.
InspectorController parent;
InspectorController details;
InspectorTreeController inspectorTree;
public readonly FlutterTreeType treeType;
public readonly InspectorService inspectorService;
StreamSubscription<IsolateRef> flutterIsolateSubscription;
IsolateRef _activeIsolate;
bool _disposed = false;
RateLimiter _refreshRateLimiter;
/// Groups used to manage and cancel requests to load data to display directly
/// in the tree.
InspectorObjectGroupManager _treeGroups;
/// Groups used to manage and cancel requests to determine what the current
/// selection is.
///
/// This group needs to be kept separate from treeGroups as the selection is
/// shared more with the details subtree.
/// TODO(jacobr): is there a way we can unify the selection and tree groups?
InspectorObjectGroupManager _selectionGroups;
/// Node being highlighted due to the current hover.
InspectorTreeNode currentShowNode
{
get
{
return inspectorTree.hover;
}
set
{
inspectorTree.hover = value;
}
}
bool flutterAppFrameReady = false;
bool treeLoadStarted = false;
RemoteDiagnosticsNode subtreeRoot;
bool programaticSelectionChangeInProgress = false;
public ValueListenable<InspectorTreeNode> selectedNode
{
get
{
return _selectedNode;
}
}
public readonly ValueNotifier<InspectorTreeNode> _selectedNode = new ValueNotifier((InspectorTreeNode)null);
InspectorTreeNode lastExpanded;
bool isActive = false;
public readonly Dictionary<InspectorInstanceRef, InspectorTreeNode> valueToInspectorTreeNode = new Dictionary<InspectorInstanceRef, InspectorTreeNode>();
/// When visibleToUser is false we should dispose all allocated objects and
/// not perform any actions.
bool visibleToUser = false;
bool highlightNodesShownInBothTrees = false;
bool detailsSubtree
{
get
{
return parent != null;
}
}
RemoteDiagnosticsNode selectedDiagnostic
{
get
{
return selectedNode.value?.diagnostic;
}
}
public readonly ValueNotifier<int> _selectedErrorIndex = new ValueNotifier<int>(null);
ValueListenable<int> selectedErrorIndex
{
get
{
return _selectedErrorIndex;
}
}
FlutterTreeType getTreeType() {
return treeType;
}
void setVisibleToUser(bool visible) {
if (visibleToUser == visible) {
return;
}
visibleToUser = visible;
details?.setVisibleToUser(visible);
if (visibleToUser) {
if (parent == null) {
maybeLoadUI();
}
} else {
shutdownTree(false);
}
}
bool hasDiagnosticsValue(InspectorInstanceRef _ref) {
return valueToInspectorTreeNode.ContainsKey(_ref);
}
RemoteDiagnosticsNode findDiagnosticsValue(InspectorInstanceRef _ref) {
return valueToInspectorTreeNode[_ref]?.diagnostic;
}
void endShowNode() {
highlightShowNode(null);
}
bool highlightShowFromNodeInstanceRef(InspectorInstanceRef _ref) {
return highlightShowNode(valueToInspectorTreeNode[_ref]);
}
bool highlightShowNode(InspectorTreeNode node) {
if (node == null && parent != null) {
// If nothing is highlighted, highlight the node selected in the parent
// tree so user has context of where the node selected in the parent is
// in the details tree.
node = findMatchingInspectorTreeNode(parent.selectedDiagnostic);
}
currentShowNode = node;
return true;
}
InspectorTreeNode findMatchingInspectorTreeNode(RemoteDiagnosticsNode node) {
if (node?.valueRef == null) {
return null;
}
return valueToInspectorTreeNode[node.valueRef];
}
Future<void> getPendingUpdateDone() async {
// Wait for the selection to be resolved followed by waiting for the tree to be computed.
await _selectionGroups?.pendingUpdateDone;
await _treeGroups?.pendingUpdateDone;
// TODO(jacobr): are there race conditions we need to think mroe carefully about here?
}
Future<void> refresh() {
if (!visibleToUser) {
// We will refresh again once we are visible.
// There is a risk a refresh got triggered before the view was visble.
return Future.value();
}
// TODO(jacobr): refresh the tree as well as just the properties.
if (details != null) {
return Future.wait(
[getPendingUpdateDone(), details.getPendingUpdateDone()]);
} else {
return getPendingUpdateDone();
}
}
// Note that this may be called after the controller is disposed. We need to handle nulls in the fields.
void shutdownTree(bool isolateStopped) {
// It is critical we clear all data that is kept alive by inspector object
// references in this method as that stale data will trigger inspector
// exceptions.
programaticSelectionChangeInProgress = true;
_treeGroups?.clear(isolateStopped);
_selectionGroups?.clear(isolateStopped);
currentShowNode = null;
_selectedNode.value = null;
lastExpanded = null;
subtreeRoot = null;
inspectorTree?.root = inspectorTree?.createNode();
details?.shutdownTree(isolateStopped);
programaticSelectionChangeInProgress = false;
valueToInspectorTreeNode?.clear();
}
void onIsolateStopped() {
flutterAppFrameReady = false;
treeLoadStarted = false;
shutdownTree(true);
}
@override
Future<void> onForceRefresh() async {
assert(!_disposed);
if (!visibleToUser || _disposed) {
return Future.value();
}
await recomputeTreeRoot(null, null, false);
filterErrors();
return getPendingUpdateDone();
}
void filterErrors() {
if (isSummaryTree) {
serviceManager.errorBadgeManager.filterErrors(InspectorScreen.id,
(id) => hasDiagnosticsValue(new InspectorInstanceRef(id)));
}
}
void setActivate(bool enabled) {
if (!enabled) {
onIsolateStopped();
isActive = false;
return;
}
if (isActive) {
// Already activated.
return;
}
isActive = true;
inspectorService.addClient(this);
maybeLoadUI();
}
List<String> get rootDirectories =>
_rootDirectories ?? parent.rootDirectories;
List<String> _rootDirectories;
Future<void> maybeLoadUI() async {
if (parent != null) {
// The parent controller will drive loading the UI.
return;
}
if (!visibleToUser || !isActive) {
return;
}
if (flutterAppFrameReady) {
_rootDirectories = await inspectorService.inferPubRootDirectoryIfNeeded();
// We need to start by querying the inspector service to find out the
// current state of the UI.
final queryParams = loadQueryParams();
final inspectorRef = queryParams.containsKey(inspectorRefQueryParam)
? queryParams[inspectorRefQueryParam]
: null;
await updateSelectionFromService(
firstFrame: true, inspectorRef: inspectorRef);
} else {
final ready = await inspectorService.isWidgetTreeReady();
flutterAppFrameReady = ready;
if (isActive && ready) {
await maybeLoadUI();
}
}
}
Future<void> recomputeTreeRoot(
RemoteDiagnosticsNode newSelection,
RemoteDiagnosticsNode detailsSelection,
bool setSubtreeRoot, {
int subtreeDepth = 2,
}) async {
assert(!_disposed);
if (_disposed) {
return;
}
_treeGroups.cancelNext();
try {
final group = _treeGroups.next;
final node = await (detailsSubtree
? group.getDetailsSubtree(subtreeRoot, subtreeDepth: subtreeDepth)
: group.getRoot(treeType));
if (node == null || group.disposed) {
return;
}
// TODO(jacobr): as a performance optimization we should check if the
// new tree is identical to the existing tree in which case we should
// dispose the new tree and keep the old tree.
_treeGroups.promoteNext();
clearValueToInspectorTreeNodeMapping();
if (node != null) {
final InspectorTreeNode rootNode = inspectorTree.setupInspectorTreeNode(
inspectorTree.createNode(),
node,
expandChildren: true,
expandProperties: false,
);
inspectorTree.root = rootNode;
} else {
inspectorTree.root = inspectorTree.createNode();
}
refreshSelection(newSelection, detailsSelection, setSubtreeRoot);
} catch (error) {
log(error.toString(), LogLevel.error);
_treeGroups.cancelNext();
return;
}
}
void clearValueToInspectorTreeNodeMapping() {
valueToInspectorTreeNode.clear();
}
/// Show the details subtree starting with node subtreeRoot highlighting
/// node subtreeSelection.
void showDetailSubtrees(
RemoteDiagnosticsNode subtreeRoot,
RemoteDiagnosticsNode subtreeSelection
) {
this.subtreeRoot = subtreeRoot;
details?.setSubtreeRoot(subtreeRoot, subtreeSelection);
}
InspectorInstanceRef getSubtreeRootValue() {
return subtreeRoot?.valueRef;
}
void setSubtreeRoot(
RemoteDiagnosticsNode node,
RemoteDiagnosticsNode selection,
) {
assert(detailsSubtree);
selection ??= node;
if (node != null && node == subtreeRoot) {
// Select the new node in the existing subtree.
applyNewSelection(selection, null, false);
return;
}
subtreeRoot = node;
if (node == null) {
// Passing in a null node indicates we should clear the subtree and free any memory allocated.
shutdownTree(false);
return;
}
// Clear now to eliminate frame of highlighted nodes flicker.
clearValueToInspectorTreeNodeMapping();
recomputeTreeRoot(selection, null, false);
}
InspectorTreeNode getSubtreeRootNode() {
if (subtreeRoot == null) {
return null;
}
return valueToInspectorTreeNode[subtreeRoot.valueRef];
}
void refreshSelection(RemoteDiagnosticsNode newSelection,
RemoteDiagnosticsNode detailsSelection, bool setSubtreeRoot) {
newSelection ??= selectedDiagnostic;
setSelectedNode(findMatchingInspectorTreeNode(newSelection));
syncSelectionHelper(
maybeRerootDetailsTree: setSubtreeRoot,
selection: newSelection,
detailsSelection: detailsSelection,
);
if (details != null) {
if (subtreeRoot != null && getSubtreeRootNode() == null) {
subtreeRoot = newSelection;
details.setSubtreeRoot(newSelection, detailsSelection);
}
}
syncTreeSelection();
}
void syncTreeSelection() {
programaticSelectionChangeInProgress = true;
inspectorTree.selection = selectedNode.value;
inspectorTree.expandPath(selectedNode.value);
programaticSelectionChangeInProgress = false;
animateTo(selectedNode.value);
}
void selectAndShowNode(RemoteDiagnosticsNode node) {
if (node == null) {
return;
}
selectAndShowInspectorInstanceRef(node.valueRef);
}
void selectAndShowInspectorInstanceRef(InspectorInstanceRef ref) {
final node = valueToInspectorTreeNode[ref];
if (node == null) {
return;
}
setSelectedNode(node);
syncTreeSelection();
}
InspectorTreeNode getTreeNode(RemoteDiagnosticsNode node) {
if (node == null) {
return null;
}
return valueToInspectorTreeNode[node.valueRef];
}
void maybeUpdateValueUI(InspectorInstanceRef valueRef) {
var node = valueToInspectorTreeNode[valueRef];
if (node == null) {
// The value isn't shown in the parent tree. Nothing to do.
return;
}
inspectorTree.nodeChanged(node);
}
public override void onFlutterFrame() {
flutterAppFrameReady = true;
if (!visibleToUser) {
return;
}
if (!treeLoadStarted) {
treeLoadStarted = true;
// This was the first frame.
maybeLoadUI();
}
_refreshRateLimiter.scheduleRequest();
}
bool identicalDiagnosticsNodes(
RemoteDiagnosticsNode a,
RemoteDiagnosticsNode b
) {
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
return a.dartDiagnosticRef == b.dartDiagnosticRef;
}
@override
void onInspectorSelectionChanged() {
if (!visibleToUser) {
// Don't do anything. We will update the view once it is visible again.
return;
}
if (detailsSubtree) {
// Wait for the master to update.
return;
}
updateSelectionFromService(firstFrame: false);
}
Future<void> updateSelectionFromService(
{@required bool firstFrame, String inspectorRef}) async {
if (parent != null) {
// If we have a parent controller we should wait for the parent to update
// our selection rather than updating it our self.
return;
}
if (_selectionGroups == null) {
// Already disposed. Ignore this requested to update selection.
return;
}
treeLoadStarted = true;
_selectionGroups.cancelNext();
final group = _selectionGroups.next;
if (inspectorRef != null) {
await group.setSelectionInspector(
InspectorInstanceRef(inspectorRef),
false,
);
}
final pendingSelectionFuture = group.getSelection(
selectedDiagnostic,
treeType,
isSummaryTree: isSummaryTree,
);
final Future<RemoteDiagnosticsNode> pendingDetailsFuture = isSummaryTree
? group.getSelection(selectedDiagnostic, treeType, isSummaryTree: false)
: null;
try {
final RemoteDiagnosticsNode newSelection = await pendingSelectionFuture;
if (group.disposed) return;
RemoteDiagnosticsNode detailsSelection;
if (pendingDetailsFuture != null) {
detailsSelection = await pendingDetailsFuture;
if (group.disposed) return;
}
if (!firstFrame &&
detailsSelection?.valueRef == details?.selectedDiagnostic?.valueRef &&
newSelection?.valueRef == selectedDiagnostic?.valueRef) {
// No need to change the selection as it didn't actually change.
_selectionGroups.cancelNext();
return;
}
_selectionGroups.promoteNext();
subtreeRoot = newSelection;
applyNewSelection(newSelection, detailsSelection, true);
} catch (error) {
if (_selectionGroups.next == group) {
log(error.toString(), LogLevel.error);
_selectionGroups.cancelNext();
}
}
}
void applyNewSelection(
RemoteDiagnosticsNode newSelection,
RemoteDiagnosticsNode detailsSelection,
bool setSubtreeRoot,
) {
final InspectorTreeNode nodeInTree =
findMatchingInspectorTreeNode(newSelection);
if (nodeInTree == null) {
// The tree has probably changed since we last updated. Do a full refresh
// so that the tree includes the new node we care about.
recomputeTreeRoot(newSelection, detailsSelection, setSubtreeRoot);
}
refreshSelection(newSelection, detailsSelection, setSubtreeRoot);
}
void animateTo(InspectorTreeNode node) {
if (node == null) {
return;
}
final List<InspectorTreeNode> targets = [node];
// Backtrack to the the first non-property parent so that all properties
// for the node are visible if one property is animated to. This is helpful
// as typically users want to view the properties of a node as a chunk.
while (node.parent != null && node.diagnostic?.isProperty == true) {
node = node.parent;
}
// Make sure we scroll so that immediate un-expanded children
// are also in view. There is no risk in including these children as
// the amount of space they take up is bounded. This also ensures that if
// a node is selected, its properties will also be selected as by
// convention properties are the first children of a node and properties
// typically do not have children and are never expanded by default.
for (InspectorTreeNode child in node.children) {
final RemoteDiagnosticsNode diagnosticsNode = child.diagnostic;
targets.add(child);
if (!child.isLeaf && child.isExpanded) {
// Stop if we get to expanded children as they might be too large
// to try to scroll into view.
break;
}
if (diagnosticsNode != null && !diagnosticsNode.isProperty) {
break;
}
}
inspectorTree.animateToTargets(targets);
}
void setSelectedNode(InspectorTreeNode newSelection) {
if (newSelection == selectedNode.value) {
return;
}
_selectedNode.value = newSelection;
lastExpanded = null; // New selected node takes precedence.
endShowNode();
if (details != null) {
details.endShowNode();
} else if (parent != null) {
parent.endShowNode();
}
_updateSelectedErrorFromNode(_selectedNode.value);
animateTo(selectedNode.value);
}
/// Update the index of the selected error based on a node that has been
/// selected in the tree.
void _updateSelectedErrorFromNode(InspectorTreeNode node) {
final inspectorRef = node?.diagnostic?.valueRef?.id;
final errors = serviceManager.errorBadgeManager
.erroredItemsForPage(InspectorScreen.id)
.value;
// Check whether the node that was just selected has any errors associated
// with it.
var errorIndex = inspectorRef != null
? errors.keys.toList().indexOf(inspectorRef)
: null;
if (errorIndex == -1) {
errorIndex = null;
}
_selectedErrorIndex.value = errorIndex;
if (errorIndex != null) {
// Mark the error as "seen" as this will render slightly differently
// so the user can track which errored nodes they've viewed.
serviceManager.errorBadgeManager
.markErrorAsRead(InspectorScreen.id, errors[inspectorRef]);
// Also clear the error badge since new errors may have arrived while
// the inspector was visible (normally they're cleared when visiting
// the screen) and visiting an errored node seems an appropriate
// acknowledgement of the errors.
serviceManager.errorBadgeManager.clearErrors(InspectorScreen.id);
}
}
/// Updates the index of the selected error and selects its node in the tree.
void selectErrorByIndex(int index) {
_selectedErrorIndex.value = index;
if (index == null) return;
final errors = serviceManager.errorBadgeManager
.erroredItemsForPage(InspectorScreen.id)
.value;
updateSelectionFromService(
firstFrame: false, inspectorRef: errors.keys.elementAt(index));
}
void _onExpand(InspectorTreeNode node) {
inspectorTree.maybePopulateChildren(node);
}
void selectionChanged() {
if (visibleToUser == false) {
return;
}
InspectorTreeNode node = inspectorTree.selection;
if (node != null) {
inspectorTree.maybePopulateChildren(node);
}
if (programaticSelectionChangeInProgress) {
return;
}
if (node != null) {
setSelectedNode(node);
bool maybeReroot = isSummaryTree &&
details != null &&
selectedDiagnostic != null &&
!details.hasDiagnosticsValue(selectedDiagnostic.valueRef);
syncSelectionHelper(
maybeRerootDetailsTree: maybeReroot,
selection: selectedDiagnostic,
detailsSelection: selectedDiagnostic,
);
if (!maybeReroot) {
if (isSummaryTree && details != null) {
details.selectAndShowNode(selectedDiagnostic);
} else if (parent != null) {
parent
.selectAndShowNode(firstAncestorInParentTree(selectedNode.value));
}
}
}
}
RemoteDiagnosticsNode firstAncestorInParentTree(InspectorTreeNode node) {
if (parent == null) {
return node.diagnostic;
}
while (node != null) {
var diagnostic = node.diagnostic;
if (diagnostic != null &&
parent.hasDiagnosticsValue(diagnostic.valueRef)) {
return parent.findDiagnosticsValue(diagnostic.valueRef);
}
node = node.parent;
}
return null;
}
void syncSelectionHelper(
bool maybeRerootDetailsTree = true,
RemoteDiagnosticsNode selection = null,
RemoteDiagnosticsNode detailsSelection = null
) {
if (selection != null) {
if (selection.isCreatedByLocalProject) {
_navigateTo(selection);
}
}
if (detailsSubtree || details == null) {
if (selection != null) {
var toSelect = selectedNode.value;
while (toSelect != null && toSelect.diagnostic.isProperty) {
toSelect = toSelect.parent;
}
if (toSelect != null) {
var diagnosticToSelect = toSelect.diagnostic;
diagnosticToSelect.setSelectionInspector(true);
}
}
}
if (maybeRerootDetailsTree) {
showDetailSubtrees(selection, detailsSelection);
} else if (selection != null) {
// We can't rely on the details tree to update the selection on the server in this case.
selection.setSelectionInspector(true);
}
}
void _navigateTo(RemoteDiagnosticsNode diagnostic) {
// TODO(jacobr): dispatch an event over the inspectorService requesting a
// navigate operation.
}
public override void dispose() {
D.assert(!_disposed);
_disposed = true;
flutterIsolateSubscription.cancel();
if (inspectorService != null) {
shutdownTree(false);
}
_treeGroups?.clear(false);
_treeGroups = null;
_selectionGroups?.clear(false);
_selectionGroups = null;
details?.dispose();
base.dispose();
}
static string treeTypeDisplayName(FlutterTreeType treeType) {
switch (treeType) {
case FlutterTreeType.widget:
return "Widget";
case FlutterTreeType.renderObject:
return "Render Objects";
default:
return null;
}
}
void _onNodeAdded(
InspectorTreeNode node,
RemoteDiagnosticsNode diagnosticsNode
) {
InspectorInstanceRef valueRef = diagnosticsNode.valueRef;
// Properties do not have unique values so should not go in the valueToInspectorTreeNode map.
if (valueRef.id != null && !diagnosticsNode.isProperty) {
valueToInspectorTreeNode[valueRef] = node;
}
}
Future expandAllNodesInDetailsTree() {
details.recomputeTreeRoot(
inspectorTree.selection?.diagnostic,
details.inspectorTree.selection?.diagnostic ??
details.inspectorTree.root?.diagnostic,
false,
subtreeDepth: maxJsInt
);
}
Future collapseDetailsToSelected() {
details.inspectorTree.collapseToSelected();
details.animateTo(details.inspectorTree.selection);
return null;
}
/// execute given [callback] when minimum Flutter [version] is met.
void _onVersionSupported(
SemanticVersion version,
VoidCallback callback
) {
final flutterVersionServiceListenable = serviceManager
.registeredServiceListenable(registrations.flutterVersion.service);
addAutoDisposeListener(flutterVersionServiceListenable, () async {
final registered = flutterVersionServiceListenable.value;
if (registered) {
final flutterVersion =
FlutterVersion.parse((await serviceManager.flutterVersion).json);
if (flutterVersion.isSupported(supportedVersion: version)) {
callback();
}
}
});
}
void _checkForExpandCollapseSupport() {
if (onExpandCollapseSupported == null) return;
// Configurable subtree depth is available in versions of Flutter
// greater than or equal to 1.9.7, but the flutterVersion service is
// not available until 1.10.1, so we will check for 1.10.1 here.
_onVersionSupported(
SemanticVersion(major: 1, minor: 10, patch: 1),
onExpandCollapseSupported
);
}
void _checkForLayoutExplorerSupport() {
if (onLayoutExplorerSupported == null) return;
_onVersionSupported(
SemanticVersion(major: 1, minor: 13, patch: 1),
onLayoutExplorerSupported
);
}
}
}

1001
com.unity.uiwidgets.devtools/Editor/inspector/inspector_data_models.cs
文件差异内容过多而无法显示
查看文件

408
com.unity.uiwidgets.devtools/Editor/inspector/inspector_screen.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.DevTools.inspector;
using Unity.UIWidgets.DevTools.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.widgets;
using UnityEngine;
namespace Unity.UIWidgets.DevTools.inspector
{
public class InspectorScreen : Screen {
public InspectorScreen():base(
id: id,
requiresLibrary: null,//flutterLibraryUri,
requiresDebugBuild: true,
title: "Flutter Inspector",
icon: Octicons.deviceMobile
)
{
}
public static readonly string id = "inspector";
public new string docPageId
{
get
{
return screenId;
}
}
public override Widget build(BuildContext context)
{
return new InspectorScreenBody();
}
}
public class InspectorScreenBody : StatefulWidget {
public InspectorScreenBody(){}
public override State createState()
{
return new InspectorScreenBodyState();
}
}
public class InspectorScreenBodyState : State<InspectorScreenBody>, BlockingActionMixin, AutoDisposeMixin
{
bool _expandCollapseSupported = false;
bool _layoutExplorerSupported = false;
bool connectionInProgress = false;
InspectorService inspectorService;
InspectorController inspectorController;
InspectorTreeControllerFlutter summaryTreeController;
InspectorTreeControllerFlutter detailsTreeController;
bool displayedWidgetTrackingNotice = false;
// bool enableButtons
// {
// get
// {
// return actionInProgress == false && connectionInProgress == false;
// }
// }
public static readonly Key summaryTreeKey = Key.key("Summary Tree");
public static readonly Key detailsTreeKey = Key.key("Details Tree");
public override void initState() {
base.initState();
// ga.screen(InspectorScreen.id);
// autoDispose(
// serviceManager.onConnectionAvailable.listen(_handleConnectionStart));
// if (serviceManager.hasConnection) {
// _handleConnectionStart(serviceManager.service);
// }
// autoDispose(
// serviceManager.onConnectionClosed.listen(_handleConnectionStop));
}
public override void dispose() {
inspectorService?.dispose();
inspectorController?.dispose();
base.dispose();
}
// void _onExpandClick() {
// blockWhileInProgress(inspectorController.expandAllNodesInDetailsTree);
// }
// void _onResetClick() {
// blockWhileInProgress(inspectorController.collapseDetailsToSelected);
// }
public override Widget build(BuildContext context) {
var summaryTree = _buildSummaryTreeColumn();
var detailsTree = InspectorTree(
key: detailsTreeKey,
controller: detailsTreeController
);
var splitAxis = Split.axisFor(context, 0.85);
return new Column(
children: new List<Widget>{
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget>{
new ValueListenableBuilder(
valueListenable: serviceManager.serviceExtensionManager
.hasServiceExtension(
extensions.toggleSelectWidgetMode.extension),
builder: (_, selectModeSupported, __) => {
return ServiceExtensionButtonGroup(
extensions:
selectModeSupported
? extensions.toggleSelectWidgetMode
: extensions.toggleOnDeviceWidgetInspector,
minIncludeTextWidth: 650
);
}
),
new SizedBox(width: denseSpacing),
IconLabelButton(
onPressed: _refreshInspector,
icon: Icons.refresh,
label: "Refresh Tree",
includeTextWidth: 750
),
new Spacer(),
new Row(children: getServiceExtensionWidgets()),
}
),
new SizedBox(height: denseRowSpacing),
new Expanded(
child: Split(
axis: splitAxis,
initialFractions: const [0.33, 0.67],
children: [
summaryTree,
InspectorDetailsTabController(
detailsTree: detailsTree,
controller: inspectorController,
actionButtons: _expandCollapseButtons(),
layoutExplorerSupported: _layoutExplorerSupported,
),
],
),
),
},
);
}
Widget _buildSummaryTreeColumn() => OutlineDecoration(
child: ValueListenableBuilder(
valueListenable: serviceManager.errorBadgeManager
.erroredItemsForPage(InspectorScreen.id),
builder: (_, LinkedHashMap<String, DevToolsError> errors, __) {
final inspectableErrors = errors.map(
(key, value) => MapEntry(key, value as InspectableWidgetError));
return Stack(
children: [
InspectorTree(
key: summaryTreeKey,
controller: summaryTreeController,
isSummaryTree: true,
widgetErrors: inspectableErrors,
),
if (errors.isNotEmpty && inspectorController != null)
ValueListenableBuilder(
valueListenable: inspectorController.selectedErrorIndex,
builder: (_, selectedErrorIndex, __) => Positioned(
top: 0,
right: 0,
child: ErrorNavigator(
errors: inspectableErrors,
errorIndex: selectedErrorIndex,
onSelectError: inspectorController.selectErrorByIndex,
),
),
)
],
);
},
),
);
List<Widget> getServiceExtensionWidgets() {
return [
ServiceExtensionButtonGroup(
minIncludeTextWidth: 1050,
extensions: [extensions.slowAnimations],
),
const SizedBox(width: denseSpacing),
ServiceExtensionButtonGroup(
minIncludeTextWidth: 1050,
extensions: [extensions.debugPaint, extensions.debugPaintBaselines],
),
const SizedBox(width: denseSpacing),
ServiceExtensionButtonGroup(
minIncludeTextWidth: 1250,
extensions: [
extensions.repaintRainbow,
extensions.invertOversizedImages,
],
),
// TODO(jacobr): implement TogglePlatformSelector.
// TogglePlatformSelector().selector
];
}
Widget _expandCollapseButtons() {
if (!_expandCollapseSupported) return null;
return Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: FixedHeightOutlinedButton(
onPressed: enableButtons ? _onExpandClick : null,
child: const Text(
'Expand all',
overflow: TextOverflow.ellipsis,
),
),
),
const SizedBox(width: denseSpacing),
Flexible(
child: FixedHeightOutlinedButton(
onPressed: enableButtons ? _onResetClick : null,
child: const Text(
'Collapse to selected',
overflow: TextOverflow.ellipsis,
),
),
)
],
),
);
}
void _onExpandCollapseSupported() {
setState(() {
_expandCollapseSupported = true;
});
}
void _onLayoutExplorerSupported() {
setState(() {
_layoutExplorerSupported = true;
});
}
void _handleConnectionStart(VmService service) async {
setState(() {
connectionInProgress = true;
});
try {
// Init the inspector service, or return null.
await ensureInspectorServiceDependencies();
inspectorService =
await InspectorService.create(service).catchError((e) => null);
} finally {
setState(() {
connectionInProgress = false;
});
}
if (inspectorService == null) {
return;
}
setState(() {
inspectorController?.dispose();
summaryTreeController = InspectorTreeControllerFlutter();
detailsTreeController = InspectorTreeControllerFlutter();
inspectorController = InspectorController(
inspectorTree: summaryTreeController,
detailsTree: detailsTreeController,
inspectorService: inspectorService,
treeType: FlutterTreeType.widget,
onExpandCollapseSupported: _onExpandCollapseSupported,
onLayoutExplorerSupported: _onLayoutExplorerSupported,
);
// Clear any existing badge/errors for older errors that were collected.
serviceManager.errorBadgeManager.clearErrors(InspectorScreen.id);
inspectorController.filterErrors();
// TODO(jacobr): move this notice display to once a day.
if (!displayedWidgetTrackingNotice) {
// ignore: unawaited_futures
inspectorService.isWidgetCreationTracked().then((bool value) {
if (value) {
return;
}
displayedWidgetTrackingNotice = true;
// TODO(jacobr): implement showMessage.
// framework.showMessage(
// message: trackWidgetCreationWarning,
// screenId: inspectorScreenId,
//);
});
}
});
}
void _handleConnectionStop(dynamic event) {
inspectorController?.setActivate(false);
inspectorController?.dispose();
setState(() {
inspectorController = null;
});
}
void _refreshInspector() {
ga.select(inspector, refresh);
blockWhileInProgress(() async {
await inspectorController?.onForceRefresh();
});
}
}
class ErrorNavigator extends StatelessWidget {
const ErrorNavigator({
Key key,
@required this.errors,
@required this.errorIndex,
@required this.onSelectError,
}) : super(key: key);
final LinkedHashMap<String, InspectableWidgetError> errors;
final int errorIndex;
final Function(int) onSelectError;
@override
Widget build(BuildContext context) {
final label = errorIndex != null
? 'Error ${errorIndex + 1}/${errors.length}'
: 'Errors: ${errors.length}';
return Container(
color: devtoolsError,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: defaultSpacing,
vertical: denseSpacing,
),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: denseSpacing),
child: Text(label),
),
IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
splashRadius: defaultIconSize,
icon: const Icon(Icons.keyboard_arrow_up),
onPressed: _previousError,
),
IconButton(
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
splashRadius: defaultIconSize,
icon: const Icon(Icons.keyboard_arrow_down),
onPressed: _nextError,
),
],
),
),
);
}
void _previousError() {
var newIndex = errorIndex == null ? errors.length - 1 : errorIndex - 1;
while (newIndex < 0) {
newIndex += errors.length;
}
onSelectError(newIndex);
}
void _nextError() {
final newIndex = errorIndex == null ? 0 : (errorIndex + 1) % errors.length;
onSelectError(newIndex);
}
}
}

144
com.unity.uiwidgets.devtools/Editor/inspector/inspector_screen_details_tab.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.widgets;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.DevTools.inspector.layout_explorer;
namespace Unity.UIWidgets.DevTools.inspector
{
public class inspector_screen_details_tab
{
}
public class InspectorDetailsTabController : StatefulWidget {
public InspectorDetailsTabController(
Widget detailsTree = null,
Widget actionButtons = null,
InspectorController controller = null,
bool layoutExplorerSupported = false,
Key key = null
) : base(key: key)
{
this.detailsTree = detailsTree;
this.actionButtons = actionButtons;
this.controller = controller;
this.layoutExplorerSupported = layoutExplorerSupported;
}
public readonly Widget detailsTree;
public readonly Widget actionButtons;
public readonly InspectorController controller;
public readonly bool layoutExplorerSupported;
public override State createState()
{
return new _InspectorDetailsTabControllerState();
}
}
public class _InspectorDetailsTabControllerState : State<InspectorDetailsTabController>, TickerProviderStateMixin, AutoDisposeMixin
{
public static readonly int _detailsTreeTabIndex = 1;
public static readonly int _tabsLengthWithLayoutExplorer = 2;
public static readonly int _tabsLengthWithoutLayoutExplorer = 1;
TabController _tabControllerWithLayoutExplorer;
TabController _tabControllerWithoutLayoutExplorer;
public override void initState() {
base.initState();
addAutoDisposeListener(
_tabControllerWithLayoutExplorer =
new TabController(length: _tabsLengthWithLayoutExplorer, vsync: this)
);
addAutoDisposeListener(
_tabControllerWithoutLayoutExplorer =
new TabController(length: _tabsLengthWithoutLayoutExplorer, vsync: this)
);
}
public override Widget build(BuildContext context)
{
List<Widget> tabs = new List<Widget>();
if (widget.layoutExplorerSupported)
tabs.Add(_buildTab("Layout Explorer"));
tabs.Add(_buildTab("Details Tree"));
List<Widget> tabViews = new List<Widget>();
if (widget.layoutExplorerSupported)
tabViews.Add(new LayoutExplorerTab(controller: widget.controller));
tabViews.Add(widget.detailsTree);
var _tabController = widget.layoutExplorerSupported
? _tabControllerWithLayoutExplorer
: _tabControllerWithoutLayoutExplorer;
var theme = Theme.of(context);
var focusColor = theme.focusColor;
var borderSide = new BorderSide(color: focusColor);
var hasActionButtons = widget.actionButtons != null &&
_tabController.index == _detailsTreeTabIndex;
return new Column(
children: new List<Widget>{
new SizedBox(
height: 50.0f,
child: new Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: new List<Widget>{
new Container(
color: focusColor,
child: new TabBar(
controller: _tabController,
labelColor: theme.textTheme.bodyText1.color,
tabs: tabs,
isScrollable: true
)
),
new Expanded(
child: new Container(
decoration: new BoxDecoration(border: new Border(bottom: borderSide)),
child: hasActionButtons
? widget.actionButtons
: new SizedBox()
)
)
}
)
),
new Expanded(
child: new Container(
decoration: new BoxDecoration(
border: new Border(
left: borderSide,
bottom: borderSide,
right: borderSide
)
),
child: new TabBarView(
physics: CommonThemeUtils.defaultTabBarViewPhysics,
controller: _tabController,
children: tabViews
)
)
),
}
);
}
Widget _buildTab(string tabName) {
return new Tab(
child: new Text(
tabName,
overflow: TextOverflow.ellipsis
)
);
}
}
}

1001
com.unity.uiwidgets.devtools/Editor/inspector/inspector_service.cs
文件差异内容过多而无法显示
查看文件

43
com.unity.uiwidgets.devtools/Editor/inspector/inspector_text_style.cs


using uiwidgets;
using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using ThemeUtils = Unity.UIWidgets.DevTools.inspector.layout_explorer.ui.ThemeUtils;
namespace Unity.UIWidgets.DevTools.inspector
{
public class inspector_text_styles
{
public static TextStyle unimportant(ColorScheme colorScheme)
{
return new TextStyle(
color: CommonThemeUtils.isLight ? Colors.grey.shade500 : Colors.grey.shade600);
}
public static TextStyle regular = new TextStyle();
public static TextStyle warning(ColorScheme colorScheme) => new TextStyle(
color:
CommonThemeUtils.isLight ? Colors.orange.shade900 : Colors.orange.shade400);
public static TextStyle error(ColorScheme colorScheme) => new TextStyle(
color: CommonThemeUtils.isLight ? Colors.red.shade500 : Colors.red.shade400
);
public static TextStyle link(ColorScheme colorScheme) => new TextStyle(
color: CommonThemeUtils.isLight ? Colors.blue.shade700 : Colors.blue.shade300,
decoration: TextDecoration.underline
);
public static TextStyle regularBold(ColorScheme colorScheme) => new TextStyle(
color: CommonThemeUtils.defaultForeground,
fontWeight: FontWeight.w700
);
public static TextStyle regularItalic(ColorScheme colorScheme) => new TextStyle(
color: CommonThemeUtils.defaultForeground,
fontStyle: FontStyle.italic
);
public static TextStyle unimportantItalic(ColorScheme colorScheme) =>
unimportant(colorScheme).merge(new TextStyle(
fontStyle: FontStyle.italic
));
}
}

873
com.unity.uiwidgets.devtools/Editor/inspector/inspector_tree.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using uiwidgets;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.DevTools.inspector;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.DevTools.inspector
{
public class InspectorTreeUtils
{
public static readonly Regex treeNodePrimaryDescriptionPattern = new Regex(@"^([\w ]+)(.*)$");
// TODO(jacobr): temporary workaround for missing structure from assertion thrown building
// widget errors.
public static readonly Regex assertionThrownBuildingError = new Regex(
@"^(The following assertion was thrown building [a-zA-Z]+)(\(.*\))(:)$");
public delegate void TreeEventCallback(InspectorTreeNode node);
public static readonly float iconPadding = 5.0f;
public static readonly float chartLineStrokeWidth = 1.0f;
public static readonly float columnWidth = 16.0f;
public static readonly float verticalPadding = 10.0f;
public static readonly float rowHeight = 24.0f;
// TODO(jacobr): merge this scheme with other color schemes in DevTools.
public static Color selectedRowBackgroundColor
{
get
{
return CommonThemeUtils.isLight
? Color.fromARGB(255, 202, 191, 69)
: Color.fromARGB(255, 99, 101, 103);
}
}
public static Color hoverColor
{
get
{
return CommonThemeUtils.isLight ? Colors.yellowAccent : Color.fromARGB(255, 70, 73, 76);
}
}
}
// TODO(kenz): extend TreeNode class to share tree logic.
public class InspectorTreeNode {
public InspectorTreeNode(
InspectorTreeNode parent = null,
bool expandChildren = true
)
{
_children = new List<InspectorTreeNode>();
_parent = parent;
_isExpanded = expandChildren;
}
bool showLinesToChildren {
get
{
return _children.Count > 1 && !_children.Last().isProperty;
}
}
public bool isDirty
{
get
{
return _isDirty;
}
set
{
if (value) {
_isDirty = true;
_shouldShow = null;
if (_childrenCount == null) {
// Already dirty.
return;
}
_childrenCount = null;
if (parent != null) {
parent.isDirty = true;
}
} else {
_isDirty = false;
}
}
}
bool _isDirty = true;
/// Returns whether the node is currently visible in the tree.
void updateShouldShow(bool value) {
if (value != _shouldShow) {
_shouldShow = value;
foreach (var child in children) {
child.updateShouldShow(value);
}
}
}
bool? shouldShow {
get
{
_shouldShow = _shouldShow ?? parent == null || parent.isExpanded && parent.shouldShow.Value;
return _shouldShow;
}
}
bool? _shouldShow;
public bool selected = false;
RemoteDiagnosticsNode _diagnostic;
public readonly List<InspectorTreeNode> _children;
public IEnumerable<InspectorTreeNode> children
{
get
{
return _children;
}
}
bool isCreatedByLocalProject
{
get
{
return _diagnostic.isCreatedByLocalProject;
}
}
bool isProperty
{
get
{
return diagnostic == null || diagnostic.isProperty;
}
}
public bool isExpanded
{
get
{
return _isExpanded;
}
set
{
if (value != _isExpanded) {
_isExpanded = value;
isDirty = true;
if (_shouldShow ?? false) {
foreach (var child in children) {
child.updateShouldShow(value);
}
}
}
}
}
bool _isExpanded;
bool allowExpandCollapse = true;
bool showExpandCollapse {
get
{
return (diagnostic?.hasChildren == true || children.Any()) &&
allowExpandCollapse;
}
}
public InspectorTreeNode parent
{
get
{
return _parent;
}
set
{
_parent = value;
_parent.isDirty = true;
}
}
InspectorTreeNode _parent;
public RemoteDiagnosticsNode diagnostic
{
get
{
return _diagnostic;
}
set
{
_diagnostic = value;
_isExpanded = value.childrenReady;
//isDirty = true;
}
}
public int? childrenCount {
get
{
if (!isExpanded) {
_childrenCount = 0;
}
if (_childrenCount != null) {
return _childrenCount;
}
int count = 0;
foreach (InspectorTreeNode child in _children) {
count += (child.subtreeSize?? 0);
}
_childrenCount = count;
return _childrenCount;
}
}
public bool hasPlaceholderChildren {
get
{
return children.Count() == 1 && children.First().diagnostic == null;
}
}
int? _childrenCount;
public int? subtreeSize
{
get
{
return childrenCount + 1;
}
}
bool isLeaf
{
get
{
return _children.isEmpty();
}
}
// TODO(jacobr): move getRowIndex to the InspectorTree class.
public int getRowIndex(InspectorTreeNode node) {
int index = 0;
while (true) {
InspectorTreeNode parent = node.parent;
if (parent == null) {
break;
}
foreach (InspectorTreeNode sibling in parent._children) {
if (sibling == node) {
break;
}
index += (sibling.subtreeSize?? 0);
}
index += 1;
node = parent;
}
return index;
}
// TODO(jacobr): move this method to the InspectorTree class.
// TODO: optimize this method.
/// Use [getCachedRow] wherever possible, as [getRow] is slow and can cause
/// performance problems.
public InspectorTreeRow getRow(int index) {
List<int> ticks = new List<int>();
InspectorTreeNode node = this;
if (subtreeSize <= index) {
return null;
}
int current = 0;
int depth = 0;
while (node != null) {
var style = node.diagnostic?.style;
bool indented = style != DiagnosticsTreeStyle.flat &&
style != DiagnosticsTreeStyle.error;
if (current == index) {
return new InspectorTreeRow(
node: node,
index: index,
ticks: ticks,
depth: depth,
lineToParent:
!node.isProperty && index != 0 && node.parent.showLinesToChildren
);
}
D.assert(index > current);
current++;
List<InspectorTreeNode> children = node._children;
int i;
for (i = 0; i < children.Count; ++i) {
var child = children[i];
var subtreeSize = child.subtreeSize;
if (current + subtreeSize > index) {
node = child;
if (children.Count > 1 &&
i + 1 != children.Count &&
!children.Last().isProperty) {
if (indented) {
ticks.Add(depth);
}
}
break;
}
current += (subtreeSize?? 0);
}
D.assert(i < children.Count);
if (indented) {
depth++;
}
}
D.assert(false); // internal error.
return null;
}
public void removeChild(InspectorTreeNode child) {
child.parent = null;
var removed = _children.Remove(child);
D.assert(removed != null);
isDirty = true;
}
public void appendChild(InspectorTreeNode child) {
_children.Add(child);
child.parent = this;
isDirty = true;
}
public void clearChildren() {
_children.Clear();
isDirty = true;
}
}
/// A row in the tree with all information required to render it.
public class InspectorTreeRow {
public InspectorTreeRow(
InspectorTreeNode node,
int? index = null,
List<int> ticks = null,
int? depth = null,
bool? lineToParent = null
)
{
this.node = node;
this.index = index;
this.ticks = ticks;
this.depth = depth;
this.lineToParent = lineToParent;
}
public readonly InspectorTreeNode node;
/// Column indexes of ticks to draw lines from parents to children.
public readonly List<int> ticks;
public readonly int? depth;
public readonly int? index;
public readonly bool? lineToParent;
bool isSelected
{
get
{
return node.selected;
}
}
}
public delegate void NodeAddedCallback(InspectorTreeNode node, RemoteDiagnosticsNode diagnosticsNode);
public class InspectorTreeConfig {
public delegate void OnClientActiveChange(bool added);
public InspectorTreeConfig(
bool? summaryTree = null,
FlutterTreeType? treeType = null,
NodeAddedCallback onNodeAdded = null,
OnClientActiveChange onClientActiveChange = null,
VoidCallback onSelectionChange = null,
InspectorTreeUtils.TreeEventCallback onExpand = null,
InspectorTreeUtils.TreeEventCallback onHover = null
)
{
this.summaryTree = summaryTree;
this.treeType = treeType;
this.onNodeAdded = onNodeAdded;
this.onSelectionChange = onSelectionChange;
this.onExpand = onExpand;
this.onHover = onHover;
}
public readonly bool? summaryTree;
public readonly FlutterTreeType? treeType;
public readonly NodeAddedCallback onNodeAdded;
public readonly VoidCallback onSelectionChange;
public readonly OnClientActiveChange ONClientActiveChange;
public readonly InspectorTreeUtils.TreeEventCallback onExpand;
public readonly InspectorTreeUtils.TreeEventCallback onHover;
}
public abstract class InspectorTreeController
{
protected abstract void setState(VoidCallback fn);
public InspectorTreeNode root
{
get
{
return _root;
}
set
{
setState(() => {
_root = value;
});
}
}
InspectorTreeNode _root;
RemoteDiagnosticsNode subtreeRoot; // Optional.
public InspectorTreeNode selection
{
get
{
return _selection;
}
set
{
if (value == _selection) return;
setState(() => {
_selection.selected = false;
_selection = value;
_selection.selected = true;
if (config.onSelectionChange != null) {
config.onSelectionChange();
}
});
}
}
InspectorTreeNode _selection;
InspectorTreeConfig config
{
get
{
return _config;
}
set
{
// Only allow setting config once.
D.assert(_config == null);
_config = value;
}
}
InspectorTreeConfig _config;
InspectorTreeNode hover
{
get
{
return _hover;
}
set
{
if (value == _hover) {
return;
}
setState(() => {
_hover = value;
// TODO(jacobr): we could choose to repaint only a portion of the UI
});
}
}
InspectorTreeNode _hover;
float? lastContentWidth;
public abstract InspectorTreeNode createNode();
public readonly List<InspectorTreeRow> cachedRows = new List<InspectorTreeRow>();
// TODO: we should add a listener instead that clears the cache when the
// root is marked as dirty.
void _maybeClearCache() {
if (root.isDirty) {
cachedRows.Clear();
root.isDirty = false;
lastContentWidth = null;
}
}
public InspectorTreeRow getCachedRow(int index) {
_maybeClearCache();
while (cachedRows.Count <= index) {
cachedRows.Add(null);
}
cachedRows[index] = cachedRows[index] ??root.getRow(index);
return cachedRows[index];
}
double getRowOffset(int index) {
return (getCachedRow(index)?.depth ?? 0) * InspectorTreeUtils.columnWidth;
}
RemoteDiagnosticsNode currentHoverDiagnostic;
void navigateUp() {
_navigateHelper(-1);
}
void navigateDown() {
_navigateHelper(1);
}
void navigateLeft() {
// This logic is consistent with how IntelliJ handles tree navigation on
// on left arrow key press.
if (selection == null) {
_navigateHelper(-1);
return;
}
if (selection.isExpanded) {
setState(() => {
selection.isExpanded = false;
});
return;
}
if (selection.parent != null) {
selection = selection.parent;
}
}
void navigateRight() {
// This logic is consistent with how IntelliJ handles tree navigation on
// on right arrow key press.
if (selection == null || selection.isExpanded) {
_navigateHelper(1);
return;
}
setState(() => {
selection.isExpanded = true;
});
}
void _navigateHelper(int indexOffset) {
if (numRows == 0) return;
if (selection == null) {
selection = root;
return;
}
selection = root
.getRow(
(root.getRowIndex(selection) + indexOffset).clamp(0, numRows.Value - 1))
?.node;
}
float horizontalPadding
{
get
{
return 10.0f;
}
}
double getDepthIndent(int depth) {
return (depth + 1) * InspectorTreeUtils.columnWidth + horizontalPadding;
}
double getRowY(int index) {
return InspectorTreeUtils.rowHeight * index + InspectorTreeUtils.verticalPadding;
}
void nodeChanged(InspectorTreeNode node) {
if (node == null) return;
setState(() => {
node.isDirty = true;
});
}
void removeNodeFromParent(InspectorTreeNode node) {
setState(() => {
node.parent?.removeChild(node);
});
}
void appendChild(InspectorTreeNode node, InspectorTreeNode child) {
setState(() => {
node.appendChild(child);
});
}
void expandPath(InspectorTreeNode node) {
setState(() => {
_expandPath(node);
});
}
void _expandPath(InspectorTreeNode node) {
while (node != null) {
if (!node.isExpanded) {
node.isExpanded = true;
}
node = node.parent;
}
}
public void collapseToSelected() {
setState(() => {
_collapseAllNodes(root);
if (selection == null) return;
_expandPath(selection);
});
}
void _collapseAllNodes(InspectorTreeNode root) {
root.isExpanded = false;
foreach (var child in root.children)
{
_collapseAllNodes(child);
}
}
int? numRows
{
get
{
return root != null ? root.subtreeSize : 0;
}
}
int getRowIndex(float y) => (int)((y - InspectorTreeUtils.verticalPadding) / InspectorTreeUtils.rowHeight);
public InspectorTreeRow getRowForNode(InspectorTreeNode node) {
return getCachedRow(root.getRowIndex(node));
}
InspectorTreeRow getRow(Offset offset) {
if (root == null) return null;
int row = getRowIndex(offset.dy);
return row < root.subtreeSize ? getCachedRow(row) : null;
}
public abstract void animateToTargets(List<InspectorTreeNode> targets);
void onExpandRow(InspectorTreeRow row) {
setState(() => {
row.node.isExpanded = true;
if (config.onExpand != null) {
config.onExpand(row.node);
}
});
}
void onCollapseRow(InspectorTreeRow row) {
setState(() => {
row.node.isExpanded = false;
});
}
void onSelectRow(InspectorTreeRow row) {
selection = row.node;
expandPath(row.node);
}
bool expandPropertiesByDefault(DiagnosticsTreeStyle style) {
// This code matches the text style defaults for which styles are
// by default and which aren't.
switch (style) {
case DiagnosticsTreeStyle.none:
case DiagnosticsTreeStyle.singleLine:
case DiagnosticsTreeStyle.errorProperty:
return false;
case DiagnosticsTreeStyle.sparse:
case DiagnosticsTreeStyle.offstage:
case DiagnosticsTreeStyle.dense:
case DiagnosticsTreeStyle.transition:
case DiagnosticsTreeStyle.error:
case DiagnosticsTreeStyle.whitespace:
case DiagnosticsTreeStyle.flat:
case DiagnosticsTreeStyle.shallow:
case DiagnosticsTreeStyle.truncateChildren:
return true;
}
return true;
}
InspectorTreeNode setupInspectorTreeNode(
InspectorTreeNode node,
RemoteDiagnosticsNode diagnosticsNode,
bool expandChildren = true,
bool expandProperties = true
) {
D.assert(expandChildren != null);
D.assert(expandProperties != null);
node.diagnostic = diagnosticsNode;
if (config.onNodeAdded != null) {
config.onNodeAdded(node, diagnosticsNode);
}
if (diagnosticsNode.hasChildren ||
diagnosticsNode.inlineProperties.isNotEmpty()) {
if (diagnosticsNode.childrenReady || !diagnosticsNode.hasChildren) {
bool styleIsMultiline =
expandPropertiesByDefault(diagnosticsNode.style.Value);
setupChildren(
diagnosticsNode,
node,
node.diagnostic.childrenNow,
expandChildren: expandChildren && styleIsMultiline,
expandProperties: expandProperties && styleIsMultiline
);
} else {
node.clearChildren();
node.appendChild(createNode());
}
}
return node;
}
void setupChildren(
RemoteDiagnosticsNode parent,
InspectorTreeNode treeNode,
List<RemoteDiagnosticsNode> children,
bool expandChildren = true,
bool expandProperties = true
) {
D.assert(expandChildren != null);
D.assert(expandProperties != null);
treeNode.isExpanded = expandChildren;
if (treeNode.children.Any()) {
// Only case supported is this is the loading node.
D.assert(treeNode.children.Count() == 1);
removeNodeFromParent(treeNode.children.First());
}
var inlineProperties = parent.inlineProperties;
if (inlineProperties != null) {
foreach (RemoteDiagnosticsNode property in inlineProperties) {
appendChild(
treeNode,
setupInspectorTreeNode(
createNode(),
property,
expandChildren: expandProperties,
expandProperties: expandProperties
)
);
}
}
if (children != null) {
foreach (RemoteDiagnosticsNode child in children) {
appendChild(
treeNode,
setupInspectorTreeNode(
createNode(),
child,
expandChildren: expandChildren,
expandProperties: expandProperties
)
);
}
}
}
public Future maybePopulateChildren(InspectorTreeNode treeNode) {
RemoteDiagnosticsNode diagnostic = treeNode.diagnostic;
if (diagnostic != null &&
diagnostic.hasChildren &&
(treeNode.hasPlaceholderChildren || !treeNode.children.Any())) {
try
{
diagnostic.children.then((children) =>
{
if (treeNode.hasPlaceholderChildren || !treeNode.children.Any()) {
setupChildren(
diagnostic,
treeNode,
children as List<RemoteDiagnosticsNode>,
expandChildren: true,
expandProperties: false
);
nodeChanged(treeNode);
if (treeNode == selection) {
expandPath(treeNode);
}
}
});
} catch (Exception e) {
Debug.Log(e.ToString());
}
}
return null;
}
}
mixin InspectorTreeFixedRowHeightController on InspectorTreeController {
Rect getBoundingBox(InspectorTreeRow row);
void scrollToRect(Rect targetRect);
public override void animateToTargets(List<InspectorTreeNode> targets) {
Rect targetRect;
foreach (InspectorTreeNode target in targets) {
var row = InspectorTreeController.getRowForNode(target);
if (row != null) {
var rowRect = getBoundingBox(row);
targetRect =
targetRect == null ? rowRect : targetRect.expandToInclude(rowRect);
}
}
if (targetRect == null || targetRect.isEmpty) return;
targetRect = targetRect.inflate(20.0f);
scrollToRect(targetRect);
}
}
}

453
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/box/box.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.DevTools.inspector.layout_explorer.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using ThemeUtils = Unity.UIWidgets.DevTools.inspector.layout_explorer.ui.ThemeUtils;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.box{
public class BoxUtils
{
public static string describeBoxName(LayoutProperties properties) {
var title = properties.node.description;
var renderDescription = properties.node?.renderObject?.description;
// TODO(jacobr): consider de-emphasizing the render object name by putting it
// in more transparent text or just calling the widget Parent instead of
// surfacing a widget name.
if(renderDescription != null) {
title += " - $renderDescription";
}
return title;
}
}
public class BoxLayoutExplorerWidget : LayoutExplorerWidget {
public BoxLayoutExplorerWidget(
InspectorController inspectorController = null,
Key key = null
) : base(inspectorController, key: key)
{
}
public static bool shouldDisplay(RemoteDiagnosticsNode node) {
// TODO(jacobr) pass a RemoteDiagnosticsNode to this method that contains
// the layout explorer related supplemental properties so that we can
// accurately determine whether the widget uses box layout.
return node != null;
}
public override State createState()
{
return new _BoxLayoutExplorerWidgetState();
}
}
public class _BoxLayoutExplorerWidgetState :
LayoutExplorerWidgetState<BoxLayoutExplorerWidget, LayoutProperties>
{
public override RemoteDiagnosticsNode getRoot(RemoteDiagnosticsNode node) {
if (!shouldDisplay(node)) return null;
return node;
}
public override bool shouldDisplay(RemoteDiagnosticsNode node) {
return BoxLayoutExplorerWidget.shouldDisplay(selectedNode);
}
public override AnimatedLayoutProperties<LayoutProperties> computeAnimatedProperties(
LayoutProperties nextProperties
) {
return new AnimatedLayoutProperties<LayoutProperties>(
animatedProperties?.copyWith() ?? properties,
nextProperties,
changeAnimation
);
}
public override LayoutProperties computeLayoutProperties(RemoteDiagnosticsNode node)
{
return new LayoutProperties(node);
}
public override void updateHighlighted(LayoutProperties newProperties) {
setState(() =>{
// This implementation will need to change if we support showing more than
// a single widget in the box visualization for the layout explorer.
if (newProperties != null && selectedNode == newProperties.node) {
highlighted = newProperties;
} else {
highlighted = null;
}
});
}
public override Widget build(BuildContext context) {
if (properties == null) return new SizedBox();
return new Container(
margin: EdgeInsets.all(ThemeUtils.margin),
padding: EdgeInsets.only(bottom: ThemeUtils.margin, right: ThemeUtils.margin),
child: new AnimatedBuilder(
animation: changeController,
builder: (context2, _) => {
return new LayoutBuilder(builder: _buildLayout);
}
)
);
}
/// TODO(jacobr): see if we can unify with the stylized version of the overall
/// layout used for Flex. Our constraints are quite different as we can
/// guarantee that the entire layout fits without scrolling while in the Flex
/// case that would be difficult.
static List<float?> minFractionLayout(
float? availableSize,
List<float?> sizes,
List<float?> minFractions
) {
D.assert(sizes.Count == minFractions.Count);
var length = sizes.Count;
float total = 1.0f;
var fractions = minFractions.ToList();
foreach (var size in sizes) {
if (size != null) {
total += Mathf.Max(0, size.Value);
}
}
float totalFraction = 0.0f;
for (int i = 0; i < length; i++) {
var size = sizes[i];
if (size != null) {
fractions[i] = Mathf.Max(size.Value / total, minFractions[i]?? 0.0f);
totalFraction += fractions[i].Value;
} else {
fractions[i] = 0.0f;
}
}
if (Mathf.Abs(totalFraction - 1.0f) > 1E-10) {
for (int i = 0; i < length; i++) {
fractions[i] = fractions[i] / totalFraction;
}
}
var output = new List<float?>();
foreach (var fraction in fractions) {
output.Add(fraction * availableSize);
}
return output;
}
public Widget _buildChild(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var parentProperties = this.parentProperties ?? properties;
var parentSize = parentProperties.size;
var boxParentData = new BoxParentData();
boxParentData.offset = new Offset(0, 0);
var offset = properties.node?.parentData ?? boxParentData;
if (properties.size == null) {
return new Center(
child: new Text(
"Visualizing layouts for ${properties.description} widgets is not yet supported."
)
);
}
return new LayoutBuilder(
builder: (BuildContext context2, BoxConstraints constraints) =>
{
// Subtract out one pixel border on each side.
var availableHeight = constraints.maxHeight - 2;
var availableWidth = constraints.maxWidth - 2;
var minFractions = new List<float?> {0.2f, 0.5f, 0.2f};
float? nullOutZero(float value)
{
return value != 0.0f ? value : (float?) null;
}
var widths = new List<float?>
{
nullOutZero(offset.offset.dx),
properties.size.width,
nullOutZero(parentSize != null
? parentSize.width - (properties.size.width + offset.offset.dx)
: 0.0f)
};
var heights = new List<float?>
{
nullOutZero(offset.offset.dy),
properties.size.height,
nullOutZero(parentSize != null
? parentSize.height - (properties.size.height + offset.offset.dy)
: 0.0f)
};
// 3 element array with [left padding, widget width, right padding].
var displayWidths = minFractionLayout(
availableSize: availableWidth,
sizes: widths,
minFractions: minFractions
);
// 3 element array with [top padding, widget height, bottom padding].
var displayHeights = minFractionLayout(
availableSize: availableHeight,
sizes: heights,
minFractions: minFractions
);
var widgetWidth = displayWidths[1];
var widgetHeight = displayHeights[1];
var safeParentSize = parentSize ?? properties.size;
List<Widget> widgets = new List<Widget>();
widgets.Add(new LayoutExplorerBackground(colorScheme: colorScheme));
if (widths[0] != null)
{
widgets.Add(new PaddingVisualizerWidget(
new RenderProperties(
axis: Axis.horizontal,
size: new Size(displayWidths[0].Value, widgetHeight.Value),
offset: new Offset(0, displayHeights[0].Value),
realSize: new Size(widths[0].Value, safeParentSize.height),
layoutProperties: properties,
isFreeSpace: true
),
horizontal: true
));
}
if (heights[0] != null)
{
widgets.Add(new PaddingVisualizerWidget(
new RenderProperties(
axis: Axis.horizontal,
size: new Size(widgetWidth.Value, displayHeights[0].Value),
offset: new Offset(displayWidths[0].Value, 0),
realSize: new Size(safeParentSize.width, heights[0].Value),
layoutProperties: properties,
isFreeSpace: true
),
horizontal: false
));
}
if (widths[2] != null)
{
widgets.Add(new PaddingVisualizerWidget(
new RenderProperties(
axis: Axis.horizontal,
size: new Size(displayWidths[2].Value, widgetHeight.Value),
offset: new Offset(
displayWidths[0].Value + displayWidths[1].Value, displayHeights[0].Value),
realSize: new Size(widths[2].Value, safeParentSize.height),
layoutProperties: properties,
isFreeSpace: true
),
horizontal: true
));
}
if (heights[2] != null)
{
widgets.Add(new PaddingVisualizerWidget(
new RenderProperties(
axis: Axis.horizontal,
size: new Size(widgetWidth.Value, displayHeights[2].Value),
offset: new Offset(displayWidths[0].Value,
displayHeights[0].Value + displayHeights[1].Value),
realSize: new Size(safeParentSize.width, heights[2].Value),
layoutProperties: properties,
isFreeSpace: true
),
horizontal: false
));
}
widgets.Add(new BoxChildVisualizer(
isSelected: true,
state: this,
layoutProperties: properties,
renderProperties:
new RenderProperties(
axis: Axis.horizontal,
size: new Size(widgetWidth.Value, widgetHeight.Value),
offset: new Offset(displayWidths[0].Value, displayHeights[0].Value),
realSize: properties.size,
layoutProperties: properties
)
));
return new Container(
width: constraints.maxWidth,
height: constraints.maxHeight,
decoration: new BoxDecoration(
border: Border.all(
color: ThemeUtils.regularWidgetColor
)
),
child: new Stack(
children: widgets
)
);
}
);
}
LayoutProperties parentProperties {
get
{
var parentElement = properties?.node?.parentRenderElement;
if (parentElement == null) return null;
var parentProperties = computeLayoutProperties(parentElement);
if (parentProperties.size == null) return null;
return parentProperties;
}
}
Widget _buildLayout(BuildContext context, BoxConstraints constraints) {
var maxHeight = constraints.maxHeight;
var maxWidth = constraints.maxWidth;
Widget widget = _buildChild(context);
var parentProperties = this.parentProperties;
if (parentProperties != null) {
widget = new WidgetVisualizer(
// TODO(jacobr): this node's name can be misleading more often than
// in the flex case the widget doesn't have its own RenderObject.
// Consider showing the true ancestor for the summary tree that first
// has a different render object.
title: BoxUtils.describeBoxName(parentProperties),
largeTitle: true,
layoutProperties: parentProperties,
isSelected: false,
child: new VisualizeWidthAndHeightWithConstraints(
properties: parentProperties,
warnIfUnconstrained: false,
child: new Padding(
padding: EdgeInsets.all(CommonThemeUtils.denseSpacing),
child: widget
)
)
);
}
return new Container(
constraints: new BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight),
child: widget
);
}
}
public class BoxChildVisualizer : StatelessWidget {
public BoxChildVisualizer(
Key key = null,
_BoxLayoutExplorerWidgetState state = null,
LayoutProperties layoutProperties = null,
RenderProperties renderProperties = null,
bool isSelected = false
) : base(key: key)
{
this.state = state;
this.layoutProperties = layoutProperties;
this.renderProperties = renderProperties;
this.isSelected = isSelected;
}
public readonly _BoxLayoutExplorerWidgetState state;
public readonly bool isSelected;
public readonly LayoutProperties layoutProperties;
public readonly RenderProperties renderProperties;
public LayoutProperties root
{
get
{
return state.properties;
}
}
public LayoutProperties properties
{
get
{
return renderProperties.layoutProperties;
}
}
public override Widget build(BuildContext context2) {
var renderSize = renderProperties.size;
var renderOffset = renderProperties.offset;
Widget buildEntranceAnimation(BuildContext context3, Widget child) {
var size = renderSize;
// TODO(jacobr): does this entrance animation really add value.
return new Opacity(
opacity: Mathf.Min(state.entranceCurve.value * 5, 1.0f),
child: new Padding(
padding: EdgeInsets.symmetric(
horizontal: Mathf.Max(0.0f, (renderSize.width - size.width) / 2),
vertical: Mathf.Max(0.0f, (renderSize.height - size.height) / 2)
),
child: child
)
);
}
return new Positioned(
top: renderOffset.dy,
left: renderOffset.dx,
child: new InkWell(
onTap: () => state.onTap(properties),
onDoubleTap: () => state.onDoubleTap(properties),
onLongPress: () => state.onDoubleTap(properties),
child: new SizedBox(
width: utils.safePositiveFloat(renderSize.width),
height: utils.safePositiveFloat(renderSize.height),
child: new AnimatedBuilder(
animation: state.entranceController,
builder: buildEntranceAnimation,
child: new WidgetVisualizer(
isSelected: isSelected,
layoutProperties: layoutProperties,
title: BoxUtils.describeBoxName(properties),
// TODO(jacobr): consider surfacing the overflow size information
// if we determine
// overflowSide: properties.overflowSide,
// We only show one child at a time so a large title is safe.
largeTitle: true,
child: new VisualizeWidthAndHeightWithConstraints(
arrowHeadSize: ThemeUtils.arrowHeadSize,
properties: properties,
warnIfUnconstrained: false,
child: null
)
)
)
)
)
);
}
}
}

807
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/flex/flex.cs


using System;
using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.DevTools.inspector.layout_explorer.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using UnityEngine.UIElements;
using Align = Unity.UIWidgets.widgets.Align;
using Color = Unity.UIWidgets.ui.Color;
using FontStyle = Unity.UIWidgets.ui.FontStyle;
using Image = Unity.UIWidgets.widgets.Image;
using Object = System.Object;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using ThemeUtils = Unity.UIWidgets.DevTools.inspector.layout_explorer.ui.ThemeUtils;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.flex
{
public class FlexLayoutExplorerWidget : LayoutExplorerWidget {
public FlexLayoutExplorerWidget(
InspectorController inspectorController,
Key key = null
) : base(inspectorController, key: key){}
public static bool shouldDisplay(RemoteDiagnosticsNode node) {
return (node?.isFlex ?? false) || (node?.parent?.isFlex ?? false);
}
public override State createState()
{
return new _FlexLayoutExplorerWidgetState();
}
}
public class _FlexLayoutExplorerWidgetState : LayoutExplorerWidgetState<FlexLayoutExplorerWidget, FlexLayoutProperties> {
ScrollController scrollController = new ScrollController();
public Axis? direction
{
get
{
return properties.direction;
}
}
Color horizontalColor(ColorScheme colorScheme)
{
return properties.isMainAxisHorizontal
? ThemeUtils.mainAxisColor
: ThemeUtils.crossAxisColor;
}
Color verticalColor(ColorScheme colorScheme)
{
return properties.isMainAxisVertical
? ThemeUtils.mainAxisColor
: ThemeUtils.crossAxisColor;
}
Color horizontalTextColor(ColorScheme colorScheme)
{
return properties.isMainAxisHorizontal
? ThemeUtils.mainAxisTextColor
: ThemeUtils.crossAxisTextColor;
}
Color verticalTextColor(ColorScheme colorScheme)
{
return properties.isMainAxisVertical
? ThemeUtils.mainAxisTextColor
: ThemeUtils.crossAxisTextColor;
}
string flexType
{
get
{
return properties.type;
}
}
public override RemoteDiagnosticsNode getRoot(RemoteDiagnosticsNode node) {
if (!shouldDisplay(node)) return null;
if (node.isFlex) return node;
return node.parent;
}
public override bool shouldDisplay(RemoteDiagnosticsNode node) {
return FlexLayoutExplorerWidget.shouldDisplay(selectedNode);
}
public override AnimatedLayoutProperties<FlexLayoutProperties> computeAnimatedProperties(
FlexLayoutProperties nextProperties) {
return new AnimatedFlexLayoutProperties(
(FlexLayoutProperties)animatedProperties?.copyWith() ?? properties,
nextProperties,
changeAnimation
);
}
public override FlexLayoutProperties computeLayoutProperties(RemoteDiagnosticsNode node)
{
return FlexLayoutProperties.fromDiagnostics(node);
}
public override void updateHighlighted(FlexLayoutProperties newProperties) {
setState(() => {
if (selectedNode.isFlex) {
highlighted = newProperties;
} else {
var idx = selectedNode.parent.childrenNow.IndexOf(selectedNode);
if (newProperties == null || newProperties.children == null) return;
if (idx != -1) highlighted = newProperties.children[idx];
}
});
}
Widget _buildAxisAlignmentDropdown(Axis axis, ColorScheme colorScheme) {
Color color = axis == direction
? ThemeUtils.mainAxisTextColor
: ThemeUtils.crossAxisTextColor;
List<object> alignmentEnumEntries;
Object selected;
if (axis == direction)
{
alignmentEnumEntries = new List<object>
{
Enum.GetValues(typeof(MainAxisAlignment))
.Cast<MainAxisAlignment>().ToList()
};
selected = properties.mainAxisAlignment;
} else {
alignmentEnumEntries = alignmentEnumEntries = new List<object>
{
Enum.GetValues(typeof(CrossAxisAlignment))
.Cast<CrossAxisAlignment>().ToList()
};
if (properties.textBaseline == null) {
// TODO(albertusangga): Look for ways to visualize baseline when it is null
alignmentEnumEntries.Remove(CrossAxisAlignment.baseline);
}
selected = properties.crossAxisAlignment;
}
List<DropdownMenuItem<object>> dropdownMenuItems = new List<DropdownMenuItem<object>>();
foreach (var alignment in alignmentEnumEntries)
{
dropdownMenuItems.Add(new DropdownMenuItem<object>(
value: alignment,
child: new Container(
padding: EdgeInsets.symmetric(vertical: ThemeUtils.margin),
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: new List<Widget>{
new Expanded(
child: new Text(
alignment.ToString(),
style: new TextStyle(color: color),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis
)
),
new Flexible(
child: Image.asset(
(axis == direction)
? FlexUtils.mainAxisAssetImageUrl(direction.Value, (MainAxisAlignment)alignment)
: FlexUtils.crossAxisAssetImageUrl(direction.Value, (CrossAxisAlignment)alignment),
fit: BoxFit.fitHeight,
color: color
)
)
}
)
)
));
}
return new RotatedBox(
quarterTurns: axis == Axis.vertical ? 3 : 0,
child: new Container(
constraints: new BoxConstraints(
maxWidth: ThemeUtils.dropdownMaxSize,
maxHeight: ThemeUtils.dropdownMaxSize
),
child: new DropdownButton<object>(
value: selected,
isExpanded: true,
// Avoid showing an underline for the main axis and cross-axis drop downs.
underline: new SizedBox(),
iconEnabledColor: axis == properties.direction
? ThemeUtils.mainAxisColor
: ThemeUtils.crossAxisColor,
selectedItemBuilder: (context) =>
{
List<Widget> widgets = new List<Widget>();
foreach (var alignment in alignmentEnumEntries)
{
widgets.Add(new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: new List<Widget>{
new Expanded(
flex: 2,
child: new Text(
alignment.ToString(),
style: new TextStyle(color: color),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis
)
),
new Flexible(
child: Image.asset(
(axis == direction)
? FlexUtils.mainAxisAssetImageUrl(direction.Value, (MainAxisAlignment)alignment)
: FlexUtils.crossAxisAssetImageUrl(direction.Value, (CrossAxisAlignment)alignment),
height: ThemeUtils.axisAlignmentAssetImageHeight,
fit: BoxFit.fitHeight,
color: color
)
)
}
));
}
return widgets;
},
items: dropdownMenuItems,
onChanged: (object newSelection) => {
FlexLayoutProperties changedProperties;
if (axis == direction) {
changedProperties =
properties.copyWith(mainAxisAlignment: (MainAxisAlignment)newSelection);
} else {
changedProperties =
properties.copyWith(crossAxisAlignment: (CrossAxisAlignment)newSelection);
}
//[!!!] not sure about this
var service = properties.node.inspectorService;
var valueRef = properties.node.valueRef;
markAsDirty();
// service.invokeSetFlexProperties(
// valueRef,
// changedProperties.mainAxisAlignment,
// changedProperties.crossAxisAlignment
// );
}
)
)
);
}
public override Widget build(BuildContext context) {
if (properties == null) return new SizedBox();
return new Container(
margin: EdgeInsets.all(ThemeUtils.margin),
padding: EdgeInsets.only(bottom: ThemeUtils.margin, right: ThemeUtils.margin),
child: new AnimatedBuilder(
animation: changeController,
builder: (context2, _) => {
return new LayoutBuilder(builder: _buildLayout);
}
)
);
}
Widget _buildLayout(BuildContext context, BoxConstraints constraints) {
var colorScheme = Theme.of(context).colorScheme;
var maxHeight = constraints.maxHeight;
var maxWidth = constraints.maxWidth;
var flexDescription = new Align(
alignment: Alignment.centerLeft,
child: new Container(
margin: EdgeInsets.only(
top: ThemeUtils.mainAxisArrowIndicatorSize,
left: ThemeUtils.crossAxisArrowIndicatorSize + ThemeUtils.margin
),
child: new InkWell(
onTap: () => onTap(properties),
child: new WidgetVisualizer(
title: flexType,
layoutProperties: properties,
isSelected: highlighted == properties,
overflowSide: properties.overflowSide,
hint: new Container(
padding: EdgeInsets.all(4.0f),
child: new Text(
$"Total Flex Factor: {properties?.totalFlex}",
textScaleFactor: ThemeUtils.largeTextScaleFactor,
style: new TextStyle(
color: ThemeUtils.emphasizedTextColor,
fontWeight: FontWeight.bold
),
overflow: TextOverflow.ellipsis
)
),
child: new VisualizeFlexChildren(
state: this,
properties: properties,
children: children,
highlighted: highlighted,
scrollController: scrollController,
direction: direction
)
)
)
)
);
var verticalAxisDescription = new Align(
alignment: Alignment.bottomLeft,
child: new Container(
margin: EdgeInsets.only(top: ThemeUtils.mainAxisArrowIndicatorSize + ThemeUtils.margin),
width: ThemeUtils.crossAxisArrowIndicatorSize,
child: new Column(
children: new List<Widget>{
new Expanded(
child: new ArrowWrapper(
arrowColor: verticalColor(colorScheme),
child: new Truncateable(
truncate: maxHeight <= ThemeUtils.minHeightToAllowTruncating,
child: new RotatedBox(
quarterTurns: 3,
child: new Text(
properties.verticalDirectionDescription,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
textScaleFactor: ThemeUtils.largeTextScaleFactor,
style: new TextStyle(
color: verticalTextColor(colorScheme)
)
)
)
),
type: ArrowType.down
)
),
new Truncateable(
truncate: maxHeight <= ThemeUtils.minHeightToAllowTruncating,
child: _buildAxisAlignmentDropdown(Axis.vertical, colorScheme)
),
}
)
)
);
var horizontalAxisDescription = new Align(
alignment: Alignment.topRight,
child: new Container(
margin: EdgeInsets.only(left: ThemeUtils.crossAxisArrowIndicatorSize + ThemeUtils.margin),
height: ThemeUtils.mainAxisArrowIndicatorSize,
child: new Row(
children: new List<Widget>{
new Expanded(
child: new ArrowWrapper(
arrowColor: horizontalColor(colorScheme),
child: new Truncateable(
truncate: maxWidth <= ThemeUtils.minWidthToAllowTruncating,
child: new Text(
properties.horizontalDirectionDescription,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
textScaleFactor: ThemeUtils.largeTextScaleFactor,
style: new TextStyle(color: horizontalTextColor(colorScheme))
)
),
type: ArrowType.right
)
),
new Truncateable(
truncate: maxWidth <= ThemeUtils.minWidthToAllowTruncating,
child: _buildAxisAlignmentDropdown(Axis.horizontal, colorScheme)
),
}
)
)
);
return new Container(
constraints: new BoxConstraints(maxWidth: maxWidth, maxHeight: maxHeight),
child: new Stack(
children: new List<Widget>{
flexDescription,
verticalAxisDescription,
horizontalAxisDescription
}
)
);
}
}
public class VisualizeFlexChildren : StatefulWidget {
public VisualizeFlexChildren(
Key key = null,
_FlexLayoutExplorerWidgetState state = null,
FlexLayoutProperties properties = null,
List<LayoutProperties> children = null,
LayoutProperties highlighted = null,
ScrollController scrollController = null,
Axis? direction = null
) : base(key: key)
{
this.state = state;
this.properties = properties;
this.children = children;
this.highlighted = highlighted;
this.scrollController = scrollController;
this.direction = direction;
}
public readonly FlexLayoutProperties properties;
public readonly List<LayoutProperties> children;
public readonly LayoutProperties highlighted;
public readonly ScrollController scrollController;
public readonly Axis? direction;
public readonly _FlexLayoutExplorerWidgetState state;
public override State createState()
{
return new _VisualizeFlexChildrenState();
}
}
public class _VisualizeFlexChildrenState : State<VisualizeFlexChildren> {
LayoutProperties lastHighlighted;
public static readonly GlobalKey selectedChildKey = GlobalKey.key(debugLabel: "selectedChild");
public override Widget build(BuildContext context) {
if (lastHighlighted != widget.highlighted) {
lastHighlighted = widget.highlighted;
if (widget.highlighted != null) {
WidgetsBinding.instance.addPostFrameCallback((_) => {
var selectedRenderObject =
selectedChildKey.currentContext?.findRenderObject();
if (selectedRenderObject != null &&
widget.scrollController.hasClients) {
widget.scrollController.position.ensureVisible(
selectedRenderObject,
alignment: 0.5f,
duration: CommonThemeUtils.defaultDuration
);
}
});
}
}
if (!widget.properties.hasChildren) {
return new Center(child: new Text("No Children"));
}
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var contents = new Container(
decoration: new BoxDecoration(
border: Border.all(
color: theme.primaryColorLight
)
),
margin: EdgeInsets.only(top: ThemeUtils.margin, left: ThemeUtils.margin),
child: new LayoutBuilder(builder: (context2, constraints) => {
var maxWidth = constraints.maxWidth;
var maxHeight = constraints.maxHeight;
float maxSizeAvailable(Axis axis) {
return axis == Axis.horizontal ? maxWidth : maxHeight;
}
var childrenAndMainAxisSpacesRenderProps =
widget.properties.childrenRenderProperties(
smallestRenderWidth: ThemeUtils.minRenderWidth,
largestRenderWidth: ThemeUtils.defaultMaxRenderWidth,
smallestRenderHeight: ThemeUtils.minRenderHeight,
largestRenderHeight: ThemeUtils.defaultMaxRenderHeight,
maxSizeAvailable: maxSizeAvailable
);
List<RenderProperties> renderProperties = new List<RenderProperties>();
List<RenderProperties> mainAxisSpaces = new List<RenderProperties>();
foreach (var prop in childrenAndMainAxisSpacesRenderProps)
{
if (!prop.isFreeSpace)
{
renderProperties.Add(prop);
}
else
{
mainAxisSpaces.Add(prop);
}
}
var crossAxisSpaces = widget.properties.crossAxisSpaces(
childrenRenderProperties: renderProperties,
maxSizeAvailable: maxSizeAvailable
);
var childrenRenderWidgets = new List<Widget>();
for (var i = 0; i < widget.children.Count; i++) {
var child = widget.children[i];
var isSelected = widget.highlighted == child;
childrenRenderWidgets.Add( new FlexChildVisualizer(
key: isSelected ? selectedChildKey : null,
state: widget.state,
layoutProperties: child,
isSelected: isSelected,
renderProperties: renderProperties[i]
));
}
List<Widget> freeSpacesWidgets = new List<Widget>();
var propertiesList = new List<RenderProperties>(mainAxisSpaces.Union(crossAxisSpaces));
foreach (var property in propertiesList)
{
freeSpacesWidgets.Add(new FreeSpaceVisualizerWidget(property));
}
List<Widget> widgets = new List<Widget>();
widgets.Add(new LayoutExplorerBackground(colorScheme: colorScheme));
widgets = widgets.Union(freeSpacesWidgets).Union(childrenRenderWidgets).ToList();
float sum_width = 0;
float sum_height = 0;
foreach (var prop in childrenAndMainAxisSpacesRenderProps)
{
sum_width += prop.width;
sum_height += prop.height;
}
return new Scrollbar(
isAlwaysShown: true,
controller: widget.scrollController,
child: new SingleChildScrollView(
scrollDirection: widget.properties.direction.Value,
controller: widget.scrollController,
child: new ConstrainedBox(
constraints: new BoxConstraints(
minWidth: maxWidth,
minHeight: maxHeight,
maxWidth: widget.direction == Axis.horizontal
? sum_width
: maxWidth,
maxHeight: widget.direction == Axis.vertical
? sum_height
: maxHeight
).normalize(),
child: new Stack(
children: widgets
)
)
)
);
})
);
return new VisualizeWidthAndHeightWithConstraints(
child: contents,
properties: widget.properties
);
}
}
/// Widget that represents and visualize a direct child of Flex widget.
public class FlexChildVisualizer : StatelessWidget {
public readonly int maximumFlexFactorOptions = 5;
public FlexChildVisualizer(
Key key = null,
_FlexLayoutExplorerWidgetState state = null,
LayoutProperties layoutProperties = null,
RenderProperties renderProperties = null,
bool? isSelected = null
) : base(key: key)
{
this.state = state;
this.layoutProperties = layoutProperties;
this.renderProperties = renderProperties;
this.isSelected = isSelected;
}
public readonly _FlexLayoutExplorerWidgetState state;
public readonly bool? isSelected;
public readonly LayoutProperties layoutProperties;
public readonly RenderProperties renderProperties;
FlexLayoutProperties root
{
get
{
return state.properties;
}
}
LayoutProperties properties
{
get
{
return renderProperties.layoutProperties;
}
}
void onChangeFlexFactor(int newFlexFactor) {
// var node = properties.node;
// var inspectorService = await node.inspectorService;
// state.markAsDirty();
// await inspectorService.invokeSetFlexFactor(
// node.valueRef,
// newFlexFactor
// );
}
void onChangeFlexFit(FlexFit newFlexFit) {
// var node = properties.node;
// var inspectorService = node.inspectorService;
// state.markAsDirty();
// inspectorService.invokeSetFlexFit(
// node.valueRef,
// newFlexFit
// );
}
Widget _buildFlexFactorChangerDropdown(int maximumFlexFactor) {
Widget buildMenuitemChild(int flexFactor) {
return new Text(
$"flex: {flexFactor}",
style: flexFactor == properties.flexFactor
? new TextStyle(
fontWeight: FontWeight.bold,
color: ThemeUtils.emphasizedTextColor
)
: new TextStyle(color: ThemeUtils.emphasizedTextColor)
);
}
DropdownMenuItem<int> buildMenuItem(int flexFactor) {
return new DropdownMenuItem<int>(
value: flexFactor,
child: buildMenuitemChild(flexFactor)
);
}
List<DropdownMenuItem<int>> items = new List<DropdownMenuItem<int>>();
items.Add(buildMenuItem(default)); // May has porblems
for (var i = 0; i <= maximumFlexFactor; ++i)
{
items.Add(buildMenuItem(i));
}
return new DropdownButton<int>(
value: (int)properties.flexFactor?.clamp(0, maximumFlexFactor),
onChanged: onChangeFlexFactor,
iconEnabledColor: ThemeUtils.textColor,
underline: ThemeUtils.buildUnderline(),
items: items
);
}
Widget _buildFlexFitChangerDropdown() {
Widget flexFitDescription(FlexFit flexFit)
{
return new Text(
$"fit: {flexFit.ToString()}",
style: new TextStyle(color: ThemeUtils.emphasizedTextColor)
);
}
// Disable FlexFit changer if widget is Expanded.
if (properties.description == "Expanded") {
return flexFitDescription(FlexFit.tight);
}
DropdownMenuItem<FlexFit> buildMenuItem(FlexFit flexFit) {
return new DropdownMenuItem<FlexFit>(
value: flexFit,
child: flexFitDescription(flexFit)
);
}
List<DropdownMenuItem<FlexFit>> items = new List<DropdownMenuItem<FlexFit>>();
items.Add(buildMenuItem(FlexFit.loose));
if (properties.description != "Expanded") items.Add(buildMenuItem(FlexFit.tight));
return new DropdownButton<FlexFit>(
value: properties.flexFit.Value,
onChanged: onChangeFlexFit,
underline: ThemeUtils.buildUnderline(),
iconEnabledColor: ThemeUtils.emphasizedTextColor,
items: items
);
}
Widget _buildContent(ColorScheme colorScheme)
{
List<Widget> widgets = new List<Widget>();
widgets.Add(new Flexible(
child: _buildFlexFactorChangerDropdown(maximumFlexFactorOptions)
));
if (!properties.hasFlexFactor)
{
widgets.Add(new Text(
root.isMainAxisHorizontal ?"unconstrained horizontal" : "unconstrained vertical",
style: new TextStyle(
color: ThemeUtils.unconstrainedColor,
fontStyle: FontStyle.italic
),
maxLines: 2,
softWrap: true,
overflow: TextOverflow.ellipsis,
textScaleFactor: ThemeUtils.smallTextScaleFactor,
textAlign: TextAlign.center
));
}
widgets.Add(_buildFlexFitChangerDropdown());
return new Container(
margin: EdgeInsets.only(
top: ThemeUtils.margin,
left: ThemeUtils.margin
),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: widgets
)
);
}
public override Widget build(BuildContext context) {
var renderSize = renderProperties.size;
var renderOffset = renderProperties.offset;
Widget buildEntranceAnimation(BuildContext context2, Widget child) {
var vertical = root.isMainAxisVertical;
var horizontal = root.isMainAxisHorizontal;
Size size = renderSize;
if (properties.hasFlexFactor) {
size = new SizeTween(
begin: new Size(
horizontal ? ThemeUtils.minRenderWidth - ThemeUtils.entranceMargin : renderSize.width,
vertical ? ThemeUtils.minRenderHeight - ThemeUtils.entranceMargin : renderSize.height
),
end: renderSize
).evaluate(state.entranceCurve);
}
// Not-expanded widgets enter much faster.
return new Opacity(
opacity: Mathf.Min(state.entranceCurve.value * 5, 1.0f),
child: new Padding(
padding: EdgeInsets.symmetric(
horizontal: Mathf.Max(0.0f, (renderSize.width - size.width) / 2),
vertical: Mathf.Max(0.0f, (renderSize.height - size.height) / 2)
),
child: child
)
);
}
var colorScheme = Theme.of(context).colorScheme;
return new Positioned(
top: renderOffset.dy,
left: renderOffset.dx,
child: new InkWell(
onTap: () => state.onTap(properties),
onDoubleTap: () => state.onDoubleTap(properties),
onLongPress: () => state.onDoubleTap(properties),
child: new SizedBox(
width: renderSize.width,
height: renderSize.height,
child: new AnimatedBuilder(
animation: state.entranceController,
builder: buildEntranceAnimation,
child: new WidgetVisualizer(
isSelected: isSelected.Value,
layoutProperties: layoutProperties,
title: properties.description,
overflowSide: properties.overflowSide,
child: new VisualizeWidthAndHeightWithConstraints(
arrowHeadSize: ThemeUtils.arrowHeadSize,
child: new Align(
alignment: Alignment.topRight,
child: _buildContent(colorScheme)
),
properties: properties
)
)
)
)
)
);
}
}
}

266
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/flex/utils.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.DevTools.inspector.layout_explorer.ui;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.flex
{
public class FlexUtils
{
public static string crossAxisAssetImageUrl(Axis direction, CrossAxisAlignment alignment)
{
return "assets/img/layout_explorer/cross_axis_alignment/" +
$"{flexType(direction)}_{alignment.ToString()}.png";
}
public static string mainAxisAssetImageUrl(Axis direction, MainAxisAlignment alignment)
{
return "assets/img/layout_explorer/main_axis_alignment/" +
$"{flexType(direction)}_{alignment.ToString()}.png";
}
public static string flexType(Axis direction)
{
switch (direction)
{
case Axis.horizontal:
return "row";
case Axis.vertical:
default:
return "column";
}
}
}
public delegate float MaxSizeAvailable(Axis axis);
public class AnimatedFlexLayoutProperties
: AnimatedLayoutProperties<FlexLayoutProperties>//, FlexLayoutProperties
{
public AnimatedFlexLayoutProperties(FlexLayoutProperties begin,
FlexLayoutProperties end, Animation<float> animation)
: base(begin, end, animation) { }
public new CrossAxisAlignment? crossAxisAlignment
{
get { return end.crossAxisAlignment; }
}
public new MainAxisAlignment? mainAxisAlignment
{
get { return end.mainAxisAlignment; }
}
public List<RenderProperties> childrenRenderProperties( // public override List<RenderProperties> childrenRenderProperties
float smallestRenderWidth,
float largestRenderWidth,
float smallestRenderHeight,
float largestRenderHeight,
MaxSizeAvailable maxSizeAvailable
)
{
var beginRenderProperties = begin.childrenRenderProperties(
smallestRenderHeight: smallestRenderHeight,
smallestRenderWidth: smallestRenderWidth,
largestRenderHeight: largestRenderHeight,
largestRenderWidth: largestRenderWidth,
maxSizeAvailable: maxSizeAvailable
);
var endRenderProperties = end.childrenRenderProperties(
smallestRenderHeight: smallestRenderHeight,
smallestRenderWidth: smallestRenderWidth,
largestRenderHeight: largestRenderHeight,
largestRenderWidth: largestRenderWidth,
maxSizeAvailable: maxSizeAvailable
);
var result = new List<RenderProperties>();
for (var i = 0; i < children?.Count; i++)
{
var beginProps = beginRenderProperties[i];
var endProps = endRenderProperties[i];
var t = animation.value;
result.Add(
new RenderProperties(
axis: endProps.axis,
offset: Offset.lerp(beginProps.offset, endProps.offset, t),
size: Size.lerp(beginProps.size, endProps.size, t),
realSize: Size.lerp(beginProps.realSize, endProps.realSize, t),
layoutProperties: new AnimatedLayoutProperties<LayoutProperties>(
beginProps.layoutProperties,
endProps.layoutProperties,
animation
)
)
);
}
// Add in the free space from the end.
// TODO(djshuckerow): We should make free space a part of
// RenderProperties so that we can animate between those.
foreach (var property in endRenderProperties)
{
if (property.isFreeSpace)
{
result.Add(property);
}
}
return result;
}
public new float? crossAxisDimension
{
get
{
return utils.lerpFloat(
begin.crossAxisDimension,
end.crossAxisDimension,
animation.value
);
}
}
public new Axis crossAxisDirection
{
get { return end.crossAxisDirection; }
}
public List<RenderProperties> crossAxisSpaces( // public override List<RenderProperties> crossAxisSpaces
List<RenderProperties> childrenRenderProperties,
MaxSizeAvailable maxSizeAvailable
) {
return end.crossAxisSpaces(
childrenRenderProperties: childrenRenderProperties,
maxSizeAvailable: maxSizeAvailable
);
}
public new Axis? direction
{
get { return end.direction; }
}
public new string horizontalDirectionDescription
{
get { return end.horizontalDirectionDescription; }
}
public new bool isMainAxisHorizontal
{
get { return end.isMainAxisHorizontal; }
}
public new bool isMainAxisVertical
{
get { return end.isMainAxisVertical; }
}
public new float? mainAxisDimension
{
get
{
return utils.lerpFloat(
begin.mainAxisDimension,
end.mainAxisDimension,
animation.value
);
}
}
public new MainAxisSize? mainAxisSize
{
get { return end.mainAxisSize; }
}
public new TextBaseline? textBaseline
{
get { return end.textBaseline; }
}
public new TextDirection? textDirection
{
get { return end.textDirection; }
}
public new float? totalFlex
{
get { return utils.lerpFloat(begin.totalFlex, end.totalFlex, animation.value); }
}
public new string type
{
get { return end.type; }
}
public new VerticalDirection? verticalDirection
{
get { return end.verticalDirection; }
}
public new string verticalDirectionDescription
{
get { return end.verticalDirectionDescription; }
}
public new FlexLayoutProperties copyWith(
Size size = null,
List<LayoutProperties> children = null,
BoxConstraints constraints = null,
bool? isFlex = null,
string description = null,
float? flexFactor = null,
FlexFit? flexFit = null,
Axis? direction = null,
MainAxisAlignment? mainAxisAlignment = null,
MainAxisSize? mainAxisSize = null,
CrossAxisAlignment? crossAxisAlignment = null,
TextDirection? textDirection = null,
VerticalDirection? verticalDirection = null,
TextBaseline? textBaseline = null
)
{
return new FlexLayoutProperties(
size: size ?? this.size,
children: children ?? this.children,
node: node,
constraints: constraints ?? this.constraints,
isFlex: isFlex ?? this.isFlex,
description: description ?? this.description,
flexFactor: flexFactor ?? this.flexFactor,
direction: direction ?? this.direction,
mainAxisAlignment: mainAxisAlignment ?? this.mainAxisAlignment,
mainAxisSize: mainAxisSize ?? this.mainAxisSize,
crossAxisAlignment: crossAxisAlignment ?? this.crossAxisAlignment,
textDirection: textDirection ?? this.textDirection,
verticalDirection: verticalDirection ?? this.verticalDirection,
textBaseline: textBaseline ?? this.textBaseline
);
}
public new bool startIsTopLeft
{
get { return end.startIsTopLeft; }
}
}
}

93
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/layout_explorer.cs


using Unity.UIWidgets.DevTools.inspector.layout_explorer.box;
using Unity.UIWidgets.DevTools.inspector.layout_explorer.flex;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer
{
public class LayoutExplorerTab : StatefulWidget {
public LayoutExplorerTab(Key key = null, InspectorController controller = null) : base(key: key)
{
this.controller = controller;
}
public readonly InspectorController controller;
public override State createState()
{
return new _LayoutExplorerTabState();
}
}
public class _LayoutExplorerTabState : State<LayoutExplorerTab> //, AutomaticKeepAliveClientMixin<LayoutExplorerTab>, AutoDisposeMixin
{
InspectorController controller
{
get
{
return widget.controller;
}
}
RemoteDiagnosticsNode selected
{
get
{
return null; //controller?.selectedNode?.value?.diagnostic;
}
}
RemoteDiagnosticsNode previousSelection;
Widget rootWidget(RemoteDiagnosticsNode node) {
if (FlexLayoutExplorerWidget.shouldDisplay(node)) {
return new FlexLayoutExplorerWidget(controller);
}
if (BoxLayoutExplorerWidget.shouldDisplay(node)) {
return new BoxLayoutExplorerWidget(controller);
}
return new Center(
child: new Text(
node != null
? "Currently, Layout Explorer only supports Box and Flex-based widgets."
: "Select a widget to view its layout.",
textAlign: TextAlign.center,
overflow: TextOverflow.clip
)
);
}
void onSelectionChanged() {
if (rootWidget(previousSelection).GetType() !=
rootWidget(selected).GetType()) {
setState(() => previousSelection = selected);
}
}
public override void initState() {
base.initState();
//addAutoDisposeListener(controller.selectedNode, onSelectionChanged);
}
public override Widget build(BuildContext context) {
//base.build(context);
return rootWidget(selected);
}
public new bool wantKeepAlive
{
get
{
return true;
}
}
}
}

374
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/arrow.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public class ArrowUtils{
public static Color defaultArrowColor = Colors.white;
public static float defaultArrowStrokeWidth = 2.0f;
public static float defaultDistanceToArrow = 4.0f;
public static Axis axis(ArrowType? type)
{
return (type == ArrowType.up || type == ArrowType.down)
? Axis.vertical
: Axis.horizontal;
}
}
public enum ArrowType {
up,
left,
right,
down,
}
public class ArrowWrapper : StatelessWidget {
public ArrowWrapper(
Key key = null,
Widget child = null,
ArrowType? type = null,
Color arrowColor = null,
float? arrowHeadSize = null,
float? arrowStrokeWidth = null,
float? childMarginFromArrow = null
)
{
D.assert(type != null);
D.assert(arrowHeadSize != null && arrowHeadSize > 0.0);
D.assert(arrowStrokeWidth != null && arrowHeadSize > 0.0);
D.assert(childMarginFromArrow != null && childMarginFromArrow > 0.0);
direction = ArrowUtils.axis(type);
isBidirectional = false;
startArrowType = type;
endArrowType = type;
this.child = child;
this.arrowColor = arrowColor?? ArrowUtils.defaultArrowColor;
this.arrowHeadSize = arrowHeadSize?? CommonThemeUtils.defaultIconSize;
this.arrowStrokeWidth = arrowStrokeWidth ?? ArrowUtils.defaultArrowStrokeWidth;
this.childMarginFromArrow = childMarginFromArrow ?? ArrowUtils.defaultDistanceToArrow;
}
public ArrowWrapper(
Key key = null,
Widget child = null,
Axis? direction = null,
Color arrowColor = null,
float? arrowHeadSize = null,
float? arrowStrokeWidth = null,
float? childMarginFromArrow = null
)
{
D.assert(direction != null);
D.assert(arrowColor != null);
D.assert(arrowHeadSize != null && arrowHeadSize >= 0.0);
D.assert(arrowStrokeWidth != null && arrowHeadSize >= 0.0);
D.assert(childMarginFromArrow != null && childMarginFromArrow >= 0.0);
isBidirectional = true;
startArrowType =
direction == Axis.horizontal ? ArrowType.left : ArrowType.up;
endArrowType =
direction == Axis.horizontal ? ArrowType.right : ArrowType.down;
this.child = child;
this.direction = direction;
this.arrowColor = arrowColor?? ArrowUtils.defaultArrowColor;
this.arrowHeadSize = arrowHeadSize?? CommonThemeUtils.defaultIconSize;
this.arrowStrokeWidth = arrowStrokeWidth ?? ArrowUtils.defaultArrowStrokeWidth;
this.childMarginFromArrow = childMarginFromArrow ?? ArrowUtils.defaultDistanceToArrow;
}
public readonly Color arrowColor;
public readonly float? arrowHeadSize;
public readonly float? arrowStrokeWidth;
public readonly Widget child;
public readonly float childMarginFromArrow;
public readonly Axis? direction;
public readonly bool isBidirectional;
public readonly ArrowType? startArrowType;
public readonly ArrowType? endArrowType;
public float verticalMarginFromArrow {
get
{
if (child == null || direction == Axis.horizontal) return 0.0f;
return childMarginFromArrow;
}
}
public float horizontalMarginFromArrow {
get
{
if (child == null || direction == Axis.vertical) return 0.0f;
return childMarginFromArrow;
}
}
public override Widget build(BuildContext context)
{
List<Widget> widgets = new List<Widget>();
widgets.Add(new Expanded(
child: new Container(
margin: EdgeInsets.only(
bottom: verticalMarginFromArrow,
right: horizontalMarginFromArrow
),
child: new ArrowWidget(
color: arrowColor,
headSize: arrowHeadSize,
strokeWidth: arrowStrokeWidth,
type: startArrowType,
shouldDrawHead: isBidirectional
? true
: (startArrowType == ArrowType.left ||
startArrowType == ArrowType.up)
)
)
));
if(child!= null)widgets.Add(child);
widgets.Add(new Expanded(
child: new Container(
margin: EdgeInsets.only(
top: verticalMarginFromArrow,
left: horizontalMarginFromArrow
),
child: new ArrowWidget(
color: arrowColor,
headSize: arrowHeadSize,
strokeWidth: arrowStrokeWidth,
type: endArrowType,
shouldDrawHead: isBidirectional
? true
: (endArrowType == ArrowType.right ||
endArrowType == ArrowType.down)
)
)
));
return new Flex(
direction: direction.Value,
children: widgets
);
}
}
public class ArrowWidget : StatelessWidget {
public ArrowWidget(
Color color = null,
float? headSize = null,
Key key = null,
bool shouldDrawHead = true,
float? strokeWidth = null,
ArrowType? type = null
) : base(key: key)
{
this.headSize = headSize ?? CommonThemeUtils.defaultIconSize;
this.strokeWidth = strokeWidth ?? ArrowUtils.defaultArrowStrokeWidth;
D.assert(headSize != null && headSize > 0.0);
D.assert(strokeWidth != null && strokeWidth > 0.0);
D.assert(type != null);
this.color = color ?? ArrowUtils.defaultArrowColor;
this.shouldDrawHead = shouldDrawHead;
this.type = type;
_painter = new _ArrowPainter(
headSize: headSize,
color: color,
strokeWidth: strokeWidth,
type: type,
shouldDrawHead: shouldDrawHead
);
}
public readonly Color color;
/// The arrow head is a Equilateral triangle
public readonly float? headSize;
public readonly float? strokeWidth;
public readonly ArrowType? type;
public readonly CustomPainter _painter;
public readonly bool shouldDrawHead;
public override Widget build(BuildContext context) {
return new CustomPaint(
painter: _painter,
child: new Container()
);
}
}
public class _ArrowPainter : CustomPainter {
public _ArrowPainter(
float? headSize = null,
float? strokeWidth = null,
Color color = null,
bool shouldDrawHead = true,
ArrowType? type = null
)
{
D.assert(headSize != null);
D.assert(color != null);
D.assert(strokeWidth != null);
D.assert(type != null);
D.assert(shouldDrawHead != null);
// the height of an equilateral triangle
headHeight = 0.5f * Mathf.Sqrt(3) * headSize.Value;
this.headSize = headSize?? CommonThemeUtils.defaultIconSize;
this.strokeWidth = strokeWidth?? ArrowUtils.defaultArrowStrokeWidth;
this.color = color?? ArrowUtils.defaultArrowColor;
this.shouldDrawHead = shouldDrawHead;
this.type = type;
}
public readonly Color color;
public readonly float headSize;
public readonly bool shouldDrawHead;
public readonly float strokeWidth;
public readonly ArrowType? type;
public readonly float headHeight;
bool headIsGreaterThanConstraint(Size size) {
if (type == ArrowType.left || type == ArrowType.right) {
return headHeight >= (size.width);
}
return headHeight >= (size.height);
}
public bool shouldRepaint(CustomPainter oldDelegate)
{
return !(oldDelegate is _ArrowPainter &&
headSize == ((_ArrowPainter)oldDelegate).headSize &&
strokeWidth == ((_ArrowPainter)oldDelegate).strokeWidth &&
color == ((_ArrowPainter)oldDelegate).color &&
type == ((_ArrowPainter)oldDelegate).type);
}
public void paint(Canvas canvas, Size size)
{
Paint paint = new Paint();
paint.color = color;
paint.strokeWidth = strokeWidth;
var originX = size.width / 2;
var originY = size.height / 2;
Offset lineStartingPoint = Offset.zero;
Offset lineEndingPoint = Offset.zero;
if (!headIsGreaterThanConstraint(size) && shouldDrawHead) {
Offset p1 = null, p2 = null, p3 = null;
var headSizeDividedBy2 = headSize / 2;
switch (type) {
case ArrowType.up:
p1 = new Offset(originX, 0);
p2 = new Offset(originX - headSizeDividedBy2, headHeight);
p3 = new Offset(originX + headSizeDividedBy2, headHeight);
break;
case ArrowType.left:
p1 = new Offset(0, originY);
p2 = new Offset(headHeight, originY - headSizeDividedBy2);
p3 = new Offset(headHeight, originY + headSizeDividedBy2);
break;
case ArrowType.right:
var startingX = size.width - headHeight;
p1 = new Offset(size.width, originY);
p2 = new Offset(startingX, originY - headSizeDividedBy2);
p3 = new Offset(startingX, originY + headSizeDividedBy2);
break;
case ArrowType.down:
var startingY = size.height - headHeight;
p1 = new Offset(originX, size.height);
p2 = new Offset(originX - headSizeDividedBy2, startingY);
p3 = new Offset(originX + headSizeDividedBy2, startingY);
break;
}
Path path = new Path();
path.moveTo(p1.dx, p1.dy);
path.lineTo(p2.dx, p2.dy);
path.lineTo(p3.dx, p3.dy);
path.close();
canvas.drawPath(path, paint);
switch (type) {
case ArrowType.up:
lineStartingPoint = new Offset(originX, headHeight);
lineEndingPoint = new Offset(originX, size.height);
break;
case ArrowType.left:
lineStartingPoint = new Offset(headHeight, originY);
lineEndingPoint = new Offset(size.width, originY);
break;
case ArrowType.right:
var arrowHeadStartingX = size.width - headHeight;
lineStartingPoint = new Offset(0, originY);
lineEndingPoint = new Offset(arrowHeadStartingX, originY);
break;
case ArrowType.down:
var headStartingY = size.height - headHeight;
lineStartingPoint = new Offset(originX, 0);
lineEndingPoint = new Offset(originX, headStartingY);
break;
}
} else {
// draw full line
switch (type) {
case ArrowType.up:
case ArrowType.down:
lineStartingPoint = new Offset(originX, 0);
lineEndingPoint = new Offset(originX, size.height);
break;
case ArrowType.left:
case ArrowType.right:
lineStartingPoint = new Offset(0, originY);
lineEndingPoint = new Offset(size.width, originY);
break;
}
}
canvas.drawLine(
lineStartingPoint,
lineEndingPoint,
paint
);
}
public bool? hitTest(Offset position)
{
throw new System.NotImplementedException();
}
public void addListener(VoidCallback listener)
{
throw new System.NotImplementedException();
}
public void removeListener(VoidCallback listener)
{
throw new System.NotImplementedException();
}
}
}

39
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/dimension.cs


using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public class Dimension
{
public static Widget dimensionDescription(
TextSpan description,
bool overflow,
ColorScheme colorScheme
) {
var text = Text.rich(
description,
textAlign: TextAlign.center,
style:
ThemeUtils.dimensionIndicatorTextStyle,
overflow: TextOverflow.ellipsis
);
if (overflow) {
return new Container(
padding: EdgeInsets.symmetric(
vertical: ThemeUtils.minPadding,
horizontal: ThemeUtils.overflowTextHorizontalPadding
),
decoration: new BoxDecoration(
color: ThemeUtils.overflowBackgroundColor,
borderRadius: BorderRadius.circular(4.0f)
),
child: new Center(child: text)
);
}
return text;
}
}
}

170
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/free_space.cs


using System.Collections.Generic;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public class FreeSpaceVisualizerWidget : StatelessWidget {
public FreeSpaceVisualizerWidget(
RenderProperties renderProperties,
Key key = null
) : base(key: key)
{
}
public readonly RenderProperties renderProperties;
public override Widget build(BuildContext context) {
var colorScheme = Theme.of(context).colorScheme;
var heightDescription =
$"h={renderProperties.realHeight}";
var widthDescription = $"w={renderProperties.realWidth}";
var showWidth = renderProperties.realWidth !=
(renderProperties.layoutProperties?.width);
var widthWidget = new Container(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: new List<Widget>{
new Flexible(
child: Dimension.dimensionDescription(
new TextSpan(
text: widthDescription
),
false,
colorScheme
)
),
new Container(
margin: EdgeInsets.symmetric(vertical: ThemeUtils.arrowMargin),
child: new ArrowWrapper(
arrowColor: ThemeUtils.widthIndicatorColor,
direction: Axis.horizontal,
arrowHeadSize: ThemeUtils.arrowHeadSize
)
),
}
)
);
var heightWidget = new Container(
width: ThemeUtils.heightOnlyIndicatorSize,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: new List<Widget>{
new Flexible(
child: Dimension.dimensionDescription(
new TextSpan(text: heightDescription),
false,
colorScheme
)
),
new Container(
margin: EdgeInsets.symmetric(horizontal: ThemeUtils.arrowMargin),
child: new ArrowWrapper(
arrowColor: ThemeUtils.heightIndicatorColor,
direction: Axis.vertical,
arrowHeadSize: ThemeUtils.arrowHeadSize,
childMarginFromArrow: 0.0f
)
)
}
)
);
return new Positioned(
top: renderProperties.offset.dy,
left: renderProperties.offset.dx,
child: new Container(
width: renderProperties.width,
height: renderProperties.height,
child: new Tooltip(
message: $"{widthDescription}\n{heightDescription}",
child: showWidth ? widthWidget : heightWidget
)
)
);
}
}
public class PaddingVisualizerWidget : StatelessWidget {
public PaddingVisualizerWidget(
RenderProperties renderProperties,
bool horizontal,
Key key = null
) : base(key: key)
{
this.renderProperties = renderProperties;
this.horizontal = horizontal;
}
public readonly RenderProperties renderProperties;
public readonly bool horizontal;
public override Widget build(BuildContext context) {
var colorScheme = Theme.of(context).colorScheme;
var heightDescription =
$"h={renderProperties.realHeight}";
var widthDescription = $"w={renderProperties.realWidth}";
var widthWidget = new Container(child:
new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: new List<Widget>{
new Flexible(
child: Dimension.dimensionDescription(
new TextSpan(
text: widthDescription
),
false,
colorScheme
)
),
new Container(
margin: EdgeInsets.symmetric(vertical: ThemeUtils.arrowMargin),
child: new ArrowWrapper(
arrowColor: ThemeUtils.widthIndicatorColor,
direction: Axis.horizontal,
arrowHeadSize: ThemeUtils.arrowHeadSize
)
)
}
)
);
var heightWidget = new Container(
width: ThemeUtils.heightOnlyIndicatorSize,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: new List<Widget>{
new Flexible(
child: Dimension.dimensionDescription(
new TextSpan(text: heightDescription),
false,
colorScheme
)
),
new Container(
margin: EdgeInsets.symmetric(horizontal: ThemeUtils.arrowMargin),
child: new ArrowWrapper(
arrowColor: ThemeUtils.heightIndicatorColor,
direction: Axis.vertical,
arrowHeadSize: ThemeUtils.arrowHeadSize,
childMarginFromArrow: 0.0f
)
)
}
)
);
return new Positioned(
top: renderProperties.offset.dy,
left: renderProperties.offset.dx,
child: new Container(
width: utils.safePositiveFloat(renderProperties.width),
height: utils.safePositiveFloat(renderProperties.height),
child: horizontal ? widthWidget : heightWidget
)
);
}
}
}

71
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/overflow_indicator_painter.cs


using System.Collections.Generic;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public class OverflowIndicatorPainter : CustomPainter {
public OverflowIndicatorPainter(OverflowSide side, float size)
{
this.side = side;
this.size = size;
indicatorPaint.shader = Gradient.linear(
new Offset(0.0f, 0.0f),
new Offset(10.0f, 10.0f),
new List<Color>{
black, yellow, yellow, black
},
new List<float>{
0.25f, 0.25f, 0.75f, 0.75f
},
TileMode.repeated
);
}
public readonly OverflowSide side;
public readonly float size;
Color black = new Color(0xBF000000);
Color yellow = new Color(0xBFFFFF00);
Paint indicatorPaint = new Paint();
public void paint(Canvas canvas, Size size) {
var bottomOverflow = OverflowSide.bottom == side;
var width = bottomOverflow ? size.width : this.size;
var height = !bottomOverflow ? size.height : this.size;
var left = bottomOverflow ? 0.0f : size.width - width;
var top = side == OverflowSide.right ? 0.0f : size.height - height;
var rect = Rect.fromLTWH(left, top, width, height);
canvas.drawRect(rect, indicatorPaint);
}
public bool shouldRepaint(CustomPainter oldDelegate) {
return oldDelegate is OverflowIndicatorPainter &&
(side != ((OverflowIndicatorPainter)oldDelegate).side || size != ((OverflowIndicatorPainter)oldDelegate).size);
}
// [!!!] Not Implemented
public bool? hitTest(Offset position)
{
throw new System.NotImplementedException();
}
public void addListener(VoidCallback listener)
{
throw new System.NotImplementedException();
}
public void removeListener(VoidCallback listener)
{
throw new System.NotImplementedException();
}
}
}

218
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/theme.cs


using uiwidgets;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public class ThemeUtils
{
public static readonly float margin = 8.0f;
public static readonly float arrowHeadSize = 8.0f;
public static readonly float arrowMargin = 4.0f;
public static readonly float arrowStrokeWidth = 1.5f;
/// Hardcoded sizes for scaling the flex children widget properly.
public static readonly float minRenderWidth = 250.0f;
public static readonly float minRenderHeight = 250.0f;
public static readonly float minPadding = 2.0f;
public static readonly float overflowTextHorizontalPadding = 8.0f;
/// The size to shrink a widget by when animating it in.
public static readonly float entranceMargin = 50.0f;
public static readonly float defaultMaxRenderWidth = 400.0f;
public static readonly float defaultMaxRenderHeight = 400.0f;
public static readonly float widgetTitleMaxWidthPercentage = 0.75f;
/// Hardcoded arrow size respective to its cross axis (because it's unconstrained).
public static readonly float heightAndConstraintIndicatorSize = 48.0f;
public static readonly float widthAndConstraintIndicatorSize = 56.0f;
public static readonly float mainAxisArrowIndicatorSize = 48.0f;
public static readonly float crossAxisArrowIndicatorSize = 48.0f;
public static readonly float heightOnlyIndicatorSize = 72.0f;
public static readonly float widthOnlyIndicatorSize = 32.0f;
/// Minimum size to display width/height inside the arrow
public static readonly float minWidthToDisplayWidthInsideArrow = 200.0f;
public static readonly float minHeightToDisplayHeightInsideArrow = 200.0f;
public static readonly float largeTextScaleFactor = 1.2f;
public static readonly float smallTextScaleFactor = 0.8f;
/// Height for limiting asset image (selected one in the drop down).
public static readonly float axisAlignmentAssetImageHeight = 24.0f;
/// Width for limiting asset image (when drop down menu is open for the vertical).
public static readonly float axisAlignmentAssetImageWidth = 96.0f;
public static readonly float dropdownMaxSize = 220.0f;
public static readonly float minHeightToAllowTruncating = 375.0f;
public static readonly float minWidthToAllowTruncating = 375.0f;
// Story of Layout colors
public static readonly Color mainAxisLightColor = new Color(0xff2c5daa);
public static readonly Color mainAxisDarkColor = new Color(0xff2c5daa);
public static readonly Color rowColor = new Color(0xff2c5daa);
public static readonly Color columnColor = new Color(0xff77974d);
public static readonly Color regularWidgetColor = new Color(0xff88b1de);
public static readonly Color selectedWidgetColor = new Color(0xff36c6f4);
public static readonly Color textColor = new Color(0xff55767f);
public static readonly Color emphasizedTextColor = new Color(0xff009aca);
public static readonly Color crossAxisLightColor = new Color(0xff8ac652);
public static readonly Color crossAxisDarkColor = new Color(0xff8ac652);
public static readonly Color mainAxisTextColorLight = new Color(0xFF1375bc);
public static readonly Color mainAxisTextColorDark = new Color(0xFF1375bc);
public static readonly Color crossAxisTextColorLight = new Color(0xFF66672C);
public static readonly Color crossAxisTextColorsDark = new Color(0xFFB3D25A);
public static readonly Color overflowBackgroundColorDark = new Color(0xFFB00020);
public static readonly Color overflowBackgroundColorLight = new Color(0xFFB00020);
public static readonly Color overflowTextColorDark = new Color(0xfff5846b);
public static readonly Color overflowTextColorLight = new Color(0xffdea089);
public static readonly Color backgroundColorSelectedDark = new Color(
0x4d474747); // TODO(jacobr): we would like Color(0x4dedeeef) but that makes the background show through.
public static readonly Color backgroundColorSelectedLight = new Color(0x4dedeeef);
public static Color mainAxisColor
{
get
{
return CommonThemeUtils.isLight? mainAxisLightColor : mainAxisDarkColor;
}
}
public static Color widgetNameColor
{
get
{
return CommonThemeUtils.isLight? Colors.white : Colors.black;
}
}
public static Color crossAxisColor
{
get
{
return CommonThemeUtils.isLight? crossAxisLightColor : crossAxisDarkColor;
}
}
public static Color mainAxisTextColor
{
get
{
return CommonThemeUtils.isLight? mainAxisTextColorLight : mainAxisTextColorDark;
}
}
public static Color crossAxisTextColor
{
get
{
return CommonThemeUtils.isLight? crossAxisTextColorLight : crossAxisTextColorsDark;
}
}
public static Color overflowBackgroundColor
{
get
{
return CommonThemeUtils.isLight? overflowBackgroundColorLight : overflowBackgroundColorDark;
}
}
public static Color overflowTextColor
{
get
{
return CommonThemeUtils.isLight? overflowTextColorLight : overflowTextColorDark;
}
}
public static Color backgroundColorSelected
{
get
{
return CommonThemeUtils.isLight? backgroundColorSelectedLight : backgroundColorSelectedDark;
}
}
public static Color backgroundColor
{
get
{
return CommonThemeUtils.isLight? backgroundColorLight : backgroundColorDark;
}
}
public static Color unconstrainedColor
{
get
{
return CommonThemeUtils.isLight? unconstrainedLightColor : unconstrainedDarkColor;
}
}
public static readonly Color backgroundColorDark = new Color(0xff30302f);
public static readonly Color backgroundColorLight = new Color(0xffffffff);
public static readonly Color unconstrainedDarkColor = new Color(0xffdea089);
public static readonly Color unconstrainedLightColor = new Color(0xfff5846b);
public static readonly Color widthIndicatorColor = textColor;
public static readonly Color heightIndicatorColor = textColor;
public static readonly string negativeSpaceDarkAssetName =
"assets/img/layout_explorer/negative_space_dark.png";
public static readonly string negativeSpaceLightAssetName =
"assets/img/layout_explorer/negative_space_light.png";
public static TextStyle dimensionIndicatorTextStyle = new TextStyle(
height: 1.0f,
letterSpacing: 1.1f,
color: emphasizedTextColor
);
public static TextStyle overflowingDimensionIndicatorTextStyle(ColorScheme colorScheme)
{
return dimensionIndicatorTextStyle.merge(
new TextStyle(
fontWeight: FontWeight.bold,
color: overflowTextColor
)
);
}
public static Widget buildUnderline() {
return new Container(
height: 1.0f,
decoration: new BoxDecoration(
border: new Border(
bottom: new BorderSide(
color: textColor,
width: 0.0f
)
)
)
);
}
}
}

193
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/widget_constraints.cs


using System.Collections.Generic;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public class VisualizeWidthAndHeightWithConstraints : StatelessWidget {
public VisualizeWidthAndHeightWithConstraints(
LayoutProperties properties = null,
float? arrowHeadSize = null,
Widget child = null,
bool warnIfUnconstrained = true
)
{
this.properties = properties;
this.child = child;
this.arrowHeadSize = arrowHeadSize?? CommonThemeUtils.defaultIconSize;
this.warnIfUnconstrained = warnIfUnconstrained;
}
public readonly Widget child;
public readonly LayoutProperties properties;
public readonly float? arrowHeadSize;
public readonly bool warnIfUnconstrained;
public override Widget build(BuildContext context) {
var showChildrenWidthsSum =
properties is FlexLayoutProperties && properties.isOverflowWidth;
var bottomHeight = ThemeUtils.widthAndConstraintIndicatorSize;
var rightWidth = ThemeUtils.heightAndConstraintIndicatorSize;
var colorScheme = Theme.of(context).colorScheme;
var showOverflowHeight =
properties is FlexLayoutProperties && properties.isOverflowHeight;
List<InlineSpan> spans = new List<InlineSpan>();
spans.Add(new TextSpan(
text: $"{properties.describeHeight()}"
));
if (properties.constraints != null)
{
if (!showOverflowHeight) spans.Add(new TextSpan(text: "\n"));
spans.Add(new TextSpan(
text: $" ({properties.describeHeightConstraints()})",
style: properties.constraints.hasBoundedHeight ||
!warnIfUnconstrained
? null
: new TextStyle(
color: ThemeUtils.unconstrainedColor
)
));
if (showOverflowHeight)
{
spans.Add(new TextSpan(
text: "\nchildren take: " +
$"{InspectorDataModelsUtils.sum(properties.childrenHeights)}"
));
}
}
var heightDescription = new RotatedBox(
quarterTurns: 1,
child: Dimension.dimensionDescription(
new TextSpan(
children: spans
),
properties.isOverflowHeight,
colorScheme)
);
var right = new Container(
margin: EdgeInsets.only(
top: ThemeUtils.margin,
left: ThemeUtils.margin,
bottom: bottomHeight,
right: ThemeUtils.minPadding // custom margin for not sticking to the corner
),
child: new LayoutBuilder(builder: (context2, constraints) => {
var displayHeightOutsideArrow =
constraints.maxHeight < ThemeUtils.minHeightToDisplayHeightInsideArrow;
List<Widget> widgets = new List<Widget>();
widgets.Add(new Truncateable(
truncate: !displayHeightOutsideArrow,
child: new Container(
margin: EdgeInsets.symmetric(horizontal: ThemeUtils.arrowMargin),
child: new ArrowWrapper(
arrowColor: ThemeUtils.heightIndicatorColor,
arrowStrokeWidth: ThemeUtils.arrowStrokeWidth,
arrowHeadSize: arrowHeadSize,
direction: Axis.vertical,
child: displayHeightOutsideArrow ? null : heightDescription
)
)
));
if (displayHeightOutsideArrow)
{
widgets.Add(new Flexible(
child: heightDescription
));
}
return new Row(
children: widgets
);
})
);
List<InlineSpan> spams2 = new List<InlineSpan>();
spams2.Add(new TextSpan(text: $"{properties.describeWidth()}; "));
if (properties.constraints != null)
{
if (!showChildrenWidthsSum) spams2.Add(new TextSpan(text: "\n"));
spams2.Add(new TextSpan(
text: $"({properties.describeWidthConstraints()})",
style:
properties.constraints.hasBoundedWidth || !warnIfUnconstrained
? null
: new TextStyle(color: ThemeUtils.unconstrainedColor)
));
}
if (showChildrenWidthsSum)
{
spams2.Add(new TextSpan(
text: "\nchildren take " +
$"{InspectorDataModelsUtils.sum(properties.childrenWidths)}"
));
}
var widthDescription = Dimension.dimensionDescription(
new TextSpan(
children: spams2
),
properties.isOverflowWidth,
colorScheme
);
var bottom = new Container(
margin: EdgeInsets.only(
top: ThemeUtils.margin,
left: ThemeUtils.margin,
right: rightWidth,
bottom: ThemeUtils.minPadding
),
child: new LayoutBuilder(builder: (context3, constraints) => {
var maxWidth = constraints.maxWidth;
var displayWidthOutsideArrow =
maxWidth < ThemeUtils.minWidthToDisplayWidthInsideArrow;
List<Widget> widgets = new List<Widget>();
widgets.Add(new Truncateable(
truncate: !displayWidthOutsideArrow,
child: new Container(
margin: EdgeInsets.symmetric(vertical: ThemeUtils.arrowMargin),
child: new ArrowWrapper(
arrowColor: ThemeUtils.widthIndicatorColor,
arrowHeadSize: arrowHeadSize,
arrowStrokeWidth: ThemeUtils.arrowStrokeWidth,
direction: Axis.horizontal,
child: displayWidthOutsideArrow ? null : widthDescription
)
)
));
if (displayWidthOutsideArrow)
{
widgets.Add(new Flexible(
child: new Container(
padding: EdgeInsets.only(top: ThemeUtils.minPadding),
child: widthDescription
)
));
}
return new Column(
children: widgets
);
})
);
return new BorderLayout(
center: child,
right: right,
rightWidth: rightWidth,
bottom: bottom,
bottomHeight: bottomHeight
);
}
}
}

345
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/layout_explorer_widget.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public delegate Future OnSelectionChangedCallback();
public class LayoutExplorerWidgetUtils
{
const float maxRequestsPerSecond = 3.0f;
}
public abstract class LayoutExplorerWidget : StatefulWidget {
public LayoutExplorerWidget(
InspectorController inspectorController,
Key key
) : base(key: key)
{
this.inspectorController = inspectorController;
}
public readonly InspectorController inspectorController;
}
public abstract class LayoutExplorerWidgetState<W, L> :
InspectorServiceClient where W : LayoutExplorerWidget where L : LayoutProperties // TickerProviderStateMixin, InspectorServiceClient where W : LayoutExplorerWidget where L : LayoutProperties
{
public LayoutExplorerWidgetState() {
_onSelectionChangedCallback = onSelectionChanged;
}
public AnimationController entranceController;
public CurvedAnimation entranceCurve;
public AnimationController changeController;
public CurvedAnimation changeAnimation;
L _previousProperties;
L _properties;
InspectorObjectGroupManager objectGroupManager;
public AnimatedLayoutProperties<L> animatedProperties
{
get
{
return _animatedProperties;
}
}
AnimatedLayoutProperties<L> _animatedProperties;
public L properties
{
get
{
return _previousProperties ?? _animatedProperties ?? _properties;
}
}
public RemoteDiagnosticsNode selectedNode
{
get
{
return null; //inspectorController?.selectedNode?.value?.diagnostic;
}
}
public InspectorController inspectorController
{
get
{
return null; //widget.inspectorController;
}
}
public InspectorService inspectorService
{
get
{
return inspectorController?.inspectorService;
}
}
//RateLimiter rateLimiter;
public OnSelectionChangedCallback _onSelectionChangedCallback;
Future onSelectionChanged() {
if (!mounted) return async2.Future.value(); //if (!mounted) return async2.Future.value();
if (!shouldDisplay(selectedNode)) {
return async2.Future.value();
}
var prevRootId = id(_properties?.node);
var newRootId = id(getRoot(selectedNode));
var shouldFetch = prevRootId != newRootId;
if (shouldFetch) {
_dirty = false;
fetchLayoutProperties().then((newSelection) =>
{
_setProperties(newSelection);
});
} else {
updateHighlighted(_properties);
}
return async2.Future.value();
}
/// Whether this layout explorer can work with this kind of node.
public abstract bool shouldDisplay(RemoteDiagnosticsNode node);
public Size size
{
get
{
return properties.size;
}
}
public List<LayoutProperties> children
{
get
{
return properties.displayChildren;
}
}
public LayoutProperties highlighted;
/// Returns the root widget to show.
///
/// For cases such as Flex widgets or in the future ListView widgets we may
/// want to show the layout for all widgets under a root that is the parent
/// of the current widget.
public abstract RemoteDiagnosticsNode getRoot(RemoteDiagnosticsNode node);
Future<L> fetchLayoutProperties() {
objectGroupManager?.cancelNext();
var nextObjectGroup = objectGroupManager.next;
var res = nextObjectGroup.getLayoutExplorerNode(
getRoot(selectedNode)
).then((node)=>{computeLayoutProperties(node);});
if (!nextObjectGroup.disposed) {
D.assert(objectGroupManager.next == nextObjectGroup);
objectGroupManager.promoteNext();
}
return res;
}
public abstract L computeLayoutProperties(RemoteDiagnosticsNode node);
public abstract AnimatedLayoutProperties<L> computeAnimatedProperties(L nextProperties);
public abstract void updateHighlighted(L newProperties);
string id(RemoteDiagnosticsNode node) => node?.dartDiagnosticRef?.id;
void _registerInspectorControllerService() {
inspectorController?.selectedNode?.addListener(_onSelectionChangedCallback);
inspectorService?.addClient(this);
}
void _unregisterInspectorControllerService() {
inspectorController?.selectedNode
?.removeListener(_onSelectionChangedCallback);
inspectorService?.removeClient(this);
}
public override void initState() {
base.initState();
rateLimiter = RateLimiter(maxRequestsPerSecond, refresh);
_registerInspectorControllerService();
_initAnimationStates();
_updateObjectGroupManager();
// TODO(jacobr): put inspector controller in Controllers and
// update on didChangeDependencies.
_animateProperties();
}
public override void didUpdateWidget(W oldWidget) {
base.didUpdateWidget(oldWidget);
_updateObjectGroupManager();
_animateProperties();
if (oldWidget.inspectorController != inspectorController) {
_unregisterInspectorControllerService();
_registerInspectorControllerService();
}
}
public override void dispose() {
entranceController.dispose();
changeController.dispose();
_unregisterInspectorControllerService();
base.dispose();
}
void _animateProperties() {
if (_animatedProperties != null) {
changeController.forward();
}
if (_previousProperties != null) {
entranceController.reverse();
} else {
entranceController.forward();
}
}
Future setSelectionInspector(RemoteDiagnosticsNode node) {
node.inspectorService.then((service) =>
{
service.setSelectionInspector(node.valueRef, false);
});
return null;
}
// update selected widget and trigger selection listener event to change focus.
public void refreshSelection(RemoteDiagnosticsNode node) {
inspectorController.refreshSelection(node, node, true);
}
public Future onTap(LayoutProperties properties) {
setState(() => highlighted = properties);
setSelectionInspector(properties.node);
return null;
}
public void onDoubleTap(LayoutProperties properties) {
refreshSelection(properties.node);
}
Future refresh() {
if (!_dirty) return null;
_dirty = false;
fetchLayoutProperties().then((updatedProperties) =>
{
if (updatedProperties != null) _changeProperties(updatedProperties);
});
return null;
}
void _changeProperties(L nextProperties) {
if (!mounted || nextProperties == null) return;
updateHighlighted(nextProperties);
setState(() => {
_animatedProperties = computeAnimatedProperties(nextProperties);
changeController.forward(from: 0.0f);
});
}
void _setProperties(L newProperties) {
if (!mounted) return;
updateHighlighted(newProperties);
if (_properties == newProperties) {
return;
}
setState(() => {
_previousProperties = _previousProperties ?? _properties;
_properties = newProperties;
});
_animateProperties();
}
void _initAnimationStates()
{
entranceController = longAnimationController(
this
);
entranceController.addStatusListener((status) => {
if (status == AnimationStatus.dismissed) {
setState(() => {
_previousProperties = null;
entranceController.forward();
});
}
});
entranceCurve = defaultCurvedAnimation(entranceController);
changeController = longAnimationController(this);
changeController.addStatusListener((status) => {
if (status == AnimationStatus.completed) {
setState(() => {
_properties = _animatedProperties.end;
_animatedProperties = null;
changeController._value = 0.0f;
});
}
});
changeAnimation = defaultCurvedAnimation(changeController);
}
void _updateObjectGroupManager() {
var service = inspectorController.inspectorService;
if (service != objectGroupManager?.inspectorService) {
objectGroupManager = InspectorObjectGroupManager(
service,
"flex-layout"
);
}
onSelectionChanged();
}
bool _dirty = false;
public override void onFlutterFrame() {
if (!mounted) return;
if (_dirty) {
rateLimiter.scheduleRequest();
}
}
// TODO(albertusangga): Investigate why onForceRefresh is not getting called.
public override Future<Object> onForceRefresh()
{
fetchLayoutProperties().then((v) =>
{
_setProperties(v);
});
return null;
}
public override Future onInspectorSelectionChanged() {
return null;
}
/// Register callback to be executed once Flutter frame is ready.
public void markAsDirty() {
_dirty = true;
}
}
}

569
com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/utils.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.DevTools;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using Image = Unity.UIWidgets.widgets.Image;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.DevTools.inspector.layout_explorer.ui
{
public class BorderLayout : StatelessWidget {
public BorderLayout(
Key key = null,
Widget left= null,
float? leftWidth = null,
Widget top= null,
float? topHeight = null,
Widget right= null,
float? rightWidth = null,
Widget bottom = null,
float? bottomHeight = null,
Widget center = null
) : base(key: key)
{
D.assert(left != null ||
top != null ||
right != null ||
bottom != null ||
center != null);
this.left = left;
this.leftWidth = leftWidth;
this.top = top;
this.topHeight = topHeight;
this.right = right;
this.rightWidth = rightWidth;
this.bottom = bottom;
this.bottomHeight = bottomHeight;
this.center = center;
}
public readonly Widget center;
public readonly Widget top;
public readonly Widget left;
public readonly Widget right;
public readonly Widget bottom;
public readonly float? leftWidth;
public readonly float? rightWidth;
public readonly float? topHeight;
public readonly float? bottomHeight;
CrossAxisAlignment crossAxisAlignment {
get
{
if (left != null && right != null) {
return CrossAxisAlignment.center;
} else if (left == null && right != null) {
return CrossAxisAlignment.start;
} else if (left != null && right == null) {
return CrossAxisAlignment.end;
} else {
return CrossAxisAlignment.start;
}
}
}
public override Widget build(BuildContext context)
{
List<Widget> widgets = new List<Widget>();
widgets.Add(new Center(
child: new Container(
margin: EdgeInsets.only(
left: leftWidth ?? 0,
right: rightWidth ?? 0,
top: topHeight ?? 0,
bottom: bottomHeight ?? 0
),
child: center
)
));
if (top != null)
{
widgets.Add(new Align(
alignment: Alignment.topCenter,
child: new Container(height: topHeight, child: top)
));
}
if (left != null)
{
widgets.Add(new Align(
alignment: Alignment.centerLeft,
child: new Container(width: leftWidth, child: left)
));
}
if (right != null)
{
widgets.Add(new Align(
alignment: Alignment.centerRight,
child: new Container(width: rightWidth, child: right)
));
}
if (bottom != null)
{
widgets.Add(new Align(
alignment: Alignment.bottomCenter,
child: new Container(height: bottomHeight, child: bottom)
));
}
return new Stack(
children: widgets
);
}
}
public class Truncateable : StatelessWidget {
public Truncateable(Key key = null , bool truncate = false, Widget child = null) : base(key: key)
{
this.truncate = truncate;
this.child = child;
}
public readonly Widget child;
public readonly bool truncate;
public override Widget build(BuildContext context) {
return new Flexible(flex: truncate ? 1 : 0, child: child);
}
}
public class WidgetVisualizer : StatelessWidget {
public WidgetVisualizer(
Key key = null,
bool isSelected = false,
string title = null,
Widget hint = null,
LayoutProperties layoutProperties = null,
Widget child = null,
OverflowSide? overflowSide = null,
bool largeTitle = false
) : base(key: key)
{
D.assert(title != null);
this.child = child;
this.isSelected = isSelected;
this.title = title;
this.hint = hint;
this.layoutProperties = layoutProperties;
this.overflowSide = overflowSide;
this.largeTitle = largeTitle;
}
public readonly LayoutProperties layoutProperties;
public readonly string title;
public readonly Widget child;
public readonly Widget hint;
public readonly bool isSelected;
public readonly bool largeTitle;
public readonly OverflowSide? overflowSide;
public static readonly float overflowIndicatorSize = 20.0f;
bool drawOverflow
{
get
{
return overflowSide != null;
}
}
public override Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var properties = layoutProperties;
Color borderColor = ThemeUtils.regularWidgetColor;
if (properties is FlexLayoutProperties) {
borderColor =
((FlexLayoutProperties)properties)?.direction == Axis.horizontal ? ThemeUtils.rowColor : ThemeUtils.columnColor;
}
if (isSelected) {
borderColor = ThemeUtils.selectedWidgetColor;
}
List<Widget> widgets1 = new List<Widget>();
widgets1.Add(new Flexible(
child: new Container(
constraints: new BoxConstraints(
maxWidth: largeTitle
? ThemeUtils.defaultMaxRenderWidth
: ThemeUtils.minRenderWidth *
ThemeUtils.widgetTitleMaxWidthPercentage),
child: new Center(
child: new Text(
title,
style:
new TextStyle(color: ThemeUtils.widgetNameColor),
overflow: TextOverflow.ellipsis
)
),
decoration: new BoxDecoration(color: borderColor),
padding: EdgeInsets.all(4.0f)
)
));
if (hint != null)
{
widgets1.Add(new Flexible(child: hint));
}
List<Widget> widgets2 = new List<Widget>();
widgets2.Add(new IntrinsicHeight(
child: new Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: widgets1
)
));
if (child != null) widgets2.Add(new Expanded(child: child));
List<Widget> widgets3 = new List<Widget>();
if (drawOverflow)
{
widgets3.Add(Positioned.fill(
child: new CustomPaint(
painter: new OverflowIndicatorPainter(
overflowSide.Value,
overflowIndicatorSize
)
)
));
widgets3.Add(new Container(
margin: EdgeInsets.only(
right: overflowSide == OverflowSide.right
? overflowIndicatorSize
: 0.0f,
bottom: overflowSide == OverflowSide.bottom
? overflowIndicatorSize
: 0.0f
),
color: isSelected ? ThemeUtils.backgroundColorSelected : null,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: widgets2
)
));
}
return new Container(
child: new Stack(
children: widgets3
),
decoration: new BoxDecoration(
border: Border.all(
color: borderColor
),
color: Color.black //theme.canvasColor.darken()
)
);
}
}
public class AnimatedLayoutProperties<T> : LayoutProperties where T : LayoutProperties
{
public AnimatedLayoutProperties(T begin, T end, Animation<float> animation)
{
D.assert(begin != null);
D.assert(end != null);
D.assert(begin.children?.Count == end.children?.Count);
for (var i = 0; i < begin.children.Count; i++)
_children.Add(new AnimatedLayoutProperties<LayoutProperties>(
begin.children[i],
end.children[i],
animation
));
this.begin = begin;
this.end = end;
this.animation = animation;
}
public readonly T begin;
public readonly T end;
public readonly Animation<float> animation;
public readonly List<LayoutProperties> _children;
public new LayoutProperties parent
{
get
{
return end.parent;
}
set
{
end.parent = value;
}
}
public new List<LayoutProperties> children {
get
{
return _children;
}
}
List<float?> _lerpList(List<float?> l1, List<float?> l2) {
D.assert(l1.Count == l2.Count);
List<float?> result = new List<float?>();
for (var i = 0; i < children.Count; i++)
{
result.Add(utils.lerpFloat(l1[i], l2[i], animation.value));
}
return result;
}
public List<float?> childrenDimensions(Axis axis) { // public override List<float?> childrenDimensions(Axis axis)
var beginDimensions = begin.childrenDimensions(axis);
var endDimensions = end.childrenDimensions(axis);
return _lerpList(beginDimensions, endDimensions);
}
public new List<float?> childrenHeights
{
get
{
return _lerpList(begin.childrenHeights, end.childrenHeights);
}
}
public new List<float?> childrenWidths
{
get
{
return _lerpList(begin.childrenWidths, end.childrenWidths);
}
}
public new BoxConstraints constraints {
get
{
try {
return BoxConstraints.lerp(
begin.constraints, end.constraints, animation.value);
} catch (Exception e) {
return end.constraints;
}
}
}
public string describeWidthConstraints() { // public override string describeWidthConstraints()
return constraints.hasBoundedWidth
? LayoutProperties.describeAxis(
constraints.minWidth, constraints.maxWidth, "w")
: "w=unconstrained";
}
public string describeHeightConstraints() { // public override string describeHeightConstraints()
return constraints.hasBoundedHeight
? LayoutProperties.describeAxis(
constraints.minHeight, constraints.maxHeight, "h")
: "h=unconstrained";
}
public string describeWidth() //public override string describeWidth()
{
return $"w={size.width}";
}
public new string describeHeight()
{
return $"h={size.height}";
}
public new string description
{
get
{
return end.description;
}
}
public float? dimension(Axis axis) { // public override float? dimension(Axis axis)
return utils.lerpFloat(
begin.dimension(axis),
end.dimension(axis),
animation.value
);
}
public new float? flexFactor
{
get
{
return utils.lerpFloat(begin.flexFactor, end.flexFactor, animation.value);
}
}
public new bool hasChildren
{
get
{
return children.isNotEmpty();
}
}
public new float? height
{
get
{
return size.height;
}
}
public new bool isFlex
{
get
{
return begin.isFlex.Value && end.isFlex.Value;
}
}
public new RemoteDiagnosticsNode node
{
get
{
return end.node;
}
}
public new Size size
{
get
{
return Size.lerp(begin.size, end.size, animation.value);
}
}
public new int totalChildren
{
get
{
return end.totalChildren;
}
}
public new float? width
{
get
{
return size.width;
}
}
public new bool hasFlexFactor
{
get
{
return begin.hasFlexFactor && end.hasFlexFactor;
}
}
public LayoutProperties copyWith( //public override LayoutProperties copyWith
List<LayoutProperties> children = null,
BoxConstraints constraints = null,
string description = null,
float? flexFactor = null,
FlexFit? flexFit = null,
bool? isFlex = null,
Size size = null
)
{
return new LayoutProperties();
// return LayoutProperties.values(
// node: node,
// children: children ?? this.children,
// constraints: constraints ?? this.constraints,
// description: description ?? this.description,
// flexFactor: flexFactor ?? this.flexFactor,
// flexFit: flexFit ?? this.flexFit,
// isFlex: isFlex ?? this.isFlex,
// size: size ?? this.size
// );
}
public new bool isOverflowWidth
{
get
{
return end.isOverflowWidth;
}
}
public new bool isOverflowHeight
{
get
{
return end.isOverflowHeight;
}
}
public new FlexFit? flexFit
{
get
{
return end.flexFit;
}
}
public new List<LayoutProperties> displayChildren
{
get
{
return end.displayChildren;
}
}
}
public class LayoutExplorerBackground : StatelessWidget {
public LayoutExplorerBackground(
Key key = null,
ColorScheme colorScheme = null
) : base(key: key)
{
this.colorScheme = colorScheme;
}
public readonly ColorScheme colorScheme;
public override Widget build(BuildContext context) {
return Positioned.fill(
child: new Opacity(
opacity: CommonThemeUtils.isLight ? 0.3f : 0.2f,
child: Image.asset(
CommonThemeUtils.isLight
? ThemeUtils.negativeSpaceLightAssetName
: ThemeUtils.negativeSpaceDarkAssetName,
fit: BoxFit.none,
repeat: ImageRepeat.repeat,
alignment: Alignment.topLeft
)
)
);
}
}
}

21
com.unity.uiwidgets.devtools/Editor/listenable.cs


using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools
{
public class FixedValueListenable<T> : ValueListenable<T> {
public FixedValueListenable(T _value)
{
this._value = _value;
}
public readonly T _value;
public void addListener(VoidCallback listener){} //public override void addListener(VoidCallback listener){}
public void removeListener(VoidCallback listener){} //public override void removeListener(VoidCallback listener){}
public T value => _value; // public override T value => _value;
}
}

20
com.unity.uiwidgets.devtools/Editor/main.cs


using Unity.UIWidgets.DevTools.analytics;
using Unity.UIWidgets.DevTools.config_specific.ide_theme;
using Unity.UIWidgets.Editor;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools
{
public class Devetool : UIWidgetsEditorPanel
{
public IdeTheme ideTheme = null;
protected override void main()
{
ui_.runApp(new DevToolsApp(appUtils.defaultScreens, ideTheme, stub_provider.analyticsProvider));
}
}
}

290
com.unity.uiwidgets.devtools/Editor/screen.cs


using System;
using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.async2;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Rect = Unity.UIWidgets.ui.Rect;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.DevTools
{
public class ScreenUtils
{
public static bool shouldShowScreen(Screen screen) {
if (globals.offlineMode) {
return screen.worksOffline;
}
if (screen.requiresLibrary != null) {
if (!globals.serviceManager.isServiceAvailable ||
!globals.serviceManager.isolateManager.selectedIsolateAvailable.isCompleted ||
!globals.serviceManager.libraryUriAvailableNow(screen.requiresLibrary)) {
return false;
}
}
if (screen.requiresDartVm) {
if (!globals.serviceManager.isServiceAvailable ||
!globals.serviceManager.connectedApp.isRunningOnDartVM) {
return false;
}
}
if (screen.requiresDebugBuild) {
if (!globals.serviceManager.isServiceAvailable ||
globals.serviceManager.connectedApp.isProfileBuildNow) {
return false;
}
}
if (screen.requiresVmDeveloperMode) {
if (!globals.preferences.vmDeveloperModeEnabled.value) {
return false;
}
}
return true;
}
}
public abstract class Screen {
public Screen(
string screenId,
string title = null,
IconData icon = null,
Key tabKey = null,
string requiresLibrary = null,
bool requiresDartVm = false,
bool requiresDebugBuild = false,
bool requiresVmDeveloperMode = false,
bool worksOffline = false
)
{
this.screenId = screenId;
this.title = title;
this.icon = icon;
this.tabKey = tabKey;
this.requiresLibrary = requiresLibrary;
this.requiresDartVm = requiresDartVm;
this.requiresDebugBuild = requiresDebugBuild;
this.requiresVmDeveloperMode = requiresVmDeveloperMode;
this.worksOffline = worksOffline;
}
public Screen(
string id = null,
string requiresLibrary = null,
bool requiresDartVm = false,
bool requiresDebugBuild = false,
bool requiresVmDeveloperMode = false,
bool worksOffline = false,
string title = null,
IconData icon = null,
Key tabKey = null
)
{
this.screenId = id;
this.requiresLibrary = requiresLibrary;
this.requiresDartVm = requiresDartVm;
this.requiresDebugBuild = requiresDebugBuild;
this.requiresVmDeveloperMode = requiresVmDeveloperMode;
this.worksOffline = worksOffline;
this.title = title;
this.icon = icon;
this.tabKey = tabKey;
}
public readonly string screenId;
public readonly string title;
public readonly IconData icon;
public readonly Key tabKey;
public readonly string requiresLibrary;
public readonly bool requiresDartVm;
public readonly bool requiresDebugBuild;
public readonly bool requiresVmDeveloperMode;
public readonly bool worksOffline;
// ValueListenable<bool> showIsolateSelector
// {
// get
// {
// return FixedValueListenable<bool>(false);
// }
// }
string docPageId
{
get
{
return null;
}
}
int badgeCount
{
get
{
return 0;
}
}
Widget buildTab(BuildContext context) {
return new ValueListenableBuilder<int>(
valueListenable:
globals.serviceManager.errorBadgeManager.errorCountNotifier(screenId),
builder: (context2, count, _) => {
var tab = new Tab(
key: tabKey,
child: new Row(
children: new List<Widget>{
new Icon(icon, size: CommonThemeUtils.defaultIconSize),
new Padding(
padding: EdgeInsets.only(left: CommonThemeUtils.denseSpacing),
child: new Text(title)
),
}
)
);
if (count > 0) {
var painter = new TextPainter(
text: new TextSpan(
text: title
),
textDirection: TextDirection.ltr
);
var titleWidth = painter.width;
return new LayoutBuilder(
builder: (context3, constraints) =>{
return new Stack(
children: new List<Widget>{
new CustomPaint(
size: new Size(CommonThemeUtils.defaultIconSize + CommonThemeUtils.denseSpacing + titleWidth, 0),
painter: new BadgePainter(number: count)
),
tab,
}
);
}
);
}
return tab;
}
);
}
public abstract Widget build(BuildContext context);
Widget buildStatus(BuildContext context, TextTheme textTheme) {
return null;
}
}
// mixin OfflineScreenMixin<T extends StatefulWidget, U> on State<T> {
// bool loadingOfflineData
// {
// get
// {
// return _loadingOfflineData;
// }
// }
//
// bool _loadingOfflineData = false;
//
// bool shouldLoadOfflineData();
//
// FutureOr processOfflineData(U offlineData);
//
// Future loadOfflineData(U offlineData) {
// setState(() => {
// _loadingOfflineData = true;
// });
// processOfflineData(offlineData).then(() =>
// {
// setState(() => {
// _loadingOfflineData = false;
// });
// });
// }
// }
public class BadgePainter : CustomPainter {
public BadgePainter(int? number = null)
{
this.number = number;
}
public readonly int? number;
public void paint(Canvas canvas, Size size)
{
Paint paint = new Paint();
paint.color = CommonThemeUtils.devtoolsError;
paint.style = PaintingStyle.fill;
TextPainter countPainter = new TextPainter(
text: new TextSpan(
text: $"{number}",
style: new TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold
)
),
textDirection: TextDirection.ltr
);
countPainter.layout();
var badgeWidth = Mathf.Max(
CommonThemeUtils.defaultIconSize,
countPainter.width + CommonThemeUtils.denseSpacing
);
canvas.drawOval(
Rect.fromLTWH(size.width, 0, badgeWidth, CommonThemeUtils.defaultIconSize),
paint
);
countPainter.paint(
canvas,
new Offset(size.width + (badgeWidth - countPainter.width) / 2, 0)
);
}
public bool shouldRepaint(CustomPainter oldDelegate) {
if (oldDelegate is BadgePainter) {
return number != ((BadgePainter)oldDelegate).number;
}
return true;
}
public bool? hitTest(Offset position)
{
throw new NotImplementedException();
}
public void addListener(VoidCallback listener)
{
throw new NotImplementedException();
}
public void removeListener(VoidCallback listener)
{
throw new NotImplementedException();
}
}
}

59
com.unity.uiwidgets.devtools/Editor/theme.cs


using System;
using uiwidgets;
using Unity.UIWidgets.DevTools.config_specific.ide_theme;
using Unity.UIWidgets.DevTools.inspector.layout_explorer.ui;
using Unity.UIWidgets.material;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
namespace Unity.UIWidgets.DevTools
{
public class CommonThemeUtils
{
public static readonly float denseSpacing = 8.0f;
public static readonly float defaultIconSize = 16.0f;
public static readonly TimeSpan defaultDuration = TimeSpan.FromMilliseconds(200);
public static bool isLight = false;
public static readonly NeverScrollableScrollPhysics defaultTabBarViewPhysics = new NeverScrollableScrollPhysics();
public static readonly Color devtoolsError = new Color(0xFFAF4054);
public static Color defaultBackground
{
get
{
return isLight ? Colors.white : Colors.black;
}
}
public static Color defaultForeground
{
get
{
return isLight ? Colors.black : Color.fromARGB(255, 187, 187, 187);
}
}
// ThemeData themeFor(
// bool isDarkTheme,
// IdeTheme ideTheme
// ) {
// // If the theme specifies a background color, use it to infer a theme.
// if (isValidDarkColor(ideTheme?.backgroundColor)) {
// return _darkTheme(ideTheme);
// } else if (isValidLightColor(ideTheme?.backgroundColor)) {
// return _lightTheme(ideTheme);
// }
//
// return isDarkTheme ? _darkTheme(ideTheme) : _lightTheme(ideTheme);
// }
// bool isValidDarkColor(Color color) {
// if (color == null) {
// return false;
// }
// return color.computeLuminance() <= _lightDarkLuminanceThreshold;
// }
}
}

311
com.unity.uiwidgets.devtools/Editor/ui/icons.cs


using System;
using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.DevTools.ui;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using Image = Unity.UIWidgets.widgets.Image;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.DevTools.ui
{
public class IconsUtils
{
public static Image createImageIcon(string url, float? size = null) {
return new Image(
image: new AssetImage(url),
height: size?? CommonThemeUtils.defaultIconSize,
width: size?? CommonThemeUtils.defaultIconSize
);
}
}
public class CustomIcon : StatelessWidget {
public CustomIcon(
IconKind kind = null,
string text = null,
bool isAbstract = false
)
{
this.kind = kind;
this.text = text;
this.isAbstract = isAbstract;
}
public readonly IconKind kind;
public readonly string text;
public readonly bool isAbstract;
public Image baseIcon
{
get
{
return kind.icon;
}
}
public override Widget build(BuildContext context) {
return new Container(
width: baseIcon.width,
height: baseIcon.height,
child: new Stack(
alignment: AlignmentDirectional.center,
children: new List<Widget>{
baseIcon,
new Text(
text,
textAlign: TextAlign.center,
style: new TextStyle(fontSize: 9, color: new Color(0xFF231F20))
),
}
)
);
}
}
public class CustomIconMaker {
public readonly Dictionary<string, CustomIcon> iconCache = new Dictionary<string, CustomIcon>();
public CustomIcon getCustomIcon(string fromText, IconKind kind = null, bool isAbstract = false)
{
kind = kind ?? IconKind.classIcon;
if (fromText?.isEmpty() != false)
{
return null;
}
string text = char.ToUpper(fromText[0]).ToString();
string mapKey = $"{text}_${kind.name}_{isAbstract}";
return iconCache.putIfAbsent(mapKey, () => {
return new CustomIcon(kind: kind, text: text, isAbstract: isAbstract);
});
}
public CustomIcon fromWidgetName(string name) {
if (name == null) {
return null;
}
bool isPrivate = name.StartsWith("_");
while (name.isNotEmpty() && !isAlphabetic(name[0])) {
name = name.Substring(1);
}
if (name.isEmpty()) {
return null;
}
return getCustomIcon(
name,
kind: isPrivate ? IconKind.method : IconKind.classIcon
);
}
public CustomIcon fromInfo(String name) {
if (name == null) {
return null;
}
if (name.isEmpty()) {
return null;
}
return getCustomIcon(name, kind: IconKind.info);
}
bool isAlphabetic(int ch) {
return (ch < '0' || ch > '9') &&
ch != '_' &&
ch != '$';
}
}
public class IconKind {
public IconKind(string name, Image icon, Image abstractIcon = null)
{
this.name = name;
this.icon = icon;
this.abstractIcon = abstractIcon ?? icon;
}
public static IconKind classIcon = new IconKind(
"class",
IconsUtils.createImageIcon("icons/custom/class.png"),
IconsUtils.createImageIcon("icons/custom/class_abstract.png")
);
public static IconKind field = new IconKind(
"fields",
IconsUtils.createImageIcon("icons/custom/fields.png")
);
public static IconKind _interface = new IconKind(
"interface",
IconsUtils.createImageIcon("icons/custom/interface.png")
);
public static IconKind method = new IconKind(
"method",
IconsUtils.createImageIcon("icons/custom/method.png"),
IconsUtils.createImageIcon("icons/custom/method_abstract.png")
);
public static IconKind property = new IconKind(
"property",
IconsUtils.createImageIcon("icons/custom/property.png")
);
public static IconKind info = new IconKind(
"info",
IconsUtils.createImageIcon("icons/custom/info.png")
);
public readonly string name;
public readonly Image icon;
public readonly Image abstractIcon;
}
public class ColorIcon : StatelessWidget {
public ColorIcon(Color color)
{
this.color = color;
}
public readonly Color color;
public override Widget build(BuildContext context) {
var colorScheme = Theme.of(context).colorScheme;
return new CustomPaint(
painter: new _ColorIconPainter(color, colorScheme),
size: new Size(CommonThemeUtils.defaultIconSize, CommonThemeUtils.defaultIconSize)
);
}
}
public class ColorIconMaker {
public readonly Dictionary<Color, ColorIcon> iconCache = new Dictionary<Color, ColorIcon>();
public ColorIcon getCustomIcon(Color color) {
return iconCache.putIfAbsent(color, () => new ColorIcon(color));
}
}
public class _ColorIconPainter : CustomPainter {
public _ColorIconPainter(Color color, ColorScheme colorScheme)
{
this.color = color;
this.colorScheme = colorScheme;
}
public readonly Color color;
public readonly ColorScheme colorScheme;
public const float iconMargin = 1;
public void paint(Canvas canvas, Size size)
{
var greyPaint = new Paint();
greyPaint.color = Colors.grey;
var iconRect = Rect.fromLTRB(
iconMargin,
iconMargin,
size.width - iconMargin,
size.height - iconMargin
);
Paint bgPaint = new Paint();
bgPaint.color = CommonThemeUtils.defaultBackground;
canvas.drawRect(
Rect.fromLTRB(
iconMargin,
iconMargin,
size.width - iconMargin,
size.height - iconMargin
),
bgPaint
);
canvas.drawRect(
Rect.fromLTRB(
iconMargin,
iconMargin,
size.width * 0.5f,
size.height * 0.5f
),
greyPaint
);
canvas.drawRect(
Rect.fromLTRB(
size.width * 0.5f,
size.height * 0.5f,
size.width - iconMargin,
size.height - iconMargin
),
greyPaint
);
canvas.drawRect(
iconRect,
new Paint()
{
color = color
}
);
Paint temp2 = new Paint();
canvas.drawRect(
iconRect,
new Paint()
{
style = PaintingStyle.stroke,
color = CommonThemeUtils.defaultForeground
}
);
}
public bool shouldRepaint(CustomPainter oldDelegate) {
// if (oldDelegate is _ColorIconPainter) {
// return ((_ColorIconPainter)oldDelegate).colorScheme.isLight != CommonThemeUtils.isLight;
// }
return true;
}
public bool? hitTest(Offset position)
{
throw new NotImplementedException();
}
public void addListener(VoidCallback listener)
{
throw new NotImplementedException();
}
public void removeListener(VoidCallback listener)
{
throw new NotImplementedException();
}
}
public class FlutterMaterialIcons {
public FlutterMaterialIcons(){}
public static Icon getIconForCodePoint(int charCode, ColorScheme colorScheme) {
return new Icon(new IconData(charCode), color: CommonThemeUtils.defaultForeground);
}
}
public class Octicons {
public static readonly IconData bug = new IconData(61714, fontFamily: "Octicons");
public static readonly IconData info = new IconData(61778, fontFamily: "Octicons");
public static readonly IconData deviceMobile = new IconData(61739, fontFamily: "Octicons");
public static readonly IconData fileZip = new IconData(61757, fontFamily: "Octicons");
public static readonly IconData clippy = new IconData(61724, fontFamily: "Octicons");
public static readonly IconData package = new IconData(61812, fontFamily: "Octicons");
public static readonly IconData dashboard = new IconData(61733, fontFamily: "Octicons");
public static readonly IconData pulse = new IconData(61823, fontFamily: "Octicons");
}
}

59
com.unity.uiwidgets.devtools/Editor/utils.cs


using System.Collections.Generic;
using System.Linq;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;
namespace Unity.UIWidgets.DevTools
{
public class utils
{
public static float safePositiveFloat(float value) {
if (value.isNaN()) return 0.0f;
return Mathf.Max(value, 0.0f);
}
public static float? lerpFloat(float? a, float? b, float t) {
if (a == b || (a?.isNaN() == true) && (b?.isNaN() == true))
return a;
a = a?? 0.0f;
b = b?? 0.0f;
D.assert(a.Value.isFinite(), ()=>"Cannot interpolate between finite and non-finite values");
D.assert(b.Value.isFinite(), ()=>"Cannot interpolate between finite and non-finite values");
D.assert(t.isFinite(), ()=>"t must be finite when interpolating between values");
return a * (1.0f - t) + b * t;
}
}
public class JsonUtils {
public JsonUtils(){}
public static string getStringMember(Dictionary<string, object> json, string memberName) {
// TODO(jacobr): should we handle non-string values with a reasonable
// toString differently?
return (string)json[memberName];
}
public static int getIntMember(Dictionary<string, object> json, string memberName)
{
if (!json.ContainsKey(memberName)) return -1;
return (int)(json.getOrDefault(memberName));
}
public static List<string> getValues(Dictionary<string, object> json, string member) {
List<object> values = json[member] as List<object>;
if (values == null || values.isEmpty()) {
return new List<string>();
}
return values.Cast<string>().ToList();
}
public static bool hasJsonData(string data) {
return data != null && data.isNotEmpty() && data != "null";
}
}
}

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialButtonSample.cs.meta


fileFormatVersion: 2
guid: 347e0378e419f46f2af69e1214f1ae5b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialInkWellSample.cs.meta


fileFormatVersion: 2
guid: b9e88c33e9f304d40bab9c27f1276033
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialNavigationBarSample.cs.meta


fileFormatVersion: 2
guid: cc34344049d222b468f2487b97076448
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialSliderSample.cs.meta


fileFormatVersion: 2
guid: 0152b6f575e6ca44baea965a23ebe498
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialTabBarSample.cs.meta


fileFormatVersion: 2
guid: fecd06f805d0b0f4ead134b1ad6a9a49
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialThemeSample.cs.meta


fileFormatVersion: 2
guid: b2a39cf0b83823749a13aebaf25cfb1b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/TableSample.cs.meta


fileFormatVersion: 2
guid: 1cba65c03fbf50f4cac974273a7c9445
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

11
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/ReorderableListSample.cs.meta


fileFormatVersion: 2
guid: f510d7fbd19431046bfb4b92c39b4da4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

47
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/BottomAppBarSample.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class BottomAppBarSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new BottomAppBarWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class BottomAppBarWidget : StatelessWidget {
public BottomAppBarWidget(Key key = null) : base(key) {
}
public override Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey,
bottomNavigationBar: new BottomAppBar(
child: new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: new List<Widget> {
new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.menu), onPressed: () => { }),
new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.account_balance),
onPressed: () => { })
})));
}
}
}

107
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/DividerAndButton.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class DividerAndButton : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MyApp());
}
class MyApp : StatelessWidget
{
public override Widget build(BuildContext context)
{
return new MaterialApp(
home: new DividerAndButtonSample()
);
}
}
public class DividerAndButtonSample : StatefulWidget {
public DividerAndButtonSample(Key key = null) : base(key) {
}
public override State createState() {
return new _DividerAndButtonState();
}
}
public class _DividerAndButtonState : State<DividerAndButtonSample> {
string title = "Hello";
string subtitle = "World";
TextEditingController controller = new TextEditingController("xxxxxx");
public override Widget build(BuildContext context) {
return new DividerTheme(
data: new DividerThemeData(
),
child: new Container(
height: 200,
padding: EdgeInsets.all(10),
decoration: new BoxDecoration(
color: new Color(0xFFEF1F7F),
border: Border.all(color: Color.fromARGB(255, 0xDF, 0x10, 0x70), width: 5),
borderRadius: BorderRadius.all(20)
),
child: new Center(
child: new Column(
children: new List<Widget>() {
new Text(this.title),
new Divider(),
new Text(this.subtitle),
new Divider(),
new Container(
width: 500,
decoration: new BoxDecoration(border: Border.all(new Color(0xFF00FF00), 1)),
child: new EditableText(
controller: this.controller,
focusNode: new FocusNode(),
style: new TextStyle(
fontSize: 18,
height: 1.5f,
color: new Color(0xFFFF89FD)),
cursorColor: Color.fromARGB(255, 0, 0, 0),
backgroundCursorColor: Colors.grey
)
),
new Divider(),
new ButtonBar(
children: new List<Widget> {
new FlatButton(
onPressed: () => {
this.setState(() => { this.title = this.controller.text; });
},
padding: EdgeInsets.all(5.0f),
child: new Center(
child: new Text("Set Title")
)
),
new RaisedButton(
onPressed: () => {
this.setState(() => { this.subtitle = this.controller.text; });
},
padding: EdgeInsets.all(5.0f),
child: new Center(
child: new Text("Set Subtitle")
)
)
}
)
}
)
)
));
}
}
}
}

204
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialAppBarSample.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class MaterialAppBarSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new MaterialAppBarWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class MaterialAppBarWidget : StatefulWidget {
public MaterialAppBarWidget(Key key = null) : base(key) {
}
public override State createState() {
return new MaterialAppBarWidgetState();
}
}
public class MaterialAppBarWidgetState : State<MaterialAppBarWidget> {
Choice _selectedChoice = Choice.choices[0];
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>.key();
VoidCallback _showBottomSheetCallback;
public override void initState() {
base.initState();
this._showBottomSheetCallback = this._showBottomSheet;
}
void _showBottomSheet() {
this.setState(() => { this._showBottomSheetCallback = null; });
this._scaffoldKey.currentState.showBottomSheet((BuildContext subContext) => {
ThemeData themeData = Theme.of(subContext);
return new Container(
decoration: new BoxDecoration(
border: new Border(
top: new BorderSide(
color: themeData.disabledColor))),
child: new Padding(
padding: EdgeInsets.all(32.0f),
child: new Text("This is a Material persistent bottom sheet. Drag downwards to dismiss it.",
textAlign: TextAlign.center,
style: new TextStyle(
color: themeData.accentColor,
fontSize: 16.0f))
)
);
}).closed.then((object obj) => {
if (this.mounted) {
this.setState(() => { this._showBottomSheetCallback = this._showBottomSheet; });
}
});
}
void _select(Choice choice) {
this.setState(() => { this._selectedChoice = choice; });
}
public override Widget build(BuildContext context) {
return new Scaffold(
key: this._scaffoldKey,
appBar: new AppBar(
title: new Text("Basic AppBar"),
actions: new List<Widget> {
new IconButton(
icon: new Icon(Choice.choices[0].icon),
//color: Colors.blue,
onPressed: () => { this._select((Choice.choices[0])); }
),
new IconButton(
icon: new Icon(Choice.choices[1].icon),
//color: Colors.blue,
onPressed: () => { this._select((Choice.choices[1])); }
),
new PopupMenuButton<Choice>(
onSelected: this._select,
itemBuilder: (BuildContext subContext) => {
List<PopupMenuEntry<Choice>> popupItems = new List<PopupMenuEntry<Choice>>();
for (int i = 2; i < Choice.choices.Count; i++) {
popupItems.Add(new PopupMenuItem<Choice>(
value: Choice.choices[i],
child: new Text(Choice.choices[i].title)));
}
return popupItems;
}
)
}
),
body: new Padding(
padding: EdgeInsets.all(16.0f),
child: new ChoiceCard(choice: this._selectedChoice)
),
floatingActionButton: new FloatingActionButton(
backgroundColor: Colors.redAccent,
child: new Icon(Unity.UIWidgets.material.Icons.add_alert),
onPressed: this._showBottomSheetCallback
),
drawer: new Drawer(
child: new ListView(
padding: EdgeInsets.zero,
children: new List<Widget> {
new ListTile(
leading: new Icon(Unity.UIWidgets.material.Icons.account_circle),
title: new Text("Login"),
onTap: () => { }
),
new Divider(
height: 2.0f),
new ListTile(
leading: new Icon(Unity.UIWidgets.material.Icons.account_balance_wallet),
title: new Text("Wallet"),
onTap: () => { }
),
new Divider(
height: 2.0f),
new ListTile(
leading: new Icon(Unity.UIWidgets.material.Icons.accessibility),
title: new Text("Balance"),
onTap: () => { }
)
}
)
)
);
}
}
class Choice {
public Choice(string title, IconData icon) {
this.title = title;
this.icon = icon;
}
public readonly string title;
public readonly IconData icon;
public static List<Choice> choices = new List<Choice> {
new Choice("Car", Unity.UIWidgets.material.Icons.directions_car),
new Choice("Bicycle", Unity.UIWidgets.material.Icons.directions_bike),
new Choice("Boat", Unity.UIWidgets.material.Icons.directions_boat),
new Choice("Bus", Unity.UIWidgets.material.Icons.directions_bus),
new Choice("Train", Unity.UIWidgets.material.Icons.directions_railway),
new Choice("Walk", Unity.UIWidgets.material.Icons.directions_walk)
};
}
class ChoiceCard : StatelessWidget {
public ChoiceCard(Key key = null, Choice choice = null) : base(key: key) {
this.choice = choice;
}
public readonly Choice choice;
public override Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.display1;
return new Card(
color: Colors.white,
child: new Center(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: new List<Widget> {
new Icon(this.choice.icon, size: 128.0f, color: textStyle.color),
new RaisedButton(
child: new Text(this.choice.title, style: textStyle),
onPressed: () => {
SnackBar snackBar = new SnackBar(
content: new Text(this.choice.title + " is chosen !"),
action: new SnackBarAction(
label: "Ok",
onPressed: () => { }));
Scaffold.of(context).showSnackBar(snackBar);
})
}
)
)
);
}
}
}

73
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialButtonSample.cs


using System.Collections.Generic;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Material = Unity.UIWidgets.material.Material;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class MaterialButtonSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new MaterialButtonWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class MaterialButtonWidget : StatefulWidget {
public MaterialButtonWidget(Key key = null) : base(key) {
}
public override State createState() {
return new MaterialButtonWidgetState();
}
}
public class MaterialButtonWidgetState : State<MaterialButtonWidget> {
public override Widget build(BuildContext context) {
return new Stack(
children: new List<Widget> {
new Material(
child: new Center(
child: new Column(
children: new List<Widget> {
new Padding(padding: EdgeInsets.only(top: 30f)),
new MaterialButton(
shape: new RoundedRectangleBorder(borderRadius: BorderRadius.all(20.0f)),
color: new Color(0xFF00FF00),
splashColor: new Color(0xFFFF0011),
highlightColor: new Color(0x88FF0011),
child: new Text("Click Me"),
onPressed: () => { Debug.Log("pressed flat button"); }
),
new Padding(padding: EdgeInsets.only(top: 30f)),
new MaterialButton(
shape: new RoundedRectangleBorder(borderRadius: BorderRadius.all(20.0f)),
color: new Color(0xFFFF00FF),
splashColor: new Color(0xFFFF0011),
highlightColor: new Color(0x88FF0011),
elevation: 4.0f,
child: new Text("Click Me"),
onPressed: () => { Debug.Log("pressed raised button"); }
)
}
)
)
)
}
);
}
}
}

56
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialInkWellSample.cs


using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Material = Unity.UIWidgets.material.Material;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class MaterialInkWellSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new MaterialInkWellWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class MaterialInkWellWidget : StatefulWidget {
public MaterialInkWellWidget(Key key = null) : base(key) {
}
public override State createState() {
return new MaterialInkWidgetState();
}
}
public class MaterialInkWidgetState : State<MaterialInkWellWidget> {
public override Widget build(BuildContext context) {
return new Material(
//color: Colors.blue,
child: new Center(
child: new Container(
width: 200,
height: 200,
child: new InkWell(
borderRadius: BorderRadius.circular(2.0f),
highlightColor: new Color(0xAAFF0000),
splashColor: new Color(0xAA0000FF),
onTap: () => { Debug.Log("on tap"); }
)
)
)
);
}
}
}

86
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialNavigationBarSample.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class MaterialNavigationBarSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new MaterialNavigationBarWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
class MaterialNavigationBarWidget : StatefulWidget {
public MaterialNavigationBarWidget(Key key = null) : base(key) {
}
public override State createState() {
return new MaterialNavigationBarWidgetState();
}
}
class MaterialNavigationBarWidgetState : SingleTickerProviderStateMixin<MaterialNavigationBarWidget> {
int _currentIndex = 0;
public MaterialNavigationBarWidgetState() {
}
public override Widget build(BuildContext context) {
return new Scaffold(
bottomNavigationBar: new Container(
height: 100,
color: Colors.blue,
child: new Center(
child: new BottomNavigationBar(
type: BottomNavigationBarType.shifting,
// type: BottomNavigationBarType.fix,
items: new List<BottomNavigationBarItem> {
new BottomNavigationBarItem(
icon: new Icon(icon: Unity.UIWidgets.material.Icons.work, size: 30),
title: new Text("Work"),
activeIcon: new Icon(icon: Unity.UIWidgets.material.Icons.work, size: 50),
backgroundColor: Colors.blue
),
new BottomNavigationBarItem(
icon: new Icon(icon: Unity.UIWidgets.material.Icons.home, size: 30),
title: new Text("Home"),
activeIcon: new Icon(icon: Unity.UIWidgets.material.Icons.home, size: 50),
backgroundColor: Colors.blue
),
new BottomNavigationBarItem(
icon: new Icon(icon: Unity.UIWidgets.material.Icons.shop, size: 30),
title: new Text("Shop"),
activeIcon: new Icon(icon: Unity.UIWidgets.material.Icons.shop, size: 50),
backgroundColor: Colors.blue
),
new BottomNavigationBarItem(
icon: new Icon(icon: Unity.UIWidgets.material.Icons.school, size: 30),
title: new Text("School"),
activeIcon: new Icon(icon: Unity.UIWidgets.material.Icons.school, size: 50),
backgroundColor: Colors.blue
),
},
currentIndex: this._currentIndex,
onTap: (value) => { this.setState(() => { this._currentIndex = value; }); }
)
)
)
);
}
}
}

61
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialSliderSample.cs


using System.Collections.Generic;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class MaterialSliderSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new MaterialSliderWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class MaterialSliderWidget : StatefulWidget {
public override State createState() {
return new MaterialSliderState();
}
}
public class MaterialSliderState : State<MaterialSliderWidget> {
float _value = 0.8f;
void onChanged(float value) {
this.setState(() => { this._value = value; });
}
public override Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Slider and Indicators")),
body: new Column(
children: new List<Widget> {
new Padding(
padding: EdgeInsets.only(top: 100.0f),
child: new Container(
child: new Slider(
divisions: 10,
min: 0.4f,
label: "Here",
value: this._value,
onChanged: this.onChanged))
)
}
)
);
}
}
}

130
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialTabBarSample.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class MaterialTabBarSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new TabBarDemo()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class TabBarDemo : StatelessWidget
{
public override Widget build(BuildContext context)
{
return new MaterialTabBarWidget();
/*
return new DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
bottom: new TabBar(
tabs: new List<Widget> {
new Tab(icon: new Icon(Icons.directions_car)),
new Tab(icon: new Icon(Icons.directions_transit)),
new Tab(icon: new Icon(Icons.directions_bike)),
}
),
title: new Text("Tabs Demo")
),
body: new TabBarView(
children: new List<Widget> {
new Icon(Icons.directions_car),
new Icon(Icons.directions_transit),
new Icon(Icons.directions_bike),
}
)
)
);*/
}
}
public class MaterialTabBarWidget : StatefulWidget {
public MaterialTabBarWidget(Key key = null) : base(key) {
}
public override State createState() {
return new MaterialTabBarWidgetState();
}
}
public class MaterialTabBarWidgetState : SingleTickerProviderStateMixin<MaterialTabBarWidget> {
TabController _tabController;
public override void initState() {
base.initState();
this._tabController = new TabController(vsync: this, length: Choice.choices.Count);
}
public override void dispose() {
this._tabController.dispose();
base.dispose();
}
void _nextPage(int delta) {
int newIndex = this._tabController.index + delta;
if (newIndex < 0 || newIndex >= this._tabController.length) {
return;
}
this._tabController.animateTo(newIndex);
}
public override Widget build(BuildContext context) {
List<Widget> tapChildren = new List<Widget>();
foreach (Choice choice in Choice.choices) {
tapChildren.Add(
new Padding(
padding: EdgeInsets.all(16.0f),
child: new ChoiceCard(choice: choice)));
}
return new Scaffold(
appBar: new AppBar(
title: new Center(
child: new Text("AppBar Bottom Widget")
),
leading: new IconButton(
tooltip: "Previous choice",
icon: new Icon(Unity.UIWidgets.material.Icons.arrow_back),
onPressed: () => { this._nextPage(-1); }
),
actions: new List<Widget> {
new IconButton(
icon: new Icon(Unity.UIWidgets.material.Icons.arrow_forward),
tooltip: "Next choice",
onPressed: () => { this._nextPage(1); })
},
bottom: new PreferredSize(
preferredSize: Size.fromHeight(48.0f),
child: new Theme(
data: Theme.of(context).copyWith(accentColor: Colors.white),
child: new Container(
height: 48.0f,
alignment: Alignment.center,
child: new TabPageSelector(
controller: this._tabController))))
),
body: new TabBarView(
controller: this._tabController,
children: tapChildren
));
}
}
}

82
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialThemeSample.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.animation;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Image = Unity.UIWidgets.widgets.Image;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class MaterialThemeSample: UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
home: new MaterialThemeSampleWidget(),
darkTheme: new ThemeData(primaryColor: Colors.black26)
));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class MaterialThemeSampleWidget: StatefulWidget {
public override State createState() {
return new _MaterialThemeSampleWidgetState();
}
}
class _MaterialThemeSampleWidgetState : State<MaterialThemeSampleWidget> {
public override Widget build(BuildContext context) {
return new Theme(
data: new ThemeData(
appBarTheme: new AppBarTheme(
color: Colors.purple
),
bottomAppBarTheme: new BottomAppBarTheme(
color: Colors.blue
),
cardTheme: new CardTheme(
color: Colors.red,
elevation: 2.0f
)
),
child: new Scaffold(
appBar: new AppBar(title: new Text("Test App Bar Theme")),
body: new Center(
child: new Card(
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.all(5.0f)
),
child: new Container(
height: 250,
child: new Column(
children: new List<Widget> {
new Text("Card Theme")
}
)
)
)
),
bottomNavigationBar: new BottomAppBar(
child: new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: new List<Widget> {
new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.menu), onPressed: () => { }),
new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.account_balance), onPressed: () => { })
})
)
)
);
}
}
}

79
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/ReorderableListSample.cs


using System.Collections.Generic;
using System.Linq;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Material = Unity.UIWidgets.material.Material;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class ReorderableListSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new MaterialReorderableListViewWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
class MaterialReorderableListViewWidget : StatefulWidget {
public MaterialReorderableListViewWidget(Key key = null) : base(key) {
}
public override State createState() {
return new MaterialReorderableListViewWidgetState();
}
}
class MaterialReorderableListViewWidgetState : State<MaterialReorderableListViewWidget> {
List<string> _list = new List<string> {"Apple", "Ball", "Cat", "Dog", "Elephant"};
public override Widget build(BuildContext context) {
return new Scaffold(
body: new ReorderableListView(
children: this._list.Select<string, Widget>((item) =>
{
return new ListTile(
Key.key(item),
title: new Text(item),
trailing: new Icon(Icons.menu));
}).ToList(),
onReorder: (int start, int current) =>
{
if (start < current) {
int end = current - 1;
string startItem = _list[start];
int i = 0;
int local = start;
do {
_list[local] = _list[++local];
i++;
} while (i < end - start);
_list[end] = startItem;
}
// dragging from bottom to top
else if (start > current) {
string startItem = _list[start];
for (int i = start; i > current; i--) {
_list[i] = _list[i - 1];
}
_list[current] = startItem;
}
setState(() => {});
}
)
);
}
}
}

53
Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/TableSample.cs


using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample {
public class TableSample : UIWidgetsPanel {
protected override void main() {
ui_.runApp(new MaterialApp(
showPerformanceOverlay: false,
home: new TableWidget()));
}
protected new void OnEnable() {
base.OnEnable();
}
}
public class TableWidget : StatelessWidget {
public TableWidget(Key key = null) : base(key) {
}
public override Widget build(BuildContext context) {
return new Scaffold(
body: new Table(
children: new List<TableRow> {
new TableRow(
decoration: new BoxDecoration(color: Colors.blue),
children: new List<Widget> {
new Text("item 1"),
new Text("item 2")
}
),
new TableRow(children: new List<Widget> {
new Text("item 3"),
new Text("item 4")
}
)
},
defaultVerticalAlignment: TableCellVerticalAlignment.middle));
}
}
}

3
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery.meta


fileFormatVersion: 2
guid: 20b07a92c4c64919aa3adf9322758a65
timeCreated: 1612322595

14
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/UIWidgetsGallery.asmdef


{
"name": "UIWidgetsGallery",
"references": [
"Unity.UIWidgets"
],
"optionalUnityReferences": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

27
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/GalleryMain.cs


using System.Collections.Generic;
using UIWidgetsGallery.gallery;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery
{
public class GalleryMain : UIWidgetsPanel
{
protected new void OnEnable()
{
base.OnEnable();
}
protected override void main()
{
ui_.runApp(new GalleryApp());
}
protected override void onEnable()
{
AddFont("Material Icons", new List<string> {"MaterialIcons-Regular.ttf"}, new List<int> {0});
AddFont("CupertinoIcons", new List<string> {"CupertinoIcons.ttf"}, new List<int> {0});
AddFont("GalleryIcons", new List<string> {"gallery/GalleryIcons.ttf"}, new List<int> {0});
}
}
}

3
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine.meta


fileFormatVersion: 2
guid: fb97b78a8c204fbb9c9dbf667cb23004
timeCreated: 1612342718

3
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/material.meta


fileFormatVersion: 2
guid: 27e2b642479d4616ad980ef32f76e152
timeCreated: 1612406310

19
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs


using System;
using UIWidgetsGallery.demo.shrine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.demo
{
class ShrineDemo : StatelessWidget
{
public ShrineDemo(Key key = null) : base(key: key){}
public static readonly string routeName = "/shrine";
public override Widget build(BuildContext context)
{
return new ShrineApp();
}
}
}

3
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs.meta


fileFormatVersion: 2
guid: b5da65292c17429ba697ebdefb05c8e4
timeCreated: 1612512022

276
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_alert_demo.cs


using System.Collections.Generic;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.gallery {
public class CupertinoAlertDemo : StatefulWidget {
public static string routeName = "/cupertino/alert";
public override State createState()
{
return new _CupertinoAlertDemoState();
}
}
public class _CupertinoAlertDemoState : State<CupertinoAlertDemo> {
string lastSelectedValue;
void showDemoDialog(BuildContext context = null, Widget child = null) {
CupertinoRouteUtils.showCupertinoDialog<string>(
context: context,
builder: (BuildContext context1) => child
).then_((string value) =>{
if (value != null) {
setState(()=> { lastSelectedValue = value; });
}
});
}
void showDemoActionSheet(BuildContext context = null, Widget child = null) {
CupertinoRouteUtils.showCupertinoModalPopup<string>(
context: context,
builder: (BuildContext context1) => child
).then_((string value) =>{
if (value != null) {
setState(()=> { lastSelectedValue = value; });
}
});
}
public override Widget build(BuildContext context)
{
List<Widget> createWidgets(BuildContext context1)
{
List<Widget> widgets = new List<Widget>();
widgets.Add(new CupertinoScrollbar(
child: new ListView(
// Add more padding to the normal safe area.
padding: EdgeInsets.symmetric(vertical: 24.0f, horizontal: 72.0f)
+ MediaQuery.of(context1).padding,
children: new List<Widget>
{
CupertinoButton.filled(
child: new Text("Alert"),
onPressed: () => _onAlertPress(context1)
),
new Padding(padding: EdgeInsets.all(8.0f)),
CupertinoButton.filled(
child: new Text("Alert with Title"),
padding: EdgeInsets.symmetric(vertical: 16.0f, horizontal: 36.0f),
onPressed: () => _onAlertWithTitlePress(context1)
),
new Padding(padding: EdgeInsets.all(8.0f)),
CupertinoButton.filled(
child: new Text("Alert with Buttons"),
padding: EdgeInsets.symmetric(vertical: 16.0f, horizontal: 36.0f),
onPressed: () => _onAlertWithButtonsPress(context1)
),
new Padding(padding: EdgeInsets.all(8.0f)),
CupertinoButton.filled(
child: new Text("Alert Buttons Only"),
padding: EdgeInsets.symmetric(vertical: 16.0f, horizontal: 36.0f),
onPressed: () =>
{
showDemoDialog(
context: context1,
child: new CupertinoDessertDialog()
);
}
),
new Padding(padding: EdgeInsets.all(8.0f)),
CupertinoButton.filled(
child: new Text("Action Sheet"),
padding: EdgeInsets.symmetric(vertical: 16.0f, horizontal: 36.0f),
onPressed: () => _onActionSheetPress(context1)
)
}
)
));
if (lastSelectedValue != null)
widgets.Add(new Positioned(
bottom: 32.0f,
child: new Text($"You selected: {lastSelectedValue}")
));
return widgets;
}
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
middle: new Text("Alerts"),
previousPageTitle: "Cupertino"
//,trailing: CupertinoDemoDocumentationButton(CupertinoAlertDemo.routeName)
),
child: new DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: new Builder(
builder: (BuildContext context1) => {
return new Stack(
alignment: Alignment.center,
children: createWidgets(context1)
);
}
)
)
);
}
void _onAlertPress(BuildContext context) {
showDemoDialog(
context: context,
child: new CupertinoAlertDialog(
title: new Text("Discard draft?"),
actions: new List<Widget>{
new CupertinoDialogAction(
child: new Text("Discard"),
isDestructiveAction: true,
onPressed: () => Navigator.pop(context, "Discard")
),
new CupertinoDialogAction(
child: new Text("Cancel"),
isDefaultAction: true,
onPressed: () => Navigator.pop(context, "Cancel")
),
}
)
);
}
void _onAlertWithTitlePress(BuildContext context) {
showDemoDialog(
context: context,
child: new CupertinoAlertDialog(
title: new Text("Allow \"Maps\" to access your location while you are using the app?"),
content: new Text("Your current location will be displayed on the map and used " +
"for directions, nearby search results, and estimated travel times."),
actions: new List<Widget>{
new CupertinoDialogAction(
child: new Text("Don't Allow"),
onPressed: () => Navigator.pop(context, "Disallow")
),
new CupertinoDialogAction(
child: new Text("Allow"),
onPressed: () => Navigator.pop(context, "Allow")
),
}
)
);
}
void _onAlertWithButtonsPress(BuildContext context) {
showDemoDialog(
context: context,
child: new CupertinoDessertDialog(
title: new Text("Select Favorite Dessert"),
content: new Text("Please select your favorite type of dessert from the " +
"list below. Your selection will be used to customize the suggested " +
"list of eateries in your area.")
)
);
}
void _onActionSheetPress(BuildContext context) {
showDemoActionSheet(
context: context,
child: new CupertinoActionSheet(
title: new Text("Favorite Dessert"),
message: new Text("Please select the best dessert from the options below."),
actions: new List<Widget>{
new CupertinoActionSheetAction(
child: new Text("Profiteroles"),
onPressed: () => Navigator.pop(context, "Profiteroles")
),
new CupertinoActionSheetAction(
child: new Text("Cannolis"),
onPressed: () => Navigator.pop(context, "Cannolis")
),
new CupertinoActionSheetAction(
child: new Text("Trifle"),
onPressed: () => Navigator.pop(context, "Trifle")
),
},
cancelButton: new CupertinoActionSheetAction(
child: new Text("Cancel"),
isDefaultAction: true,
onPressed: () => Navigator.pop(context, "Cancel")
)
)
);
}
}
public class CupertinoDessertDialog : StatelessWidget {
public CupertinoDessertDialog(Key key = null, Widget title = null, Widget content = null) : base(key: key)
{
this.title = title;
this.content = content;
}
public readonly Widget title;
public readonly Widget content;
public override Widget build(BuildContext context) {
return new CupertinoAlertDialog(
title: title,
content: content,
actions: new List<Widget>{
new CupertinoDialogAction(
child: new Text("Cheesecake"),
onPressed: () => {
Navigator.pop(context, "Cheesecake");
}
),
new CupertinoDialogAction(
child: new Text("Tiramisu"),
onPressed: () => {
Navigator.pop(context, "Tiramisu");
}
),
new CupertinoDialogAction(
child: new Text("Apple Pie"),
onPressed: () => {
Navigator.pop(context, "Apple Pie");
}
),
new CupertinoDialogAction(
child: new Text("Devil's food cake"),
onPressed: () => {
Navigator.pop(context, "Devil's food cake");
}
),
new CupertinoDialogAction(
child: new Text("Banana Split"),
onPressed: () => {
Navigator.pop(context, "Banana Split");
}
),
new CupertinoDialogAction(
child: new Text("Oatmeal Cookie"),
onPressed: () => {
Navigator.pop(context, "Oatmeal Cookies");
}
),
new CupertinoDialogAction(
child: new Text("Chocolate Brownie"),
onPressed: () => {
Navigator.pop(context, "Chocolate Brownies");
}
),
new CupertinoDialogAction(
child: new Text("Cancel"),
isDestructiveAction: true,
onPressed: () => {
Navigator.pop(context, "Cancel");
}
)
}
);
}
}
}

89
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_buttons_demo.cs


using System.Collections.Generic;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.gallery {
public class CupertinoButtonsDemo : StatefulWidget {
public static string routeName = "/cupertino/buttons";
public override State createState() {
return new _CupertinoButtonDemoState();
}
}
public class _CupertinoButtonDemoState : State<CupertinoButtonsDemo> {
int _pressedCount = 0;
public override Widget build(BuildContext context)
{
string timeStr = _pressedCount == 1 ? "" : "s";
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
middle: new Text("Buttons"),
previousPageTitle: "Cupertino"
//trailing: CupertinoDemoDocumentationButton(CupertinoButtonsDemo.routeName),
),
child: new DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: new SafeArea(
child: new Column(
children: new List<Widget>{
new Padding(
padding: EdgeInsets.all(16.0f),
child: new Text(
"iOS themed buttons are flat. They can have borders or backgrounds but " +
"only when necessary."
)
),
new Expanded(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: new List<Widget>
{
new Text(_pressedCount > 0
? $"Button pressed {_pressedCount} time{timeStr}"
: " "),
new Padding(padding: EdgeInsets.all(12.0f)),
new Align(
alignment: new Alignment(0.0f, -0.2f),
child: new Row(
mainAxisSize: MainAxisSize.min,
children: new List<Widget>
{
new CupertinoButton(
child: new Text("Cupertino Button"),
onPressed: () => { setState(() => { _pressedCount += 1; }); }
),
new CupertinoButton(
child: new Text("Disabled"),
onPressed: null
)
}
)
),
new Padding(padding: EdgeInsets.all(12.0f)),
CupertinoButton.filled(
child: new Text("With Background"),
onPressed: () => { setState(() => { _pressedCount += 1; }); }
),
new Padding(padding: EdgeInsets.all(12.0f)),
CupertinoButton.filled(
child: new Text("Disabled"),
onPressed: null
)
}
)
)
}
)
)
)
);
}
}
}

307
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs


using System;
using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using Brightness = Unity.UIWidgets.ui.Brightness;
namespace UIWidgetsGallery.gallery {
class CupertinoPickerDemoUtils {
public const float _kPickerSheetHeight = 216.0f;
public const float _kPickerItemHeight = 32.0f;
public static List<string> coolColorNames = new List<string>{
"Sarcoline", "Coquelicot", "Smaragdine", "Mikado", "Glaucous", "Wenge",
"Fulvous", "Xanadu", "Falu", "Eburnean", "Amaranth", "Australien",
"Banan", "Falu", "Gingerline", "Incarnadine", "Labrador", "Nattier",
"Pervenche", "Sinoper", "Verditer", "Watchet", "Zaffre",
};
}
public class CupertinoPickerDemo : StatefulWidget {
public const string routeName = "/cupertino/picker";
public override State createState() {
return new _CupertinoPickerDemoState();
}
}
public class _CupertinoPickerDemoState : State<CupertinoPickerDemo> {
int _selectedColorIndex = 0;
TimeSpan timer = new TimeSpan();
DateTime date = DateTime.Now;
DateTime time = DateTime.Now;
DateTime dateTime = DateTime.Now;
Widget _buildColorPicker(BuildContext context) {
FixedExtentScrollController scrollController = new FixedExtentScrollController(initialItem: _selectedColorIndex);
List<Widget> widgets = new List<Widget>();
for (int i = 0; i < CupertinoPickerDemoUtils.coolColorNames.Count; i++)
{
widgets.Add(new Center(
child: new Text(CupertinoPickerDemoUtils.coolColorNames[i])
));
}
return new GestureDetector(
onTap: () => {
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
semanticsDismissible: true,
builder: (BuildContext context1) =>{
return new _BottomPicker(
child: new CupertinoPicker(
scrollController: scrollController,
itemExtent: CupertinoPickerDemoUtils._kPickerItemHeight,
backgroundColor: CupertinoColors.systemBackground.resolveFrom(context1),
onSelectedItemChanged: (int index)=> {
setState(() => _selectedColorIndex = index);
},
children: widgets
)
);
}
);
},
child: new _Menu(
children: new List<Widget>{
new Text("Favorite Color"),
new Text(
CupertinoPickerDemoUtils.coolColorNames[_selectedColorIndex],
style: new TextStyle(color: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context))
)
}
)
);
}
Widget _buildCountdownTimerPicker(BuildContext context) {
return new GestureDetector(
onTap: () =>
{
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
semanticsDismissible: true,
builder: (BuildContext context1) =>
{
return new _BottomPicker(
child: new CupertinoTimerPicker(
backgroundColor: CupertinoColors.systemBackground.resolveFrom(context1),
initialTimerDuration: timer,
onTimerDurationChanged: (TimeSpan newTimer) =>{
setState(() => timer = newTimer);
}
)
);
}
);
},
child: new _Menu(
children: new List<Widget>{
new Text("Countdown Timer"),
new Text(
$"{timer.Hours}:" +
$"{(timer.Minutes % 60).ToString("00")}:" +
$"{(timer.Seconds % 60).ToString("00")}",
style: new TextStyle(color: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context))
),
}
)
);
}
Widget _buildDatePicker(BuildContext context)
{
return new GestureDetector(
onTap: () =>
{
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
semanticsDismissible: true,
builder: (BuildContext context1) =>
{
return new _BottomPicker(
child: new CupertinoDatePicker(
backgroundColor: CupertinoColors.systemBackground.resolveFrom(context1),
mode: CupertinoDatePickerMode.date,
initialDateTime: date,
onDateTimeChanged: (DateTime newDateTime) =>{
setState(() => date = newDateTime);
}
)
);
}
);
},
child: new _Menu(
children: new List<Widget>{
new Text("Date"),
new Text(
date.ToString("MMMM dd, yyyy"),
style: new TextStyle(color: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context))
),
}
)
);
}
Widget _buildTimePicker(BuildContext context) {
return new GestureDetector(
onTap: () =>{
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
semanticsDismissible: true,
builder: (BuildContext context1) =>{
return new _BottomPicker(
child: new CupertinoDatePicker(
backgroundColor: CupertinoColors.systemBackground.resolveFrom(context1),
mode: CupertinoDatePickerMode.time,
initialDateTime: time,
onDateTimeChanged: (newDateTime) => {
setState(() => time = newDateTime);
}
)
);
}
);
},
child:new _Menu(
children: new List<Widget>{
new Text("Time"),
new Text(
time.ToString("h:mm tt"),
style: new TextStyle(color: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context))
),
}
)
);
}
Widget _buildDateAndTimePicker(BuildContext context) {
return new GestureDetector(
onTap: ()=> {
CupertinoRouteUtils.showCupertinoModalPopup(
context: context,
semanticsDismissible: true,
builder: (BuildContext context1) =>{
return new _BottomPicker(
child: new CupertinoDatePicker(
backgroundColor: CupertinoColors.systemBackground.resolveFrom(context1),
mode: CupertinoDatePickerMode.dateAndTime,
initialDateTime: dateTime,
onDateTimeChanged: (newDateTime) => {
setState(() => dateTime = newDateTime);
}
)
);
}
);
},
child: new _Menu(
children:new List<Widget>{
new Text("Date and Time"),
new Text(
dateTime.ToString("MMMM dd, yyyy") + " " + dateTime.ToString("HH:mm tt"),
style: new TextStyle(color: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context))
),
}
)
);
}
public override Widget build(BuildContext context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
middle: new Text("Picker"),
// We're specifying a back label here because the previous page is a
// Material page. CupertinoPageRoutes could auto-populate these back
// labels.
previousPageTitle: "Cupertino"
//trailing: CupertinoDemoDocumentationButton(CupertinoPickerDemo.routeName)
),
child: new DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: new ListView(
children: new List<Widget>{
new Padding(padding: EdgeInsets.only(top: 32.0f)),
_buildColorPicker(context),
_buildCountdownTimerPicker(context),
_buildDatePicker(context),
_buildTimePicker(context),
_buildDateAndTimePicker(context),
}
)
)
);
}
}
public class _BottomPicker : StatelessWidget {
public _BottomPicker(
Key key = null,
Widget child = null
) : base(key: key){
D.assert(child != null);
this.child = child;
}
public readonly Widget child;
public override Widget build(BuildContext context)
{
return new Container(
height: CupertinoPickerDemoUtils._kPickerSheetHeight,
padding: EdgeInsets.only(top: 6.0f),
color: CupertinoColors.label.resolveFrom(context).darkColor,
child: new DefaultTextStyle(
style: new TextStyle(
color: CupertinoColors.label.resolveFrom(context),
fontSize: 22.0f
),
child: new GestureDetector(
onTap: () =>{ },
child: new SafeArea(
top: false,
child: child
)
)
)
);
}
}
public class _Menu : StatelessWidget {
public _Menu(
Key key = null,
List<Widget> children = null
) :base(key: key) {
D.assert(children != null);
this.children = children;
}
public readonly List<Widget> children;
public override Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
border: new Border(
top: new BorderSide(color: CupertinoColors.inactiveGray, width: 0),
bottom: new BorderSide(color: CupertinoColors.inactiveGray, width: 0)
)
),
height: 44,
child: new Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: children
)
)
);
}
}
}

67
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_slider_demo.cs


using System.Collections.Generic;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.widgets;
namespace UIWidgetsGallery.gallery {
public class CupertinoSliderDemo : StatefulWidget {
public static string routeName = "/cupertino/slider";
public override State createState() {
return new _CupertinoSliderDemoState();
}
}
public class _CupertinoSliderDemoState : State<CupertinoSliderDemo> {
float _value = 25.0f;
float _discreteValue = 20.0f;
public override Widget build(BuildContext context)
{
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
middle: new Text("Sliders"),
previousPageTitle: "Cupertino"
),
child: new DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: new SafeArea(
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: new List< Widget >{
new Column(
mainAxisSize: MainAxisSize.min,
children: new List< Widget >{
new CupertinoSlider(
value: _value,
min: 0.0f,
max: 100.0f,
onChanged: (float value) => { setState(() => { _value = value; }); }
),
new Text($"Cupertino Continuous: {_value:F1}"),
}
),
new Column(
mainAxisSize: MainAxisSize.min,
children: new List< Widget >{
new CupertinoSlider(
value: _discreteValue,
min: 0.0f,
max: 100.0f,
divisions: 5,
onChanged: (float value) => { setState(() => { _discreteValue = value; }); }
),
new Text($"Cupertino Discrete: {_discreteValue}")
}
)
}
)
)
)
)
);
}
}
}

873
Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_navigation_demo.cs


using System.Collections.Generic;
//using RSG;
using Unity.UIWidgets.cupertino;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
//using Unity.UIWidgets.material;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
using Brightness = Unity.UIWidgets.ui.Brightness;
namespace UIWidgetsGallery.gallery {
class CupertinoNavigationDemoUtils {
public const int _kChildCount = 50;
public static List<Color> coolColors = new List<Color> {
Color.fromARGB(255, 255, 59, 48),
Color.fromARGB(255, 255, 149, 0),
Color.fromARGB(255, 255, 204, 0),
Color.fromARGB(255, 76, 217, 100),
Color.fromARGB(255, 90, 200, 250),
Color.fromARGB(255, 0, 122, 255),
Color.fromARGB(255, 88, 86, 214),
Color.fromARGB(255, 255, 45, 85),
};
public static List<string> coolColorNames = new List<string> {
"Sarcoline", "Coquelicot", "Smaragdine", "Mikado", "Glaucous", "Wenge",
"Fulvous", "Xanadu", "Falu", "Eburnean", "Amaranth", "Australien",
"Banan", "Falu", "Gingerline", "Incarnadine", "Labrador", "Nattier",
"Pervenche", "Sinoper", "Verditer", "Watchet", "Zafre",
};
public static Widget trailingButtons {
get {
return new Row(
mainAxisSize: MainAxisSize.min,
children: new List<Widget> {
//new CupertinoDemoDocumentationButton(CupertinoNavigationDemo.routeName),
new Padding(padding: EdgeInsets.only(left: 8.0f)),
new ExitButton(),
}
);
}
}
public static List<Widget> buildTab2Conversation() {
return new List<Widget> {
new Tab2ConversationRow(
text: "My Xanadu doesn't look right"
),
new Tab2ConversationRow(
avatar: new Tab2ConversationAvatar(
text: "KL",
color: new Color(0xFFFD5015)
),
text: "We'll rush you a new one.\nIt's gonna be incredible"
),
new Tab2ConversationRow(
text: "Awesome thanks!"
),
new Tab2ConversationRow(
avatar: new Tab2ConversationAvatar(
text: "SJ",
color: new Color(0xFF34CAD6)
),
text: "We'll send you our\nnewest Labrador too!"
),
new Tab2ConversationRow(
text: "Yay"
),
new Tab2ConversationRow(
avatar: new Tab2ConversationAvatar(
text: "KL",
color: new Color(0xFFFD5015)
),
text: "Actually there's one more thing..."
),
new Tab2ConversationRow(
text: "What's that ? "
),
};
}
}
public class CupertinoNavigationDemo : StatelessWidget {
public CupertinoNavigationDemo() {
colorItems = new List<Color>();
for (int i = 0; i < CupertinoNavigationDemoUtils._kChildCount; i++) {
colorItems.Add(CupertinoNavigationDemoUtils.coolColors[
Random.Range(0, CupertinoNavigationDemoUtils.coolColors.Count)
]);
}
colorNameItems = new List<string>();
for (int i = 0; i < CupertinoNavigationDemoUtils._kChildCount; i++) {
colorNameItems.Add(CupertinoNavigationDemoUtils.coolColorNames[
Random.Range(0, CupertinoNavigationDemoUtils.coolColorNames.Count)
]);
}
}
public static string routeName = "/cupertino/navigation";
public readonly List<Color> colorItems;
public readonly List<string> colorNameItems;
public override Widget build(BuildContext context) {
return new WillPopScope(
//onWillPop: () => { return Promise<bool>.Resolved(true); },
//onWillPop: this.onWillPop,
child: new DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: new CupertinoTabScaffold(
tabBar: new CupertinoTabBar(
items: new List<BottomNavigationBarItem> {
new BottomNavigationBarItem(
icon: new Icon(CupertinoIcons.home),
title: new Text("Home")
),
new BottomNavigationBarItem(
icon: new Icon(CupertinoIcons.conversation_bubble),
title: new Text("Support")
),
new BottomNavigationBarItem(
icon: new Icon(CupertinoIcons.profile_circled),
title: new Text("Profile")
)
}
),
tabBuilder: (BuildContext _context, int index) => {
D.assert(index >= 0 && index <= 2);
switch (index) {
case 0:
return new CupertinoTabView(
builder: (BuildContext _context1) => {
return new CupertinoDemoTab1(
colorItems: colorItems,
colorNameItems: colorNameItems
);
},
defaultTitle: "Colors"
);
case 1:
return new CupertinoTabView(
builder: (BuildContext _context2) => new CupertinoDemoTab2(),
defaultTitle: "Support Chat"
);
case 2:
return new CupertinoTabView(
builder: (BuildContext _context3) => new CupertinoDemoTab3(),
defaultTitle: "Account"
);
}
return null;
}
)
)
);
}
}
class ExitButton : StatelessWidget {
public ExitButton() { }
public override Widget build(BuildContext context) {
return new CupertinoButton(
padding: EdgeInsets.zero,
child: new Tooltip(
message: "Back",
child: new Text("Exit"),
excludeFromSemantics: true
),
onPressed: () => { Navigator.of(context, rootNavigator: true).pop<object>(); }
);
}
}
class CupertinoDemoTab1 : StatelessWidget {
public CupertinoDemoTab1(
List<Color> colorItems = null,
List<string> colorNameItems = null
) {
this.colorItems = colorItems ?? new List<Color>();
this.colorNameItems = colorNameItems ?? new List<string>();
}
public readonly List<Color> colorItems;
public readonly List<string> colorNameItems;
public override Widget build(BuildContext context) {
return new CupertinoPageScaffold(
backgroundColor: CupertinoColors.systemGroupedBackground,
child: new CustomScrollView(
slivers: new List<Widget> {
new CupertinoSliverNavigationBar(
trailing: CupertinoNavigationDemoUtils.trailingButtons
),
new SliverPadding(
padding: MediaQuery.of(context).removePadding(
removeTop: true,
removeLeft: true,
removeRight: true
).padding,
sliver: new SliverList(
del: new SliverChildBuilderDelegate(
(BuildContext _context, int index) => {
return new Tab1RowItem(
index: index,
lastItem: index == CupertinoNavigationDemoUtils._kChildCount - 1,
color: colorItems[index],
colorName: colorNameItems[index]
);
},
childCount: CupertinoNavigationDemoUtils._kChildCount
)
)
)
}
)
);
}
}
class Tab1RowItem : StatelessWidget {
public Tab1RowItem(
int index,
bool lastItem,
Color color,
string colorName
) {
this.index = index;
this.lastItem = lastItem;
this.color = color;
this.colorName = colorName;
}
public readonly int index;
public readonly bool lastItem;
public readonly Color color;
public readonly string colorName;
public override Widget build(BuildContext context)
{
Widget row = new GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () =>
{
Navigator.of(context).push(new CupertinoPageRoute(
title: colorName,
builder: (BuildContext _context) => new Tab1ItemPage(
color: color,
colorName: colorName,
index: index
)
));
},
child: new Container(
color: CupertinoDynamicColor.resolve(CupertinoColors.systemBackground, context),
child: new SafeArea(
top: false,
bottom: false,
child: new Padding(
padding: EdgeInsets.only(left: 16.0f, top: 8.0f, bottom: 8.0f, right: 8.0f),
child: new Row(
children: new List<Widget>
{
new Container(
height: 60.0f,
width: 60.0f,
decoration: new BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(8.0f)
)
),
new Expanded(
child: new Padding(
padding: EdgeInsets.symmetric(horizontal: 12.0f),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget>
{
new Text(colorName),
new Padding(padding: EdgeInsets.only(top: 8.0f)),
new Text(
"Buy this cool color",
style: new TextStyle(
color: CupertinoDynamicColor.resolve(
CupertinoColors.secondaryLabel, context),
fontSize: 13.0f,
fontWeight: FontWeight.w300
)
)
}
)
)
),
new CupertinoButton(
padding: EdgeInsets.zero,
child: new Icon(CupertinoIcons.plus_circled
),
onPressed: () => { }
),
new CupertinoButton(
padding: EdgeInsets.zero,
child: new Icon(CupertinoIcons.share
),
onPressed: () => { }
)
}
)
)
)
)
);
if (lastItem) {
return row;
}
return new Column(
children: new List<Widget> {
row,
new Container(
height: 1.0f,
color: CupertinoDynamicColor.resolve(CupertinoColors.separator, context)
)
}
);
}
}
class Tab1ItemPage : StatefulWidget {
public Tab1ItemPage(
Color color,
string colorName,
int index
) {
this.color = color;
this.colorName = colorName;
this.index = index;
}
public readonly Color color;
public readonly string colorName;
public readonly int index;
public override State createState() {
return new Tab1ItemPageState();
}
}
class Tab1ItemPageState : State<Tab1ItemPage> {
public override void initState() {
base.initState();
relatedColors = new List<Color>();
for (int i = 0; i < 10; i++) {
relatedColors.Add(Color.fromARGB(
255,
(widget.color.red + Random.Range(-50, 50)).clamp(0, 255),
(widget.color.green + Random.Range(-50, 50)).clamp(0, 255),
(widget.color.blue + Random.Range(-50, 50)).clamp(0, 255)
));
}
}
List<Color> relatedColors;
public override Widget build(BuildContext context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
trailing: new ExitButton()
),
child: new SafeArea(
top: false,
bottom: false,
child: new ListView(
children: new List<Widget> {
new Padding(padding: EdgeInsets.only(top: 16.0f)),
new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0f),
child: new Row(
mainAxisSize: MainAxisSize.max,
children: new List<Widget> {
new Container(
height: 128.0f,
width: 128.0f,
decoration: new BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(24.0f)
)
),
new Padding(padding: EdgeInsets.only(left: 18.0f)),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: new List<Widget> {
new Text(widget.colorName,
style: new TextStyle(fontSize: 24.0f,
fontWeight: FontWeight.bold)
),
new Padding(padding: EdgeInsets.only(top: 6.0f)),
new Text(
$"Item number {widget.index}",
style: new TextStyle(
color: CupertinoDynamicColor.resolve(CupertinoColors.secondaryLabel, context),
fontSize: 16.0f,
fontWeight: FontWeight.w100
)
),
new Padding(padding: EdgeInsets.only(top: 20.0f)),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: new List<Widget> {
CupertinoButton.filled(
minSize: 30.0f,
padding: EdgeInsets.symmetric(horizontal: 24.0f),
borderRadius: BorderRadius.circular(16.0f),
child: new Text(
"GET",
style: new TextStyle(
fontSize: 14.0f,
fontWeight: FontWeight.w700,
letterSpacing: -0.28f
)
),
onPressed: () => { }
),
CupertinoButton.filled(
minSize: 30.0f,
padding: EdgeInsets.zero,
borderRadius: BorderRadius.circular(32.0f),
child: new Icon(CupertinoIcons.ellipsis),
onPressed: () => { }
)
}
)
}
)
)
}
)
),
new Padding(
padding: EdgeInsets.only(left: 16.0f, top: 28.0f, bottom: 8.0f),
child: new Text(
"USERS ALSO LIKED",
style: new TextStyle(
color: new Color(0xFF646464),
letterSpacing: -0.60f,
fontSize: 15.0f,
fontWeight: FontWeight.w500
)
)
),
new SizedBox(
height: 200.0f,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemExtent: 160.0f,
itemBuilder: (BuildContext _context, int index) => {
return new Padding(
padding: EdgeInsets.only(left: 16.0f),
child: new Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(8.0f),
color: relatedColors[index]
),
child: new Center(
child: new CupertinoButton(
child: new Icon(
CupertinoIcons.plus_circled,
color: CupertinoColors.white,
size: 36.0f
),
onPressed: () => { }
)
)
)
);
}
)
)
}
)
)
);
}
}
class CupertinoDemoTab2 : StatelessWidget {
public override Widget build(BuildContext context) {
var listViewList = new List<Widget>();
listViewList.Add(new CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevatedlayer,
child: new Tab2Header()
));
listViewList.AddRange(CupertinoNavigationDemoUtils.buildTab2Conversation());
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
trailing: CupertinoNavigationDemoUtils.trailingButtons
),
child:
new SafeArea(
child: new ListView(
children: listViewList
)
)
);
}
}
class Tab2Header : StatelessWidget {
public override Widget build(BuildContext context)
{
return new Padding(
padding: EdgeInsets.all(16.0f),
child: new SafeArea(
top: false,
bottom: false,
child: new ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(16.0f)),
child: new Column(
mainAxisSize: MainAxisSize.min,
children: new List<Widget>
{
new Container(
decoration: new BoxDecoration(
color: CupertinoDynamicColor.resolve(CupertinoColors.systemFill, context)
),
child: new Padding(
padding: EdgeInsets.symmetric(horizontal: 18.0f, vertical: 12.0f),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: new List<Widget>
{
new Text(
"SUPPORT TICKET",
style: new TextStyle(
color: new Color(0xFF646464),
letterSpacing: -0.9f,
fontSize: 14.0f,
fontWeight: FontWeight.w500
)
),
new Text(
"Show More",
style: new TextStyle(
color: CupertinoDynamicColor.resolve(CupertinoColors.secondaryLabel, context),
letterSpacing: -0.6f,
fontSize: 12.0f,
fontWeight: FontWeight.w500
)
)
}
)
)
),
new Container(
decoration: new BoxDecoration(
color: CupertinoDynamicColor.resolve(CupertinoColors.quaternarySystemFill, context)
),
child: new Padding(
padding: EdgeInsets.symmetric(horizontal: 18.0f, vertical: 12.0f),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: new List<Widget>
{
new Text(
"Product or product packaging damaged during transit",
style: new TextStyle(
fontSize: 16.0f,
fontWeight: FontWeight.w700,
letterSpacing: -0.46f
)
),
new Padding(padding: EdgeInsets.only(top: 16.0f)),
new Text(
"REVIEWERS",
style: new TextStyle(
color: new Color(0xFF646464),
fontSize: 12.0f,
letterSpacing: -0.6f,
fontWeight: FontWeight.w500
)
),
new Padding(padding: EdgeInsets.only(top: 8.0f)),
new Row(
children: new List<Widget>
{
new Container(
width: 44.0f,
height: 44.0f,
decoration: new BoxDecoration(
image: new DecorationImage(
image: new FileImage(
file: "gallery/people/square/trevor.png"
)
),
shape: BoxShape.circle
)
),
new Padding(padding: EdgeInsets.only(left: 8.0f)),
new Container(
width: 44.0f,
height: 44.0f,
decoration: new BoxDecoration(
image: new DecorationImage(
image: new FileImage(
"gallery/people/square/sandra.png"
)
),
shape: BoxShape.circle
)
),
new Padding(padding: EdgeInsets.only(left: 2.0f)),
new Icon(
CupertinoIcons.check_mark_circled,
color: new Color(0xFF646464),
size: 20.0f
)
}
)
}
)
)
)
}
)
)
)
);
}
}
enum Tab2ConversationBubbleColor {
blue,
gray,
}
class Tab2ConversationBubble : StatelessWidget {
public Tab2ConversationBubble(
string text,
Tab2ConversationBubbleColor color
) {
this.text = text;
this.color = color;
}
public readonly string text;
public readonly Tab2ConversationBubbleColor color;
public override Widget build(BuildContext context) {
Color backgroundColor = null;
Color foregroundColor = null;
switch (color) {
case Tab2ConversationBubbleColor.gray:
backgroundColor = CupertinoDynamicColor.resolve(CupertinoColors.systemFill, context);
foregroundColor = CupertinoDynamicColor.resolve(CupertinoColors.label, context);
break;
case Tab2ConversationBubbleColor.blue:
backgroundColor = CupertinoTheme.of(context).primaryColor;
foregroundColor = CupertinoColors.white;
break;
}
return new Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(18.0f)),
color: backgroundColor
),
margin: EdgeInsets.symmetric(horizontal: 8.0f, vertical: 8.0f),
padding: EdgeInsets.symmetric(horizontal: 14.0f, vertical: 10.0f),
child: new Text(
text,
style: new TextStyle(
color: foregroundColor,
letterSpacing: -0.4f,
fontSize: 15.0f,
fontWeight: FontWeight.w400
)
)
);
}
}
class Tab2ConversationAvatar : StatelessWidget {
public Tab2ConversationAvatar(
string text,
Color color
) {
this.text = text;
this.color = color;
}
public readonly string text;
public readonly Color color;
public override Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
shape: BoxShape.circle,
gradient: new LinearGradient(
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
colors: new List<Color> {
color,
Color.fromARGB(
color.alpha,
(color.red - 60).clamp(0, 255),
(color.green - 60).clamp(0, 255),
(color.blue - 60).clamp(0, 255)
)
}
)
),
margin: EdgeInsets.only(left: 8.0f, bottom: 8.0f),
padding: EdgeInsets.all(12.0f),
child: new Text(
text,
style: new TextStyle(
color: CupertinoColors.white,
fontSize: 13.0f,
fontWeight: FontWeight.w500
)
)
);
}
}
class Tab2ConversationRow : StatelessWidget {
public Tab2ConversationRow(
string text,
Tab2ConversationAvatar avatar = null
) {
this.avatar = avatar;
this.text = text;
}
public readonly Tab2ConversationAvatar avatar;
public readonly string text;
public override Widget build(BuildContext context) {
bool isSelf = avatar == null;
List<Widget> children = new List<Widget>();
if (avatar != null) {
children.Add(avatar);
}
children.Add(
new CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevatedlayer,
child: new Tab2ConversationBubble(
text: text,
color: isSelf
? Tab2ConversationBubbleColor.blue
: Tab2ConversationBubbleColor.gray
)
)
);
return new SafeArea(
child: new Row(
mainAxisAlignment: isSelf ? MainAxisAlignment.end : MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: isSelf ? CrossAxisAlignment.center : CrossAxisAlignment.end,
children: children
)
);
}
}
class CupertinoDemoTab3 : StatelessWidget {
public override Widget build(BuildContext context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
trailing: CupertinoNavigationDemoUtils.trailingButtons
),
child: new SafeArea(
child: new ListView(
children: new List<Widget> {
new Padding(padding: EdgeInsets.only(top: 32.0f)),
new GestureDetector(
onTap: () => {
Navigator.of(context, rootNavigator: true).push(
new CupertinoPageRoute(
fullscreenDialog: true,
builder: (BuildContext _context) => new Tab3Dialog()
)
);
},
child: new Container(
decoration: new BoxDecoration(
color: CupertinoTheme.of(context).scaffoldBackgroundColor,
border: new Border(
top: new BorderSide(color: new Color(0xFFBCBBC1), width: 0.0f),
bottom: new BorderSide(color: new Color(0xFFBCBBC1), width: 0.0f)
)
),
height: 44.0f,
child: new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0f, vertical: 8.0f),
child: new SafeArea(
top: false,
bottom: false,
child: new Row(
children: new List<Widget> {
new Text(
"Sign in",
style: new TextStyle(color: CupertinoTheme.of(context)
.primaryColor)
),
}
)
)
)
)
)
}
)
)
);
}
}
class Tab3Dialog : StatelessWidget {
public override Widget build(BuildContext context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
leading: new CupertinoButton(
child: new Text("Cancel"),
padding: EdgeInsets.zero,
onPressed: () => { Navigator.of(context).pop(false); }
)
),
child: new Center(
child: new Column(
mainAxisSize: MainAxisSize.min,
children: new List<Widget> {
new Icon(
CupertinoIcons.profile_circled,
size: 160.0f,
color: new Color(0xFF646464)
),
new Padding(padding: EdgeInsets.only(top: 18.0f)),
CupertinoButton.filled(
child: new Text("Sign in"),
onPressed: () => { Navigator.pop<object>(context); }
),
}
)
)
);
}
}
}

部分文件因为文件数量过多而无法显示

正在加载...
取消
保存