比较提交
合并到: unity-tech-cn:main
unity-tech-cn:/main
unity-tech-cn:/tag-release_1.5.4
unity-tech-cn:/kgdev
unity-tech-cn:/siyaoH-1.17-PlatformMessage
unity-tech-cn:/dev_k12
unity-tech-cn:/uiwidgets_1.0
unity-tech-cn:/work_around_tal
unity-tech-cn:/zgh-devtools
unity-tech-cn:/zxw-devTools
unity-tech-cn:/siyaoH-1.17-vulkan
unity-tech-cn:/siyaoH-1.17-vulkan2
unity-tech-cn:/zgh-build_scripts
unity-tech-cn:/siyaoH-1.17-android
unity-tech-cn:/siyaoH-1.17-externalTexture
unity-tech-cn:/siyaoH-1.17-debugDraw
unity-tech-cn:/2.0.3
unity-tech-cn:/siyaoH-1.17-rawTouches
unity-tech-cn:/prepare_2.0.6
unity-tech-cn:/fix_shortcuts
unity-tech-cn:/fix_alignment_issue
unity-tech-cn:/wangshuang
unity-tech-cn:/siyaoH-paste
unity-tech-cn:/zxw-fix_build_script
unity-tech-cn:/siyaoH-1.17-fixLottie
unity-tech-cn:/zxw-support_editor_dragdrop
unity-tech-cn:/siyaoH-1.17-fixAndroid
unity-tech-cn:/siyaoH-1.17-HandleEvent
unity-tech-cn:/siyaoH-1.17-combineTouch
unity-tech-cn:/refine_readme
unity-tech-cn:/2.0.4
unity-tech-cn:/2.0.1
unity-tech-cn:/dev-packman
unity-tech-cn:/zxw-fix_text_shadow_issue
unity-tech-cn:/zxw-fix_backdropfilter
unity-tech-cn:/zxw-fix_transform_hover_issue
unity-tech-cn:/zxw-refine_codegen
unity-tech-cn:/zxw-fix_material_datepicker_issue
unity-tech-cn:/zxw-fix_material_text_direction
unity-tech-cn:/zxw-fix_path_metric_crash
unity-tech-cn:/demo
unity-tech-cn:/jiacheng-demo_proctice
unity-tech-cn:/zxw-3d
unity-tech-cn:/zxw-fix_crash
unity-tech-cn:/zxw-fix_input_issue
unity-tech-cn:/zxw-3d_android
unity-tech-cn:/main/LAO
拉取从: unity-tech-cn:zgh-devtools
unity-tech-cn:/main
unity-tech-cn:/tag-release_1.5.4
unity-tech-cn:/kgdev
unity-tech-cn:/siyaoH-1.17-PlatformMessage
unity-tech-cn:/dev_k12
unity-tech-cn:/uiwidgets_1.0
unity-tech-cn:/work_around_tal
unity-tech-cn:/zgh-devtools
unity-tech-cn:/zxw-devTools
unity-tech-cn:/siyaoH-1.17-vulkan
unity-tech-cn:/siyaoH-1.17-vulkan2
unity-tech-cn:/zgh-build_scripts
unity-tech-cn:/siyaoH-1.17-android
unity-tech-cn:/siyaoH-1.17-externalTexture
unity-tech-cn:/siyaoH-1.17-debugDraw
unity-tech-cn:/2.0.3
unity-tech-cn:/siyaoH-1.17-rawTouches
unity-tech-cn:/prepare_2.0.6
unity-tech-cn:/fix_shortcuts
unity-tech-cn:/fix_alignment_issue
unity-tech-cn:/wangshuang
unity-tech-cn:/siyaoH-paste
unity-tech-cn:/zxw-fix_build_script
unity-tech-cn:/siyaoH-1.17-fixLottie
unity-tech-cn:/zxw-support_editor_dragdrop
unity-tech-cn:/siyaoH-1.17-fixAndroid
unity-tech-cn:/siyaoH-1.17-HandleEvent
unity-tech-cn:/siyaoH-1.17-combineTouch
unity-tech-cn:/refine_readme
unity-tech-cn:/2.0.4
unity-tech-cn:/2.0.1
unity-tech-cn:/dev-packman
unity-tech-cn:/zxw-fix_text_shadow_issue
unity-tech-cn:/zxw-fix_backdropfilter
unity-tech-cn:/zxw-fix_transform_hover_issue
unity-tech-cn:/zxw-refine_codegen
unity-tech-cn:/zxw-fix_material_datepicker_issue
unity-tech-cn:/zxw-fix_material_text_direction
unity-tech-cn:/zxw-fix_path_metric_crash
unity-tech-cn:/demo
unity-tech-cn:/jiacheng-demo_proctice
unity-tech-cn:/zxw-3d
unity-tech-cn:/zxw-fix_crash
unity-tech-cn:/zxw-fix_input_issue
unity-tech-cn:/zxw-3d_android
unity-tech-cn:/main/LAO
此合并请求有变更与目标分支冲突。
/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 次代码提交
main
...
zgh-devtools
作者 | SHA1 | 备注 | 提交日期 |
---|---|---|---|
guanghuispark | d0b6e5fc | Merge branch 'master' into zgh/devtools | 3 年前 |
共有 568 个文件被更改,包括 13751 次插入 和 8 次删除
-
5Samples/UIWidgetsSamples_2019_4/Packages/manifest.json
-
3com.unity.uiwidgets/Runtime/animation/animation_controller.cs
-
2com.unity.uiwidgets/Runtime/foundation/key.cs
-
4com.unity.uiwidgets/Runtime/widgets/single_child_scroll_view.cs
-
2com.unity.uiwidgets/Runtime/semantics/semantics.cs.meta
-
8Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample.meta
-
3Samples/UIWidgetsSamples_2019_4/Assets/Editor.meta
-
8com.unity.uiwidgets/Runtime/cupertino.meta
-
8com.unity.uiwidgets/Runtime/redux.meta
-
8com.unity.uiwidgets/Runtime/Plugins.meta
-
9com.unity.uiwidgets.devtools/package.json
-
23com.unity.uiwidgets.devtools/Editor/enum_utils.cs
-
11com.unity.uiwidgets.devtools/Editor/Unity.UIWidgets.DevTools.Editor.asmdef
-
23com.unity.uiwidgets.devtools/Editor/analytics/provider.cs
-
53com.unity.uiwidgets.devtools/Editor/analytics/stub_provider.cs
-
548com.unity.uiwidgets.devtools/Editor/app.cs
-
3com.unity.uiwidgets.devtools/Editor/config_specific.meta
-
18com.unity.uiwidgets.devtools/Editor/config_specific/ide_theme/ide_theme.cs
-
52com.unity.uiwidgets.devtools/Editor/globals.cs
-
3com.unity.uiwidgets.devtools/Editor/globals.cs.meta
-
260com.unity.uiwidgets.devtools/Editor/inspector/diagnostics.cs
-
1001com.unity.uiwidgets.devtools/Editor/inspector/diagnostics_node.cs
-
152com.unity.uiwidgets.devtools/Editor/inspector/flutter_widget.cs
-
974com.unity.uiwidgets.devtools/Editor/inspector/inspector_controller.cs
-
1001com.unity.uiwidgets.devtools/Editor/inspector/inspector_data_models.cs
-
408com.unity.uiwidgets.devtools/Editor/inspector/inspector_screen.cs
-
144com.unity.uiwidgets.devtools/Editor/inspector/inspector_screen_details_tab.cs
-
1001com.unity.uiwidgets.devtools/Editor/inspector/inspector_service.cs
-
43com.unity.uiwidgets.devtools/Editor/inspector/inspector_text_style.cs
-
873com.unity.uiwidgets.devtools/Editor/inspector/inspector_tree.cs
-
453com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/box/box.cs
-
807com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/flex/flex.cs
-
266com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/flex/utils.cs
-
93com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/layout_explorer.cs
-
374com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/arrow.cs
-
39com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/dimension.cs
-
170com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/free_space.cs
-
71com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/overflow_indicator_painter.cs
-
218com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/theme.cs
-
193com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/widget_constraints.cs
-
345com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/layout_explorer_widget.cs
-
569com.unity.uiwidgets.devtools/Editor/inspector/layout_explorer/ui/utils.cs
-
21com.unity.uiwidgets.devtools/Editor/listenable.cs
-
20com.unity.uiwidgets.devtools/Editor/main.cs
-
290com.unity.uiwidgets.devtools/Editor/screen.cs
-
59com.unity.uiwidgets.devtools/Editor/theme.cs
-
311com.unity.uiwidgets.devtools/Editor/ui/icons.cs
-
59com.unity.uiwidgets.devtools/Editor/utils.cs
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialButtonSample.cs.meta
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialInkWellSample.cs.meta
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialNavigationBarSample.cs.meta
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialSliderSample.cs.meta
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialTabBarSample.cs.meta
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialThemeSample.cs.meta
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/TableSample.cs.meta
-
11Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/ReorderableListSample.cs.meta
-
47Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/BottomAppBarSample.cs
-
107Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/DividerAndButton.cs
-
204Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialAppBarSample.cs
-
73Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialButtonSample.cs
-
56Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialInkWellSample.cs
-
86Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialNavigationBarSample.cs
-
61Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialSliderSample.cs
-
130Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialTabBarSample.cs
-
82Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/MaterialThemeSample.cs
-
79Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/ReorderableListSample.cs
-
53Samples/UIWidgetsSamples_2019_4/Assets/MaterialSample/TableSample.cs
-
3Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/gallery.meta
-
14Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/UIWidgetsGallery.asmdef
-
27Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/GalleryMain.cs
-
3Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine.meta
-
3Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/material.meta
-
19Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs
-
3Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/shrine_demo.cs.meta
-
276Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_alert_demo.cs
-
89Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_buttons_demo.cs
-
307Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_picker_demo.cs
-
67Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_slider_demo.cs
-
873Samples/UIWidgetsSamples_2019_4/Assets/UIWidgetsGallery/demo/cupertino/cupertino_navigation_demo.cs
|
|||
fileFormatVersion: 2 |
|||
guid: c0f377c78881e9243879a01b93524d63 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: fbf4348f25a14fb7a48fd287e0d01b88 |
|||
timeCreated: 1614220178 |
|
|||
fileFormatVersion: 2 |
|||
guid: 5c3d34af75804254da0566129e0d4ac0 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: a074d5db1a47743f19fc0f9ac709e173 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 13f5f7be9f33d144d89d2195786677c7 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
{ |
|||
"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": { |
|||
} |
|||
} |
|
|||
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]; |
|||
} |
|||
|
|||
} |
|
|||
{ |
|||
"name": "Unity.UIWidgets.DevTools.Editor", |
|||
"references": [ |
|||
"Unity.UIWidgets", |
|||
"Unity.UIWidgets.Editor" |
|||
], |
|||
"includePlatforms": [ |
|||
"Editor" |
|||
], |
|||
"excludePlatforms": [] |
|||
} |
|
|||
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(); |
|||
} |
|||
} |
|
|||
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() {} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|
|||
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, |
|||
], |
|||
), |
|||
); |
|||
}*/ |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|
|||
fileFormatVersion: 2 |
|||
guid: ec20b4818fa849a88ade34bed3bc361d |
|||
timeCreated: 1615531810 |
|
|||
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; |
|||
} |
|||
} |
|
|||
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;
|
|||
// }
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 2cd9c39bad944654afbb4b9928bf19b9 |
|||
timeCreated: 1615544477 |
|
|||
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
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
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"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|
|||
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
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
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); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
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 |
|||
)); |
|||
} |
|||
} |
|
|||
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); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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 |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
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 |
|||
) |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|
|||
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; } |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
|
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|
|||
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(); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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; |
|||
} |
|||
} |
|||
} |
|
|||
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 |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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(); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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 |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|
|||
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 |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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; |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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 |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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;
|
|||
} |
|||
} |
|
|||
|
|||
|
|||
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)); |
|||
} |
|||
} |
|||
} |
|
|||
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(); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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;
|
|||
// }
|
|||
|
|||
} |
|||
} |
|
|||
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"); |
|||
} |
|||
} |
|
|||
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"; |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 347e0378e419f46f2af69e1214f1ae5b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: b9e88c33e9f304d40bab9c27f1276033 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: cc34344049d222b468f2487b97076448 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 0152b6f575e6ca44baea965a23ebe498 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: fecd06f805d0b0f4ead134b1ad6a9a49 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: b2a39cf0b83823749a13aebaf25cfb1b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 1cba65c03fbf50f4cac974273a7c9445 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: f510d7fbd19431046bfb4b92c39b4da4 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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: () => { }) |
|||
}))); |
|||
} |
|||
} |
|||
} |
|
|||
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") |
|||
) |
|||
) |
|||
} |
|||
) |
|||
} |
|||
) |
|||
) |
|||
)); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
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); |
|||
}) |
|||
} |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
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"); } |
|||
) |
|||
} |
|||
) |
|||
) |
|||
) |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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"); } |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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; }); } |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
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)) |
|||
) |
|||
} |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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 |
|||
)); |
|||
} |
|||
} |
|||
} |
|
|||
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: () => { }) |
|||
}) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
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(() => {}); |
|||
} |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
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)); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 20b07a92c4c64919aa3adf9322758a65 |
|||
timeCreated: 1612322595 |
|
|||
{ |
|||
"name": "UIWidgetsGallery", |
|||
"references": [ |
|||
"Unity.UIWidgets" |
|||
], |
|||
"optionalUnityReferences": [], |
|||
"includePlatforms": [], |
|||
"excludePlatforms": [], |
|||
"allowUnsafeCode": false, |
|||
"overrideReferences": false, |
|||
"precompiledReferences": [], |
|||
"autoReferenced": true, |
|||
"defineConstraints": [] |
|||
} |
|
|||
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}); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: fb97b78a8c204fbb9c9dbf667cb23004 |
|||
timeCreated: 1612342718 |
|
|||
fileFormatVersion: 2 |
|||
guid: 27e2b642479d4616ad980ef32f76e152 |
|||
timeCreated: 1612406310 |
|
|||
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(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b5da65292c17429ba697ebdefb05c8e4 |
|||
timeCreated: 1612512022 |
|
|||
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"); |
|||
} |
|||
) |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
} |
|
|||
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 |
|||
) |
|||
} |
|||
) |
|||
) |
|||
} |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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 |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|
|||
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}") |
|||
} |
|||
) |
|||
} |
|||
) |
|||
) |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|
|||
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); } |
|||
), |
|||
} |
|||
) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
部分文件因为文件数量过多而无法显示
撰写
预览
正在加载...
取消
保存
Reference in new issue